From 17cd464526ca962932f6ea32f6ef1ffe944a3aee Mon Sep 17 00:00:00 2001 From: Naveen N Date: Wed, 20 Apr 2016 10:37:41 +0530 Subject: [PATCH] * Handle RPF check failure for floating-ip ecmp. Change-Id: Icf21c3a40783c9b4ff22e5340ae6599f2c293551 Closes-bug:#1555000 --- src/vnsw/agent/pkt/flow_table.cc | 5 - src/vnsw/agent/pkt/test/SConscript | 7 + src/vnsw/agent/pkt/test/test_ecmp_local.cc | 148 ++++++++ src/vnsw/agent/pkt/test/test_ecmp_mx.cc | 370 +++++++++++++++++++ src/vnsw/agent/pkt/test/test_fip_dst_ecmp.cc | 192 ++++++++++ src/vnsw/agent/pkt/test/test_fip_src_ecmp.cc | 327 ++++++++++++++++ 6 files changed, 1044 insertions(+), 5 deletions(-) create mode 100644 src/vnsw/agent/pkt/test/test_ecmp_local.cc create mode 100644 src/vnsw/agent/pkt/test/test_ecmp_mx.cc create mode 100644 src/vnsw/agent/pkt/test/test_fip_dst_ecmp.cc create mode 100644 src/vnsw/agent/pkt/test/test_fip_src_ecmp.cc diff --git a/src/vnsw/agent/pkt/flow_table.cc b/src/vnsw/agent/pkt/flow_table.cc index 53d58be2d5d..a45675c5f0c 100644 --- a/src/vnsw/agent/pkt/flow_table.cc +++ b/src/vnsw/agent/pkt/flow_table.cc @@ -3413,11 +3413,6 @@ void FlowTable::SetComponentIndex(FlowEntry *fe, const NextHopKey *nh_key, } const NextHop *nh = rt->GetActiveNextHop(); - if (nh->GetType() != NextHop::COMPOSITE) { - rflow->set_ecmp_rpf_nh(this, 0); - return; - } - //Set composite NH based on local mpls label flow if (mpls_path_select) { nh = rt->GetLocalNextHop(); diff --git a/src/vnsw/agent/pkt/test/SConscript b/src/vnsw/agent/pkt/test/SConscript index 94e1d9647e4..2c5351a9722 100644 --- a/src/vnsw/agent/pkt/test/SConscript +++ b/src/vnsw/agent/pkt/test/SConscript @@ -34,6 +34,13 @@ test_sg_flow = AgentEnv.MakeTestCmd(env, 'test_sg_flow', pkt_flaky_test_suite) test_sg_tcp_flow = AgentEnv.MakeTestCmd(env, 'test_sg_tcp_flow', pkt_flaky_test_suite) test_vrf_assign_acl = AgentEnv.MakeTestCmd(env, 'test_vrf_assign_acl', pkt_flaky_test_suite) +test_ecmp_mx = AgentEnv.MakeTestCmd(env, 'test_ecmp_mx', pkt_test_suite) +test_fip_dst_ecmp = AgentEnv.MakeTestCmd(env, 'test_fip_dst_ecmp', + pkt_test_suite) +test_fip_src_ecmp = AgentEnv.MakeTestCmd(env, 'test_fip_src_ecmp', + pkt_test_suite) +test_ecmp_local = AgentEnv.MakeTestCmd(env, 'test_ecmp_local', + pkt_test_suite) test_xml_packet_ut = AgentEnv.MakeTestCmdSrc(env, 'test_xml_packet_ut', [ diff --git a/src/vnsw/agent/pkt/test/test_ecmp_local.cc b/src/vnsw/agent/pkt/test/test_ecmp_local.cc new file mode 100644 index 00000000000..274c4a2eb86 --- /dev/null +++ b/src/vnsw/agent/pkt/test/test_ecmp_local.cc @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved. + */ +#include "base/os.h" +#include "test/test_cmn_util.h" +#include "test_pkt_util.h" +#include "pkt/flow_proto.h" + +#define AGE_TIME 10*1000 + +void RouterIdDepInit(Agent *agent) { +} + +struct PortInfo input1[] = { + {"vnet1", 1, "1.1.1.1", "00:00:00:01:01:01", 1, 1}, + {"vnet2", 2, "1.1.1.1", "00:00:00:01:01:01", 1, 2} +}; + +class EcmpTest : public ::testing::Test { + virtual void SetUp() { + agent_ = Agent::GetInstance(); + + boost::system::error_code ec; + bgp_peer = CreateBgpPeer(Ip4Address::from_string("0.0.0.1", ec), + "xmpp channel"); + client->WaitForIdle(); + + flow_proto_ = agent_->pkt()->get_flow_proto(); + CreateVmportWithEcmp(input1, 2); + AddVn("vn2", 2); + AddVrf("vrf2"); + client->WaitForIdle(); + strcpy(router_id, agent_->router_id().to_string().c_str()); + strcpy(MX_0, "100.1.1.1"); + strcpy(MX_1, "100.1.1.2"); + strcpy(MX_2, "100.1.1.3"); + strcpy(MX_3, "100.1.1.4"); + + const VmInterface *vmi = static_cast(VmPortGet(1)); + vm1_label = vmi->label(); + vmi = static_cast(VmPortGet(2)); + vm2_label = vmi->label(); + eth_intf_id = EthInterfaceGet("vnet0")->id(); + } + + virtual void TearDown() { + DeleteVmportEnv(input1, 2, true); + DelVn("vn2"); + DelVrf("vrf2"); + client->WaitForIdle(); + DeleteBgpPeer(bgp_peer); + client->WaitForIdle(); + EXPECT_FALSE(VrfFind("vrf1", true)); + EXPECT_FALSE(VrfFind("vrf2", true)); + WAIT_FOR(1000, 1000, (flow_proto_->FlowCount() == 0)); + client->WaitForIdle(); + } +public: + void AddRemoteEcmpRoute(const string vrf_name, const string ip, + uint32_t plen, const string vn, int count, bool reverse = false, + bool same_label = false) { + //If there is a local route, include that always + Ip4Address vm_ip = Ip4Address::from_string(ip); + + ComponentNHKeyList comp_nh_list; + int remote_server_ip = 0x64010101; + int label = 16; + SecurityGroupList sg_id_list; + + for(int i = 0; i < count; i++) { + ComponentNHKeyPtr comp_nh(new ComponentNHKey( + label, Agent::GetInstance()->fabric_vrf_name(), + Agent::GetInstance()->router_id(), + Ip4Address(remote_server_ip++), + false, TunnelType::AllType())); + comp_nh_list.push_back(comp_nh); + if (!same_label) { + label++; + } + } + if (reverse) { + std::reverse(comp_nh_list.begin(), comp_nh_list.end()); + } + + EcmpTunnelRouteAdd(bgp_peer, vrf_name, vm_ip, plen, + comp_nh_list, -1, vn, sg_id_list, + PathPreference()); + } + + FlowProto *get_flow_proto() const { return flow_proto_; } + Agent *agent_; + Peer *bgp_peer; + FlowProto *flow_proto_; + AgentXmppChannel *channel; + char router_id[80]; + char MX_0[80]; + char MX_1[80]; + char MX_2[80]; + char MX_3[80]; + int vm1_label; + int vm2_label; + int agg_label; + int eth_intf_id; +}; + +//Send packet from ECMP VM to ECMP MX +//Verify component index is set and correspnding +//rpf nexthop +TEST_F(EcmpTest, EcmpTest_1) { + AddRemoteEcmpRoute("vrf1", "0.0.0.0", 0, "vn1", 4); + AddRemoteEcmpRoute("vrf1", "1.1.1.1", 32, "vn1", 4); + + TxIpPacket(VmPortGetId(1), "1.1.1.1", "2.1.1.1", 1); + client->WaitForIdle(); + + AgentRoute *rt = RouteGet("vrf1", Ip4Address::from_string("0.0.0.0"), 0); + InetUnicastRouteEntry *src_rt = static_cast( + RouteGet("vrf1", Ip4Address::from_string("1.1.1.1"), 32)); + + FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), + "1.1.1.1", "2.1.1.1", 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().nh.get() == src_rt->GetLocalNextHop()); + + //Reverse flow is no ECMP + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + + DeleteRoute("vrf1", "0.0.0.0", 0, bgp_peer); + client->WaitForIdle(); + sleep(1); + client->WaitForIdle(); + WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); +} + +int main(int argc, char *argv[]) { + GETUSERARGS(); + client = TestInit(init_file, ksync_init, true, true, true, 100*1000); + int ret = RUN_ALL_TESTS(); + client->WaitForIdle(); + TestShutdown(); + delete client; + return ret; +} diff --git a/src/vnsw/agent/pkt/test/test_ecmp_mx.cc b/src/vnsw/agent/pkt/test/test_ecmp_mx.cc new file mode 100644 index 00000000000..6d877b4abcc --- /dev/null +++ b/src/vnsw/agent/pkt/test/test_ecmp_mx.cc @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved. + */ + +#include "base/os.h" +#include "test/test_cmn_util.h" +#include "test_pkt_util.h" +#include "pkt/flow_proto.h" + +#define AGE_TIME 10*1000 + +void RouterIdDepInit(Agent *agent) { +} + +struct PortInfo input1[] = { + {"vnet1", 1, "1.1.1.1", "00:00:00:01:01:01", 1, 1}, +}; + +class EcmpTest : public ::testing::Test { + virtual void SetUp() { + agent_ = Agent::GetInstance(); + + boost::system::error_code ec; + bgp_peer = CreateBgpPeer(Ip4Address::from_string("0.0.0.1", ec), + "xmpp channel"); + client->WaitForIdle(); + + flow_proto_ = agent_->pkt()->get_flow_proto(); + CreateVmportWithEcmp(input1, 1); + AddVn("vn2", 2); + AddVrf("vrf2"); + client->WaitForIdle(); + strcpy(router_id, agent_->router_id().to_string().c_str()); + strcpy(MX_0, "100.1.1.1"); + strcpy(MX_1, "100.1.1.2"); + strcpy(MX_2, "100.1.1.3"); + strcpy(MX_3, "100.1.1.4"); + + const VmInterface *vmi = static_cast(VmPortGet(1)); + vm1_label = vmi->label(); + eth_intf_id = EthInterfaceGet("vnet0")->id(); + } + + virtual void TearDown() { + DeleteVmportEnv(input1, 1, true); + DelVn("vn2"); + DelVrf("vrf2"); + client->WaitForIdle(); + DeleteBgpPeer(bgp_peer); + client->WaitForIdle(); + EXPECT_FALSE(VrfFind("vrf1", true)); + EXPECT_FALSE(VrfFind("vrf2", true)); + WAIT_FOR(1000, 1000, (flow_proto_->FlowCount() == 0)); + client->WaitForIdle(); + } +public: + void AddRemoteEcmpRoute(const string vrf_name, const string ip, + uint32_t plen, const string vn, int count, bool reverse = false, + bool same_label = false) { + //If there is a local route, include that always + Ip4Address vm_ip = Ip4Address::from_string(ip); + + ComponentNHKeyList comp_nh_list; + int remote_server_ip = 0x64010101; + int label = 16; + SecurityGroupList sg_id_list; + + for(int i = 0; i < count; i++) { + ComponentNHKeyPtr comp_nh(new ComponentNHKey( + label, Agent::GetInstance()->fabric_vrf_name(), + Agent::GetInstance()->router_id(), + Ip4Address(remote_server_ip++), + false, TunnelType::AllType())); + comp_nh_list.push_back(comp_nh); + if (!same_label) { + label++; + } + } + if (reverse) { + std::reverse(comp_nh_list.begin(), comp_nh_list.end()); + } + + EcmpTunnelRouteAdd(bgp_peer, vrf_name, vm_ip, plen, + comp_nh_list, -1, vn, sg_id_list, + PathPreference()); + } + + FlowProto *get_flow_proto() const { return flow_proto_; } + Agent *agent_; + Peer *bgp_peer; + FlowProto *flow_proto_; + AgentXmppChannel *channel; + char router_id[80]; + char MX_0[80]; + char MX_1[80]; + char MX_2[80]; + char MX_3[80]; + int vm1_label; + int eth_intf_id; +}; + +//Send packet from VM to ECMP MX +//Verify component index is set and correspnding +//rpf nexthop +TEST_F(EcmpTest, EcmpTest_1) { + AddRemoteEcmpRoute("vrf1", "0.0.0.0", 0, "vn1", 4); + + TxIpPacket(VmPortGetId(1), "1.1.1.1", "2.1.1.1", 1); + client->WaitForIdle(); + + AgentRoute *rt = RouteGet("vrf1", Ip4Address::from_string("0.0.0.0"), 0); + + FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), + "1.1.1.1", "2.1.1.1", 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + + //Reverse flow is no ECMP + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + + DeleteRoute("vrf1", "0.0.0.0", 0, bgp_peer); + client->WaitForIdle(); + WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); +} + +//Send packet from ECMP MX to VM +//Verify: +// Forward flow is ecmp +// Reverse flow ecmp index is not set +// Reverse flow rpf next is composite NH +// and the index matches originating MX +TEST_F(EcmpTest, EcmpTest_2) { + AddRemoteEcmpRoute("vrf1", "0.0.0.0", 0, "vn1", 4); + + TxIpMplsPacket(eth_intf_id, MX_2, router_id, vm1_label, + "8.8.8.8", "1.1.1.1", 1, 10); + client->WaitForIdle(); + + AgentRoute *rt = RouteGet("vrf1", Ip4Address::from_string("0.0.0.0"), 0); + FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), + "1.1.1.1", "8.8.8.8", 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx == 2); + + //Reverse flow is no ECMP + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + + DeleteRoute("vrf1", "0.0.0.0", 0, bgp_peer); + client->WaitForIdle(); + WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); +} + +//Send packet from MX3 to VM +//Verify that index are set fine +TEST_F(EcmpTest, EcmpTest_3) { + AddRemoteEcmpRoute("vrf1", "0.0.0.0", 0, "vn1", 4); + + TxIpMplsPacket(eth_intf_id, MX_3, router_id, vm1_label, + "8.8.8.8", "1.1.1.1", 1, 10); + client->WaitForIdle(); + + AgentRoute *rt = RouteGet("vrf1", Ip4Address::from_string("0.0.0.0"), 0); + FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), + "1.1.1.1", "8.8.8.8", 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx == 3); + + //Reverse flow is no ECMP + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + + DeleteRoute("vrf1", "0.0.0.0", 0, bgp_peer); + client->WaitForIdle(); + WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); +} + +//Send packet from MX1 to VM +//Trap a ecmp resolve packet from MX2 to VM +//verify that component index gets update +TEST_F(EcmpTest, EcmpTest_4) { + AddRemoteEcmpRoute("vrf1", "0.0.0.0", 0, "vn1", 4); + + TxIpMplsPacket(eth_intf_id, MX_0, router_id, vm1_label, + "8.8.8.8", "1.1.1.1", 1, 10); + client->WaitForIdle(); + + AgentRoute *rt = RouteGet("vrf1", Ip4Address::from_string("0.0.0.0"), 0); + FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), + "1.1.1.1", "8.8.8.8", 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx == 0); + + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + + TxIpMplsPacket(eth_intf_id, MX_2, router_id, vm1_label, + "8.8.8.8", "1.1.1.1", 1, 10); + client->WaitForIdle(); + EXPECT_TRUE(entry->data().component_nh_idx == 2); + EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + + DeleteRoute("vrf1", "0.0.0.0", 0, bgp_peer); + client->WaitForIdle(); + WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); +} + +//Send packet from MX to VM +// Fwd flow has no vrf assign ACL +// Reverese flow has vrf assign ACL +// hence component index has to set based on vrf2 +TEST_F(EcmpTest, EcmpTest_5) { + AddRemoteEcmpRoute("vrf1", "0.0.0.0", 0, "vn2", 4); + //Reverse all the nexthop in vrf2 + AddRemoteEcmpRoute("vrf2", "0.0.0.0", 0, "vn2", 4, true); + + AddVrfAssignNetworkAcl("Acl", 10, "vn1", "vn2", "pass", "vrf2"); + AddLink("virtual-network", "vn1", "access-control-list", "Acl"); + client->WaitForIdle(); + + TxIpMplsPacket(eth_intf_id, MX_3, router_id, vm1_label, + "8.8.8.8", "1.1.1.1", 1, 10); + client->WaitForIdle(); + + AgentRoute *rt = RouteGet("vrf2", Ip4Address::from_string("0.0.0.0"), 0); + FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), + "1.1.1.1", "8.8.8.8", 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(entry != NULL); + //MX 3 would be placed at index 0 because of nexthop + //order reversal + EXPECT_TRUE(entry->data().component_nh_idx == 0); + + //Reverse flow is no ECMP + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + + DeleteRoute("vrf1", "0.0.0.0", 0, bgp_peer); + DeleteRoute("vrf2", "0.0.0.0", 0, bgp_peer); + DelLink("virtual-network", "vn1", "access-control-list", "Acl"); + DelAcl("Acl"); + client->WaitForIdle(); + WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); +} + +//Floating IP traffic from VM going to ECMP MX +TEST_F(EcmpTest, EcmpTest_6) { + //Setup + //Add IP 2.1.1.1 as floating IP to 1.1.1.1 + //Make address 8.8.8.8 reachable on 4 MX + //Send traffic from MX3 to FIP + //Verify RPF nh and component index + AddVn("fip", 3); + AddVrf("fip:fip"); + AddLink("virtual-network", "fip", "routing-instance", "fip:fip"); + AddFloatingIpPool("fip-pool1", 1); + AddFloatingIp("fip1", 1, "2.1.1.1"); + AddLink("floating-ip", "fip1", "floating-ip-pool", "fip-pool1"); + AddLink("floating-ip-pool", "fip-pool1", "virtual-network", "fip"); + AddLink("virtual-machine-interface", "vnet1", "floating-ip", "fip1"); + client->WaitForIdle(); + AddRemoteEcmpRoute("fip:fip", "0.0.0.0", 0, "fip", 4); + client->WaitForIdle(); + + TxIpMplsPacket(eth_intf_id, MX_3, router_id, vm1_label, + "8.8.8.8", "2.1.1.1", 1, 10); + client->WaitForIdle(); + + AgentRoute *rt = RouteGet("fip:fip", Ip4Address::from_string("0.0.0.0"), 0); + FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), + "1.1.1.1", "8.8.8.8", 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx == 3); + + //Reverse flow is no ECMP + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + + //Clean up + DeleteRoute("fip:fip", "0.0.0.0", 0, bgp_peer); + DelLink("virtual-machine-interface", "vnet1", "floating-ip", "fip1"); + DelLink("floating-ip-pool", "fip-pool1", "virtual-network", "fip"); + DelNode("floating-ip", "fip1"); + DelNode("floating-ip-pool", "fip-pool1"); + client->WaitForIdle(); + DelLink("virtual-network", "fip", "routing-instance", "fip:fip"); + DelVrf("fip:fip"); + DelVn("fip"); + client->WaitForIdle(); + WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); +} + +//Floating IP traffic from VM going to ECMP MX with +//VRF translation to vrf2 +TEST_F(EcmpTest, EcmpTest_7) { + //Setup + //Add IP 2.1.1.1 as floating IP to 1.1.1.1 + //Make address 8.8.8.8 reachable on 4 MX + //Send traffic from MX3 to FIP with vrf translation + //Verify RPF nh and component index + AddVn("fip", 3); + AddVrf("fip:fip"); + AddLink("virtual-network", "fip", "routing-instance", "fip:fip"); + AddFloatingIpPool("fip-pool1", 1); + AddFloatingIp("fip1", 1, "2.1.1.1"); + AddLink("floating-ip", "fip1", "floating-ip-pool", "fip-pool1"); + AddLink("floating-ip-pool", "fip-pool1", "virtual-network", "fip"); + AddLink("virtual-machine-interface", "vnet1", "floating-ip", "fip1"); + client->WaitForIdle(); + AddRemoteEcmpRoute("fip:fip", "0.0.0.0", 0, "fip", 4); + //Add the routes in vrf2 in reverese order + AddRemoteEcmpRoute("vrf2", "0.0.0.0", 0, "fip", 4, true); + client->WaitForIdle(); + + AddVrfAssignNetworkAcl("Acl", 10, "fip", "fip", "pass", "vrf2"); + AddLink("virtual-network", "fip", "access-control-list", "Acl"); + client->WaitForIdle(); + + TxIpMplsPacket(eth_intf_id, MX_3, router_id, vm1_label, + "8.8.8.8", "2.1.1.1", 1, 10); + client->WaitForIdle(); + + AgentRoute *rt = RouteGet("vrf2", Ip4Address::from_string("0.0.0.0"), 0); + FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), + "1.1.1.1", "8.8.8.8", 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx == 0); + + //Reverse flow is no ECMP + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + + //Clean up + DeleteRoute("fip:fip", "0.0.0.0", 0, bgp_peer); + DelLink("virtual-machine-interface", "vnet1", "floating-ip", "fip1"); + DelLink("floating-ip-pool", "fip-pool1", "virtual-network", "fip"); + DelNode("floating-ip", "fip1"); + DelNode("floating-ip-pool", "fip-pool1"); + client->WaitForIdle(); + DelLink("virtual-network", "fip", "routing-instance", "fip:fip"); + DelVrf("fip:fip"); + DelVn("fip"); + client->WaitForIdle(); + WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); +} + +int main(int argc, char *argv[]) { + GETUSERARGS(); + client = TestInit(init_file, ksync_init, true, true, true, 100*1000); + int ret = RUN_ALL_TESTS(); + client->WaitForIdle(); + TestShutdown(); + delete client; + return ret; +} diff --git a/src/vnsw/agent/pkt/test/test_fip_dst_ecmp.cc b/src/vnsw/agent/pkt/test/test_fip_dst_ecmp.cc new file mode 100644 index 00000000000..00cfd63473e --- /dev/null +++ b/src/vnsw/agent/pkt/test/test_fip_dst_ecmp.cc @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved. + */ + +#include "base/os.h" +#include "test/test_cmn_util.h" +#include "test_pkt_util.h" +#include "pkt/flow_proto.h" + +#define AGE_TIME 10*1000 +#define VN2 "default-project:vn2" +#define VRF2 "default-project:vn2:vn2" + +void RouterIdDepInit(Agent *agent) { +} + +struct PortInfo input1[] = { + {"vnet1", 1, "1.1.1.1", "00:00:00:01:01:01", 1, 1}, +}; + +class FipEcmpTest : public ::testing::Test { + virtual void SetUp() { + agent_ = Agent::GetInstance(); + + boost::system::error_code ec; + bgp_peer = CreateBgpPeer(Ip4Address::from_string("0.0.0.1", ec), + "xmpp channel"); + client->WaitForIdle(); + + flow_proto_ = agent_->pkt()->get_flow_proto(); + CreateVmportWithEcmp(input1, 1); + AddVn(VN2, 2); + AddVrf(VRF2); + AddLink("virtual-network", VN2, "routing-instance", + VRF2); + client->WaitForIdle(); + //Attach floating-ip + //Add floating IP for vnet1 + AddFloatingIpPool("fip-pool1", 1); + AddFloatingIp("fip1", 1, "2.1.1.1", "1.1.1.1"); + AddLink("floating-ip", "fip1", "floating-ip-pool", "fip-pool1"); + AddLink("floating-ip-pool", "fip-pool1", "virtual-network", + VN2); + client->WaitForIdle(); + AddLink("virtual-machine-interface", "vnet1", "floating-ip", "fip1"); + client->WaitForIdle(); + + strcpy(router_id, agent_->router_id().to_string().c_str()); + strcpy(MX_0, "100.1.1.1"); + strcpy(MX_1, "100.1.1.2"); + strcpy(MX_2, "100.1.1.3"); + strcpy(MX_3, "100.1.1.4"); + + const VmInterface *vmi = static_cast(VmPortGet(1)); + vm1_label = vmi->label(); + eth_intf_id = EthInterfaceGet("vnet0")->id(); + } + + virtual void TearDown() { + DelLink("virtual-network", VN2, "routing-instance", + VRF2); + DelLink("floating-ip", "fip1", "floating-ip-pool", "fip-pool1"); + DelLink("floating-ip-pool", "fip-pool1", + "virtual-network", VN2); + DelLink("virtual-machine-interface", "vnet1", "floating-ip", "fip1"); + DelFloatingIp("fip1"); + DelFloatingIpPool("fip-pool1"); + client->WaitForIdle(); + + DeleteVmportEnv(input1, 1, true); + DelVn(VN2); + DelVrf(VRF2); + client->WaitForIdle(); + DeleteBgpPeer(bgp_peer); + client->WaitForIdle(); + EXPECT_FALSE(VrfFind("vrf1", true)); + EXPECT_FALSE(VrfFind("vrf2", true)); + WAIT_FOR(1000, 1000, (flow_proto_->FlowCount() == 0)); + client->WaitForIdle(); + } +public: + void AddRemoteEcmpRoute(const string vrf_name, const string ip, + uint32_t plen, const string vn, int count, bool reverse = false, + bool same_label = false) { + //If there is a local route, include that always + Ip4Address vm_ip = Ip4Address::from_string(ip); + + ComponentNHKeyList comp_nh_list; + int remote_server_ip = 0x64010101; + int label = 16; + SecurityGroupList sg_id_list; + + for(int i = 0; i < count; i++) { + ComponentNHKeyPtr comp_nh(new ComponentNHKey( + label, Agent::GetInstance()->fabric_vrf_name(), + Agent::GetInstance()->router_id(), + Ip4Address(remote_server_ip++), + false, TunnelType::AllType())); + comp_nh_list.push_back(comp_nh); + if (!same_label) { + label++; + } + } + if (reverse) { + std::reverse(comp_nh_list.begin(), comp_nh_list.end()); + } + + EcmpTunnelRouteAdd(bgp_peer, vrf_name, vm_ip, plen, + comp_nh_list, -1, vn, sg_id_list, + PathPreference()); + } + + FlowProto *get_flow_proto() const { return flow_proto_; } + Agent *agent_; + Peer *bgp_peer; + FlowProto *flow_proto_; + AgentXmppChannel *channel; + char router_id[80]; + char MX_0[80]; + char MX_1[80]; + char MX_2[80]; + char MX_3[80]; + int vm1_label; + int eth_intf_id; +}; + +//Packet from VM to destination ECMP +TEST_F(FipEcmpTest, Test_1) { + AddRemoteEcmpRoute(VRF2, "0.0.0.0", 0, VN2, 4); + + TxIpPacket(VmPortGetId(1), "1.1.1.1", "8.8.8.8", 1); + client->WaitForIdle(); + + FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), + "1.1.1.1", "8.8.8.8", 1, 0, 0, + GetFlowKeyNH(1)); + + AgentRoute *rt = RouteGet(VRF2, Ip4Address::from_string("2.1.1.1"), 32); + + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().nh.get() == rt->GetActiveNextHop()); + + + rt = RouteGet(VRF2, Ip4Address::from_string("0.0.0.0"), 0); + //Reverse flow is no ECMP + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + + DeleteRoute(VRF2, "0.0.0.0", 0, bgp_peer); + client->WaitForIdle(); + WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); +} + +//Packet from external ECMP source to fip +TEST_F(FipEcmpTest, Test_2) { + AddRemoteEcmpRoute(VRF2, "0.0.0.0", 0, VN2, 4); + + TxIpMplsPacket(eth_intf_id, MX_2, router_id, vm1_label, + "8.8.8.8", "2.1.1.1", 1, 10); + client->WaitForIdle(); + + AgentRoute *rt = RouteGet("vrf1", Ip4Address::from_string("1.1.1.1"), 32); + FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), + "1.1.1.1", "8.8.8.8", 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx == 2); + EXPECT_TRUE(entry->data().nh.get() == rt->GetActiveNextHop()); + + rt = RouteGet(VRF2, Ip4Address::from_string("0.0.0.0"), 0); + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + + DeleteRoute(VRF2, "0.0.0.0", 0, bgp_peer); + client->WaitForIdle(); + WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); +} + +int main(int argc, char *argv[]) { + GETUSERARGS(); + client = TestInit(init_file, ksync_init, true, true, true, 100*1000); + int ret = RUN_ALL_TESTS(); + client->WaitForIdle(); + TestShutdown(); + delete client; + return ret; +} diff --git a/src/vnsw/agent/pkt/test/test_fip_src_ecmp.cc b/src/vnsw/agent/pkt/test/test_fip_src_ecmp.cc new file mode 100644 index 00000000000..ab7b617dfd9 --- /dev/null +++ b/src/vnsw/agent/pkt/test/test_fip_src_ecmp.cc @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved. + */ + +#include "base/os.h" +#include "test/test_cmn_util.h" +#include "test_pkt_util.h" +#include "pkt/flow_proto.h" + +#define AGE_TIME 10*1000 +#define VN2 "default-project:vn2" +#define VRF2 "default-project:vn2:vn2" + +void RouterIdDepInit(Agent *agent) { +} + +struct PortInfo input1[] = { + {"vnet1", 1, "1.1.1.1", "00:00:00:01:01:01", 1, 1}, + {"vnet2", 2, "1.1.1.2", "00:00:00:01:01:02", 1, 2}, +}; + +class FipEcmpTest : public ::testing::Test { + virtual void SetUp() { + agent_ = Agent::GetInstance(); + + boost::system::error_code ec; + bgp_peer = CreateBgpPeer(Ip4Address::from_string("0.0.0.1", ec), + "xmpp channel"); + client->WaitForIdle(); + + flow_proto_ = agent_->pkt()->get_flow_proto(); + CreateVmportWithEcmp(input1, 2); + AddVn(VN2, 2); + AddVrf(VRF2); + AddLink("virtual-network", VN2, "routing-instance", + VRF2); + client->WaitForIdle(); + //Attach floating-ip + //Add floating IP for vnet1 + AddFloatingIpPool("fip-pool1", 1); + AddFloatingIp("fip1", 1, "2.1.1.1", "1.1.1.1"); + AddLink("floating-ip", "fip1", "floating-ip-pool", "fip-pool1"); + AddLink("floating-ip-pool", "fip-pool1", "virtual-network", + VN2); + client->WaitForIdle(); + AddLink("virtual-machine-interface", "vnet1", "floating-ip", "fip1"); + client->WaitForIdle(); + + strcpy(router_id, agent_->router_id().to_string().c_str()); + strcpy(MX_0, "100.1.1.1"); + strcpy(MX_1, "100.1.1.2"); + strcpy(MX_2, "100.1.1.3"); + strcpy(MX_3, "100.1.1.4"); + + const VmInterface *vmi = static_cast(VmPortGet(1)); + vm1_label = vmi->label(); + eth_intf_id = EthInterfaceGet("vnet0")->id(); + } + + virtual void TearDown() { + DelLink("virtual-network", VN2, "routing-instance", + VRF2); + DelLink("floating-ip", "fip1", "floating-ip-pool", "fip-pool1"); + DelLink("floating-ip-pool", "fip-pool1", + "virtual-network", VN2); + DelLink("virtual-machine-interface", "vnet1", "floating-ip", "fip1"); + DelFloatingIp("fip1"); + DelFloatingIpPool("fip-pool1"); + client->WaitForIdle(); + + DeleteVmportEnv(input1, 2, true); + DelVn(VN2); + DelVrf(VRF2); + client->WaitForIdle(); + DeleteBgpPeer(bgp_peer); + client->WaitForIdle(); + EXPECT_FALSE(VrfFind("vrf1", true)); + EXPECT_FALSE(VrfFind("vrf2", true)); + WAIT_FOR(1000, 1000, (flow_proto_->FlowCount() == 0)); + client->WaitForIdle(); + } +public: + + void AddLocalEcmpFip() { + AddFloatingIpPool("fip-pool1", 1); + AddFloatingIp("fip1", 1, "2.1.1.1", "1.1.1.1"); + AddLink("floating-ip", "fip1", "floating-ip-pool", "fip-pool1"); + AddLink("floating-ip-pool", "fip-pool1", "virtual-network", + VN2); + client->WaitForIdle(); + AddLink("virtual-machine-interface", "vnet2", "floating-ip", "fip1"); + client->WaitForIdle(); + } + + void DeleteLocalEcmpFip() { + DelLink("floating-ip", "fip1", "floating-ip-pool", "fip-pool1"); + DelLink("floating-ip-pool", "fip-pool1", + "virtual-network", VN2); + DelLink("virtual-machine-interface", "vnet2", "floating-ip", "fip1"); + DelFloatingIp("fip1"); + DelFloatingIpPool("fip-pool1"); + client->WaitForIdle(); + } + + void AddRemoteEcmpFip() { + //If there is a local route, include that always + Ip4Address fip = Ip4Address::from_string("2.1.1.1"); + + ComponentNHKeyList comp_nh_list; + SecurityGroupList sg_id_list; + ComponentNHKeyPtr comp_nh(new ComponentNHKey( + 16, Agent::GetInstance()->fabric_vrf_name(), + Agent::GetInstance()->router_id(), + Ip4Address(0x64010101), + false, TunnelType::AllType())); + ComponentNHKeyPtr comp_nh1(new ComponentNHKey(vm1_label, + MakeUuid(1), InterfaceNHFlags::INET4)); + comp_nh_list.push_back(comp_nh1); + + EcmpTunnelRouteAdd(bgp_peer, VRF2, fip, 32, + comp_nh_list, -1, VN2, sg_id_list, + PathPreference()); + } + + void DeleteRemoteEcmpFip() { + Ip4Address fip = Ip4Address::from_string("2.1.1.1"); + agent_->fabric_inet4_unicast_table()-> + DeleteReq(bgp_peer, VRF2, fip, 32, + new ControllerVmRoute(bgp_peer)); + client->WaitForIdle(); + } + + void AddRemoteEcmpRoute(const string vrf_name, const string ip, + uint32_t plen, const string vn, int count, bool reverse = false, + bool same_label = false) { + //If there is a local route, include that always + Ip4Address vm_ip = Ip4Address::from_string(ip); + + ComponentNHKeyList comp_nh_list; + int remote_server_ip = 0x64010101; + int label = 16; + SecurityGroupList sg_id_list; + + for(int i = 0; i < count; i++) { + ComponentNHKeyPtr comp_nh(new ComponentNHKey( + label, Agent::GetInstance()->fabric_vrf_name(), + Agent::GetInstance()->router_id(), + Ip4Address(remote_server_ip++), + false, TunnelType::AllType())); + comp_nh_list.push_back(comp_nh); + if (!same_label) { + label++; + } + } + if (reverse) { + std::reverse(comp_nh_list.begin(), comp_nh_list.end()); + } + + EcmpTunnelRouteAdd(bgp_peer, vrf_name, vm_ip, plen, + comp_nh_list, -1, vn, sg_id_list, + PathPreference()); + } + + FlowProto *get_flow_proto() const { return flow_proto_; } + Agent *agent_; + Peer *bgp_peer; + FlowProto *flow_proto_; + AgentXmppChannel *channel; + char router_id[80]; + char MX_0[80]; + char MX_1[80]; + char MX_2[80]; + char MX_3[80]; + int vm1_label; + int ecmp_label; + int eth_intf_id; +}; + +//Packet from VM with ECMP FIP to destination ECMP +//ECMP FIP resides on remote compute node +TEST_F(FipEcmpTest, Test_1) { + AddRemoteEcmpRoute(VRF2, "0.0.0.0", 0, VN2, 4); + AddRemoteEcmpFip(); + + TxIpPacket(VmPortGetId(1), "1.1.1.1", "8.8.8.8", 1); + client->WaitForIdle(); + + FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), + "1.1.1.1", "8.8.8.8", 1, 0, 0, + GetFlowKeyNH(1)); + + InetUnicastRouteEntry *rt = static_cast( + RouteGet(VRF2, Ip4Address::from_string("2.1.1.1"), 32)); + + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().nh.get() == rt->GetLocalNextHop()); + + + rt = static_cast( + RouteGet(VRF2, Ip4Address::from_string("0.0.0.0"), 0)); + //Reverse flow is no ECMP + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + + DeleteRoute(VRF2, "0.0.0.0", 0, bgp_peer); + DeleteRemoteEcmpFip(); + client->WaitForIdle(); + WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); +} + +//Packet from external ECMP source to fip in ECMP +//ECMP FIP resides on differnt compute node +TEST_F(FipEcmpTest, Test_2) { + AddRemoteEcmpRoute(VRF2, "0.0.0.0", 0, VN2, 4); + //Add 2.1.1.1 route with ECMP of local and remote compute node + AddRemoteEcmpFip(); + + TxIpMplsPacket(eth_intf_id, MX_2, router_id, vm1_label, + "8.8.8.8", "2.1.1.1", 1, 10); + client->WaitForIdle(); + + InetUnicastRouteEntry *rt = static_cast( + RouteGet("vrf1", Ip4Address::from_string("1.1.1.1"), 32)); + FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), + "1.1.1.1", "8.8.8.8", 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx == 2); + EXPECT_TRUE(entry->data().nh.get() == rt->GetLocalNextHop()); + + rt = static_cast( + RouteGet(VRF2, Ip4Address::from_string("0.0.0.0"), 0)); + + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + + DeleteRoute(VRF2, "0.0.0.0", 0, bgp_peer); + DeleteRemoteEcmpFip(); + client->WaitForIdle(); + WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); +} + +//Packet from VM with ECMP FIP to destination ECMP +//Both FIP instance reside on same compute node +TEST_F(FipEcmpTest, Test_3) { + AddRemoteEcmpRoute(VRF2, "0.0.0.0", 0, VN2, 4); + AddLocalEcmpFip(); + + TxIpPacket(VmPortGetId(1), "1.1.1.1", "8.8.8.8", 1); + client->WaitForIdle(); + + FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), + "1.1.1.1", "8.8.8.8", 1, 0, 0, + GetFlowKeyNH(1)); + + InetUnicastRouteEntry *rt = static_cast( + RouteGet("vrf1", Ip4Address::from_string("1.1.1.1"), 32)); + + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(entry->data().nh.get() == rt->GetLocalNextHop()); + + rt = static_cast( + RouteGet(VRF2, Ip4Address::from_string("0.0.0.0"), 0)); + //Reverse flow is no ECMP + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx != + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + + DeleteRoute(VRF2, "0.0.0.0", 32, bgp_peer); + DeleteLocalEcmpFip(); + client->WaitForIdle(); + WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); +} + +//Packet from external ECMP source to fip in ECMP +//Both FIP reside on same compute node +TEST_F(FipEcmpTest, Test_4) { + AddRemoteEcmpRoute(VRF2, "0.0.0.0", 0, VN2, 4); + AddRemoteEcmpFip(); + + InetUnicastRouteEntry *rt = static_cast( + RouteGet(VRF2, Ip4Address::from_string("2.1.1.1"), 32)); + uint16_t ecmp_label = rt->GetActivePath()->GetActiveLabel(); + + TxIpMplsPacket(eth_intf_id, MX_2, router_id, ecmp_label, + "8.8.8.8", "2.1.1.1", 1, 10); + client->WaitForIdle(); + + rt = static_cast( + RouteGet("vrf1", Ip4Address::from_string("1.1.1.1"), 32)); + FlowEntry *entry = FlowGet(VrfGet("vrf1")->vrf_id(), + "1.1.1.1", "8.8.8.8", 1, 0, 0, GetFlowKeyNH(1)); + EXPECT_TRUE(entry != NULL); + EXPECT_TRUE(entry->data().component_nh_idx == 2); + EXPECT_TRUE(entry->data().nh.get() == rt->GetLocalNextHop()); + + rt = static_cast( + RouteGet(VRF2, Ip4Address::from_string("0.0.0.0"), 0)); + + FlowEntry *rev_entry = entry->reverse_flow_entry(); + EXPECT_TRUE(rev_entry->data().component_nh_idx == + CompositeNH::kInvalidComponentNHIdx); + EXPECT_TRUE(rev_entry->data().nh.get() == rt->GetActiveNextHop()); + + DeleteRoute(VRF2, "0.0.0.0", 0, bgp_peer); + DeleteLocalEcmpFip(); + client->WaitForIdle(); + WAIT_FOR(1000, 1000, (get_flow_proto()->FlowCount() == 0)); +} + +int main(int argc, char *argv[]) { + GETUSERARGS(); + client = TestInit(init_file, ksync_init, true, true, true, 100*1000); + int ret = RUN_ALL_TESTS(); + client->WaitForIdle(); + TestShutdown(); + delete client; + return ret; +}