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;