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 344d75e63dd..58af5028925 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); }