From 227038a444b98f922b4a4f44b85ae60f9ee86e1c Mon Sep 17 00:00:00 2001 From: Neale Ranns Date: Fri, 21 Apr 2017 01:07:59 -0700 Subject: IP Flow Hash Config fixes - the flow hash config is (and was) cached on the load-balance object so the fib_table_t struct is not used a switch time. Therefore changes to the table's flow hash config need to be propagated to all load-balances and hance all FIB entries in the table. - enable API for setting the IPv6 table flow hash config - use only the hash config in the fib_table_t object and not on the ipX_fib_t - add tests. Change-Id: Ib804c11162c6d4972c764957562c372f663e05d4 Signed-off-by: Neale Ranns --- test/test_ip4.py | 137 ++++++++++++++++++++++++++++++++++++++++++ test/test_ip6.py | 149 ++++++++++++++++++++++++++++++++++++++++++++++ test/vpp_papi_provider.py | 19 ++++++ 3 files changed, 305 insertions(+) (limited to 'test') diff --git a/test/test_ip4.py b/test/test_ip4.py index ed364b62..3fe61e26 100644 --- a/test/test_ip4.py +++ b/test/test_ip4.py @@ -766,5 +766,142 @@ class TestIPSubNets(VppTestCase): rx = self.pg1.get_capture(1) +class TestIPLoadBalance(VppTestCase): + """ IPv4 Load-Balancing """ + + def setUp(self): + super(TestIPLoadBalance, self).setUp() + + self.create_pg_interfaces(range(5)) + + for i in self.pg_interfaces: + i.admin_up() + i.config_ip4() + i.resolve_arp() + + def tearDown(self): + super(TestIPLoadBalance, self).tearDown() + for i in self.pg_interfaces: + i.unconfig_ip4() + i.admin_down() + + def send_and_expect_load_balancing(self, input, pkts, outputs): + input.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + for oo in outputs: + rx = oo._get_capture(1) + self.assertNotEqual(0, len(rx)) + + def test_ip_load_balance(self): + """ IP Load-Balancing """ + + # + # An array of packets that differ only in the destination port + # + port_pkts = [] + + # + # An array of packets that differ only in the source address + # + src_pkts = [] + + for ii in range(65): + port_pkts.append((Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IP(dst="10.0.0.1", src="20.0.0.1") / + UDP(sport=1234, dport=1234 + ii) / + Raw('\xa5' * 100))) + src_pkts.append((Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IP(dst="10.0.0.1", src="20.0.0.%d" % ii) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100))) + + route_10_0_0_1 = VppIpRoute(self, "10.0.0.1", 32, + [VppRoutePath(self.pg1.remote_ip4, + self.pg1.sw_if_index), + VppRoutePath(self.pg2.remote_ip4, + self.pg2.sw_if_index)]) + route_10_0_0_1.add_vpp_config() + + # + # inject the packet on pg0 - expect load-balancing across the 2 paths + # - since the default hash config is to use IP src,dst and port + # src,dst + # We are not going to ensure equal amounts of packets across each link, + # since the hash algorithm is statistical and therefore this can never + # be guaranteed. But wuth 64 different packets we do expect some + # balancing. So instead just ensure there is traffic on each link. + # + self.send_and_expect_load_balancing(self.pg0, port_pkts, + [self.pg1, self.pg2]) + self.send_and_expect_load_balancing(self.pg0, src_pkts, + [self.pg1, self.pg2]) + + # + # change the flow hash config so it's only IP src,dst + # - now only the stream with differing source address will + # load-balance + # + self.vapi.set_ip_flow_hash(0, src=1, dst=1, sport=0, dport=0) + + self.send_and_expect_load_balancing(self.pg0, src_pkts, + [self.pg1, self.pg2]) + + self.pg0.add_stream(port_pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg2.get_capture(len(port_pkts)) + + # + # change the flow hash config back to defaults + # + self.vapi.set_ip_flow_hash(0, src=1, dst=1, sport=1, dport=1) + + # + # Recursive prefixes + # - testing that 2 stages of load-balancing occurs and there is no + # polarisation (i.e. only 2 of 4 paths are used) + # + port_pkts = [] + src_pkts = [] + + for ii in range(257): + port_pkts.append((Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IP(dst="1.1.1.1", src="20.0.0.1") / + UDP(sport=1234, dport=1234 + ii) / + Raw('\xa5' * 100))) + src_pkts.append((Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IP(dst="1.1.1.1", src="20.0.0.%d" % ii) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100))) + + route_10_0_0_2 = VppIpRoute(self, "10.0.0.2", 32, + [VppRoutePath(self.pg3.remote_ip4, + self.pg3.sw_if_index), + VppRoutePath(self.pg4.remote_ip4, + self.pg4.sw_if_index)]) + route_10_0_0_2.add_vpp_config() + + route_1_1_1_1 = VppIpRoute(self, "1.1.1.1", 32, + [VppRoutePath("10.0.0.2", 0xffffffff), + VppRoutePath("10.0.0.1", 0xffffffff)]) + route_1_1_1_1.add_vpp_config() + + # + # inject the packet on pg0 - expect load-balancing across all 4 paths + # + self.vapi.cli("clear trace") + self.send_and_expect_load_balancing(self.pg0, port_pkts, + [self.pg1, self.pg2, + self.pg3, self.pg4]) + self.send_and_expect_load_balancing(self.pg0, src_pkts, + [self.pg1, self.pg2, + self.pg3, self.pg4]) + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) diff --git a/test/test_ip6.py b/test/test_ip6.py index 3ba09230..ebeffe20 100644 --- a/test/test_ip6.py +++ b/test/test_ip6.py @@ -1133,5 +1133,154 @@ class TestIPDisabled(VppTestCase): self.send_and_assert_no_replies(self.pg1, pm, "IPv6 disabled") +class TestIP6LoadBalance(VppTestCase): + """ IPv6 Load-Balancing """ + + def setUp(self): + super(TestIP6LoadBalance, self).setUp() + + self.create_pg_interfaces(range(5)) + + for i in self.pg_interfaces: + i.admin_up() + i.config_ip6() + i.resolve_ndp() + + def tearDown(self): + super(TestIP6LoadBalance, self).tearDown() + for i in self.pg_interfaces: + i.unconfig_ip6() + i.admin_down() + + def send_and_expect_load_balancing(self, input, pkts, outputs): + input.add_stream(pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + for oo in outputs: + rx = oo._get_capture(1) + self.assertNotEqual(0, len(rx)) + + def test_ip6_load_balance(self): + """ IPv6 Load-Balancing """ + + # + # An array of packets that differ only in the destination port + # + port_pkts = [] + + # + # An array of packets that differ only in the source address + # + src_pkts = [] + + for ii in range(65): + port_pkts.append((Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IPv6(dst="3000::1", src="3000:1::1") / + UDP(sport=1234, dport=1234 + ii) / + Raw('\xa5' * 100))) + src_pkts.append((Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IPv6(dst="3000::1", src="3000:1::%d" % ii) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100))) + + route_3000_1 = VppIpRoute(self, "3000::1", 128, + [VppRoutePath(self.pg1.remote_ip6, + self.pg1.sw_if_index, + is_ip6=1), + VppRoutePath(self.pg2.remote_ip6, + self.pg2.sw_if_index, + is_ip6=1)], + is_ip6=1) + route_3000_1.add_vpp_config() + + # + # inject the packet on pg0 - expect load-balancing across the 2 paths + # - since the default hash config is to use IP src,dst and port + # src,dst + # We are not going to ensure equal amounts of packets across each link, + # since the hash algorithm is statistical and therefore this can never + # be guaranteed. But wuth 64 different packets we do expect some + # balancing. So instead just ensure there is traffic on each link. + # + self.send_and_expect_load_balancing(self.pg0, port_pkts, + [self.pg1, self.pg2]) + self.send_and_expect_load_balancing(self.pg0, src_pkts, + [self.pg1, self.pg2]) + + # + # change the flow hash config so it's only IP src,dst + # - now only the stream with differing source address will + # load-balance + # + self.vapi.set_ip_flow_hash(0, is_ip6=1, src=1, dst=1, sport=0, dport=0) + + self.send_and_expect_load_balancing(self.pg0, src_pkts, + [self.pg1, self.pg2]) + + self.pg0.add_stream(port_pkts) + self.pg_enable_capture(self.pg_interfaces) + self.pg_start() + + rx = self.pg2.get_capture(len(port_pkts)) + + # + # change the flow hash config back to defaults + # + self.vapi.set_ip_flow_hash(0, is_ip6=1, src=1, dst=1, sport=1, dport=1) + + # + # Recursive prefixes + # - testing that 2 stages of load-balancing occurs and there is no + # polarisation (i.e. only 2 of 4 paths are used) + # + port_pkts = [] + src_pkts = [] + + for ii in range(257): + port_pkts.append((Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IPv6(dst="4000::1", src="4000:1::1") / + UDP(sport=1234, dport=1234 + ii) / + Raw('\xa5' * 100))) + src_pkts.append((Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IPv6(dst="4000::1", src="4000:1::%d" % ii) / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100))) + + route_3000_2 = VppIpRoute(self, "3000::2", 128, + [VppRoutePath(self.pg3.remote_ip6, + self.pg3.sw_if_index, + is_ip6=1), + VppRoutePath(self.pg4.remote_ip6, + self.pg4.sw_if_index, + is_ip6=1)], + is_ip6=1) + route_3000_2.add_vpp_config() + + route_4000_1 = VppIpRoute(self, "4000::1", 128, + [VppRoutePath("3000::1", + 0xffffffff, + is_ip6=1), + VppRoutePath("3000::2", + 0xffffffff, + is_ip6=1)], + is_ip6=1) + route_4000_1.add_vpp_config() + + # + # inject the packet on pg0 - expect load-balancing across all 4 paths + # + self.vapi.cli("clear trace") + self.send_and_expect_load_balancing(self.pg0, port_pkts, + [self.pg1, self.pg2, + self.pg3, self.pg4]) + self.send_and_expect_load_balancing(self.pg0, src_pkts, + [self.pg1, self.pg2, + self.pg3, self.pg4]) + + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner) diff --git a/test/vpp_papi_provider.py b/test/vpp_papi_provider.py index d94c0cb6..83c4a83b 100644 --- a/test/vpp_papi_provider.py +++ b/test/vpp_papi_provider.py @@ -277,6 +277,25 @@ class VppPapiProvider(object): {'sw_if_index': sw_if_index, 'suppress': suppress}) + def set_ip_flow_hash(self, + table_id, + src=1, + dst=1, + sport=1, + dport=1, + proto=1, + reverse=0, + is_ip6=0): + return self.api(self.papi.set_ip_flow_hash, + {'vrf_id': table_id, + 'src': src, + 'dst': dst, + 'dport': dport, + 'sport': sport, + 'proto': proto, + 'reverse': reverse, + 'is_ipv6': is_ip6}) + def ip6_nd_proxy(self, address, sw_if_index, is_del=0): return self.api(self.papi.ip6nd_proxy_add_del, {'address': address, -- cgit 1.2.3-korg