From a9079bfa3e6a90d968b1813a83e7864df5998d42 Mon Sep 17 00:00:00 2001 From: Praveen K V Date: Fri, 3 Feb 2017 10:02:29 +0530 Subject: [PATCH] Ensure flow-stickiness in case of ECMP with bridged forward flow and routed reverse flow This is part of change needed to ensure flow-stickiness when forward flow is bridged and reverse flow is routed. It has following parts, Change to support ECMP Index management by VRouter VRouter now manages the ECMP Index for a flow. It keeps tracks source for a flow and updates the ECMP Index in reverse flow such that reverse flow are sent to origin. This commit supports ECMP Index management by VRouter. The key for reverse flow can potentially depend on the ECMP Index used for forward flow. Hence, Agent computes initial ECMP Index for forward flows still. However, agent does not compute ECMP Index for reverse flow. Re-organized the ECMP and RPF management mode in the process. Re-organized UT code for ECMP and RPF checks Pending: 1. Skip VRF assignment for bridged packets 2. Support Unrestricted Proxy-ARP on interface Change-Id: Iff3ee337ef9721df3e91336960691dc2480a22bb Partial-Bug: #1648696 Partial-Bug: #1645978 --- src/vnsw/agent/oper/nexthop.cc | 51 + src/vnsw/agent/oper/nexthop.h | 4 + src/vnsw/agent/pkt/flow_entry.cc | 578 ++-- src/vnsw/agent/pkt/flow_entry.h | 49 +- src/vnsw/agent/pkt/flow_mgmt.cc | 12 +- src/vnsw/agent/pkt/flow_table.cc | 7 +- src/vnsw/agent/pkt/pkt.sandesh | 3 + src/vnsw/agent/pkt/pkt_flow_info.cc | 309 +-- src/vnsw/agent/pkt/pkt_flow_info.h | 7 +- src/vnsw/agent/pkt/pkt_handler.cc | 2 - src/vnsw/agent/pkt/pkt_handler.h | 2 +- src/vnsw/agent/pkt/pkt_sandesh_flow.cc | 7 +- src/vnsw/agent/pkt/test/SConscript | 12 +- src/vnsw/agent/pkt/test/rpf-flow.xml | 8 +- src/vnsw/agent/pkt/test/test_ecmp.cc | 2388 ----------------- src/vnsw/agent/pkt/test/test_ecmp.h | 316 +++ src/vnsw/agent/pkt/test/test_ecmp_fip.cc | 404 +++ src/vnsw/agent/pkt/test/test_ecmp_local.cc | 629 +++-- src/vnsw/agent/pkt/test/test_ecmp_mx.cc | 96 +- src/vnsw/agent/pkt/test/test_ecmp_remote.cc | 1156 ++++++++ .../agent/pkt/test/test_ecmp_service_vlan.cc | 1343 +++++++++ src/vnsw/agent/pkt/test/test_fip_dst_ecmp.cc | 200 -- src/vnsw/agent/pkt/test/test_fip_src_ecmp.cc | 30 +- src/vnsw/agent/pkt/test/test_flow_audit.cc | 24 +- src/vnsw/agent/pkt/test/test_flow_base.cc | 22 +- .../agent/pkt/test/test_flow_trace_filter.cc | 19 - src/vnsw/agent/pkt/test/test_flow_update.cc | 18 +- src/vnsw/agent/pkt/test/test_flow_util.h | 4 +- .../agent/pkt/test/test_pkt_flow_limits.cc | 19 - src/vnsw/agent/pkt/test/test_pkt_util.cc | 80 +- src/vnsw/agent/pkt/test/test_pkt_util.h | 13 +- src/vnsw/agent/pkt/test/test_rpf_flow.cc | 407 --- src/vnsw/agent/pkt/test/test_rpf_uc.cc | 582 ++++ src/vnsw/agent/pkt/test/test_xml_packet_ut.cc | 25 +- src/vnsw/agent/pkt/vrouter_interface.h | 1 - src/vnsw/agent/test-xml/test_xml_oper.cc | 6 +- src/vnsw/agent/test/test_cmn_util.h | 7 + src/vnsw/agent/test/test_util.cc | 10 + .../agent/vrouter/ksync/flowtable_ksync.cc | 6 +- 39 files changed, 4962 insertions(+), 3894 deletions(-) delete mode 100644 src/vnsw/agent/pkt/test/test_ecmp.cc create mode 100644 src/vnsw/agent/pkt/test/test_ecmp.h create mode 100644 src/vnsw/agent/pkt/test/test_ecmp_fip.cc create mode 100644 src/vnsw/agent/pkt/test/test_ecmp_remote.cc create mode 100644 src/vnsw/agent/pkt/test/test_ecmp_service_vlan.cc delete mode 100644 src/vnsw/agent/pkt/test/test_fip_dst_ecmp.cc delete mode 100644 src/vnsw/agent/pkt/test/test_rpf_flow.cc create mode 100644 src/vnsw/agent/pkt/test/test_rpf_uc.cc diff --git a/src/vnsw/agent/oper/nexthop.cc b/src/vnsw/agent/oper/nexthop.cc index 9e8888a7931..f287b7a3eb7 100644 --- a/src/vnsw/agent/oper/nexthop.cc +++ b/src/vnsw/agent/oper/nexthop.cc @@ -1422,6 +1422,57 @@ const NextHop* CompositeNH::GetLocalNextHop() const { return NULL; } +bool CompositeNH::HasVmInterface(const VmInterface *vmi) const { + ComponentNHList::const_iterator comp_nh_it = + component_nh_list_.begin(); + for(;comp_nh_it != component_nh_list_.end(); comp_nh_it++) { + if ((*comp_nh_it) == NULL) { + continue; + } + + if ((*comp_nh_it)->nh()->GetType() == NextHop::INTERFACE) { + const InterfaceNH *intf_nh = dynamic_cast + ((*comp_nh_it)->nh()); + if (intf_nh->GetInterface() == vmi) + return true; + } + if ((*comp_nh_it)->nh()->GetType() == NextHop::VLAN) { + const VlanNH *vlan_nh = dynamic_cast + ((*comp_nh_it)->nh()); + if (vlan_nh->GetInterface() == vmi) + return true; + } + } + return false; +} + +uint32_t CompositeNH::PickMember(uint32_t seed, uint32_t affinity_index) const { + uint32_t idx = kInvalidComponentNHIdx; + size_t size = component_nh_list_.size(); + if (size == 0) { + return idx; + } + + if (affinity_index != kInvalidComponentNHIdx) { + const NextHop *nh = GetNH(affinity_index); + if (nh != NULL && nh->IsActive()) { + return affinity_index; + } + } + + idx = seed % size; + while (component_nh_list_[idx].get() == NULL || + component_nh_list_[idx]->nh() == NULL || + component_nh_list_[idx]->nh()->IsActive() == false) { + idx = (idx + 1) % size; + if (idx == seed % size) { + idx = kInvalidComponentNHIdx; + break; + } + } + return idx; +} + NextHop *CompositeNHKey::AllocEntry() const { VrfEntry *vrf = static_cast (Agent::GetInstance()->vrf_table()->Find(&vrf_key_, true)); diff --git a/src/vnsw/agent/oper/nexthop.h b/src/vnsw/agent/oper/nexthop.h index 1e40f89c6a2..1d5d512ac64 100644 --- a/src/vnsw/agent/oper/nexthop.h +++ b/src/vnsw/agent/oper/nexthop.h @@ -1550,6 +1550,7 @@ class CompositeNH : public NextHop { return active_count; } + uint32_t PickMember(uint32_t seed, uint32_t affinity_index) const; const NextHop* GetNH(uint32_t idx) const { if (idx >= component_nh_list_.size()) { return NULL; @@ -1606,6 +1607,7 @@ class CompositeNH : public NextHop { } return idx; } + bool HasVmInterface(const VmInterface *vmi) const; bool GetIndex(ComponentNH &nh, uint32_t &idx) const; const ComponentNH* Get(uint32_t idx) const { return component_nh_list_[idx].get(); @@ -1647,7 +1649,9 @@ class CompositeNH : public NextHop { ///////////////////////////////////////////////////////////////////////////// class NextHopTable : public AgentDBTable { public: + static const uint32_t kRpfDisableIndex = 0; static const uint32_t kRpfDiscardIndex = 2; + NextHopTable(DB *db, const std::string &name); virtual ~NextHopTable(); diff --git a/src/vnsw/agent/pkt/flow_entry.cc b/src/vnsw/agent/pkt/flow_entry.cc index 1fd6d1aee73..1d728d97698 100644 --- a/src/vnsw/agent/pkt/flow_entry.cc +++ b/src/vnsw/agent/pkt/flow_entry.cc @@ -268,7 +268,7 @@ void FlowData::Reset() { intf_entry.reset(NULL); in_vm_entry.Reset(true); out_vm_entry.Reset(true); - nh.reset(NULL); + src_ip_nh.reset(NULL); vrf = VrfEntry::kInvalidIndex; mirror_vrf = VrfEntry::kInvalidIndex; dest_vrf = 0; @@ -282,10 +282,11 @@ void FlowData::Reset() { flow_source_plen_map.clear(); flow_dest_plen_map.clear(); enable_rpf = true; - l2_rpf_plen = Address::kMaxV4PrefixLen; + rpf_nh.reset(NULL); + rpf_plen = Address::kMaxV4PrefixLen; + rpf_vrf = VrfEntry::kInvalidIndex; vm_cfg_name = ""; bgp_as_a_service_port = 0; - ecmp_rpf_nh_ = 0; acl_assigned_vrf_index_ = VrfEntry::kInvalidIndex; qos_config_idx = AgentQosConfigTable::kInvalidIndex; ttl = 0; @@ -552,7 +553,6 @@ bool FlowEntry::InitFlowCmn(const PktFlowInfo *info, const PktControlInfo *ctrl, data_.in_vm_entry.SetVm(ctrl->vm_); data_.out_vm_entry.SetVm(rev_ctrl->vm_); l3_flow_ = info->l3_flow; - data_.ecmp_rpf_nh_ = 0; data_.acl_assigned_vrf_index_ = VrfEntry::kInvalidIndex; return true; } @@ -580,7 +580,7 @@ void FlowEntry::InitFwdFlow(const PktFlowInfo *info, const PktInfo *pkt, reset_flags(FlowEntry::IngressDir); } if (ctrl->rt_ != NULL) { - SetRpfNH(flow_table_, ctrl->rt_); + RpfInit(ctrl->rt_); } if (info->bgp_router_service_flow) { @@ -656,7 +656,7 @@ void FlowEntry::InitRevFlow(const PktFlowInfo *info, const PktInfo *pkt, } } if (ctrl->rt_ != NULL) { - SetRpfNH(flow_table_, ctrl->rt_); + RpfInit(ctrl->rt_); } if (info->bgp_router_service_flow) { @@ -681,12 +681,7 @@ void FlowEntry::InitRevFlow(const PktFlowInfo *info, const PktInfo *pkt, } else { reset_flags(FlowEntry::EcmpFlow); } - data_.component_nh_idx = info->in_component_nh_idx; - if (info->trap_rev_flow) { - set_flags(FlowEntry::Trap); - } else { - reset_flags(FlowEntry::Trap); - } + data_.component_nh_idx = CompositeNH::kInvalidComponentNHIdx; reset_flags(FlowEntry::UnknownUnicastFlood); if (info->flood_unknown_unicast) { @@ -854,6 +849,275 @@ static bool ShouldDrop(uint32_t action) { return false; } +///////////////////////////////////////////////////////////////////////////// +// Flow RPF +// +// VRouter enforces RPF check based on RPF-NH programmed in the flow. The RPF +// NH can be of two types, +// - Unicast NH : +// In this case, VRouter matches with incoming interface/tunnel-src with +// RPF NH the flow +// +// - ECMP NH : +// In this case, VRouter picks an ECMP component-nh and matches with incoming +// interface/tunnel-src. The index for component-nh is got from reverse flow. +// +// RPF-NH cases +// ------------ +// 1. Baremetals +// Agent is not aware of IP address assigned for baremetals. So, RPF check +// for baremetals is based on the L2-Route +// +// 2. Layer-2 Flows +// If agent know the Inet route for source-ip in packet, RPF is based on +// the Inet route for source-ip. There are some exceptions for this rule, +// - If Inet route for source-ip is ECMP, then RPF is based on layer-2 route +// - If Inet route for source-ip is not-host route, then RPF is based on +// layer-2 routes +// +// 3. Layer-3 Flows from VMI +// RPF check will be based on the InterfaceNH for VMI +// +// 4. Layer-3 Flows from Fabric with unicast-NH +// The unicast-nh is used as RPF-NH +// +// 5. Layer-3 Flows from Fabric with composite-NH +// VRouter picks NH from flow and the ecmp-index from reverse flow. +// +// The ecmp-index in reverse is computed based on route for dest-ip in +// VRF got post VRF translation if any +// +// Note, the RPF must be picked post VRF translation since order of members +// in Composite-NH may vary across VRF +// +// Route Tracking +// -------------- +// Flow Management should track the route for ip1 in vrf4 to update RPF-NH +// +// RPF Computation +// --------------- +// RPF computation happens in two stages +// 1. FlowEntry creation (RpfInit): +// Called during FlowEntry init. Computes src_ip_nh for flow. +// +// For layer-2 flows, RPF-NH Is same as src_ip_nh +// For Non-ECMP layer-3 flows, RPF-NH is same as src_ip_nh +// For ECMP layer-3 flows, RPF-NH must be computed only after VRF +// translation is computed for reverse flow. +// +// 2. Post ACL processing (RpfUpdate): +// Post ACL processing, all VRF translation rules are identified. +// The RPF-NH is computed in this method. +///////////////////////////////////////////////////////////////////////////// +// Utility method to set rpf_vrf and rpf_plen fields +static void SetRpfFieldsInternal(FlowEntry *flow, const AgentRoute *rt) { + // If there is no route, we should track default route + if (rt == NULL) { + flow->data().rpf_vrf = flow->data().vrf; + flow->data().rpf_plen = 0; + return; + } + + if (dynamic_cast(rt)) { + flow->data().rpf_vrf = rt->vrf()->vrf_id(); + flow->data().rpf_plen = rt->plen(); + return; + } + + // Route is not INET. Dont track any route + flow->data().rpf_vrf = VrfEntry::kInvalidIndex; + flow->data().rpf_plen = 0; + return; +} + +// Utility method to set src_ip_nh fields +void FlowEntry::RpfSetSrcIpNhFields(const AgentRoute *rt, + const NextHop *src_ip_nh) { + data_.src_ip_nh.reset(src_ip_nh); + SetRpfFieldsInternal(this, rt); + return; +} + +void FlowEntry::RpfSetRpfNhFields(const NextHop *rpf_nh) { + data_.rpf_nh.reset(rpf_nh); +} + +// Utility method to set rpf_nh fields +void FlowEntry::RpfSetRpfNhFields(const AgentRoute *rt, const NextHop *rpf_nh) { + data_.rpf_nh.reset(rpf_nh); + SetRpfFieldsInternal(this, rt); + return; +} + +// This method is called when flow is initialized. The RPF-NH cannot be +// computed at this stage since we dont know if reverse flow has VRF +// translation or not. +// This method only sets src_ip_nh field +// +// In case of layer-3 flow "rt" is inet route for source-ip in source-vrf +// In case of layer-2 flow "rt" is l2 route for smac in source-vrf +void FlowEntry::RpfInit(const AgentRoute *rt) { + // Set src_ip_nh based on rt first + RpfSetSrcIpNhFields(rt, rt->GetActiveNextHop()); + + // RPF enabled? + bool rpf_enable = true; + if (data_.vn_entry && data_.vn_entry->enable_rpf() == false) + rpf_enable = false; + + // The src_ip_nh can change below only for l2 flows + // For l3-flow, rt will already be a INET route + if (l3_flow()) + return; + + // For layer-2 flows, we use l2-route for RPF in following cases + // 1. Interface is of type BAREMETAL (ToR/TSN only) + // + // 2. ECMP is not supported for l2-flows. If src-ip lookup resulted in + // ECMP-NH fallback to the original l2-route + // + // 3. In case of OVS, ToR will export layer-2 route and MX will export a + // layer-3 subnet route covering all the ToRs. In this case, when ToR + // send layer-2 packet the layer-3 route will point to MX and RPF fails. + // Assuming MX only gives subnet-route, use inet-route only if its + // host-route + const VmInterface *vmi = + dynamic_cast(intf_entry()); + if (vmi && vmi->vmi_type() == VmInterface::BAREMETAL) { + return; + } + + const InetUnicastRouteEntry *src_ip_rt = + static_cast + (FlowEntry::GetUcRoute(rt->vrf(), key().src_addr)); + + if (src_ip_rt == NULL) { + if (rpf_enable) { + set_flags(FlowEntry::ShortFlow); + short_flow_reason_ = SHORT_NO_SRC_ROUTE_L2RPF; + } + return; + } + + if (src_ip_rt->IsHostRoute() == false) + return; + + const NextHop *src_ip_nh = src_ip_rt->GetActiveNextHop(); + if (src_ip_nh->GetType() == NextHop::COMPOSITE) + return; + + RpfSetSrcIpNhFields(src_ip_rt, src_ip_nh); + return; +} + +// Should src_ip_nh be treated as RPF-NH +bool FlowEntry::RpfFromSrcIpNh() const { + // For l2-flows, src_ip_nh is same as RPF-NH + if (l3_flow() == false) + return true; + + // Dont bother about RPF for short-flows + if (is_flags_set(FlowEntry::ShortFlow)) + return true; + + // rpf-nh can change only in case of composite + if (data_.src_ip_nh->GetType() != NextHop::COMPOSITE) + return true; + + const FlowEntry *rflow = reverse_flow_entry(); + if (rflow == NULL) { + FlowInfo flow_info; + FillFlowInfo(flow_info); + FLOW_TRACE(Trace, "Invalid reverse flow for setting ECMP index", + flow_info); + return true; + } + + return false; +} + +void FlowEntry::RpfComputeIngress() { + const NextHop *nh = NULL; + // If we are here, its guaranteed that src_ip_nh is composite + const CompositeNH *cnh = + dynamic_cast(data_.src_ip_nh.get()); + assert(cnh != NULL); + + // Use flow_key_nh if VMI is part of composite. Else its guaranteed + // that RPF will fail for flow. So, use discard-nh for RPF + const VmInterface *vmi = + dynamic_cast(intf_entry()); + if (cnh->HasVmInterface(vmi)) { + nh = vmi->flow_key_nh(); + } else { + nh = data_.src_ip_nh.get(); + } + + // Change only the RPF-NH. The vrf and plen dont change here + RpfSetRpfNhFields(nh); + return; +} + +void FlowEntry::RpfComputeEgress() { + const FlowEntry *rflow = reverse_flow_entry(); + + // RPF-NH can change only if VRF in forward flow and translated VRF in + // reverse flow are different + if (rflow->data().flow_dest_vrf == data_.vrf) { + RpfSetRpfNhFields(data_.src_ip_nh.get()); + return; + } + + // Find destination VRF from reverse flow + const VrfEntry *vrf = rflow->GetDestinationVrf(); + if (vrf == NULL) { + FlowInfo flow_info; + FillFlowInfo(flow_info); + FLOW_TRACE(Trace, "Error setting RPF NH. Destination VRF not found " + "in reverse flow", flow_info); + return; + } + + InetUnicastRouteEntry *rt = static_cast + (FlowEntry::GetUcRoute(vrf, key().src_addr)); + if (!rt ) { + FlowInfo flow_info; + FillFlowInfo(flow_info); + FLOW_TRACE(Trace, "Error setting RPF NH. Route not found in " + "destination vrf", flow_info); + return; + } + + RpfSetRpfNhFields(rt, rt->GetActiveNextHop()); + return; +} + +// Computes RPF-NH for flow based on ip_src_nh +// Must be called after policy lookup is done +void FlowEntry::RpfUpdate() { + // Is RPF check enabled? + data_.enable_rpf = true; + if (data_.vn_entry) { + data_.enable_rpf = data_.vn_entry->enable_rpf(); + } + + if (data_.enable_rpf == false) { + data_.rpf_nh = NULL; + return; + } + + if (RpfFromSrcIpNh()) { + data_.rpf_nh = data_.src_ip_nh; + return; + } + + if (is_flags_set(FlowEntry::IngressDir)) { + RpfComputeIngress(); + } else { + RpfComputeEgress(); + } +} + ///////////////////////////////////////////////////////////////////////////// // Flow entry fileds updation routines ///////////////////////////////////////////////////////////////////////////// @@ -911,93 +1175,6 @@ void FlowEntry::GetDestRouteInfo(const AgentRoute *rt) { } } -void FlowEntry::UpdateRpf() { - if (data_.vn_entry) { - data_.enable_rpf = data_.vn_entry->enable_rpf(); - } else { - data_.enable_rpf = true; - } -} - -//Given a NH take reference on the NH and set the RPF -bool FlowEntry::SetRpfNHState(FlowTable *ft, const NextHop *nh) { - - if (data_.nh.get() != nh) { - data_.nh = nh; - return true; - } - return false; -} - -bool FlowEntry::SetRpfNH(FlowTable *ft, const AgentRoute *rt) { - bool ret = false; - - if (data().ecmp_rpf_nh_ != 0) { - //Set RPF NH based on reverese flow route - return ret; - } - - if (!rt) { - return SetRpfNHState(ft, NULL); - } - - //If l2 flow has a ip route entry present in - //layer 3 table, then use that for calculating - //rpf nexthop, else use layer 2 route entry(baremetal - //scenario where layer 3 route may not be present) - bool is_baremetal = false; - const VmInterface *vmi = dynamic_cast(intf_entry()); - if (vmi && vmi->vmi_type() == VmInterface::BAREMETAL) { - is_baremetal = true; - } - - data_.l2_rpf_plen = Address::kMaxV4PrefixLen; - if (l3_flow() == false && is_baremetal == false) { - //For ingress flow, agent always sets - //rpf NH from layer 3 route entry - //In case of egress flow if route entry is present - //and its a host route entry use it for RPF NH - //For baremetal case since IP address may not be known - //agent uses layer 2 route entry - InetUnicastRouteEntry *ip_rt = static_cast( - FlowEntry::GetUcRoute(rt->vrf(), key().src_addr)); - if (ip_rt && - ip_rt->GetActiveNextHop()->GetType() == NextHop::COMPOSITE) { - //L2 flow cant point to composite NH, set RPF NH based on - //layer 2 route irrespective prefix lenght of layer 3 route, - //this is to avoid packet drop in scenario where transition - //happened from non-ecmp to ECMP. - } else if (is_flags_set(FlowEntry::IngressDir) || - (ip_rt && ip_rt->IsHostRoute())) { - if (data_.vn_entry && data_.vn_entry->enable_rpf() && !ip_rt) { - set_flags(FlowEntry::ShortFlow); - short_flow_reason_ = SHORT_NO_SRC_ROUTE_L2RPF; - return ret; - } - rt = ip_rt; - if (rt) { - data_.l2_rpf_plen = rt->plen(); - } - } - } - - const NextHop *nh = NULL; - if (rt && rt->GetActiveNextHop()) { - nh = rt->GetActiveNextHop(); - } - - return SetRpfNHState(ft, nh); -} - -bool FlowEntry::SetEcmpRpfNH(FlowTable *ft, uint32_t nh_id) { - if (!nh_id) { - return SetRpfNHState(ft, NULL); - } - - const NextHop *nh = ft->agent()->nexthop_table()->FindNextHop(nh_id); - return SetRpfNHState(ft, nh); -} - VmInterfaceKey FlowEntry::InterfaceIdToKey(Agent *agent, uint32_t id) { if (id != Interface::kInvalidIndex) { Interface *itf = agent->interface_table()->FindInterface(id); @@ -1154,6 +1331,10 @@ void FlowEntry::GetPolicy(const VnEntry *vn, const FlowEntry *rflow) { } void FlowEntry::GetVrfAssignAcl() { + // VRF-Assign rules valid only for routed packets + if (l3_flow() == false) + return; + if (data_.intf_entry == NULL) { return; } @@ -1766,9 +1947,7 @@ bool FlowEntry::DoPolicy() { // Flow policy action compute routines ///////////////////////////////////////////////////////////////////////////// void FlowEntry::ResyncFlow() { - UpdateRpf(); DoPolicy(); - UpdateEcmpInfo(); // If this is forward flow, update the SG action for reflexive entry FlowEntry *rflow = (is_flags_set(FlowEntry::ReverseFlow) == false) ? @@ -1781,7 +1960,7 @@ void FlowEntry::ResyncFlow() { } const VrfEntry* -FlowEntry::GetDestinationVrf() { +FlowEntry::GetDestinationVrf() const { const VrfEntry *vrf = NULL; VrfTable *vrf_table = flow_table()->agent()->vrf_table(); @@ -1794,186 +1973,6 @@ FlowEntry::GetDestinationVrf() { return vrf; } -void FlowEntry::SetComponentIndex(const NextHopKey *nh_key, - uint32_t label, bool mpls_path_select) { - - const VrfEntry *vrf = GetDestinationVrf(); - if (vrf == NULL) { - FlowInfo flow_info; - FillFlowInfo(flow_info); - FLOW_TRACE(Trace, "Invalid reverse while setting ECMP index", flow_info); - return; - } - - FlowEntry *rflow = reverse_flow_entry(); - const IpAddress dip = rflow->key().src_addr; - InetUnicastRouteEntry *rt = - static_cast(FlowEntry::GetUcRoute(vrf, dip)); - if (!rt ) { - rflow->set_ecmp_rpf_nh(0); - return; - } - - const NextHop *nh = rt->GetActiveNextHop(); - //Set composite NH based on local mpls label flow - if (mpls_path_select) { - nh = rt->GetLocalNextHop(); - } - - if (!nh) { - rflow->set_ecmp_rpf_nh(0); - return; - } - - if (nh->GetType() != NextHop::COMPOSITE) { - rflow->set_ecmp_rpf_nh(nh->id()); - return; - } - - const CompositeNH *comp_nh = static_cast(nh); - - const NextHop *component_nh_ptr = static_cast( - flow_table()->agent()->nexthop_table()->FindActiveEntry(nh_key)); - ComponentNH component_nh(label, component_nh_ptr); - - uint32_t idx = 0; - if (comp_nh->GetIndex(component_nh, idx)) { - if (data_.component_nh_idx != idx) { - data_.component_nh_idx = idx; - } - //Update the reverse flow source RPF check based on this - //composite NH - rflow->set_ecmp_rpf_nh(nh->id()); - } else { - FlowInfo flow_info; - FillFlowInfo(flow_info); - FLOW_TRACE(Trace, "Invalid reverse while setting ECMP index", flow_info); - } -} - -void FlowEntry::SetLocalFlowEcmpIndex() { - //There are 2 scenarios possible when the destination - //interface is a VM interface - //1> Flow is local flow - // In this scenario component NH Index has to be set based - // on that of BGP path since packets are getting routed - // based on route table - //2> Flow is remote i.e packet came with mpls label from a tunnel - // In this sceanrio if mpls label points to composite NH - // pick component index from there, if mpls label points to - // interface NH there is no need to set component index - uint32_t label; - FlowEntry *rflow = reverse_flow_entry(); - - const VmInterface *vm_port = NULL; - if (rflow->data().intf_entry->type() == Interface::VM_INTERFACE) { - vm_port = - static_cast(rflow->data().intf_entry.get()); - label = vm_port->label(); - } else { - const InetInterface *inet_intf = - static_cast(rflow->data().intf_entry.get()); - label = inet_intf->label(); - } - - //Find the source NH - const NextHop *nh = flow_table()->agent()->nexthop_table()-> - FindNextHop(rflow->key().nh); - if (nh == NULL) { - return; - } - DBEntryBase::KeyPtr key = nh->GetDBRequestKey(); - NextHopKey *nh_key = static_cast(key.get()); - - //All component nexthop are NULL - nh_key->SetPolicy(false); - - if (nh->GetType() == NextHop::VLAN) { - const VlanNH *vlan_nh = static_cast(nh); - label = vm_port->GetServiceVlanLabel(vlan_nh->GetVrf()); - } - - bool mpls_path = false; - if (!is_flags_set(FlowEntry::LocalFlow)) { - mpls_path = true; - } - - SetComponentIndex(nh_key, label, mpls_path); -} - -void FlowEntry::SetRemoteFlowEcmpIndex() { - uint32_t label; - - //Get tunnel info from reverse flow - label = 0; - boost::system::error_code ec; - Ip4Address dest_ip = Ip4Address::from_string(peer_vrouter_, ec); - if (ec.value() != 0) { - return; - } - - boost::scoped_ptr nh_key( - new TunnelNHKey(flow_table()->agent()->fabric_vrf_name(), - flow_table()->agent()->router_id(), - dest_ip, false, tunnel_type())); - SetComponentIndex(nh_key.get(), label, false); -} - -void FlowEntry::UpdateEcmpInfo() { - FlowEntry *rflow = reverse_flow_entry(); - - if (is_flags_set(FlowEntry::EcmpFlow) == false || - is_flags_set(FlowEntry::ShortFlow) || - l3_flow() == false) { - return; - } - - if (rflow == NULL) { - FlowInfo flow_info; - FillFlowInfo(flow_info); - FLOW_TRACE(Trace, "Invalid reverse flow for setting ECMP index", flow_info); - return; - } - - bool local_flow = false; - if (is_flags_set(FlowEntry::LocalFlow) || - !is_flags_set(FlowEntry::IngressDir)) { - local_flow = true; - } - - if (local_flow) { - SetLocalFlowEcmpIndex(); - } else { - SetRemoteFlowEcmpIndex(); - } -} - -void FlowEntry::set_ecmp_rpf_nh(uint32_t id) { - if (data_.ecmp_rpf_nh_ == id) { - return; - } - - data_.ecmp_rpf_nh_ = id; - bool update_ksync = false; - - if (!id) { - const VrfEntry *vrf = flow_table()->agent()->vrf_table()-> - FindVrfFromId(data().flow_source_vrf); - if (vrf) { - //Flow transitioned from ECMP to non ecmp - InetUnicastRouteEntry *ip_rt = static_cast( - FlowEntry::GetUcRoute(vrf, key().src_addr)); - update_ksync = SetRpfNH(flow_table(), ip_rt); - } - } else { - update_ksync = SetEcmpRpfNH(flow_table(), id); - } - - if (ksync_entry() && update_ksync) { - flow_table()->UpdateKSync(this, true); - } -} - bool FlowEntry::SetQosConfigIndex() { uint32_t i = AgentQosConfigTable::kInvalidIndex; MatchAclParamsList::const_iterator it; @@ -2378,7 +2377,7 @@ void FlowEntry::SetAclAction(std::vector &acl_action_l) const { SetAclListAclAction(vrf_assign_acl_l, acl_action_l, acl_type); } -void FlowEntry::FillFlowInfo(FlowInfo &info) { +void FlowEntry::FillFlowInfo(FlowInfo &info) const { info.set_gen_id(gen_id_); info.set_flow_index(flow_handle_); if (key_.family == Address::INET) { @@ -2467,7 +2466,7 @@ void FlowEntry::FillFlowInfo(FlowInfo &info) { if (data_.match_p.action_info.action & (1 << TrafficAction::MIRROR)) { info.set_mirror(true); - std::vector::iterator it; + std::vector::const_iterator it; std::vector mirror_l; for (it = data_.match_p.action_info.mirror_l.begin(); it != data_.match_p.action_info.mirror_l.end(); @@ -2500,6 +2499,17 @@ void FlowEntry::FillFlowInfo(FlowInfo &info) { if (flow_table_) { info.set_table_id(flow_table_->table_index()); } + + if (rpf_nh()) { + info.set_rpf_nh(rpf_nh()->id()); + } else { + info.set_rpf_nh(0xFFFFFFFF); + } + if (src_ip_nh()) { + info.set_src_ip_nh(src_ip_nh()->id()); + } else { + info.set_src_ip_nh(0xFFFFFFFF); + } } static void SetAclListAceId(const AclDBEntry *acl, diff --git a/src/vnsw/agent/pkt/flow_entry.h b/src/vnsw/agent/pkt/flow_entry.h index 750c0bd0e9a..713a6b81249 100644 --- a/src/vnsw/agent/pkt/flow_entry.h +++ b/src/vnsw/agent/pkt/flow_entry.h @@ -274,7 +274,7 @@ struct FlowData { InterfaceConstRef intf_entry; VmFlowRef in_vm_entry; VmFlowRef out_vm_entry; - NextHopConstRef nh; + NextHopConstRef src_ip_nh; uint32_t vrf; uint32_t mirror_vrf; uint32_t dest_vrf; @@ -294,10 +294,18 @@ struct FlowData { // on route add. key for the map is vrf and data is prefix length FlowRouteRefMap flow_source_plen_map; FlowRouteRefMap flow_dest_plen_map; + + // RPF related bool enable_rpf; - uint8_t l2_rpf_plen; + // RPF NH for the flow + NextHopConstRef rpf_nh; + // When RPF is derived from a INET route, flow-management uses VRF and plen + // below to track the route for any NH change + // rpf_vrf will be VrfEntry::kInvalidIndex if flow uses l2-route for RPF + uint32_t rpf_vrf; + uint8_t rpf_plen; + std::string vm_cfg_name; - uint32_t ecmp_rpf_nh_; uint32_t acl_assigned_vrf_index_; uint32_t qos_config_idx; // IMPORTANT: Keep this structure assignable. Assignment operator is used in @@ -568,7 +576,9 @@ class FlowEntry { VmFlowRef *in_vm_flow_ref() { return &(data_.in_vm_entry); } const VmEntry *in_vm_entry() const { return data_.in_vm_entry.vm(); } const VmEntry *out_vm_entry() const { return data_.out_vm_entry.vm(); } - const NextHop *nh() const { return data_.nh.get(); } + const NextHop *src_ip_nh() const { return data_.src_ip_nh.get(); } + const NextHop *rpf_nh() const { return data_.rpf_nh.get(); } + uint32_t GetEcmpIndex() const { return data_.component_nh_idx; } const uint32_t bgp_as_a_service_port() const { if (is_flags_set(FlowEntry::BgpRouterService)) return data_.bgp_as_a_service_port; @@ -586,9 +596,12 @@ class FlowEntry { } bool deleted() { return deleted_; } - bool IsShortFlow() { return is_flags_set(FlowEntry::ShortFlow); } + bool IsShortFlow() const { return is_flags_set(FlowEntry::ShortFlow); } + bool IsEcmpFlow() const { return is_flags_set(FlowEntry::EcmpFlow); } + bool IsNatFlow() const { return is_flags_set(FlowEntry::NatFlow); } // Flow action routines void ResyncFlow(); + void RpfUpdate(); bool ActionRecompute(); bool DoPolicy(); void MakeShortFlow(FlowShortReason reason); @@ -600,7 +613,7 @@ class FlowEntry { bool add_implicit_allow, FlowPolicyInfo *info); void ResetPolicy(); - void FillFlowInfo(FlowInfo &info); + void FillFlowInfo(FlowInfo &info) const; void GetPolicyInfo(const VnEntry *vn, const FlowEntry *rflow); void GetPolicyInfo(const FlowEntry *rflow); void GetPolicyInfo(const VnEntry *vn); @@ -665,24 +678,22 @@ class FlowEntry { friend void intrusive_ptr_add_ref(FlowEntry *fe); friend void intrusive_ptr_release(FlowEntry *fe); - bool SetRpfNH(FlowTable *ft, const AgentRoute *rt); - bool SetEcmpRpfNH(FlowTable*, uint32_t); - bool SetRpfNHState(FlowTable*, const NextHop*); + + void RpfInit(const AgentRoute *rt); + void RpfSetRpfNhFields(const NextHop *rpf_nh); + void RpfSetRpfNhFields(const AgentRoute *rt, const NextHop *rpf_nh); + void RpfSetSrcIpNhFields(const AgentRoute *rt, const NextHop *src_ip_nh); + bool RpfFromSrcIpNh() const; + void RpfComputeEgress(); + void RpfComputeIngress(); + bool InitFlowCmn(const PktFlowInfo *info, const PktControlInfo *ctrl, const PktControlInfo *rev_ctrl, FlowEntry *rflow); + VmInterfaceKey InterfaceIdToKey(Agent *agent, uint32_t id); void GetSourceRouteInfo(const AgentRoute *rt); void GetDestRouteInfo(const AgentRoute *rt); - void UpdateRpf(); - VmInterfaceKey InterfaceIdToKey(Agent *agent, uint32_t id); const std::string InterfaceIdToVmCfgName(Agent *agent, uint32_t id); - void SetComponentIndex(const NextHopKey *nh_key, - uint32_t label, bool mpls_path); - const VrfEntry *GetDestinationVrf(); - void set_ecmp_rpf_nh(uint32_t id); - void UpdateEcmpInfo(); - void SetRemoteFlowEcmpIndex(); - void SetLocalFlowEcmpIndex(); - void set_ecmp_rpf_nh() const; + const VrfEntry *GetDestinationVrf() const; bool SetQosConfigIndex(); void SetSgAclInfo(const FlowPolicyInfo &fwd_flow_info, const FlowPolicyInfo &rev_flow_info, bool tcp_rev_sg); diff --git a/src/vnsw/agent/pkt/flow_mgmt.cc b/src/vnsw/agent/pkt/flow_mgmt.cc index 85e7a6d0b7e..66ebd5538d9 100644 --- a/src/vnsw/agent/pkt/flow_mgmt.cc +++ b/src/vnsw/agent/pkt/flow_mgmt.cc @@ -1458,9 +1458,9 @@ FlowMgmtEntry *InterfaceFlowMgmtTree::Allocate(const FlowMgmtKey *key) { // Nh Flow Management ///////////////////////////////////////////////////////////////////////////// void NhFlowMgmtTree::ExtractKeys(FlowEntry *flow, FlowMgmtKeyTree *tree) { - if (flow->nh() == NULL) + if (flow->rpf_nh() == NULL) return; - NhFlowMgmtKey *key = new NhFlowMgmtKey(flow->nh()); + NhFlowMgmtKey *key = new NhFlowMgmtKey(flow->rpf_nh()); AddFlowMgmtKey(tree, key); } @@ -1549,10 +1549,12 @@ void InetRouteFlowMgmtTree::ExtractKeys(FlowEntry *flow, FlowMgmtKeyTree *tree, void InetRouteFlowMgmtTree::ExtractKeys(FlowEntry *flow, FlowMgmtKeyTree *tree) { + if (flow->l3_flow() == false) { - if (flow->data().flow_source_vrf != VrfEntry::kInvalidIndex) { - ExtractKeys(flow, tree, flow->data().flow_source_vrf, - flow->key().src_addr, flow->data().l2_rpf_plen); + // For l2-flows Track INET route for RPF only + if (flow->data().rpf_vrf != VrfEntry::kInvalidIndex) { + ExtractKeys(flow, tree, flow->data().rpf_vrf, + flow->key().src_addr, flow->data().rpf_plen); } return; } diff --git a/src/vnsw/agent/pkt/flow_table.cc b/src/vnsw/agent/pkt/flow_table.cc index 20e1c3f711d..88c9923c807 100644 --- a/src/vnsw/agent/pkt/flow_table.cc +++ b/src/vnsw/agent/pkt/flow_table.cc @@ -572,7 +572,7 @@ void FlowTable::HandleRevaluateDBEntry(const DBEntry *entry, FlowEntry *flow, if (flow->vn_entry() && flow->vn_entry()->IsDeleted()) return; - if (flow->nh() && flow->nh()->IsDeleted()) + if (flow->rpf_nh() && flow->rpf_nh()->IsDeleted()) return; if (flow->intf_entry() && flow->intf_entry()->IsDeleted()) @@ -598,6 +598,11 @@ void FlowTable::HandleRevaluateDBEntry(const DBEntry *entry, FlowEntry *flow, rflow->ResyncFlow(); flow->ResyncFlow(); + // RPF computation can be done only after policy processing. + // Do RPF computation now + flow->RpfUpdate(); + rflow->RpfUpdate(); + // the SG action could potentially have changed // due to reflexive nature. Update KSync for reverse flow first UpdateKSync(rflow, true); diff --git a/src/vnsw/agent/pkt/pkt.sandesh b/src/vnsw/agent/pkt/pkt.sandesh index f9560fd1088..85d8ad686e6 100644 --- a/src/vnsw/agent/pkt/pkt.sandesh +++ b/src/vnsw/agent/pkt/pkt.sandesh @@ -108,6 +108,7 @@ struct SandeshFlowData { 43: optional string sg_rule_uuid; 44: optional string nw_ace_uuid; 45: i32 rpf_nh; + 62: i32 src_ip_nh; 46: optional string drop_reason; 47: optional string peer_vrouter; 48: optional string tunnel_type; @@ -265,6 +266,8 @@ struct FlowInfo { 43: string drop_reason; 44: u16 table_id; 46: string short_flow_reason; + 47: u32 rpf_nh; + 48: u32 src_ip_nh; } /** diff --git a/src/vnsw/agent/pkt/pkt_flow_info.cc b/src/vnsw/agent/pkt/pkt_flow_info.cc index 0e1022f9954..e0f92250abd 100644 --- a/src/vnsw/agent/pkt/pkt_flow_info.cc +++ b/src/vnsw/agent/pkt/pkt_flow_info.cc @@ -167,6 +167,55 @@ static bool IsVgwOrVmInterface(const Interface *intf) { return false; } +static bool PickEcmpMember(const NextHop **nh, const PktInfo *pkt, + PktFlowInfo *info, + const EcmpLoadBalance &ecmp_load_balance) { + // We dont support ECMP in L2 yet. Return failure to drop packet + if (pkt->l3_forwarding == false) { + info->out_component_nh_idx = CompositeNH::kInvalidComponentNHIdx; + return true; + } + + const CompositeNH *comp_nh = static_cast(*nh); + // ECMP supported only if composite-type is ECMP or LOCAL_ECMP + if (comp_nh->composite_nh_type() != Composite::ECMP && + comp_nh->composite_nh_type() != Composite::LOCAL_ECMP) { + info->out_component_nh_idx = CompositeNH::kInvalidComponentNHIdx; + return true; + } + + info->ecmp = true; + // If this is flow revluation, + // 1. If flow transitions from non-ECMP to ECMP, the old-nh will be first + // member in the composite-nh. So set affinity-nh index to 0 + // 2. If flow is already ECMP, the out-component-nh-idx is retained as + // affinity + if (pkt->type == PktType::MESSAGE && + info->out_component_nh_idx == CompositeNH::kInvalidComponentNHIdx) { + info->out_component_nh_idx = 0; + } + + // Compute out_component_nh_idx, + // 1. In case of non-ECMP to ECMP transition, component-nh-index and + // in-turn affinity-nh is set to 0 + // 2. In case of MESSAGE, the old component-nh-index is used as affinity-nh + // 3. In case of new flows, new index is allocated + // + // If affinity-nh is set but points to deleted NH, then affinity is ignored + // and new index is allocated + info->out_component_nh_idx = + comp_nh->PickMember(pkt->hash(ecmp_load_balance), + info->out_component_nh_idx); + *nh = comp_nh->GetNH(info->out_component_nh_idx); + + // TODO: Should we re-hash here? + if (!(*nh) || (*nh)->IsActive() == false) { + return false; + } + + return true; +} + // Get interface from a NH. Also, decode ECMP information from NH // Responsible to set following fields, // out->nh_ : outgoing Nexthop Index. Will also be used to set reverse flow-key @@ -184,49 +233,10 @@ static bool NhDecode(const NextHop *nh, const PktInfo *pkt, PktFlowInfo *info, if (!nh->IsActive()) return false; - // If its composite NH, find interface information from the component NH - const CompositeNH *comp_nh = NULL; - - // Out NH points to Composite. We only expect ECMP Composite-NH - // Find the out_component_nh_idx - if (nh->GetType() == NextHop::COMPOSITE) { - // We dont expect L2 multicast packets. Return failure to drop packet - if (pkt->l3_forwarding == false) - return false; - comp_nh = static_cast(nh); - if (comp_nh->composite_nh_type() == Composite::ECMP || - comp_nh->composite_nh_type() == Composite::LOCAL_ECMP) { - info->ecmp = true; - const CompositeNH *comp_nh = static_cast(nh); - - if (pkt->type == PktType::MESSAGE && - info->out_component_nh_idx == - CompositeNH::kInvalidComponentNHIdx) { - info->out_component_nh_idx = 0; - } - - // Compute out_component_nh_idx if, - // 1. out_compoenent_nh_idx was set, but points to a deleted NH - // This can happen if flow is trapped for ECMP resolution from - // vrouter - // 2. New flow being setup - // 3. If flow transitions from Non-ECMP to ECMP, the - // out_component_nh_idx is set in RewritePktInfo. We want to - // retain the index - if (info->out_component_nh_idx == - CompositeNH::kInvalidComponentNHIdx || - (comp_nh->GetNH(info->out_component_nh_idx) == NULL)) { - info->out_component_nh_idx = comp_nh->hash(pkt-> - hash(ecmp_load_balance)); - } - nh = comp_nh->GetNH(info->out_component_nh_idx); - // TODO: Should we re-hash here? - if (!nh || nh->IsActive() == false) { - return false; - } - } - } else { - info->out_component_nh_idx = CompositeNH::kInvalidComponentNHIdx; + // If nh is Composite, pick the ECMP first. The nh index and vrf used + // in reverse flow will depend on the ECMP member picked + if (PickEcmpMember(&nh, pkt, info, ecmp_load_balance) == false) { + return false; } NextHopTable *nh_table = info->agent->nexthop_table(); @@ -285,40 +295,44 @@ static bool NhDecode(const NextHop *nh, const PktInfo *pkt, PktFlowInfo *info, // have MPLS label. The MPLS label can point to // 1. In case of non-ECMP, label will points to local interface // 2. In case of ECMP, label will point to ECMP of local-composite members + // Setup the NH for reverse flow appropriately case NextHop::TUNNEL: { - if (pkt->l3_forwarding) { - const InetUnicastRouteEntry *rt = - static_cast(in->rt_); - if (rt != NULL && rt->GetLocalNextHop()) { - const NextHop *local_nh = rt->GetLocalNextHop(); - out->nh_ = local_nh->id(); - if (local_nh->GetType() == NextHop::INTERFACE) { - const Interface *local_intf = - static_cast(local_nh)->GetInterface(); - //Get policy enabled nexthop only for - //vm interface, in case of vgw or service interface in - //transparent mode we should still - //use policy disabled interface - if (local_intf && - local_intf->type() == Interface::VM_INTERFACE) { - if (local_nh->IsActive()) { - out->nh_ = local_intf->flow_key_nh()->id(); - } else { - LogError(pkt, "Invalid or Inactive ifindex"); - info->short_flow = true; - info->short_flow_reason = - FlowEntry::SHORT_UNAVIALABLE_INTERFACE; - } - } - } - } else { - out->nh_ = in->nh_; - } - } else { - // Bridged flow. ECMP not supported for L2 flows - out->nh_ = in->nh_; - } + // out->intf_ is invalid for packets going out on tunnel. Reset it. out->intf_ = NULL; + + // Packet going out on tunnel. Assume NH in reverse flow is same as + // that of forward flow. It can be over-written down if route for + // source-ip is ECMP + out->nh_ = in->nh_; + + // The NH in reverse flow can change only if ECMP-NH is used. There is + // no ECMP for layer2 flows + if (pkt->l3_forwarding == false) { + break; + } + + // If source-ip is in ECMP, reverse flow would use ECMP-NH as key + const InetUnicastRouteEntry *rt = + dynamic_cast(in->rt_); + if (rt == NULL) { + break; + } + + // Get only local-NH from route + const NextHop *local_nh = rt->GetLocalNextHop(); + if (local_nh && local_nh->IsActive() == false) { + LogError(pkt, "Invalid or Inactive local nexthop "); + info->short_flow = true; + info->short_flow_reason = FlowEntry::SHORT_UNAVIALABLE_INTERFACE; + break; + } + + // Change NH in reverse flow if route points to composite-NH + const CompositeNH *comp_nh = dynamic_cast + (local_nh); + if (comp_nh != NULL) { + out->nh_ = comp_nh->id(); + } break; } @@ -491,91 +505,6 @@ static const VnListType *RouteToVn(const AgentRoute *rt) { return &path->dest_vn_list(); } -static void SetInEcmpIndex(const PktInfo *pkt, PktFlowInfo *flow_info, - PktControlInfo *in, PktControlInfo *out) { - if (!in->rt_) { - return; - } - - if (in->rt_->GetActiveNextHop()->GetType() != NextHop::COMPOSITE) { - return; - } - - Agent *agent = flow_info->agent; - const InetUnicastRouteEntry *rt = - static_cast(in->rt_); - NextHop *component_nh_ptr = NULL; - uint32_t label; - //Frame key for component NH - if (flow_info->ingress) { - //Ingress flow - const VmInterface *vm_port = static_cast(in->intf_); - const VrfEntry *vrf = agent->vrf_table()->FindVrfFromId(pkt->vrf); - if (vm_port->HasServiceVlan() && vm_port->vrf() != vrf) { - //Packet came on service VRF - label = vm_port->GetServiceVlanLabel(vrf); - uint32_t vlan = vm_port->GetServiceVlanTag(vrf); - - const VlanNH key(const_cast(vm_port), vlan); - component_nh_ptr = static_cast - (agent->nexthop_table()->FindActiveEntryNoLock(&key)); - } else { - InterfaceNH key(const_cast(vm_port), false, - InterfaceNHFlags::INET4, - vm_port->vm_mac()); - component_nh_ptr = static_cast - (agent->nexthop_table()->FindActiveEntryNoLock(&key)); - label = vm_port->label(); - } - } else { - //Packet from fabric - Ip4Address dest_ip(pkt->tunnel.ip_saddr); - TunnelNH key(agent->fabric_vrf(), agent->router_id(), dest_ip, false, - pkt->tunnel.type); - //Get component NH pointer - component_nh_ptr = static_cast - (agent->nexthop_table()->FindActiveEntryNoLock(&key)); - //Get Label to be used to reach destination server - const CompositeNH *nh = - static_cast(rt->GetActiveNextHop()); - label = nh->GetRemoteLabel(dest_ip); - } - - const NextHop *nh = NULL; - if (out->intf_) { - //Local destination, use active path - nh = rt->GetActiveNextHop(); - } else { - //Destination on remote server - //Choose local path, which will also pointed by MPLS label - if (rt->FindPath(agent->ecmp_peer())) { - nh = rt->FindPath(agent->ecmp_peer())->ComputeNextHop(agent); - } else { - //Aggregarated routes may not have local path - //Derive local path - nh = rt->GetLocalNextHop(); - } - } - - flow_info->ecmp = true; - if (nh && nh->GetType() == NextHop::COMPOSITE) { - const CompositeNH *comp_nh = static_cast(nh); - //Find component entry index in composite NH - uint32_t idx = 0; - if (label != MplsTable::kInvalidLabel) { - ComponentNH component_nh(label, component_nh_ptr); - if (comp_nh->GetIndex(component_nh, idx)) { - flow_info->in_component_nh_idx = idx; - flow_info->ecmp = true; - } - } - } else { - //Ideally this case is not ecmp, as on reverse flow we are hitting - //a interface NH and not composite NH, install reverse flow for consistency - flow_info->ecmp = true; - } -} - bool PktFlowInfo::RouteAllowNatLookupCommon(const AgentRoute *rt, uint32_t sport, uint32_t dport, @@ -1458,10 +1387,6 @@ bool PktFlowInfo::UnknownUnicastFlow(const PktInfo *pkt, bool PktFlowInfo::Process(const PktInfo *pkt, PktControlInfo *in, PktControlInfo *out) { - if (pkt->agent_hdr.cmd == AgentHdr::TRAP_ECMP_RESOLVE) { - RewritePktInfo(pkt->agent_hdr.cmd_param); - } - in->intf_ = agent->interface_table()->FindInterface(pkt->agent_hdr.ifindex); out->nh_ = in->nh_ = pkt->agent_hdr.nh; @@ -1548,7 +1473,7 @@ bool PktFlowInfo::Process(const PktInfo *pkt, PktControlInfo *in, //to the component index if (in->rt_->GetActiveNextHop() && in->rt_->GetActiveNextHop()->GetType() == NextHop::COMPOSITE) { - SetInEcmpIndex(pkt, this, in, out); + ecmp = true; } if (out->rt_ && out->rt_->GetActiveNextHop() && @@ -1720,8 +1645,7 @@ void PktFlowInfo::Add(const PktInfo *pkt, PktControlInfo *in, } if ((pkt->type == PktType::MESSAGE && - pkt->agent_hdr.cmd == AgentHdr::TRAP_FLOW_MISS) || - pkt->agent_hdr.cmd == AgentHdr::TRAP_ECMP_RESOLVE) { + pkt->agent_hdr.cmd == AgentHdr::TRAP_FLOW_MISS)) { update = true; } @@ -1792,6 +1716,12 @@ void PktFlowInfo::Add(const PktInfo *pkt, PktControlInfo *in, rflow->ResyncFlow(); } + // RPF computation can be done only after policy processing. + // Do RPF computation now + flow->RpfUpdate(); + if (rflow) + rflow->RpfUpdate(); + /* Fip stats info in not updated in InitFwdFlow and InitRevFlow because * both forward and reverse flows are not not linked to each other yet. * We need both forward and reverse flows to update Fip stats info */ @@ -1863,51 +1793,6 @@ void PktFlowInfo::UpdateFipStatsInfo } } -//If a packet is trapped for ecmp resolve, dp might have already -//overwritten original packet(NAT case), hence get actual packet by -//overwritting packet with data in flow entry. -void PktFlowInfo::RewritePktInfo(uint32_t flow_index) { - - std::ostringstream ostr; - ostr << "ECMP Resolve for flow index " << flow_index; - PKTFLOW_TRACE(Err,ostr.str()); - KSyncFlowMemory *obj = - flow_table->agent()->ksync()->ksync_flow_memory(); - - FlowKey key; - if (!obj->GetFlowKey(flow_index, &key)) { - std::ostringstream ostr; - ostr << "ECMP Resolve: unable to find flow index " << flow_index; - PKTFLOW_TRACE(Err,ostr.str()); - return; - } - - FlowEntry *flow = flow_table->Find(key); - if (!flow) { - std::ostringstream ostr; - ostr << "ECMP Resolve: unable to find flow index " << flow_index; - PKTFLOW_TRACE(Err,ostr.str()); - return; - } - - pkt->ip_saddr = key.src_addr; - pkt->ip_daddr = key.dst_addr; - pkt->ip_proto = key.protocol; - pkt->sport = key.src_port; - pkt->dport = key.dst_port; - pkt->agent_hdr.vrf = flow->data().vrf; - pkt->vrf = flow->data().vrf; - pkt->agent_hdr.nh = key.nh; - // If component_nh_idx is not set, assume that NH transitioned from - // Non ECMP to ECMP. The old Non-ECMP NH would always be placed at index 0 - // in this case. So, set ECMP index 0 in this case - if (flow->data().component_nh_idx == CompositeNH::kInvalidComponentNHIdx) { - out_component_nh_idx = 0; - } else { - out_component_nh_idx = flow->data().component_nh_idx; - } - return; -} void PktFlowInfo::SetPktInfo(boost::shared_ptr pkt_info) { l3_flow = pkt_info->l3_forwarding; family = pkt_info->family; diff --git a/src/vnsw/agent/pkt/pkt_flow_info.h b/src/vnsw/agent/pkt/pkt_flow_info.h index 4f5db8a68a6..8795500e677 100644 --- a/src/vnsw/agent/pkt/pkt_flow_info.h +++ b/src/vnsw/agent/pkt/pkt_flow_info.h @@ -50,8 +50,8 @@ class PktFlowInfo { short_flow(false), local_flow(false), linklocal_flow(false), tcp_ack(false), linklocal_bind_local_port(false), linklocal_src_port_fd(kLinkLocalInvalidFd), - ecmp(false), in_component_nh_idx(-1), out_component_nh_idx(-1), - trap_rev_flow(false), fip_snat(false), fip_dnat(false), snat_fip(), + ecmp(false), out_component_nh_idx(-1), + fip_snat(false), fip_dnat(false), snat_fip(), short_flow_reason(0), peer_vrouter(), tunnel_type(TunnelType::INVALID), flood_unknown_unicast(false), bgp_router_service_flow(false), alias_ip_flow(false), ttl(0) { @@ -83,7 +83,6 @@ class PktFlowInfo { static bool GetIngressNwPolicyAclList(const Interface *intf, const VnEntry *vn, MatchPolicy *m_policy); - void RewritePktInfo(uint32_t index); bool VrfTranslate(const PktInfo *pkt, PktControlInfo *ctrl, PktControlInfo *rev_flow, const IpAddress &src_ip, bool nat_flow); @@ -169,9 +168,7 @@ class PktFlowInfo { int linklocal_src_port_fd; bool ecmp; - uint32_t in_component_nh_idx; uint32_t out_component_nh_idx; - bool trap_rev_flow; // Following fields are required for FIP stats accounting bool fip_snat; diff --git a/src/vnsw/agent/pkt/pkt_handler.cc b/src/vnsw/agent/pkt/pkt_handler.cc index 0e42e3fa73b..f7e5e7dfa8a 100644 --- a/src/vnsw/agent/pkt/pkt_handler.cc +++ b/src/vnsw/agent/pkt/pkt_handler.cc @@ -915,7 +915,6 @@ bool PktHandler::IsManagedTORPacket(Interface *intf, PktInfo *pkt_info, bool PktHandler::IsFlowPacket(PktInfo *pkt_info) { if (pkt_info->agent_hdr.cmd == AgentHdr::TRAP_FLOW_MISS || - pkt_info->agent_hdr.cmd == AgentHdr::TRAP_ECMP_RESOLVE || pkt_info->agent_hdr.cmd == AgentHdr::TRAP_FLOW_ACTION_HOLD) { return true; } @@ -924,7 +923,6 @@ bool PktHandler::IsFlowPacket(PktInfo *pkt_info) { bool PktHandler::IsFlowPacket(const AgentHdr &agent_hdr) { if (agent_hdr.cmd == AgentHdr::TRAP_FLOW_MISS || - agent_hdr.cmd == AgentHdr::TRAP_ECMP_RESOLVE || agent_hdr.cmd == AgentHdr::TRAP_FLOW_ACTION_HOLD) { return true; } diff --git a/src/vnsw/agent/pkt/pkt_handler.h b/src/vnsw/agent/pkt/pkt_handler.h index 1c1c9a502a0..ef43b86f68e 100644 --- a/src/vnsw/agent/pkt/pkt_handler.h +++ b/src/vnsw/agent/pkt/pkt_handler.h @@ -122,7 +122,7 @@ struct AgentHdr { TRAP_FLOW_MISS = AGENT_TRAP_FLOW_MISS, TRAP_L3_PROTOCOLS = AGENT_TRAP_L3_PROTOCOLS, TRAP_DIAG = AGENT_TRAP_DIAG, - TRAP_ECMP_RESOLVE = AGENT_TRAP_ECMP_RESOLVE, + TRAP_UNUSED_1, TRAP_SOURCE_MISMATCH = AGENT_TRAP_SOURCE_MISMATCH, TRAP_HANDLE_DF = AGENT_TRAP_HANDLE_DF, TRAP_TOR_CONTROL_PKT = AGENT_TRAP_TOR_CONTROL_PKT, diff --git a/src/vnsw/agent/pkt/pkt_sandesh_flow.cc b/src/vnsw/agent/pkt/pkt_sandesh_flow.cc index 602508d622a..d7787770bf6 100644 --- a/src/vnsw/agent/pkt/pkt_sandesh_flow.cc +++ b/src/vnsw/agent/pkt/pkt_sandesh_flow.cc @@ -88,8 +88,11 @@ using boost::system::error_code; data.set_fip_vm_interface_idx(fip_intf_id); \ SetAclInfo(data, fe); \ data.set_nh(fe->key().nh); \ - if (fe->data().nh.get() != NULL) { \ - data.set_rpf_nh(fe->data().nh.get()->id()); \ + if (fe->data().src_ip_nh.get() != NULL) { \ + data.set_src_ip_nh(fe->data().src_ip_nh.get()->id()); \ + } \ + if (fe->data().rpf_nh.get() != NULL) { \ + data.set_rpf_nh(fe->data().rpf_nh.get()->id()); \ } \ data.set_peer_vrouter(fe->peer_vrouter()); \ data.set_tunnel_type(fe->tunnel_type().ToString()); \ diff --git a/src/vnsw/agent/pkt/test/SConscript b/src/vnsw/agent/pkt/test/SConscript index 5e448a614ab..3fecb867db9 100644 --- a/src/vnsw/agent/pkt/test/SConscript +++ b/src/vnsw/agent/pkt/test/SConscript @@ -42,14 +42,13 @@ test_flow_nat = AgentEnv.MakeTestCmd(env, 'test_flow_nat', pkt_test_suite) test_flow_policy = AgentEnv.MakeTestCmd(env, 'test_flow_policy', pkt_test_suite) test_flow_update = AgentEnv.MakeTestCmd(env, 'test_flow_update', pkt_test_suite) test_pkt_flowv6 = AgentEnv.MakeTestCmd(env, 'test_pkt_flowv6', pkt_test_suite) -test_rpf_flow = AgentEnv.MakeTestCmd(env, 'test_rpf_flow', pkt_flaky_test_suite) +test_rpf_uc = AgentEnv.MakeTestCmd(env, 'test_rpf_uc', pkt_test_suite) test_pkt_parse = AgentEnv.MakeTestCmd(env, 'test_pkt_parse', pkt_flaky_test_suite) test_flowtable = AgentEnv.MakeTestCmd(env, 'test_flowtable', pkt_test_suite) test_pkt_fip = AgentEnv.MakeTestCmd(env, 'test_pkt_fip', pkt_flaky_test_suite) test_flow_native_lb = AgentEnv.MakeTestCmd(env, 'test_flow_native_lb', pkt_test_suite) test_flow_fip = AgentEnv.MakeTestCmd(env, 'test_flow_fip', pkt_test_suite) -test_ecmp = AgentEnv.MakeTestCmd(env, 'test_ecmp', pkt_flaky_test_suite) test_flow_scale = AgentEnv.MakeTestCmd(env, 'test_flow_scale', pkt_flaky_test_suite) test_flow_freelist = AgentEnv.MakeTestCmd(env, 'test_flow_freelist', pkt_test_suite) test_sg_flow = AgentEnv.MakeTestCmd(env, 'test_sg_flow', pkt_flaky_test_suite) @@ -60,12 +59,13 @@ test_vrf_assign_acl = AgentEnv.MakeTestCmd(env, 'test_vrf_assign_acl', test_vrf_assign_acl_fip = AgentEnv.MakeTestCmd(env, 'test_vrf_assign_acl_fip', pkt_test_suite) test_ecmp_mx = AgentEnv.MakeTestCmd(env, 'test_ecmp_mx', pkt_test_suite) -test_fip_dst_ecmp = AgentEnv.MakeTestCmd(env, 'test_fip_dst_ecmp', - pkt_test_suite) test_fip_src_ecmp = AgentEnv.MakeTestCmd(env, 'test_fip_src_ecmp', pkt_test_suite) -test_ecmp_local = AgentEnv.MakeTestCmd(env, 'test_ecmp_local', - pkt_test_suite) +test_ecmp_local = AgentEnv.MakeTestCmd(env, 'test_ecmp_local', pkt_test_suite) +test_ecmp_fip = AgentEnv.MakeTestCmd(env, 'test_ecmp_fip', pkt_test_suite) +test_ecmp_remote = AgentEnv.MakeTestCmd(env, 'test_ecmp_remote', pkt_test_suite) +test_ecmp_service_vlan = AgentEnv.MakeTestCmd(env, 'test_ecmp_service_vlan', + pkt_test_suite) test_bgp_service = AgentEnv.MakeTestCmd(env, 'test_bgp_service', pkt_test_suite); test_flow_qos = AgentEnv.MakeTestCmd(env, 'test_flow_qos', diff --git a/src/vnsw/agent/pkt/test/rpf-flow.xml b/src/vnsw/agent/pkt/test/rpf-flow.xml index 20215d84889..6b4c4a840f0 100644 --- a/src/vnsw/agent/pkt/test/rpf-flow.xml +++ b/src/vnsw/agent/pkt/test/rpf-flow.xml @@ -78,7 +78,7 @@ + dvn="vn1" action="deny" rpf_nh="12"/> @@ -118,7 +118,7 @@ + dvn="vn1" action="deny" rpf_nh="12"/> @@ -152,7 +152,7 @@ + dvn="vn1" action="deny" rpf_nh="12"/> @@ -166,7 +166,7 @@ + dvn="vn1" action="deny" rpf_nh="12"/> diff --git a/src/vnsw/agent/pkt/test/test_ecmp.cc b/src/vnsw/agent/pkt/test/test_ecmp.cc deleted file mode 100644 index d0847fe4149..00000000000 --- a/src/vnsw/agent/pkt/test/test_ecmp.cc +++ /dev/null @@ -1,2388 +0,0 @@ -/* - * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved. - */ - -#include "base/os.h" -#include "test/test_cmn_util.h" -#include "test_pkt_util.h" -#include "pkt/flow_proto.h" - -#define AGE_TIME 10*1000 - -void RouterIdDepInit(Agent *agent) { -} - -struct PortInfo input1[] = { - {"vnet1", 1, "1.1.1.1", "00:00:00:01:01:01", 2, 1}, -}; -//virtual IP VM -struct PortInfo input2[] = { - {"vnet2", 2, "2.1.1.1", "00:00:00:01:01:01", 2, 2}, - {"vnet3", 3, "2.1.1.1", "00:00:00:02:02:01", 2, 3}, - {"vnet4", 4, "2.1.1.1", "00:00:00:02:02:01", 2, 4}, -}; - -struct PortInfo input3[] = { - {"vnet5", 5, "3.1.1.1", "00:00:00:01:01:01", 3, 5}, - {"vnet6", 6, "3.1.1.2", "00:00:00:02:02:01", 3, 6}, - {"vnet7", 7, "3.1.1.3", "00:00:00:02:02:01", 3, 7}, -}; - -struct PortInfo input4[] = { - {"vnet8", 8, "4.1.1.1", "00:00:00:01:01:01", 4, 8}, -}; - -class EcmpTest : public ::testing::Test { - virtual void SetUp() { - agent_ = Agent::GetInstance(); - flow_proto_ = agent_->pkt()->get_flow_proto(); - // ----- - // ------------ - // ----- - CreateVmportWithEcmp(input1, 1); - CreateVmportWithEcmp(input2, 3); - CreateVmportFIpEnv(input3, 3); - CreateVmportFIpEnv(input4, 1); - client->WaitForIdle(); - for (uint32_t i = 1; i < 9; i++) { - EXPECT_TRUE(VmPortActive(i)); - } - - boost::system::error_code ec; - bgp_peer = CreateBgpPeer(Ip4Address::from_string("0.0.0.1", ec), - "xmpp channel"); - client->WaitForIdle(); - - //Add floating IP for vrf2 interface to talk to - //vrf3 - AddFloatingIpPool("fip-pool1", 1); - AddFloatingIp("fip1", 1, "3.1.1.100"); - AddLink("floating-ip", "fip1", "floating-ip-pool", "fip-pool1"); - AddLink("floating-ip-pool", "fip-pool1", "virtual-network", - "default-project:vn3"); - AddLink("virtual-machine-interface", "vnet2", "floating-ip", "fip1"); - AddLink("virtual-machine-interface", "vnet3", "floating-ip", "fip1"); - AddLink("virtual-machine-interface", "vnet4", "floating-ip", "fip1"); - client->WaitForIdle(); - Ip4Address ip = Ip4Address::from_string("3.1.1.100"); - InetUnicastRouteEntry *rt = RouteGet("default-project:vn3:vn3", ip, 32); - EXPECT_TRUE(rt != NULL); - EXPECT_TRUE(rt->GetActiveNextHop()->GetType() == NextHop::COMPOSITE); - mpls_label_1 = rt->GetActiveLabel(); - - ip = Ip4Address::from_string("2.1.1.1"); - rt = RouteGet("vrf2", ip, 32); - EXPECT_TRUE(rt != NULL); - EXPECT_TRUE(rt->GetActiveNextHop()->GetType() == NextHop::COMPOSITE); - mpls_label_2 = rt->GetActiveLabel(); - - //Add floating IP for vrf3 interfaces to talk vrf4 - AddFloatingIpPool("fip-pool2", 2); - AddFloatingIp("fip2", 2, "4.1.1.100"); - AddLink("floating-ip", "fip2", "floating-ip-pool", "fip-pool2"); - AddLink("floating-ip-pool", "fip-pool2", "virtual-network", - "default-project:vn4"); - AddLink("virtual-machine-interface", "vnet5", "floating-ip", "fip2"); - AddLink("virtual-machine-interface", "vnet6", "floating-ip", "fip2"); - AddLink("virtual-machine-interface", "vnet7", "floating-ip", "fip2"); - client->WaitForIdle(); - ip = Ip4Address::from_string("4.1.1.100"); - rt = RouteGet("default-project:vn4:vn4", ip, 32); - EXPECT_TRUE(rt != NULL); - EXPECT_TRUE(rt->GetActiveNextHop()->GetType() == NextHop::COMPOSITE); - mpls_label_3 = rt->GetActiveLabel(); - - //Populate ethernet interface id - eth_intf_id_ = EthInterfaceGet("vnet0")->id(); - - remote_vm_ip1_ = Ip4Address::from_string("2.2.2.2"); - remote_vm_ip2_ = Ip4Address::from_string("3.3.3.3"); - remote_vm_ip3_ = Ip4Address::from_string("4.4.4.4"); - remote_server_ip_ = Ip4Address::from_string("10.10.1.1"); - - //Add couple of remote VM routes for generating packet - Inet4TunnelRouteAdd(bgp_peer, "vrf2", remote_vm_ip1_, 32, - remote_server_ip_, TunnelType::GREType(), - 30, "vn2", SecurityGroupList(), - PathPreference()); - - Inet4TunnelRouteAdd(bgp_peer, "default-project:vn3:vn3", - remote_vm_ip2_, 32, - remote_server_ip_, TunnelType::GREType(), - 30, "default-project:vn3", SecurityGroupList(), - PathPreference()); - - Inet4TunnelRouteAdd(bgp_peer, "default-project:vn4:vn4", - remote_vm_ip3_, 32, - remote_server_ip_, TunnelType::GREType(), - 30, "default-project:vn4", SecurityGroupList(), - PathPreference()); - client->WaitForIdle(); - } - - virtual void TearDown() { - DelLink("virtual-machine-interface", "vnet2", "floating-ip", "fip1"); - DelLink("virtual-machine-interface", "vnet3", "floating-ip", "fip1"); - DelLink("virtual-machine-interface", "vnet4", "floating-ip", "fip1"); - DelLink("floating-ip-pool", "fip-pool1", "virtual-network", - "default-project:vn3"); - client->WaitForIdle(); - - DelLink("virtual-machine-interface", "vnet5", "floating-ip", "fip2"); - DelLink("virtual-machine-interface", "vnet6", "floating-ip", "fip2"); - DelLink("virtual-machine-interface", "vnet7", "floating-ip", "fip2"); - DelLink("floating-ip-pool", "fip-pool2", "virtual-network", - "default-project:vn4"); - client->WaitForIdle(); - DeleteVmportEnv(input1, 1, false); - DeleteVmportEnv(input2, 3, true); - DeleteVmportFIpEnv(input3, 3, true); - DeleteVmportFIpEnv(input4, 1, true); - Agent::GetInstance()->fabric_inet4_unicast_table()->DeleteReq(bgp_peer, - "vrf2", remote_vm_ip1_, 32, new ControllerVmRoute(bgp_peer)); - Agent::GetInstance()->fabric_inet4_unicast_table()->DeleteReq(bgp_peer, - "default-project:vn3:vn3", remote_vm_ip2_, 32, - new ControllerVmRoute(bgp_peer)); - Agent::GetInstance()->fabric_inet4_unicast_table()->DeleteReq(bgp_peer, - "default-project:vn4:vn4", remote_vm_ip3_, 32, - new ControllerVmRoute(bgp_peer)); - - client->WaitForIdle(); - DeleteBgpPeer(bgp_peer); - EXPECT_FALSE(VrfFind("vrf1", true)); - EXPECT_FALSE(VrfFind("vrf2", true)); - EXPECT_FALSE(VrfFind("default-project:vn3:vn3", true)); - EXPECT_FALSE(VrfFind("default-project:vn4:vn4", true)); - } - -public: - FlowProto *get_flow_proto() const { return flow_proto_; } - uint32_t GetServiceVlanNH(uint32_t intf_id, std::string vrf_name) const { - const VmInterface *vm_intf = VmInterfaceGet(intf_id); - const VrfEntry *vrf = VrfGet(vrf_name.c_str()); - uint32_t label = vm_intf->GetServiceVlanLabel(vrf); - int nh_id = Agent::GetInstance()->mpls_table()-> - FindMplsLabel(label)->nexthop()->id(); - return nh_id; - } - - void AddRemoteEcmpRoute(const string vrf_name, const string ip, - uint32_t plen, const string vn, int count, - ComponentNHKeyList local_list, - bool same_label = false) { - //If there is a local route, include that always - Ip4Address vm_ip = Ip4Address::from_string(ip); - - ComponentNHKeyList comp_nh_list = local_list; - - int remote_server_ip = 0x0A0A0A0A; - int label = 16; - SecurityGroupList sg_id_list; - - for(int i = 0; i < count; i++) { - ComponentNHKeyPtr comp_nh(new ComponentNHKey( - label, Agent::GetInstance()->fabric_vrf_name(), - Agent::GetInstance()->router_id(), - Ip4Address(remote_server_ip++), - false, TunnelType::GREType())); - comp_nh_list.push_back(comp_nh); - if (!same_label) { - label++; - } - } - EcmpTunnelRouteAdd(bgp_peer, vrf_name, vm_ip, plen, - comp_nh_list, -1, vn, sg_id_list, - PathPreference()); - } - - void AddLocalVmRoute(const string vrf_name, const string ip, uint32_t plen, - const string vn, uint32_t intf_uuid) { - const VmInterface *vm_intf = static_cast - (VmPortGet(intf_uuid)); - VmInterfaceKey intf_key(AgentKey::ADD_DEL_CHANGE, MakeUuid(intf_uuid), ""); - VnListType vn_list; - vn_list.insert(vn); - LocalVmRoute *local_vm_route = - new LocalVmRoute(intf_key, vm_intf->label(), - VxLanTable::kInvalidvxlan_id, false, vn_list, - InterfaceNHFlags::INET4, SecurityGroupList(), - CommunityList(), - PathPreference(), Ip4Address(0), - EcmpLoadBalance(), false, false, - bgp_peer->sequence_number(), - false); - InetUnicastAgentRouteTable *rt_table = - agent_->vrf_table()->GetInet4UnicastRouteTable(vrf_name); - - rt_table->AddLocalVmRouteReq(bgp_peer, vrf_name, - Ip4Address::from_string(ip), plen, - static_cast(local_vm_route)); - } - - void AddRemoteVmRoute(const string vrf_name, const string ip, uint32_t plen, - const string vn) { - const Ip4Address addr = Ip4Address::from_string(ip); - VnListType vn_list; - vn_list.insert(vn); - ControllerVmRoute *data = - ControllerVmRoute::MakeControllerVmRoute(bgp_peer, - agent_->fabric_vrf_name(), agent_->router_id(), - vrf_name, addr, TunnelType::GREType(), 16, - vn_list, SecurityGroupList(), - PathPreference(), false, EcmpLoadBalance(), - false); - InetUnicastAgentRouteTable::AddRemoteVmRouteReq(bgp_peer, - vrf_name, addr, plen, data); - } - - void DeleteRemoteRoute(const string vrf_name, const string ip, - uint32_t plen) { - Ip4Address server_ip = Ip4Address::from_string(ip); - agent_->fabric_inet4_unicast_table()->DeleteReq(bgp_peer, - vrf_name, server_ip, plen, new ControllerVmRoute(bgp_peer)); - } - - uint32_t eth_intf_id_; - Ip4Address remote_vm_ip1_; - Ip4Address remote_vm_ip2_; - Ip4Address remote_vm_ip3_; - Ip4Address remote_server_ip_; - uint32_t mpls_label_1; - uint32_t mpls_label_2; - uint32_t mpls_label_3; - Peer *bgp_peer; - Agent *agent_; - FlowProto *flow_proto_; - AgentXmppChannel *channel; -}; - -//Ping from vrf1 to vrf2(which has ECMP vip) -TEST_F(EcmpTest, EcmpTest_1) { - TxIpPacket(VmPortGetId(1), "1.1.1.1", "2.1.1.1", 1); - client->WaitForIdle(); - - FlowEntry *entry = FlowGet(VrfGet("vrf2")->vrf_id(), - "1.1.1.1", "2.1.1.1", 1, 0, 0, GetFlowKeyNH(1)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - - //Reverse flow is no ECMP - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); -} - -//Ping from vrf2(vip and floating IP ECMP) to vrf3 -TEST_F(EcmpTest, EcmpTest_2) { - TxIpPacket(VmPortGetId(4), "2.1.1.1", "3.1.1.1", 1); - client->WaitForIdle(); - FlowEntry *entry = FlowGet(VrfGet("vrf2")->vrf_id(), - "2.1.1.1", "3.1.1.1", 1, 0, 0, GetFlowKeyNH(4)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->is_flags_set(FlowEntry::NatFlow) == true); - - //Reverse flow should be set and should also be ECMP - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - - //Make sure reverse component index point to right interface - Ip4Address ip = Ip4Address::from_string("2.1.1.1"); - const CompositeNH *comp_nh = static_cast - (RouteGet("vrf2", ip, 32)->GetActiveNextHop()); - const InterfaceNH *intf_nh = static_cast - (comp_nh->Get(rev_entry->data().component_nh_idx)->nh()); - EXPECT_TRUE(intf_nh->GetInterface()->name() == "vnet4"); -} - -//Ping from vrf3(floating IP is ECMP) to vrf4 -TEST_F(EcmpTest, EcmpTest_3) { - TxIpPacket(VmPortGetId(5), "3.1.1.1", "4.1.1.1", 1); - client->WaitForIdle(); - - FlowEntry *entry = FlowGet(VrfGet("default-project:vn3:vn3")->vrf_id(), - "3.1.1.1", "4.1.1.1", 1, 0, 0, GetFlowKeyNH(5)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->is_flags_set(FlowEntry::NatFlow) == true); - - //Reverse flow should be set and should also be ECMP - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - //Make sure reverse component index point to right interface - Ip4Address ip = Ip4Address::from_string("4.1.1.100"); - const CompositeNH *comp_nh = static_cast - (RouteGet("default-project:vn4:vn4", ip, 32)->GetActiveNextHop()); - const InterfaceNH *intf_nh = static_cast - (comp_nh->Get(rev_entry->data().component_nh_idx)->nh()); - EXPECT_TRUE(intf_nh->GetInterface()->name() == "vnet5"); -} - -//Ping from vrf3(floating IP is ECMP) to vrf4 -TEST_F(EcmpTest, EcmpTest_7) { - TxIpPacket(VmPortGetId(6), "3.1.1.2", "4.1.1.1", 1); - client->WaitForIdle(); - - FlowEntry *entry = FlowGet(VrfGet("default-project:vn3:vn3")->vrf_id(), - "3.1.1.2", "4.1.1.1", 1, 0, 0, GetFlowKeyNH(6)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->is_flags_set(FlowEntry::NatFlow) == true); - - //Reverse flow should be set and should also be ECMP - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - //Make sure reverse component index point to right interface - Ip4Address ip = Ip4Address::from_string("4.1.1.100"); - const CompositeNH *comp_nh = static_cast - (RouteGet("default-project:vn4:vn4", ip, 32)->GetActiveNextHop()); - const InterfaceNH *intf_nh = static_cast - (comp_nh->Get(rev_entry->data().component_nh_idx)->nh()); - EXPECT_TRUE(intf_nh->GetInterface()->name() == "vnet6"); -} - -//Ping from external world to ECMP vip -TEST_F(EcmpTest, EcmpTest_4) { - //VIP of vrf2 interfaces - char vm_ip[80] = "2.1.1.1"; - char router_id[80]; - char remote_server_ip[80]; - char remote_vm_ip[80]; - - strcpy(router_id, Agent::GetInstance()->router_id().to_string().c_str()); - strcpy(remote_server_ip, remote_server_ip_.to_string().c_str()); - strcpy(remote_vm_ip, remote_vm_ip1_.to_string().c_str()); - - TxIpMplsPacket(eth_intf_id_, remote_server_ip, router_id, mpls_label_2, - remote_vm_ip, vm_ip, 1, 10); - - client->WaitForIdle(); - int nh_id = GetActiveLabel(MplsLabel::VPORT_NH, mpls_label_2)->nexthop()->id(); - FlowEntry *entry = FlowGet(VrfGet("vrf2")->vrf_id(), - remote_vm_ip, vm_ip, 1, 0, 0, nh_id); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - - //Reverse flow should be set and should also be ECMP - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); -} - -//Ping from external world to ECMP fip -TEST_F(EcmpTest, EcmpTest_5) { - //FIP of vrf3 interfaces - char vm_ip[80] = "4.1.1.100"; - char router_id[80]; - char remote_server_ip[80]; - char remote_vm_ip[80]; - - strcpy(router_id, Agent::GetInstance()->router_id().to_string().c_str()); - strcpy(remote_server_ip, remote_server_ip_.to_string().c_str()); - strcpy(remote_vm_ip, remote_vm_ip3_.to_string().c_str()); - - TxIpMplsPacket(eth_intf_id_, remote_server_ip, router_id, mpls_label_3, - remote_vm_ip, vm_ip, 1, 10); - client->WaitForIdle(); - - int nh_id = GetActiveLabel(MplsLabel::VPORT_NH, mpls_label_3)->nexthop()->id(); - FlowEntry *entry = FlowGet(VrfGet("default-project:vn4:vn4")->vrf_id(), - remote_vm_ip, vm_ip, 1, 0, 0, nh_id); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->is_flags_set(FlowEntry::NatFlow) == true); - - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); -} - -//Ping from vrf3(floating IP is ECMP) to vrf4 -TEST_F(EcmpTest, EcmpTest_6) { - TxIpPacket(VmPortGetId(8), "4.1.1.1", "4.1.1.100", 1); - client->WaitForIdle(); - - FlowEntry *entry = FlowGet(VrfGet("default-project:vn4:vn4")->vrf_id(), - "4.1.1.1", "4.1.1.100", 1, 0, 0, - GetFlowKeyNH(8)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->is_flags_set(FlowEntry::NatFlow) == true); - - //Reverse flow should be set and should also be ECMP - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); -} - -TEST_F(EcmpTest, EcmpTest_8) { - Ip4Address ip = Ip4Address::from_string("30.30.30.0"); - ComponentNHKeyList comp_nh; - Ip4Address server_ip1 = Ip4Address::from_string("15.15.15.15"); - Ip4Address server_ip2 = Ip4Address::from_string("15.15.15.16"); - Ip4Address server_ip3 = Ip4Address::from_string("15.15.15.17"); - - ComponentNHKeyPtr comp_nh_data1(new ComponentNHKey( - 16, Agent::GetInstance()->fabric_vrf_name(), - Agent::GetInstance()->router_id(), server_ip1, false, - TunnelType::GREType())); - comp_nh.push_back(comp_nh_data1); - - ComponentNHKeyPtr comp_nh_data2(new ComponentNHKey( - 17, Agent::GetInstance()->fabric_vrf_name(), - Agent::GetInstance()->router_id(), - server_ip2, false, TunnelType::GREType())); - comp_nh.push_back(comp_nh_data2); - - ComponentNHKeyPtr comp_nh_data3(new ComponentNHKey( - 18, Agent::GetInstance()->fabric_vrf_name(), - Agent::GetInstance()->router_id(), - server_ip3, false, TunnelType::GREType())); - comp_nh.push_back(comp_nh_data3); - - SecurityGroupList sg_list; - EcmpTunnelRouteAdd(bgp_peer, "vrf2", ip, 24, comp_nh, -1, "vn2", sg_list, - PathPreference()); - client->WaitForIdle(); - - //VIP of vrf2 interfaces - char vm_ip[80] = "1.1.1.1"; - char router_id[80]; - char remote_server_ip[80]; - char remote_vm_ip[80]; - - strcpy(router_id, Agent::GetInstance()->router_id().to_string().c_str()); - strcpy(remote_server_ip, "15.15.15.16"); - strcpy(remote_vm_ip, "30.30.30.1"); - - const VmInterface *vintf = - static_cast(VmPortGet(1)); - TxIpMplsPacket(eth_intf_id_, remote_server_ip, router_id, vintf->label(), - remote_vm_ip, vm_ip, 1, 10); - - client->WaitForIdle(); - int nh_id = GetActiveLabel(MplsLabel::VPORT_NH, vintf->label())-> - nexthop()->id(); - FlowEntry *entry = FlowGet(VrfGet("vrf2")->vrf_id(), - remote_vm_ip, vm_ip, 1, 0, 0, nh_id); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - - //Reverse flow should be set and should also be ECMP - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(rev_entry->data().component_nh_idx == 1); - Agent::GetInstance()->fabric_inet4_unicast_table()->DeleteReq(bgp_peer, - "vrf2", ip, 24, - new ControllerVmRoute(bgp_peer)); - client->WaitForIdle(); -} - -//Ping from a interface with ECMP FIP to remote machine -TEST_F(EcmpTest, EcmpTest_9) { - char remote_vm_ip[80]; - - strcpy(remote_vm_ip, remote_vm_ip3_.to_string().c_str()); - TxIpPacket(VmPortGetId(5), "3.1.1.1", remote_vm_ip, 1); - client->WaitForIdle(); - - FlowEntry *entry = FlowGet(VrfGet("default-project:vn3:vn3")->vrf_id(), - "3.1.1.1", remote_vm_ip, 1, 0, 0, - GetFlowKeyNH(5)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->is_flags_set(FlowEntry::NatFlow) == true); - EXPECT_TRUE(entry->data().vrf == - VrfGet("default-project:vn3:vn3")->vrf_id()); - EXPECT_TRUE(entry->data().dest_vrf == - VrfGet("default-project:vn4:vn4")->vrf_id()); - std::string vn_name("default-project:vn4"); - EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name)); - EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name)); - - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(rev_entry->data().vrf == VrfGet("default-project:vn4:vn4")->vrf_id()); - EXPECT_TRUE(rev_entry->data().dest_vrf == VrfGet("default-project:vn3:vn3")->vrf_id()); - EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name)); - EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name)); -} - -//Ping from vip to ECMP VIP with ingress vrf and egress VRF having -//different order of component NH -TEST_F(EcmpTest, EcmpTest_10) { - struct PortInfo input1[] = { - {"vnet9", 9, "9.1.1.1", "00:00:00:01:01:01", 9, 9}, - }; - CreateVmportWithEcmp(input1, 1); - client->WaitForIdle(); - - //Leak route for 2.1.1.1 in vrf9 and - Ip4Address vm_ip = Ip4Address::from_string("2.1.1.1"); - InetUnicastRouteEntry *rt = RouteGet("vrf2", vm_ip, 32); - EXPECT_TRUE(rt != NULL); - - const CompositeNH *composite_nh = static_cast( - rt->GetActiveNextHop()); - ComponentNHKeyList comp_nh_list = composite_nh->component_nh_key_list(); - std::reverse(comp_nh_list.begin(), comp_nh_list.end()); - AddRemoteEcmpRoute("vrf9", "2.1.1.1", 32, "vn2", 0, comp_nh_list); - AddLocalVmRoute("vrf2", "9.1.1.1", 32, "vn9", 9); - client->WaitForIdle(); - - rt = RouteGet("vrf9", vm_ip, 32); - EXPECT_TRUE(rt != NULL); - composite_nh = static_cast( - rt->GetActiveNextHop()); - TxIpPacket(VmPortGetId(2), "2.1.1.1", "9.1.1.1", 1); - client->WaitForIdle(); - - FlowEntry *entry = FlowGet(VrfGet("vrf2")->vrf_id(), - "2.1.1.1", "9.1.1.1", 1, 0, 0, GetFlowKeyNH(2)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->is_flags_set(FlowEntry::ShortFlow) == false); - - //Reverse flow is no ECMP - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - //Make sure reverse flow packet is destined to vnet2 - const InterfaceNH *nh = static_cast( - composite_nh->GetNH(rev_entry->data().component_nh_idx)); - EXPECT_TRUE(nh->GetInterface()->name() == "vnet2"); - EXPECT_TRUE(rev_entry->is_flags_set(FlowEntry::ShortFlow) == false); - - TxIpPacket(VmPortGetId(3), "2.1.1.1", "9.1.1.1", 1); - client->WaitForIdle(); - entry = FlowGet(VrfGet("vrf2")->vrf_id(), - "2.1.1.1", "9.1.1.1", 1, 0, 0, GetFlowKeyNH(3)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->is_flags_set(FlowEntry::ShortFlow) == false); - - //Reverse flow is no ECMP - rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - //Make sure reverse flow packet is destined to vnet3 - nh = static_cast( - composite_nh->GetNH(rev_entry->data().component_nh_idx)); - EXPECT_TRUE(nh->GetInterface()->name() == "vnet3"); - EXPECT_TRUE(rev_entry->is_flags_set(FlowEntry::ShortFlow) == false); - - TxIpPacket(VmPortGetId(4), "2.1.1.1", "9.1.1.1", 1); - client->WaitForIdle(); - entry = FlowGet(VrfGet("vrf2")->vrf_id(), - "2.1.1.1", "9.1.1.1", 1, 0, 0, GetFlowKeyNH(4)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->is_flags_set(FlowEntry::ShortFlow) == false); - - //Reverse flow is no ECMP - rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - //Make sure reverse flow packet is destined to vnet4 - nh = static_cast( - composite_nh->GetNH(rev_entry->data().component_nh_idx)); - EXPECT_TRUE(nh->GetInterface()->name() == "vnet4"); - EXPECT_TRUE(rev_entry->is_flags_set(FlowEntry::ShortFlow) == false); - - DeleteVmportEnv(input1, 1, true); - DeleteRemoteRoute("vrf2", "9.1.1.1", 32); - client->WaitForIdle(); - WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); - EXPECT_FALSE(VrfFind("vrf9")); -} - -//Ping from vip to ECMP FIP with ingress vrf and egress VRF having -//different order of component NH -TEST_F(EcmpTest, EcmpTest_11) { - //Add service VRF and VN - struct PortInfo input1[] = { - {"vnet9", 9, "9.1.1.1", "00:00:00:01:01:01", 9, 9}, - }; - CreateVmportWithEcmp(input1, 1); - client->WaitForIdle(); - - //Leak route for 2.1.1.1 in vrf9 and - Ip4Address vm_ip = Ip4Address::from_string("3.1.1.100"); - InetUnicastRouteEntry *rt = RouteGet("default-project:vn3:vn3", vm_ip, 32); - EXPECT_TRUE(rt != NULL); - - const CompositeNH *composite_nh = static_cast( - rt->GetActiveNextHop()); - ComponentNHKeyList comp_nh_list = composite_nh->component_nh_key_list(); - std::reverse(comp_nh_list.begin(), comp_nh_list.end()); - AddRemoteEcmpRoute("vrf9", "3.1.1.100", 32, "default-project:vn3", 0, - comp_nh_list); - AddLocalVmRoute("default-project:vn3:vn3", "9.1.1.1", 32, "vn9", 9); - client->WaitForIdle(); - - Ip4Address vm_src_ip = Ip4Address::from_string("2.1.1.1"); - rt = RouteGet("vrf2", vm_src_ip, 32); - EXPECT_TRUE(rt != NULL); - composite_nh = static_cast( - rt->GetActiveNextHop()); - - TxIpPacket(VmPortGetId(2), "2.1.1.1", "9.1.1.1", 1); - client->WaitForIdle(); - - FlowEntry *entry = FlowGet(VrfGet("vrf2")->vrf_id(), - "2.1.1.1", "9.1.1.1", 1, 0, 0, GetFlowKeyNH(2)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->is_flags_set(FlowEntry::ShortFlow) == false); - - //Reverse flow is no ECMP - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - //Make sure reverse flow packet is destined to vnet2 - const InterfaceNH *nh = static_cast( - composite_nh->GetNH(rev_entry->data().component_nh_idx)); - EXPECT_TRUE(nh->GetInterface()->name() == "vnet2"); - EXPECT_TRUE(rev_entry->is_flags_set(FlowEntry::ShortFlow) == false); - - TxIpPacket(VmPortGetId(3), "2.1.1.1", "9.1.1.1", 1); - client->WaitForIdle(); - entry = FlowGet(VrfGet("vrf2")->vrf_id(), - "2.1.1.1", "9.1.1.1", 1, 0, 0, GetFlowKeyNH(3)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->is_flags_set(FlowEntry::ShortFlow) == false); - - //Reverse flow is no ECMP - rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - //Make sure reverse flow packet is destined to vnet3 - nh = static_cast( - composite_nh->GetNH(rev_entry->data().component_nh_idx)); - EXPECT_TRUE(nh->GetInterface()->name() == "vnet3"); - EXPECT_TRUE(rev_entry->is_flags_set(FlowEntry::ShortFlow) == false); - - TxIpPacket(VmPortGetId(4), "2.1.1.1", "9.1.1.1", 1); - client->WaitForIdle(); - entry = FlowGet(VrfGet("vrf2")->vrf_id(), - "2.1.1.1", "9.1.1.1", 1, 0, 0, GetFlowKeyNH(4)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->is_flags_set(FlowEntry::ShortFlow) == false); - - //Reverse flow is no ECMP - rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - //Make sure reverse flow packet is destined to vnet4 - nh = static_cast( - composite_nh->GetNH(rev_entry->data().component_nh_idx)); - EXPECT_TRUE(nh->GetInterface()->name() == "vnet4"); - EXPECT_TRUE(rev_entry->is_flags_set(FlowEntry::ShortFlow) == false); - - DeleteVmportEnv(input1, 1, true); - DeleteRemoteRoute("default-project:vn3:vn3", "9.1.1.1", 32); - client->WaitForIdle(); - WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); - EXPECT_FALSE(VrfFind("vrf9")); -} - -TEST_F(EcmpTest, EcmpTest_12) { - struct PortInfo input1[] = { - {"vnet9", 9, "9.1.1.1", "00:00:00:01:01:01", 9, 9}, - }; - CreateVmportWithEcmp(input1, 1); - client->WaitForIdle(); - - VmInterface *intf = static_cast(VmPortGet(9)); - uint32_t label = intf->label(); - - AddVn("default-project:vn10", 10); - AddVrf("default-project:vn10:vn10", 10); - AddLink("virtual-network", "default-project:vn10", - "routing-instance", "default-project:vn10:vn10"); - AddFloatingIpPool("fip-pool9", 10); - AddFloatingIp("fip9", 10, "10.10.10.2"); - AddLink("floating-ip", "fip9", "floating-ip-pool", "fip-pool9"); - AddLink("floating-ip-pool", "fip-pool9", "virtual-network", - "default-project:vn10"); - AddLink("virtual-machine-interface", "vnet9", "floating-ip", "fip9"); - client->WaitForIdle(); - - Ip4Address gw_rt = Ip4Address::from_string("0.0.0.0"); - Ip4Address remote_server_ip1 = Ip4Address::from_string("10.10.10.100"); - Ip4Address remote_server_ip2 = Ip4Address::from_string("10.10.10.101"); - Agent *agent = Agent::GetInstance(); - ComponentNHKeyPtr nh_data1(new ComponentNHKey(30, agent->fabric_vrf_name(), - agent->router_id(), - remote_server_ip1, - false, - TunnelType::DefaultType())); - ComponentNHKeyPtr nh_data2(new ComponentNHKey(20, agent->fabric_vrf_name(), - agent->router_id(), - remote_server_ip2, - false, - TunnelType::DefaultType())); - ComponentNHKeyList comp_nh_list; - comp_nh_list.push_back(nh_data1); - comp_nh_list.push_back(nh_data2); - EcmpTunnelRouteAdd(bgp_peer, "default-project:vn10:vn10", gw_rt, 0, - comp_nh_list, -1, "default-project:vn10", - SecurityGroupList(), PathPreference()); - client->WaitForIdle(); - - TxIpPacket(VmPortGetId(9), "9.1.1.1", "10.1.1.1", 1); - client->WaitForIdle(); - FlowEntry *entry; - entry = FlowGet(VrfGet("default-project:vn10:vn10")->vrf_id(), - "9.1.1.1", "10.1.1.1", 1, 0, 0, GetFlowKeyNH(9)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->is_flags_set(FlowEntry::ShortFlow) == false); - - uint32_t reverse_index = entry->reverse_flow_entry()->flow_handle(); - if (entry->data().component_nh_idx == 0) { - TxIpMplsPacket(eth_intf_id_, "10.10.10.101", - agent->router_id().to_string().c_str(), - label, "10.1.1.1", "10.10.10.2", 1, reverse_index); - client->WaitForIdle(); - EXPECT_TRUE(entry->data().component_nh_idx == 1); - } else { - TxIpMplsPacket(eth_intf_id_, "10.10.10.100", - agent->router_id().to_string().c_str(), - label, "10.1.1.1", "10.10.10.2", 1, reverse_index); - client->WaitForIdle(); - EXPECT_TRUE(entry->data().component_nh_idx == 0); - } - - DeleteVmportEnv(input1, 1, true); - DeleteRemoteRoute("default-project:vn10:vn10", "0.0.0.0", 0); - DelLink("floating-ip", "fip9", "floating-ip-pool", "fip-pool9"); - DelLink("floating-ip-pool", "fip-pool9", - "virtual-network", "default-project:vn10"); - DelLink("virtual-machine-interface", "vnet9", "floating-ip", "fip9"); - DelFloatingIp("fip9"); - DelFloatingIpPool("fip-pool9"); - client->WaitForIdle(); - DelVrf("default-project:vn10:vn10"); - DelVn("default-project:vn10"); - client->WaitForIdle(); - WAIT_FOR(100, 1000, VrfFind("default-project:vn10:vn10", true) == false); - client->WaitForIdle(); - EXPECT_TRUE(get_flow_proto()->FlowCount() == 0); - EXPECT_FALSE(VrfFind("vrf9", true)); -} - -//Add a test case to check if rpf NH of flow using floating IP -//is set properly upon nexthop change of from 2 destination to 3 -//destinations -TEST_F(EcmpTest, EcmpTest_13) { - struct PortInfo input1[] = { - {"vnet9", 9, "9.1.1.1", "00:00:00:01:01:01", 9, 9}, - }; - CreateVmportWithEcmp(input1, 1); - client->WaitForIdle(); - - VmInterface *intf = static_cast(VmPortGet(9)); - uint32_t label = intf->label(); - - AddVn("default-project:vn10", 10); - AddVrf("default-project:vn10:vn10", 10); - AddLink("virtual-network", "default-project:vn10", - "routing-instance", "default-project:vn10:vn10"); - AddFloatingIpPool("fip-pool9", 10); - AddFloatingIp("fip9", 10, "10.10.10.2"); - AddLink("floating-ip", "fip9", "floating-ip-pool", "fip-pool9"); - AddLink("floating-ip-pool", "fip-pool9", "virtual-network", - "default-project:vn10"); - AddLink("virtual-machine-interface", "vnet9", "floating-ip", "fip9"); - client->WaitForIdle(); - - Ip4Address gw_rt = Ip4Address::from_string("0.0.0.0"); - Ip4Address remote_server_ip1 = Ip4Address::from_string("10.10.10.100"); - Ip4Address remote_server_ip2 = Ip4Address::from_string("10.10.10.101"); - Ip4Address remote_server_ip3 = Ip4Address::from_string("10.10.10.102"); - Agent *agent = Agent::GetInstance(); - ComponentNHKeyPtr nh_data1(new ComponentNHKey(30, agent->fabric_vrf_name(), - agent->router_id(), - remote_server_ip1, - false, - TunnelType::DefaultType())); - ComponentNHKeyPtr nh_data2(new ComponentNHKey(20, agent->fabric_vrf_name(), - agent->router_id(), - remote_server_ip2, - false, - TunnelType::DefaultType())); - ComponentNHKeyList comp_nh_list; - comp_nh_list.push_back(nh_data1); - comp_nh_list.push_back(nh_data2); - EcmpTunnelRouteAdd(bgp_peer, "default-project:vn10:vn10", gw_rt, 0, - comp_nh_list, false, "default-project:vn10", - SecurityGroupList(), PathPreference()); - client->WaitForIdle(); - - TxIpPacket(VmPortGetId(9), "9.1.1.1", "10.1.1.1", 1); - client->WaitForIdle(); - FlowEntry *entry; - entry = FlowGet(VrfGet("default-project:vn10:vn10")->vrf_id(), - "9.1.1.1", "10.1.1.1", 1, 0, 0, GetFlowKeyNH(9)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->is_flags_set(FlowEntry::ShortFlow) == false); - - uint32_t index; - uint32_t reverse_index = entry->reverse_flow_entry()->flow_handle(); - if (entry->data().component_nh_idx == 0) { - TxIpMplsPacket(eth_intf_id_, "10.10.10.101", - agent->router_id().to_string().c_str(), - label, "10.1.1.1", "10.10.10.2", 1, reverse_index); - client->WaitForIdle(); - EXPECT_TRUE(entry->data().component_nh_idx == 1); - index = 1; - } else { - TxIpMplsPacket(eth_intf_id_, "10.10.10.100", - agent->router_id().to_string().c_str(), - label, "10.1.1.1", "10.10.10.2", 1, reverse_index); - client->WaitForIdle(); - EXPECT_TRUE(entry->data().component_nh_idx == 0); - index = 0; - } - - //Update the route to make the composite NH point to new nexthop - ComponentNHKeyPtr nh_data3(new ComponentNHKey(20, agent->fabric_vrf_name(), - agent->router_id(), - remote_server_ip3, - false, - TunnelType::DefaultType())); - comp_nh_list.push_back(nh_data3); - EcmpTunnelRouteAdd(bgp_peer, "default-project:vn10:vn10", gw_rt, 0, - comp_nh_list, false, "default-project:vn10", - SecurityGroupList(), PathPreference()); - client->WaitForIdle(); - EXPECT_TRUE(entry->data().component_nh_idx == index); - //Make sure flow has the right nexthop set. - InetUnicastRouteEntry *rt = RouteGet("default-project:vn10:vn10", gw_rt, 0); - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->nh() == rt->GetActiveNextHop()); - - DeleteVmportEnv(input1, 1, true); - DeleteRemoteRoute("default-project:vn10:vn10", "0.0.0.0", 0); - DelLink("floating-ip", "fip9", "floating-ip-pool", "fip-pool9"); - DelLink("floating-ip-pool", "fip-pool9", - "virtual-network", "default-project:vn10"); - DelLink("virtual-machine-interface", "vnet9", "floating-ip", "fip9"); - DelFloatingIp("fip9"); - DelFloatingIpPool("fip-pool9"); - client->WaitForIdle(); - DelVrf("default-project:vn10:vn10"); - DelVn("default-project:vn10"); - client->WaitForIdle(); - WAIT_FOR(100, 1000, VrfFind("default-project:vn10:vn10", true) == false); - client->WaitForIdle(); - EXPECT_TRUE(get_flow_proto()->FlowCount() == 0); - EXPECT_FALSE(VrfFind("vrf9", true)); -} - -//Add a test case to check if rpf NH of flow using floating IP -//gets properly upon nexthop change from ecmp to unicast -TEST_F(EcmpTest, EcmpTest_14) { - struct PortInfo input1[] = { - {"vnet9", 9, "9.1.1.1", "00:00:00:01:01:01", 9, 9}, - }; - CreateVmportWithEcmp(input1, 1); - client->WaitForIdle(); - - AddVn("default-project:vn10", 10); - AddVrf("default-project:vn10:vn10", 10); - AddLink("virtual-network", "default-project:vn10", - "routing-instance", "default-project:vn10:vn10"); - AddFloatingIpPool("fip-pool9", 10); - AddFloatingIp("fip9", 10, "10.10.10.2"); - AddLink("floating-ip", "fip9", "floating-ip-pool", "fip-pool9"); - AddLink("floating-ip-pool", "fip-pool9", "virtual-network", - "default-project:vn10"); - AddLink("virtual-machine-interface", "vnet9", "floating-ip", "fip9"); - client->WaitForIdle(); - - Ip4Address gw_rt = Ip4Address::from_string("0.0.0.0"); - Ip4Address remote_server_ip1 = Ip4Address::from_string("10.10.10.100"); - Ip4Address remote_server_ip2 = Ip4Address::from_string("10.10.10.101"); - Agent *agent = Agent::GetInstance(); - ComponentNHKeyPtr nh_data1(new ComponentNHKey(30, agent->fabric_vrf_name(), - agent->router_id(), - remote_server_ip1, - false, - TunnelType::DefaultType())); - ComponentNHKeyPtr nh_data2(new ComponentNHKey(20, agent->fabric_vrf_name(), - agent->router_id(), - remote_server_ip2, - false, - TunnelType::DefaultType())); - ComponentNHKeyList comp_nh_list; - comp_nh_list.push_back(nh_data1); - comp_nh_list.push_back(nh_data2); - EcmpTunnelRouteAdd(bgp_peer, "default-project:vn10:vn10", gw_rt, 0, - comp_nh_list, false, "default-project:vn10", - SecurityGroupList(), PathPreference()); - client->WaitForIdle(); - - TxIpPacket(VmPortGetId(9), "9.1.1.1", "10.1.1.1", 1); - client->WaitForIdle(); - FlowEntry *entry; - entry = FlowGet(VrfGet("default-project:vn10:vn10")->vrf_id(), - "9.1.1.1", "10.1.1.1", 1, 0, 0, GetFlowKeyNH(9)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->is_flags_set(FlowEntry::ShortFlow) == false); - - //Update the route to make the remote destination unicast - Inet4TunnelRouteAdd(bgp_peer, "default-project:vn10:vn10", gw_rt, 0, - Ip4Address::from_string("8.8.8.8"), - TunnelType::ComputeType(TunnelType::MplsType()), - 100, "default-project:vn10", SecurityGroupList(), - PathPreference()); - client->WaitForIdle(); - //Make sure flow has the right nexthop set. - InetUnicastRouteEntry *rt = RouteGet("default-project:vn10:vn10", gw_rt, 0); - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->nh() == rt->GetActiveNextHop()); - - DeleteVmportEnv(input1, 1, true); - DeleteRemoteRoute("default-project:vn10:vn10", "0.0.0.0", 0); - DelLink("floating-ip", "fip9", "floating-ip-pool", "fip-pool9"); - DelLink("floating-ip-pool", "fip-pool9", - "virtual-network", "default-project:vn10"); - DelLink("virtual-machine-interface", "vnet9", "floating-ip", "fip9"); - DelFloatingIp("fip9"); - DelFloatingIpPool("fip-pool9"); - client->WaitForIdle(); - DelVrf("default-project:vn10:vn10"); - DelVn("default-project:vn10"); - client->WaitForIdle(); - WAIT_FOR(100, 1000, VrfFind("default-project:vn10:vn10", true) == false); - client->WaitForIdle(); - EXPECT_TRUE(get_flow_proto()->FlowCount() == 0); - EXPECT_FALSE(VrfFind("vrf9", true)); -} - -TEST_F(EcmpTest, EcmpTest_15) { - struct PortInfo input1[] = { - {"vnet9", 9, "9.1.1.1", "00:00:00:01:01:01", 9, 9}, - }; - CreateVmportWithEcmp(input1, 1); - client->WaitForIdle(); - - Ip4Address gw_rt = Ip4Address::from_string("0.0.0.0"); - Ip4Address remote_server_ip1 = Ip4Address::from_string("10.10.10.100"); - Ip4Address remote_server_ip2 = Ip4Address::from_string("10.10.10.101"); - Agent *agent = Agent::GetInstance(); - ComponentNHKeyPtr nh_data1(new ComponentNHKey(30, agent->fabric_vrf_name(), - agent->router_id(), - remote_server_ip1, - false, - TunnelType::DefaultType())); - ComponentNHKeyPtr nh_data2(new ComponentNHKey(20, agent->fabric_vrf_name(), - agent->router_id(), - remote_server_ip2, - false, - TunnelType::DefaultType())); - ComponentNHKeyList comp_nh_list; - comp_nh_list.push_back(nh_data1); - comp_nh_list.push_back(nh_data2); - EcmpTunnelRouteAdd(bgp_peer, "vrf9", gw_rt, 0, - comp_nh_list, false, "vn9", - SecurityGroupList(), PathPreference()); - client->WaitForIdle(); - - TxIpPacket(VmPortGetId(9), "9.1.1.1", "10.1.1.1", 1); - client->WaitForIdle(); - FlowEntry *entry; - entry = FlowGet(VrfGet("vrf9")->vrf_id(), - "9.1.1.1", "10.1.1.1", 1, 0, 0, GetFlowKeyNH(9)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->is_flags_set(FlowEntry::ShortFlow) == false); - - //Update the route to make the remote destination unicast - Inet4TunnelRouteAdd(bgp_peer, "vrf9", gw_rt, 0, - Ip4Address::from_string("8.8.8.8"), - TunnelType::ComputeType(TunnelType::MplsType()), - 100, "vn9", SecurityGroupList(), - PathPreference()); - client->WaitForIdle(); - //Make sure flow has the right nexthop set. - InetUnicastRouteEntry *rt = RouteGet("vrf9", gw_rt, 0); - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->nh() == rt->GetActiveNextHop()); - - DeleteVmportEnv(input1, 1, true); - client->WaitForIdle(); - EXPECT_TRUE(get_flow_proto()->FlowCount() == 0); - EXPECT_FALSE(VrfFind("vrf9", true)); -} - -//In case of ECMP route leaked across VRF, there might -//not be local VM peer path in all the VRF, in that case -//we are picking local component NH from composite NH -//Since those component NH are policy disabled, flow -//calculation should pick policy enabled local NH for -//flow key calculation. This test case verifies the same -TEST_F(EcmpTest, EcmpTest_16) { - AddVrf("vrf9"); - client->WaitForIdle(); - - Ip4Address remote_server_ip1 = Ip4Address::from_string("10.10.10.100"); - //Add default route in vrf 9 pointing to remote VM - AddRemoteVmRoute("vrf9", "0.0.0.0", 0, "vn3"); - AddRemoteVmRoute("vrf2", "0.0.0.0", 0, "vn3"); - client->WaitForIdle(); - - VmInterface *intf = static_cast(VmPortGet(1)); - uint32_t label = intf->label(); - Agent *agent = Agent::GetInstance(); - //Add a ECMP route for traffic origiator - ComponentNHKeyPtr nh_data1(new ComponentNHKey(label, MakeUuid(1), - InterfaceNHFlags::INET4, - intf->vm_mac())); - ComponentNHKeyPtr nh_data2(new ComponentNHKey(20, agent->fabric_vrf_name(), - agent->router_id(), - remote_server_ip1, - false, - TunnelType::DefaultType())); - ComponentNHKeyList comp_nh_list; - comp_nh_list.push_back(nh_data1); - comp_nh_list.push_back(nh_data2); - //Delete local VM peer route for 1.1.1.1 to simulate - //error case, local component NH would be picked from - //composite NH, than from local vm peer - Ip4Address ip = Ip4Address::from_string("1.1.1.1"); - agent->fabric_inet4_unicast_table()->DeleteReq(intf->peer(), "vrf2", - ip, 32, NULL); - EcmpTunnelRouteAdd(bgp_peer, "vrf9", ip, 32, - comp_nh_list, false, "vn3", - SecurityGroupList(), PathPreference()); - EcmpTunnelRouteAdd(bgp_peer, "vrf2", ip, 32, - comp_nh_list, false, "vn2", - SecurityGroupList(), PathPreference()); - client->WaitForIdle(); - - //Add a vrf assign ACL to vn1, so that traffic is forwarded via - //vrf 9 - AddVrfAssignNetworkAcl("Acl", 10, "vn2", "vn3", "pass", "vrf9"); - AddLink("virtual-network", "vn2", "access-control-list", "Acl"); - client->WaitForIdle(); - - TxIpPacket(VmPortGetId(1), "1.1.1.1", "10.1.1.1", 1); - client->WaitForIdle(); - - FlowEntry *entry; - FlowEntry *rev_entry; - entry = FlowGet(VrfGet("vrf2")->vrf_id(), - "1.1.1.1", "10.1.1.1", 1, 0, 0, GetFlowKeyNH(1)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - - rev_entry = FlowGet(VrfGet("vrf9")->vrf_id(), - "10.1.1.1", "1.1.1.1", 1, 0, 0, GetFlowKeyNH(1)); - EXPECT_TRUE(rev_entry != NULL); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - - client->WaitForIdle(); - DelLink("virtual-network", "vn2", "access-control-list", "Acl"); - DelNode("access-control-list", "Acl"); - - DeleteRemoteRoute("vrf9", "1.1.1.1", 32); - DeleteRemoteRoute("vrf9", "0.0.0.0", 0); - DeleteRemoteRoute("vrf2", "0.0.0.0", 0); - client->WaitForIdle(); - DelVrf("vrf9"); - client->WaitForIdle(); - - WAIT_FOR(1000, 1000, get_flow_proto()->FlowCount() == 0); - EXPECT_FALSE(VrfFind("vrf9", true)); -} - -TEST_F(EcmpTest, EcmpTest_17) { - TxIpPacket(VmPortGetId(1), "1.1.1.1", "2.1.1.1", 1); - client->WaitForIdle(); - - FlowEntry *entry = FlowGet(VrfGet("vrf2")->vrf_id(), - "1.1.1.1", "2.1.1.1", 1, 0, 0, GetFlowKeyNH(1)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - - //Reverse flow is no ECMP - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(rev_entry->data().ecmp_rpf_nh_ != 0); - - AddRemoteVmRoute("vrf2", "2.1.1.1", 32, "vn10"); - client->WaitForIdle(); - entry = FlowGet(VrfGet("vrf2")->vrf_id(), - "1.1.1.1", "2.1.1.1", 1, 0, 0, GetFlowKeyNH(1)); - EXPECT_TRUE(entry->is_flags_set(FlowEntry::ShortFlow) == true); -} - -TEST_F(EcmpTest, EcmpReEval_1) { - TxIpPacket(VmPortGetId(1), "1.1.1.1", "2.1.1.1", 1); - client->WaitForIdle(); - FlowEntry *entry = FlowGet(VrfGet("vrf2")->vrf_id(), - "1.1.1.1", "2.1.1.1", 1, 0, 0, GetFlowKeyNH(1)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - - //Reverse flow is no ECMP - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - - uint32_t ecmp_index = entry->data().component_nh_idx; - //Delete VM corresponding to index component_nh_idx - IntfCfgDel(input2, ecmp_index); - client->WaitForIdle(); - //Enqueue a re-evaluate request - TxIpPacket(VmPortGetId(1), "1.1.1.1", "2.1.1.1", 1); - client->WaitForIdle(); - //Upon interface deletion flow would have been deleted, get flow again - FlowEntry *entry2 = FlowGet(VrfGet("vrf2")->vrf_id(), - "1.1.1.1", "2.1.1.1", 1, 0, 0, GetFlowKeyNH(1)); - - //Verify compoennt NH index is different - EXPECT_TRUE(entry2->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - //make sure new ecmp index is different from that of old ecmp index - EXPECT_TRUE(ecmp_index != entry2->data().component_nh_idx); -} - -//Send a flow for VM to non ECMP dip -//Change the nexthop pointed by non ECMP dip to ECMP dip -//Check if re-evaluation happens -TEST_F(EcmpTest, EcmpReEval_2) { - //Add a remote VM route for 3.1.1.10 - Ip4Address remote_vm_ip = Ip4Address::from_string("3.1.1.10"); - Ip4Address remote_server_ip = Ip4Address::from_string("10.10.10.10"); - Inet4TunnelRouteAdd(bgp_peer, "vrf2",remote_vm_ip, 32, - remote_server_ip, TunnelType::GREType(), 16, "vn2", - SecurityGroupList(), PathPreference()); - client->WaitForIdle(); - TxIpPacket(VmPortGetId(1), "1.1.1.1", "3.1.1.10", 1); - client->WaitForIdle(); - - FlowEntry *entry = FlowGet(VrfGet("vrf2")->vrf_id(), - "1.1.1.1", "3.1.1.10", 1, 0, 0, GetFlowKeyNH(1)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - - //Convert dip to ECMP nh - ComponentNHKeyList local_comp_nh; - AddRemoteEcmpRoute("vrf2", "3.1.1.10", 32, "vn2", 2, local_comp_nh); - client->WaitForIdle(); - - //Enqueue a re-evaluate request - TxIpPacketEcmp(VmPortGetId(1), "1.1.1.1", "3.1.1.10", 1); - client->WaitForIdle(); - entry = FlowGet(VrfGet("vrf2")->vrf_id(), - "1.1.1.1", "3.1.1.10", 1, 0, 0, GetFlowKeyNH(1)); - EXPECT_TRUE(entry != NULL); - //Since flow already existed, use same old NH which would be at index 0 - EXPECT_TRUE(entry->data().component_nh_idx == 0); - - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - DeleteRemoteRoute("vrf2", "3.1.1.10", 32); -} - -//Send a flow for VM to non ECMP dip -//Change the nexthop pointed by non ECMP dip to ECMP dip -//Check if re-evaluation happens -TEST_F(EcmpTest, EcmpReEval_3) { - struct PortInfo input1[] = { - {"vnet10", 10, "10.1.1.1", "00:00:00:01:01:01", 10, 10}, - {"vnet11", 11, "11.1.1.1", "00:00:00:01:01:01", 10, 11}, - }; - CreateVmportFIpEnv(input1, 2); - client->WaitForIdle(); - - //Associate floating IP with one interface - AddFloatingIpPool("fip-pool3", 3); - AddFloatingIp("fip3", 3, "3.1.1.10"); - AddLink("floating-ip", "fip3", "floating-ip-pool", "fip-pool3"); - AddLink("floating-ip-pool", "fip-pool3", "virtual-network", "vn2"); - AddLink("virtual-machine-interface", "vnet10", "floating-ip", "fip3"); - client->WaitForIdle(); - - TxIpPacket(VmPortGetId(1), "1.1.1.1", "3.1.1.10", 1); - client->WaitForIdle(); - - FlowEntry *entry = FlowGet(VrfGet("vrf2")->vrf_id(), - "1.1.1.1", "3.1.1.10", 1, 0, 0, GetFlowKeyNH(1)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - - AddLink("virtual-machine-interface", "vnet11", "floating-ip", "fip3"); - client->WaitForIdle(); - - //Enqueue a re-evaluate request - TxIpPacketEcmp(VmPortGetId(1), "1.1.1.1", "3.1.1.10", 1); - client->WaitForIdle(); - entry = FlowGet(VrfGet("vrf2")->vrf_id(), "1.1.1.1", "3.1.1.10", 1, 0, 0, - GetFlowKeyNH(1)); - EXPECT_TRUE(entry != NULL); - //Since flow already existed, use same old NH which would be at index 0 - EXPECT_TRUE(entry->data().component_nh_idx == 0); - - FlowEntry *rev_entry1 = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry == rev_entry1); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - - DelLink("virtual-machine-interface", "vnet10", "floating-ip", "fip3"); - DelLink("virtual-machine-interface", "vnet11", "floating-ip", "fip3"); - DelLink("floating-ip-pool", "fip-pool3", "virtual-network", "vn2"); - client->WaitForIdle(); - DeleteVmportFIpEnv(input1, 2, true); - client->WaitForIdle(); - EXPECT_FALSE(VrfFind("vn10:vn10")); -} - -TEST_F(EcmpTest, ServiceVlanTest_1) { - struct PortInfo input1[] = { - {"vnet10", 10, "10.1.1.1", "00:00:00:01:01:01", 10, 10}, - {"vnet11", 11, "11.1.1.1", "00:00:00:01:01:01", 10, 11}, - {"vnet12", 12, "12.1.1.1", "00:00:00:01:01:01", 10, 12}, - }; - - CreateVmportWithEcmp(input1, 3); - client->WaitForIdle(); - EXPECT_TRUE(VmPortActive(10)); - EXPECT_TRUE(VmPortActive(11)); - EXPECT_TRUE(VmPortActive(11)); - - //Add service VRF and VN - struct PortInfo input2[] = { - {"vnet13", 13, "11.1.1.252", "00:00:00:01:01:01", 11, 13}, - }; - CreateVmportWithEcmp(input2, 1); - client->WaitForIdle(); - - AddVmPortVrf("ser1", "11.1.1.253", 1); - AddLink("virtual-machine-interface-routing-instance", "ser1", - "routing-instance", "vrf11"); - AddLink("virtual-machine-interface-routing-instance", "ser1", - "virtual-machine-interface", "vnet10"); - AddLink("virtual-machine-interface-routing-instance", "ser1", - "virtual-machine-interface", "vnet11"); - AddLink("virtual-machine-interface-routing-instance", "ser1", - "virtual-machine-interface", "vnet12"); - client->WaitForIdle(); - - const VrfEntry *vrf = Agent::GetInstance()->vrf_table()->FindVrfFromName("vrf11"); - TxIpPacket(VmPortGetId(11), "11.1.1.253", "11.1.1.252", 1, 10, vrf->vrf_id()); - client->WaitForIdle(); - - int nh_id = GetServiceVlanNH(11, "vrf11"); - FlowEntry *entry = FlowGet(VrfGet("vrf11")->vrf_id(), - "11.1.1.253", "11.1.1.252", 1, 0, 0, nh_id); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - - //Reverse flow is no ECMP - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - - DelLink("virtual-machine-interface-routing-instance", "ser1", - "routing-instance", "vrf11"); - DelLink("virtual-machine-interface-routing-instance", "ser1", - "virtual-machine-interface", "vnet10"); - DelLink("virtual-machine-interface-routing-instance", "ser1", - "virtual-machine-interface", "vnet11"); - DelLink("virtual-machine-interface-routing-instance", "ser1", - "virtual-machine-interface", "vnet12"); - DeleteVmportEnv(input1, 3, true); - DeleteVmportEnv(input2, 1, true); - client->WaitForIdle(); - EXPECT_TRUE(get_flow_proto()->FlowCount() == 0); - EXPECT_FALSE(VrfFind("vrf11")); - EXPECT_FALSE(VrfFind("vrf10")); -} - -//Service VM with ECMP instantiated in two -//different remote server -//Packet sent from a VM instance to service VM instance -TEST_F(EcmpTest, ServiceVlanTest_2) { - struct PortInfo input1[] = { - {"vnet10", 10, "10.1.1.1", "00:00:00:01:01:01", 10, 10}, - }; - CreateVmportWithEcmp(input1, 1); - client->WaitForIdle(); - - ComponentNHKeyList local_comp_nh; - AddRemoteEcmpRoute("vrf10", "11.1.1.0", 24, "vn11", 2, local_comp_nh); - - uint32_t vrf_id = Agent::GetInstance()->vrf_table()->FindVrfFromName("vrf10")->vrf_id(); - TxIpPacket(VmPortGetId(10), "10.1.1.1", "11.1.1.252", 1, 10, vrf_id); - client->WaitForIdle(); - - FlowEntry *entry = FlowGet(VrfGet("vrf10")->vrf_id(), - "10.1.1.1", "11.1.1.252", 1, 0, 0, GetFlowKeyNH(10)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->data().vrf == vrf_id); - EXPECT_TRUE(entry->data().dest_vrf == vrf_id); - std::string vn_name_10("vn10"); - std::string vn_name_11("vn11"); - EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_10)); - EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_11)); - - //Reverse flow is no ECMP - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(rev_entry->data().vrf == vrf_id); - EXPECT_TRUE(rev_entry->data().dest_vrf == vrf_id); - EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_11)); - EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_10)); - - DeleteVmportEnv(input1, 1, true); - DeleteRemoteRoute("vrf10", "11.1.1.0", 24); - client->WaitForIdle(); - EXPECT_TRUE(get_flow_proto()->FlowCount() == 0); - EXPECT_FALSE(VrfFind("vrf10")); -} - -//Service VM with ECMP instantiated in local and remote server -//Packet sent from a VM instance to service VM instance -//Packet sent from a remote VM to service instance -TEST_F(EcmpTest, ServiceVlanTest_3) { - struct PortInfo input1[] = { - {"vnet10", 10, "10.1.1.1", "00:00:00:01:01:01", 10, 10}, - }; - CreateVmportWithEcmp(input1, 1); - client->WaitForIdle(); - - //Add service VM in vrf11 as mgmt VRF - struct PortInfo input2[] = { - {"vnet11", 11, "1.1.1.252", "00:00:00:01:01:01", 11, 11}, - }; - CreateVmportWithEcmp(input2, 1); - client->WaitForIdle(); - - AddVrf("service-vrf1", 12); - AddVmPortVrf("ser1", "10.1.1.2", 1); - AddLink("virtual-machine-interface-routing-instance", "ser1", - "routing-instance", "service-vrf1"); - AddLink("virtual-machine-interface-routing-instance", "ser1", - "virtual-machine-interface", "vnet11"); - client->WaitForIdle(); - - //Leak aggregarete route to vrf10 - Ip4Address service_vm_ip = Ip4Address::from_string("10.1.1.2"); - InetUnicastRouteEntry *rt = RouteGet("service-vrf1", service_vm_ip, 32); - ComponentNHKeyList comp_nh_list; - EXPECT_TRUE(rt != NULL); - const VlanNH *vlan_nh = static_cast(rt->GetActiveNextHop()); - ComponentNHKeyPtr comp_nh(new ComponentNHKey(rt->GetActiveLabel(), - vlan_nh->GetVlanTag(), vlan_nh->GetIfUuid())); - uint32_t vlan_label = rt->GetActiveLabel(); - comp_nh_list.push_back(comp_nh); - AddRemoteEcmpRoute("vrf10", "11.1.1.0", 24, "vn11", 1, comp_nh_list); - AddRemoteEcmpRoute("service-vrf1", "11.1.1.0", 24, "vn11", 1, comp_nh_list); - - //Leak a remote VM route in service-vrf - AddRemoteVmRoute("service-vrf1", "10.1.1.3", 32, "vn10"); - client->WaitForIdle(); - - uint32_t vrf_id = Agent::GetInstance()->vrf_table()->FindVrfFromName("vrf10")->vrf_id(); - uint32_t service_vrf_id = - Agent::GetInstance()->vrf_table()->FindVrfFromName("service-vrf1")->vrf_id(); - - //Choose some random source and destination port - uint32_t sport = rand() % 65535; - uint32_t dport = rand() % 65535; - for (uint32_t i = 0; i < 100; i++) { - uint32_t hash_id = rand() % 65535; - TxTcpPacket(VmPortGetId(10), "10.1.1.1", "11.1.1.252", sport, dport, - false, hash_id); - client->WaitForIdle(); - - FlowEntry *entry = FlowGet(VrfGet("vrf10")->vrf_id(), - "10.1.1.1", "11.1.1.252", IPPROTO_TCP, sport, dport, - GetFlowKeyNH(10)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->data().vrf == vrf_id); - if (entry->data().component_nh_idx == 1) { - //Remote component index will be installed at index 1 - //Destination on remote server, hence destination - //VRF will be same as that of interface - EXPECT_TRUE(entry->data().dest_vrf == vrf_id); - } else { - //Destination on local server, destination VRF - //will be that of service VRF - LOG(DEBUG, "Dest vrf " << entry->data().dest_vrf << "Service VRF:" << service_vrf_id); - EXPECT_TRUE(entry->data().dest_vrf == service_vrf_id); - } - - std::string vn_name_10("vn10"); - std::string vn_name_11("vn11"); - EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_10)); - EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_11)); - - //Reverse flow is no ECMP - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - if (entry->data().component_nh_idx == 1) { - //Reverse flow originates remote server, hence - //source VRF is same as destination interface VRF - EXPECT_TRUE(rev_entry->data().vrf == vrf_id); - } else { - //Reverse flow on same server, source VRF will be that - //of service VM vlan interface - EXPECT_TRUE(rev_entry->data().vrf == service_vrf_id); - } - EXPECT_TRUE(rev_entry->data().dest_vrf == vrf_id); - EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_11)); - EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_10)); - sport++; - dport++; - } - - //Leak a remote VM route in service-vrf - AddRemoteVmRoute("service-vrf1", "10.1.1.3", 32, "vn10"); - client->WaitForIdle(); - //Send a packet from a remote VM to service VM - //Below scenario wouldnt happen with vrouter, as destination - //packet came with explicit unicast mpls tag - sport = rand() % 65535; - dport = rand() % 65535; - for (uint32_t i = 0; i < 100; i++) { - char router_id[80]; - strcpy(router_id, Agent::GetInstance()->router_id().to_string().c_str()); - - uint32_t hash_id = rand() % 65535; - TxTcpMplsPacket(eth_intf_id_, "10.11.11.1", router_id, - vlan_label, "10.1.1.3", "11.1.1.252", - sport, dport, false, hash_id); - client->WaitForIdle(); - - int nh_id = - GetActiveLabel(MplsLabel::VPORT_NH, vlan_label)->nexthop()->id(); - FlowEntry *entry = FlowGet(VrfGet("service-vrf1")->vrf_id(), - "10.1.1.3", "11.1.1.252", IPPROTO_TCP, sport, dport, nh_id); - EXPECT_TRUE(entry != NULL); - //No ECMP as packet came with explicit mpls label - //pointing to vlan NH - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - sport++; - dport++; - } - - DelLink("virtual-machine-interface-routing-instance", "ser1", - "routing-instance", "service-vrf1"); - DelLink("virtual-machine-interface-routing-instance", "ser1", - "virtual-machine-interface", "vnet11"); - DeleteVmportEnv(input2, 1, true); - DeleteRemoteRoute("service-vrf1", "11.1.1.0", 24); - DelVrf("service-vrf1"); - DeleteVmportEnv(input1, 1, true); - client->WaitForIdle(); - - WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); - EXPECT_FALSE(VrfFind("vrf11")); - EXPECT_FALSE(VrfFind("vrf10")); - EXPECT_FALSE(VrfFind("service-vrf1")); -} - - -//Service VM with ECMP instantaited in local server -//Test with packet from virtual-machine to service VM instance -//Test with packet from external-world to service VM instance -TEST_F(EcmpTest, ServiceVlanTest_4) { - struct PortInfo input1[] = { - {"vnet10", 10, "10.1.1.1", "00:00:00:01:01:01", 10, 10}, - }; - CreateVmportWithEcmp(input1, 1); - client->WaitForIdle(); - - //Add service VM in vrf11 as mgmt VRF - struct PortInfo input2[] = { - {"vnet11", 11, "1.1.1.252", "00:00:00:01:01:01", 11, 11}, - {"vnet12", 12, "1.1.1.253", "00:00:00:01:01:01", 11, 12}, - }; - CreateVmportWithEcmp(input2, 2); - client->WaitForIdle(); - - AddVrf("service-vrf1", 12); - AddVmPortVrf("ser1", "10.1.1.2", 1); - AddLink("virtual-machine-interface-routing-instance", "ser1", - "routing-instance", "service-vrf1"); - AddLink("virtual-machine-interface-routing-instance", "ser1", - "virtual-machine-interface", "vnet11"); - AddLink("virtual-machine-interface-routing-instance", "ser1", - "virtual-machine-interface", "vnet12"); - client->WaitForIdle(); - - uint32_t vrf_id = Agent::GetInstance()->vrf_table()->FindVrfFromName("vrf10")->vrf_id(); - uint32_t service_vrf_id = - Agent::GetInstance()->vrf_table()->FindVrfFromName("service-vrf1")->vrf_id(); - - //Leak aggregarete route to vrf10 - Ip4Address service_vm_ip = Ip4Address::from_string("10.1.1.2"); - InetUnicastRouteEntry *rt = RouteGet("service-vrf1", service_vm_ip, 32); - EXPECT_TRUE(rt != NULL); - - DBEntryBase::KeyPtr key_ref = rt->GetActiveNextHop()->GetDBRequestKey(); - CompositeNHKey *composite_nh_key = static_cast - (key_ref.get()); - ComponentNHKeyPtr comp_nh_data(new ComponentNHKey(rt->GetActiveLabel(), - Composite::ECMP, true, composite_nh_key->component_nh_key_list(), - "service-vrf1")); - ComponentNHKeyList comp_nh_list; - comp_nh_list.push_back(comp_nh_data); - AddRemoteEcmpRoute("vrf10", "11.1.1.0", 24, "vn11", 0, comp_nh_list); - AddRemoteEcmpRoute("service-vrf1", "11.1.1.0", 24, "vn11", 0, comp_nh_list); - - //Choose some random source and destination port - uint32_t sport = rand() % 65535; - uint32_t dport = rand() % 65535; - for (uint32_t i = 0; i < 100; i++) { - uint32_t hash_id = rand() % 65535; - TxTcpPacket(VmPortGetId(10), "10.1.1.1", "11.1.1.252", sport, dport, - false, hash_id); - client->WaitForIdle(); - - FlowEntry *entry = FlowGet(VrfGet("vrf10")->vrf_id(), - "10.1.1.1", "11.1.1.252", IPPROTO_TCP, sport, dport, - GetFlowKeyNH(10)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->data().vrf == vrf_id); - //Packet destined to service interface, vrf has to be - //service vlan VRF - EXPECT_TRUE(entry->data().dest_vrf == service_vrf_id); - - std::string vn_name_10("vn10"); - std::string vn_name_11("vn11"); - EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_10)); - EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_11)); - - //Reverse flow is no ECMP - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - //Packet from service interface, vrf has to be - //service vlan VRF - EXPECT_TRUE(rev_entry->data().vrf == service_vrf_id); - EXPECT_TRUE(rev_entry->data().dest_vrf == vrf_id); - EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_11)); - EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_10)); - sport++; - dport++; - } - - //Leak a remote VM route in service-vrf - AddRemoteVmRoute("service-vrf1", "10.1.1.3", 32, "vn10"); - client->WaitForIdle(); - uint32_t label = rt->GetActiveLabel(); - //Send a packet from a remote VM to service VM - sport = rand() % 65535; - dport = rand() % 65535; - for (uint32_t i = 0; i < 100; i++) { - char router_id[80]; - strcpy(router_id, Agent::GetInstance()->router_id().to_string().c_str()); - - uint32_t hash_id = rand() % 65535; - TxTcpMplsPacket(eth_intf_id_, "10.11.11.1", router_id, - label, "10.1.1.3", "11.1.1.252", - sport, dport, false, hash_id); - client->WaitForIdle(); - - int nh_id = GetActiveLabel(MplsLabel::VPORT_NH, label)->nexthop()->id(); - FlowEntry *entry = FlowGet(VrfGet("service-vrf1")->vrf_id(), - "10.1.1.3", "11.1.1.252", IPPROTO_TCP, sport, dport, nh_id); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->data().vrf == service_vrf_id); - //Packet destined to service interface, vrf has to be - //service vlan VRF - LOG(DEBUG, "Vrf" << entry->data().dest_vrf << ":" << service_vrf_id); - EXPECT_TRUE(entry->data().dest_vrf == service_vrf_id); - - std::string vn_name_10("vn10"); - std::string vn_name_11("vn11"); - EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_10)); - EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_11)); - - //Reverse flow is no ECMP - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - //Packet from service interface, vrf has to be - //service vlan VRF - EXPECT_TRUE(rev_entry->data().vrf == service_vrf_id); - EXPECT_TRUE(rev_entry->data().dest_vrf == service_vrf_id); - EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_11)); - EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_10)); - sport++; - dport++; - } - - DelLink("virtual-machine-interface-routing-instance", "ser1", - "routing-instance", "service-vrf1"); - DelLink("virtual-machine-interface-routing-instance", "ser1", - "virtual-machine-interface", "vnet11"); - DelLink("virtual-machine-interface-routing-instance", "ser1", - "virtual-machine-interface", "vnet12"); - DeleteVmportEnv(input2, 2, true); - DelVrf("service-vrf1"); - client->WaitForIdle(2); - WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); - DeleteVmportEnv(input1, 1, true); - client->WaitForIdle(); - EXPECT_FALSE(VrfFind("vrf11")); - EXPECT_FALSE(VrfFind("vrf10")); - EXPECT_FALSE(VrfFind("service-vrf1")); -} - -//Packet from a right service VM interface(both service vm instance launched on same server) -//reaching end host on same machine -TEST_F(EcmpTest, ServiceVlanTest_5) { - struct PortInfo input1[] = { - {"vnet11", 11, "11.1.1.1", "00:00:00:01:01:01", 11, 11}, - }; - CreateVmportWithEcmp(input1, 1); - client->WaitForIdle(); - - //Add service VM in vrf13 as mgmt VRF - struct PortInfo input2[] = { - {"vnet13", 13, "1.1.1.252", "00:00:00:01:01:01", 13, 13}, - {"vnet14", 14, "1.1.1.253", "00:00:00:01:01:01", 13, 14}, - }; - CreateVmportWithEcmp(input2, 2); - client->WaitForIdle(); - - AddVrf("service-vrf1", 12); - AddVmPortVrf("ser1", "11.1.1.2", 1); - AddLink("virtual-machine-interface-routing-instance", "ser1", - "routing-instance", "service-vrf1"); - AddLink("virtual-machine-interface-routing-instance", "ser1", - "virtual-machine-interface", "vnet13"); - AddLink("virtual-machine-interface-routing-instance", "ser1", - "virtual-machine-interface", "vnet14"); - client->WaitForIdle(); - - const MacAddress mac("02:00:00:00:00:02"); - EXPECT_TRUE(L2RouteFind("service-vrf1",mac)); - - uint32_t vrf_id = Agent::GetInstance()->vrf_table()->FindVrfFromName("vrf11")->vrf_id(); - uint32_t service_vrf_id = - Agent::GetInstance()->vrf_table()->FindVrfFromName("service-vrf1")->vrf_id(); - - Ip4Address service_vm_ip = Ip4Address::from_string("11.1.1.2"); - InetUnicastRouteEntry *rt = RouteGet("service-vrf1", service_vm_ip, 32); - EXPECT_TRUE(rt != NULL); - - DBEntryBase::KeyPtr key_ref = rt->GetActiveNextHop()->GetDBRequestKey(); - CompositeNHKey *composite_nh_key = static_cast - (key_ref.get()); - ComponentNHKeyPtr comp_nh_data(new ComponentNHKey(rt->GetActiveLabel(), - Composite::ECMP, true, composite_nh_key->component_nh_key_list(), - "service-vrf1")); - ComponentNHKeyList comp_nh_list; - comp_nh_list.push_back(comp_nh_data); - //Leak a aggregarate route to service VRF - AddRemoteEcmpRoute("service-vrf1", "10.1.1.0", 24, "vn10", 0, comp_nh_list); - //Leak a aggresgarate route vrf 11 - AddRemoteEcmpRoute("vrf11", "10.1.1.0", 24, "vn10", 0, comp_nh_list); - //Leak route for vm11 to service vrf - AddLocalVmRoute("service-vrf1", "11.1.1.1", 32, "vn11", 11); - client->WaitForIdle(); - Ip4Address vn10_agg_ip = Ip4Address::from_string("10.1.1.0"); - const CompositeNH *comp_nh = static_cast - (RouteGet("service-vrf1", vn10_agg_ip, 24)->GetActiveNextHop()); - - uint32_t vnet13_vlan_nh = GetServiceVlanNH(13, "service-vrf1"); - uint32_t vnet14_vlan_nh = GetServiceVlanNH(14, "service-vrf1"); - - //Choose some random source and destination port - uint32_t sport = rand() % 65535; - uint32_t dport = rand() % 65535; - for (uint32_t i = 0; i < 100; i++) { - uint32_t hash_id = rand() % 65535; - TxTcpPacket(VmPortGetId(13), "10.1.1.1", "11.1.1.1", sport, dport, - false, hash_id, service_vrf_id); - client->WaitForIdle(); - - FlowEntry *entry = FlowGet(service_vrf_id, - "10.1.1.1", "11.1.1.1", IPPROTO_TCP, sport, dport, - vnet13_vlan_nh); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->data().vrf == service_vrf_id); - //Packet destined to vm11, vrf has to be vrf 11 - EXPECT_TRUE(entry->data().dest_vrf == vrf_id); - - std::string vn_name_10("vn10"); - std::string vn_name_11("vn11"); - EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_10)); - EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_11)); - - FlowEntry *rev_entry = entry->reverse_flow_entry(); - const InterfaceNH *intf_nh = static_cast - (comp_nh->GetNH(rev_entry->data().component_nh_idx)); - EXPECT_TRUE(intf_nh->GetIfUuid() == MakeUuid(13)); - //Packet to service interface, vrf has to be - //service vlan VRF - EXPECT_TRUE(rev_entry->data().vrf == vrf_id); - EXPECT_TRUE(rev_entry->data().dest_vrf == service_vrf_id); - EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_11)); - EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_10)); - sport++; - dport++; - } - - //Send traffice from second service interface and expect things to be fine - for (uint32_t i = 0; i < 100; i++) { - uint32_t hash_id = rand() % 65535; - TxTcpPacket(VmPortGetId(14), "10.1.1.1", "11.1.1.1", sport, dport, - false, hash_id, service_vrf_id); - client->WaitForIdle(); - - FlowEntry *entry = FlowGet(service_vrf_id, - "10.1.1.1", "11.1.1.1", IPPROTO_TCP, sport, dport, - vnet14_vlan_nh); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->data().vrf == service_vrf_id); - //Packet destined to vm11, vrf has to be vrf11 - EXPECT_TRUE(entry->data().dest_vrf == vrf_id); - - std::string vn_name_10("vn10"); - std::string vn_name_11("vn11"); - EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_10)); - EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_11)); - - //make sure reverse flow points to right index - FlowEntry *rev_entry = entry->reverse_flow_entry(); - const InterfaceNH *intf_nh = static_cast - (comp_nh->GetNH(rev_entry->data().component_nh_idx)); - EXPECT_TRUE(intf_nh->GetIfUuid() == MakeUuid(14)); - - //Packet from vm11 to service vrf - EXPECT_TRUE(rev_entry->data().vrf == vrf_id); - EXPECT_TRUE(rev_entry->data().dest_vrf == service_vrf_id); - EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_11)); - EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_10)); - sport++; - dport++; - } - - DelLink("virtual-machine-interface-routing-instance", "ser1", - "virtual-machine-interface", "vnet13"); - client->WaitForIdle(); - EXPECT_TRUE(L2RouteFind("service-vrf1",mac)); - - DelLink("virtual-machine-interface-routing-instance", "ser1", - "virtual-machine-interface", "vnet14"); - client->WaitForIdle(); - EXPECT_FALSE(L2RouteFind("service-vrf1",mac)); - - DelLink("virtual-machine-interface-routing-instance", "ser1", - "routing-instance", "service-vrf1"); - DeleteVmportEnv(input2, 2, true); - DelVrf("service-vrf1"); - client->WaitForIdle(); - //Make sure all flows are also deleted - EXPECT_TRUE(get_flow_proto()->FlowCount() == 0); - - DeleteVmportEnv(input1, 1, true); - client->WaitForIdle(); - EXPECT_FALSE(VrfFind("vrf11")); - EXPECT_FALSE(VrfFind("vrf13")); - EXPECT_FALSE(VrfFind("service-vrf1")); -} - -//Packet from a right service VM interface(both service vm instance launched on same server) -//reaching end host on a remote machine -//Packet from remote host reaching service VM -//Packet from remote ecmp host reaching service VM -TEST_F(EcmpTest, ServiceVlanTest_6) { - //Add service VM in vrf13 as mgmt VRF - struct PortInfo input2[] = { - {"vnet13", 13, "1.1.1.252", "00:00:00:01:01:01", 13, 13}, - {"vnet14", 14, "1.1.1.253", "00:00:00:01:01:01", 13, 14}, - }; - CreateVmportWithEcmp(input2, 2); - client->WaitForIdle(); - - AddVrf("service-vrf1", 12); - AddVmPortVrf("ser1", "11.1.1.2", 1); - AddLink("virtual-machine-interface-routing-instance", "ser1", - "routing-instance", "service-vrf1"); - AddLink("virtual-machine-interface-routing-instance", "ser1", - "virtual-machine-interface", "vnet13"); - AddLink("virtual-machine-interface-routing-instance", "ser1", - "virtual-machine-interface", "vnet14"); - client->WaitForIdle(); - - std::string vn_name_10("vn10"); - std::string vn_name_11("vn11"); - uint32_t service_vrf_id = - Agent::GetInstance()->vrf_table()->FindVrfFromName("service-vrf1")->vrf_id(); - - Ip4Address service_vm_ip = Ip4Address::from_string("11.1.1.2"); - InetUnicastRouteEntry *rt = RouteGet("service-vrf1", service_vm_ip, 32); - EXPECT_TRUE(rt != NULL); - uint32_t mpls_label = rt->GetActiveLabel(); - - DBEntryBase::KeyPtr key_ref = rt->GetActiveNextHop()->GetDBRequestKey(); - CompositeNHKey *composite_nh_key = static_cast - (key_ref.get()); - ComponentNHKeyPtr comp_nh_data(new ComponentNHKey(rt->GetActiveLabel(), - Composite::ECMP, true, composite_nh_key->component_nh_key_list(), - "service-vrf1")); - ComponentNHKeyList comp_nh_list; - comp_nh_list.push_back(comp_nh_data); - - //Leak a aggregarate route to service VRF - AddRemoteEcmpRoute("service-vrf1", "10.1.1.0", 24, "vn10", 0, comp_nh_list); - //Leak routes from right vrf to service vrf - AddRemoteVmRoute("service-vrf1", "11.1.1.1", 32, "vn11"); - comp_nh_list.clear(); - AddRemoteEcmpRoute("service-vrf1", "11.1.1.3", 32, "vn11", 2, comp_nh_list); - client->WaitForIdle(); - Ip4Address vn10_agg_ip = Ip4Address::from_string("10.1.1.0"); - const CompositeNH *comp_nh = static_cast - (RouteGet("service-vrf1", vn10_agg_ip, 24)->GetActiveNextHop()); - - uint32_t vnet13_vlan_nh = GetServiceVlanNH(13, "service-vrf1"); - uint32_t vnet14_vlan_nh = GetServiceVlanNH(14, "service-vrf1"); - //Choose some random source and destination port - uint32_t sport = rand() % 65535; - uint32_t dport = rand() % 65535; - for (uint32_t i = 0; i < 10; i++) { - uint32_t hash_id = rand() % 65535; - TxTcpPacket(VmPortGetId(13), "10.1.1.1", "11.1.1.1", sport, dport, - false, hash_id, service_vrf_id); - client->WaitForIdle(); - - FlowEntry *entry = FlowGet(service_vrf_id, - "10.1.1.1", "11.1.1.1", IPPROTO_TCP, sport, dport, - vnet13_vlan_nh); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->data().vrf == service_vrf_id); - //Packet destined to remote server, vrf would be same as service vrf - EXPECT_TRUE(entry->data().dest_vrf == service_vrf_id); - - EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_10)); - EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_11)); - - FlowEntry *rev_entry = entry->reverse_flow_entry(); - const InterfaceNH *intf_nh = static_cast - (comp_nh->GetNH(rev_entry->data().component_nh_idx)); - EXPECT_TRUE(intf_nh->GetIfUuid() == MakeUuid(13)); - //Packet to service interface, vrf has to be - //service vlan VRF - EXPECT_TRUE(rev_entry->data().vrf == service_vrf_id); - EXPECT_TRUE(rev_entry->data().dest_vrf == service_vrf_id); - EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_11)); - EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_10)); - sport++; - dport++; - } - - //Send traffic from second service interface - for (uint32_t i = 0; i < 10; i++) { - uint32_t hash_id = rand() % 65535; - TxTcpPacket(VmPortGetId(14), "10.1.1.1", "11.1.1.1", sport, dport, - false, hash_id, service_vrf_id); - client->WaitForIdle(); - - FlowEntry *entry = FlowGet(service_vrf_id, - "10.1.1.1", "11.1.1.1", IPPROTO_TCP, sport, dport, - vnet14_vlan_nh); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->data().vrf == service_vrf_id); - //Packet destined to remote server, vrf would be same as service vrf - EXPECT_TRUE(entry->data().dest_vrf == service_vrf_id); - - EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_10)); - EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_11)); - - //make sure reverse flow points to right index - FlowEntry *rev_entry = entry->reverse_flow_entry(); - const InterfaceNH *intf_nh = static_cast - (comp_nh->GetNH(rev_entry->data().component_nh_idx)); - EXPECT_TRUE(intf_nh->GetIfUuid() == MakeUuid(14)); - - EXPECT_TRUE(rev_entry->data().vrf == service_vrf_id); - EXPECT_TRUE(rev_entry->data().dest_vrf == service_vrf_id); - EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_11)); - EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_10)); - sport++; - dport++; - } - - char router_id[80]; - strcpy(router_id, Agent::GetInstance()->router_id().to_string().c_str()); - //Send traffic from remote VM service - for (uint32_t i = 0; i < 10; i++) { - uint32_t hash_id = rand() % 65535; - TxTcpMplsPacket(eth_intf_id_, "10.10.10.10", router_id, - mpls_label, "11.1.1.1", "10.1.1.1", - sport, dport, false, hash_id); - client->WaitForIdle(); - - int nh_id = - GetActiveLabel(MplsLabel::VPORT_NH, mpls_label)->nexthop()->id(); - FlowEntry *entry = FlowGet(service_vrf_id, - "11.1.1.1", "10.1.1.1", IPPROTO_TCP, sport, dport, nh_id); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->data().vrf == service_vrf_id); - EXPECT_TRUE(entry->data().dest_vrf == service_vrf_id); - EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_11)); - EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_10)); - - //make sure reverse flow is no ecmp - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(rev_entry->data().vrf == service_vrf_id); - EXPECT_TRUE(rev_entry->data().dest_vrf == service_vrf_id); - EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_10)); - EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_11)); - sport++; - dport++; - } - - //Send traffic from remote VM which is also ecmp - for (uint32_t i = 0; i < 10; i++) { - uint32_t hash_id = rand() % 65535; - TxTcpMplsPacket(eth_intf_id_, "10.10.10.10", router_id, - mpls_label, "11.1.1.3", "10.1.1.1", - sport, dport, false, hash_id); - client->WaitForIdle(); - - int nh_id = - GetActiveLabel(MplsLabel::VPORT_NH, mpls_label)->nexthop()->id(); - FlowEntry *entry = FlowGet(service_vrf_id, - "11.1.1.3", "10.1.1.1", IPPROTO_TCP, sport, dport, nh_id); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->data().vrf == service_vrf_id); - EXPECT_TRUE(entry->data().dest_vrf == service_vrf_id); - EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_11)); - EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_10)); - - //make sure reverse flow is no ecmp - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx == 0); - EXPECT_TRUE(rev_entry->data().vrf == service_vrf_id); - EXPECT_TRUE(rev_entry->data().dest_vrf == service_vrf_id); - EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_10)); - EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_11)); - sport++; - dport++; - } - - //Send traffic from remote VM which is also ecmp - for (uint32_t i = 0; i < 10; i++) { - uint32_t hash_id = rand() % 65535; - TxTcpMplsPacket(eth_intf_id_, "10.10.10.11", router_id, - mpls_label, "11.1.1.3", "10.1.1.1", - sport, dport, false, hash_id); - client->WaitForIdle(); - int nh_id = - GetActiveLabel(MplsLabel::VPORT_NH, mpls_label)->nexthop()->id(); - FlowEntry *entry = FlowGet(service_vrf_id, - "11.1.1.3", "10.1.1.1", IPPROTO_TCP, sport, dport, nh_id); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - - EXPECT_TRUE(entry->data().dest_vrf == service_vrf_id); - EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_11)); - EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_10)); - - //make sure reverse flow is no ecmp - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx == 1); - EXPECT_TRUE(rev_entry->data().vrf == service_vrf_id); - EXPECT_TRUE(rev_entry->data().dest_vrf == service_vrf_id); - EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_10)); - EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_11)); - sport++; - dport++; - } - - DeleteRemoteRoute("service-vrf1", "10.1.1.0", 24); - DeleteRemoteRoute("service-vrf1", "11.1.1.3", 32); - DelLink("virtual-machine-interface-routing-instance", "ser1", - "routing-instance", "service-vrf1"); - DelLink("virtual-machine-interface-routing-instance", "ser1", - "virtual-machine-interface", "vnet13"); - DelLink("virtual-machine-interface-routing-instance", "ser1", - "virtual-machine-interface", "vnet14"); - client->WaitForIdle(); - DeleteVmportEnv(input2, 2, true); - DelVrf("service-vrf1"); - client->WaitForIdle(); - WAIT_FOR(10000, 1000, (get_flow_proto()->FlowCount() == 0)); - client->WaitForIdle(); - EXPECT_FALSE(VrfFind("vrf13")); - EXPECT_FALSE(VrfFind("service-vrf1")); -} - -//Packet from a right service VM interface(one service instance on local server, -// one on remote server) reaching end host on a local machine -TEST_F(EcmpTest, ServiceVlanTest_7) { - struct PortInfo input1[] = { - {"vnet11", 11, "11.1.1.1", "00:00:00:01:01:01", 11, 11}, - }; - CreateVmportWithEcmp(input1, 1); - client->WaitForIdle(); - - //Add service VM in vrf13 as mgmt VRF - struct PortInfo input2[] = { - {"vnet13", 13, "1.1.1.252", "00:00:00:01:01:01", 13, 13}, - }; - CreateVmportWithEcmp(input2, 1); - client->WaitForIdle(); - - AddVrf("service-vrf1", 12); - AddVmPortVrf("ser1", "11.1.1.2", 1); - AddLink("virtual-machine-interface-routing-instance", "ser1", - "routing-instance", "service-vrf1"); - AddLink("virtual-machine-interface-routing-instance", "ser1", - "virtual-machine-interface", "vnet13"); - client->WaitForIdle(); - - uint32_t vrf_id = Agent::GetInstance()->vrf_table()->FindVrfFromName("vrf11")->vrf_id(); - uint32_t service_vrf_id = - Agent::GetInstance()->vrf_table()->FindVrfFromName("service-vrf1")->vrf_id(); - - Ip4Address service_vm_ip = Ip4Address::from_string("11.1.1.2"); - InetUnicastRouteEntry *rt = RouteGet("service-vrf1", service_vm_ip, 32); - EXPECT_TRUE(rt != NULL); - - ComponentNHKeyList comp_nh_list; - const VlanNH *vlan_nh = static_cast(rt->GetActiveNextHop()); - ComponentNHKeyPtr comp_nh_data(new ComponentNHKey( - rt->GetActiveLabel(), vlan_nh->GetVlanTag(), vlan_nh->GetIfUuid())); - comp_nh_list.push_back(comp_nh_data); - - //Leak a aggregarate route to service VRF - AddRemoteEcmpRoute("service-vrf1", "10.1.1.0", 24, "vn10", 1, comp_nh_list); - //Leak a aggregarate route tp vrf11 - AddRemoteEcmpRoute("vrf11", "10.1.1.0", 24, "vn10", 1, comp_nh_list); - //Leak route for vm11 to service vrf - AddLocalVmRoute("service-vrf1", "11.1.1.1", 32, "vn11", 11); - client->WaitForIdle(); - Ip4Address vn10_agg_ip = Ip4Address::from_string("10.1.1.0"); - const CompositeNH *comp_nh = static_cast - (RouteGet("service-vrf1", vn10_agg_ip, 24)->GetActiveNextHop()); - uint32_t vnet13_vlan_nh = GetServiceVlanNH(13, "service-vrf1"); - - //Choose some random source and destination port - uint32_t sport = rand() % 65535; - uint32_t dport = rand() % 65535; - for (uint32_t i = 0; i < 100; i++) { - uint32_t hash_id = rand() % 65535; - TxTcpPacket(VmPortGetId(13), "10.1.1.1", "11.1.1.1", sport, dport, - false, hash_id, service_vrf_id); - client->WaitForIdle(); - - FlowEntry *entry = FlowGet(service_vrf_id, - "10.1.1.1", "11.1.1.1", IPPROTO_TCP, sport, dport, - vnet13_vlan_nh); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->data().vrf == service_vrf_id); - //Packet destined to vm11, vrf has to be vrf 11 - EXPECT_TRUE(entry->data().dest_vrf == vrf_id); - - std::string vn_name_10("vn10"); - std::string vn_name_11("vn11"); - EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_10)); - EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_11)); - - FlowEntry *rev_entry = entry->reverse_flow_entry(); - const InterfaceNH *intf_nh = static_cast - (comp_nh->GetNH(rev_entry->data().component_nh_idx)); - EXPECT_TRUE(intf_nh->GetIfUuid() == MakeUuid(13)); - //Packet to service interface, vrf has to be - //service vlan VRF - EXPECT_TRUE(rev_entry->data().vrf == vrf_id); - EXPECT_TRUE(rev_entry->data().dest_vrf == service_vrf_id); - EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_11)); - EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_10)); - sport++; - dport++; - } - - DelLink("virtual-machine-interface-routing-instance", "ser1", - "routing-instance", "service-vrf1"); - DelLink("virtual-machine-interface-routing-instance", "ser1", - "virtual-machine-interface", "vnet13"); - DeleteVmportEnv(input2, 1, true); - client->WaitForIdle(); - EXPECT_TRUE(get_flow_proto()->FlowCount() == 0); - - DeleteVmportEnv(input1, 1, true); - DelVrf("service-vrf1"); - client->WaitForIdle(); - EXPECT_FALSE(VrfFind("vrf11")); - EXPECT_FALSE(VrfFind("vrf13")); - EXPECT_FALSE(VrfFind("service-vrf1")); -} - -//Packet from a right service VM interface (one service instance on local server, -// one on remote server), reaching end host on a remote machine -TEST_F(EcmpTest,ServiceVlanTest_8) { - //Add service VM in vrf13 as mgmt VRF - struct PortInfo input2[] = { - {"vnet13", 13, "1.1.1.252", "00:00:00:01:01:01", 13, 13}, - }; - CreateVmportWithEcmp(input2, 1); - client->WaitForIdle(); - - AddVrf("service-vrf1", 12); - AddVmPortVrf("ser1", "11.1.1.2", 1); - AddLink("virtual-machine-interface-routing-instance", "ser1", - "routing-instance", "service-vrf1"); - AddLink("virtual-machine-interface-routing-instance", "ser1", - "virtual-machine-interface", "vnet13"); - client->WaitForIdle(); - - uint32_t service_vrf_id = - Agent::GetInstance()->vrf_table()->FindVrfFromName("service-vrf1")->vrf_id(); - - Ip4Address service_vm_ip = Ip4Address::from_string("11.1.1.2"); - InetUnicastRouteEntry *rt = RouteGet("service-vrf1", service_vm_ip, 32); - EXPECT_TRUE(rt != NULL); - - ComponentNHKeyList comp_nh_list; - const VlanNH *vlan_nh = static_cast(rt->GetActiveNextHop()); - ComponentNHKeyPtr comp_nh_data(new ComponentNHKey( - rt->GetActiveLabel(), vlan_nh->GetVlanTag(), - vlan_nh->GetIfUuid())); - comp_nh_list.push_back(comp_nh_data); - - //Leak a aggregarate route to service VRF - AddRemoteEcmpRoute("service-vrf1", "10.1.1.0", 24, "vn10", 1, comp_nh_list); - //Leak route a for remote server to service vrf - AddRemoteVmRoute("service-vrf1", "11.1.1.1", 32, "vn11"); - client->WaitForIdle(); - - uint32_t vnet13_vlan_nh = GetServiceVlanNH(13, "service-vrf1"); - //Choose some random source and destination port - uint32_t sport = rand() % 65535; - uint32_t dport = rand() % 65535; - for (uint32_t i = 0; i < 100; i++) { - uint32_t hash_id = rand() % 65535; - TxTcpPacket(VmPortGetId(13), "10.1.1.1", "11.1.1.1", sport, dport, - false, hash_id, service_vrf_id); - client->WaitForIdle(); - - FlowEntry *entry = FlowGet(service_vrf_id, - "10.1.1.1", "11.1.1.1", IPPROTO_TCP, sport, dport, - vnet13_vlan_nh); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->data().vrf == service_vrf_id); - //Packet destined to remote server, vrf has to be service vrf - EXPECT_TRUE(entry->data().dest_vrf == service_vrf_id); - std::string vn_name_10("vn10"); - std::string vn_name_11("vn11"); - EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_10)); - EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_11)); - EXPECT_TRUE(entry->nh()->GetType() == NextHop::VLAN); - - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - //Packet to service interface, vrf has to be - //service vlan VRF - EXPECT_TRUE(rev_entry->data().vrf == service_vrf_id); - EXPECT_TRUE(rev_entry->data().dest_vrf == service_vrf_id); - EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_11)); - EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_10)); - sport++; - dport++; - } - - DelLink("virtual-machine-interface-routing-instance", "ser1", - "routing-instance", "service-vrf1"); - DelLink("virtual-machine-interface-routing-instance", "ser1", - "virtual-machine-interface", "vnet13"); - DeleteVmportEnv(input2, 1, true); - DelVrf("service-vrf1"); - client->WaitForIdle(); - EXPECT_TRUE(get_flow_proto()->FlowCount() == 0); - EXPECT_FALSE(VrfFind("vrf13")); - EXPECT_FALSE(VrfFind("service-vrf1")); -} - -TEST_F(EcmpTest, TrapFlag) { - AddRemoteVmRoute("vrf2", "10.1.1.1", 32, "vn2"); - TxIpPacket(VmPortGetId(1), "1.1.1.1", "10.1.1.1", 1); - client->WaitForIdle(); - - FlowEntry *entry = FlowGet(VrfGet("vrf2")->vrf_id(), - "1.1.1.1", "10.1.1.1", 1, 0, 0, GetFlowKeyNH(1)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - - //Reverse flow is no ECMP - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - - ComponentNHKeyList local_comp_nh; - AddRemoteEcmpRoute("vrf2", "10.1.1.1", 32, "vn2", 2, local_comp_nh); - client->WaitForIdle(); - EXPECT_TRUE(rev_entry->is_flags_set(FlowEntry::Trap) == true); - - TxIpPacket(VmPortGetId(1), "1.1.1.1", "10.1.1.1", 1); - client->WaitForIdle(); - - entry = FlowGet(VrfGet("vrf2")->vrf_id(), - "1.1.1.1", "10.1.1.1", 1, 0, 0, GetFlowKeyNH(1)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - - //Reverse flow is no ECMP - rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(rev_entry->is_flags_set(FlowEntry::Trap) == false); - Ip4Address remote_vm = Ip4Address::from_string("10.1.1.1"); - Agent::GetInstance()->fabric_inet4_unicast_table()->DeleteReq(bgp_peer, - "vrf2", remote_vm, 32, new ControllerVmRoute(bgp_peer)); - client->WaitForIdle(); -} - -//Send a packet from vgw to ecmp destination -TEST_F(EcmpTest, VgwFlag) { - Agent *agent = Agent::GetInstance(); - InetInterface::CreateReq(agent->interface_table(), "vgw1", - InetInterface::SIMPLE_GATEWAY, "vrf2", - Ip4Address(0), 0, Ip4Address(0), Agent::NullString(), - "", Interface::TRANSPORT_ETHERNET); - client->WaitForIdle(); - - InetInterfaceKey *intf_key = new InetInterfaceKey("vgw1"); - InetInterface *intf = InetInterfaceGet("vgw1"); - std::auto_ptr nh_key(new InterfaceNHKey(intf_key, false, - InterfaceNHFlags::INET4, - intf->mac())); - - Ip4Address remote_server_ip1 = Ip4Address::from_string("10.10.10.100"); - ComponentNHKeyPtr nh_data1(new ComponentNHKey(16, nh_key)); - ComponentNHKeyPtr nh_data2(new ComponentNHKey(20, agent->fabric_vrf_name(), - agent->router_id(), - remote_server_ip1, - false, - TunnelType::DefaultType())); - Ip4Address ip = Ip4Address::from_string("0.0.0.0"); - ComponentNHKeyList comp_nh_list; - comp_nh_list.push_back(nh_data1); - comp_nh_list.push_back(nh_data2); - EcmpTunnelRouteAdd(bgp_peer, "vrf2", ip, 0, - comp_nh_list, false, "vn2", - SecurityGroupList(), PathPreference()); - client->WaitForIdle(); - - //Send packet on vgw interface - TxIpPacket(intf->id(), "100.1.1.1", "2.2.2.2", 1); - client->WaitForIdle(); - - FlowEntry *entry; - FlowEntry *rev_entry; - entry = FlowGet(VrfGet("vrf2")->vrf_id(), - "2.2.2.2", "100.1.1.1", 1, 0, 0, intf->flow_key_nh()->id()); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - - rev_entry = FlowGet(VrfGet("vrf2")->vrf_id(), - "100.1.1.1", "2.2.2.2", 1, 0, 0, intf->flow_key_nh()->id()); - EXPECT_TRUE(rev_entry != NULL); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - - client->WaitForIdle(); - agent->fabric_inet4_unicast_table()->DeleteReq(bgp_peer, "vrf2", - ip, 0, new ControllerVmRoute(bgp_peer)); - InetInterface::DeleteReq(agent->interface_table(), "vgw1"); - client->WaitForIdle(); - WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); -} - -int main(int argc, char *argv[]) { - GETUSERARGS(); - client = TestInit(init_file, ksync_init, true, true, true, 100*1000); - int ret = RUN_ALL_TESTS(); - client->WaitForIdle(); - TestShutdown(); - delete client; - return ret; -} diff --git a/src/vnsw/agent/pkt/test/test_ecmp.h b/src/vnsw/agent/pkt/test/test_ecmp.h new file mode 100644 index 00000000000..98a61276c99 --- /dev/null +++ b/src/vnsw/agent/pkt/test/test_ecmp.h @@ -0,0 +1,316 @@ +#ifndef _VNSW_AGENT_PKT_TEST_TEST_ECMP_H__ +#define _VNSW_AGENT_PKT_TEST_TEST_ECMP_H__ +/* + * Copyright (c) 2017 Juniper Networks, Inc. All rights reserved. + */ + +#include "base/os.h" +#include "test/test_cmn_util.h" +#include "test_pkt_util.h" +#include "pkt/flow_proto.h" +#include + +// Number of interfaces in base class +#define VMI_MAX_COUNT 256 +#define REMOTE_COMPUTE_1 "100.100.100.1" +#define REMOTE_COMPUTE_2 "100.100.100.2" +#define REMOTE_COMPUTE_3 "100.100.100.3" + +#define REMOTE_NON_ECMP_1 "1.1.1.101" +#define REMOTE_NON_ECMP_2 "1.1.1.102" + +// ECMP Route with remote members only +#define REMOTE_ECMP_IP_1 "1.1.1.40" + +IpamInfo ipam_info_1[] = { + {"1.1.1.0", 24, "1.1.1.254"}, +}; + +// A non-ecmp port +struct PortInfo input1[] = { + {"vif1", 1, "1.1.1.1", "00:01:01:01:01:01", 1, 1}, + {"vif2", 2, "1.1.1.2", "00:01:01:01:01:02", 1, 1}, +}; + +// ECMP Ports-1 - All members are local +struct PortInfo input10[] = { + {"vif11", 11, "1.1.1.10", "00:01:01:01:01:11", 1, 11}, + {"vif12", 12, "1.1.1.10", "00:01:01:01:01:12", 1, 12}, + {"vif13", 13, "1.1.1.10", "00:01:01:01:01:13", 1, 13}, +}; + +// ECMP Ports-2 - All members are local +struct PortInfo input11[] = { + {"vif21", 21, "1.1.1.20", "00:01:01:01:01:21", 1, 21}, + {"vif22", 22, "1.1.1.20", "00:01:01:01:01:22", 1, 22}, + {"vif23", 23, "1.1.1.20", "00:01:01:01:01:23", 1, 23}, +}; + +// ECMP Ports-3 - Has both local and remote members +struct PortInfo input13[] = { + {"vif31", 31, "1.1.1.30", "00:01:01:01:01:31", 1, 31}, + {"vif32", 32, "1.1.1.30", "00:01:01:01:01:32", 1, 32}, + {"vif33", 33, "1.1.1.30", "00:01:01:01:01:33", 1, 33}, +}; + +IpamInfo ipam_info_2[] = { + {"2.1.1.0", 24, "2.1.1.254"}, +}; + +struct PortInfo input21[] = { + {"vif101", 101, "2.1.1.1", "00:02:01:01:01:01", 2, 101}, +}; + +struct PortInfo input22[] = { + {"vif111", 111, "2.1.1.10", "00:02:01:01:01:11", 2, 111}, + {"vif112", 112, "2.1.1.10", "00:02:01:01:01:12", 2, 112}, + {"vif113", 113, "2.1.1.10", "00:02:01:01:01:13", 2, 113}, +}; + +class EcmpTest : public ::testing::Test { +public: + EcmpTest() : + agent_(Agent::GetInstance()), + flow_proto_(agent_->pkt()->get_flow_proto()) { + } + virtual ~EcmpTest() { } + + virtual void SetUp() { + boost::system::error_code ec; + bgp_peer_ = CreateBgpPeer(Ip4Address::from_string("0.0.0.1", ec), + "xmpp channel"); + client->WaitForIdle(); + + // Get ethernet interface id + eth_intf_id_ = EthInterfaceGet("vnet0")->id(); + + CreateVmportEnv(input1, 2); + CreateVmportWithEcmp(input10, 3); + CreateVmportWithEcmp(input11, 3); + CreateVmportWithEcmp(input13, 3); + AddIPAM("vn1", ipam_info_1, 1); + client->WaitForIdle(); + + // Add remote non-ECMP route + Inet4TunnelRouteAdd(bgp_peer_, "vrf1", + Ip4Address::from_string(REMOTE_NON_ECMP_1), 32, + Ip4Address::from_string(REMOTE_COMPUTE_1), + TunnelType::AllType(), + 16, "vn1", SecurityGroupList(), PathPreference()); + Inet4TunnelRouteAdd(bgp_peer_, "vrf1", + Ip4Address::from_string(REMOTE_NON_ECMP_2), 32, + Ip4Address::from_string(REMOTE_COMPUTE_1), + TunnelType::AllType(), + 17, "vn1", SecurityGroupList(), PathPreference()); + client->WaitForIdle(); + + // Add ECMP Route members to 1.1.1.30. It has both local and remote + // members + InetUnicastRouteEntry *rt = + RouteGet("vrf1", Ip4Address::from_string("1.1.1.30"), 32); + EXPECT_TRUE(rt != NULL); + const CompositeNH *composite_nh = static_cast + (rt->GetActiveNextHop()); + ComponentNHKeyList comp_nh_list = composite_nh->component_nh_key_list(); + AddRemoteEcmpRoute("vrf1", "1.1.1.30", 32, "vn1", 3, comp_nh_list); + + // Add ECMP Route members to 1.1.1.40. It has both only remote members + AddRemoteEcmpRoute("vrf1", REMOTE_ECMP_IP_1, 32, "vn1", 3, + ComponentNHKeyList()); + + FlowStatsTimerStartStop(agent_, true); + GetInfo(); + } + + virtual void TearDown() { + FlushFlowTable(); + client->WaitForIdle(); + WAIT_FOR(1000, 1000, (flow_proto_->FlowCount() == 0)); + + DeleteRemoteRoute("vrf1", REMOTE_NON_ECMP_1, 32); + DeleteRemoteRoute("vrf1", REMOTE_NON_ECMP_2, 32); + DeleteRemoteRoute("vrf1", "1.1.1.30", 32); + DeleteRemoteRoute("vrf1", REMOTE_ECMP_IP_1, 32); + client->WaitForIdle(); + + DeleteVmportEnv(input1, 2, false); + DeleteVmportEnv(input10, 3, false); + DeleteVmportEnv(input11, 3, false); + DeleteVmportEnv(input13, 3, true); + client->WaitForIdle(); + + DelIPAM("vn1"); + client->WaitForIdle(); + + DeleteBgpPeer(bgp_peer_); + FlowStatsTimerStartStop(agent_, false); + WAIT_FOR(1000, 1000, (agent_->vrf_table()->Size() == 1)); + } + + void GetInfo() { + for (uint32_t i = 1; i <= 256; i++) { + vmi_[i] = VmInterfaceGet(i); + if (vmi_[i]) + EXPECT_TRUE(VmPortActive(i)); + } + } + + void FlushFlowTable() { + client->EnqueueFlowFlush(); + client->WaitForIdle(); + EXPECT_EQ(0U, flow_proto_->FlowCount()); + } + + uint32_t GetServiceVlanNH(uint32_t intf_id, + const std::string &vrf_name) const { + const VmInterface *vm_intf = VmInterfaceGet(intf_id); + const VrfEntry *vrf = VrfGet(vrf_name.c_str()); + uint32_t label = vm_intf->GetServiceVlanLabel(vrf); + return agent_->mpls_table()->FindMplsLabel(label)->nexthop()->id(); + } + + void AddRemoteEcmpRoute(const std::string &vrf_name, const std::string &ip, + uint32_t plen, const std::string &vn, int count, + const ComponentNHKeyList &local_list, + bool same_label = false) { + //If there is a local route, include that always + Ip4Address vm_ip = Ip4Address::from_string(ip); + ComponentNHKeyList comp_nh_list = local_list; + int remote_server_ip = 0x0A0A0A0A; + int label = 16; + SecurityGroupList sg_id_list; + for(int i = 0; i < count; i++) { + ComponentNHKeyPtr comp_nh + (new ComponentNHKey(label, agent_->fabric_vrf_name(), + agent_->router_id(), + Ip4Address(remote_server_ip++), false, + TunnelType::GREType())); + comp_nh_list.push_back(comp_nh); + if (!same_label) { + label++; + } + } + EcmpTunnelRouteAdd(bgp_peer_, vrf_name, vm_ip, plen, comp_nh_list, -1, + vn, sg_id_list, PathPreference()); + } + + void AddLocalVmRoute(const std::string &vrf_name, const std::string &ip, + uint32_t plen, const std::string &vn, + uint32_t intf_uuid) { + const VmInterface *vm_intf = + static_cast (VmPortGet(intf_uuid)); + VmInterfaceKey intf_key(AgentKey::ADD_DEL_CHANGE, MakeUuid(intf_uuid), + ""); + VnListType vn_list; + vn_list.insert(vn); + LocalVmRoute *local_vm_rt = + new LocalVmRoute(intf_key, vm_intf->label(), + VxLanTable::kInvalidvxlan_id, false, vn_list, + InterfaceNHFlags::INET4, SecurityGroupList(), + CommunityList(), PathPreference(), Ip4Address(0), + EcmpLoadBalance(), false, false, + bgp_peer_->sequence_number(), false); + InetUnicastAgentRouteTable *rt_table = + agent_->vrf_table()->GetInet4UnicastRouteTable(vrf_name); + + rt_table->AddLocalVmRouteReq(bgp_peer_, vrf_name, + Ip4Address::from_string(ip), plen, + static_cast(local_vm_rt)); + } + + void AddRemoteVmRoute(const std::string &vrf_name, const std::string &ip, + uint32_t plen, const std::string &vn) { + const Ip4Address addr = Ip4Address::from_string(ip); + VnListType vn_list; + vn_list.insert(vn); + ControllerVmRoute *data = ControllerVmRoute::MakeControllerVmRoute + (bgp_peer_, agent_->fabric_vrf_name(), agent_->router_id(), + vrf_name, addr, TunnelType::GREType(), 16, vn_list, + SecurityGroupList(), PathPreference(), false, EcmpLoadBalance(), + false); + InetUnicastAgentRouteTable::AddRemoteVmRouteReq(bgp_peer_, vrf_name, + addr, plen, data); + } + + void DeleteRemoteRoute(const std::string &vrf_name, const std::string &ip, + uint32_t plen) { + Ip4Address server_ip = Ip4Address::from_string(ip); + agent_->fabric_inet4_unicast_table()->DeleteReq + (bgp_peer_, vrf_name, server_ip, plen, + new ControllerVmRoute(bgp_peer_)); + } + + // Get VMI for the reverse flow + const VmInterface *GetOutVmi(const FlowEntry *flow) { + const VrfEntry *vrf = VrfGet(flow->data().flow_dest_vrf); + const AgentRoute *rt = + FlowEntry::GetUcRoute(vrf, flow->key().dst_addr); + const NextHop *nh = rt->GetActiveNextHop(); + + if (dynamic_cast(nh)) { + const CompositeNH *cnh = dynamic_cast(nh); + nh = cnh->GetNH(flow->GetEcmpIndex()); + } + + if (dynamic_cast(nh)) { + const InterfaceNH *intf_nh = dynamic_cast(nh); + return dynamic_cast(intf_nh->GetInterface()); + } + + return NULL; + } + + // Get outgoing NH for a ECMP flow + const NextHop *GetOutMemberNh(const FlowEntry *flow) { + const VrfEntry *vrf = VrfGet(flow->data().flow_dest_vrf); + const AgentRoute *rt = + FlowEntry::GetUcRoute(vrf, flow->key().dst_addr); + const NextHop *nh = rt->GetActiveNextHop(); + + if (dynamic_cast(nh)) { + const CompositeNH *cnh = dynamic_cast(nh); + return cnh->GetNH(flow->GetEcmpIndex()); + } + + return nh; + } + + // Get outgoing NH for flow + const NextHop *GetOutNh(const FlowEntry *flow) { + const VrfEntry *vrf = VrfGet(flow->data().flow_dest_vrf); + const AgentRoute *rt = + FlowEntry::GetUcRoute(vrf, flow->key().dst_addr); + return rt->GetActiveNextHop(); + } + + // Get RPF-NH when it is supposed to be local composite-NH + const NextHop *GetLocalCompRpfNh(const FlowEntry *flow) { + const FlowEntry *rflow = flow->reverse_flow_entry(); + const VrfEntry *vrf = VrfGet(flow->data().flow_dest_vrf); + const InetUnicastRouteEntry *rt = + dynamic_cast + (FlowEntry::GetUcRoute(vrf, rflow->key().dst_addr)); + return rt->GetLocalNextHop(); + } + + // Leak route for ip in vrf1 to vrf2 by re-ordering of ECMP components + void LeakRoute(const char *vrf1, const char *ip, const char *vrf2) { + InetUnicastRouteEntry *rt = + RouteGet(vrf1, Ip4Address::from_string(ip), 32); + EXPECT_TRUE(rt != NULL); + const CompositeNH *composite_nh = + static_cast(rt->GetActiveNextHop()); + ComponentNHKeyList comp_nh_list = composite_nh->component_nh_key_list(); + std::reverse(comp_nh_list.begin(), comp_nh_list.end()); + AddRemoteEcmpRoute(vrf2, ip, 32, rt->dest_vn_name(), 0, comp_nh_list); + } + + // Leak route for 2.1.1.10 into vrf-1. But with changed order of components +protected: + Agent *agent_; + FlowProto *flow_proto_; + uint32_t eth_intf_id_; + BgpPeer *bgp_peer_; + VmInterface *vmi_[VMI_MAX_COUNT]; +}; +#endif // _VNSW_AGENT_PKT_TEST_TEST_ECMP_H__ diff --git a/src/vnsw/agent/pkt/test/test_ecmp_fip.cc b/src/vnsw/agent/pkt/test/test_ecmp_fip.cc new file mode 100644 index 00000000000..f5660657e70 --- /dev/null +++ b/src/vnsw/agent/pkt/test/test_ecmp_fip.cc @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved. + */ +#include "base/os.h" +#include "test/test_cmn_util.h" +#include "test_pkt_util.h" +#include "test_ecmp.h" +#include "pkt/flow_proto.h" + +#define FIP_NON_ECMP_1 "2.1.1.5" +#define FIP_ECMP_1 "2.1.1.20" +#define FIP_REMOTE_ECMP_1 "2.1.1.40" +#define FIP_REMOTE_NON_ECMP_1 "2.1.1.101" + +class EcmpFipTest : public EcmpTest { +public: + EcmpFipTest() : EcmpTest() { } + virtual ~EcmpFipTest() { } + + virtual void SetUp() { + EcmpTest::SetUp(); + AddIPAM("vn2", ipam_info_2, 1); + CreateVmportEnv(input21, 1); + CreateVmportWithEcmp(input22, 3); + client->WaitForIdle(); + + // Configure floating-ip + AddFloatingIpPool("fip-pool20", 20); + AddFloatingIp("fip20", 1, FIP_ECMP_1); + AddLink("floating-ip", "fip20", "floating-ip-pool", "fip-pool20"); + AddLink("floating-ip-pool", "fip-pool20", "virtual-network", "vn2"); + + AddLink("virtual-machine-interface", "vif21", "floating-ip", "fip20"); + AddLink("virtual-machine-interface", "vif22", "floating-ip", "fip20"); + AddLink("virtual-machine-interface", "vif23", "floating-ip", "fip20"); + + AddFloatingIp("fip21", 1, FIP_NON_ECMP_1); + AddLink("floating-ip", "fip21", "floating-ip-pool", "fip-pool20"); + AddLink("virtual-machine-interface", "vif2", "floating-ip", "fip21"); + client->WaitForIdle(); + + // Add ECMP Route members to FIP_REMOTE_ECMP_1 + AddRemoteEcmpRoute("vrf2", FIP_REMOTE_ECMP_1, 32, "vn2", 3, + ComponentNHKeyList()); + + Inet4TunnelRouteAdd(bgp_peer_, "vrf2", + Ip4Address::from_string(FIP_REMOTE_NON_ECMP_1), 32, + Ip4Address::from_string(REMOTE_COMPUTE_1), + TunnelType::AllType(), + 16, "vn2", SecurityGroupList(), PathPreference()); + client->WaitForIdle(); + GetInfo(); + } + + virtual void TearDown() { + DelLink("virtual-machine-interface", "vif21", "floating-ip", "fip20"); + DelLink("virtual-machine-interface", "vif22", "floating-ip", "fip20"); + DelLink("virtual-machine-interface", "vif23", "floating-ip", "fip20"); + + // Delete floating-ip + DelLink("floating-ip", "fip20", "floating-ip-pool", "fip-pool20"); + DelLink("floating-ip-pool", "fip-pool20", "virtual-network", "vn2"); + DelFloatingIpPool("fip-pool20"); + DelFloatingIp("fip20"); + + // Add ECMP Route members to FIP_REMOTE_ECMP_1 + DeleteRemoteRoute("vrf2", FIP_REMOTE_ECMP_1, 32); + DeleteRemoteRoute("vrf2", FIP_REMOTE_NON_ECMP_1, 32); + + DeleteVmportEnv(input21, 1, false); + DeleteVmportEnv(input22, 3, true); + DelIPAM("vn2"); + client->WaitForIdle(); + + EcmpTest::TearDown(); + } +}; + +// Ping from non-ecmp to ECMP address in vrf2. +// Source has FIP +// Local flow +TEST_F(EcmpFipTest, Local_Src_Fip_NonEcmpToEcmp_1) { + for (int i = 0; i < 16; i++) { + TxTcpPacket(VmPortGetId(2), "1.1.1.2", "2.1.1.10", 1000, i, false); + client->WaitForIdle(); + + FlowEntry *flow = FlowGet(GetVrfId("vrf1"), "1.1.1.2", "2.1.1.10", + 6, 1000, i, GetFlowKeyNH(2)); + EXPECT_TRUE(flow); + EXPECT_TRUE(flow->data().component_nh_idx + != CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[2]->flow_key_nh()); + + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + const VmInterface *out_vmi = GetOutVmi(flow); + EXPECT_TRUE(rflow->rpf_nh() == out_vmi->flow_key_nh()); + } +} + +// Ping from non-ecmp to ECMP address in vrf2. +// Source has FIP +// Flow from VMI to fabric +TEST_F(EcmpFipTest, Remote_Src_Fip_NonEcmpToEcmp_1) { + for (int i = 0; i < 16; i++) { + TxTcpPacket(VmPortGetId(2), "1.1.1.2", FIP_REMOTE_ECMP_1, 1000, i, + false); + client->WaitForIdle(); + + FlowEntry *flow = + FlowGet(GetVrfId("vrf1"), "1.1.1.2", FIP_REMOTE_ECMP_1, 6, 1000, i, + GetFlowKeyNH(2)); + EXPECT_TRUE(flow); + EXPECT_TRUE(flow->data().component_nh_idx + != CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[2]->flow_key_nh()); + + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + InetUnicastRouteEntry *rt = + RouteGet("vrf2", Ip4Address::from_string(FIP_REMOTE_ECMP_1), 32); + EXPECT_TRUE(rflow->rpf_nh() == rt->GetActiveNextHop()); + } +} + +// Ping from ecmp member in vrf1 to non-ECMP address in vrf2. +// Source has FIP +// Local flow +TEST_F(EcmpFipTest, Local_Src_Fip_EcmpToNonEcmp_1) { + TxIpPacket(VmPortGetId(21), "1.1.1.20", "2.1.1.1", 1); + client->WaitForIdle(); + + FlowEntry *flow = FlowGet(GetVrfId("vrf1"), "1.1.1.20", "2.1.1.1", + 1, 0, 0, GetFlowKeyNH(21)); + EXPECT_TRUE(flow); + EXPECT_TRUE(flow->data().component_nh_idx + == CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[21]->flow_key_nh()); + EXPECT_TRUE(flow->IsNatFlow()); + + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rflow->rpf_nh() == vmi_[101]->flow_key_nh()); + FlushFlowTable(); + client->WaitForIdle(); + + // Repeat for vmi-22 + TxIpPacket(VmPortGetId(22), "1.1.1.20", "2.1.1.1", 1); + client->WaitForIdle(); + + flow = FlowGet(GetVrfId("vrf1"), "1.1.1.20", "2.1.1.1", 1, 0, 0, + GetFlowKeyNH(22)); + EXPECT_TRUE(flow); + EXPECT_TRUE(flow->data().component_nh_idx + == CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[22]->flow_key_nh()); + EXPECT_TRUE(flow->IsNatFlow()); + + rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rflow->rpf_nh() == vmi_[101]->flow_key_nh()); + FlushFlowTable(); + client->WaitForIdle(); + + // Repeat for vmi-23 + TxIpPacket(VmPortGetId(23), "1.1.1.20", "2.1.1.1", 1); + client->WaitForIdle(); + + flow = FlowGet(GetVrfId("vrf1"), "1.1.1.20", "2.1.1.1", 1, 0, 0, + GetFlowKeyNH(23)); + EXPECT_TRUE(flow); + EXPECT_TRUE(flow->data().component_nh_idx + == CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[23]->flow_key_nh()); + EXPECT_TRUE(flow->IsNatFlow()); + + rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rflow->rpf_nh() == vmi_[101]->flow_key_nh()); +} + +// Ping from ecmp member in vrf1 to non-ECMP address in vrf2. +// Source has FIP +// Flow from VMI to fabric +TEST_F(EcmpFipTest, Remote_Src_Fip_EcmpToNonEcmp_1) { + TxIpPacket(VmPortGetId(21), "1.1.1.20", FIP_REMOTE_NON_ECMP_1, 1); + client->WaitForIdle(); + + FlowEntry *flow = FlowGet(GetVrfId("vrf1"), "1.1.1.20", + FIP_REMOTE_NON_ECMP_1, 1, 0, 0, GetFlowKeyNH(21)); + EXPECT_TRUE(flow); + EXPECT_TRUE(flow->data().component_nh_idx + == CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[21]->flow_key_nh()); + EXPECT_TRUE(flow->IsNatFlow()); + + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + const TunnelNH *tnh = NULL; + tnh = dynamic_cast(rflow->rpf_nh()); + EXPECT_TRUE(tnh != NULL); + if (tnh) { + EXPECT_TRUE(*tnh->GetDip() == Ip4Address::from_string(REMOTE_COMPUTE_1)); + } + + FlushFlowTable(); + client->WaitForIdle(); + + // Repeat for vmi-22 + TxIpPacket(VmPortGetId(22), "1.1.1.20", FIP_REMOTE_NON_ECMP_1, 1); + client->WaitForIdle(); + + flow = FlowGet(GetVrfId("vrf1"), "1.1.1.20", FIP_REMOTE_NON_ECMP_1, 1, 0, 0, + GetFlowKeyNH(22)); + EXPECT_TRUE(flow); + EXPECT_TRUE(flow->data().component_nh_idx + == CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[22]->flow_key_nh()); + EXPECT_TRUE(flow->IsNatFlow()); + + rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + tnh = dynamic_cast(rflow->rpf_nh()); + EXPECT_TRUE(tnh != NULL); + if (tnh) { + EXPECT_TRUE(*tnh->GetDip() == Ip4Address::from_string(REMOTE_COMPUTE_1)); + } + + FlushFlowTable(); + client->WaitForIdle(); + + // Repeat for vmi-23 + TxIpPacket(VmPortGetId(23), "1.1.1.20", FIP_REMOTE_NON_ECMP_1, 1); + client->WaitForIdle(); + + flow = FlowGet(GetVrfId("vrf1"), "1.1.1.20", FIP_REMOTE_NON_ECMP_1, 1, 0, 0, + GetFlowKeyNH(23)); + EXPECT_TRUE(flow); + EXPECT_TRUE(flow->data().component_nh_idx + == CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[23]->flow_key_nh()); + EXPECT_TRUE(flow->IsNatFlow()); + + rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + tnh = dynamic_cast(rflow->rpf_nh()); + EXPECT_TRUE(tnh != NULL); + if (tnh) { + EXPECT_TRUE(*tnh->GetDip() == Ip4Address::from_string(REMOTE_COMPUTE_1)); + } + +} + +// VMI-23 is in vrf1 and has FIP in vrf2 +// Route for 3.1.1.1 leaked from vrf3 to vrf2. Order of members reversed +// Route for 2.1.1.20 leaked from vrf2 to vrf3. Order of members reversed +// Ping from 1.1.1.20 to 3.1.1.1 +// Local Flow +TEST_F(EcmpFipTest, Local_Src_Fip_EcmpToNonEcmp_3) { + // Add config for vrf3 + struct PortInfo input[] = { + {"vnet200", 200, "3.1.1.1", "00:03:01:01:01:01", 3, 200}, + {"vnet201", 201, "3.1.1.1", "00:03:01:01:01:02", 3, 201}, + {"vnet202", 202, "3.1.1.1", "00:03:01:01:01:03", 3, 202}, + }; + IpamInfo ipam_info_3[] = { + {"3.1.1.0", 24, "3.1.1.254"}, + }; + AddIPAM("vn3", ipam_info_3, 1); + CreateVmportWithEcmp(input, 3); + client->WaitForIdle(); + + // Leak route for 2.1.1.20 into vrf-3. But with changed order of components + LeakRoute("vrf2", "2.1.1.20", "vrf3"); + + // Leak route for 3.1.1.1 into vrf-2. But with changed order of components + LeakRoute("vrf3", "3.1.1.1", "vrf2"); + + // Tx packet from port-21. Must go thru src-fip + TxIpPacket(vmi_[21]->id(), "1.1.1.20", "3.1.1.1", 1); + client->WaitForIdle(); + + FlowEntry *flow = FlowGet(GetVrfId("vrf1"), "1.1.1.20", "3.1.1.1", + 1, 0, 0, GetFlowKeyNH(21)); + EXPECT_TRUE(flow != NULL); + EXPECT_TRUE(flow->data().component_nh_idx + != CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[21]->flow_key_nh()); + + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + const VmInterface *out_vmi = GetOutVmi(flow); + EXPECT_TRUE(rflow->rpf_nh() == out_vmi->flow_key_nh()); + FlushFlowTable(); + client->WaitForIdle(); + + // Repeat for port-22 + TxIpPacket(vmi_[22]->id(), "1.1.1.20", "3.1.1.1", 1); + client->WaitForIdle(); + + flow = FlowGet(GetVrfId("vrf1"), "1.1.1.20", "3.1.1.1", 1, 0, 0, + GetFlowKeyNH(22)); + EXPECT_TRUE(flow != NULL); + EXPECT_TRUE(flow->data().component_nh_idx + != CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[22]->flow_key_nh()); + + rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + out_vmi = GetOutVmi(flow); + EXPECT_TRUE(rflow->rpf_nh() == out_vmi->flow_key_nh()); + FlushFlowTable(); + client->WaitForIdle(); + + // Repeat for port-23 + TxIpPacket(vmi_[23]->id(), "1.1.1.20", "3.1.1.1", 1); + client->WaitForIdle(); + + flow = FlowGet(GetVrfId("vrf1"), "1.1.1.20", "3.1.1.1", 1, 0, 0, + GetFlowKeyNH(23)); + EXPECT_TRUE(flow != NULL); + EXPECT_TRUE(flow->data().component_nh_idx + != CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[23]->flow_key_nh()); + + rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + out_vmi = GetOutVmi(flow); + EXPECT_TRUE(rflow->rpf_nh() == out_vmi->flow_key_nh()); + FlushFlowTable(); + client->WaitForIdle(); + + DelIPAM("vn3"); + DeleteVmportEnv(input, 3, true); + client->WaitForIdle(); +} + +// Ping from non-ecmp IP address to address in ECMP. +// Dest has FIP +// Local Flow +TEST_F(EcmpFipTest, Local_Dst_Fip_NonEcmpToEcmp_1) { + TxIpPacket(VmPortGetId(101), "2.1.1.1", "2.1.1.20", 1); + client->WaitForIdle(); + + FlowEntry *flow = FlowGet(GetVrfId("vrf2"), "2.1.1.1", "2.1.1.20", + 1, 0, 0, GetFlowKeyNH(101)); + EXPECT_TRUE(flow); + EXPECT_TRUE(flow->data().component_nh_idx + != CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[101]->flow_key_nh()); + EXPECT_TRUE(flow->IsNatFlow()); + + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + const VmInterface *out_vmi = GetOutVmi(flow); + EXPECT_TRUE(rflow->rpf_nh() == out_vmi->flow_key_nh()); +} + +// Ping from non-ecmp to ECMP address. +// Destination has FIP +// Local flow +TEST_F(EcmpFipTest, Remote_Dst_Fip_EcmpToNonEcmp_1) { + TxIpMplsPacket(eth_intf_id_, REMOTE_COMPUTE_1, + agent_->router_id().to_string().c_str(), + vmi_[2]->label(), FIP_REMOTE_ECMP_1, FIP_NON_ECMP_1, 1); + client->WaitForIdle(); + + FlowEntry *flow = + FlowGet(GetVrfId("vrf1"), FIP_REMOTE_ECMP_1, FIP_NON_ECMP_1, 1, 0, 0, + GetFlowKeyNH(2)); + EXPECT_TRUE(flow); + EXPECT_TRUE(flow->data().component_nh_idx + == CompositeNH::kInvalidComponentNHIdx); + InetUnicastRouteEntry *rt = + RouteGet("vrf2", Ip4Address::from_string(FIP_REMOTE_ECMP_1), 32); + EXPECT_TRUE(flow->rpf_nh() == rt->GetActiveNextHop()); + + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rflow->rpf_nh() == vmi_[2]->flow_key_nh()); +} + +int main(int argc, char *argv[]) { + GETUSERARGS(); + client = TestInit(init_file, ksync_init, true, true, true, 100*1000); + int ret = RUN_ALL_TESTS(); + client->WaitForIdle(); + TestShutdown(); + delete client; + return ret; +} diff --git a/src/vnsw/agent/pkt/test/test_ecmp_local.cc b/src/vnsw/agent/pkt/test/test_ecmp_local.cc index b1750e6c337..4dd2c3146c8 100644 --- a/src/vnsw/agent/pkt/test/test_ecmp_local.cc +++ b/src/vnsw/agent/pkt/test/test_ecmp_local.cc @@ -4,207 +4,524 @@ #include "base/os.h" #include "test/test_cmn_util.h" #include "test_pkt_util.h" +#include "test_ecmp.h" #include "pkt/flow_proto.h" -#define AGE_TIME 10*1000 - -void RouterIdDepInit(Agent *agent) { -} - -struct PortInfo input1[] = { - {"vnet1", 1, "1.1.1.1", "00:00:00:01:01:01", 1, 1}, - {"vnet2", 2, "1.1.1.1", "00:00:00:01:01:01", 1, 2} -}; -IpamInfo ipam_info[] = { - {"1.1.1.0", 24, "1.1.1.10"}, -}; +class LocalEcmpTest : public EcmpTest { +public: + LocalEcmpTest() : EcmpTest() { } + virtual ~LocalEcmpTest() { } -class EcmpTest : public ::testing::Test { virtual void SetUp() { - agent_ = Agent::GetInstance(); - - boost::system::error_code ec; - bgp_peer = CreateBgpPeer(Ip4Address::from_string("0.0.0.1", ec), - "xmpp channel"); - client->WaitForIdle(); - - flow_proto_ = agent_->pkt()->get_flow_proto(); - CreateVmportWithEcmp(input1, 2); - client->WaitForIdle(); - AddIPAM("vn1", ipam_info, 1); - client->WaitForIdle(); - AddVn("vn2", 2); - AddVrf("vrf2"); - client->WaitForIdle(); - strcpy(router_id, agent_->router_id().to_string().c_str()); - strcpy(MX_0, "100.1.1.1"); - strcpy(MX_1, "100.1.1.2"); - strcpy(MX_2, "100.1.1.3"); - strcpy(MX_3, "100.1.1.4"); - - const VmInterface *vmi = static_cast(VmPortGet(1)); - vm1_label = vmi->label(); - vmi = static_cast(VmPortGet(2)); - vm2_label = vmi->label(); - eth_intf_id = EthInterfaceGet("vnet0")->id(); + EcmpTest::SetUp(); } virtual void TearDown() { - DeleteVmportEnv(input1, 2, true); - client->WaitForIdle(); - DelIPAM("vn1"); - client->WaitForIdle(); - DelVn("vn2"); - DelVrf("vrf2"); - client->WaitForIdle(); - DeleteBgpPeer(bgp_peer); - client->WaitForIdle(); - EXPECT_FALSE(VrfFind("vrf1", true)); - EXPECT_FALSE(VrfFind("vrf2", true)); - WAIT_FOR(1000, 1000, (flow_proto_->FlowCount() == 0)); + EcmpTest::TearDown(); + } +}; + +// Ping from non-ECMP to ECMP +// All members of dest-ecmp on same host +TEST_F(LocalEcmpTest, NonEcmpToLocalEcmp_1) { + for (int i = 0; i < 16; i++) { + TxTcpPacket(vmi_[1]->id(), "1.1.1.1", "1.1.1.10", 100, i, false); client->WaitForIdle(); + + FlowEntry *flow = FlowGet(GetVrfId("vrf1"), "1.1.1.1", "1.1.1.10", + 6, 100, i, GetFlowKeyNH(1)); + EXPECT_TRUE(flow != NULL); + EXPECT_TRUE(flow->data().component_nh_idx + != CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[1]->flow_key_nh()); + + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + const VmInterface *out_vmi = GetOutVmi(flow); + EXPECT_TRUE(rflow->rpf_nh() == out_vmi->flow_key_nh()); } -public: - void AddRemoteEcmpRoute(const string vrf_name, const string ip, - uint32_t plen, const string vn, int count, bool reverse = false, - bool same_label = false) { - //If there is a local route, include that always - Ip4Address vm_ip = Ip4Address::from_string(ip); - - ComponentNHKeyList comp_nh_list; - int remote_server_ip = 0x64010101; - int label = 16; - SecurityGroupList sg_id_list; - - for(int i = 0; i < count; i++) { - ComponentNHKeyPtr comp_nh(new ComponentNHKey( - label, Agent::GetInstance()->fabric_vrf_name(), - Agent::GetInstance()->router_id(), - Ip4Address(remote_server_ip++), - false, TunnelType::AllType())); - comp_nh_list.push_back(comp_nh); - if (!same_label) { - label++; - } - } - if (reverse) { - std::reverse(comp_nh_list.begin(), comp_nh_list.end()); - } +} - EcmpTunnelRouteAdd(bgp_peer, vrf_name, vm_ip, plen, - comp_nh_list, -1, vn, sg_id_list, - PathPreference()); +// Ping from non-ECMP to ECMP +// ECMP has both local and remote members +TEST_F(LocalEcmpTest, NonEcmpToHybridEcmp_1) { + int local_count = 0; + int remote_count = 0; + for (int i = 0; i < 16; i++) { + TxTcpPacket(vmi_[1]->id(), "1.1.1.1", "1.1.1.30", 100, i, false); + client->WaitForIdle(); + + FlowEntry *flow = FlowGet(GetVrfId("vrf1"), "1.1.1.1", "1.1.1.30", + 6, 100, i, GetFlowKeyNH(1)); + EXPECT_TRUE(flow != NULL); + EXPECT_TRUE(flow->data().component_nh_idx + != CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[1]->flow_key_nh()); + + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + + const NextHop *nh = GetOutMemberNh(flow); + if (dynamic_cast(nh)) { + local_count++; + const VmInterface *out_vmi = GetOutVmi(flow); + EXPECT_TRUE(rflow->rpf_nh() == out_vmi->flow_key_nh()); + } else { + remote_count++; + // RPF in reverse flow points to composite-nh + EXPECT_TRUE(rflow->rpf_nh() == GetOutNh(flow)); + } } - FlowProto *get_flow_proto() const { return flow_proto_; } - Agent *agent_; - BgpPeer *bgp_peer; - FlowProto *flow_proto_; - AgentXmppChannel *channel; - char router_id[80]; - char MX_0[80]; - char MX_1[80]; - char MX_2[80]; - char MX_3[80]; - int vm1_label; - int vm2_label; - int agg_label; - int eth_intf_id; -}; + EXPECT_TRUE(local_count > 0); + EXPECT_TRUE(remote_count > 0); +} + +// Ping from non-ECMP to ECMP +// All members of dest-ecmp on same host +// ECMP transitions to Non-ECMP +TEST_F(LocalEcmpTest, NonEcmpToLocalEcmp_EcmpTransition_1) { + struct PortInfo input10_1[] = { + {"vif11", 11, "1.1.1.10", "00:01:01:01:01:11", 1, 11}, + }; + struct PortInfo input10_2[] = { + {"vif12", 12, "1.1.1.10", "00:01:01:01:01:12", 1, 12}, + }; + struct PortInfo input10_3[] = { + {"vif13", 13, "1.1.1.10", "00:01:01:01:01:13", 1, 13}, + }; + + // Delete vif13 as tests expects only 2 member ECMP + DeleteVmportEnv(input10_3, 1, false); + + TxIpPacket(vmi_[1]->id(), "1.1.1.1", "1.1.1.10", 1); + client->WaitForIdle(); + + FlowEntry *flow = FlowGet(GetVrfId("vrf1"), "1.1.1.1", "1.1.1.10", + 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(flow != NULL); + EXPECT_TRUE(flow->data().component_nh_idx + != CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[1]->flow_key_nh()); + + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + const VmInterface *out_vmi = GetOutVmi(flow); + EXPECT_TRUE(rflow->rpf_nh() == out_vmi->flow_key_nh()); + + // Delete interface other than one being used + if (out_vmi->name() == "vif11") + DeleteVmportEnv(input10_2, 1, false); + + if (out_vmi->name() == "vif12") + DeleteVmportEnv(input10_1, 1, false); + + flow = FlowGet(GetVrfId("vrf1"), "1.1.1.1", "1.1.1.10", 1, 0, 0, + GetFlowKeyNH(1)); + // Flow not ECMP anymore + EXPECT_TRUE(flow->data().component_nh_idx + == CompositeNH::kInvalidComponentNHIdx); + rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + out_vmi = GetOutVmi(flow); + EXPECT_TRUE(rflow->rpf_nh() == out_vmi->flow_key_nh()); +} + +// Ping from non-ECMP to non-ECMP +// Destination transitions from non-ECMP to ECMP +TEST_F(LocalEcmpTest, NonEcmpToLocalEcmp_EcmpTransition_2) { + struct PortInfo input10_1[] = { + {"vif11", 11, "1.1.1.10", "00:01:01:01:01:11", 1, 11}, + {"vif12", 12, "1.1.1.10", "00:01:01:01:01:12", 1, 12}, + }; + + // Delete vif11 and vif12 + DeleteVmportEnv(input10_1, 2, false); + + TxIpPacket(vmi_[1]->id(), "1.1.1.1", "1.1.1.10", 1); + client->WaitForIdle(); + + FlowEntry *flow = FlowGet(GetVrfId("vrf1"), "1.1.1.1", "1.1.1.10", + 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(flow != NULL); + EXPECT_TRUE(flow->data().component_nh_idx + == CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[1]->flow_key_nh()); + + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rflow->rpf_nh() == vmi_[13]->flow_key_nh()); + + // Restore 2 more interfaces in ECMP + CreateVmportWithEcmp(input10, 3); + client->WaitForIdle(); + + flow = FlowGet(GetVrfId("vrf1"), "1.1.1.1", "1.1.1.10", 1, 0, 0, + GetFlowKeyNH(1)); + EXPECT_TRUE(flow != NULL); + // Flow transitioned from Non-ECMP to ECMP. ecmp-index must be set to 0 + EXPECT_EQ(0, flow->data().component_nh_idx); + rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rflow->rpf_nh() == vmi_[13]->flow_key_nh()); +} + +// Ping from non-ECMP to ECMP +// All members of dest-ecmp on same host +// Delete interface being used in ECMP. Flow must be deleted +TEST_F(LocalEcmpTest, NonEcmpToLocalEcmp_EcmpDel_1) { + struct PortInfo input10_1[] = { + {"vif11", 11, "1.1.1.10", "00:01:01:01:01:11", 1, 11}, + }; + struct PortInfo input10_2[] = { + {"vif12", 12, "1.1.1.10", "00:01:01:01:01:12", 1, 12}, + }; + struct PortInfo input10_3[] = { + {"vif13", 13, "1.1.1.10", "00:01:01:01:01:13", 1, 13}, + }; + + TxIpPacket(vmi_[1]->id(), "1.1.1.1", "1.1.1.10", 1); + client->WaitForIdle(); + + FlowEntry *flow = FlowGet(GetVrfId("vrf1"), "1.1.1.1", "1.1.1.10", + 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(flow != NULL); + EXPECT_TRUE(flow->data().component_nh_idx + != CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[1]->flow_key_nh()); + + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + const VmInterface *out_vmi = GetOutVmi(flow); + EXPECT_TRUE(rflow->rpf_nh() == out_vmi->flow_key_nh()); + + // Delete interface other than one being used + if (out_vmi->name() == "vif11") + DeleteVmportEnv(input10_1, 1, false); + + if (out_vmi->name() == "vif12") + DeleteVmportEnv(input10_2, 1, false); + + if (out_vmi->name() == "vif13") + DeleteVmportEnv(input10_3, 1, false); + + EXPECT_EQ(0, flow_proto_->FlowCount()); +} + +// Ping from non-ECMP to ECMP +// All members of dest-ecmp on same host +// Delete interface not being used in ECMP. Flow must be revaluated +TEST_F(LocalEcmpTest, NonEcmpToLocalEcmp_EcmpDel_2) { + struct PortInfo input10_1[] = { + {"vif11", 11, "1.1.1.10", "00:01:01:01:01:11", 1, 11}, + }; + struct PortInfo input10_2[] = { + {"vif12", 12, "1.1.1.10", "00:01:01:01:01:12", 1, 12}, + }; + struct PortInfo input10_3[] = { + {"vif13", 13, "1.1.1.10", "00:01:01:01:01:13", 1, 13}, + }; + + TxIpPacket(vmi_[1]->id(), "1.1.1.1", "1.1.1.10", 1); + client->WaitForIdle(); + + FlowEntry *flow = FlowGet(GetVrfId("vrf1"), "1.1.1.1", "1.1.1.10", + 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(flow != NULL); + uint32_t ecmp_idx = flow->data().component_nh_idx; + EXPECT_TRUE(ecmp_idx != CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[1]->flow_key_nh()); + + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + const VmInterface *out_vmi = GetOutVmi(flow); + EXPECT_TRUE(rflow->rpf_nh() == out_vmi->flow_key_nh()); + + // Delete interface other than one being used + if (out_vmi->name() == "vif11") + DeleteVmportEnv(input10_2, 1, false); + + if (out_vmi->name() == "vif12") + DeleteVmportEnv(input10_3, 1, false); + + if (out_vmi->name() == "vif13") + DeleteVmportEnv(input10_1, 1, false); + + flow = FlowGet(GetVrfId("vrf1"), "1.1.1.1", "1.1.1.10", 1, 0, 0, + GetFlowKeyNH(1)); + // On revaluation component_nh_idx must be retained + EXPECT_EQ(ecmp_idx, flow->data().component_nh_idx); + EXPECT_TRUE(flow->IsEcmpFlow()); + rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + // out-vmi and rpf-nh must not change for reverse flow + EXPECT_TRUE(rflow->intf_entry() == out_vmi); + EXPECT_TRUE(rflow->rpf_nh() == out_vmi->flow_key_nh()); +} + +// Ping from Local only ECMP to non-ECMP +// Test repeated for all local members in ECMP +TEST_F(LocalEcmpTest, LocalEcmpToNonEcmp_1) { + TxIpPacket(vmi_[11]->id(), "1.1.1.10", "1.1.1.1", 1); + client->WaitForIdle(); + + FlowEntry *flow = FlowGet(GetVrfId("vrf1"), "1.1.1.10", "1.1.1.1", 1, 0, 0, + GetFlowKeyNH(11)); + EXPECT_TRUE(flow != NULL); + EXPECT_TRUE(flow->data().component_nh_idx + == CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[11]->flow_key_nh()); + + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rflow->rpf_nh() == vmi_[1]->flow_key_nh()); + FlushFlowTable(); + client->WaitForIdle(); + + // Repeat for vmi-12 + TxIpPacket(vmi_[12]->id(), "1.1.1.10", "1.1.1.1", 1); + client->WaitForIdle(); + + flow = FlowGet(GetVrfId("vrf1"), "1.1.1.10", "1.1.1.1", 1, 0, 0, + GetFlowKeyNH(12)); + EXPECT_TRUE(flow != NULL); + EXPECT_TRUE(flow->data().component_nh_idx + == CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[12]->flow_key_nh()); + + rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rflow->rpf_nh() == vmi_[1]->flow_key_nh()); + FlushFlowTable(); + client->WaitForIdle(); + + // Repeat for vmi-13 + TxIpPacket(vmi_[13]->id(), "1.1.1.10", "1.1.1.1", 1); + client->WaitForIdle(); + + flow = FlowGet(GetVrfId("vrf1"), "1.1.1.10", "1.1.1.1", 1, 0, 0, + GetFlowKeyNH(13)); + EXPECT_TRUE(flow != NULL); + EXPECT_TRUE(flow->data().component_nh_idx + == CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[13]->flow_key_nh()); + + rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rflow->rpf_nh() == vmi_[1]->flow_key_nh()); + FlushFlowTable(); + client->WaitForIdle(); +} + +// Ping from Hybrid ECMP to non-ECMP. +// Test repeated for all local members in ECMP +TEST_F(LocalEcmpTest, HybridEcmpToNonEcmp_1) { + TxIpPacket(vmi_[31]->id(), "1.1.1.30", "1.1.1.1", 1); + client->WaitForIdle(); -//Send packet from ECMP VM to ECMP MX -//Verify component index is set and correspnding -//rpf nexthop -TEST_F(EcmpTest, EcmpTest_1) { - AddRemoteEcmpRoute("vrf1", "0.0.0.0", 0, "vn1", 4); - AddRemoteEcmpRoute("vrf1", "1.1.1.1", 32, "vn1", 4); + FlowEntry *flow = FlowGet(GetVrfId("vrf1"), "1.1.1.30", "1.1.1.1", + 1, 0, 0, GetFlowKeyNH(31)); + EXPECT_TRUE(flow != NULL); + EXPECT_TRUE(flow->data().component_nh_idx + == CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[31]->flow_key_nh()); - TxIpPacket(VmPortGetId(1), "1.1.1.1", "2.1.1.1", 1); + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rflow->rpf_nh() == vmi_[1]->flow_key_nh()); + FlushFlowTable(); client->WaitForIdle(); - AgentRoute *rt = RouteGet("vrf1", Ip4Address::from_string("0.0.0.0"), 0); - InetUnicastRouteEntry *src_rt = static_cast( - RouteGet("vrf1", Ip4Address::from_string("1.1.1.1"), 32)); + // Repeat test for vmi-32 + TxIpPacket(vmi_[32]->id(), "1.1.1.30", "1.1.1.1", 1); + client->WaitForIdle(); - FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), - "1.1.1.1", "2.1.1.1", 1, 0, 0, GetFlowKeyNH(1)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->data().nh.get() == src_rt->GetLocalNextHop()); + flow = FlowGet(GetVrfId("vrf1"), "1.1.1.30", "1.1.1.1", 1, 0, 0, + GetFlowKeyNH(32)); + EXPECT_TRUE(flow != NULL); + EXPECT_TRUE(flow->data().component_nh_idx + == CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[32]->flow_key_nh()); - //Reverse flow is no ECMP - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rflow->rpf_nh() == vmi_[1]->flow_key_nh()); + FlushFlowTable(); + client->WaitForIdle(); - DeleteRoute("vrf1", "0.0.0.0", 0, bgp_peer); + // Repeat test for vmi-33 + TxIpPacket(vmi_[33]->id(), "1.1.1.30", "1.1.1.1", 1); client->WaitForIdle(); - sleep(1); + + flow = FlowGet(GetVrfId("vrf1"), "1.1.1.30", "1.1.1.1", 1, 0, 0, + GetFlowKeyNH(33)); + EXPECT_TRUE(flow != NULL); + EXPECT_TRUE(flow->data().component_nh_idx + == CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[33]->flow_key_nh()); + + rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rflow->rpf_nh() == vmi_[1]->flow_key_nh()); + FlushFlowTable(); client->WaitForIdle(); - WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); } -//Send packet from ECMP of AAP and verify ECMP index and -//RPF nexthop is set fine -TEST_F(EcmpTest, EcmpTest_2) { - Ip4Address ip = Ip4Address::from_string("1.1.1.10"); - std::string mac("0a:0b:0c:0d:0e:0f"); +// Ping from ECMP source to ECMP destination in same VRF +// RPF-NH in both flow must be set as interface-nh +TEST_F(LocalEcmpTest, EcmpToEcmp_1) { + TxIpPacket(vmi_[11]->id(), "1.1.1.10", "1.1.1.20", 1); + client->WaitForIdle(); - AddEcmpAap("vnet1", 1, ip, mac); - AddEcmpAap("vnet2", 2, ip, mac); - AddRemoteEcmpRoute("vrf1", "0.0.0.0", 0, "vn1", 4); + FlowEntry *flow = FlowGet(GetVrfId("vrf1"), "1.1.1.10", "1.1.1.20", + 1, 0, 0, GetFlowKeyNH(11)); + EXPECT_TRUE(flow != NULL); + EXPECT_TRUE(flow->data().component_nh_idx + != CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[11]->flow_key_nh()); + + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + const VmInterface *out_vmi = GetOutVmi(flow); + EXPECT_TRUE(rflow->rpf_nh() == out_vmi->flow_key_nh()); +} + +// Ping from ECMP source in vrf-1 to ECMP destination in vrf-2 +// RPF-NH in both flow must be set as interface-nh +TEST_F(LocalEcmpTest, EcmpToEcmp_2) { + AddIPAM("vn2", ipam_info_2, 1); + CreateVmportWithEcmp(input22, 3); client->WaitForIdle(); - TxIpPacket(VmPortGetId(1), "1.1.1.10", "2.1.1.1", 1); + // Leak route for 1.1.1.10 into vrf-2. But with changed order of components + LeakRoute("vrf1", "1.1.1.10", "vrf2"); + + // Leak route for 2.1.1.10 into vrf-1. But with changed order of components + LeakRoute("vrf2", "2.1.1.10", "vrf1"); + client->WaitForIdle(); + + // Tx packet from port-11 + TxIpPacket(vmi_[11]->id(), "1.1.1.10", "2.1.1.10", 1); client->WaitForIdle(); - AgentRoute *rt = RouteGet("vrf1", Ip4Address::from_string("0.0.0.0"), 0); - InetUnicastRouteEntry *src_rt = static_cast( - RouteGet("vrf1", Ip4Address::from_string("1.1.1.10"), 32)); + FlowEntry *flow = FlowGet(GetVrfId("vrf1"), "1.1.1.10", "2.1.1.10", + 1, 0, 0, GetFlowKeyNH(11)); + EXPECT_TRUE(flow != NULL); + EXPECT_TRUE(flow->data().component_nh_idx + != CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[11]->flow_key_nh()); - FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), - "1.1.1.10", "2.1.1.1", 1, 0, 0, GetFlowKeyNH(1)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx != + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->data().nh.get() == src_rt->GetLocalNextHop()); + const VmInterface *out_vmi = GetOutVmi(flow); + EXPECT_TRUE(rflow->rpf_nh() == out_vmi->flow_key_nh()); + FlushFlowTable(); + client->WaitForIdle(); - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx != + // Tx packet from port-12 + TxIpPacket(vmi_[12]->id(), "1.1.1.10", "2.1.1.10", 1); + client->WaitForIdle(); + + flow = FlowGet(GetVrfId("vrf1"), "1.1.1.10", "2.1.1.10", 1, 0, 0, + GetFlowKeyNH(12)); + EXPECT_TRUE(flow != NULL); + EXPECT_TRUE(flow->data().component_nh_idx + != CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[12]->flow_key_nh()); + + rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + out_vmi = GetOutVmi(flow); + EXPECT_TRUE(rflow->rpf_nh() == out_vmi->flow_key_nh()); + FlushFlowTable(); + client->WaitForIdle(); + + // Tx packet from port-13 + TxIpPacket(vmi_[13]->id(), "1.1.1.10", "2.1.1.10", 1); + client->WaitForIdle(); + + flow = FlowGet(GetVrfId("vrf1"), "1.1.1.10", "2.1.1.10", 1, 0, 0, + GetFlowKeyNH(13)); + EXPECT_TRUE(flow != NULL); + EXPECT_TRUE(flow->data().component_nh_idx + != CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh() == vmi_[13]->flow_key_nh()); + + rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + out_vmi = GetOutVmi(flow); + EXPECT_TRUE(rflow->rpf_nh() == out_vmi->flow_key_nh()); + FlushFlowTable(); + client->WaitForIdle(); - DeleteRoute("vrf1", "0.0.0.0", 0, bgp_peer); + DeleteVmportEnv(input22, 3, true); + DelIPAM("vn2"); client->WaitForIdle(); - sleep(1); +} + +// Ping from ECMP source-ip but with wrong VMI to ECMP destination in +// same VRF RPF-NH in both flow must be set as composite-nh +TEST_F(LocalEcmpTest, EcmpToEcmp_RpfFail_1) { + TxIpPacket(vmi_[1]->id(), "1.1.1.10", "1.1.1.20", 1); client->WaitForIdle(); - WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); + + FlowEntry *flow = FlowGet(GetVrfId("vrf1"), "1.1.1.10", "1.1.1.20", + 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(flow != NULL); + EXPECT_TRUE(flow->data().component_nh_idx + != CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(flow->rpf_nh()->GetType() == NextHop::COMPOSITE); + + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + const VmInterface *out_vmi = GetOutVmi(flow); + EXPECT_TRUE(rflow->rpf_nh() == out_vmi->flow_key_nh()); } -TEST_F(EcmpTest, EcmpTest_3) { +TEST_F(LocalEcmpTest, Metadata_Ecmp_1) { boost::scoped_ptr key(new InetInterfaceKey("vhost0")); - const InetInterface *vhost = static_cast( - agent_->interface_table()->FindActiveEntry(key.get())); - const VmInterface *vmi = static_cast(VmPortGet(1)); + const InetInterface *vhost = static_cast + (agent_->interface_table()->FindActiveEntry(key.get())); TxTcpPacket(vhost->id(), vhost->ip_addr().to_string().c_str(), - vmi->mdata_ip_addr().to_string().c_str(), 100, 100, false, 0); + vmi_[11]->mdata_ip_addr().to_string().c_str(), 100, 100, false, + 0); client->WaitForIdle(); - FlowEntry *entry = FlowGet(0, vhost->ip_addr().to_string().c_str(), - vmi->mdata_ip_addr().to_string().c_str(), - IPPROTO_TCP, 100, 100, vhost->flow_key_nh()->id()); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); + FlowEntry *flow = FlowGet(0, vhost->ip_addr().to_string().c_str(), + vmi_[11]->mdata_ip_addr().to_string().c_str(), + IPPROTO_TCP, 100, 100, + vhost->flow_key_nh()->id()); + EXPECT_TRUE(flow != NULL); + EXPECT_TRUE(flow->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); - InetUnicastRouteEntry *rt = RouteGet("vrf1", vmi->primary_ip_addr(), 32); + InetUnicastRouteEntry *rt = RouteGet("vrf1", vmi_[11]->primary_ip_addr(), + 32); const CompositeNH *cnh = dynamic_cast(rt->GetActiveNextHop()); - EXPECT_TRUE(cnh->GetNH(entry->data().component_nh_idx) == - vmi->l3_interface_nh_no_policy()); + EXPECT_TRUE(cnh->GetNH(flow->data().component_nh_idx) == + vmi_[11]->l3_interface_nh_no_policy()); client->WaitForIdle(); } diff --git a/src/vnsw/agent/pkt/test/test_ecmp_mx.cc b/src/vnsw/agent/pkt/test/test_ecmp_mx.cc index 385fdbe2faf..d7fc3678faf 100644 --- a/src/vnsw/agent/pkt/test/test_ecmp_mx.cc +++ b/src/vnsw/agent/pkt/test/test_ecmp_mx.cc @@ -130,7 +130,7 @@ TEST_F(EcmpTest, EcmpTest_1) { FlowEntry *rev_entry = entry->reverse_flow_entry(); EXPECT_TRUE(rev_entry->data().component_nh_idx == CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + EXPECT_TRUE(rev_entry->data().rpf_nh.get() == rt->GetActiveNextHop()); DeleteRoute("vrf1", "0.0.0.0", 0, bgp_peer); client->WaitForIdle(); @@ -139,10 +139,9 @@ TEST_F(EcmpTest, EcmpTest_1) { //Send packet from ECMP MX to VM //Verify: -// Forward flow is ecmp -// Reverse flow ecmp index is not set -// Reverse flow rpf next is composite NH -// and the index matches originating MX +// Forward flow is non-ECMP +// Reverse flow is ECMP. ECMP Index is not set +// Reverse flow rpf next is Composite NH TEST_F(EcmpTest, EcmpTest_2) { AddRemoteEcmpRoute("vrf1", "0.0.0.0", 0, "vn1", 4); @@ -151,16 +150,21 @@ TEST_F(EcmpTest, EcmpTest_2) { client->WaitForIdle(); AgentRoute *rt = RouteGet("vrf1", Ip4Address::from_string("0.0.0.0"), 0); + FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), - "1.1.1.1", "8.8.8.8", 1, 0, 0, GetFlowKeyNH(1)); + "8.8.8.8", "1.1.1.1", 1, 0, 0, GetFlowKeyNH(1)); EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == 2); + EXPECT_TRUE(entry->IsEcmpFlow()); + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().rpf_nh.get() == rt->GetActiveNextHop()); - //Reverse flow is no ECMP + //Reverse flow is ECMP FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + EXPECT_TRUE(rev_entry->IsEcmpFlow()); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().rpf_nh->id() == GetFlowKeyNH(1)); DeleteRoute("vrf1", "0.0.0.0", 0, bgp_peer); client->WaitForIdle(); @@ -178,15 +182,19 @@ TEST_F(EcmpTest, EcmpTest_3) { AgentRoute *rt = RouteGet("vrf1", Ip4Address::from_string("0.0.0.0"), 0); FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), - "1.1.1.1", "8.8.8.8", 1, 0, 0, GetFlowKeyNH(1)); + "8.8.8.8", "1.1.1.1", 1, 0, 0, GetFlowKeyNH(1)); EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == 3); + EXPECT_TRUE(entry->IsEcmpFlow()); + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().rpf_nh.get() == rt->GetActiveNextHop()); - //Reverse flow is no ECMP + //Reverse flow is ECMP FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + EXPECT_TRUE(rev_entry->IsEcmpFlow()); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().rpf_nh->id() == GetFlowKeyNH(1)); DeleteRoute("vrf1", "0.0.0.0", 0, bgp_peer); client->WaitForIdle(); @@ -194,7 +202,7 @@ TEST_F(EcmpTest, EcmpTest_3) { } //Send packet from MX1 to VM -//Trap a ecmp resolve packet from MX2 to VM +//Send one more flow setup message from MX2 to VM //verify that component index gets update TEST_F(EcmpTest, EcmpTest_4) { AddRemoteEcmpRoute("vrf1", "0.0.0.0", 0, "vn1", 4); @@ -204,21 +212,30 @@ TEST_F(EcmpTest, EcmpTest_4) { client->WaitForIdle(); AgentRoute *rt = RouteGet("vrf1", Ip4Address::from_string("0.0.0.0"), 0); + FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), - "1.1.1.1", "8.8.8.8", 1, 0, 0, GetFlowKeyNH(1)); + "8.8.8.8", "1.1.1.1", 1, 0, 0, GetFlowKeyNH(1)); EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == 0); + EXPECT_TRUE(entry->IsEcmpFlow()); + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().rpf_nh.get() == rt->GetActiveNextHop()); + + //Reverse flow is ECMP FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + EXPECT_TRUE(rev_entry->IsEcmpFlow()); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().rpf_nh->id() == GetFlowKeyNH(1)); TxIpMplsPacket(eth_intf_id, MX_2, router_id, vm1_label, "8.8.8.8", "1.1.1.1", 1, 10); client->WaitForIdle(); - EXPECT_TRUE(entry->data().component_nh_idx == 2); - EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + + EXPECT_EQ(2, flow_proto_->FlowCount()); + EXPECT_TRUE(entry->data().rpf_nh.get() == rt->GetActiveNextHop()); + EXPECT_TRUE(rev_entry->data().rpf_nh->id() == GetFlowKeyNH(1)); DeleteRoute("vrf1", "0.0.0.0", 0, bgp_peer); client->WaitForIdle(); @@ -228,7 +245,7 @@ TEST_F(EcmpTest, EcmpTest_4) { //Send packet from MX to VM // Fwd flow has no vrf assign ACL // Reverese flow has vrf assign ACL -// hence component index has to set based on vrf2 +// RPF NH must be based on reverse-flow TEST_F(EcmpTest, EcmpTest_5) { AddRemoteEcmpRoute("vrf1", "0.0.0.0", 0, "vn2", 4); //Reverse all the nexthop in vrf2 @@ -254,18 +271,19 @@ TEST_F(EcmpTest, EcmpTest_5) { client->WaitForIdle(); AgentRoute *rt = RouteGet("vrf2", Ip4Address::from_string("0.0.0.0"), 0); - FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), - "1.1.1.1", "8.8.8.8", 1, 0, 0, GetFlowKeyNH(1)); + // RPF-NH in forward flow is based on translated VRF in reverse flow + FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), "8.8.8.8", "1.1.1.1", + 1, 0, 0, GetFlowKeyNH(1)); EXPECT_TRUE(entry != NULL); - //MX 3 would be placed at index 0 because of nexthop - //order reversal - EXPECT_TRUE(entry->data().component_nh_idx == 0); + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().rpf_nh.get() == rt->GetActiveNextHop()); //Reverse flow is no ECMP FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().rpf_nh.get() == vmi->flow_key_nh()); DeleteRoute("vrf1", "0.0.0.0", 0, bgp_peer); DeleteRoute("vrf2", "0.0.0.0", 0, bgp_peer); @@ -303,13 +321,14 @@ TEST_F(EcmpTest, EcmpTest_6) { FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), "1.1.1.1", "8.8.8.8", 1, 0, 0, GetFlowKeyNH(1)); EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == 3); + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); //Reverse flow is no ECMP FlowEntry *rev_entry = entry->reverse_flow_entry(); EXPECT_TRUE(rev_entry->data().component_nh_idx == CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + EXPECT_TRUE(rev_entry->data().rpf_nh.get() == rt->GetActiveNextHop()); //Clean up DeleteRoute("fip:fip", "0.0.0.0", 0, bgp_peer); @@ -359,13 +378,14 @@ TEST_F(EcmpTest, EcmpTest_7) { FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), "1.1.1.1", "8.8.8.8", 1, 0, 0, GetFlowKeyNH(1)); EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == 0); + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); //Reverse flow is no ECMP FlowEntry *rev_entry = entry->reverse_flow_entry(); EXPECT_TRUE(rev_entry->data().component_nh_idx == CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + EXPECT_TRUE(rev_entry->data().rpf_nh.get() == rt->GetActiveNextHop()); //Clean up DeleteRoute("fip:fip", "0.0.0.0", 0, bgp_peer); diff --git a/src/vnsw/agent/pkt/test/test_ecmp_remote.cc b/src/vnsw/agent/pkt/test/test_ecmp_remote.cc new file mode 100644 index 00000000000..242a4dfb8a2 --- /dev/null +++ b/src/vnsw/agent/pkt/test/test_ecmp_remote.cc @@ -0,0 +1,1156 @@ +/* + * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved. + */ + +#include "base/os.h" +#include "test/test_cmn_util.h" +#include "test_pkt_util.h" +#include "pkt/flow_proto.h" + +struct PortInfo input1[] = { + {"vnet1", 1, "1.1.1.1", "00:00:00:01:01:01", 2, 1}, +}; +//virtual IP VM +struct PortInfo input2[] = { + {"vnet2", 2, "2.1.1.1", "00:00:00:01:01:01", 2, 2}, + {"vnet3", 3, "2.1.1.1", "00:00:00:02:02:01", 2, 3}, + {"vnet4", 4, "2.1.1.1", "00:00:00:02:02:01", 2, 4}, +}; + +struct PortInfo input3[] = { + {"vnet5", 5, "3.1.1.1", "00:00:00:01:01:01", 3, 5}, + {"vnet6", 6, "3.1.1.2", "00:00:00:02:02:01", 3, 6}, + {"vnet7", 7, "3.1.1.3", "00:00:00:02:02:01", 3, 7}, +}; + +struct PortInfo input4[] = { + {"vnet8", 8, "4.1.1.1", "00:00:00:01:01:01", 4, 8}, +}; + +IpamInfo ipam_info_2[] = { + {"1.1.1.0", 24, "1.1.1.254"}, + {"2.1.1.0", 24, "2.1.1.254"}, +}; + +IpamInfo ipam_info_3[] = { + {"3.1.1.0", 24, "3.1.1.254"}, +}; + +IpamInfo ipam_info_4[] = { + {"4.1.1.0", 24, "4.1.1.254"}, +}; + +class RemoteEcmpTest : public ::testing::Test { + virtual void SetUp() { + agent_ = Agent::GetInstance(); + flow_proto_ = agent_->pkt()->get_flow_proto(); + // ----- + // ------------ + // ----- + CreateVmportWithEcmp(input1, 1); + CreateVmportWithEcmp(input2, 3); + CreateVmportFIpEnv(input3, 3); + CreateVmportFIpEnv(input4, 1); + AddIPAM("vn2", ipam_info_2, 2); + AddIPAM("default-project:vn3", ipam_info_3, 1); + AddIPAM("default-project:vn4", ipam_info_4, 1); + client->WaitForIdle(); + for (uint32_t i = 1; i < 9; i++) { + EXPECT_TRUE(VmPortActive(i)); + } + + boost::system::error_code ec; + bgp_peer = CreateBgpPeer(Ip4Address::from_string("0.0.0.1", ec), + "xmpp channel"); + client->WaitForIdle(); + + //Add floating IP for vrf2 interface to talk to + //vrf3 + AddFloatingIpPool("fip-pool1", 1); + AddFloatingIp("fip1", 1, "3.1.1.100"); + AddLink("floating-ip", "fip1", "floating-ip-pool", "fip-pool1"); + AddLink("floating-ip-pool", "fip-pool1", "virtual-network", + "default-project:vn3"); + AddLink("virtual-machine-interface", "vnet2", "floating-ip", "fip1"); + AddLink("virtual-machine-interface", "vnet3", "floating-ip", "fip1"); + AddLink("virtual-machine-interface", "vnet4", "floating-ip", "fip1"); + client->WaitForIdle(); + Ip4Address ip = Ip4Address::from_string("3.1.1.100"); + InetUnicastRouteEntry *rt = RouteGet("default-project:vn3:vn3", ip, 32); + EXPECT_TRUE(rt != NULL); + EXPECT_TRUE(rt->GetActiveNextHop()->GetType() == NextHop::COMPOSITE); + mpls_label_1 = rt->GetActiveLabel(); + + ip = Ip4Address::from_string("2.1.1.1"); + rt = RouteGet("vrf2", ip, 32); + EXPECT_TRUE(rt != NULL); + EXPECT_TRUE(rt->GetActiveNextHop()->GetType() == NextHop::COMPOSITE); + mpls_label_2 = rt->GetActiveLabel(); + + //Add floating IP for vrf3 interfaces to talk vrf4 + AddFloatingIpPool("fip-pool2", 2); + AddFloatingIp("fip2", 2, "4.1.1.100"); + AddLink("floating-ip", "fip2", "floating-ip-pool", "fip-pool2"); + AddLink("floating-ip-pool", "fip-pool2", "virtual-network", + "default-project:vn4"); + AddLink("virtual-machine-interface", "vnet5", "floating-ip", "fip2"); + AddLink("virtual-machine-interface", "vnet6", "floating-ip", "fip2"); + AddLink("virtual-machine-interface", "vnet7", "floating-ip", "fip2"); + client->WaitForIdle(); + ip = Ip4Address::from_string("4.1.1.100"); + rt = RouteGet("default-project:vn4:vn4", ip, 32); + EXPECT_TRUE(rt != NULL); + EXPECT_TRUE(rt->GetActiveNextHop()->GetType() == NextHop::COMPOSITE); + mpls_label_3 = rt->GetActiveLabel(); + + //Populate ethernet interface id + eth_intf_id_ = EthInterfaceGet("vnet0")->id(); + + remote_vm_ip1_ = Ip4Address::from_string("2.2.2.2"); + remote_vm_ip2_ = Ip4Address::from_string("3.3.3.3"); + remote_vm_ip3_ = Ip4Address::from_string("4.4.4.4"); + remote_server_ip_ = Ip4Address::from_string("10.10.1.1"); + + //Add couple of remote VM routes for generating packet + Inet4TunnelRouteAdd(bgp_peer, "vrf2", remote_vm_ip1_, 32, + remote_server_ip_, TunnelType::GREType(), + 30, "vn2", SecurityGroupList(), + PathPreference()); + + Inet4TunnelRouteAdd(bgp_peer, "default-project:vn3:vn3", + remote_vm_ip2_, 32, + remote_server_ip_, TunnelType::GREType(), + 30, "default-project:vn3", SecurityGroupList(), + PathPreference()); + + Inet4TunnelRouteAdd(bgp_peer, "default-project:vn4:vn4", + remote_vm_ip3_, 32, + remote_server_ip_, TunnelType::GREType(), + 30, "default-project:vn4", SecurityGroupList(), + PathPreference()); + client->WaitForIdle(); + FlowStatsTimerStartStop(agent_, true); + } + + void FlushFlowTable() { + client->EnqueueFlowFlush(); + client->WaitForIdle(); + EXPECT_EQ(0U, get_flow_proto()->FlowCount()); + } + + virtual void TearDown() { + FlushFlowTable(); + + DelLink("virtual-machine-interface", "vnet2", "floating-ip", "fip1"); + DelLink("virtual-machine-interface", "vnet3", "floating-ip", "fip1"); + DelLink("virtual-machine-interface", "vnet4", "floating-ip", "fip1"); + DelLink("floating-ip-pool", "fip-pool1", "virtual-network", + "default-project:vn3"); + client->WaitForIdle(); + + DelLink("virtual-machine-interface", "vnet5", "floating-ip", "fip2"); + DelLink("virtual-machine-interface", "vnet6", "floating-ip", "fip2"); + DelLink("virtual-machine-interface", "vnet7", "floating-ip", "fip2"); + DelLink("floating-ip-pool", "fip-pool2", "virtual-network", + "default-project:vn4"); + client->WaitForIdle(); + DeleteVmportEnv(input1, 1, false); + DeleteVmportEnv(input2, 3, true); + DeleteVmportFIpEnv(input3, 3, true); + DeleteVmportFIpEnv(input4, 1, true); + agent_->fabric_inet4_unicast_table()->DeleteReq(bgp_peer, + "vrf2", remote_vm_ip1_, 32, new ControllerVmRoute(bgp_peer)); + agent_->fabric_inet4_unicast_table()->DeleteReq(bgp_peer, + "default-project:vn3:vn3", remote_vm_ip2_, 32, + new ControllerVmRoute(bgp_peer)); + agent_->fabric_inet4_unicast_table()->DeleteReq(bgp_peer, + "default-project:vn4:vn4", remote_vm_ip3_, 32, + new ControllerVmRoute(bgp_peer)); + + DelIPAM("vn2"); + DelIPAM("default-project:vn3"); + DelIPAM("default-project:vn4"); + client->WaitForIdle(); + DeleteBgpPeer(bgp_peer); + EXPECT_FALSE(VrfFind("vrf1", true)); + EXPECT_FALSE(VrfFind("vrf2", true)); + EXPECT_FALSE(VrfFind("default-project:vn3:vn3", true)); + EXPECT_FALSE(VrfFind("default-project:vn4:vn4", true)); + FlowStatsTimerStartStop(agent_, false); + + WAIT_FOR(1000, 1000, (agent_->vrf_table()->Size() == 1)); + } + +public: + FlowProto *get_flow_proto() const { return flow_proto_; } + uint32_t GetServiceVlanNH(uint32_t intf_id, std::string vrf_name) const { + const VmInterface *vm_intf = VmInterfaceGet(intf_id); + const VrfEntry *vrf = VrfGet(vrf_name.c_str()); + uint32_t label = vm_intf->GetServiceVlanLabel(vrf); + int nh_id = agent_->mpls_table()-> + FindMplsLabel(label)->nexthop()->id(); + return nh_id; + } + + void AddRemoteEcmpRoute(const string vrf_name, const string ip, + uint32_t plen, const string vn, int count, + ComponentNHKeyList local_list, + bool same_label = false) { + //If there is a local route, include that always + Ip4Address vm_ip = Ip4Address::from_string(ip); + + ComponentNHKeyList comp_nh_list = local_list; + + int remote_server_ip = 0x0A0A0A0A; + int label = 16; + SecurityGroupList sg_id_list; + + for(int i = 0; i < count; i++) { + ComponentNHKeyPtr comp_nh(new ComponentNHKey( + label, agent_->fabric_vrf_name(), + agent_->router_id(), + Ip4Address(remote_server_ip++), + false, TunnelType::GREType())); + comp_nh_list.push_back(comp_nh); + if (!same_label) { + label++; + } + } + EcmpTunnelRouteAdd(bgp_peer, vrf_name, vm_ip, plen, + comp_nh_list, -1, vn, sg_id_list, + PathPreference()); + } + + void AddLocalVmRoute(const string vrf_name, const string ip, uint32_t plen, + const string vn, uint32_t intf_uuid) { + const VmInterface *vm_intf = static_cast + (VmPortGet(intf_uuid)); + VmInterfaceKey intf_key(AgentKey::ADD_DEL_CHANGE, MakeUuid(intf_uuid), ""); + VnListType vn_list; + vn_list.insert(vn); + LocalVmRoute *local_vm_route = + new LocalVmRoute(intf_key, vm_intf->label(), + VxLanTable::kInvalidvxlan_id, false, vn_list, + InterfaceNHFlags::INET4, SecurityGroupList(), + CommunityList(), PathPreference(), Ip4Address(0), + EcmpLoadBalance(), false, false, + bgp_peer->sequence_number(), false); + InetUnicastAgentRouteTable *rt_table = + agent_->vrf_table()->GetInet4UnicastRouteTable(vrf_name); + + rt_table->AddLocalVmRouteReq(bgp_peer, vrf_name, + Ip4Address::from_string(ip), plen, + static_cast(local_vm_route)); + } + + void AddRemoteVmRoute(const string vrf_name, const string ip, uint32_t plen, + const string vn) { + const Ip4Address addr = Ip4Address::from_string(ip); + VnListType vn_list; + vn_list.insert(vn); + ControllerVmRoute *data = + ControllerVmRoute::MakeControllerVmRoute(bgp_peer, + agent_->fabric_vrf_name(), agent_->router_id(), + vrf_name, addr, TunnelType::GREType(), 16, + vn_list, SecurityGroupList(), + PathPreference(), false, EcmpLoadBalance(), + false); + InetUnicastAgentRouteTable::AddRemoteVmRouteReq(bgp_peer, + vrf_name, addr, plen, data); + } + + void DeleteRemoteRoute(const string vrf_name, const string ip, + uint32_t plen) { + Ip4Address server_ip = Ip4Address::from_string(ip); + agent_->fabric_inet4_unicast_table()->DeleteReq(bgp_peer, + vrf_name, server_ip, plen, new ControllerVmRoute(bgp_peer)); + } + + uint32_t eth_intf_id_; + Ip4Address remote_vm_ip1_; + Ip4Address remote_vm_ip2_; + Ip4Address remote_vm_ip3_; + Ip4Address remote_server_ip_; + uint32_t mpls_label_1; + uint32_t mpls_label_2; + uint32_t mpls_label_3; + BgpPeer *bgp_peer; + Agent *agent_; + FlowProto *flow_proto_; +}; + +// Ping from from fabric. Non-ECMP to ECMP +TEST_F(RemoteEcmpTest, Fabric_NonEcmpToEcmp_1) { + //VIP of vrf2 interfaces + char vm_ip[80] = "2.1.1.1"; + char router_id[80]; + char remote_server_ip[80]; + char remote_vm_ip[80]; + + strcpy(router_id, agent_->router_id().to_string().c_str()); + strcpy(remote_server_ip, remote_server_ip_.to_string().c_str()); + strcpy(remote_vm_ip, remote_vm_ip1_.to_string().c_str()); + + TxIpMplsPacket(eth_intf_id_, remote_server_ip, router_id, mpls_label_2, + remote_vm_ip, vm_ip, 1, 10); + + client->WaitForIdle(); + int nh_id = GetActiveLabel(MplsLabel::VPORT_NH, mpls_label_2)->nexthop()->id(); + FlowEntry *entry = FlowGet(VrfGet("vrf2")->vrf_id(), + remote_vm_ip, vm_ip, 1, 0, 0, nh_id); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + + //Reverse flow should be set and should also be ECMP + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); +} + +// Ping from fabric. +// Non-ECMP to ECMP +// FIP DNAT case with source in vrf2 and dest in vrf1 +TEST_F(RemoteEcmpTest, Fabric_DstFip_NonEcmpToEcmp_1) { + //FIP of vrf3 interfaces + char vm_ip[80] = "4.1.1.100"; + char router_id[80]; + char remote_server_ip[80]; + char remote_vm_ip[80]; + + strcpy(router_id, agent_->router_id().to_string().c_str()); + strcpy(remote_server_ip, remote_server_ip_.to_string().c_str()); + strcpy(remote_vm_ip, remote_vm_ip3_.to_string().c_str()); + + TxIpMplsPacket(eth_intf_id_, remote_server_ip, router_id, mpls_label_3, + remote_vm_ip, vm_ip, 1, 10); + client->WaitForIdle(); + + int nh_id = GetActiveLabel(MplsLabel::VPORT_NH, mpls_label_3)->nexthop()->id(); + FlowEntry *entry = FlowGet(VrfGet("default-project:vn4:vn4")->vrf_id(), + remote_vm_ip, vm_ip, 1, 0, 0, nh_id); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->is_flags_set(FlowEntry::NatFlow) == true); + + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); +} + +// Ping from fabric. +// ECMP to Non-ECMP +// FIP DNAT case with source in vrf2 and dest in vrf1 +TEST_F(RemoteEcmpTest, Fabric_DstFip_EcmpToNonEcmp_1) { + Ip4Address ip = Ip4Address::from_string("30.30.30.0"); + ComponentNHKeyList comp_nh; + Ip4Address server_ip1 = Ip4Address::from_string("15.15.15.15"); + Ip4Address server_ip2 = Ip4Address::from_string("15.15.15.16"); + Ip4Address server_ip3 = Ip4Address::from_string("15.15.15.17"); + + ComponentNHKeyPtr comp_nh_data1(new ComponentNHKey( + 16, agent_->fabric_vrf_name(), agent_->router_id(), server_ip1, false, + TunnelType::GREType())); + comp_nh.push_back(comp_nh_data1); + + ComponentNHKeyPtr comp_nh_data2(new ComponentNHKey( + 17, agent_->fabric_vrf_name(), agent_->router_id(), server_ip2, false, + TunnelType::GREType())); + comp_nh.push_back(comp_nh_data2); + + ComponentNHKeyPtr comp_nh_data3(new ComponentNHKey( + 18, agent_->fabric_vrf_name(), agent_->router_id(), server_ip3, false, + TunnelType::GREType())); + comp_nh.push_back(comp_nh_data3); + + SecurityGroupList sg_list; + EcmpTunnelRouteAdd(bgp_peer, "vrf2", ip, 24, comp_nh, -1, "vn2", sg_list, + PathPreference()); + client->WaitForIdle(); + + //VIP of vrf2 interfaces + char vm_ip[80] = "1.1.1.1"; + char router_id[80]; + char remote_server_ip[80]; + char remote_vm_ip[80]; + + strcpy(router_id, agent_->router_id().to_string().c_str()); + strcpy(remote_server_ip, "15.15.15.16"); + strcpy(remote_vm_ip, "30.30.30.1"); + + const VmInterface *vintf = + static_cast(VmPortGet(1)); + TxIpMplsPacket(eth_intf_id_, remote_server_ip, router_id, vintf->label(), + remote_vm_ip, vm_ip, 1, 10); + + client->WaitForIdle(); + int nh_id = GetActiveLabel(MplsLabel::VPORT_NH, + vintf->label())->nexthop()->id(); + FlowEntry *entry = FlowGet(VrfGet("vrf2")->vrf_id(), + remote_vm_ip, vm_ip, 1, 0, 0, nh_id); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + + //Reverse flow should be set and should also be ECMP + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + DeleteRemoteRoute("vrf2", ip.to_string(), 24); + client->WaitForIdle(); +} + +// Ping from VMI to Fabric +// Source interface vmi-21 has FIP in vrf2 +// Dest on fabric and has ECMP route with 2 nexthops (10.10.10.100 and +// 10.10.10.101) +// +// After flow setup, the reverse flow moves from 10.10.10.100 to 10.10.10.101 +// or vice-versa +// +// In normal scenario, VRouter should take care of this flow-movement. However +// agent can come into picture if the flow is already evicted in vrouter +TEST_F(RemoteEcmpTest, Vmi_DstFip_NonEcmpToEcmp_FlowMove_1) { + struct PortInfo input1[] = { + {"vnet9", 9, "9.1.1.1", "00:00:00:01:01:01", 9, 9}, + }; + CreateVmportWithEcmp(input1, 1); + IpamInfo ipam_info_9[] = { + {"9.1.1.0", 24, "9.1.1.254"}, + }; + AddIPAM("vn9", ipam_info_9, 1); + client->WaitForIdle(); + + VmInterface *intf = static_cast(VmPortGet(9)); + uint32_t label = intf->label(); + + AddVn("default-project:vn10", 10); + AddVrf("default-project:vn10:vn10", 10); + AddLink("virtual-network", "default-project:vn10", + "routing-instance", "default-project:vn10:vn10"); + AddFloatingIpPool("fip-pool9", 10); + AddFloatingIp("fip9", 10, "10.10.10.2"); + AddLink("floating-ip", "fip9", "floating-ip-pool", "fip-pool9"); + AddLink("floating-ip-pool", "fip-pool9", "virtual-network", + "default-project:vn10"); + AddLink("virtual-machine-interface", "vnet9", "floating-ip", "fip9"); + client->WaitForIdle(); + + Ip4Address gw_rt = Ip4Address::from_string("0.0.0.0"); + Ip4Address remote_server_ip1 = Ip4Address::from_string("10.10.10.100"); + Ip4Address remote_server_ip2 = Ip4Address::from_string("10.10.10.101"); + ComponentNHKeyPtr nh_data1(new ComponentNHKey(30, agent_->fabric_vrf_name(), + agent_->router_id(), + remote_server_ip1, + false, + TunnelType::DefaultType())); + ComponentNHKeyPtr nh_data2(new ComponentNHKey(20, agent_->fabric_vrf_name(), + agent_->router_id(), + remote_server_ip2, + false, + TunnelType::DefaultType())); + ComponentNHKeyList comp_nh_list; + comp_nh_list.push_back(nh_data1); + comp_nh_list.push_back(nh_data2); + EcmpTunnelRouteAdd(bgp_peer, "default-project:vn10:vn10", gw_rt, 0, + comp_nh_list, -1, "default-project:vn10", + SecurityGroupList(), PathPreference()); + client->WaitForIdle(); + + TxIpPacket(VmPortGetId(9), "9.1.1.1", "10.1.1.1", 1); + client->WaitForIdle(); + FlowEntry *entry; + entry = FlowGet(VrfGet("default-project:vn10:vn10")->vrf_id(), + "9.1.1.1", "10.1.1.1", 1, 0, 0, GetFlowKeyNH(9)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->is_flags_set(FlowEntry::ShortFlow) == false); + + uint32_t reverse_index = entry->reverse_flow_entry()->flow_handle(); + if (entry->data().component_nh_idx == 0) { + // If forward flow setup to go on 10.10.10.100, trap reverse flow + // from 10.10.10.101 + TxIpMplsPacket(eth_intf_id_, "10.10.10.101", + agent_->router_id().to_string().c_str(), + label, "10.1.1.1", "10.10.10.2", 1, reverse_index); + client->WaitForIdle(); + // "entry" is new reverse flow now. + // VRouter manages its ECMP member index + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + } else { + // If forward flow setup to go on 10.10.10.101, trap reverse flow + // from 10.10.10.100 + TxIpMplsPacket(eth_intf_id_, "10.10.10.100", + agent_->router_id().to_string().c_str(), + label, "10.1.1.1", "10.10.10.2", 1, reverse_index); + client->WaitForIdle(); + // "entry" is new reverse flow now. + // VRouter manages its ECMP member index + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + } + + DeleteVmportEnv(input1, 1, true); + DeleteRemoteRoute("default-project:vn10:vn10", "0.0.0.0", 0); + DelLink("floating-ip", "fip9", "floating-ip-pool", "fip-pool9"); + DelLink("floating-ip-pool", "fip-pool9", + "virtual-network", "default-project:vn10"); + DelLink("virtual-machine-interface", "vnet9", "floating-ip", "fip9"); + DelFloatingIp("fip9"); + DelFloatingIpPool("fip-pool9"); + client->WaitForIdle(); + DelVrf("default-project:vn10:vn10"); + DelVn("default-project:vn10"); + client->WaitForIdle(); + WAIT_FOR(100, 1000, VrfFind("default-project:vn10:vn10", true) == false); + client->WaitForIdle(); + EXPECT_TRUE(get_flow_proto()->FlowCount() == 0); + EXPECT_FALSE(VrfFind("vrf9", true)); + DelIPAM("vn9"); +} + +// Add a test case to check if rpf NH of flow using floating IP +//is set properly upon nexthop change of from 2 destination to 3 +//destinations +TEST_F(RemoteEcmpTest, Vmi_EcmpTest_13) { + struct PortInfo input1[] = { + {"vnet9", 9, "9.1.1.1", "00:00:00:01:01:01", 9, 9}, + }; + CreateVmportWithEcmp(input1, 1); + IpamInfo ipam_info_9[] = { + {"9.1.1.0", 24, "9.1.1.254"}, + }; + AddIPAM("vn9", ipam_info_9, 1); + client->WaitForIdle(); + + VmInterface *intf = static_cast(VmPortGet(9)); + uint32_t label = intf->label(); + + AddVn("default-project:vn10", 10); + AddVrf("default-project:vn10:vn10", 10); + AddLink("virtual-network", "default-project:vn10", + "routing-instance", "default-project:vn10:vn10"); + AddFloatingIpPool("fip-pool9", 10); + AddFloatingIp("fip9", 10, "10.10.10.2"); + AddLink("floating-ip", "fip9", "floating-ip-pool", "fip-pool9"); + AddLink("floating-ip-pool", "fip-pool9", "virtual-network", + "default-project:vn10"); + AddLink("virtual-machine-interface", "vnet9", "floating-ip", "fip9"); + client->WaitForIdle(); + + Ip4Address gw_rt = Ip4Address::from_string("0.0.0.0"); + Ip4Address remote_server_ip1 = Ip4Address::from_string("10.10.10.100"); + Ip4Address remote_server_ip2 = Ip4Address::from_string("10.10.10.101"); + Ip4Address remote_server_ip3 = Ip4Address::from_string("10.10.10.102"); + ComponentNHKeyPtr nh_data1(new ComponentNHKey(30, agent_->fabric_vrf_name(), + agent_->router_id(), + remote_server_ip1, + false, + TunnelType::DefaultType())); + ComponentNHKeyPtr nh_data2(new ComponentNHKey(20, agent_->fabric_vrf_name(), + agent_->router_id(), + remote_server_ip2, + false, + TunnelType::DefaultType())); + ComponentNHKeyList comp_nh_list; + comp_nh_list.push_back(nh_data1); + comp_nh_list.push_back(nh_data2); + EcmpTunnelRouteAdd(bgp_peer, "default-project:vn10:vn10", gw_rt, 0, + comp_nh_list, false, "default-project:vn10", + SecurityGroupList(), PathPreference()); + client->WaitForIdle(); + InetUnicastRouteEntry *rt = RouteGet("default-project:vn10:vn10", gw_rt, 0); + + TxIpPacket(VmPortGetId(9), "9.1.1.1", "10.1.1.1", 1); + client->WaitForIdle(); + FlowEntry *entry; + entry = FlowGet(VrfGet("default-project:vn10:vn10")->vrf_id(), + "9.1.1.1", "10.1.1.1", 1, 0, 0, GetFlowKeyNH(9)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->is_flags_set(FlowEntry::ShortFlow) == false); + + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->rpf_nh() == rt->GetActiveNextHop()); + + uint32_t reverse_index = entry->reverse_flow_entry()->flow_handle(); + if (entry->data().component_nh_idx == 0) { + // If forward flow setup to go on 10.10.10.100, trap reverse flow + // from 10.10.10.101 + TxIpMplsPacket(eth_intf_id_, "10.10.10.101", + agent_->router_id().to_string().c_str(), + label, "10.1.1.1", "10.10.10.2", 1, reverse_index); + client->WaitForIdle(); + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + } else { + // If forward flow setup to go on 10.10.10.101, trap reverse flow + // from 10.10.10.100 + TxIpMplsPacket(eth_intf_id_, "10.10.10.100", + agent_->router_id().to_string().c_str(), + label, "10.1.1.1", "10.10.10.2", 1, reverse_index); + client->WaitForIdle(); + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + } + + //Update the route to make the composite NH point to new nexthop + ComponentNHKeyPtr nh_data3(new ComponentNHKey(20, agent_->fabric_vrf_name(), + agent_->router_id(), + remote_server_ip3, + false, + TunnelType::DefaultType())); + comp_nh_list.push_back(nh_data3); + EcmpTunnelRouteAdd(bgp_peer, "default-project:vn10:vn10", gw_rt, 0, + comp_nh_list, false, "default-project:vn10", + SecurityGroupList(), PathPreference()); + client->WaitForIdle(); + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + //Make sure flow has the right nexthop set. + rt = RouteGet("default-project:vn10:vn10", gw_rt, 0); + rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->rpf_nh() == rt->GetActiveNextHop()); + + DeleteVmportEnv(input1, 1, true); + DeleteRemoteRoute("default-project:vn10:vn10", "0.0.0.0", 0); + DelLink("floating-ip", "fip9", "floating-ip-pool", "fip-pool9"); + DelLink("floating-ip-pool", "fip-pool9", + "virtual-network", "default-project:vn10"); + DelLink("virtual-machine-interface", "vnet9", "floating-ip", "fip9"); + DelFloatingIp("fip9"); + DelFloatingIpPool("fip-pool9"); + client->WaitForIdle(); + DelVrf("default-project:vn10:vn10"); + DelVn("default-project:vn10"); + client->WaitForIdle(); + WAIT_FOR(100, 1000, VrfFind("default-project:vn10:vn10", true) == false); + client->WaitForIdle(); + EXPECT_TRUE(get_flow_proto()->FlowCount() == 0); + EXPECT_FALSE(VrfFind("vrf9", true)); + DelIPAM("vn9"); +} + +//Add a test case to check if rpf NH of flow using floating IP +//gets properly upon nexthop change from ecmp to unicast +TEST_F(RemoteEcmpTest, EcmpTest_14) { + struct PortInfo input1[] = { + {"vnet9", 9, "9.1.1.1", "00:00:00:01:01:01", 9, 9}, + }; + CreateVmportWithEcmp(input1, 1); + IpamInfo ipam_info_9[] = { + {"9.1.1.0", 24, "9.1.1.254"}, + }; + AddIPAM("vn9", ipam_info_9, 1); + client->WaitForIdle(); + + AddVn("default-project:vn10", 10); + AddVrf("default-project:vn10:vn10", 10); + AddLink("virtual-network", "default-project:vn10", + "routing-instance", "default-project:vn10:vn10"); + AddFloatingIpPool("fip-pool9", 10); + AddFloatingIp("fip9", 10, "10.10.10.2"); + AddLink("floating-ip", "fip9", "floating-ip-pool", "fip-pool9"); + AddLink("floating-ip-pool", "fip-pool9", "virtual-network", + "default-project:vn10"); + AddLink("virtual-machine-interface", "vnet9", "floating-ip", "fip9"); + client->WaitForIdle(); + + Ip4Address gw_rt = Ip4Address::from_string("0.0.0.0"); + Ip4Address remote_server_ip1 = Ip4Address::from_string("10.10.10.100"); + Ip4Address remote_server_ip2 = Ip4Address::from_string("10.10.10.101"); + ComponentNHKeyPtr nh_data1(new ComponentNHKey(30, agent_->fabric_vrf_name(), + agent_->router_id(), + remote_server_ip1, + false, + TunnelType::DefaultType())); + ComponentNHKeyPtr nh_data2(new ComponentNHKey(20, agent_->fabric_vrf_name(), + agent_->router_id(), + remote_server_ip2, + false, + TunnelType::DefaultType())); + ComponentNHKeyList comp_nh_list; + comp_nh_list.push_back(nh_data1); + comp_nh_list.push_back(nh_data2); + EcmpTunnelRouteAdd(bgp_peer, "default-project:vn10:vn10", gw_rt, 0, + comp_nh_list, false, "default-project:vn10", + SecurityGroupList(), PathPreference()); + client->WaitForIdle(); + + TxIpPacket(VmPortGetId(9), "9.1.1.1", "10.1.1.1", 1); + client->WaitForIdle(); + FlowEntry *entry; + entry = FlowGet(VrfGet("default-project:vn10:vn10")->vrf_id(), + "9.1.1.1", "10.1.1.1", 1, 0, 0, GetFlowKeyNH(9)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->is_flags_set(FlowEntry::ShortFlow) == false); + + //Update the route to make the remote destination unicast + Inet4TunnelRouteAdd(bgp_peer, "default-project:vn10:vn10", gw_rt, 0, + Ip4Address::from_string("8.8.8.8"), + TunnelType::ComputeType(TunnelType::MplsType()), + 100, "default-project:vn10", SecurityGroupList(), + PathPreference()); + client->WaitForIdle(); + //Make sure flow has the right nexthop set. + InetUnicastRouteEntry *rt = RouteGet("default-project:vn10:vn10", gw_rt, 0); + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->rpf_nh() == rt->GetActiveNextHop()); + + DeleteVmportEnv(input1, 1, true); + DeleteRemoteRoute("default-project:vn10:vn10", "0.0.0.0", 0); + DelLink("floating-ip", "fip9", "floating-ip-pool", "fip-pool9"); + DelLink("floating-ip-pool", "fip-pool9", + "virtual-network", "default-project:vn10"); + DelLink("virtual-machine-interface", "vnet9", "floating-ip", "fip9"); + DelFloatingIp("fip9"); + DelFloatingIpPool("fip-pool9"); + client->WaitForIdle(); + DelVrf("default-project:vn10:vn10"); + DelVn("default-project:vn10"); + client->WaitForIdle(); + WAIT_FOR(100, 1000, VrfFind("default-project:vn10:vn10", true) == false); + client->WaitForIdle(); + EXPECT_TRUE(get_flow_proto()->FlowCount() == 0); + EXPECT_FALSE(VrfFind("vrf9", true)); + DelIPAM("vn9"); +} + +TEST_F(RemoteEcmpTest, EcmpTest_15) { + struct PortInfo input1[] = { + {"vnet9", 9, "9.1.1.1", "00:00:00:01:01:01", 9, 9}, + }; + CreateVmportWithEcmp(input1, 1); + IpamInfo ipam_info_9[] = { + {"9.1.1.0", 24, "9.1.1.254"}, + }; + AddIPAM("vn9", ipam_info_9, 1); + client->WaitForIdle(); + + Ip4Address gw_rt = Ip4Address::from_string("0.0.0.0"); + Ip4Address remote_server_ip1 = Ip4Address::from_string("10.10.10.100"); + Ip4Address remote_server_ip2 = Ip4Address::from_string("10.10.10.101"); + ComponentNHKeyPtr nh_data1(new ComponentNHKey(30, agent_->fabric_vrf_name(), + agent_->router_id(), + remote_server_ip1, + false, + TunnelType::DefaultType())); + ComponentNHKeyPtr nh_data2(new ComponentNHKey(20, agent_->fabric_vrf_name(), + agent_->router_id(), + remote_server_ip2, + false, + TunnelType::DefaultType())); + ComponentNHKeyList comp_nh_list; + comp_nh_list.push_back(nh_data1); + comp_nh_list.push_back(nh_data2); + EcmpTunnelRouteAdd(bgp_peer, "vrf9", gw_rt, 0, + comp_nh_list, false, "vn9", + SecurityGroupList(), PathPreference()); + client->WaitForIdle(); + + TxIpPacket(VmPortGetId(9), "9.1.1.1", "10.1.1.1", 1); + client->WaitForIdle(); + FlowEntry *entry; + entry = FlowGet(VrfGet("vrf9")->vrf_id(), + "9.1.1.1", "10.1.1.1", 1, 0, 0, GetFlowKeyNH(9)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->is_flags_set(FlowEntry::ShortFlow) == false); + + //Update the route to make the remote destination unicast + Inet4TunnelRouteAdd(bgp_peer, "vrf9", gw_rt, 0, + Ip4Address::from_string("8.8.8.8"), + TunnelType::ComputeType(TunnelType::MplsType()), + 100, "vn9", SecurityGroupList(), + PathPreference()); + client->WaitForIdle(); + //Make sure flow has the right nexthop set. + InetUnicastRouteEntry *rt = RouteGet("vrf9", gw_rt, 0); + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->rpf_nh() == rt->GetActiveNextHop()); + + DeleteVmportEnv(input1, 1, true); + client->WaitForIdle(); + EXPECT_TRUE(get_flow_proto()->FlowCount() == 0); + EXPECT_FALSE(VrfFind("vrf9", true)); + DelIPAM("vn9"); +} + +//In case of ECMP route leaked across VRF, there might +//not be local VM peer path in all the VRF, in that case +//we are picking local component NH from composite NH +//Since those component NH are policy disabled, flow +//calculation should pick policy enabled local NH for +//flow key calculation. This test case verifies the same +TEST_F(RemoteEcmpTest, EcmpTest_16) { + AddVrf("vrf9"); + client->WaitForIdle(); + + Ip4Address remote_server_ip1 = Ip4Address::from_string("10.10.10.100"); + //Add default route in vrf 9 pointing to remote VM + AddRemoteVmRoute("vrf9", "0.0.0.0", 0, "vn3"); + AddRemoteVmRoute("vrf2", "0.0.0.0", 0, "vn3"); + client->WaitForIdle(); + + VmInterface *intf = static_cast(VmPortGet(1)); + uint32_t label = intf->label(); + //Add a ECMP route for traffic origiator + ComponentNHKeyPtr nh_data1(new ComponentNHKey(label, MakeUuid(1), + InterfaceNHFlags::INET4, + intf->vm_mac())); + ComponentNHKeyPtr nh_data2(new ComponentNHKey(20, agent_->fabric_vrf_name(), + agent_->router_id(), + remote_server_ip1, + false, + TunnelType::DefaultType())); + ComponentNHKeyList comp_nh_list; + comp_nh_list.push_back(nh_data1); + comp_nh_list.push_back(nh_data2); + //Delete local VM peer route for 1.1.1.1 to simulate + //error case, local component NH would be picked from + //composite NH, than from local vm peer + Ip4Address ip = Ip4Address::from_string("1.1.1.1"); + agent_->fabric_inet4_unicast_table()->DeleteReq(intf->peer(), "vrf2", + ip, 32, NULL); + EcmpTunnelRouteAdd(bgp_peer, "vrf9", ip, 32, + comp_nh_list, false, "vn3", + SecurityGroupList(), PathPreference()); + EcmpTunnelRouteAdd(bgp_peer, "vrf2", ip, 32, + comp_nh_list, false, "vn2", + SecurityGroupList(), PathPreference()); + client->WaitForIdle(); + + //Add a vrf assign ACL to vn1, so that traffic is forwarded via + //vrf 9 + AddVrfAssignNetworkAcl("Acl", 10, "vn2", "vn3", "pass", "vrf9"); + AddLink("virtual-network", "vn2", "access-control-list", "Acl"); + client->WaitForIdle(); + + TxIpPacket(VmPortGetId(1), "1.1.1.1", "10.1.1.1", 1); + client->WaitForIdle(); + + FlowEntry *entry; + FlowEntry *rev_entry; + entry = FlowGet(VrfGet("vrf2")->vrf_id(), + "1.1.1.1", "10.1.1.1", 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + + rev_entry = FlowGet(VrfGet("vrf9")->vrf_id(), + "10.1.1.1", "1.1.1.1", 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(rev_entry != NULL); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + + client->WaitForIdle(); + DelLink("virtual-network", "vn2", "access-control-list", "Acl"); + DelNode("access-control-list", "Acl"); + + DeleteRemoteRoute("vrf9", "1.1.1.1", 32); + DeleteRemoteRoute("vrf9", "0.0.0.0", 0); + DeleteRemoteRoute("vrf2", "0.0.0.0", 0); + client->WaitForIdle(); + DelVrf("vrf9"); + client->WaitForIdle(); +} + +TEST_F(RemoteEcmpTest, INVALID_EcmpTest_17) { + TxIpPacket(VmPortGetId(1), "1.1.1.1", "2.1.1.1", 1); + client->WaitForIdle(); + + FlowEntry *entry = FlowGet(VrfGet("vrf2")->vrf_id(), + "1.1.1.1", "2.1.1.1", 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + + //Reverse flow is no ECMP + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->rpf_nh() != NULL); + + AddRemoteVmRoute("vrf2", "2.1.1.1", 32, "vn10"); + client->WaitForIdle(); + entry = FlowGet(VrfGet("vrf2")->vrf_id(), + "1.1.1.1", "2.1.1.1", 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(entry->is_flags_set(FlowEntry::ShortFlow) == true); +} + +TEST_F(RemoteEcmpTest, DISABLED_EcmpReEval_1) { + TxIpPacket(VmPortGetId(1), "1.1.1.1", "2.1.1.1", 1); + client->WaitForIdle(); + FlowEntry *entry = FlowGet(VrfGet("vrf2")->vrf_id(), + "1.1.1.1", "2.1.1.1", 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + + //Reverse flow is no ECMP + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + + uint32_t ecmp_index = entry->data().component_nh_idx; + //Delete VM corresponding to index component_nh_idx + IntfCfgDel(input2, ecmp_index); + client->WaitForIdle(); + //Enqueue a re-evaluate request + TxIpPacket(VmPortGetId(1), "1.1.1.1", "2.1.1.1", 1); + client->WaitForIdle(); + //Upon interface deletion flow would have been deleted, get flow again + FlowEntry *entry2 = FlowGet(VrfGet("vrf2")->vrf_id(), + "1.1.1.1", "2.1.1.1", 1, 0, 0, GetFlowKeyNH(1)); + + //Verify compoennt NH index is different + EXPECT_TRUE(entry2->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + //make sure new ecmp index is different from that of old ecmp index + EXPECT_TRUE(ecmp_index != entry2->data().component_nh_idx); +} + +//Send a flow for VM to non ECMP dip +//Change the nexthop pointed by non ECMP dip to ECMP dip +//Check if re-evaluation happens +TEST_F(RemoteEcmpTest, EcmpReEval_2) { + //Add a remote VM route for 3.1.1.10 + Ip4Address remote_vm_ip = Ip4Address::from_string("3.1.1.10"); + Ip4Address remote_server_ip = Ip4Address::from_string("10.10.10.10"); + Inet4TunnelRouteAdd(bgp_peer, "vrf2",remote_vm_ip, 32, + remote_server_ip, TunnelType::GREType(), 16, "vn2", + SecurityGroupList(), PathPreference()); + client->WaitForIdle(); + TxIpPacket(VmPortGetId(1), "1.1.1.1", "3.1.1.10", 1); + client->WaitForIdle(); + + FlowEntry *entry = FlowGet(VrfGet("vrf2")->vrf_id(), + "1.1.1.1", "3.1.1.10", 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + + //Convert dip to ECMP nh + ComponentNHKeyList local_comp_nh; + AddRemoteEcmpRoute("vrf2", "3.1.1.10", 32, "vn2", 2, local_comp_nh); + client->WaitForIdle(); + + //Enqueue a re-evaluate request + TxIpPacket(VmPortGetId(1), "1.1.1.1", "3.1.1.10", 1); + client->WaitForIdle(); + entry = FlowGet(VrfGet("vrf2")->vrf_id(), + "1.1.1.1", "3.1.1.10", 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(entry != NULL); + //Since flow already existed, use same old NH which would be at index 0 + EXPECT_TRUE(entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + DeleteRemoteRoute("vrf2", "3.1.1.10", 32); +} + +//Send a flow for VM to non ECMP dip +//Change the nexthop pointed by non ECMP dip to ECMP dip +//Check if re-evaluation happens +TEST_F(RemoteEcmpTest, EcmpReEval_3) { + struct PortInfo input1[] = { + {"vnet10", 10, "10.1.1.1", "00:00:00:01:01:01", 10, 10}, + {"vnet11", 11, "11.1.1.1", "00:00:00:01:01:01", 10, 11}, + }; + CreateVmportFIpEnv(input1, 2); + IpamInfo ipam_info_10[] = { + {"10.1.1.0", 24, "10.1.1.254"}, + {"11.1.1.0", 24, "11.1.1.254"}, + }; + AddIPAM("vn10", ipam_info_10, 2); + client->WaitForIdle(); + + //Associate floating IP with one interface + AddFloatingIpPool("fip-pool3", 3); + AddFloatingIp("fip3", 3, "3.1.1.10"); + AddLink("floating-ip", "fip3", "floating-ip-pool", "fip-pool3"); + AddLink("floating-ip-pool", "fip-pool3", "virtual-network", "vn2"); + AddLink("virtual-machine-interface", "vnet10", "floating-ip", "fip3"); + client->WaitForIdle(); + + TxIpPacket(VmPortGetId(1), "1.1.1.1", "3.1.1.10", 1); + client->WaitForIdle(); + + FlowEntry *entry = FlowGet(VrfGet("vrf2")->vrf_id(), + "1.1.1.1", "3.1.1.10", 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + + AddLink("virtual-machine-interface", "vnet11", "floating-ip", "fip3"); + client->WaitForIdle(); + + //Enqueue a re-evaluate request + TxIpPacket(VmPortGetId(1), "1.1.1.1", "3.1.1.10", 1); + client->WaitForIdle(); + entry = FlowGet(VrfGet("vrf2")->vrf_id(), "1.1.1.1", "3.1.1.10", 1, 0, 0, + GetFlowKeyNH(1)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + + DelLink("virtual-machine-interface", "vnet10", "floating-ip", "fip3"); + DelLink("virtual-machine-interface", "vnet11", "floating-ip", "fip3"); + DelLink("floating-ip-pool", "fip-pool3", "virtual-network", "vn2"); + client->WaitForIdle(); + DeleteVmportFIpEnv(input1, 2, true); + client->WaitForIdle(); + EXPECT_FALSE(VrfFind("vn10:vn10")); + DelIPAM("vn10"); +} + +//Send a packet from vgw to ecmp destination +TEST_F(RemoteEcmpTest, DISABLE_VgwFlag) { + InetInterface::CreateReq(agent_->interface_table(), "vgw1", + InetInterface::SIMPLE_GATEWAY, "vrf2", + Ip4Address(0), 0, Ip4Address(0), Agent::NullString(), + "", Interface::TRANSPORT_ETHERNET); + client->WaitForIdle(); + + InetInterfaceKey *intf_key = new InetInterfaceKey("vgw1"); + InetInterface *intf = InetInterfaceGet("vgw1"); + std::auto_ptr nh_key(new InterfaceNHKey(intf_key, false, + InterfaceNHFlags::INET4, + intf->mac())); + + Ip4Address remote_server_ip1 = Ip4Address::from_string("10.10.10.100"); + ComponentNHKeyPtr nh_data1(new ComponentNHKey(16, nh_key)); + ComponentNHKeyPtr nh_data2(new ComponentNHKey(20, agent_->fabric_vrf_name(), + agent_->router_id(), + remote_server_ip1, + false, + TunnelType::DefaultType())); + Ip4Address ip = Ip4Address::from_string("0.0.0.0"); + ComponentNHKeyList comp_nh_list; + comp_nh_list.push_back(nh_data1); + comp_nh_list.push_back(nh_data2); + EcmpTunnelRouteAdd(bgp_peer, "vrf2", ip, 0, + comp_nh_list, false, "vn2", + SecurityGroupList(), PathPreference()); + client->WaitForIdle(); + + //Send packet on vgw interface + TxIpPacket(intf->id(), "100.1.1.1", "2.2.2.2", 1); + client->WaitForIdle(); + + FlowEntry *entry; + FlowEntry *rev_entry; + entry = FlowGet(VrfGet("vrf2")->vrf_id(), + "2.2.2.2", "100.1.1.1", 1, 0, 0, intf->flow_key_nh()->id()); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + + rev_entry = FlowGet(VrfGet("vrf2")->vrf_id(), + "100.1.1.1", "2.2.2.2", 1, 0, 0, intf->flow_key_nh()->id()); + EXPECT_TRUE(rev_entry != NULL); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + + client->WaitForIdle(); + agent_->fabric_inet4_unicast_table()->DeleteReq(bgp_peer, "vrf2", + ip, 0, new ControllerVmRoute(bgp_peer)); + InetInterface::DeleteReq(agent_->interface_table(), "vgw1"); + client->WaitForIdle(); +} + +// Packet from non-ECMP interface to ECMP Interface +// Send packet from ECMP VM to ECMP MX +// Verify component index is set and correspnding rpf nexthop +TEST_F(RemoteEcmpTest, DISABLED_EcmpTest_1) { + AddRemoteEcmpRoute("vrf1", "0.0.0.0", 0, "vn1", 4, ComponentNHKeyList()); + AddRemoteEcmpRoute("vrf1", "1.1.1.1", 32, "vn1", 4, ComponentNHKeyList()); + + TxIpPacket(VmPortGetId(1), "1.1.1.1", "2.1.1.1", 1); + client->WaitForIdle(); + + AgentRoute *rt = RouteGet("vrf1", Ip4Address::from_string("0.0.0.0"), 0); + InetUnicastRouteEntry *src_rt = static_cast( + RouteGet("vrf1", Ip4Address::from_string("1.1.1.1"), 32)); + + FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), + "1.1.1.1", "2.1.1.1", 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().rpf_nh.get() == src_rt->GetLocalNextHop()); + + //Reverse flow is no ECMP + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().rpf_nh.get() == rt->GetActiveNextHop()); + + DeleteRoute("vrf1", "0.0.0.0", 0, bgp_peer); + client->WaitForIdle(); + sleep(1); + client->WaitForIdle(); + WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); +} + +//Send packet from ECMP of AAP and verify ECMP index and +//RPF nexthop is set fine +TEST_F(RemoteEcmpTest, DISABLED_EcmpTest_2) { + Ip4Address ip = Ip4Address::from_string("1.1.1.10"); + std::string mac("0a:0b:0c:0d:0e:0f"); + + AddEcmpAap("vnet1", 1, ip, mac); + AddEcmpAap("vnet2", 2, ip, mac); + AddRemoteEcmpRoute("vrf1", "0.0.0.0", 0, "vn1", 4, ComponentNHKeyList()); + client->WaitForIdle(); + + TxIpPacket(VmPortGetId(1), "1.1.1.10", "2.1.1.1", 1); + client->WaitForIdle(); + + AgentRoute *rt = RouteGet("vrf1", Ip4Address::from_string("0.0.0.0"), 0); + InetUnicastRouteEntry *src_rt = static_cast( + RouteGet("vrf1", Ip4Address::from_string("1.1.1.10"), 32)); + + FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), + "1.1.1.10", "2.1.1.1", 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().rpf_nh.get() == src_rt->GetLocalNextHop()); + + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().rpf_nh.get() == rt->GetActiveNextHop()); + + DeleteRoute("vrf1", "0.0.0.0", 0, bgp_peer); + client->WaitForIdle(); + sleep(1); + client->WaitForIdle(); + WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); +} + +int main(int argc, char *argv[]) { + GETUSERARGS(); + client = TestInit(init_file, ksync_init, true, true, true, 100*1000); + int ret = RUN_ALL_TESTS(); + client->WaitForIdle(); + TestShutdown(); + delete client; + return ret; +} diff --git a/src/vnsw/agent/pkt/test/test_ecmp_service_vlan.cc b/src/vnsw/agent/pkt/test/test_ecmp_service_vlan.cc new file mode 100644 index 00000000000..f187612f12e --- /dev/null +++ b/src/vnsw/agent/pkt/test/test_ecmp_service_vlan.cc @@ -0,0 +1,1343 @@ +/* + * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved. + */ + +#include "base/os.h" +#include "test/test_cmn_util.h" +#include "test_pkt_util.h" +#include "pkt/flow_proto.h" + +struct PortInfo input1[] = { + {"vnet1", 1, "1.1.1.1", "00:00:00:01:01:01", 2, 1}, +}; +//virtual IP VM +struct PortInfo input2[] = { + {"vnet2", 2, "2.1.1.1", "00:00:00:01:01:01", 2, 2}, + {"vnet3", 3, "2.1.1.1", "00:00:00:02:02:01", 2, 3}, + {"vnet4", 4, "2.1.1.1", "00:00:00:02:02:01", 2, 4}, +}; + +struct PortInfo input3[] = { + {"vnet5", 5, "3.1.1.1", "00:00:00:01:01:01", 3, 5}, + {"vnet6", 6, "3.1.1.2", "00:00:00:02:02:01", 3, 6}, + {"vnet7", 7, "3.1.1.3", "00:00:00:02:02:01", 3, 7}, +}; + +struct PortInfo input4[] = { + {"vnet8", 8, "4.1.1.1", "00:00:00:01:01:01", 4, 8}, +}; + +IpamInfo ipam_info_2[] = { + {"1.1.1.0", 24, "1.1.1.254"}, + {"2.1.1.0", 24, "2.1.1.254"}, +}; + +IpamInfo ipam_info_3[] = { + {"3.1.1.0", 24, "3.1.1.254"}, +}; + +IpamInfo ipam_info_4[] = { + {"4.1.1.0", 24, "4.1.1.254"}, +}; + +class EcmpTest : public ::testing::Test { + virtual void SetUp() { + agent_ = Agent::GetInstance(); + flow_proto_ = agent_->pkt()->get_flow_proto(); + // ----- + // ------------ + // ----- + CreateVmportWithEcmp(input1, 1); + CreateVmportWithEcmp(input2, 3); + CreateVmportFIpEnv(input3, 3); + CreateVmportFIpEnv(input4, 1); + AddIPAM("vn2", ipam_info_2, 2); + AddIPAM("default-project:vn3", ipam_info_3, 1); + AddIPAM("default-project:vn4", ipam_info_4, 1); + client->WaitForIdle(); + for (uint32_t i = 1; i < 9; i++) { + EXPECT_TRUE(VmPortActive(i)); + } + + boost::system::error_code ec; + bgp_peer = CreateBgpPeer(Ip4Address::from_string("0.0.0.1", ec), + "xmpp channel"); + client->WaitForIdle(); + + //Add floating IP for vrf2 interface to talk to + //vrf3 + AddFloatingIpPool("fip-pool1", 1); + AddFloatingIp("fip1", 1, "3.1.1.100"); + AddLink("floating-ip", "fip1", "floating-ip-pool", "fip-pool1"); + AddLink("floating-ip-pool", "fip-pool1", "virtual-network", + "default-project:vn3"); + AddLink("virtual-machine-interface", "vnet2", "floating-ip", "fip1"); + AddLink("virtual-machine-interface", "vnet3", "floating-ip", "fip1"); + AddLink("virtual-machine-interface", "vnet4", "floating-ip", "fip1"); + client->WaitForIdle(); + Ip4Address ip = Ip4Address::from_string("3.1.1.100"); + InetUnicastRouteEntry *rt = RouteGet("default-project:vn3:vn3", ip, 32); + EXPECT_TRUE(rt != NULL); + EXPECT_TRUE(rt->GetActiveNextHop()->GetType() == NextHop::COMPOSITE); + mpls_label_1 = rt->GetActiveLabel(); + + ip = Ip4Address::from_string("2.1.1.1"); + rt = RouteGet("vrf2", ip, 32); + EXPECT_TRUE(rt != NULL); + EXPECT_TRUE(rt->GetActiveNextHop()->GetType() == NextHop::COMPOSITE); + mpls_label_2 = rt->GetActiveLabel(); + + //Add floating IP for vrf3 interfaces to talk vrf4 + AddFloatingIpPool("fip-pool2", 2); + AddFloatingIp("fip2", 2, "4.1.1.100"); + AddLink("floating-ip", "fip2", "floating-ip-pool", "fip-pool2"); + AddLink("floating-ip-pool", "fip-pool2", "virtual-network", + "default-project:vn4"); + AddLink("virtual-machine-interface", "vnet5", "floating-ip", "fip2"); + AddLink("virtual-machine-interface", "vnet6", "floating-ip", "fip2"); + AddLink("virtual-machine-interface", "vnet7", "floating-ip", "fip2"); + client->WaitForIdle(); + ip = Ip4Address::from_string("4.1.1.100"); + rt = RouteGet("default-project:vn4:vn4", ip, 32); + EXPECT_TRUE(rt != NULL); + EXPECT_TRUE(rt->GetActiveNextHop()->GetType() == NextHop::COMPOSITE); + mpls_label_3 = rt->GetActiveLabel(); + + //Populate ethernet interface id + eth_intf_id_ = EthInterfaceGet("vnet0")->id(); + + remote_vm_ip1_ = Ip4Address::from_string("2.2.2.2"); + remote_vm_ip2_ = Ip4Address::from_string("3.3.3.3"); + remote_vm_ip3_ = Ip4Address::from_string("4.4.4.4"); + remote_server_ip_ = Ip4Address::from_string("10.10.1.1"); + + //Add couple of remote VM routes for generating packet + Inet4TunnelRouteAdd(bgp_peer, "vrf2", remote_vm_ip1_, 32, + remote_server_ip_, TunnelType::GREType(), + 30, "vn2", SecurityGroupList(), + PathPreference()); + + Inet4TunnelRouteAdd(bgp_peer, "default-project:vn3:vn3", + remote_vm_ip2_, 32, + remote_server_ip_, TunnelType::GREType(), + 30, "default-project:vn3", SecurityGroupList(), + PathPreference()); + + Inet4TunnelRouteAdd(bgp_peer, "default-project:vn4:vn4", + remote_vm_ip3_, 32, + remote_server_ip_, TunnelType::GREType(), + 30, "default-project:vn4", SecurityGroupList(), + PathPreference()); + client->WaitForIdle(); + FlowStatsTimerStartStop(agent_, true); + } + + void FlushFlowTable() { + client->EnqueueFlowFlush(); + client->WaitForIdle(); + EXPECT_EQ(0U, get_flow_proto()->FlowCount()); + } + + virtual void TearDown() { + FlushFlowTable(); + + DelLink("virtual-machine-interface", "vnet2", "floating-ip", "fip1"); + DelLink("virtual-machine-interface", "vnet3", "floating-ip", "fip1"); + DelLink("virtual-machine-interface", "vnet4", "floating-ip", "fip1"); + DelLink("floating-ip-pool", "fip-pool1", "virtual-network", + "default-project:vn3"); + client->WaitForIdle(); + + DelLink("virtual-machine-interface", "vnet5", "floating-ip", "fip2"); + DelLink("virtual-machine-interface", "vnet6", "floating-ip", "fip2"); + DelLink("virtual-machine-interface", "vnet7", "floating-ip", "fip2"); + DelLink("floating-ip-pool", "fip-pool2", "virtual-network", + "default-project:vn4"); + client->WaitForIdle(); + DeleteVmportEnv(input1, 1, false); + DeleteVmportEnv(input2, 3, true); + DeleteVmportFIpEnv(input3, 3, true); + DeleteVmportFIpEnv(input4, 1, true); + agent_->fabric_inet4_unicast_table()->DeleteReq(bgp_peer, + "vrf2", remote_vm_ip1_, 32, new ControllerVmRoute(bgp_peer)); + agent_->fabric_inet4_unicast_table()->DeleteReq(bgp_peer, + "default-project:vn3:vn3", remote_vm_ip2_, 32, + new ControllerVmRoute(bgp_peer)); + agent_->fabric_inet4_unicast_table()->DeleteReq(bgp_peer, + "default-project:vn4:vn4", remote_vm_ip3_, 32, + new ControllerVmRoute(bgp_peer)); + + DelIPAM("vn2"); + DelIPAM("default-project:vn3"); + DelIPAM("default-project:vn4"); + client->WaitForIdle(); + DeleteBgpPeer(bgp_peer); + EXPECT_FALSE(VrfFind("vrf1", true)); + EXPECT_FALSE(VrfFind("vrf2", true)); + EXPECT_FALSE(VrfFind("default-project:vn3:vn3", true)); + EXPECT_FALSE(VrfFind("default-project:vn4:vn4", true)); + FlowStatsTimerStartStop(agent_, false); + + WAIT_FOR(1000, 1000, (agent_->vrf_table()->Size() == 1)); + } + +public: + FlowProto *get_flow_proto() const { return flow_proto_; } + uint32_t GetServiceVlanNH(uint32_t intf_id, std::string vrf_name) const { + const VmInterface *vm_intf = VmInterfaceGet(intf_id); + const VrfEntry *vrf = VrfGet(vrf_name.c_str()); + uint32_t label = vm_intf->GetServiceVlanLabel(vrf); + int nh_id = agent_->mpls_table()-> + FindMplsLabel(label)->nexthop()->id(); + return nh_id; + } + + void AddRemoteEcmpRoute(const string vrf_name, const string ip, + uint32_t plen, const string vn, int count, + ComponentNHKeyList local_list, + bool same_label = false) { + //If there is a local route, include that always + Ip4Address vm_ip = Ip4Address::from_string(ip); + + ComponentNHKeyList comp_nh_list = local_list; + + int remote_server_ip = 0x0A0A0A0A; + int label = 16; + SecurityGroupList sg_id_list; + + for(int i = 0; i < count; i++) { + ComponentNHKeyPtr comp_nh(new ComponentNHKey( + label, agent_->fabric_vrf_name(), + agent_->router_id(), + Ip4Address(remote_server_ip++), + false, TunnelType::GREType())); + comp_nh_list.push_back(comp_nh); + if (!same_label) { + label++; + } + } + EcmpTunnelRouteAdd(bgp_peer, vrf_name, vm_ip, plen, + comp_nh_list, -1, vn, sg_id_list, + PathPreference()); + } + + void AddLocalVmRoute(const string vrf_name, const string ip, uint32_t plen, + const string vn, uint32_t intf_uuid) { + const VmInterface *vm_intf = static_cast + (VmPortGet(intf_uuid)); + VmInterfaceKey intf_key(AgentKey::ADD_DEL_CHANGE, MakeUuid(intf_uuid), ""); + VnListType vn_list; + vn_list.insert(vn); + LocalVmRoute *local_vm_route = + new LocalVmRoute(intf_key, vm_intf->label(), + VxLanTable::kInvalidvxlan_id, false, vn_list, + InterfaceNHFlags::INET4, SecurityGroupList(), + CommunityList(), PathPreference(), Ip4Address(0), + EcmpLoadBalance(), false, false, + bgp_peer->sequence_number(), false); + InetUnicastAgentRouteTable *rt_table = + agent_->vrf_table()->GetInet4UnicastRouteTable(vrf_name); + + rt_table->AddLocalVmRouteReq(bgp_peer, vrf_name, + Ip4Address::from_string(ip), plen, + static_cast(local_vm_route)); + } + + void AddRemoteVmRoute(const string vrf_name, const string ip, uint32_t plen, + const string vn) { + const Ip4Address addr = Ip4Address::from_string(ip); + VnListType vn_list; + vn_list.insert(vn); + ControllerVmRoute *data = + ControllerVmRoute::MakeControllerVmRoute(bgp_peer, + agent_->fabric_vrf_name(), agent_->router_id(), + vrf_name, addr, TunnelType::GREType(), 16, + vn_list, SecurityGroupList(), + PathPreference(), false, EcmpLoadBalance(), + false); + InetUnicastAgentRouteTable::AddRemoteVmRouteReq(bgp_peer, + vrf_name, addr, plen, data); + } + + void DeleteRemoteRoute(const string vrf_name, const string ip, + uint32_t plen) { + Ip4Address server_ip = Ip4Address::from_string(ip); + agent_->fabric_inet4_unicast_table()->DeleteReq(bgp_peer, + vrf_name, server_ip, plen, new ControllerVmRoute(bgp_peer)); + } + + uint32_t eth_intf_id_; + Ip4Address remote_vm_ip1_; + Ip4Address remote_vm_ip2_; + Ip4Address remote_vm_ip3_; + Ip4Address remote_server_ip_; + uint32_t mpls_label_1; + uint32_t mpls_label_2; + uint32_t mpls_label_3; + BgpPeer *bgp_peer; + Agent *agent_; + FlowProto *flow_proto_; +}; + +TEST_F(EcmpTest, ServiceVlanTest_1) { + struct PortInfo input1[] = { + {"vnet10", 10, "10.1.1.1", "00:00:00:01:01:01", 10, 10}, + {"vnet11", 11, "11.1.1.1", "00:00:00:01:01:01", 10, 11}, + {"vnet12", 12, "12.1.1.1", "00:00:00:01:01:01", 10, 12}, + }; + + CreateVmportWithEcmp(input1, 3); + IpamInfo ipam_info_10[] = { + {"10.1.1.0", 24, "10.1.1.254"}, + {"11.1.1.0", 24, "11.1.1.254"}, + {"12.1.1.0", 24, "12.1.1.254"}, + }; + AddIPAM("vn10", ipam_info_10, 3); + + client->WaitForIdle(); + EXPECT_TRUE(VmPortActive(10)); + EXPECT_TRUE(VmPortActive(11)); + EXPECT_TRUE(VmPortActive(11)); + + //Add service VRF and VN + struct PortInfo input2[] = { + {"vnet13", 13, "11.1.1.252", "00:00:00:01:01:01", 11, 13}, + }; + CreateVmportWithEcmp(input2, 1); + IpamInfo ipam_info_11[] = { + {"11.1.1.0", 24, "11.1.1.254"}, + }; + AddIPAM("vn11", ipam_info_11, 1); + client->WaitForIdle(); + + AddVmPortVrf("ser1", "11.1.1.253", 1); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "routing-instance", "vrf11"); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet10"); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet11"); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet12"); + client->WaitForIdle(); + + const VrfEntry *vrf = Agent::GetInstance()->vrf_table()->FindVrfFromName("vrf11"); + TxIpPacket(VmPortGetId(11), "11.1.1.253", "11.1.1.252", 1, 10, vrf->vrf_id()); + client->WaitForIdle(); + + int nh_id = GetServiceVlanNH(11, "vrf11"); + FlowEntry *entry = FlowGet(VrfGet("vrf11")->vrf_id(), + "11.1.1.253", "11.1.1.252", 1, 0, 0, nh_id); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + + //Reverse flow is no ECMP + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + + DelLink("virtual-machine-interface-routing-instance", "ser1", + "routing-instance", "vrf11"); + DelLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet10"); + DelLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet11"); + DelLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet12"); + DeleteVmportEnv(input1, 3, true); + DeleteVmportEnv(input2, 1, true); + client->WaitForIdle(); + EXPECT_FALSE(VrfFind("vrf11")); + EXPECT_FALSE(VrfFind("vrf10")); + + DelIPAM("vn10"); + DelIPAM("vn11"); +} + +//Service VM with ECMP instantiated in two +//different remote server +//Packet sent from a VM instance to service VM instance +TEST_F(EcmpTest, ServiceVlanTest_2) { + struct PortInfo input1[] = { + {"vnet10", 10, "10.1.1.1", "00:00:00:01:01:01", 10, 10}, + }; + CreateVmportWithEcmp(input1, 1); + IpamInfo ipam_info_10[] = { + {"10.1.1.0", 24, "10.1.1.254"}, + }; + AddIPAM("vn10", ipam_info_10, 1); + client->WaitForIdle(); + + ComponentNHKeyList local_comp_nh; + AddRemoteEcmpRoute("vrf10", "11.1.1.0", 24, "vn11", 2, local_comp_nh); + + uint32_t vrf_id = Agent::GetInstance()->vrf_table()->FindVrfFromName("vrf10")->vrf_id(); + TxIpPacket(VmPortGetId(10), "10.1.1.1", "11.1.1.252", 1, 10, vrf_id); + client->WaitForIdle(); + + FlowEntry *entry = FlowGet(VrfGet("vrf10")->vrf_id(), + "10.1.1.1", "11.1.1.252", 1, 0, 0, GetFlowKeyNH(10)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().vrf == vrf_id); + EXPECT_TRUE(entry->data().dest_vrf == vrf_id); + std::string vn_name_10("vn10"); + std::string vn_name_11("vn11"); + EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_10)); + EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_11)); + + //Reverse flow is no ECMP + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().vrf == vrf_id); + EXPECT_TRUE(rev_entry->data().dest_vrf == vrf_id); + EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_11)); + EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_10)); + + DeleteVmportEnv(input1, 1, true); + DeleteRemoteRoute("vrf10", "11.1.1.0", 24); + client->WaitForIdle(); + EXPECT_FALSE(VrfFind("vrf10")); + DelIPAM("vn10"); +} + +//Service VM with ECMP instantiated in local and remote server +//Packet sent from a VM instance to service VM instance +//Packet sent from a remote VM to service instance +TEST_F(EcmpTest, ServiceVlanTest_3) { + struct PortInfo input1[] = { + {"vnet10", 10, "10.1.1.1", "00:00:00:01:01:01", 10, 10}, + }; + CreateVmportWithEcmp(input1, 1); + IpamInfo ipam_info_10[] = { + {"10.1.1.0", 24, "10.1.1.254"}, + }; + AddIPAM("vn10", ipam_info_10, 1); + client->WaitForIdle(); + + //Add service VM in vrf11 as mgmt VRF + struct PortInfo input2[] = { + {"vnet11", 11, "1.1.1.252", "00:00:00:01:01:01", 11, 11}, + }; + CreateVmportWithEcmp(input2, 1); + IpamInfo ipam_info_11[] = { + {"11.1.1.0", 24, "11.1.1.254"}, + }; + AddIPAM("vn11", ipam_info_11, 1); + client->WaitForIdle(); + + AddVrf("service-vrf1", 12); + AddVmPortVrf("ser1", "10.1.1.2", 1); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "routing-instance", "service-vrf1"); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet11"); + client->WaitForIdle(); + + //Leak aggregarete route to vrf10 + Ip4Address service_vm_ip = Ip4Address::from_string("10.1.1.2"); + InetUnicastRouteEntry *rt = RouteGet("service-vrf1", service_vm_ip, 32); + ComponentNHKeyList comp_nh_list; + EXPECT_TRUE(rt != NULL); + const VlanNH *vlan_nh = static_cast(rt->GetActiveNextHop()); + ComponentNHKeyPtr comp_nh(new ComponentNHKey(rt->GetActiveLabel(), + vlan_nh->GetVlanTag(), vlan_nh->GetIfUuid())); + uint32_t vlan_label = rt->GetActiveLabel(); + comp_nh_list.push_back(comp_nh); + AddRemoteEcmpRoute("vrf10", "11.1.1.0", 24, "vn11", 1, comp_nh_list); + AddRemoteEcmpRoute("service-vrf1", "11.1.1.0", 24, "vn11", 1, comp_nh_list); + + //Leak a remote VM route in service-vrf + AddRemoteVmRoute("service-vrf1", "10.1.1.3", 32, "vn10"); + client->WaitForIdle(); + + uint32_t vrf_id = Agent::GetInstance()->vrf_table()->FindVrfFromName("vrf10")->vrf_id(); + uint32_t service_vrf_id = + Agent::GetInstance()->vrf_table()->FindVrfFromName("service-vrf1")->vrf_id(); + + //Choose some random source and destination port + uint32_t sport = rand() % 65535; + uint32_t dport = rand() % 65535; + for (uint32_t i = 0; i < 32; i++) { + uint32_t hash_id = i + 100; + TxTcpPacket(VmPortGetId(10), "10.1.1.1", "11.1.1.252", sport, dport, + false, hash_id); + client->WaitForIdle(); + + FlowEntry *entry = FlowGet(VrfGet("vrf10")->vrf_id(), + "10.1.1.1", "11.1.1.252", IPPROTO_TCP, sport, dport, + GetFlowKeyNH(10)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().vrf == vrf_id); + if (entry->data().component_nh_idx == 1) { + //Remote component index will be installed at index 1 + //Destination on remote server, hence destination + //VRF will be same as that of interface + EXPECT_TRUE(entry->data().dest_vrf == vrf_id); + } else { + //Destination on local server, destination VRF + //will be that of service VRF + LOG(DEBUG, "Dest vrf " << entry->data().dest_vrf << "Service VRF:" << service_vrf_id); + EXPECT_TRUE(entry->data().dest_vrf == service_vrf_id); + } + + std::string vn_name_10("vn10"); + std::string vn_name_11("vn11"); + EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_10)); + EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_11)); + + //Reverse flow is no ECMP + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + if (entry->data().component_nh_idx == 1) { + //Reverse flow originates remote server, hence + //source VRF is same as destination interface VRF + EXPECT_TRUE(rev_entry->data().vrf == vrf_id); + } else { + //Reverse flow on same server, source VRF will be that + //of service VM vlan interface + EXPECT_TRUE(rev_entry->data().vrf == service_vrf_id); + } + EXPECT_TRUE(rev_entry->data().dest_vrf == vrf_id); + EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_11)); + EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_10)); + sport++; + dport++; + } + + //Leak a remote VM route in service-vrf + AddRemoteVmRoute("service-vrf1", "10.1.1.3", 32, "vn10"); + client->WaitForIdle(); + //Send a packet from a remote VM to service VM + //Below scenario wouldnt happen with vrouter, as destination + //packet came with explicit unicast mpls tag + sport = rand() % 65535; + dport = rand() % 65535; + for (uint32_t i = 0; i < 32; i++) { + char router_id[80]; + strcpy(router_id, Agent::GetInstance()->router_id().to_string().c_str()); + + uint32_t hash_id = i + 200; + TxTcpMplsPacket(eth_intf_id_, "10.11.11.1", router_id, + vlan_label, "10.1.1.3", "11.1.1.252", + sport, dport, false, hash_id); + client->WaitForIdle(); + + int nh_id = + GetActiveLabel(MplsLabel::VPORT_NH, vlan_label)->nexthop()->id(); + FlowEntry *entry = FlowGet(VrfGet("service-vrf1")->vrf_id(), + "10.1.1.3", "11.1.1.252", IPPROTO_TCP, sport, dport, nh_id); + EXPECT_TRUE(entry != NULL); + //No ECMP as packet came with explicit mpls label + //pointing to vlan NH + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + sport++; + dport++; + } + + DelLink("virtual-machine-interface-routing-instance", "ser1", + "routing-instance", "service-vrf1"); + DelLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet11"); + DeleteVmportEnv(input2, 1, true); + DeleteRemoteRoute("service-vrf1", "11.1.1.0", 24); + DelVrf("service-vrf1"); + DeleteVmportEnv(input1, 1, true); + client->WaitForIdle(); + + WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); + EXPECT_FALSE(VrfFind("vrf11")); + EXPECT_FALSE(VrfFind("vrf10")); + EXPECT_FALSE(VrfFind("service-vrf1")); + DelIPAM("vn10"); + DelIPAM("vn11"); +} + + +//Service VM with ECMP instantaited in local server +//Test with packet from virtual-machine to service VM instance +//Test with packet from external-world to service VM instance +TEST_F(EcmpTest, ServiceVlanTest_4) { + struct PortInfo input1[] = { + {"vnet10", 10, "10.1.1.1", "00:00:00:01:01:01", 10, 10}, + }; + CreateVmportWithEcmp(input1, 1); + IpamInfo ipam_info_10[] = { + {"10.1.1.0", 24, "10.1.1.254"}, + }; + AddIPAM("vn10", ipam_info_10, 1); + client->WaitForIdle(); + + //Add service VM in vrf11 as mgmt VRF + struct PortInfo input2[] = { + {"vnet11", 11, "1.1.1.252", "00:00:00:01:01:01", 11, 11}, + {"vnet12", 12, "1.1.1.253", "00:00:00:01:01:01", 11, 12}, + }; + CreateVmportWithEcmp(input2, 2); + IpamInfo ipam_info_11[] = { + {"1.1.1.0", 24, "1.1.1.254"}, + }; + AddIPAM("vn11", ipam_info_11, 1); + client->WaitForIdle(); + + AddVrf("service-vrf1", 12); + AddVmPortVrf("ser1", "10.1.1.2", 1); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "routing-instance", "service-vrf1"); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet11"); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet12"); + client->WaitForIdle(); + + uint32_t vrf_id = agent_->vrf_table()->FindVrfFromName("vrf10")->vrf_id(); + uint32_t service_vrf_id = + agent_->vrf_table()->FindVrfFromName("service-vrf1")->vrf_id(); + + //Leak aggregarete route to vrf10 + Ip4Address service_vm_ip = Ip4Address::from_string("10.1.1.2"); + InetUnicastRouteEntry *rt = RouteGet("service-vrf1", service_vm_ip, 32); + EXPECT_TRUE(rt != NULL); + + DBEntryBase::KeyPtr key_ref = rt->GetActiveNextHop()->GetDBRequestKey(); + CompositeNHKey *composite_nh_key = static_cast + (key_ref.get()); + ComponentNHKeyPtr comp_nh_data(new ComponentNHKey(rt->GetActiveLabel(), + Composite::LOCAL_ECMP, true, composite_nh_key->component_nh_key_list(), + "service-vrf1")); + ComponentNHKeyList comp_nh_list; + comp_nh_list.push_back(comp_nh_data); + AddRemoteEcmpRoute("vrf10", "11.1.1.0", 24, "vn11", 0, comp_nh_list); + AddRemoteEcmpRoute("service-vrf1", "11.1.1.0", 24, "vn11", 0, comp_nh_list); + client->WaitForIdle(); + + Ip4Address dest_ip = Ip4Address::from_string("11.1.1.0"); + InetUnicastRouteEntry *rt_vrf10 = RouteGet("vrf10", dest_ip, 24); + const NextHop *nh_vrf10 = rt_vrf10->GetActiveNextHop(); + EXPECT_TRUE(dynamic_cast(nh_vrf10) != NULL); + + InetUnicastRouteEntry *rt_svrf = RouteGet("service-vrf1", dest_ip, 24); + const NextHop *nh_svrf = rt_svrf->GetActiveNextHop(); + EXPECT_TRUE(dynamic_cast(nh_svrf) != NULL); + + //Choose some random source and destination port + uint32_t sport = rand() % 65535; + uint32_t dport = rand() % 65535; + for (uint32_t i = 0; i < 32; i++) { + uint32_t hash_id = i + 100; + TxTcpPacket(VmPortGetId(10), "10.1.1.1", "11.1.1.252", sport, dport, + false, hash_id); + client->WaitForIdle(); + + FlowEntry *entry = FlowGet(VrfGet("vrf10")->vrf_id(), + "10.1.1.1", "11.1.1.252", IPPROTO_TCP, sport, dport, + GetFlowKeyNH(10)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().vrf == vrf_id); + //Packet destined to service interface, vrf has to be + //service vlan VRF + EXPECT_TRUE(entry->data().dest_vrf == service_vrf_id); + + std::string vn_name_10("vn10"); + std::string vn_name_11("vn11"); + EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_10)); + EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_11)); + + //Reverse flow is no ECMP + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + //Packet from service interface, vrf has to be + //service vlan VRF + EXPECT_TRUE(rev_entry->data().vrf == service_vrf_id); + EXPECT_TRUE(rev_entry->data().dest_vrf == vrf_id); + EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_11)); + EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_10)); + sport++; + dport++; + } + + //Leak a remote VM route in service-vrf + AddRemoteVmRoute("service-vrf1", "10.1.1.3", 32, "vn10"); + client->WaitForIdle(); + uint32_t label = rt->GetActiveLabel(); + //Send a packet from a remote VM to service VM + sport = rand() % 65535; + dport = rand() % 65535; + for (uint32_t i = 0; i < 32; i++) { + char router_id[80]; + strcpy(router_id, Agent::GetInstance()->router_id().to_string().c_str()); + + uint32_t hash_id = i + 200; + TxTcpMplsPacket(eth_intf_id_, "10.11.11.1", router_id, + label, "10.1.1.3", "11.1.1.252", + sport, dport, false, hash_id); + client->WaitForIdle(); + + int nh_id = GetActiveLabel(MplsLabel::VPORT_NH, label)->nexthop()->id(); + FlowEntry *entry = FlowGet(VrfGet("service-vrf1")->vrf_id(), + "10.1.1.3", "11.1.1.252", IPPROTO_TCP, sport, dport, nh_id); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().vrf == service_vrf_id); + //Packet destined to service interface, vrf has to be + //service vlan VRF + LOG(DEBUG, "Vrf" << entry->data().dest_vrf << ":" << service_vrf_id); + EXPECT_TRUE(entry->data().dest_vrf == service_vrf_id); + + std::string vn_name_10("vn10"); + std::string vn_name_11("vn11"); + EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_10)); + EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_11)); + + //Reverse flow is no ECMP + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + //Packet from service interface, vrf has to be + //service vlan VRF + EXPECT_TRUE(rev_entry->data().vrf == service_vrf_id); + EXPECT_TRUE(rev_entry->data().dest_vrf == service_vrf_id); + EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_11)); + EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_10)); + sport++; + dport++; + } + + DelLink("virtual-machine-interface-routing-instance", "ser1", + "routing-instance", "service-vrf1"); + DelLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet11"); + DelLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet12"); + DeleteVmportEnv(input2, 2, true); + DelVrf("service-vrf1"); + client->WaitForIdle(2); + DeleteVmportEnv(input1, 1, true); + client->WaitForIdle(); + EXPECT_FALSE(VrfFind("vrf11")); + EXPECT_FALSE(VrfFind("vrf10")); + EXPECT_FALSE(VrfFind("service-vrf1")); + DelIPAM("vn10"); + DelIPAM("vn11"); +} + +//Packet from a right service VM interface(both service vm instance launched on same server) +//reaching end host on same machine +TEST_F(EcmpTest, ServiceVlanTest_5) { + struct PortInfo input1[] = { + {"vnet11", 11, "11.1.1.1", "00:00:00:01:01:01", 11, 11}, + }; + CreateVmportWithEcmp(input1, 1); + IpamInfo ipam_info_11[] = { + {"11.1.1.0", 24, "11.1.1.254"}, + }; + AddIPAM("vn11", ipam_info_11, 1); + client->WaitForIdle(); + + //Add service VM in vrf13 as mgmt VRF + struct PortInfo input2[] = { + {"vnet13", 13, "1.1.1.252", "00:00:00:01:01:01", 13, 13}, + {"vnet14", 14, "1.1.1.253", "00:00:00:01:01:01", 13, 14}, + }; + CreateVmportWithEcmp(input2, 2); + IpamInfo ipam_info_13[] = { + {"1.1.1.0", 24, "1.1.1.254"}, + }; + AddIPAM("vn13", ipam_info_13, 1); + client->WaitForIdle(); + + AddVrf("service-vrf1", 12); + AddVmPortVrf("ser1", "11.1.1.2", 1); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "routing-instance", "service-vrf1"); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet13"); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet14"); + client->WaitForIdle(); + + const MacAddress mac("02:00:00:00:00:02"); + EXPECT_TRUE(L2RouteFind("service-vrf1",mac)); + + uint32_t vrf_id = Agent::GetInstance()->vrf_table()->FindVrfFromName("vrf11")->vrf_id(); + uint32_t service_vrf_id = + Agent::GetInstance()->vrf_table()->FindVrfFromName("service-vrf1")->vrf_id(); + + Ip4Address service_vm_ip = Ip4Address::from_string("11.1.1.2"); + InetUnicastRouteEntry *rt = RouteGet("service-vrf1", service_vm_ip, 32); + EXPECT_TRUE(rt != NULL); + + DBEntryBase::KeyPtr key_ref = rt->GetActiveNextHop()->GetDBRequestKey(); + CompositeNHKey *composite_nh_key = static_cast + (key_ref.get()); + ComponentNHKeyPtr comp_nh_data(new ComponentNHKey(rt->GetActiveLabel(), + Composite::LOCAL_ECMP, true, composite_nh_key->component_nh_key_list(), + "service-vrf1")); + ComponentNHKeyList comp_nh_list; + comp_nh_list.push_back(comp_nh_data); + //Leak a aggregarate route to service VRF + AddRemoteEcmpRoute("service-vrf1", "10.1.1.0", 24, "vn10", 0, comp_nh_list); + //Leak a aggresgarate route vrf 11 + AddRemoteEcmpRoute("vrf11", "10.1.1.0", 24, "vn10", 0, comp_nh_list); + //Leak route for vm11 to service vrf + AddLocalVmRoute("service-vrf1", "11.1.1.1", 32, "vn11", 11); + client->WaitForIdle(); + + uint32_t vnet13_vlan_nh = GetServiceVlanNH(13, "service-vrf1"); + uint32_t vnet14_vlan_nh = GetServiceVlanNH(14, "service-vrf1"); + + //Choose some random source and destination port + uint32_t sport = rand() % 65535; + uint32_t dport = rand() % 65535; + for (uint32_t i = 0; i < 32; i++) { + uint32_t hash_id = i + 100; + TxTcpPacket(VmPortGetId(13), "10.1.1.1", "11.1.1.1", sport, dport, + false, hash_id, service_vrf_id); + client->WaitForIdle(); + + FlowEntry *entry = FlowGet(service_vrf_id, + "10.1.1.1", "11.1.1.1", IPPROTO_TCP, sport, dport, + vnet13_vlan_nh); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().vrf == service_vrf_id); + //Packet destined to vm11, vrf has to be vrf 11 + EXPECT_TRUE(entry->data().dest_vrf == vrf_id); + + std::string vn_name_10("vn10"); + std::string vn_name_11("vn11"); + EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_10)); + EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_11)); + + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + //Packet to service interface, vrf has to be + //service vlan VRF + EXPECT_TRUE(rev_entry->data().vrf == vrf_id); + EXPECT_TRUE(rev_entry->data().dest_vrf == service_vrf_id); + EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_11)); + EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_10)); + sport++; + dport++; + } + + //Send traffice from second service interface and expect things to be fine + for (uint32_t i = 0; i < 32; i++) { + uint32_t hash_id = i + 200; + TxTcpPacket(VmPortGetId(14), "10.1.1.1", "11.1.1.1", sport, dport, + false, hash_id, service_vrf_id); + client->WaitForIdle(); + + FlowEntry *entry = FlowGet(service_vrf_id, + "10.1.1.1", "11.1.1.1", IPPROTO_TCP, sport, dport, + vnet14_vlan_nh); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().vrf == service_vrf_id); + //Packet destined to vm11, vrf has to be vrf11 + EXPECT_TRUE(entry->data().dest_vrf == vrf_id); + + std::string vn_name_10("vn10"); + std::string vn_name_11("vn11"); + EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_10)); + EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_11)); + + //make sure reverse flow points to right index + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + + //Packet from vm11 to service vrf + EXPECT_TRUE(rev_entry->data().vrf == vrf_id); + EXPECT_TRUE(rev_entry->data().dest_vrf == service_vrf_id); + EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_11)); + EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_10)); + sport++; + dport++; + } + + DelLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet13"); + client->WaitForIdle(); + EXPECT_TRUE(L2RouteFind("service-vrf1",mac)); + + DelLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet14"); + client->WaitForIdle(); + EXPECT_FALSE(L2RouteFind("service-vrf1",mac)); + + DelLink("virtual-machine-interface-routing-instance", "ser1", + "routing-instance", "service-vrf1"); + DeleteVmportEnv(input2, 2, true); + DelVrf("service-vrf1"); + client->WaitForIdle(); + DeleteVmportEnv(input1, 1, true); + client->WaitForIdle(); + EXPECT_FALSE(VrfFind("vrf11")); + EXPECT_FALSE(VrfFind("vrf13")); + EXPECT_FALSE(VrfFind("service-vrf1")); + DelIPAM("vn11"); + DelIPAM("vn13"); +} + +//Packet from a right service VM interface(both service vm instance launched on same server) +//reaching end host on a remote machine +//Packet from remote host reaching service VM +//Packet from remote ecmp host reaching service VM +TEST_F(EcmpTest, ServiceVlanTest_6) { + //Add service VM in vrf13 as mgmt VRF + struct PortInfo input2[] = { + {"vnet13", 13, "1.1.1.252", "00:00:00:01:01:01", 13, 13}, + {"vnet14", 14, "1.1.1.253", "00:00:00:01:01:01", 13, 14}, + }; + CreateVmportWithEcmp(input2, 2); + IpamInfo ipam_info_13[] = { + {"1.1.1.0", 24, "1.1.1.254"}, + }; + AddIPAM("vn13", ipam_info_13, 1); + client->WaitForIdle(); + + AddVrf("service-vrf1", 12); + AddVmPortVrf("ser1", "11.1.1.2", 1); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "routing-instance", "service-vrf1"); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet13"); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet14"); + client->WaitForIdle(); + + std::string vn_name_10("vn10"); + std::string vn_name_11("vn11"); + uint32_t service_vrf_id = + Agent::GetInstance()->vrf_table()->FindVrfFromName("service-vrf1")->vrf_id(); + + Ip4Address service_vm_ip = Ip4Address::from_string("11.1.1.2"); + InetUnicastRouteEntry *rt = RouteGet("service-vrf1", service_vm_ip, 32); + EXPECT_TRUE(rt != NULL); + uint32_t mpls_label = rt->GetActiveLabel(); + + DBEntryBase::KeyPtr key_ref = rt->GetActiveNextHop()->GetDBRequestKey(); + CompositeNHKey *composite_nh_key = static_cast + (key_ref.get()); + ComponentNHKeyPtr comp_nh_data(new ComponentNHKey(rt->GetActiveLabel(), + Composite::LOCAL_ECMP, true, composite_nh_key->component_nh_key_list(), + "service-vrf1")); + ComponentNHKeyList comp_nh_list; + comp_nh_list.push_back(comp_nh_data); + + //Leak a aggregarate route to service VRF + AddRemoteEcmpRoute("service-vrf1", "10.1.1.0", 24, "vn10", 0, comp_nh_list); + //Leak routes from right vrf to service vrf + AddRemoteVmRoute("service-vrf1", "11.1.1.1", 32, "vn11"); + comp_nh_list.clear(); + AddRemoteEcmpRoute("service-vrf1", "11.1.1.3", 32, "vn11", 2, comp_nh_list); + client->WaitForIdle(); + + uint32_t vnet13_vlan_nh = GetServiceVlanNH(13, "service-vrf1"); + uint32_t vnet14_vlan_nh = GetServiceVlanNH(14, "service-vrf1"); + //Choose some random source and destination port + uint32_t sport = rand() % 65535; + uint32_t dport = rand() % 65535; + for (uint32_t i = 0; i < 10; i++) { + uint32_t hash_id = i + 100; + TxTcpPacket(VmPortGetId(13), "10.1.1.1", "11.1.1.1", sport, dport, + false, hash_id, service_vrf_id); + client->WaitForIdle(); + + FlowEntry *entry = FlowGet(service_vrf_id, + "10.1.1.1", "11.1.1.1", IPPROTO_TCP, sport, dport, + vnet13_vlan_nh); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().vrf == service_vrf_id); + //Packet destined to remote server, vrf would be same as service vrf + EXPECT_TRUE(entry->data().dest_vrf == service_vrf_id); + + EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_10)); + EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_11)); + + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + //Packet to service interface, vrf has to be + //service vlan VRF + EXPECT_TRUE(rev_entry->data().vrf == service_vrf_id); + EXPECT_TRUE(rev_entry->data().dest_vrf == service_vrf_id); + EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_11)); + EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_10)); + sport++; + dport++; + } + + //Send traffic from second service interface + for (uint32_t i = 0; i < 10; i++) { + uint32_t hash_id = i + 200; + TxTcpPacket(VmPortGetId(14), "10.1.1.1", "11.1.1.1", sport, dport, + false, hash_id, service_vrf_id); + client->WaitForIdle(); + + FlowEntry *entry = FlowGet(service_vrf_id, + "10.1.1.1", "11.1.1.1", IPPROTO_TCP, sport, dport, + vnet14_vlan_nh); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().vrf == service_vrf_id); + //Packet destined to remote server, vrf would be same as service vrf + EXPECT_TRUE(entry->data().dest_vrf == service_vrf_id); + + EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_10)); + EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_11)); + + //make sure reverse flow points to right index + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + + EXPECT_TRUE(rev_entry->data().vrf == service_vrf_id); + EXPECT_TRUE(rev_entry->data().dest_vrf == service_vrf_id); + EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_11)); + EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_10)); + sport++; + dport++; + } + + char router_id[80]; + strcpy(router_id, Agent::GetInstance()->router_id().to_string().c_str()); + //Send traffic from remote VM service + for (uint32_t i = 0; i < 10; i++) { + uint32_t hash_id = i + 100; + TxTcpMplsPacket(eth_intf_id_, "10.10.10.10", router_id, + mpls_label, "11.1.1.1", "10.1.1.1", + sport, dport, false, hash_id); + client->WaitForIdle(); + + int nh_id = + GetActiveLabel(MplsLabel::VPORT_NH, mpls_label)->nexthop()->id(); + FlowEntry *entry = FlowGet(service_vrf_id, + "11.1.1.1", "10.1.1.1", IPPROTO_TCP, sport, dport, nh_id); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().vrf == service_vrf_id); + EXPECT_TRUE(entry->data().dest_vrf == service_vrf_id); + EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_11)); + EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_10)); + + //make sure reverse flow is no ecmp + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().vrf == service_vrf_id); + EXPECT_TRUE(rev_entry->data().dest_vrf == service_vrf_id); + EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_10)); + EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_11)); + sport++; + dport++; + } + + //Send traffic from remote VM which is also ecmp + for (uint32_t i = 0; i < 10; i++) { + uint32_t hash_id = i + 200; + TxTcpMplsPacket(eth_intf_id_, "10.10.10.10", router_id, + mpls_label, "11.1.1.3", "10.1.1.1", + sport, dport, false, hash_id); + client->WaitForIdle(); + + int nh_id = + GetActiveLabel(MplsLabel::VPORT_NH, mpls_label)->nexthop()->id(); + FlowEntry *entry = FlowGet(service_vrf_id, + "11.1.1.3", "10.1.1.1", IPPROTO_TCP, sport, dport, nh_id); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().vrf == service_vrf_id); + EXPECT_TRUE(entry->data().dest_vrf == service_vrf_id); + EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_11)); + EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_10)); + + //make sure reverse flow is no ecmp + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().vrf == service_vrf_id); + EXPECT_TRUE(rev_entry->data().dest_vrf == service_vrf_id); + EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_10)); + EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_11)); + sport++; + dport++; + } + + //Send traffic from remote VM which is also ecmp + for (uint32_t i = 0; i < 10; i++) { + uint32_t hash_id = i + 100; + TxTcpMplsPacket(eth_intf_id_, "10.10.10.11", router_id, + mpls_label, "11.1.1.3", "10.1.1.1", + sport, dport, false, hash_id); + client->WaitForIdle(); + int nh_id = + GetActiveLabel(MplsLabel::VPORT_NH, mpls_label)->nexthop()->id(); + FlowEntry *entry = FlowGet(service_vrf_id, + "11.1.1.3", "10.1.1.1", IPPROTO_TCP, sport, dport, nh_id); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + + EXPECT_TRUE(entry->data().dest_vrf == service_vrf_id); + EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_11)); + EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_10)); + + //make sure reverse flow is no ecmp + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().vrf == service_vrf_id); + EXPECT_TRUE(rev_entry->data().dest_vrf == service_vrf_id); + EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_10)); + EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_11)); + sport++; + dport++; + } + + DeleteRemoteRoute("service-vrf1", "10.1.1.0", 24); + DeleteRemoteRoute("service-vrf1", "11.1.1.3", 32); + DelLink("virtual-machine-interface-routing-instance", "ser1", + "routing-instance", "service-vrf1"); + DelLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet13"); + DelLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet14"); + client->WaitForIdle(); + DeleteVmportEnv(input2, 2, true); + DelVrf("service-vrf1"); + client->WaitForIdle(); + EXPECT_FALSE(VrfFind("vrf13")); + EXPECT_FALSE(VrfFind("service-vrf1")); + DelIPAM("vn13"); +} + +// Packet from a right service VM interface(one service instance on +// local server, one on remote server) reaching end host on a local machine +TEST_F(EcmpTest, ServiceVlanTest_7) { + struct PortInfo input1[] = { + {"vnet11", 11, "11.1.1.1", "00:00:00:01:01:01", 11, 11}, + }; + CreateVmportWithEcmp(input1, 1); + IpamInfo ipam_info_11[] = { + {"11.1.1.0", 24, "11.1.1.254"}, + }; + AddIPAM("vn11", ipam_info_11, 1); + client->WaitForIdle(); + + //Add service VM in vrf13 as mgmt VRF + struct PortInfo input2[] = { + {"vnet13", 13, "1.1.1.252", "00:00:00:01:01:01", 13, 13}, + }; + CreateVmportWithEcmp(input2, 1); + IpamInfo ipam_info_12[] = { + {"12.1.1.0", 24, "12.1.1.254"}, + }; + AddIPAM("vn12", ipam_info_12, 1); + client->WaitForIdle(); + + AddVrf("service-vrf1", 12); + AddVmPortVrf("ser1", "11.1.1.2", 1); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "routing-instance", "service-vrf1"); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet13"); + client->WaitForIdle(); + + uint32_t vrf_id = Agent::GetInstance()->vrf_table()->FindVrfFromName("vrf11")->vrf_id(); + uint32_t service_vrf_id = + Agent::GetInstance()->vrf_table()->FindVrfFromName("service-vrf1")->vrf_id(); + + Ip4Address service_vm_ip = Ip4Address::from_string("11.1.1.2"); + InetUnicastRouteEntry *rt = RouteGet("service-vrf1", service_vm_ip, 32); + EXPECT_TRUE(rt != NULL); + + ComponentNHKeyList comp_nh_list; + const VlanNH *vlan_nh = static_cast(rt->GetActiveNextHop()); + ComponentNHKeyPtr comp_nh_data(new ComponentNHKey( + rt->GetActiveLabel(), vlan_nh->GetVlanTag(), vlan_nh->GetIfUuid())); + comp_nh_list.push_back(comp_nh_data); + + //Leak a aggregarate route to service VRF + AddRemoteEcmpRoute("service-vrf1", "10.1.1.0", 24, "vn10", 1, comp_nh_list); + //Leak a aggregarate route tp vrf11 + AddRemoteEcmpRoute("vrf11", "10.1.1.0", 24, "vn10", 1, comp_nh_list); + //Leak route for vm11 to service vrf + AddLocalVmRoute("service-vrf1", "11.1.1.1", 32, "vn11", 11); + client->WaitForIdle(); + uint32_t vnet13_vlan_nh = GetServiceVlanNH(13, "service-vrf1"); + + //Choose some random source and destination port + uint32_t sport = rand() % 65535; + uint32_t dport = rand() % 65535; + for (uint32_t i = 0; i < 32; i++) { + uint32_t hash_id = i + 100; + TxTcpPacket(VmPortGetId(13), "10.1.1.1", "11.1.1.1", sport, dport, + false, hash_id, service_vrf_id); + client->WaitForIdle(); + + FlowEntry *entry = FlowGet(service_vrf_id, + "10.1.1.1", "11.1.1.1", IPPROTO_TCP, sport, dport, + vnet13_vlan_nh); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().vrf == service_vrf_id); + //Packet destined to vm11, vrf has to be vrf 11 + EXPECT_TRUE(entry->data().dest_vrf == vrf_id); + + std::string vn_name_10("vn10"); + std::string vn_name_11("vn11"); + EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_10)); + EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_11)); + + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + //Packet to service interface, vrf has to be + //service vlan VRF + EXPECT_TRUE(rev_entry->data().vrf == vrf_id); + EXPECT_TRUE(rev_entry->data().dest_vrf == service_vrf_id); + EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_11)); + EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_10)); + sport++; + dport++; + } + + DelLink("virtual-machine-interface-routing-instance", "ser1", + "routing-instance", "service-vrf1"); + DelLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet13"); + DeleteVmportEnv(input2, 1, true); + + DeleteVmportEnv(input1, 1, true); + DelVrf("service-vrf1"); + client->WaitForIdle(); + EXPECT_FALSE(VrfFind("vrf11")); + EXPECT_FALSE(VrfFind("vrf13")); + EXPECT_FALSE(VrfFind("service-vrf1")); + DelIPAM("vn11"); + DelIPAM("vn12"); +} + +// Packet from a right service VM interface (one service instance on local +// server, one on remote server), reaching end host on a remote machine +TEST_F(EcmpTest,ServiceVlanTest_8) { + //Add service VM in vrf13 as mgmt VRF + struct PortInfo input2[] = { + {"vnet13", 13, "1.1.1.252", "00:00:00:01:01:01", 13, 13}, + }; + CreateVmportWithEcmp(input2, 1); + IpamInfo ipam_info_13[] = { + {"1.1.1.0", 24, "1.1.1.254"}, + }; + AddIPAM("vn13", ipam_info_13, 1); + client->WaitForIdle(); + + AddVrf("service-vrf1", 12); + AddVmPortVrf("ser1", "11.1.1.2", 1); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "routing-instance", "service-vrf1"); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet13"); + client->WaitForIdle(); + + uint32_t service_vrf_id = + Agent::GetInstance()->vrf_table()->FindVrfFromName("service-vrf1")->vrf_id(); + + Ip4Address service_vm_ip = Ip4Address::from_string("11.1.1.2"); + InetUnicastRouteEntry *rt = RouteGet("service-vrf1", service_vm_ip, 32); + EXPECT_TRUE(rt != NULL); + + ComponentNHKeyList comp_nh_list; + const VlanNH *vlan_nh = static_cast(rt->GetActiveNextHop()); + ComponentNHKeyPtr comp_nh_data(new ComponentNHKey( + rt->GetActiveLabel(), vlan_nh->GetVlanTag(), + vlan_nh->GetIfUuid())); + comp_nh_list.push_back(comp_nh_data); + + //Leak a aggregarate route to service VRF + AddRemoteEcmpRoute("service-vrf1", "10.1.1.0", 24, "vn10", 1, comp_nh_list); + //Leak route a for remote server to service vrf + AddRemoteVmRoute("service-vrf1", "11.1.1.1", 32, "vn11"); + client->WaitForIdle(); + + uint32_t vnet13_vlan_nh = GetServiceVlanNH(13, "service-vrf1"); + //Choose some random source and destination port + uint32_t sport = rand() % 65535; + uint32_t dport = rand() % 65535; + for (uint32_t i = 0; i < 32; i++) { + uint32_t hash_id = rand() % 65535; + TxTcpPacket(VmPortGetId(13), "10.1.1.1", "11.1.1.1", sport, dport, + false, hash_id, service_vrf_id); + client->WaitForIdle(); + + FlowEntry *entry = FlowGet(service_vrf_id, + "10.1.1.1", "11.1.1.1", IPPROTO_TCP, sport, dport, + vnet13_vlan_nh); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().vrf == service_vrf_id); + //Packet destined to remote server, vrf has to be service vrf + EXPECT_TRUE(entry->data().dest_vrf == service_vrf_id); + std::string vn_name_10("vn10"); + std::string vn_name_11("vn11"); + EXPECT_TRUE(VnMatch(entry->data().source_vn_list, vn_name_10)); + EXPECT_TRUE(VnMatch(entry->data().dest_vn_list, vn_name_11)); + EXPECT_TRUE(entry->rpf_nh() == VmPortGet(13)->flow_key_nh()); + + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + //Packet to service interface, vrf has to be + //service vlan VRF + EXPECT_TRUE(rev_entry->data().vrf == service_vrf_id); + EXPECT_TRUE(rev_entry->data().dest_vrf == service_vrf_id); + EXPECT_TRUE(VnMatch(rev_entry->data().source_vn_list, vn_name_11)); + EXPECT_TRUE(VnMatch(rev_entry->data().dest_vn_list, vn_name_10)); + sport++; + dport++; + } + + DelLink("virtual-machine-interface-routing-instance", "ser1", + "routing-instance", "service-vrf1"); + DelLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet13"); + DeleteVmportEnv(input2, 1, true); + DelVrf("service-vrf1"); + client->WaitForIdle(); + EXPECT_FALSE(VrfFind("vrf13")); + EXPECT_FALSE(VrfFind("service-vrf1")); + DelIPAM("vn13"); +} + +int main(int argc, char *argv[]) { + GETUSERARGS(); + client = TestInit(init_file, ksync_init, true, true, true, 100*1000); + int ret = RUN_ALL_TESTS(); + client->WaitForIdle(); + TestShutdown(); + delete client; + return ret; +} diff --git a/src/vnsw/agent/pkt/test/test_fip_dst_ecmp.cc b/src/vnsw/agent/pkt/test/test_fip_dst_ecmp.cc deleted file mode 100644 index 1f0f9f0c346..00000000000 --- a/src/vnsw/agent/pkt/test/test_fip_dst_ecmp.cc +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved. - */ - -#include "base/os.h" -#include "test/test_cmn_util.h" -#include "test_pkt_util.h" -#include "pkt/flow_proto.h" - -#define AGE_TIME 10*1000 -#define VN2 "default-project:vn2" -#define VRF2 "default-project:vn2:vn2" - -void RouterIdDepInit(Agent *agent) { -} - -struct PortInfo input1[] = { - {"vnet1", 1, "1.1.1.1", "00:00:00:01:01:01", 1, 1}, -}; -IpamInfo ipam_info[] = { - {"1.1.1.0", 24, "1.1.1.10"}, -}; - -class FipEcmpTest : public ::testing::Test { - virtual void SetUp() { - agent_ = Agent::GetInstance(); - - boost::system::error_code ec; - bgp_peer = CreateBgpPeer(Ip4Address::from_string("0.0.0.1", ec), - "xmpp channel"); - client->WaitForIdle(); - - flow_proto_ = agent_->pkt()->get_flow_proto(); - CreateVmportWithEcmp(input1, 1); - client->WaitForIdle(); - AddIPAM("vn1", ipam_info, 1); - client->WaitForIdle(); - AddVn(VN2, 2); - AddVrf(VRF2); - AddLink("virtual-network", VN2, "routing-instance", - VRF2); - client->WaitForIdle(); - //Attach floating-ip - //Add floating IP for vnet1 - AddFloatingIpPool("fip-pool1", 1); - AddFloatingIp("fip1", 1, "2.1.1.1", "1.1.1.1"); - AddLink("floating-ip", "fip1", "floating-ip-pool", "fip-pool1"); - AddLink("floating-ip-pool", "fip-pool1", "virtual-network", - VN2); - client->WaitForIdle(); - AddLink("virtual-machine-interface", "vnet1", "floating-ip", "fip1"); - client->WaitForIdle(); - - strcpy(router_id, agent_->router_id().to_string().c_str()); - strcpy(MX_0, "100.1.1.1"); - strcpy(MX_1, "100.1.1.2"); - strcpy(MX_2, "100.1.1.3"); - strcpy(MX_3, "100.1.1.4"); - - const VmInterface *vmi = static_cast(VmPortGet(1)); - vm1_label = vmi->label(); - eth_intf_id = EthInterfaceGet("vnet0")->id(); - } - - virtual void TearDown() { - DelLink("virtual-network", VN2, "routing-instance", - VRF2); - DelLink("floating-ip", "fip1", "floating-ip-pool", "fip-pool1"); - DelLink("floating-ip-pool", "fip-pool1", - "virtual-network", VN2); - DelLink("virtual-machine-interface", "vnet1", "floating-ip", "fip1"); - DelFloatingIp("fip1"); - DelFloatingIpPool("fip-pool1"); - client->WaitForIdle(); - - DeleteVmportEnv(input1, 1, true); - DelVn(VN2); - DelVrf(VRF2); - client->WaitForIdle(); - DelIPAM("vn1"); - client->WaitForIdle(); - DeleteBgpPeer(bgp_peer); - client->WaitForIdle(); - EXPECT_FALSE(VrfFind("vrf1", true)); - EXPECT_FALSE(VrfFind("vrf2", true)); - WAIT_FOR(1000, 1000, (flow_proto_->FlowCount() == 0)); - client->WaitForIdle(); - } -public: - void AddRemoteEcmpRoute(const string vrf_name, const string ip, - uint32_t plen, const string vn, int count, bool reverse = false, - bool same_label = false) { - //If there is a local route, include that always - Ip4Address vm_ip = Ip4Address::from_string(ip); - - ComponentNHKeyList comp_nh_list; - int remote_server_ip = 0x64010101; - int label = 16; - SecurityGroupList sg_id_list; - - for(int i = 0; i < count; i++) { - ComponentNHKeyPtr comp_nh(new ComponentNHKey( - label, Agent::GetInstance()->fabric_vrf_name(), - Agent::GetInstance()->router_id(), - Ip4Address(remote_server_ip++), - false, TunnelType::AllType())); - comp_nh_list.push_back(comp_nh); - if (!same_label) { - label++; - } - } - if (reverse) { - std::reverse(comp_nh_list.begin(), comp_nh_list.end()); - } - - EcmpTunnelRouteAdd(bgp_peer, vrf_name, vm_ip, plen, - comp_nh_list, -1, vn, sg_id_list, - PathPreference()); - } - - FlowProto *get_flow_proto() const { return flow_proto_; } - Agent *agent_; - BgpPeer *bgp_peer; - FlowProto *flow_proto_; - AgentXmppChannel *channel; - char router_id[80]; - char MX_0[80]; - char MX_1[80]; - char MX_2[80]; - char MX_3[80]; - int vm1_label; - int eth_intf_id; -}; - -//Packet from VM to destination ECMP -TEST_F(FipEcmpTest, Test_1) { - AddRemoteEcmpRoute(VRF2, "0.0.0.0", 0, VN2, 4); - - TxIpPacket(VmPortGetId(1), "1.1.1.1", "8.8.8.8", 1); - client->WaitForIdle(); - - FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), - "1.1.1.1", "8.8.8.8", 1, 0, 0, - GetFlowKeyNH(1)); - - AgentRoute *rt = RouteGet(VRF2, Ip4Address::from_string("2.1.1.1"), 32); - - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->data().nh.get() == rt->GetActiveNextHop()); - - - rt = RouteGet(VRF2, Ip4Address::from_string("0.0.0.0"), 0); - //Reverse flow is no ECMP - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); - - DeleteRoute(VRF2, "0.0.0.0", 0, bgp_peer); - client->WaitForIdle(); - WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); -} - -//Packet from external ECMP source to fip -TEST_F(FipEcmpTest, Test_2) { - AddRemoteEcmpRoute(VRF2, "0.0.0.0", 0, VN2, 4); - - TxIpMplsPacket(eth_intf_id, MX_2, router_id, vm1_label, - "8.8.8.8", "2.1.1.1", 1, 10); - client->WaitForIdle(); - - AgentRoute *rt = RouteGet("vrf1", Ip4Address::from_string("1.1.1.1"), 32); - FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), - "1.1.1.1", "8.8.8.8", 1, 0, 0, GetFlowKeyNH(1)); - EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == 2); - EXPECT_TRUE(entry->data().nh.get() == rt->GetActiveNextHop()); - - rt = RouteGet(VRF2, Ip4Address::from_string("0.0.0.0"), 0); - FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); - - DeleteRoute(VRF2, "0.0.0.0", 0, bgp_peer); - client->WaitForIdle(); - WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); -} - -int main(int argc, char *argv[]) { - GETUSERARGS(); - client = TestInit(init_file, ksync_init, true, true, true, 100*1000); - int ret = RUN_ALL_TESTS(); - client->WaitForIdle(); - TestShutdown(); - delete client; - return ret; -} diff --git a/src/vnsw/agent/pkt/test/test_fip_src_ecmp.cc b/src/vnsw/agent/pkt/test/test_fip_src_ecmp.cc index 02b145cf9ad..0a1d3eeecbb 100644 --- a/src/vnsw/agent/pkt/test/test_fip_src_ecmp.cc +++ b/src/vnsw/agent/pkt/test/test_fip_src_ecmp.cc @@ -205,8 +205,8 @@ TEST_F(FipEcmpTest, Test_1) { EXPECT_TRUE(entry != NULL); EXPECT_TRUE(entry->data().component_nh_idx != - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->data().nh.get() == rt->GetLocalNextHop()); + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().rpf_nh.get() == rt->GetLocalNextHop()); rt = static_cast( @@ -215,7 +215,7 @@ TEST_F(FipEcmpTest, Test_1) { FlowEntry *rev_entry = entry->reverse_flow_entry(); EXPECT_TRUE(rev_entry->data().component_nh_idx == CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + EXPECT_TRUE(rev_entry->data().rpf_nh.get() == rt->GetActiveNextHop()); DeleteRoute(VRF2, "0.0.0.0", 0, bgp_peer); DeleteRemoteEcmpFip(); @@ -239,8 +239,9 @@ TEST_F(FipEcmpTest, Test_2) { FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), "1.1.1.1", "8.8.8.8", 1, 0, 0, GetFlowKeyNH(1)); EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == 2); - EXPECT_TRUE(entry->data().nh.get() == rt->GetLocalNextHop()); + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().rpf_nh.get() == rt->GetLocalNextHop()); rt = static_cast( RouteGet(VRF2, Ip4Address::from_string("0.0.0.0"), 0)); @@ -248,7 +249,7 @@ TEST_F(FipEcmpTest, Test_2) { FlowEntry *rev_entry = entry->reverse_flow_entry(); EXPECT_TRUE(rev_entry->data().component_nh_idx == CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + EXPECT_TRUE(rev_entry->data().rpf_nh.get() == rt->GetActiveNextHop()); DeleteRoute(VRF2, "0.0.0.0", 0, bgp_peer); DeleteRemoteEcmpFip(); @@ -275,15 +276,15 @@ TEST_F(FipEcmpTest, Test_3) { EXPECT_TRUE(entry != NULL); EXPECT_TRUE(entry->data().component_nh_idx != CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(entry->data().nh.get() == rt->GetLocalNextHop()); + EXPECT_TRUE(entry->data().rpf_nh.get() == rt->GetLocalNextHop()); rt = static_cast( RouteGet(VRF2, Ip4Address::from_string("0.0.0.0"), 0)); //Reverse flow is no ECMP FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx != + EXPECT_TRUE(rev_entry->data().component_nh_idx == CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + EXPECT_TRUE(rev_entry->data().rpf_nh.get() == rt->GetActiveNextHop()); DeleteRoute(VRF2, "0.0.0.0", 32, bgp_peer); DeleteLocalEcmpFip(); @@ -310,16 +311,17 @@ TEST_F(FipEcmpTest, Test_4) { FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), "1.1.1.1", "8.8.8.8", 1, 0, 0, GetFlowKeyNH(1)); EXPECT_TRUE(entry != NULL); - EXPECT_TRUE(entry->data().component_nh_idx == 2); - EXPECT_TRUE(entry->data().nh.get() == rt->GetLocalNextHop()); + EXPECT_TRUE(entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().rpf_nh.get() == rt->GetLocalNextHop()); rt = static_cast( RouteGet(VRF2, Ip4Address::from_string("0.0.0.0"), 0)); FlowEntry *rev_entry = entry->reverse_flow_entry(); - EXPECT_TRUE(rev_entry->data().component_nh_idx == - CompositeNH::kInvalidComponentNHIdx); - EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().rpf_nh.get() == rt->GetActiveNextHop()); DeleteRoute(VRF2, "0.0.0.0", 0, bgp_peer); DeleteLocalEcmpFip(); diff --git a/src/vnsw/agent/pkt/test/test_flow_audit.cc b/src/vnsw/agent/pkt/test/test_flow_audit.cc index 2c675b0f87a..8868866d178 100644 --- a/src/vnsw/agent/pkt/test/test_flow_audit.cc +++ b/src/vnsw/agent/pkt/test/test_flow_audit.cc @@ -17,15 +17,6 @@ struct PortInfo input[] = { }; VmInterface *vmi0; -static bool FlowStatsTimerStartStopTrigger(FlowStatsCollectorObject *obj, - bool stop) { - for (int i = 0; i < FlowStatsCollectorObject::kMaxCollectors; i++) { - FlowStatsCollector *fsc = obj->GetCollector(i); - fsc->TestStartStopTimer(stop); - } - return true; -} - class FlowAuditTest : public ::testing::Test { public: FlowAuditTest() : agent_(Agent::GetInstance()) { @@ -43,7 +34,7 @@ class FlowAuditTest : public ::testing::Test { vmi0 = VmInterfaceGet(input[0].intf_id); assert(vmi0); - FlowStatsTimerStartStop(true); + FlowStatsTimerStartStop(agent_, true); KFlowPurgeHold(); } @@ -53,21 +44,10 @@ class FlowAuditTest : public ::testing::Test { DeleteVmportEnv(input, 1, true, 1); client->WaitForIdle(); - FlowStatsTimerStartStop(false); + FlowStatsTimerStartStop(agent_, false); KFlowPurgeHold(); } - void FlowStatsTimerStartStop (bool stop) { - int task_id = - agent_->task_scheduler()->GetTaskId(kTaskFlowStatsCollector); - std::auto_ptr trigger_ - (new TaskTrigger(boost::bind(FlowStatsTimerStartStopTrigger, - flow_stats_collector_, stop), - task_id, 0)); - trigger_->Set(); - client->WaitForIdle(); - } - bool FlowTableWait(size_t count) { int i = 1000; while (i > 0) { diff --git a/src/vnsw/agent/pkt/test/test_flow_base.cc b/src/vnsw/agent/pkt/test/test_flow_base.cc index e88bdace759..0c48ccf27db 100644 --- a/src/vnsw/agent/pkt/test/test_flow_base.cc +++ b/src/vnsw/agent/pkt/test/test_flow_base.cc @@ -107,24 +107,6 @@ PhysicalInterface *eth; static void NHNotify(DBTablePartBase *partition, DBEntryBase *entry) { } -static bool FlowStatsTimerStartStopTrigger (bool stop) { - FlowStatsCollectorObject *obj = Agent::GetInstance()->flow_stats_manager()-> - default_flow_stats_collector_obj(); - for (int i = 0; i < FlowStatsCollectorObject::kMaxCollectors; i++) { - FlowStatsCollector *fsc = obj->GetCollector(i); - fsc->TestStartStopTimer(stop); - } - return true; -} - -static void FlowStatsTimerStartStop (bool stop) { - int task_id = TaskScheduler::GetInstance()->GetTaskId(kTaskFlowStatsCollector); - std::auto_ptr trigger_ - (new TaskTrigger(boost::bind(FlowStatsTimerStartStopTrigger, stop), task_id, 0)); - trigger_->Set(); - client->WaitForIdle(); -} - class FlowTest : public ::testing::Test { public: FlowTest() : peer_(NULL), agent_(Agent::GetInstance()) { @@ -704,7 +686,7 @@ class FlowTest : public ::testing::Test { gw_ip, 32, "vn5", false); client->WaitForIdle(); - FlowStatsTimerStartStop(true); + FlowStatsTimerStartStop(agent_, true); client->WaitForIdle(); } @@ -751,7 +733,7 @@ class FlowTest : public ::testing::Test { EXPECT_EQ(0U, agent()->acl_table()->Size()); EXPECT_EQ(1U, agent()->vrf_table()->Size()); DeleteBgpPeer(peer_); - FlowStatsTimerStartStop(false); + FlowStatsTimerStartStop(agent_, false); client->WaitForIdle(3); WAIT_FOR(100, 1, (0U == get_flow_proto()->FlowCount())); } diff --git a/src/vnsw/agent/pkt/test/test_flow_trace_filter.cc b/src/vnsw/agent/pkt/test/test_flow_trace_filter.cc index ec5605a657d..09abb8a3573 100644 --- a/src/vnsw/agent/pkt/test/test_flow_trace_filter.cc +++ b/src/vnsw/agent/pkt/test/test_flow_trace_filter.cc @@ -17,25 +17,6 @@ struct PortInfo input[] = { {"vnet3", 3, "1.1.1.13", "00:00:01:01:01:03", 1, 1}, }; -static bool FlowStatsTimerStartStopTrigger (Agent *agent, bool stop) { - FlowStatsCollectorObject *obj = agent->flow_stats_manager()-> - default_flow_stats_collector_obj(); - for (int i = 0; i < FlowStatsCollectorObject::kMaxCollectors; i++) { - FlowStatsCollector *fsc = obj->GetCollector(i); - fsc->TestStartStopTimer(stop); - } - return true; -} - -static void FlowStatsTimerStartStop (Agent *agent, bool stop) { - int task_id = agent->task_scheduler()->GetTaskId(kTaskFlowStatsCollector); - std::auto_ptr trigger_ - (new TaskTrigger(boost::bind(FlowStatsTimerStartStopTrigger, agent, - stop), task_id, 0)); - trigger_->Set(); - client->WaitForIdle(); -} - class FlowTraceFilterTest : public ::testing::Test { public: virtual void SetUp() { diff --git a/src/vnsw/agent/pkt/test/test_flow_update.cc b/src/vnsw/agent/pkt/test/test_flow_update.cc index f803ce44c47..c61ee97fb39 100644 --- a/src/vnsw/agent/pkt/test/test_flow_update.cc +++ b/src/vnsw/agent/pkt/test/test_flow_update.cc @@ -330,7 +330,7 @@ TEST_F(FlowTest, Flow_with_encap_change) { FlowEntry *fe = FlowGet(VrfGet("vrf5")->vrf_id(), remote_vm1_ip, vm1_ip, 1, 0, 0, GetFlowKeyNH(input[0].intf_id)); - const NextHop *nh = fe->data().nh.get(); + const NextHop *nh = fe->data().rpf_nh.get(); EXPECT_TRUE(nh != NULL); EXPECT_TRUE(nh->GetType() == NextHop::TUNNEL); const TunnelNH *tnh = static_cast(nh); @@ -340,8 +340,8 @@ TEST_F(FlowTest, Flow_with_encap_change) { client->WaitForIdle(); fe = FlowGet(VrfGet("vrf5")->vrf_id(), remote_vm1_ip, vm1_ip, 1, 0, 0, GetFlowKeyNH(input[0].intf_id)); - EXPECT_TRUE(fe->data().nh.get() != NULL); - nh = fe->data().nh.get(); + EXPECT_TRUE(fe->data().rpf_nh.get() != NULL); + nh = fe->data().rpf_nh.get(); EXPECT_TRUE(nh != NULL); EXPECT_TRUE(nh->GetType() == NextHop::TUNNEL); tnh = static_cast(nh); @@ -351,8 +351,8 @@ TEST_F(FlowTest, Flow_with_encap_change) { client->WaitForIdle(); fe = FlowGet(VrfGet("vrf5")->vrf_id(), remote_vm1_ip, vm1_ip, 1, 0, 0, GetFlowKeyNH(input[0].intf_id)); - EXPECT_TRUE(fe->data().nh.get() != NULL); - nh = fe->data().nh.get(); + EXPECT_TRUE(fe->data().rpf_nh.get() != NULL); + nh = fe->data().rpf_nh.get(); EXPECT_TRUE(nh != NULL); EXPECT_TRUE(nh->GetType() == NextHop::TUNNEL); tnh = static_cast(nh); @@ -362,8 +362,8 @@ TEST_F(FlowTest, Flow_with_encap_change) { client->WaitForIdle(); fe = FlowGet(VrfGet("vrf5")->vrf_id(), remote_vm1_ip, vm1_ip, 1, 0, 0, GetFlowKeyNH(input[0].intf_id)); - EXPECT_TRUE(fe->data().nh.get() != NULL); - nh = fe->data().nh.get(); + EXPECT_TRUE(fe->data().rpf_nh.get() != NULL); + nh = fe->data().rpf_nh.get(); EXPECT_TRUE(nh != NULL); EXPECT_TRUE(nh->GetType() == NextHop::TUNNEL); tnh = static_cast(nh); @@ -373,8 +373,8 @@ TEST_F(FlowTest, Flow_with_encap_change) { client->WaitForIdle(); fe = FlowGet(VrfGet("vrf5")->vrf_id(), remote_vm1_ip, vm1_ip, 1, 0, 0, GetFlowKeyNH(input[0].intf_id)); - EXPECT_TRUE(fe->data().nh.get() != NULL); - nh = fe->data().nh.get(); + EXPECT_TRUE(fe->data().rpf_nh.get() != NULL); + nh = fe->data().rpf_nh.get(); EXPECT_TRUE(nh != NULL); EXPECT_TRUE(nh->GetType() == NextHop::TUNNEL); tnh = static_cast(nh); diff --git a/src/vnsw/agent/pkt/test/test_flow_util.h b/src/vnsw/agent/pkt/test/test_flow_util.h index d998b30f9ae..cca930787d4 100644 --- a/src/vnsw/agent/pkt/test/test_flow_util.h +++ b/src/vnsw/agent/pkt/test/test_flow_util.h @@ -535,8 +535,8 @@ class VerifyRpf : public FlowVerify { void Verify(FlowEntry *fe) { FlowEntry *rev = fe->reverse_flow_entry(); EXPECT_TRUE(rev != NULL); - EXPECT_TRUE(fe->data().nh->id() == forward_flow_rpf_nh_); - EXPECT_TRUE(rev->data().nh->id() == reverse_flow_rpf_nh_); + EXPECT_TRUE(fe->data().rpf_nh->id() == forward_flow_rpf_nh_); + EXPECT_TRUE(rev->data().rpf_nh->id() == reverse_flow_rpf_nh_); } private: uint32_t forward_flow_rpf_nh_; diff --git a/src/vnsw/agent/pkt/test/test_pkt_flow_limits.cc b/src/vnsw/agent/pkt/test/test_pkt_flow_limits.cc index 4f8e3c1e15f..175352ff48f 100644 --- a/src/vnsw/agent/pkt/test/test_pkt_flow_limits.cc +++ b/src/vnsw/agent/pkt/test/test_pkt_flow_limits.cc @@ -60,25 +60,6 @@ VmInterface *vmi_2; VmInterface *vmi_3; std::string eth_itf; -static bool FlowStatsTimerStartStopTrigger (Agent *agent, bool stop) { - FlowStatsCollectorObject *obj = agent->flow_stats_manager()-> - default_flow_stats_collector_obj(); - for (int i = 0; i < FlowStatsCollectorObject::kMaxCollectors; i++) { - FlowStatsCollector *fsc = obj->GetCollector(i); - fsc->TestStartStopTimer(stop); - } - return true; -} - -static void FlowStatsTimerStartStop (Agent *agent, bool stop) { - int task_id = agent->task_scheduler()->GetTaskId(kTaskFlowStatsCollector); - std::auto_ptr trigger_ - (new TaskTrigger(boost::bind(FlowStatsTimerStartStopTrigger, agent, - stop), task_id, 0)); - trigger_->Set(); - client->WaitForIdle(); -} - class FlowTest : public ::testing::Test { public: FlowTest() : agent_(Agent::GetInstance()) { diff --git a/src/vnsw/agent/pkt/test/test_pkt_util.cc b/src/vnsw/agent/pkt/test/test_pkt_util.cc index 40b0b101de4..0fc2310e362 100644 --- a/src/vnsw/agent/pkt/test/test_pkt_util.cc +++ b/src/vnsw/agent/pkt/test/test_pkt_util.cc @@ -59,17 +59,6 @@ void TxL2Packet(int ifindex, const char *smac, const char *dmac, delete pkt; } -void TxIpPacketEcmp(int ifindex, const char *sip, const char *dip, - int proto, int hash_id) { - PktGen *pkt = new PktGen(); - MakeIpPacket(pkt, ifindex, sip, dip, proto, hash_id, AgentHdr::TRAP_ECMP_RESOLVE); - uint8_t *ptr(new uint8_t[pkt->GetBuffLen()]); - memcpy(ptr, pkt->GetBuff(), pkt->GetBuffLen()); - client->agent_init()->pkt0()->ProcessFlowPacket(ptr, pkt->GetBuffLen(), - pkt->GetBuffLen()); - delete pkt; -} - void MakeUdpPacket(PktGen *pkt, int ifindex, const char *sip, const char *dip, uint16_t sport, uint16_t dport, int hash_id, uint32_t vrf_id) { @@ -130,8 +119,8 @@ void MakeIpMplsPacket(PktGen *pkt, int ifindex, const char *out_sip, const char *sip, const char *dip, uint8_t proto, int hash_id) { pkt->AddEthHdr("00:00:00:00:00:01", "00:00:00:00:00:02", 0x800); - pkt->AddAgentHdr(ifindex, AgentHdr::TRAP_FLOW_MISS, hash_id, MplsToVrfId(label), - label); + pkt->AddAgentHdr(ifindex, AgentHdr::TRAP_FLOW_MISS, hash_id, + MplsToVrfId(label), label); pkt->AddEthHdr("00:00:5E:00:01:00", "00:00:00:00:00:01", 0x800); pkt->AddIpHdr(out_sip, out_dip, IPPROTO_GRE); pkt->AddGreHdr(); @@ -156,6 +145,40 @@ void TxIpMplsPacket(int ifindex, const char *out_sip, delete pkt; } +void MakeL2IpMplsPacket(PktGen *pkt, int ifindex, const char *out_sip, + const char *out_dip, uint32_t label, + const char *smac, const char *dmac, + const char *sip, const char *dip, uint8_t proto, + int hash_id) { + pkt->AddEthHdr("00:00:00:00:00:01", "00:00:00:00:00:02", 0x800); + pkt->AddAgentHdr(ifindex, AgentHdr::TRAP_FLOW_MISS, hash_id, + MplsToVrfId(label), label); + pkt->AddEthHdr("00:00:5E:00:01:00", "00:00:00:00:00:01", 0x800); + pkt->AddIpHdr(out_sip, out_dip, IPPROTO_GRE); + pkt->AddGreHdr(); + pkt->AddMplsHdr(label, true); + pkt->AddEthHdr(dmac, smac, 0x800); + pkt->AddIpHdr(sip, dip, proto); + if (proto == 1) { + pkt->AddIcmpHdr(); + } +} + +void TxL2IpMplsPacket(int ifindex, const char *out_sip, + const char *out_dip, uint32_t label, + const char *smac, const char *dmac, + const char *sip, const char *dip, uint8_t proto, + int hash_id) { + PktGen *pkt = new PktGen(); + MakeL2IpMplsPacket(pkt, ifindex, out_sip, out_dip, label, smac, dmac, sip, + dip, proto, hash_id); + uint8_t *ptr(new uint8_t[pkt->GetBuffLen()]); + memcpy(ptr, pkt->GetBuff(), pkt->GetBuffLen()); + client->agent_init()->pkt0()->ProcessFlowPacket(ptr, pkt->GetBuffLen(), + pkt->GetBuffLen()); + delete pkt; +} + void TxIpPBBPacket(int ifindex, const char *out_sip, const char *out_dip, uint32_t label, uint32_t vrf, const MacAddress &b_smac, const MacAddress &b_dmac, uint32_t isid, @@ -266,18 +289,6 @@ void TxIp6Packet(int ifindex, const char *sip, const char *dip, int proto, delete pkt; } -void TxIp6PacketEcmp(int ifindex, const char *sip, const char *dip, - int proto, int hash_id) { - PktGen *pkt = new PktGen(); - MakeIp6Packet(pkt, ifindex, sip, dip, proto, hash_id, - AGENT_TRAP_ECMP_RESOLVE); - uint8_t *ptr(new uint8_t[pkt->GetBuffLen()]); - memcpy(ptr, pkt->GetBuff(), pkt->GetBuffLen()); - client->agent_init()->pkt0()->ProcessFlowPacket(ptr, pkt->GetBuffLen(), - pkt->GetBuffLen()); - delete pkt; -} - void MakeUdp6Packet(PktGen *pkt, int ifindex, const char *sip, const char *dip, uint16_t sport, uint16_t dport, int hash_id, uint32_t vrf_id) { @@ -433,3 +444,22 @@ void TxL2Ip6Packet(int ifindex, const char *smac, const char *dmac, pkt->GetBuffLen()); delete pkt; } + +static bool FlowStatsTimerStartStopTrigger (Agent *agent, bool stop) { + FlowStatsCollectorObject *obj = agent->flow_stats_manager()-> + default_flow_stats_collector_obj(); + for (int i = 0; i < FlowStatsCollectorObject::kMaxCollectors; i++) { + FlowStatsCollector *fsc = obj->GetCollector(i); + fsc->TestStartStopTimer(stop); + } + return true; +} + +void FlowStatsTimerStartStop(Agent *agent, bool stop) { + int task_id = agent->task_scheduler()->GetTaskId(kTaskFlowStatsCollector); + std::auto_ptr trigger_ + (new TaskTrigger(boost::bind(FlowStatsTimerStartStopTrigger, agent, + stop), task_id, 0)); + trigger_->Set(); + client->WaitForIdle(); +} diff --git a/src/vnsw/agent/pkt/test/test_pkt_util.h b/src/vnsw/agent/pkt/test/test_pkt_util.h index 2ba51ac4a0c..a8509e0c5ab 100644 --- a/src/vnsw/agent/pkt/test/test_pkt_util.h +++ b/src/vnsw/agent/pkt/test/test_pkt_util.h @@ -19,8 +19,6 @@ extern void TxL2Packet(int ifindex, const char *smac, const char *dmac, const char *sip, const char *dip, int proto, int hash_id = 1, int vrf = -1, uint16_t sport = 0, uint16_t dport = 0); -extern void TxIpPacketEcmp(int ifindex, const char *sip, const char *dip, - int proto, int hash_id = 1); extern void MakeUdpPacket(PktGen *pkt, int ifindex, const char *sip, const char *dip, uint16_t sport, uint16_t dport, @@ -49,6 +47,11 @@ extern void TxIpMplsPacket(int ifindex, const char *out_sip, const char *out_dip, uint32_t label, const char *sip, const char *dip, uint8_t proto, int hash_id = 1); +extern void TxL2IpMplsPacket(int ifindex, const char *out_sip, + const char *out_dip, uint32_t label, + const char *smac, const char *dmac, + const char *sip, const char *dip, uint8_t proto, + int hash_id = 1); extern void MakeUdpMplsPacket(PktGen *pkt, int ifindex, const char *out_sip, const char *out_dip, uint32_t label, const char *sip, const char *dip, uint16_t sport, @@ -74,9 +77,6 @@ extern void MakeIp6Packet(PktGen *pkt, int ifindex, const char *sip, extern void TxIp6Packet(int ifindex, const char *sip, const char *dip, int proto, int hash_id = 1, int vrf = -1); -extern void TxIp6PacketEcmp(int ifindex, const char *sip, const char *dip, - int proto, int hash_id = 1); - extern void MakeUdp6Packet(PktGen *pkt, int ifindex, const char *sip, const char *dip, uint16_t sport, uint16_t dport, int hash_id, uint32_t vrf_id); @@ -124,4 +124,7 @@ extern void TxIpPBBPacket(int ifindex, const char *out_sip, const char *out_dip, const MacAddress &b_dmac, uint32_t isid, const MacAddress &c_smac, MacAddress &c_dmac, const char *sip, const char *dip, int hash_id = 1); + +void FlowStatsTimerStartStop(Agent *agent, bool stop); + #endif // __TEST_PKT_UTIL_H__ diff --git a/src/vnsw/agent/pkt/test/test_rpf_flow.cc b/src/vnsw/agent/pkt/test/test_rpf_flow.cc deleted file mode 100644 index 4ee9db51523..00000000000 --- a/src/vnsw/agent/pkt/test/test_rpf_flow.cc +++ /dev/null @@ -1,407 +0,0 @@ -/* - * Copyright (c) 2014 Juniper Networks, Inc. All rights reserved. - */ - -#include "base/os.h" -#include "test/test_cmn_util.h" -#include "test_flow_util.h" -#include "ksync/ksync_sock_user.h" -#include "oper/tunnel_nh.h" -#include - -#define MAX_VNET 4 - -void RouterIdDepInit(Agent *agent) { -} - -#define vm1_ip "11.1.1.1" -#define vm2_ip "11.1.1.2" -#define vm3_ip "11.1.1.3" -#define remote_vm1_ip "12.1.1.3" -#define vn5_unused_ip "11.1.1.10" - -int fd_table[MAX_VNET]; - -struct PortInfo input[] = { - {"flow0", 6, vm1_ip, "00:00:00:01:01:01", 5, 1}, - {"flow1", 7, vm2_ip, "00:00:00:01:01:02", 5, 2}, - {"flow2", 8, vm3_ip, "00:00:00:01:01:03", 5, 3}, -}; - -int hash_id; -VmInterface *flow0; -VmInterface *flow1; -VmInterface *flow2; - -static void NHNotify(DBTablePartBase *partition, DBEntryBase *entry) { -} - -class FlowRpfTest : public ::testing::Test { -public: - FlowRpfTest() : peer_(NULL), agent_(Agent::GetInstance()) { - flow_proto_ = agent_->pkt()->get_flow_proto(); - } - - bool FlowTableWait(size_t count) { - int i = 1000; - while (i > 0) { - i--; - if (get_flow_proto()->FlowCount() == count) { - break; - } - client->WaitForIdle(); - usleep(1); - } - return (get_flow_proto()->FlowCount() == count); - } - - void FlushFlowTable() { - client->EnqueueFlowFlush(); - client->WaitForIdle(); - EXPECT_EQ(0U, get_flow_proto()->FlowCount()); - } - - static void TestTearDown() { - client->Reset(); - if (ksync_init_) { - DeleteTapIntf(fd_table, MAX_VNET); - } - client->WaitForIdle(); - } - - static void TestSetup(bool ksync_init) { - ksync_init_ = ksync_init; - if (ksync_init_) { - CreateTapInterfaces("flow", MAX_VNET, fd_table); - client->WaitForIdle(); - } - } - -protected: - virtual void SetUp() { - unsigned int vn_count = 0; - EXPECT_EQ(0U, get_flow_proto()->FlowCount()); - hash_id = 1; - client->Reset(); - CreateVmportEnv(input, 3, 1); - client->WaitForIdle(5); - vn_count++; - - EXPECT_TRUE(VmPortActive(input, 0)); - EXPECT_TRUE(VmPortActive(input, 1)); - EXPECT_TRUE(VmPortActive(input, 2)); - EXPECT_TRUE(VmPortPolicyEnable(input, 0)); - EXPECT_TRUE(VmPortPolicyEnable(input, 1)); - EXPECT_TRUE(VmPortPolicyEnable(input, 2)); - EXPECT_EQ(6U, agent()->interface_table()->Size()); - EXPECT_EQ(3U, agent()->vm_table()->Size()); - EXPECT_EQ(vn_count, agent()->vn_table()->Size()); - EXPECT_EQ(3U, PortSubscribeSize(agent())); - - flow0 = VmInterfaceGet(input[0].intf_id); - assert(flow0); - flow1 = VmInterfaceGet(input[1].intf_id); - assert(flow1); - flow2 = VmInterfaceGet(input[2].intf_id); - assert(flow2); - - client->WaitForIdle(); - peer_ = CreateBgpPeer(Ip4Address(1), "BGP Peer 1"); - Ip4Address gw_ip = Ip4Address::from_string("11.1.1.254"); - //Add a gateway route pointing to pkt0 - VrfEntry *vrf = VrfGet("vrf5"); - static_cast( - vrf->GetInet4UnicastRouteTable())->AddHostRoute("vrf5", - gw_ip, 32, "vn5", false); - client->WaitForIdle(); - } - - virtual void TearDown() { - FlushFlowTable(); - client->Reset(); - Ip4Address gw_ip = Ip4Address::from_string("11.1.1.254"); - Agent::GetInstance()->fabric_inet4_unicast_table()->DeleteReq( - Agent::GetInstance()->local_peer(), "vrf5", gw_ip, 32, NULL); - client->WaitForIdle(); - DeleteVmportEnv(input, 3, true, 1); - client->WaitForIdle(3); - client->PortDelNotifyWait(3); - EXPECT_FALSE(VmPortFind(input, 0)); - EXPECT_FALSE(VmPortFind(input, 1)); - EXPECT_FALSE(VmPortFind(input, 2)); - EXPECT_EQ(3U, agent()->interface_table()->Size()); - - EXPECT_EQ(0U, agent()->vm_table()->Size()); - EXPECT_EQ(0U, agent()->vn_table()->Size()); - EXPECT_EQ(0U, agent()->acl_table()->Size()); - DeleteBgpPeer(peer_); - } - - Agent *agent() {return agent_;} - FlowProto *get_flow_proto() const { return flow_proto_; } - -private: - static bool ksync_init_; - BgpPeer *peer_; - Agent *agent_; - FlowProto *flow_proto_; -}; - -bool FlowRpfTest::ksync_init_; - -TEST_F(FlowRpfTest, Flow_rpf_failure_missing_route) { - EXPECT_EQ(0U, get_flow_proto()->FlowCount()); - - TestFlow flow[] = { - { - TestFlowPkt(Address::INET, remote_vm1_ip, vm2_ip, 1, 0, 0, "vrf5", - flow0->id()), - {} - } - }; - - CreateFlow(flow, 1); - client->WaitForIdle(); - - uint32_t vrf_id = VrfGet("vrf5")->vrf_id(); - FlowEntry *fe = FlowGet(vrf_id, remote_vm1_ip, vm2_ip, 1, 0, 0, - flow0->flow_key_nh()->id()); - - EXPECT_TRUE(fe != NULL); - if (fe != NULL) { - WAIT_FOR(1000, 500, (fe->is_flags_set(FlowEntry::ShortFlow) == true)); - EXPECT_TRUE(fe->short_flow_reason() == FlowEntry::SHORT_NO_SRC_ROUTE); - uint32_t fe_action = fe->match_p().action_info.action; - EXPECT_TRUE(((fe_action) & (1 << TrafficAction::DENY)) != 0); - } - client->WaitForIdle(); - - DeleteFlow(flow, 1); - client->WaitForIdle(); - - EXPECT_TRUE(FlowTableWait(0)); -} - -TEST_F(FlowRpfTest, Flow_rpf_failure_subnet_discard_route) { - EXPECT_EQ(0U, get_flow_proto()->FlowCount()); - - IpamInfo ipam_info[] = { - {"11.1.1.0", 24, "11.1.1.200", true}, - }; - AddIPAM("vn5", ipam_info, 1); - client->WaitForIdle(); - - TestFlow flow[] = { - { - TestFlowPkt(Address::INET, vn5_unused_ip, vm2_ip, 1, 0, 0, "vrf5", - flow0->id()), - {} - } - }; - - CreateFlow(flow, 1); - client->WaitForIdle(); - - uint32_t vrf_id = VrfGet("vrf5")->vrf_id(); - FlowEntry *fe = FlowGet(vrf_id, vn5_unused_ip, vm2_ip, 1, 0, 0, - flow0->flow_key_nh()->id()); - - EXPECT_TRUE(fe != NULL); - if (fe != NULL) { - WAIT_FOR(1000, 500, (fe->is_flags_set(FlowEntry::ShortFlow) == true)); - EXPECT_TRUE(fe->short_flow_reason() == FlowEntry::SHORT_NO_SRC_ROUTE); - uint32_t fe_action = fe->match_p().action_info.action; - EXPECT_TRUE(((fe_action) & (1 << TrafficAction::DENY)) != 0); - } - client->WaitForIdle(); - - DeleteFlow(flow, 1); - DelIPAM("vn5"); - client->WaitForIdle(); - - EXPECT_TRUE(FlowTableWait(0)); -} - - -// Packets originates from flow0 interface with ip address of flow2 interface. -// this should result in programming vrouter to perform rpf check with flow2 -// interface so all the packets coming from flow0 interface will be dropped in -// vrouter, with error invalid source. -TEST_F(FlowRpfTest, Flow_rpf_failure_invalid_source) { - EXPECT_EQ(0U, get_flow_proto()->FlowCount()); - - TestFlow flow[] = { - { - TestFlowPkt(Address::INET, vm3_ip, vm2_ip, 1, 0, 0, "vrf5", - flow0->id()), - {} - } - }; - - CreateFlow(flow, 1); - client->WaitForIdle(); - - uint32_t vrf_id = VrfGet("vrf5")->vrf_id(); - FlowEntry *fe = FlowGet(vrf_id, vm3_ip, vm2_ip, 1, 0, 0, - flow0->flow_key_nh()->id()); - - EXPECT_TRUE(fe != NULL); - EXPECT_TRUE(fe->data().enable_rpf == true); - if (fe != NULL) { - WAIT_FOR(1000, 500, (fe->is_flags_set(FlowEntry::ShortFlow) != true)); - const NextHop *nh = fe->nh(); - EXPECT_TRUE(nh->GetType() == NextHop::INTERFACE); - if (nh->GetType() == NextHop::INTERFACE) { - const InterfaceNH *intf_nh = static_cast(nh); - const Interface *intf = intf_nh->GetInterface(); - EXPECT_TRUE(intf->name() == "flow2"); - } - } - client->WaitForIdle(); - - DeleteFlow(flow, 1); - client->WaitForIdle(); - - EXPECT_TRUE(FlowTableWait(0)); -} - -//Disable RPF on a VN and verify that -//rpf check is disabled -TEST_F(FlowRpfTest, FlowDisableRpf) { - EXPECT_EQ(0U, get_flow_proto()->FlowCount()); - DisableRpf("vn5", 5); - - TestFlow flow[] = { - { - TestFlowPkt(Address::INET, vm1_ip, vm2_ip, 1, 0, 0, "vrf5", - flow0->id()), - {new VerifyRpfEnable(false, false)} - } - }; - - CreateFlow(flow, 1); - client->WaitForIdle(); - - DeleteFlow(flow, 1); - client->WaitForIdle(); - - EnableRpf("vn5", 5); - EXPECT_TRUE(FlowTableWait(0)); -} - -//Setup flow with RPF enabled -//Change the rpf enable field in VN -//Verify that RPF is disabled on flow -TEST_F(FlowRpfTest, FlowEnableDisableRpf) { - EXPECT_EQ(0U, get_flow_proto()->FlowCount()); - EnableRpf("vn5", 5); - TestFlow flow[] = { - { - TestFlowPkt(Address::INET, vm1_ip, vm2_ip, 1, 0, 0, "vrf5", - flow0->id()), - {new VerifyRpfEnable(true, true)} - } - }; - - CreateFlow(flow, 1); - client->WaitForIdle(); - - //Disable RPF - DisableRpf("vn5", 5); - uint32_t vrf_id = VrfGet("vrf5")->vrf_id(); - FlowEntry *fe = FlowGet(vrf_id, vm1_ip, vm2_ip, 1, 0, 0, - flow0->flow_key_nh()->id()); - EXPECT_TRUE(fe->data().enable_rpf == false); - - FlowEntry *rfe = FlowGet(vrf_id, vm2_ip, vm1_ip, 1, 0, 0, - flow1->flow_key_nh()->id()); - EXPECT_TRUE(rfe->data().enable_rpf == false); - - DeleteFlow(flow, 1); - client->WaitForIdle(); - - EnableRpf("vn5", 5); - EXPECT_TRUE(FlowTableWait(0)); -} - - -//Add floating IP for a flow0 interface and disable -//rpf on the VN from where floating IP is enabled -//Verify rpf check is disabled -TEST_F(FlowRpfTest, FipDisableRpf) { - EXPECT_EQ(0U, get_flow_proto()->FlowCount()); - - AddVn("default-project:vn2", 2); - AddVrf("default-project:vn2:vn2", 2); - AddLink("virtual-network", "default-project:vn2", "routing-instance", - "default-project:vn2:vn2"); - //Add floating IP for vnet1 - AddFloatingIpPool("fip-pool1", 1); - AddFloatingIp("fip1", 1, "2.1.1.100"); - AddLink("floating-ip", "fip1", "floating-ip-pool", "fip-pool1"); - AddLink("floating-ip-pool", "fip-pool1", "virtual-network", - "default-project:vn2"); - client->WaitForIdle(); - AddLink("virtual-machine-interface", "flow0", "floating-ip", "fip1"); - client->WaitForIdle(); - Ip4Address floating_ip = Ip4Address::from_string("2.1.1.100"); - EXPECT_TRUE(RouteFind("default-project:vn2:vn2", floating_ip, 32)); - EXPECT_TRUE(VmPortFloatingIpCount(6, 1)); - DisableRpf("default-project:vn2", 2); - - Ip4Address addr = Ip4Address::from_string("0.0.0.0"); - Ip4Address gw = Ip4Address::from_string("10.1.1.2"); - - Inet4TunnelRouteAdd(NULL, "default-project:vn2:vn2", addr, 0, gw, - TunnelType::AllType(), 8, "default-project:vn2", - SecurityGroupList(), PathPreference()); - client->WaitForIdle(); - - TestFlow flow[] = { - { - TestFlowPkt(Address::INET, vm1_ip, vn5_unused_ip, 1, 0, 0, "vrf5", - flow0->id()), - {new VerifyRpfEnable(false, false), - new VerifyNat(vn5_unused_ip, "2.1.1.100", 1, 0, 0), - } - } - }; - - CreateFlow(flow, 1); - client->WaitForIdle(); - - DeleteFlow(flow, 1); - client->WaitForIdle(); - - DeleteRoute("default-project:vn2:vn2", "0.0.0.0", 0); - client->WaitForIdle(); - DelLink("virtual-network", "default-project:vn2", "routing-instance", - "default-project:vn2:vn2"); - DelLink("floating-ip", "fip1", "floating-ip-pool", "fip-pool1"); - DelLink("floating-ip-pool", "fip-pool1", - "virtual-network", "default-project:vn2"); - DelLink("virtual-machine-interface", "flow0", "floating-ip", "fip1"); - DelFloatingIp("fip1"); - DelFloatingIpPool("fip-pool1"); - client->WaitForIdle(); - DelLink("virtual-network", "vn2", "routing-instance", - "default-project:vn2:vn2"); - DelVrf("default-project:vn2:vn2"); - DelVn("default-project:vn2"); - client->WaitForIdle(); -} - -int main(int argc, char *argv[]) { - GETUSERARGS(); - - client = - TestInit(init_file, ksync_init, true, true, true, (1000000 * 60 * 10), (3 * 60 * 1000)); - - FlowRpfTest::TestSetup(ksync_init); - int ret = RUN_ALL_TESTS(); - usleep(1000); - Agent::GetInstance()->event_manager()->Shutdown(); - AsioStop(); - TaskScheduler::GetInstance()->Terminate(); - return ret; -} diff --git a/src/vnsw/agent/pkt/test/test_rpf_uc.cc b/src/vnsw/agent/pkt/test/test_rpf_uc.cc new file mode 100644 index 00000000000..5f4045a5353 --- /dev/null +++ b/src/vnsw/agent/pkt/test/test_rpf_uc.cc @@ -0,0 +1,582 @@ +/* + * Copyright (c) 2014 Juniper Networks, Inc. All rights reserved. + */ +// File to test RPF in case of unicast-l3 and unicast-l2 flows + +#include "base/os.h" +#include "test/test_cmn_util.h" +#include "test_flow_util.h" +#include "ksync/ksync_sock_user.h" +#include "oper/tunnel_nh.h" +#include + +#define VM1_IP "1.1.1.1" +#define VM1_MAC "00:00:00:01:01:01" + +#define VM2_IP "1.1.1.2" +#define VM2_MAC "00:00:00:01:01:02" + +#define VM3_IP "1.1.1.3" +#define VM3_MAC "00:00:00:01:01:03" + +#define REMOTE_VM1_IP "1.1.1.10" +#define REMOTE_VM1_MAC "00:00:00:02:02:01" + +#define REMOTE_VM2_IP "1.1.1.100" +#define REMOTE_VM2_MAC "00:00:00:10:10:01" + +#define REMOTE_FIP1_IP "2.2.2.101" +#define unknown_ip_1 "1.1.1.10" +#define unknown_mac_1 "00:FF:FF:FF:FF:00" + +struct PortInfo input[] = { + {"vmi0", 1, VM1_IP, VM1_MAC, 1, 1}, + {"vmi1", 2, VM2_IP, VM2_MAC, 1, 2}, +}; +IpamInfo ipam_info_1[] = { + {"1.1.1.0", 24, "1.1.1.254"}, +}; + +class FlowRpfTest : public ::testing::Test { +public: + FlowRpfTest() : agent_(Agent::GetInstance()), peer_(NULL) { + flow_proto_ = agent_->pkt()->get_flow_proto(); + eth0_ = EthInterfaceGet("vnet0"); + strcpy(router_id_, agent_->router_id().to_string().c_str()); + } + + virtual void SetUp() { + CreateVmportEnv(input, 2, 1); + AddIPAM("vn1", ipam_info_1, 1); + client->WaitForIdle(); + + for (int i = 0; i < 2; i++) { + EXPECT_TRUE(VmPortActive(input, i)); + EXPECT_TRUE(VmPortPolicyEnable(input, i)); + } + + EXPECT_EQ(5U, agent_->interface_table()->Size()); + EXPECT_EQ(2U, agent_->vm_table()->Size()); + EXPECT_EQ(1U, agent_->vn_table()->Size()); + + vmi0_ = VmInterfaceGet(input[0].intf_id); + assert(vmi0_); + vmi1_ = VmInterfaceGet(input[1].intf_id); + assert(vmi1_); + + peer_ = CreateBgpPeer(Ip4Address(1), "BGP Peer 1"); + client->WaitForIdle(); + + CreateRemoteRoute("vrf1", REMOTE_VM1_IP, 32, "100.100.100.1", 10, + "vn1"); + CreateL2RemoteRoute("vrf1", REMOTE_VM1_MAC, "100.100.100.1", 20, + "vn1"); + client->WaitForIdle(); + + vn1_ = VnGet(1); + vrf1_ = VrfGet("vrf1"); + EXPECT_TRUE(vn1_ != NULL); + + FlowStatsTimerStartStop(agent_, true); + } + + virtual void TearDown() { + DeleteRemoteRoute("vrf1", REMOTE_VM1_IP, 32); + DeleteL2RemoteRoute("vrf1", REMOTE_VM1_MAC); + client->WaitForIdle(); + + DelIPAM("vn1"); + DeleteVmportEnv(input, 2, true, 1); + client->WaitForIdle(); + + FlushFlowTable(); + WAIT_FOR(1000, 100, (flow_proto_->FlowCount() == 0)); + + for (int i = 0; i < 3; i++) { + EXPECT_FALSE(VmPortFind(input, i)); + } + DeleteBgpPeer(peer_); + FlushFlowTable(); + client->WaitForIdle(); + + EXPECT_EQ(3U, agent_->interface_table()->Size()); + EXPECT_EQ(0U, agent_->vm_table()->Size()); + EXPECT_EQ(0U, agent_->vn_table()->Size()); + EXPECT_EQ(0U, agent_->acl_table()->Size()); + EXPECT_EQ(0U, flow_proto_->FlowCount()); + FlowStatsTimerStartStop(agent_, false); + } + + void CreateRemoteRoute(const char *vrf, const char *remote_vm, + uint8_t plen, const char *srvr, int label, + const char *vn) { + Ip4Address addr = Ip4Address::from_string(remote_vm); + Ip4Address gw = Ip4Address::from_string(srvr); + Inet4TunnelRouteAdd(peer_, vrf, addr, plen, gw, TunnelType::MplsType(), + label, vn, SecurityGroupList(), PathPreference()); + client->WaitForIdle(); + WAIT_FOR(1000, 500, (RouteFind(vrf, addr, plen) == true)); + } + + void CreateRemoteEcmpRoute(const char *vrf_name, const char *ip, + uint32_t plen, const char *vn, int count) { + Ip4Address vm_ip = Ip4Address::from_string(ip); + ComponentNHKeyList comp_nh_list; + SecurityGroupList sg_id_list; + int remote_server_ip = 0x0A0A0A0A; + int label = 16; + + for(int i = 0; i < count; i++) { + ComponentNHKeyPtr comp_nh + (new ComponentNHKey(label, agent_->fabric_vrf_name(), + agent_->router_id(), + Ip4Address(remote_server_ip++), + false, TunnelType::GREType())); + comp_nh_list.push_back(comp_nh); + label++; + } + EcmpTunnelRouteAdd(peer_, vrf_name, vm_ip, plen, comp_nh_list, -1, vn, + sg_id_list, PathPreference()); + } + + void DeleteRemoteRoute(const char *vrf, const char *ip, uint8_t plen) { + Ip4Address addr = Ip4Address::from_string(ip); + agent_->fabric_inet4_unicast_table()->DeleteReq + (peer_, vrf, addr, plen, + new ControllerVmRoute(static_cast(peer_))); + client->WaitForIdle(); + WAIT_FOR(1000, 100, (RouteFind(vrf, addr, 32) == false)); + } + + void CreateL2RemoteRoute(const char *vrf, const char *mac, + const char *srvr, int label, const char *vn) { + Ip4Address addr = Ip4Address::from_string(srvr); + + MacAddress m = MacAddress::FromString(mac); + BridgeTunnelRouteAdd(peer_, vrf, TunnelType::AllType(), addr, label, + m, Ip4Address(0), 32, false); + client->WaitForIdle(); + WAIT_FOR(1000, 500, (L2RouteFind(vrf, m) == true)); + } + + void DeleteL2RemoteRoute(const char *vrf, const char *mac) { + MacAddress m = MacAddress::FromString(mac); + EvpnAgentRouteTable::DeleteReq + (peer_, vrf, m, Ip4Address(0), 0, + new ControllerVmRoute(static_cast(peer_))); + client->WaitForIdle(); + WAIT_FOR(1000, 500, (L2RouteFind(vrf, m) == false)); + } + +protected: + Agent *agent_; + FlowProto *flow_proto_; + BgpPeer *peer_; + PhysicalInterface *eth0_; + char router_id_[64]; + VmInterface *vmi0_; + VmInterface *vmi1_; + VmInterface *flow2_; + VnEntry *vn1_; + VrfEntry *vrf1_; +}; + +// Disable RPF on VN, verify that flows created on VN have RPF disabled +TEST_F(FlowRpfTest, Cfg_DisableRpf_) { + DisableRpf("vn1", 1); + EXPECT_FALSE(vn1_->enable_rpf()); + + TxIpPacket(vmi0_->id(), VM1_IP, VM2_IP, 1); + client->WaitForIdle(); + FlowEntry *flow = FlowGet(vrf1_->vrf_id(), VM1_IP, VM2_IP, 1, 0, 0, + vmi0_->flow_key_nh()->id()); + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_FALSE(flow->data().enable_rpf); + EXPECT_TRUE(flow->rpf_nh() == NULL); + + EXPECT_FALSE(rflow->data().enable_rpf); + EXPECT_TRUE(rflow->rpf_nh() == NULL); + + EnableRpf("vn1", 1); + EXPECT_TRUE(vn1_->enable_rpf()); +} + +// Setup flow with RPF enabled +// Change the rpf from enable to disable in VN +// Verify that RPF is disabled on flow +TEST_F(FlowRpfTest, Cfg_EnableDisableRpf) { + TxIpPacket(vmi0_->id(), VM1_IP, VM2_IP, 1); + client->WaitForIdle(); + FlowEntry *flow = FlowGet(vrf1_->vrf_id(), VM1_IP, VM2_IP, 1, 0, 0, + vmi0_->flow_key_nh()->id()); + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(flow->data().enable_rpf); + EXPECT_TRUE(flow->rpf_nh() != NULL); + + EXPECT_TRUE(rflow->data().enable_rpf); + EXPECT_TRUE(rflow->rpf_nh() != NULL); + + // Disable RPF + DisableRpf("vn1", 1); + client->WaitForIdle(); + + // Verify RPF disabled on the flow + WAIT_FOR(1000, 1000, (flow->data().enable_rpf == false)); + WAIT_FOR(1000, 1000, (flow->rpf_nh() == NULL)); + + WAIT_FOR(1000, 1000, (rflow->data().enable_rpf == false)); + WAIT_FOR(1000, 1000, (rflow->rpf_nh() == NULL)); + + EnableRpf("vn1", 1); + EXPECT_TRUE(vn1_->enable_rpf()); + + // RPF must be enabled on flows again + WAIT_FOR(1000, 1000, (flow->data().enable_rpf)); + WAIT_FOR(1000, 1000, (flow->rpf_nh() != NULL)); + + WAIT_FOR(1000, 1000, (rflow->data().enable_rpf)); + WAIT_FOR(1000, 1000, (rflow->rpf_nh() != NULL)); +} + +// Setup flow with RPF disabled. Change rpf to enable +// Verify that RPF is enabled on flow +TEST_F(FlowRpfTest, Cfg_DisableEnableRpf) { + // Disable RPF + DisableRpf("vn1", 1); + client->WaitForIdle(); + + TxIpPacket(vmi0_->id(), VM1_IP, VM2_IP, 1); + client->WaitForIdle(); + FlowEntry *flow = FlowGet(vrf1_->vrf_id(), VM1_IP, VM2_IP, 1, 0, 0, + vmi0_->flow_key_nh()->id()); + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_FALSE(flow->data().enable_rpf); + EXPECT_TRUE(flow->rpf_nh() == NULL); + // src_ip_nh must still be set + EXPECT_TRUE(flow->data().src_ip_nh.get() != NULL); + + EXPECT_FALSE(rflow->data().enable_rpf); + EXPECT_TRUE(rflow->rpf_nh() == NULL); + // src_ip_nh must still be set + EXPECT_TRUE(rflow->data().src_ip_nh.get() != NULL); + + // Disable RPF + EnableRpf("vn1", 1); + client->WaitForIdle(); + + // Verify RPF enabled on the flow + WAIT_FOR(1000, 1000, (flow->data().enable_rpf == true)); + WAIT_FOR(1000, 1000, (flow->rpf_nh() != NULL)); + + WAIT_FOR(1000, 1000, (rflow->data().enable_rpf == true)); + WAIT_FOR(1000, 1000, (rflow->rpf_nh() != NULL)); +} + +// Add floating IP for a vmi0_ interface and disable +// rpf on the VN from where floating IP is enabled +// Verify rpf check is disabled +TEST_F(FlowRpfTest, Cfg_FipDisableRpf) { + AddVn("default-project:vn2", 2); + AddVrf("default-project:vn2:vn2", 2); + AddLink("virtual-network", "default-project:vn2", "routing-instance", + "default-project:vn2:vn2"); + //Add floating IP for vnet1 + AddFloatingIpPool("fip-pool1", 1); + AddFloatingIp("fip1", 1, "2.1.1.100", VM1_IP); + AddLink("floating-ip", "fip1", "floating-ip-pool", "fip-pool1"); + AddLink("floating-ip-pool", "fip-pool1", "virtual-network", + "default-project:vn2"); + client->WaitForIdle(); + AddLink("virtual-machine-interface", "vmi0", "floating-ip", "fip1"); + client->WaitForIdle(); + + Ip4Address floating_ip = Ip4Address::from_string("2.1.1.100"); + EXPECT_TRUE(RouteFind("default-project:vn2:vn2", floating_ip, 32)); + EXPECT_TRUE(VmPortFloatingIpCount(1, 1)); + + CreateRemoteRoute("default-project:vn2:vn2", REMOTE_FIP1_IP, 32, + "100.100.100.1", 10, "default-project:vn2"); + client->WaitForIdle(); + + DisableRpf("default-project:vn2", 2); + client->WaitForIdle(); + const VnEntry *vn2 = VnGet(2); + EXPECT_FALSE(vn2->enable_rpf()); + + TxIpPacket(vmi0_->id(), VM1_IP, REMOTE_FIP1_IP, 1); + client->WaitForIdle(); + FlowEntry *flow = FlowGet(vrf1_->vrf_id(), VM1_IP, REMOTE_FIP1_IP, 1, 0, 0, + vmi0_->flow_key_nh()->id()); + FlowEntry *rflow = flow->reverse_flow_entry(); + + EXPECT_TRUE(flow->is_flags_set(FlowEntry::NatFlow)); + EXPECT_TRUE(rflow->is_flags_set(FlowEntry::NatFlow)); + EXPECT_FALSE(flow->data().enable_rpf); + EXPECT_FALSE(rflow->data().enable_rpf); + EXPECT_TRUE(flow->rpf_nh() == NULL); + EXPECT_TRUE(rflow->rpf_nh() == NULL); + + EnableRpf("default-project:vn2", 2); + EXPECT_TRUE(vn2->enable_rpf()); + + EXPECT_TRUE(flow->is_flags_set(FlowEntry::NatFlow)); + EXPECT_TRUE(rflow->is_flags_set(FlowEntry::NatFlow)); + EXPECT_TRUE(flow->data().enable_rpf); + EXPECT_TRUE(rflow->data().enable_rpf); + EXPECT_TRUE(flow->rpf_nh() != NULL); + EXPECT_TRUE(rflow->rpf_nh() != NULL); + + DeleteRoute("default-project:vn2:vn2", "0.0.0.0", 0); + client->WaitForIdle(); + DelLink("virtual-network", "default-project:vn2", "routing-instance", + "default-project:vn2:vn2"); + DelLink("floating-ip", "fip1", "floating-ip-pool", "fip-pool1"); + DelLink("floating-ip-pool", "fip-pool1", + "virtual-network", "default-project:vn2"); + DelLink("virtual-machine-interface", "vmi0", "floating-ip", "fip1"); + DelFloatingIp("fip1"); + DelFloatingIpPool("fip-pool1"); + client->WaitForIdle(); + DelLink("virtual-network", "vn2", "routing-instance", + "default-project:vn2:vn2"); + DelVrf("default-project:vn2:vn2"); + DelVn("default-project:vn2"); + client->WaitForIdle(); +} + +// RPF for L3 Local flow +TEST_F(FlowRpfTest, L3_Local_1) { + TxIpPacket(vmi0_->id(), VM1_IP, VM2_IP, 1); + client->WaitForIdle(); + FlowEntry *flow = FlowGet(vrf1_->vrf_id(), VM1_IP, VM2_IP, 1, 0, 0, + vmi0_->flow_key_nh()->id()); + FlowEntry *rflow = flow->reverse_flow_entry(); + + EXPECT_TRUE(flow->data().enable_rpf); + const InterfaceNH *nh = dynamic_cast(flow->rpf_nh()); + EXPECT_TRUE(nh != NULL); + EXPECT_TRUE(nh->GetInterface() == static_cast(vmi0_)); + + EXPECT_TRUE(rflow->data().enable_rpf); + const InterfaceNH *rnh = dynamic_cast(rflow->rpf_nh()); + EXPECT_TRUE(rnh->GetInterface() == static_cast(vmi1_)); +} + +// RPF for L3 flow from VM to Fabric +TEST_F(FlowRpfTest, L3_VmToFabric_1) { + TxIpPacket(vmi0_->id(), VM1_IP, REMOTE_VM1_IP, 1); + client->WaitForIdle(); + FlowEntry *flow = FlowGet(vrf1_->vrf_id(), VM1_IP, REMOTE_VM1_IP, 1, 0, 0, + vmi0_->flow_key_nh()->id()); + FlowEntry *rflow = flow->reverse_flow_entry(); + + EXPECT_TRUE(flow->data().enable_rpf); + const InterfaceNH *nh = dynamic_cast(flow->rpf_nh()); + EXPECT_TRUE(nh != NULL); + EXPECT_TRUE(nh->GetInterface() == static_cast(vmi0_)); + + EXPECT_TRUE(rflow->data().enable_rpf); + const TunnelNH *rnh = dynamic_cast(rflow->rpf_nh()); + EXPECT_TRUE(*(rnh->GetDip()) == Ip4Address::from_string("100.100.100.1")); +} + +// RPF for L3 flow from Fabric to VM +TEST_F(FlowRpfTest, L3_FabricToVm_1) { + TxIpMplsPacket(eth0_->id(), "100.100.100.1", router_id_, vmi0_->label(), + REMOTE_VM1_IP, VM1_IP, 1); + client->WaitForIdle(); + FlowEntry *flow = FlowGet(vrf1_->vrf_id(), REMOTE_VM1_IP, VM1_IP, + 1, 0, 0, vmi0_->flow_key_nh()->id()); + FlowEntry *rflow = flow->reverse_flow_entry(); + + EXPECT_TRUE(flow->data().enable_rpf); + const TunnelNH *nh = dynamic_cast(flow->rpf_nh()); + EXPECT_TRUE(*(nh->GetDip()) == Ip4Address::from_string("100.100.100.1")); + + const InterfaceNH *rnh = dynamic_cast(rflow->rpf_nh()); + EXPECT_TRUE(rnh != NULL); + EXPECT_TRUE(rnh->GetInterface() == static_cast(vmi0_)); +} + +// RPF for L2 Local flow +TEST_F(FlowRpfTest, L2_Local_1) { + TxL2Packet(vmi0_->id(), VM1_MAC, VM2_MAC, VM1_IP, VM2_IP, 1); + client->WaitForIdle(); + + FlowEntry *flow = FlowGet(vrf1_->vrf_id(), VM1_IP, VM2_IP, 1, 0, 0, + vmi0_->flow_key_nh()->id()); + FlowEntry *rflow = flow->reverse_flow_entry(); + + EXPECT_TRUE(flow->data().enable_rpf); + const InterfaceNH *nh = dynamic_cast(flow->rpf_nh()); + EXPECT_TRUE(nh != NULL); + EXPECT_TRUE(nh->GetInterface() == static_cast(vmi0_)); + + EXPECT_TRUE(rflow->data().enable_rpf); + const InterfaceNH *rnh = dynamic_cast(rflow->rpf_nh()); + EXPECT_TRUE(rnh->GetInterface() == static_cast(vmi1_)); +} + +// RPF for L2 flow from VM to Fabric +TEST_F(FlowRpfTest, L2_VmToFabric_1) { + TxL2Packet(vmi0_->id(), VM1_MAC, REMOTE_VM1_MAC, VM1_IP, REMOTE_VM1_IP, 1); + client->WaitForIdle(); + FlowEntry *flow = FlowGet(vrf1_->vrf_id(), VM1_IP, REMOTE_VM1_IP, 1, 0, 0, + vmi0_->flow_key_nh()->id()); + FlowEntry *rflow = flow->reverse_flow_entry(); + + EXPECT_TRUE(flow->data().enable_rpf); + const InterfaceNH *nh = dynamic_cast(flow->rpf_nh()); + EXPECT_TRUE(nh != NULL); + EXPECT_TRUE(nh->GetInterface() == static_cast(vmi0_)); + + EXPECT_TRUE(rflow->data().enable_rpf); + const TunnelNH *rnh = dynamic_cast(rflow->rpf_nh()); + EXPECT_TRUE(*(rnh->GetDip()) == Ip4Address::from_string("100.100.100.1")); +} + +// RPF for L2 flow from Fabric to VM +TEST_F(FlowRpfTest, L2_FabricToVm_1) { + TxL2IpMplsPacket(eth0_->id(), "100.100.100.1", router_id_, + vmi0_->l2_label(), REMOTE_VM1_MAC, VM1_MAC, + REMOTE_VM1_IP, VM1_IP, 1); + client->WaitForIdle(); + FlowEntry *flow = FlowGet(vrf1_->vrf_id(), REMOTE_VM1_IP, VM1_IP, + 1, 0, 0, MplsToNextHop(vmi0_->l2_label())->id()); + FlowEntry *rflow = flow->reverse_flow_entry(); + + EXPECT_TRUE(flow->data().enable_rpf); + const TunnelNH *nh = dynamic_cast(flow->rpf_nh()); + EXPECT_TRUE(*(nh->GetDip()) == Ip4Address::from_string("100.100.100.1")); + + const InterfaceNH *rnh = dynamic_cast(rflow->rpf_nh()); + EXPECT_TRUE(rnh != NULL); + EXPECT_TRUE(rnh->GetInterface() == static_cast(vmi0_)); +} + +// RPF for L2 Local flow based on SrcIp on different port +TEST_F(FlowRpfTest, L2_VmToFabric_SrcIp_1) { + TxL2Packet(vmi0_->id(), VM1_MAC, REMOTE_VM1_MAC, VM2_IP, REMOTE_VM1_IP, 1); + client->WaitForIdle(); + FlowEntry *flow = FlowGet(vrf1_->vrf_id(), VM2_IP, REMOTE_VM1_IP, 1, 0, 0, + vmi0_->flow_key_nh()->id()); + FlowEntry *rflow = flow->reverse_flow_entry(); + + EXPECT_TRUE(flow->data().enable_rpf); + const InterfaceNH *nh = dynamic_cast(flow->rpf_nh()); + EXPECT_TRUE(nh != NULL); + EXPECT_TRUE(nh->GetInterface() == static_cast(vmi1_)); + + EXPECT_TRUE(rflow->data().enable_rpf); + const TunnelNH *rnh = dynamic_cast(rflow->rpf_nh()); + EXPECT_TRUE(*(rnh->GetDip()) == Ip4Address::from_string("100.100.100.1")); +} + +// RPF for L2 Local flow based on SrcIp that doesnt have route +TEST_F(FlowRpfTest, L2_VmToFabric_SrcIp_2) { + TxL2Packet(vmi0_->id(), VM1_MAC, REMOTE_VM1_MAC, VM3_IP, REMOTE_VM1_IP, 1); + client->WaitForIdle(); + FlowEntry *flow = FlowGet(vrf1_->vrf_id(), VM3_IP, REMOTE_VM1_IP, 1, 0, 0, + vmi0_->flow_key_nh()->id()); + FlowEntry *rflow = flow->reverse_flow_entry(); + + EXPECT_TRUE(flow->data().enable_rpf); + const InterfaceNH *nh = dynamic_cast(flow->rpf_nh()); + EXPECT_TRUE(nh != NULL); + EXPECT_EQ(flow->short_flow_reason(), FlowEntry::SHORT_NO_SRC_ROUTE_L2RPF); + + EXPECT_TRUE(rflow->data().enable_rpf); + const TunnelNH *rnh = dynamic_cast(rflow->rpf_nh()); + EXPECT_TRUE(*(rnh->GetDip()) == Ip4Address::from_string("100.100.100.1")); +} + +// RPF for L2 Local flow based on DstIp on different port +TEST_F(FlowRpfTest, L2_VmToFabric_DstIp_1) { + CreateRemoteRoute("vrf1", REMOTE_VM2_IP, 32, "100.100.100.2", 10, "vn1"); + TxL2Packet(vmi0_->id(), VM1_MAC, REMOTE_VM1_MAC, VM1_IP, REMOTE_VM2_IP, 1); + client->WaitForIdle(); + FlowEntry *flow = FlowGet(vrf1_->vrf_id(), VM1_IP, REMOTE_VM2_IP, 1, 0, 0, + vmi0_->flow_key_nh()->id()); + FlowEntry *rflow = flow->reverse_flow_entry(); + + EXPECT_TRUE(flow->data().enable_rpf); + const InterfaceNH *nh = dynamic_cast(flow->rpf_nh()); + EXPECT_TRUE(nh != NULL); + EXPECT_TRUE(nh->GetInterface() == static_cast(vmi0_)); + + EXPECT_TRUE(rflow->data().enable_rpf); + const TunnelNH *rnh = dynamic_cast(rflow->rpf_nh()); + EXPECT_TRUE(*(rnh->GetDip()) == Ip4Address::from_string("100.100.100.2")); + DeleteRemoteRoute("vrf1", REMOTE_VM2_IP, 32); +} + +// RPF for L2 Local flow based on DstIp that doesnt have route +TEST_F(FlowRpfTest, L2_VmToFabric_DstIp_2) { + TxL2Packet(vmi0_->id(), VM1_MAC, REMOTE_VM1_MAC, VM1_IP, REMOTE_VM2_IP, 1); + client->WaitForIdle(); + FlowEntry *flow = FlowGet(vrf1_->vrf_id(), VM1_IP, REMOTE_VM2_IP, 1, 0, 0, + vmi0_->flow_key_nh()->id()); + EXPECT_TRUE(flow != NULL); + FlowEntry *rflow = flow->reverse_flow_entry(); + EXPECT_TRUE(rflow != NULL); + + EXPECT_TRUE(flow->data().enable_rpf); + const InterfaceNH *nh = dynamic_cast(flow->rpf_nh()); + EXPECT_TRUE(nh != NULL); + EXPECT_TRUE(nh->GetInterface() == static_cast(vmi0_)); + + EXPECT_TRUE(rflow->data().enable_rpf); + EXPECT_TRUE(rflow->rpf_nh() != NULL); + EXPECT_EQ(rflow->short_flow_reason(), + FlowEntry::SHORT_NO_SRC_ROUTE_L2RPF); +} + +// If source-ip hits subnet route then RPF should be based on L2 Route +TEST_F(FlowRpfTest, L2_VmToFabric_SubnetRoute_1) { + CreateRemoteRoute("vrf1", REMOTE_VM2_IP, 30, "100.100.100.2", 10, "vn1"); + TxL2Packet(vmi0_->id(), VM1_MAC, REMOTE_VM1_MAC, VM1_IP, REMOTE_VM2_IP, 1); + client->WaitForIdle(); + FlowEntry *flow = FlowGet(vrf1_->vrf_id(), VM1_IP, REMOTE_VM2_IP, 1, 0, 0, + vmi0_->flow_key_nh()->id()); + FlowEntry *rflow = flow->reverse_flow_entry(); + + EXPECT_TRUE(flow->data().enable_rpf); + const InterfaceNH *nh = dynamic_cast(flow->rpf_nh()); + EXPECT_TRUE(nh != NULL); + EXPECT_TRUE(nh->GetInterface() == static_cast(vmi0_)); + + EXPECT_TRUE(rflow->data().enable_rpf); + const TunnelNH *rnh = dynamic_cast(rflow->rpf_nh()); + EXPECT_TRUE(*(rnh->GetDip()) == Ip4Address::from_string("100.100.100.1")); + DeleteRemoteRoute("vrf1", REMOTE_VM2_IP, 30); +} + +// If source-ip hits ECMP route then RPF should be based on L2 Route +TEST_F(FlowRpfTest, L2_VmToFabric_EcmpRoute_1) { + CreateRemoteEcmpRoute("vrf1", REMOTE_VM2_IP, 32, "vn1", 3); + TxL2Packet(vmi0_->id(), VM1_MAC, REMOTE_VM1_MAC, VM1_IP, REMOTE_VM2_IP, 1); + client->WaitForIdle(); + FlowEntry *flow = FlowGet(vrf1_->vrf_id(), VM1_IP, REMOTE_VM2_IP, 1, 0, 0, + vmi0_->flow_key_nh()->id()); + FlowEntry *rflow = flow->reverse_flow_entry(); + + EXPECT_TRUE(flow->data().enable_rpf); + const InterfaceNH *nh = dynamic_cast(flow->rpf_nh()); + EXPECT_TRUE(nh != NULL); + EXPECT_TRUE(nh->GetInterface() == static_cast(vmi0_)); + + EXPECT_TRUE(rflow->data().enable_rpf); + const TunnelNH *rnh = dynamic_cast(rflow->rpf_nh()); + EXPECT_TRUE(*(rnh->GetDip()) == Ip4Address::from_string("100.100.100.1")); + DeleteRemoteRoute("vrf1", REMOTE_VM2_IP, 32); +} + +int main(int argc, char *argv[]) { + GETUSERARGS(); + client = TestInit(init_file, ksync_init, true, true, true, 100*1000); + int ret = RUN_ALL_TESTS(); + client->WaitForIdle(); + TestShutdown(); + delete client; + return ret; +} diff --git a/src/vnsw/agent/pkt/test/test_xml_packet_ut.cc b/src/vnsw/agent/pkt/test/test_xml_packet_ut.cc index a2767b85b8b..a5fa03a0f11 100644 --- a/src/vnsw/agent/pkt/test/test_xml_packet_ut.cc +++ b/src/vnsw/agent/pkt/test/test_xml_packet_ut.cc @@ -8,6 +8,7 @@ #include "test-xml/test_xml_oper.h" #include "oper/test/test_xml_physical_device.h" #include "test_xml_flow_agent_init.h" +#include "test_pkt_util.h" using namespace std; namespace opt = boost::program_options; @@ -32,15 +33,6 @@ static void GetArgs(char *test_file, int argc, char *argv[]) { return; } -static bool FlowStatsTimerStartStopTrigger(FlowStatsCollectorObject *obj, - bool stop) { - for (int i = 0; i < FlowStatsCollectorObject::kMaxCollectors; i++) { - FlowStatsCollector *fsc = obj->GetCollector(i); - fsc->TestStartStopTimer(stop); - } - return true; -} - class TestPkt : public ::testing::Test { public: virtual void SetUp() { @@ -49,7 +41,7 @@ class TestPkt : public ::testing::Test { interface_count_ = agent_->interface_table()->Size(); flow_stats_collector_ = agent_->flow_stats_manager()-> default_flow_stats_collector_obj(); - FlowStatsTimerStartStop(true); + FlowStatsTimerStartStop(agent_, true); AddIPAM("vn1", ipam_info, 1); client->WaitForIdle(); } @@ -59,22 +51,11 @@ class TestPkt : public ::testing::Test { EXPECT_EQ(agent_->vn_table()->Size(), 0); EXPECT_EQ(agent_->interface_table()->Size(), interface_count_); agent_->flow_stats_manager()->set_flow_export_count(0); - FlowStatsTimerStartStop(false); + FlowStatsTimerStartStop(agent_, false); DelIPAM("vn1"); client->WaitForIdle(); } - void FlowStatsTimerStartStop(bool stop) { - int task_id = - agent_->task_scheduler()->GetTaskId(kTaskFlowStatsCollector); - std::auto_ptr trigger_ - (new TaskTrigger(boost::bind(FlowStatsTimerStartStopTrigger, - flow_stats_collector_, stop), - task_id, 0)); - trigger_->Set(); - client->WaitForIdle(); - } - Agent *agent_; FlowProto *proto_; uint32_t interface_count_; diff --git a/src/vnsw/agent/pkt/vrouter_interface.h b/src/vnsw/agent/pkt/vrouter_interface.h index 3d00e824225..bfd2716f215 100644 --- a/src/vnsw/agent/pkt/vrouter_interface.h +++ b/src/vnsw/agent/pkt/vrouter_interface.h @@ -37,7 +37,6 @@ class VrouterControlInterface : public ControlInterface { vr_cmd_list_[AGENT_TRAP_FLOW_MISS] = AgentHdr::TRAP_FLOW_MISS; vr_cmd_list_[AGENT_TRAP_L3_PROTOCOLS] = AgentHdr::TRAP_L3_PROTOCOLS; vr_cmd_list_[AGENT_TRAP_DIAG] = AgentHdr::TRAP_DIAG; - vr_cmd_list_[AGENT_TRAP_ECMP_RESOLVE] = AgentHdr::TRAP_ECMP_RESOLVE; vr_cmd_list_[AGENT_TRAP_SOURCE_MISMATCH] = AgentHdr::TRAP_SOURCE_MISMATCH; vr_cmd_list_[AGENT_TRAP_HANDLE_DF] = AgentHdr::TRAP_HANDLE_DF; diff --git a/src/vnsw/agent/test-xml/test_xml_oper.cc b/src/vnsw/agent/test-xml/test_xml_oper.cc index 55f96591c62..dcfc2a2520e 100644 --- a/src/vnsw/agent/test-xml/test_xml_oper.cc +++ b/src/vnsw/agent/test-xml/test_xml_oper.cc @@ -1356,11 +1356,11 @@ bool AgentUtXmlFlowValidate::Validate() { if (rpf_nh_) { if (rpf_nh_ == NextHopTable::kRpfDiscardIndex) { - if (flow->data().nh.get() != NULL) { + if (flow->data().rpf_nh.get() != NULL) { return false; } - } else if (!flow->data().nh.get() || - flow->data().nh.get()->id() != rpf_nh_) { + } else if (!flow->data().rpf_nh.get() || + flow->data().rpf_nh.get()->id() != rpf_nh_) { return false; } } diff --git a/src/vnsw/agent/test/test_cmn_util.h b/src/vnsw/agent/test/test_cmn_util.h index fdec659c985..420adc75311 100644 --- a/src/vnsw/agent/test/test_cmn_util.h +++ b/src/vnsw/agent/test/test_cmn_util.h @@ -9,6 +9,11 @@ using namespace std; +#define EXPECT_TRUE_RET(a) \ + do { EXPECT_TRUE((a)); if ((a) == false) ret = false; } while(0); +#define EXPECT_FALSE_RET(a) \ + do { EXPECT_FALSE((a)); if ((a) == true) ret = false; } while(0); + class VmiSubscribeEntry; static const int kProjectUuid = 101; @@ -125,7 +130,9 @@ NextHop *InetInterfaceNHGet(NextHopTable *table, const char *ifname, NextHop *ReceiveNHGet(NextHopTable *table, const char *ifname, bool policy); bool VrfFind(const char *name); bool VrfFind(const char *name, bool ret_del); +VrfEntry *VrfGet(size_t index); VrfEntry *VrfGet(const char *name, bool ret_del=false); +uint32_t GetVrfId(const char *name); bool VnFind(int id); VnEntry *VnGet(int id); bool VxlanFind(int id); diff --git a/src/vnsw/agent/test/test_util.cc b/src/vnsw/agent/test/test_util.cc index 8812d023f58..c7a77f68313 100644 --- a/src/vnsw/agent/test/test_util.cc +++ b/src/vnsw/agent/test/test_util.cc @@ -659,6 +659,16 @@ VrfEntry *VrfGet(const char *name, bool ret_del) { return vrf; } +uint32_t GetVrfId(const char *name) { + VrfEntry *vrf = VrfGet(name, false); + return vrf->vrf_id(); +} + +VrfEntry *VrfGet(size_t index) { + return static_cast + (Agent::GetInstance()->vrf_table()->FindVrfFromId(index)); +} + bool VnFind(int id) { VnEntry *vn; VnKey key(MakeUuid(id)); diff --git a/src/vnsw/agent/vrouter/ksync/flowtable_ksync.cc b/src/vnsw/agent/vrouter/ksync/flowtable_ksync.cc index 9c55d930283..120ea672a92 100644 --- a/src/vnsw/agent/vrouter/ksync/flowtable_ksync.cc +++ b/src/vnsw/agent/vrouter/ksync/flowtable_ksync.cc @@ -321,7 +321,7 @@ int FlowTableKSyncEntry::Encode(sandesh_op::type op, char *buf, int buf_len) { (uint32_t)CompositeNH::kInvalidComponentNHIdx) { req.set_fr_ecmp_nh_index(flow_entry_->data().component_nh_idx); } else { - req.set_fr_ecmp_nh_index(0); + req.set_fr_ecmp_nh_index(-1); } if (action == VR_FLOW_ACTION_NAT) { @@ -495,8 +495,8 @@ bool FlowTableKSyncEntry::Sync() { } uint32_t nh_id = NextHopTable::kRpfDiscardIndex; - if (flow_entry_->data().nh.get()) { - nh_id = flow_entry_->data().nh.get()->id(); + if (flow_entry_->data().rpf_nh.get()) { + nh_id = flow_entry_->data().rpf_nh.get()->id(); } if (src_nh_id_ != nh_id) { src_nh_id_ = nh_id;