X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=test%2Ftest_bfd.py;h=635e7e12a760b6fd5f2b73ed1ce670ab0bdf24c3;hb=e88865d;hp=5460a2b558952a096bdc7e2a90224506d66aa277;hpb=871349371a62f1f20b159b6afead8e84f8a2322b;p=vpp.git diff --git a/test/test_bfd.py b/test/test_bfd.py index 5460a2b5589..635e7e12a76 100644 --- a/test/test_bfd.py +++ b/test/test_bfd.py @@ -2,23 +2,30 @@ """ BFD tests """ from __future__ import division -import unittest -import hashlib + import binascii +import hashlib import time -from struct import pack, unpack +import unittest from random import randint, shuffle, getrandbits from socket import AF_INET, AF_INET6, inet_ntop -from scapy.packet import Raw -from scapy.layers.l2 import Ether +from struct import pack, unpack + +from six import moves from scapy.layers.inet import UDP, IP from scapy.layers.inet6 import IPv6 +from scapy.layers.l2 import Ether +from scapy.packet import Raw + from bfd import VppBFDAuthKey, BFD, BFDAuthType, VppBFDUDPSession, \ BFDDiagCode, BFDState, BFD_vpp_echo from framework import VppTestCase, VppTestRunner, running_extended_tests -from vpp_pg_interface import CaptureTimeoutError from util import ppp +from vpp_ip import DpoProto +from vpp_ip_route import VppIpRoute, VppRoutePath +from vpp_lo_interface import VppLoInterface from vpp_papi_provider import UnexpectedApiReturnValueError +from vpp_pg_interface import CaptureTimeoutError, is_ipv6_misc USEC_IN_SEC = 1000000 @@ -50,7 +57,7 @@ class BFDAPITestCase(VppTestCase): @classmethod def setUpClass(cls): super(BFDAPITestCase, cls).setUpClass() - + cls.vapi.cli("set log class bfd level debug") try: cls.create_pg_interfaces(range(2)) for i in cls.pg_interfaces: @@ -81,7 +88,7 @@ class BFDAPITestCase(VppTestCase): session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4) session.add_vpp_config() - with self.vapi.expect_negative_api_retval(): + with self.vapi.assert_negative_api_retval(): session.add_vpp_config() session.remove_vpp_config() @@ -249,7 +256,53 @@ class BFDAPITestCase(VppTestCase): session.add_vpp_config() session.activate_auth(key2) + def test_set_del_udp_echo_source(self): + """ set/del udp echo source """ + self.create_loopback_interfaces(1) + self.loopback0 = self.lo_interfaces[0] + self.loopback0.admin_up() + echo_source = self.vapi.bfd_udp_get_echo_source() + self.assertFalse(echo_source.is_set) + self.assertFalse(echo_source.have_usable_ip4) + self.assertFalse(echo_source.have_usable_ip6) + + self.vapi.bfd_udp_set_echo_source(self.loopback0.sw_if_index) + echo_source = self.vapi.bfd_udp_get_echo_source() + self.assertTrue(echo_source.is_set) + self.assertEqual(echo_source.sw_if_index, self.loopback0.sw_if_index) + self.assertFalse(echo_source.have_usable_ip4) + self.assertFalse(echo_source.have_usable_ip6) + self.loopback0.config_ip4() + unpacked = unpack("!L", self.loopback0.local_ip4n) + echo_ip4 = pack("!L", unpacked[0] ^ 1) + echo_source = self.vapi.bfd_udp_get_echo_source() + self.assertTrue(echo_source.is_set) + self.assertEqual(echo_source.sw_if_index, self.loopback0.sw_if_index) + self.assertTrue(echo_source.have_usable_ip4) + self.assertEqual(echo_source.ip4_addr, echo_ip4) + self.assertFalse(echo_source.have_usable_ip6) + + self.loopback0.config_ip6() + unpacked = unpack("!LLLL", self.loopback0.local_ip6n) + echo_ip6 = pack("!LLLL", unpacked[0], unpacked[1], unpacked[2], + unpacked[3] ^ 1) + echo_source = self.vapi.bfd_udp_get_echo_source() + self.assertTrue(echo_source.is_set) + self.assertEqual(echo_source.sw_if_index, self.loopback0.sw_if_index) + self.assertTrue(echo_source.have_usable_ip4) + self.assertEqual(echo_source.ip4_addr, echo_ip4) + self.assertTrue(echo_source.have_usable_ip6) + self.assertEqual(echo_source.ip6_addr, echo_ip6) + + self.vapi.bfd_udp_del_echo_source() + echo_source = self.vapi.bfd_udp_get_echo_source() + self.assertFalse(echo_source.is_set) + self.assertFalse(echo_source.have_usable_ip4) + self.assertFalse(echo_source.have_usable_ip6) + + +@unittest.skipUnless(running_extended_tests(), "part of extended tests") class BFDTestSession(object): """ BFD session as seen from test framework side """ @@ -267,8 +320,8 @@ class BFDTestSession(object): self.our_seq_number = our_seq_number self.vpp_seq_number = None self.my_discriminator = 0 - self.desired_min_tx = 100000 - self.required_min_rx = 100000 + self.desired_min_tx = 300000 + self.required_min_rx = 300000 self.required_min_echo_rx = None self.detect_mult = detect_mult self.diag = BFDDiagCode.no_diagnostic @@ -553,7 +606,7 @@ def verify_udp(test, packet): def verify_event(test, event, expected_state): """ Verify correctness of event values. """ e = event - test.logger.debug("BFD: Event: %s" % repr(e)) + test.logger.debug("BFD: Event: %s" % moves.reprlib.repr(e)) test.assert_equal(e.sw_if_index, test.vpp_session.interface.sw_if_index, "BFD interface index") @@ -611,6 +664,7 @@ def wait_for_bfd_packet(test, timeout=1, pcap_time_min=None): return p +@unittest.skipUnless(running_extended_tests(), "part of extended tests") class BFD4TestCase(VppTestCase): """Bidirectional Forwarding Detection (BFD)""" @@ -622,9 +676,10 @@ class BFD4TestCase(VppTestCase): @classmethod def setUpClass(cls): super(BFD4TestCase, cls).setUpClass() + cls.vapi.cli("set log class bfd level debug") try: cls.create_pg_interfaces([0]) - cls.create_loopback_interfaces([0]) + cls.create_loopback_interfaces(1) cls.loopback0 = cls.lo_interfaces[0] cls.loopback0.config_ip4() cls.loopback0.admin_up() @@ -735,7 +790,7 @@ class BFD4TestCase(VppTestCase): pass self.assert_equal( len(self.vapi.collect_events()), 0, "number of bfd events") - self.test_session.update(required_min_rx=100000) + self.test_session.update(required_min_rx=300000) for dummy in range(3): self.test_session.send_packet() wait_for_bfd_packet( @@ -864,7 +919,8 @@ class BFD4TestCase(VppTestCase): required_min_rx=0.5 * self.vpp_session.required_min_rx) # now we wait 0.8*3*old-req-min-rx and the session should still be up self.sleep(0.8 * self.vpp_session.detect_mult * - old_required_min_rx / USEC_IN_SEC) + old_required_min_rx / USEC_IN_SEC, + "wait before finishing poll sequence") self.assert_equal(len(self.vapi.collect_events()), 0, "number of bfd events") p = wait_for_bfd_packet(self) @@ -876,11 +932,12 @@ class BFD4TestCase(VppTestCase): final[BFD].flags = "F" self.test_session.send_packet(final) # now the session should time out under new conditions - before = time.time() - e = self.vapi.wait_for_event(1, "bfd_udp_session_details") - after = time.time() detection_time = self.test_session.detect_mult *\ self.vpp_session.required_min_rx / USEC_IN_SEC + before = time.time() + e = self.vapi.wait_for_event( + 2 * detection_time, "bfd_udp_session_details") + after = time.time() self.assert_in_range(after - before, 0.9 * detection_time, 1.1 * detection_time, @@ -1063,13 +1120,13 @@ class BFD4TestCase(VppTestCase): def test_echo(self): """ echo function """ bfd_session_up(self) - self.test_session.update(required_min_echo_rx=50000) + self.test_session.update(required_min_echo_rx=150000) self.test_session.send_packet() detection_time = self.test_session.detect_mult *\ self.vpp_session.required_min_rx / USEC_IN_SEC # echo shouldn't work without echo source set - for dummy in range(3): - sleep = 0.75 * detection_time + for dummy in range(10): + sleep = self.vpp_session.required_min_rx / USEC_IN_SEC self.sleep(sleep, "delay before sending bfd packet") self.test_session.send_packet() p = wait_for_bfd_packet( @@ -1077,7 +1134,9 @@ class BFD4TestCase(VppTestCase): self.assert_equal(p[BFD].required_min_rx_interval, self.vpp_session.required_min_rx, "BFD required min rx interval") + self.test_session.send_packet() self.vapi.bfd_udp_set_echo_source(self.loopback0.sw_if_index) + echo_seen = False # should be turned on - loopback echo packets for dummy in range(3): loop_until = time.time() + 0.75 * detection_time @@ -1090,11 +1149,17 @@ class BFD4TestCase(VppTestCase): self.assertNotEqual(p[IP].src, self.loopback0.local_ip4, "BFD ECHO src IP equal to loopback IP") self.logger.debug(ppp("Looping back packet:", p)) + self.assert_equal(p[Ether].dst, self.pg0.remote_mac, + "ECHO packet destination MAC address") + p[Ether].dst = self.pg0.local_mac self.pg0.add_stream(p) self.pg_start() + echo_seen = True elif p.haslayer(BFD): - self.assertGreaterEqual(p[BFD].required_min_rx_interval, - 1000000) + if echo_seen: + self.assertGreaterEqual( + p[BFD].required_min_rx_interval, + 1000000) if "P" in p.sprintf("%BFD.flags%"): final = self.test_session.create_packet() final[BFD].flags = "F" @@ -1105,12 +1170,13 @@ class BFD4TestCase(VppTestCase): self.assert_equal(len(self.vapi.collect_events()), 0, "number of bfd events") self.test_session.send_packet() + self.assertTrue(echo_seen, "No echo packets received") @unittest.skipUnless(running_extended_tests(), "part of extended tests") def test_echo_fail(self): """ session goes down if echo function fails """ bfd_session_up(self) - self.test_session.update(required_min_echo_rx=50000) + self.test_session.update(required_min_echo_rx=150000) self.test_session.send_packet() detection_time = self.test_session.detect_mult *\ self.vpp_session.required_min_rx / USEC_IN_SEC @@ -1150,7 +1216,7 @@ class BFD4TestCase(VppTestCase): def test_echo_stop(self): """ echo function stops if peer sets required min echo rx zero """ bfd_session_up(self) - self.test_session.update(required_min_echo_rx=50000) + self.test_session.update(required_min_echo_rx=150000) self.test_session.send_packet() self.vapi.bfd_udp_set_echo_source(self.loopback0.sw_if_index) # wait for first echo packet @@ -1159,6 +1225,7 @@ class BFD4TestCase(VppTestCase): self.logger.debug(ppp("Got packet:", p)) if p[UDP].dport == BFD.udp_dport_echo: self.logger.debug(ppp("Looping back packet:", p)) + p[Ether].dst = self.pg0.local_mac self.pg0.add_stream(p) self.pg_start() break @@ -1181,7 +1248,7 @@ class BFD4TestCase(VppTestCase): def test_echo_source_removed(self): """ echo function stops if echo source is removed """ bfd_session_up(self) - self.test_session.update(required_min_echo_rx=50000) + self.test_session.update(required_min_echo_rx=150000) self.test_session.send_packet() self.vapi.bfd_udp_set_echo_source(self.loopback0.sw_if_index) # wait for first echo packet @@ -1190,6 +1257,7 @@ class BFD4TestCase(VppTestCase): self.logger.debug(ppp("Got packet:", p)) if p[UDP].dport == BFD.udp_dport_echo: self.logger.debug(ppp("Looping back packet:", p)) + p[Ether].dst = self.pg0.local_mac self.pg0.add_stream(p) self.pg_start() break @@ -1212,7 +1280,7 @@ class BFD4TestCase(VppTestCase): def test_stale_echo(self): """ stale echo packets don't keep a session up """ bfd_session_up(self) - self.test_session.update(required_min_echo_rx=50000) + self.test_session.update(required_min_echo_rx=150000) self.vapi.bfd_udp_set_echo_source(self.loopback0.sw_if_index) self.test_session.send_packet() # should be turned on - loopback echo packets @@ -1230,6 +1298,7 @@ class BFD4TestCase(VppTestCase): else: self.logger.debug(ppp("Got followup echo packet:", p)) self.logger.debug(ppp("Looping back first echo packet:", p)) + echo_packet[Ether].dst = self.pg0.local_mac self.pg0.add_stream(echo_packet) self.pg_start() elif p.haslayer(BFD): @@ -1264,7 +1333,7 @@ class BFD4TestCase(VppTestCase): def test_invalid_echo_checksum(self): """ echo packets with invalid checksum don't keep a session up """ bfd_session_up(self) - self.test_session.update(required_min_echo_rx=50000) + self.test_session.update(required_min_echo_rx=150000) self.vapi.bfd_udp_set_echo_source(self.loopback0.sw_if_index) self.test_session.send_packet() # should be turned on - loopback echo packets @@ -1278,6 +1347,7 @@ class BFD4TestCase(VppTestCase): timeout_at = time.time() + self.vpp_session.detect_mult * \ self.test_session.required_min_echo_rx / USEC_IN_SEC p[BFD_vpp_echo].checksum = getrandbits(64) + p[Ether].dst = self.pg0.local_mac self.logger.debug(ppp("Looping back modified echo packet:", p)) self.pg0.add_stream(p) self.pg_start() @@ -1385,7 +1455,22 @@ class BFD4TestCase(VppTestCase): self.assert_equal(count, 0, "number of packets received") self.assert_equal(len(events), 0, "number of events received") + def test_intf_deleted(self): + """ interface with bfd session deleted """ + intf = VppLoInterface(self) + intf.config_ip4() + intf.admin_up() + sw_if_index = intf.sw_if_index + vpp_session = VppBFDUDPSession(self, intf, intf.remote_ip4) + vpp_session.add_vpp_config() + vpp_session.admin_up() + intf.remove_vpp_config() + e = self.vapi.wait_for_event(1, "bfd_udp_session_details") + self.assert_equal(e.sw_if_index, sw_if_index, "sw_if_index") + self.assertFalse(vpp_session.query_vpp_config()) + +@unittest.skipUnless(running_extended_tests(), "part of extended tests") class BFD6TestCase(VppTestCase): """Bidirectional Forwarding Detection (BFD) (IPv6) """ @@ -1397,13 +1482,14 @@ class BFD6TestCase(VppTestCase): @classmethod def setUpClass(cls): super(BFD6TestCase, cls).setUpClass() + cls.vapi.cli("set log class bfd level debug") try: cls.create_pg_interfaces([0]) cls.pg0.config_ip6() cls.pg0.configure_ipv6_neighbors() cls.pg0.admin_up() cls.pg0.resolve_ndp() - cls.create_loopback_interfaces([0]) + cls.create_loopback_interfaces(1) cls.loopback0 = cls.lo_interfaces[0] cls.loopback0.config_ip6() cls.loopback0.admin_up() @@ -1523,17 +1609,16 @@ class BFD6TestCase(VppTestCase): self.assert_equal(udp_sport_tx, udp_sport_rx, "UDP source port (== " "ECHO packet identifier for test purposes)") - @unittest.skipUnless(running_extended_tests(), "part of extended tests") def test_echo(self): - """ echo function used """ + """ echo function """ bfd_session_up(self) - self.test_session.update(required_min_echo_rx=50000) + self.test_session.update(required_min_echo_rx=150000) self.test_session.send_packet() detection_time = self.test_session.detect_mult *\ self.vpp_session.required_min_rx / USEC_IN_SEC # echo shouldn't work without echo source set - for dummy in range(3): - sleep = 0.75 * detection_time + for dummy in range(10): + sleep = self.vpp_session.required_min_rx / USEC_IN_SEC self.sleep(sleep, "delay before sending bfd packet") self.test_session.send_packet() p = wait_for_bfd_packet( @@ -1541,7 +1626,9 @@ class BFD6TestCase(VppTestCase): self.assert_equal(p[BFD].required_min_rx_interval, self.vpp_session.required_min_rx, "BFD required min rx interval") + self.test_session.send_packet() self.vapi.bfd_udp_set_echo_source(self.loopback0.sw_if_index) + echo_seen = False # should be turned on - loopback echo packets for dummy in range(3): loop_until = time.time() + 0.75 * detection_time @@ -1554,11 +1641,17 @@ class BFD6TestCase(VppTestCase): self.assertNotEqual(p[IPv6].src, self.loopback0.local_ip6, "BFD ECHO src IP equal to loopback IP") self.logger.debug(ppp("Looping back packet:", p)) + self.assert_equal(p[Ether].dst, self.pg0.remote_mac, + "ECHO packet destination MAC address") + p[Ether].dst = self.pg0.local_mac self.pg0.add_stream(p) self.pg_start() + echo_seen = True elif p.haslayer(BFD): - self.assertGreaterEqual(p[BFD].required_min_rx_interval, - 1000000) + if echo_seen: + self.assertGreaterEqual( + p[BFD].required_min_rx_interval, + 1000000) if "P" in p.sprintf("%BFD.flags%"): final = self.test_session.create_packet() final[BFD].flags = "F" @@ -1569,8 +1662,127 @@ class BFD6TestCase(VppTestCase): self.assert_equal(len(self.vapi.collect_events()), 0, "number of bfd events") self.test_session.send_packet() + self.assertTrue(echo_seen, "No echo packets received") + + def test_intf_deleted(self): + """ interface with bfd session deleted """ + intf = VppLoInterface(self) + intf.config_ip6() + intf.admin_up() + sw_if_index = intf.sw_if_index + vpp_session = VppBFDUDPSession( + self, intf, intf.remote_ip6, af=AF_INET6) + vpp_session.add_vpp_config() + vpp_session.admin_up() + intf.remove_vpp_config() + e = self.vapi.wait_for_event(1, "bfd_udp_session_details") + self.assert_equal(e.sw_if_index, sw_if_index, "sw_if_index") + self.assertFalse(vpp_session.query_vpp_config()) + + +@unittest.skipUnless(running_extended_tests(), "part of extended tests") +class BFDFIBTestCase(VppTestCase): + """ BFD-FIB interactions (IPv6) """ + + vpp_session = None + test_session = None + + def setUp(self): + super(BFDFIBTestCase, self).setUp() + self.create_pg_interfaces(range(1)) + + self.vapi.want_bfd_events() + self.pg0.enable_capture() + + for i in self.pg_interfaces: + i.admin_up() + i.config_ip6() + i.configure_ipv6_neighbors() + def tearDown(self): + if not self.vpp_dead: + self.vapi.want_bfd_events(enable_disable=0) + + super(BFDFIBTestCase, self).tearDown() + + @staticmethod + def pkt_is_not_data_traffic(p): + """ not data traffic implies BFD or the usual IPv6 ND/RA""" + if p.haslayer(BFD) or is_ipv6_misc(p): + return True + return False + + def test_session_with_fib(self): + """ BFD-FIB interactions """ + + # packets to match against both of the routes + p = [(Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src="3001::1", dst="2001::1") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100)), + (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IPv6(src="3001::1", dst="2002::1") / + UDP(sport=1234, dport=1234) / + Raw('\xa5' * 100))] + + # A recursive and a non-recursive route via a next-hop that + # will have a BFD session + ip_2001_s_64 = VppIpRoute(self, "2001::", 64, + [VppRoutePath(self.pg0.remote_ip6, + self.pg0.sw_if_index, + proto=DpoProto.DPO_PROTO_IP6)], + is_ip6=1) + ip_2002_s_64 = VppIpRoute(self, "2002::", 64, + [VppRoutePath(self.pg0.remote_ip6, + 0xffffffff, + proto=DpoProto.DPO_PROTO_IP6)], + is_ip6=1) + ip_2001_s_64.add_vpp_config() + ip_2002_s_64.add_vpp_config() + + # bring the session up now the routes are present + self.vpp_session = VppBFDUDPSession(self, + self.pg0, + self.pg0.remote_ip6, + af=AF_INET6) + self.vpp_session.add_vpp_config() + self.vpp_session.admin_up() + self.test_session = BFDTestSession(self, self.pg0, AF_INET6) + + # session is up - traffic passes + bfd_session_up(self) + self.pg0.add_stream(p) + self.pg_start() + for packet in p: + captured = self.pg0.wait_for_packet( + 1, + filter_out_fn=self.pkt_is_not_data_traffic) + self.assertEqual(captured[IPv6].dst, + packet[IPv6].dst) + + # session is up - traffic is dropped + bfd_session_down(self) + + self.pg0.add_stream(p) + self.pg_start() + with self.assertRaises(CaptureTimeoutError): + self.pg0.wait_for_packet(1, self.pkt_is_not_data_traffic) + + # session is up - traffic passes + bfd_session_up(self) + + self.pg0.add_stream(p) + self.pg_start() + for packet in p: + captured = self.pg0.wait_for_packet( + 1, + filter_out_fn=self.pkt_is_not_data_traffic) + self.assertEqual(captured[IPv6].dst, + packet[IPv6].dst) + + +@unittest.skipUnless(running_extended_tests(), "part of extended tests") class BFDSHA1TestCase(VppTestCase): """Bidirectional Forwarding Detection (BFD) (SHA1 auth) """ @@ -1582,6 +1794,7 @@ class BFDSHA1TestCase(VppTestCase): @classmethod def setUpClass(cls): super(BFDSHA1TestCase, cls).setUpClass() + cls.vapi.cli("set log class bfd level debug") try: cls.create_pg_interfaces([0]) cls.pg0.config_ip4() @@ -1814,6 +2027,7 @@ class BFDAuthOnOffTestCase(VppTestCase): @classmethod def setUpClass(cls): super(BFDAuthOnOffTestCase, cls).setUpClass() + cls.vapi.cli("set log class bfd level debug") try: cls.create_pg_interfaces([0]) cls.pg0.config_ip4() @@ -2019,7 +2233,7 @@ class BFDCLITestCase(VppTestCase): @classmethod def setUpClass(cls): super(BFDCLITestCase, cls).setUpClass() - + cls.vapi.cli("set log class bfd level debug") try: cls.create_pg_interfaces((0,)) cls.pg0.config_ip4() @@ -2393,7 +2607,7 @@ class BFDCLITestCase(VppTestCase): def test_set_del_udp_echo_source(self): """ set/del udp echo source """ - self.create_loopback_interfaces([0]) + self.create_loopback_interfaces(1) self.loopback0 = self.lo_interfaces[0] self.loopback0.admin_up() self.cli_verify_response("show bfd echo-source",