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);
}