From 18f6501bace43ab52fd6e40449a349c53fbd017f Mon Sep 17 00:00:00 2001 From: Naveen N Date: Mon, 23 May 2016 19:15:47 +0530 Subject: [PATCH] * Set service vlan IP ecmp mode and tracking ip based on service IP ECMP mode for service vlan route would be picked from service instance ip if any, else it would be picked up from native ip Test case for same Closes-bug:#1582277 Change-Id: I31120147a3df3f7ab3d7ff8d5257343fff3c9e39 --- src/vnsw/agent/oper/agent.sandesh | 2 + src/vnsw/agent/oper/agent_path.cc | 5 + src/vnsw/agent/oper/path_preference.cc | 10 +- src/vnsw/agent/oper/path_preference.h | 8 + src/vnsw/agent/oper/test/test_aap.cc | 56 ++++++- src/vnsw/agent/oper/test/test_intf.cc | 93 ++++++++++++ src/vnsw/agent/oper/test/test_ipv6.cc | 200 +++++++++++++++++++++++++ src/vnsw/agent/oper/vm_interface.cc | 104 ++++++++++++- src/vnsw/agent/oper/vm_interface.h | 16 +- src/vnsw/agent/test/test_cmn_util.h | 1 + src/vnsw/agent/test/test_nh.cc | 43 ++++++ src/vnsw/agent/test/test_util.cc | 16 ++ 12 files changed, 544 insertions(+), 10 deletions(-) diff --git a/src/vnsw/agent/oper/agent.sandesh b/src/vnsw/agent/oper/agent.sandesh index f2ef9a2b018..812f763b6ba 100644 --- a/src/vnsw/agent/oper/agent.sandesh +++ b/src/vnsw/agent/oper/agent.sandesh @@ -339,6 +339,7 @@ struct PathPreferenceSandeshData { 2: i32 preference; 3: bool ecmp; 4: bool wait_for_traffic; + 5: optional string dependent_ip; } /** @@ -1019,6 +1020,7 @@ traceobject sandesh PathPreferenceTrace { 4: i32 sequence; 5: string state; 6: i32 retry_timeout; + 7: string dependent_ip; } /** diff --git a/src/vnsw/agent/oper/agent_path.cc b/src/vnsw/agent/oper/agent_path.cc index 2c06f4edd65..ae95dc5b77e 100644 --- a/src/vnsw/agent/oper/agent_path.cc +++ b/src/vnsw/agent/oper/agent_path.cc @@ -960,6 +960,7 @@ bool PathPreferenceData::AddChangePath(Agent *agent, AgentPath *path, } path_preference_.set_ecmp(path->path_preference().ecmp()); + path_preference_.set_dependent_ip(path->path_preference().dependent_ip()); if (path && path->path_preference() != path_preference_) { path->set_path_preference(path_preference_); @@ -1219,6 +1220,10 @@ void AgentPath::SetSandeshData(PathSandeshData &pdata) const { path_preference_data.set_ecmp(path_preference_.ecmp()); path_preference_data.set_wait_for_traffic( path_preference_.wait_for_traffic()); + if (path_preference_.dependent_ip().is_unspecified() == false) { + path_preference_data.set_dependent_ip( + path_preference_.dependent_ip().to_string()); + } pdata.set_path_preference_data(path_preference_data); pdata.set_active_label(GetActiveLabel()); if (peer()->GetType() == Peer::MAC_VM_BINDING_PEER) { diff --git a/src/vnsw/agent/oper/path_preference.cc b/src/vnsw/agent/oper/path_preference.cc index 9cf2e4fcd14..f7104e6fa59 100644 --- a/src/vnsw/agent/oper/path_preference.cc +++ b/src/vnsw/agent/oper/path_preference.cc @@ -416,8 +416,14 @@ void PathPreferenceSM::Process() { } void PathPreferenceSM::Log(std::string state) { + std::string dependent_ip_str = ""; + if (is_dependent_rt()) { + dependent_ip_str = dependent_ip().to_string(); + } + PATH_PREFERENCE_TRACE(rt_->vrf()->GetName(), rt_->GetAddressString(), - preference(), sequence(), state, timeout()); + preference(), sequence(), state, timeout(), + dependent_ip_str); } void PathPreferenceSM::EnqueuePathChange() { @@ -767,6 +773,8 @@ void PathPreferenceState::Process() { path_preference_sm->set_dependent_rt(NULL); } path_preference_sm->set_is_dependent_rt(dependent_rt); + path_preference_sm->set_dependent_ip( + path->path_preference().dependent_ip()); path_preference_sm->set_seen(true); path_preference_sm->Process(); diff --git a/src/vnsw/agent/oper/path_preference.h b/src/vnsw/agent/oper/path_preference.h index 210282c2136..adde2475164 100644 --- a/src/vnsw/agent/oper/path_preference.h +++ b/src/vnsw/agent/oper/path_preference.h @@ -88,6 +88,14 @@ class PathPreferenceSM: is_dependent_rt_ = dependent_path; } + void set_dependent_ip(const IpAddress &ip) { + path_preference_.set_dependent_ip(ip); + } + + IpAddress dependent_ip() { + return path_preference_.dependent_ip(); + } + bool seen() { return seen_; } uint32_t max_sequence() const { return max_sequence_;} void Process(); diff --git a/src/vnsw/agent/oper/test/test_aap.cc b/src/vnsw/agent/oper/test/test_aap.cc index c0d070e142e..d627d9c5ffc 100644 --- a/src/vnsw/agent/oper/test/test_aap.cc +++ b/src/vnsw/agent/oper/test/test_aap.cc @@ -46,7 +46,7 @@ void RouterIdDepInit(Agent *agent) { } struct PortInfo input[] = { - {"intf1", 1, "1.1.1.1", "00:00:00:01:01:01", 1, 1}, + {"intf1", 1, "1.1.1.1", "00:00:00:01:01:01", 1, 1, "fd10::2"}, }; class TestAap : public ::testing::Test { @@ -1133,6 +1133,60 @@ TEST_F(TestAap, StateMachine_19) { client->WaitForIdle(); } +TEST_F(TestAap, StateMachine_20) { + Ip4Address ip = Ip4Address::from_string("1.1.1.1"); + + AddVmPortVrf("ser1", "11.1.1.253", 1); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "routing-instance", "vrf1"); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "intf1"); + client->WaitForIdle(); + + Ip4Address service_vlan_rt = Ip4Address::from_string("11.1.1.253"); + VmInterface *vm_intf = VmInterfaceGet(1); + InetUnicastRouteEntry *rt = + RouteGet("vrf1", service_vlan_rt, 32); + const AgentPath *path = rt->FindPath(vm_intf->peer()); + EXPECT_TRUE(path->path_preference().sequence() == 0); + EXPECT_TRUE(path->path_preference().preference() == PathPreference::LOW); + EXPECT_TRUE(path->path_preference().ecmp() == false); + EXPECT_TRUE(path->path_preference().wait_for_traffic() == true); + + Agent::GetInstance()->oper_db()->route_preference_module()-> + EnqueueTrafficSeen(ip, 32, vm_intf->id(), vm_intf->vrf()->vrf_id(), + MacAddress::FromString(vm_intf->vm_mac())); + client->WaitForIdle(); + EXPECT_TRUE(path->path_preference().sequence() == 0); + EXPECT_TRUE(path->path_preference().preference() == PathPreference::HIGH); + EXPECT_TRUE(path->path_preference().ecmp() == false); + EXPECT_TRUE(path->path_preference().wait_for_traffic() == false); + + AddServiceInstanceIp("instaneip100", 100, "2.2.2.2", false); + AddLink("virtual-machine-interface", "intf1", "instance-ip", "instaneip100"); + client->WaitForIdle(); + EXPECT_TRUE(path->path_preference().sequence() == 0); + EXPECT_TRUE(path->path_preference().preference() == PathPreference::LOW); + EXPECT_TRUE(path->path_preference().ecmp() == false); + EXPECT_TRUE(path->path_preference().wait_for_traffic() == true); + + AddServiceInstanceIp("instaneip100", 100, "2.2.2.2", true); + client->WaitForIdle(); + EXPECT_TRUE(path->path_preference().sequence() == 0); + EXPECT_TRUE(path->path_preference().preference() == PathPreference::HIGH); + EXPECT_TRUE(path->path_preference().ecmp() == true); + EXPECT_TRUE(path->path_preference().wait_for_traffic() == false); + + Ip6Address ip6 = Ip6Address::from_string("ffd2::11"); + Agent::GetInstance()->oper_db()->route_preference_module()-> + EnqueueTrafficSeen(ip6, 128, vm_intf->id(), vm_intf->vrf()->vrf_id(), + MacAddress::FromString(vm_intf->vm_mac())); + client->WaitForIdle(); + EXPECT_TRUE(path->path_preference().sequence() == 0); + EXPECT_TRUE(path->path_preference().preference() == PathPreference::HIGH); + EXPECT_TRUE(path->path_preference().ecmp() == true); + EXPECT_TRUE(path->path_preference().wait_for_traffic() == false); +} int main(int argc, char *argv[]) { GETUSERARGS(); diff --git a/src/vnsw/agent/oper/test/test_intf.cc b/src/vnsw/agent/oper/test/test_intf.cc index 444ff154256..e8195b4412e 100644 --- a/src/vnsw/agent/oper/test/test_intf.cc +++ b/src/vnsw/agent/oper/test/test_intf.cc @@ -1717,6 +1717,11 @@ TEST_F(IntfTest, VmPortServiceVlanDelete_1) { Ip4Address service_ip = Ip4Address::from_string("2.2.2.100"); EXPECT_TRUE(RouteFind("vrf2", service_ip, 32)); EXPECT_TRUE(VmPortServiceVlanCount(1, 1)); + InetUnicastRouteEntry *rt = RouteGet("vrf2", service_ip, 32); + if (rt) { + EXPECT_TRUE(rt->GetActivePath()->path_preference().dependent_ip() == + Ip4Address::from_string("1.1.1.10")); + } DoInterfaceSandesh(""); client->WaitForIdle(); @@ -1771,6 +1776,12 @@ TEST_F(IntfTest, VmPortServiceVlanDelete_2) { Ip4Address service_ip = Ip4Address::from_string("2.2.2.100"); EXPECT_TRUE(RouteFind("vrf2", service_ip, 32)); EXPECT_TRUE(VmPortServiceVlanCount(1, 1)); + InetUnicastRouteEntry *rt = RouteGet("vrf2", service_ip, 32); + if (rt) { + EXPECT_TRUE(rt->GetActivePath()->path_preference().dependent_ip() == + Ip4Address::from_string("1.1.1.10")); + } + DoInterfaceSandesh(""); client->WaitForIdle(); @@ -1811,6 +1822,13 @@ TEST_F(IntfTest, VmPortServiceVlanDelete_2) { client->WaitForIdle(); EXPECT_TRUE(RouteFind("vrf2", service_ip, 32)); EXPECT_TRUE(VmPortServiceVlanCount(1, 1)); + + rt = RouteGet("vrf2", service_ip, 32); + if (rt) { + EXPECT_TRUE(rt->GetActivePath()->path_preference().dependent_ip() == + Ip4Address::from_string("1.1.1.10")); + } + DoInterfaceSandesh(""); client->WaitForIdle(); @@ -1867,6 +1885,11 @@ TEST_F(IntfTest, VmPortServiceVlanAdd_1) { EXPECT_TRUE(VmPortActive(input, 0)); service_ip = Ip4Address::from_string("2.2.2.100"); EXPECT_TRUE(RouteFind("vrf2", service_ip, 32)); + InetUnicastRouteEntry *rt = RouteGet("vrf2", service_ip, 32); + if (rt) { + EXPECT_TRUE(rt->GetActivePath()->path_preference().dependent_ip() == + Ip4Address::from_string("1.1.1.10")); + } //Delete config for vnet1, forcing interface to deactivate //verify that route and service vlan map gets cleaned up @@ -1913,6 +1936,11 @@ TEST_F(IntfTest, VmPortServiceVlanAdd_2) { client->WaitForIdle(); Ip4Address service_ip = Ip4Address::from_string("2.2.2.100"); EXPECT_TRUE(RouteFind("vrf2", service_ip, 32)); + InetUnicastRouteEntry *rt = RouteGet("vrf2", service_ip, 32); + if (rt) { + EXPECT_TRUE(rt->GetActivePath()->path_preference().dependent_ip() == + Ip4Address::from_string("1.1.1.10")); + } //Delete the interface, all service vlan routes should be deleted //and interface should be released @@ -2081,6 +2109,71 @@ TEST_F(IntfTest, VmPortServiceVlanVrfDelete_1) { EXPECT_FALSE(VrfFind("vrf2")); } +TEST_F(IntfTest, VmPortServiceVlanServiceIp_1) { + struct PortInfo input[] = { + {"vnet1", 1, "1.1.1.10", "00:00:00:01:01:01", 1, 1}, + }; + + client->Reset(); + CreateVmportEnv(input, 1); + client->WaitForIdle(); + EXPECT_TRUE(VmPortActive(input, 0)); + Ip4Address service_ip = Ip4Address::from_string("2.2.2.100"); + + AddVn("vn2", 2); + AddVrf("vrf2", 2); + AddLink("virtual-network", "vn2", "routing-instance", "vrf2"); + //Add service vlan for vnet1 + client->WaitForIdle(); + AddVmPortVrf("vmvrf1", "2.2.2.100", 10); + AddLink("virtual-machine-interface-routing-instance", "vmvrf1", + "routing-instance", "vrf2"); + AddLink("virtual-machine-interface-routing-instance", "vmvrf1", + "virtual-machine-interface", "vnet1"); + client->WaitForIdle(); + + InetUnicastRouteEntry *rt = RouteGet("vrf2", service_ip, 32); + EXPECT_TRUE(rt->GetActivePath()->path_preference().dependent_ip() == + Ip4Address::from_string("1.1.1.10")); + + AddServiceInstanceIp("serviceip1", 100, "1.1.1.100", false); + AddLink("virtual-machine-interface", "vnet1", "instance-ip", "serviceip1"); + client->WaitForIdle(); + + EXPECT_TRUE(rt->GetActivePath()->path_preference().dependent_ip() == + Ip4Address::from_string("1.1.1.100")); + EXPECT_TRUE(rt->GetActivePath()->path_preference().ecmp() == false); + + AddServiceInstanceIp("serviceip1", 100, "1.1.1.100", true); + client->WaitForIdle(); + EXPECT_TRUE(rt->GetActivePath()->path_preference().ecmp() == true); + + DelLink("virtual-machine-interface", "vnet1", "instance-ip", "serviceip1"); + client->WaitForIdle(); + EXPECT_TRUE(rt->GetActivePath()->path_preference().dependent_ip() == + Ip4Address::from_string("1.1.1.10")); + EXPECT_TRUE(rt->GetActivePath()->path_preference().ecmp() == false); + + DelNode("instance-ip", "serviceip1"); + //Clean up + DelLink("virtual-machine-interface-routing-instance", "vmvrf1", + "routing-instance", "vrf2"); + DelLink("virtual-machine-interface-routing-instance", "vmvrf1", + "virtual-machine-interface", "vnet1"); + DelLink("virtual-network", "vn2", "routing-instance", "vrf2"); + DelLink("virtual-network", "vn1", "virtual-machine-interface", + input[0].name); + DelVmPortVrf("vmvrf1"); + DelVrf("vrf2"); + DelVn("vn2"); + client->WaitForIdle(); + DeleteVmportEnv(input, 1, true); + client->WaitForIdle(); + EXPECT_FALSE(VrfFind("vrf1")); + EXPECT_FALSE(VrfFind("vrf2")); +} + + //Add and delete static route TEST_F(IntfTest, IntfStaticRoute) { struct PortInfo input[] = { diff --git a/src/vnsw/agent/oper/test/test_ipv6.cc b/src/vnsw/agent/oper/test/test_ipv6.cc index e3bcee03599..eec4229b0fc 100644 --- a/src/vnsw/agent/oper/test/test_ipv6.cc +++ b/src/vnsw/agent/oper/test/test_ipv6.cc @@ -539,6 +539,206 @@ TEST_F(Ipv6Test, VlanNHRoute_3) { client->WaitForIdle(); } +/* Verify that tracking ip and ecmp mode gets set properly and + * routes are added */ +TEST_F(Ipv6Test, VlanNHServiceIp_1) { + struct PortInfo input[] = { + {"vnet1", 1, "1.1.1.1", "00:00:00:01:01:01", 1, 1}, + }; + string service_vlan_ip("2.2.2.1"); + string service_vlan_ip6("fd12::1"); + + client->Reset(); + CreateVmportEnv(input, 1); + client->WaitForIdle(); + + //Verify that interface is IPv4 active. + EXPECT_TRUE(VmPortActive(input, 0)); + EXPECT_TRUE(RouteFind("vrf1", input[0].addr, 32)); + + // Add service interface-1 + AddVrf("vrf2"); + AddVmPortVrf("ser1", service_vlan_ip, 1, service_vlan_ip6); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "routing-instance", "vrf2"); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet1"); + client->WaitForIdle(); + + // Validate service vlan route + InetUnicastRouteEntry *rt = RouteGet("vrf2", + Ip4Address::from_string(service_vlan_ip), 32); + EXPECT_TRUE(rt != NULL); + + InetUnicastRouteEntry *rt6 = RouteGetV6("vrf2", + Ip6Address::from_string(service_vlan_ip6), 128); + EXPECT_TRUE(rt6 != NULL); + + AddServiceInstanceIp("serviceip1", 100, "1.1.1.100", false); + AddLink("virtual-machine-interface", "vnet1", "instance-ip", "serviceip1"); + client->WaitForIdle(); + EXPECT_TRUE(rt->GetActivePath()->path_preference().dependent_ip() == + Ip4Address::from_string("1.1.1.100")); + EXPECT_TRUE(rt->GetActivePath()->path_preference().ecmp() == false); + EXPECT_TRUE(rt6->GetActivePath()->path_preference().dependent_ip() == + Ip6Address()); + EXPECT_TRUE(rt6->GetActivePath()->path_preference().ecmp() == false); + + AddServiceInstanceIp("serviceip1", 100, "1.1.1.100", true); + client->WaitForIdle(); + + EXPECT_TRUE(rt->GetActivePath()->path_preference().dependent_ip() == + Ip4Address::from_string("1.1.1.100")); + EXPECT_TRUE(rt->GetActivePath()->path_preference().ecmp() == true); + EXPECT_TRUE(rt6->GetActivePath()->path_preference().dependent_ip() == + Ip6Address()); + EXPECT_TRUE(rt6->GetActivePath()->path_preference().ecmp() == false); + + DelLink("virtual-machine-interface", "vnet1", "instance-ip", "serviceip1"); + DelNode("instance-ip", "serviceip1"); + client->WaitForIdle(); + + EXPECT_TRUE(rt->GetActivePath()->path_preference().dependent_ip() == + Ip4Address::from_string("1.1.1.1")); + EXPECT_TRUE(rt->GetActivePath()->path_preference().ecmp() == false); + EXPECT_TRUE(rt6->GetActivePath()->path_preference().dependent_ip() == + Ip6Address()); + EXPECT_TRUE(rt6->GetActivePath()->path_preference().ecmp() == false); + + AddServiceInstanceIp("serviceip1", 100, "fd23::1", true); + AddLink("virtual-machine-interface", "vnet1", "instance-ip", "serviceip1"); + client->WaitForIdle(); + EXPECT_TRUE(rt->GetActivePath()->path_preference().dependent_ip() == + Ip4Address::from_string("1.1.1.1")); + EXPECT_TRUE(rt->GetActivePath()->path_preference().ecmp() == false); + EXPECT_TRUE(rt6->GetActivePath()->path_preference().dependent_ip() == + Ip6Address::from_string("fd23::1")); + EXPECT_TRUE(rt6->GetActivePath()->path_preference().ecmp() == true); + + DelLink("virtual-machine-interface", "vnet1", "instance-ip", "serviceip1"); + DelNode("instance-ip", "serviceip1"); + client->WaitForIdle(); + + //cleanup + DelLink("virtual-machine-interface-routing-instance", "ser1", + "routing-instance", "vrf2"); + DelLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet1"); + DelVmPortVrf("ser1"); + client->WaitForIdle(); + WAIT_FOR(100, 1000, + (RouteGet("vrf2", + Ip4Address::from_string(service_vlan_ip), 32)) == NULL); + WAIT_FOR(100, 1000, + (RouteGetV6("vrf2", + Ip6Address::from_string(service_vlan_ip6), 128)) == NULL); + + DeleteVmportEnv(input, 1, true); + client->WaitForIdle(); + + DelVrf("vrf2"); + client->WaitForIdle(); +} + +TEST_F(Ipv6Test, VlanNHServiceIp_2) { + struct PortInfo input[] = { + {"vnet1", 1, "1.1.1.1", "00:00:00:01:01:01", 1, 1}, + }; + string service_vlan_ip("2.2.2.1"); + string service_vlan_ip6("fd12::1"); + + client->Reset(); + CreateVmportEnv(input, 1); + client->WaitForIdle(); + + //Verify that interface is IPv4 active. + EXPECT_TRUE(VmPortActive(input, 0)); + EXPECT_TRUE(RouteFind("vrf1", input[0].addr, 32)); + + // Add service interface-1 + AddVrf("vrf2"); + AddVmPortVrf("ser1", service_vlan_ip, 1, service_vlan_ip6); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "routing-instance", "vrf2"); + AddLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet1"); + client->WaitForIdle(); + + // Validate service vlan route + InetUnicastRouteEntry *rt = RouteGet("vrf2", + Ip4Address::from_string(service_vlan_ip), 32); + EXPECT_TRUE(rt != NULL); + + InetUnicastRouteEntry *rt6 = RouteGetV6("vrf2", + Ip6Address::from_string(service_vlan_ip6), 128); + EXPECT_TRUE(rt6 != NULL); + + AddServiceInstanceIp("serviceip1", 100, "1.1.1.100", false); + AddServiceInstanceIp("serviceip2", 100, "fd11::1", true); + AddLink("virtual-machine-interface", "vnet1", "instance-ip", "serviceip1"); + AddLink("virtual-machine-interface", "vnet1", "instance-ip", "serviceip2"); + client->WaitForIdle(); + EXPECT_TRUE(rt->GetActivePath()->path_preference().dependent_ip() == + Ip4Address::from_string("1.1.1.100")); + EXPECT_TRUE(rt->GetActivePath()->path_preference().ecmp() == false); + EXPECT_TRUE(rt6->GetActivePath()->path_preference().dependent_ip() == + Ip6Address::from_string("fd11::1")); + EXPECT_TRUE(rt6->GetActivePath()->path_preference().ecmp() == true); + + AddServiceInstanceIp("serviceip1", 100, "1.1.1.100", true); + client->WaitForIdle(); + + EXPECT_TRUE(rt->GetActivePath()->path_preference().dependent_ip() == + Ip4Address::from_string("1.1.1.100")); + EXPECT_TRUE(rt->GetActivePath()->path_preference().ecmp() == true); + EXPECT_TRUE(rt6->GetActivePath()->path_preference().dependent_ip() == + Ip6Address::from_string("fd11::1")); + EXPECT_TRUE(rt6->GetActivePath()->path_preference().ecmp() == true); + + DelLink("virtual-machine-interface", "vnet1", "instance-ip", "serviceip1"); + DelNode("instance-ip", "serviceip1"); + client->WaitForIdle(); + + EXPECT_TRUE(rt->GetActivePath()->path_preference().dependent_ip() == + Ip4Address::from_string("1.1.1.1")); + EXPECT_TRUE(rt->GetActivePath()->path_preference().ecmp() == false); + EXPECT_TRUE(rt6->GetActivePath()->path_preference().dependent_ip() == + Ip6Address::from_string("fd11::1")); + EXPECT_TRUE(rt6->GetActivePath()->path_preference().ecmp() == true); + + DelLink("virtual-machine-interface", "vnet1", "instance-ip", "serviceip2"); + DelNode("instance-ip", "serviceip2"); + client->WaitForIdle(); + + EXPECT_TRUE(rt->GetActivePath()->path_preference().dependent_ip() == + Ip4Address::from_string("1.1.1.1")); + EXPECT_TRUE(rt->GetActivePath()->path_preference().ecmp() == false); + EXPECT_TRUE(rt6->GetActivePath()->path_preference().dependent_ip() == + Ip6Address()); + EXPECT_TRUE(rt6->GetActivePath()->path_preference().ecmp() == false); + + //cleanup + DelLink("virtual-machine-interface-routing-instance", "ser1", + "routing-instance", "vrf2"); + DelLink("virtual-machine-interface-routing-instance", "ser1", + "virtual-machine-interface", "vnet1"); + DelVmPortVrf("ser1"); + client->WaitForIdle(); + WAIT_FOR(100, 1000, + (RouteGet("vrf2", + Ip4Address::from_string(service_vlan_ip), 32)) == NULL); + WAIT_FOR(100, 1000, + (RouteGetV6("vrf2", + Ip6Address::from_string(service_vlan_ip6), 128)) == NULL); + + DeleteVmportEnv(input, 1, true); + client->WaitForIdle(); + + DelVrf("vrf2"); + client->WaitForIdle(); +} + + int main(int argc, char **argv) { GETUSERARGS(); diff --git a/src/vnsw/agent/oper/vm_interface.cc b/src/vnsw/agent/oper/vm_interface.cc index 47ab6327f54..293528a02d8 100644 --- a/src/vnsw/agent/oper/vm_interface.cc +++ b/src/vnsw/agent/oper/vm_interface.cc @@ -431,6 +431,10 @@ static void BuildInstanceIp(Agent *agent, VmInterfaceConfigData *data, IpAddress addr = IpAddress::from_string(ip->address(), err); bool is_primary = false; + if (err.value() != 0) { + return; + } + if (ip->secondary() != true && ip->service_instance_ip() != true && ip->service_health_check_ip() != true) { is_primary = true; @@ -464,6 +468,22 @@ static void BuildInstanceIp(Agent *agent, VmInterfaceConfigData *data, } } + if (ip->service_instance_ip()) { + if (addr.is_v4()) { + data->service_ip_ = addr.to_v4(); + data->service_ip_ecmp_ = false; + if (ip->mode() == "active-active") { + data->service_ip_ecmp_ = true; + } + } else if (addr.is_v6()) { + data->service_ip6_ = addr.to_v6(); + data->service_ip_ecmp6_ = false; + if (ip->mode() == "active-active") { + data->service_ip_ecmp6_ = true; + } + } + } + bool ecmp = false; if (ip->mode() == "active-active") { ecmp = true; @@ -1694,7 +1714,8 @@ VmInterfaceConfigData::VmInterfaceConfigData(Agent *agent, IFMapNode *node) : rx_vlan_id_(VmInterface::kInvalidVlanId), tx_vlan_id_(VmInterface::kInvalidVlanId), logical_interface_(nil_uuid()), ecmp_load_balance_(), - service_health_check_ip_() { + service_health_check_ip_(), service_ip_(0), + service_ip_ecmp_(false), service_ip6_(), service_ip_ecmp6_(false){ } VmInterface *VmInterfaceConfigData::OnAdd(const InterfaceTable *table, @@ -1998,6 +2019,22 @@ bool VmInterface::CopyConfig(const InterfaceTable *table, *ecmp_changed = true; } + if (service_ip_ecmp_ != data->service_ip_ecmp_ || + service_ip_ != data->service_ip_) { + service_ip_ecmp_ = data->service_ip_ecmp_; + service_ip_ = data->service_ip_; + *ecmp_changed = true; + ret = true; + } + + if (service_ip_ecmp6_ != data->service_ip_ecmp6_ || + service_ip6_ != data->service_ip6_) { + service_ip_ecmp6_ = data->service_ip_ecmp6_; + service_ip6_ = data->service_ip6_; + *ecmp_changed = true; + ret = true; + } + if (data->device_type_ != VmInterface::DEVICE_TYPE_INVALID && device_type_ != data->device_type_) { device_type_= data->device_type_; @@ -3372,6 +3409,56 @@ void VmInterface::SetPathPreference(PathPreference *pref, bool ecmp, pref->set_vrf(vrf()->GetName()); } +void VmInterface::SetServiceVlanPathPreference(PathPreference *pref, + const IpAddress &service_ip) const { + + bool ecmp_mode = false; + IpAddress dependent_ip; + + //Logic for setting ecmp and tracking IP on Service chain route + //Service vlan route can be active when interface is either + //IPV4 active or IPV6 active, hence we have to consider both + //IPV6 and IPV4 IP + //If Service vlan is for Ipv4 route, then priority is as below + //1> Service IP for v4 + //3> Primary IP for v4 + if (service_ip.is_v4()) { + if (service_ip_ != Ip4Address(0)) { + dependent_ip = service_ip_; + ecmp_mode = service_ip_ecmp_; + } else { + dependent_ip = primary_ip_addr_; + ecmp_mode = ecmp_; + } + } + + //If Service vlan is for Ipv6 route, then priority is as below + //1> Service IP for v6 + //3> Primary IP for v6 + if (service_ip.is_v6()) { + if (service_ip6_ != Ip6Address()) { + dependent_ip = service_ip6_; + ecmp_mode = service_ip_ecmp6_; + } else { + dependent_ip = primary_ip6_addr_; + ecmp_mode = ecmp6_; + } + } + + pref->set_ecmp(ecmp_mode); + if (local_preference_ != INVALID) { + pref->set_static_preference(true); + } + if (local_preference_ == HIGH) { + pref->set_preference(PathPreference::HIGH); + } else { + pref->set_preference(PathPreference::LOW); + } + + pref->set_dependent_ip(dependent_ip); + pref->set_vrf(vrf()->GetName()); +} + void VmInterface::CopyEcmpLoadBalance(EcmpLoadBalance &ecmp_load_balance) { if (ecmp_load_balance_.use_global_vrouter() == false) return ecmp_load_balance.Copy(ecmp_load_balance_); @@ -4328,7 +4415,7 @@ void VmInterface::ServiceVlan::Activate(VmInterface *interface, if (installed_ && force_update == false) return; - interface->ServiceVlanRouteAdd(*this); + interface->ServiceVlanRouteAdd(*this, force_update); installed_ = true; } @@ -4407,7 +4494,8 @@ const VrfEntry* VmInterface::GetServiceVlanVrf(uint16_t vlan_tag) const { return NULL; } -void VmInterface::ServiceVlanRouteAdd(const ServiceVlan &entry) { +void VmInterface::ServiceVlanRouteAdd(const ServiceVlan &entry, + bool force_update) { if (vrf_.get() == NULL || vn_.get() == NULL) { return; @@ -4430,9 +4518,10 @@ void VmInterface::ServiceVlanRouteAdd(const ServiceVlan &entry) { 0, entry.smac_, vn()->GetName()); VnListType vn_list; vn_list.insert(vn()->GetName()); - if (!entry.v4_rt_installed_ && !entry.addr_.is_unspecified()) { + if (force_update || + (!entry.v4_rt_installed_ && !entry.addr_.is_unspecified())) { PathPreference path_preference; - SetPathPreference(&path_preference, ecmp(), primary_ip_addr()); + SetServiceVlanPathPreference(&path_preference, entry.addr_); InetUnicastAgentRouteTable::AddVlanNHRoute (peer_.get(), entry.vrf_->GetName(), entry.addr_, 32, @@ -4440,9 +4529,10 @@ void VmInterface::ServiceVlanRouteAdd(const ServiceVlan &entry) { path_preference); entry.v4_rt_installed_ = true; } - if (!entry.v6_rt_installed_ && !entry.addr6_.is_unspecified()) { + if ((!entry.v6_rt_installed_ && !entry.addr6_.is_unspecified()) || + force_update) { PathPreference path_preference; - SetPathPreference(&path_preference, ecmp6(), primary_ip6_addr()); + SetServiceVlanPathPreference(&path_preference, entry.addr6_); InetUnicastAgentRouteTable::AddVlanNHRoute (peer_.get(), entry.vrf_->GetName(), entry.addr6_, 128, diff --git a/src/vnsw/agent/oper/vm_interface.h b/src/vnsw/agent/oper/vm_interface.h index 5965f6dbbec..0344cfddb60 100644 --- a/src/vnsw/agent/oper/vm_interface.h +++ b/src/vnsw/agent/oper/vm_interface.h @@ -481,6 +481,10 @@ class VmInterface : public Interface { const Interface *parent() const { return parent_.get(); } bool ecmp() const { return ecmp_;} bool ecmp6() const { return ecmp6_;} + Ip4Address service_ip() { return service_ip_;} + bool service_ip_ecmp() const { return service_ip_ecmp_;} + Ip6Address service_ip6() { return service_ip6_;} + bool service_ip_ecmp6() const { return service_ip_ecmp6_;} const OperDhcpOptions &oper_dhcp_options() const { return oper_dhcp_options_; } uint8_t configurer() const {return configurer_;} bool IsConfigurerSet(VmInterface::Configurer type); @@ -552,6 +556,8 @@ class VmInterface : public Interface { void SetPathPreference(PathPreference *pref, bool ecmp, const IpAddress &dependent_ip) const; + void SetServiceVlanPathPreference(PathPreference *pref, + const IpAddress &service_ip) const; void CopySgIdList(SecurityGroupList *sg_id_list) const; bool NeedMplsLabel() const; bool IsVxlanMode() const; @@ -658,7 +664,7 @@ class VmInterface : public Interface { uint32_t plen, const std::string &dest_vn, bool policy); void ServiceVlanAdd(ServiceVlan &entry); void ServiceVlanDel(ServiceVlan &entry); - void ServiceVlanRouteAdd(const ServiceVlan &entry); + void ServiceVlanRouteAdd(const ServiceVlan &entry, bool force_update); void ServiceVlanRouteDel(const ServiceVlan &entry); bool OnResyncFloatingIp(VmInterfaceConfigData *data, bool new_ipv4_active); @@ -825,6 +831,10 @@ class VmInterface : public Interface { bool mac_set_; bool ecmp_; bool ecmp6_; + Ip4Address service_ip_; + bool service_ip_ecmp_; + Ip6Address service_ip6_; + bool service_ip_ecmp6_; // disable-policy configuration on VMI. When this is configured, policy // dependent features like flows, floating-IP and SG will not work on this // VMI. However metadata-services will work because metadata route will @@ -1032,6 +1042,10 @@ struct VmInterfaceConfigData : public VmInterfaceData { boost::uuids::uuid logical_interface_; VmiEcmpLoadBalance ecmp_load_balance_; IpAddress service_health_check_ip_; + Ip4Address service_ip_; + bool service_ip_ecmp_; + Ip6Address service_ip6_; + bool service_ip_ecmp6_; }; // Definition for structures when request queued from Nova diff --git a/src/vnsw/agent/test/test_cmn_util.h b/src/vnsw/agent/test/test_cmn_util.h index ddee52307f8..c0dc79529b0 100644 --- a/src/vnsw/agent/test/test_cmn_util.h +++ b/src/vnsw/agent/test/test_cmn_util.h @@ -369,6 +369,7 @@ void DelVmPortVrf(const char *name); uint32_t PathCount(const string vrf_name, const Ip4Address &addr, int plen); bool VlanNhFind(int id, uint16_t tag); void AddInstanceIp(const char *name, int id, const char* addr); +void AddServiceInstanceIp(const char *name, int id, const char* addr, bool ecmp); void AddSubnetType(const char *name, int id, const char* addr, uint8_t); void AddActiveActiveInstanceIp(const char *name, int id, const char* addr); void AddHealthCheckServiceInstanceIp(const char *name, int id, diff --git a/src/vnsw/agent/test/test_nh.cc b/src/vnsw/agent/test/test_nh.cc index 40f6e174806..a5af32269b8 100644 --- a/src/vnsw/agent/test/test_nh.cc +++ b/src/vnsw/agent/test/test_nh.cc @@ -2495,6 +2495,49 @@ TEST_F(CfgTest, EcmpNH_19) { EXPECT_FALSE(RouteFind("vrf1", ip, 32)); } +TEST_F(CfgTest, EcmpNH_20) { + //Add interface + struct PortInfo input[] = { + {"vnet1", 1, "1.1.1.3", "00:00:00:01:01:01", 1, 1}, + {"vnet2", 2, "1.1.1.4", "00:00:00:01:01:01", 1, 2}, + }; + CreateVmportWithEcmp(input, 2); + client->WaitForIdle(); + + AddServiceInstanceIp("instance1", 100, "1.1.1.10", false); + AddLink("virtual-machine-interface", "vnet1", "instance-ip", "instance1"); + AddServiceInstanceIp("instance2", 101, "1.1.1.10", false); + AddLink("virtual-machine-interface", "vnet2", "instance-ip", "instance2"); + client->WaitForIdle(); + + Ip4Address ip = Ip4Address::from_string("1.1.1.10"); + InetUnicastRouteEntry *rt = RouteGet("vrf1", ip, 32); + EXPECT_TRUE(rt != NULL); + EXPECT_TRUE(rt->GetActiveNextHop()->GetType() == NextHop::INTERFACE); + + AddServiceInstanceIp("instance1", 100, "1.1.1.10", true); + client->WaitForIdle(); + EXPECT_TRUE(rt->GetActiveNextHop()->GetType() == NextHop::INTERFACE); + + AddServiceInstanceIp("instance2", 101, "1.1.1.10", true); + client->WaitForIdle(); + EXPECT_TRUE(rt->GetActiveNextHop()->GetType() == NextHop::COMPOSITE); + + AddServiceInstanceIp("instance2", 101, "1.1.1.10", false); + client->WaitForIdle(); + EXPECT_TRUE(rt->GetActiveNextHop()->GetType() == NextHop::INTERFACE); + + DelLink("virtual-machine-interface", "vnet1", "instance-ip", "instance1"); + DelLink("virtual-machine-interface", "vnet2", "instance-ip", "instance2"); + DelNode("instance-ip", "instance1"); + DelNode("instance-ip", "instance2"); + client->WaitForIdle(); + DeleteVmportEnv(input, 2, true); + WAIT_FOR(100, 1000, (VrfFind("vrf1") == false)); + client->WaitForIdle(); + EXPECT_FALSE(RouteFind("vrf1", ip, 32)); +} + int main(int argc, char **argv) { GETUSERARGS(); client = TestInit(init_file, ksync_init); diff --git a/src/vnsw/agent/test/test_util.cc b/src/vnsw/agent/test/test_util.cc index 043879523e9..d9fdf4adc97 100644 --- a/src/vnsw/agent/test/test_util.cc +++ b/src/vnsw/agent/test/test_util.cc @@ -2078,6 +2078,22 @@ void AddHealthCheckServiceInstanceIp(const char *name, int id, AddNode("instance-ip", name, id, buf); } +void AddServiceInstanceIp(const char *name, int id, const char *addr, bool ecmp) { + char buf[256]; + char mode[256]; + + if (ecmp) { + sprintf(mode, "active-active"); + } else { + sprintf(mode, "active-backup"); + } + + sprintf(buf, "%s" + "true" + "%s", addr, mode); + AddNode("instance-ip", name, id, buf); +} + void DelInstanceIp(const char *name) { DelNode("instance-ip", name); }