X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=test%2Ftest_bfd.py;h=e78ad0c406482e2ec9393eaace5f0c2f26f3f781;hb=61717cc38;hp=4e3f688b462951b2ea8b29d881af392395c15aea;hpb=1904c47d5f79f8e9dcc6c9ae2043e7d6be8bd6d8;p=vpp.git diff --git a/test/test_bfd.py b/test/test_bfd.py index 4e3f688b462..e78ad0c4064 100644 --- a/test/test_bfd.py +++ b/test/test_bfd.py @@ -1,24 +1,35 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 """ 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 +import scapy.compat from scapy.layers.inet import UDP, IP from scapy.layers.inet6 import IPv6 +from scapy.layers.l2 import Ether, GRE +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_papi_provider import UnexpectedApiReturnValueError +from vpp_ip import DpoProto +from vpp_ip_route import VppIpRoute, VppRoutePath +from vpp_lo_interface import VppLoInterface +from vpp_papi_provider import UnexpectedApiReturnValueError, \ + CliFailedCommandError +from vpp_pg_interface import CaptureTimeoutError, is_ipv6_misc +from vpp_gre_interface import VppGreInterface +from vpp_papi import VppEnum USEC_IN_SEC = 1000000 @@ -35,12 +46,12 @@ class AuthKeyFactory(object): while conf_key_id in self._conf_key_ids: conf_key_id = randint(0, 0xFFFFFFFF) self._conf_key_ids[conf_key_id] = 1 - key = str(bytearray([randint(0, 255) for _ in range(randint(1, 20))])) + key = scapy.compat.raw( + bytearray([randint(0, 255) for _ in range(randint(1, 20))])) return VppBFDAuthKey(test=test, auth_type=auth_type, conf_key_id=conf_key_id, key=key) -@unittest.skipUnless(running_extended_tests(), "part of extended tests") class BFDAPITestCase(VppTestCase): """Bidirectional Forwarding Detection (BFD) - API""" @@ -50,7 +61,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: @@ -62,6 +73,10 @@ class BFDAPITestCase(VppTestCase): super(BFDAPITestCase, cls).tearDownClass() raise + @classmethod + def tearDownClass(cls): + super(BFDAPITestCase, cls).tearDownClass() + def setUp(self): super(BFDAPITestCase, self).setUp() self.factory = AuthKeyFactory() @@ -81,7 +96,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() @@ -136,7 +151,7 @@ class BFDAPITestCase(VppTestCase): for key in keys: self.assertTrue(key.query_vpp_config()) # remove randomly - indexes = range(key_count) + indexes = list(range(key_count)) shuffle(indexes) removed = [] for i in indexes: @@ -249,17 +264,68 @@ 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( + sw_if_index=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.packed, 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.packed, echo_ip4) + self.assertTrue(echo_source.have_usable_ip6) + self.assertEqual(echo_source.ip6_addr.packed, 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) + class BFDTestSession(object): """ BFD session as seen from test framework side """ def __init__(self, test, interface, af, detect_mult=3, sha1_key=None, - bfd_key_id=None, our_seq_number=None): + bfd_key_id=None, our_seq_number=None, + tunnel_header=None, phy_interface=None): self.test = test self.af = af self.sha1_key = sha1_key self.bfd_key_id = bfd_key_id self.interface = interface + if phy_interface: + self.phy_interface = phy_interface + else: + self.phy_interface = self.interface self.udp_sport = randint(49152, 65535) if our_seq_number is None: self.our_seq_number = randint(0, 40000000) @@ -267,14 +333,15 @@ 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 self.your_discriminator = None self.state = BFDState.down self.auth_type = BFDAuthType.no_auth + self.tunnel_header = tunnel_header def inc_seq_num(self): """ increment sequence number, wrapping if needed """ @@ -360,17 +427,19 @@ class BFDTestSession(object): bfd.length = BFD.sha1_auth_len + BFD.bfd_pkt_len else: bfd = BFD() + packet = Ether(src=self.phy_interface.remote_mac, + dst=self.phy_interface.local_mac) + if self.tunnel_header: + packet = packet / self.tunnel_header if self.af == AF_INET6: - packet = (Ether(src=self.interface.remote_mac, - dst=self.interface.local_mac) / + packet = (packet / IPv6(src=self.interface.remote_ip6, dst=self.interface.local_ip6, hlim=255) / UDP(sport=self.udp_sport, dport=BFD.udp_dport) / bfd) else: - packet = (Ether(src=self.interface.remote_mac, - dst=self.interface.local_mac) / + packet = (packet / IP(src=self.interface.remote_ip4, dst=self.interface.local_ip4, ttl=255) / @@ -379,8 +448,9 @@ class BFDTestSession(object): self.test.logger.debug("BFD: Creating packet") self.fill_packet_fields(packet) if self.sha1_key: - hash_material = str(packet[BFD])[:32] + self.sha1_key.key + \ - "\0" * (20 - len(self.sha1_key.key)) + hash_material = scapy.compat.raw( + packet[BFD])[:32] + self.sha1_key.key + \ + b"\0" * (20 - len(self.sha1_key.key)) self.test.logger.debug("BFD: Calculated SHA1 hash: %s" % hashlib.sha1(hash_material).hexdigest()) packet[BFD].auth_key_hash = hashlib.sha1(hash_material).digest() @@ -391,7 +461,7 @@ class BFDTestSession(object): if packet is None: packet = self.create_packet() if interface is None: - interface = self.test.pg0 + interface = self.phy_interface self.test.logger.debug(ppp("Sending packet:", packet)) interface.add_stream(packet) self.test.pg_start() @@ -436,10 +506,10 @@ class BFDTestSession(object): # last 20 bytes represent the hash - so replace them with the key, # pad the result with zeros and hash the result hash_material = bfd.original[:-20] + self.sha1_key.key + \ - "\0" * (20 - len(self.sha1_key.key)) + b"\0" * (20 - len(self.sha1_key.key)) expected_hash = hashlib.sha1(hash_material).hexdigest() self.test.assert_equal(binascii.hexlify(bfd.auth_key_hash), - expected_hash, "Auth key hash") + expected_hash.encode(), "Auth key hash") def verify_bfd(self, packet): """ Verify correctness of BFD layer. """ @@ -455,11 +525,11 @@ class BFDTestSession(object): def bfd_session_up(test): """ Bring BFD session up """ test.logger.info("BFD: Waiting for slow hello") - p = wait_for_bfd_packet(test, 2) + p = wait_for_bfd_packet(test, 2, is_tunnel=test.vpp_session.is_tunnel) old_offset = None if hasattr(test, 'vpp_clock_offset'): old_offset = test.vpp_clock_offset - test.vpp_clock_offset = time.time() - p.time + test.vpp_clock_offset = time.time() - float(p.time) test.logger.debug("BFD: Calculated vpp clock offset: %s", test.vpp_clock_offset) if old_offset: @@ -530,13 +600,13 @@ def verify_ip(test, packet): """ Verify correctness of IP layer. """ if test.vpp_session.af == AF_INET6: ip = packet[IPv6] - local_ip = test.pg0.local_ip6 - remote_ip = test.pg0.remote_ip6 + local_ip = test.vpp_session.interface.local_ip6 + remote_ip = test.vpp_session.interface.remote_ip6 test.assert_equal(ip.hlim, 255, "IPv6 hop limit") else: ip = packet[IP] - local_ip = test.pg0.local_ip4 - remote_ip = test.pg0.remote_ip4 + local_ip = test.vpp_session.interface.local_ip4 + remote_ip = test.vpp_session.interface.remote_ip4 test.assert_equal(ip.ttl, 255, "IPv4 TTL") test.assert_equal(ip.src, local_ip, "IP source address") test.assert_equal(ip.dst, remote_ip, "IP destination address") @@ -553,28 +623,19 @@ 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") - is_ipv6 = 0 - if test.vpp_session.af == AF_INET6: - is_ipv6 = 1 - test.assert_equal(e.is_ipv6, is_ipv6, "is_ipv6") - if test.vpp_session.af == AF_INET: - test.assert_equal(e.local_addr[:4], test.vpp_session.local_addr_n, - "Local IPv4 address") - test.assert_equal(e.peer_addr[:4], test.vpp_session.peer_addr_n, - "Peer IPv4 address") - else: - test.assert_equal(e.local_addr, test.vpp_session.local_addr_n, - "Local IPv6 address") - test.assert_equal(e.peer_addr, test.vpp_session.peer_addr_n, - "Peer IPv6 address") + + test.assert_equal(str(e.local_addr), test.vpp_session.local_addr, + "Local IPv6 address") + test.assert_equal(str(e.peer_addr), test.vpp_session.peer_addr, + "Peer IPv6 address") test.assert_equal(e.state, expected_state, BFDState) -def wait_for_bfd_packet(test, timeout=1, pcap_time_min=None): +def wait_for_bfd_packet(test, timeout=1, pcap_time_min=None, is_tunnel=False): """ wait for BFD packet and verify its correctness :param timeout: how long to wait @@ -600,6 +661,10 @@ def wait_for_bfd_packet(test, timeout=1, pcap_time_min=None): (p.time, pcap_time_min), p)) else: break + if is_tunnel: + # strip an IP layer and move to the next + p = p[IP].payload + bfd = p[BFD] if bfd is None: raise Exception(ppp("Unexpected or invalid BFD packet:", p)) @@ -622,9 +687,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() @@ -637,6 +703,10 @@ class BFD4TestCase(VppTestCase): super(BFD4TestCase, cls).tearDownClass() raise + @classmethod + def tearDownClass(cls): + super(BFD4TestCase, cls).tearDownClass() + def setUp(self): super(BFD4TestCase, self).setUp() self.factory = AuthKeyFactory() @@ -648,7 +718,7 @@ class BFD4TestCase(VppTestCase): self.vpp_session.add_vpp_config() self.vpp_session.admin_up() self.test_session = BFDTestSession(self, self.pg0, AF_INET) - except: + except BaseException: self.vapi.want_bfd_events(enable_disable=0) raise @@ -693,7 +763,6 @@ class BFD4TestCase(VppTestCase): bfd_session_up(self) bfd_session_down(self) - @unittest.skipUnless(running_extended_tests(), "part of extended tests") def test_hold_up(self): """ hold BFD session up """ bfd_session_up(self) @@ -703,7 +772,6 @@ class BFD4TestCase(VppTestCase): self.assert_equal(len(self.vapi.collect_events()), 0, "number of bfd events") - @unittest.skipUnless(running_extended_tests(), "part of extended tests") def test_slow_timer(self): """ verify slow periodic control frames while session down """ packet_count = 3 @@ -718,7 +786,6 @@ class BFD4TestCase(VppTestCase): time_diff, 0.70, 1.05, "time between slow packets") prev_packet = next_packet - @unittest.skipUnless(running_extended_tests(), "part of extended tests") def test_zero_remote_min_rx(self): """ no packets when zero remote required min rx interval """ bfd_session_up(self) @@ -735,7 +802,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( @@ -743,7 +810,6 @@ class BFD4TestCase(VppTestCase): self.assert_equal( len(self.vapi.collect_events()), 0, "number of bfd events") - @unittest.skipUnless(running_extended_tests(), "part of extended tests") def test_conn_down(self): """ verify session goes down after inactivity """ bfd_session_up(self) @@ -753,7 +819,16 @@ class BFD4TestCase(VppTestCase): e = self.vapi.wait_for_event(1, "bfd_udp_session_details") verify_event(self, e, expected_state=BFDState.down) - @unittest.skipUnless(running_extended_tests(), "part of extended tests") + def test_peer_discr_reset_sess_down(self): + """ peer discriminator reset after session goes down """ + bfd_session_up(self) + detection_time = self.test_session.detect_mult *\ + self.vpp_session.required_min_rx / USEC_IN_SEC + self.sleep(detection_time, "waiting for BFD session time-out") + self.test_session.my_discriminator = 0 + wait_for_bfd_packet(self, + pcap_time_min=time.time() - self.vpp_clock_offset) + def test_large_required_min_rx(self): """ large remote required min rx interval """ bfd_session_up(self) @@ -783,7 +858,6 @@ class BFD4TestCase(VppTestCase): break self.assert_equal(count, 0, "number of packets received") - @unittest.skipUnless(running_extended_tests(), "part of extended tests") def test_immediate_remote_min_rx_reduction(self): """ immediately honor remote required min rx reduction """ self.vpp_session.remove_vpp_config() @@ -816,7 +890,6 @@ class BFD4TestCase(VppTestCase): "time between BFD packets") reference_packet = p - @unittest.skipUnless(running_extended_tests(), "part of extended tests") def test_modify_req_min_rx_double(self): """ modify session - double required min rx """ bfd_session_up(self) @@ -846,7 +919,6 @@ class BFD4TestCase(VppTestCase): self.assert_in_range(time_to_event, .9 * timeout, 1.1 * timeout, "session timeout") - @unittest.skipUnless(running_extended_tests(), "part of extended tests") def test_modify_req_min_rx_halve(self): """ modify session - halve required min rx """ self.vpp_session.modify_parameters( @@ -861,10 +933,11 @@ class BFD4TestCase(VppTestCase): # halve required min rx old_required_min_rx = self.vpp_session.required_min_rx self.vpp_session.modify_parameters( - required_min_rx=0.5 * self.vpp_session.required_min_rx) + required_min_rx=self.vpp_session.required_min_rx // 2) # 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,18 +949,18 @@ 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, "time before bfd session goes down") verify_event(self, e, expected_state=BFDState.down) - @unittest.skipUnless(running_extended_tests(), "part of extended tests") def test_modify_detect_mult(self): """ modify detect multiplier """ bfd_session_up(self) @@ -911,7 +984,6 @@ class BFD4TestCase(VppTestCase): self.assertNotIn("P", p.sprintf("%BFD.flags%"), "Poll bit not set in BFD packet") - @unittest.skipUnless(running_extended_tests(), "part of extended tests") def test_queued_poll(self): """ test poll sequence queueing """ bfd_session_up(self) @@ -977,7 +1049,8 @@ class BFD4TestCase(VppTestCase): self.assertNotIn("P", p.sprintf("%BFD.flags%"), "Poll bit set in BFD packet") - @unittest.skipUnless(running_extended_tests(), "part of extended tests") + # returning inconsistent results requiring retries in per-patch tests + @unittest.skipUnless(running_extended_tests, "part of extended tests") def test_poll_response(self): """ test correct response to control frame with poll bit set """ bfd_session_up(self) @@ -988,7 +1061,6 @@ class BFD4TestCase(VppTestCase): self, pcap_time_min=time.time() - self.vpp_clock_offset) self.assertIn("F", final.sprintf("%BFD.flags%")) - @unittest.skipUnless(running_extended_tests(), "part of extended tests") def test_no_periodic_if_remote_demand(self): """ no periodic frames outside poll sequence if remote demand set """ bfd_session_up(self) @@ -1001,7 +1073,7 @@ class BFD4TestCase(VppTestCase): / USEC_IN_SEC count = 0 for dummy in range(self.test_session.detect_mult * 2): - time.sleep(transmit_time) + self.sleep(transmit_time) self.test_session.send_packet(demand) try: p = wait_for_bfd_packet(self, timeout=0) @@ -1054,8 +1126,8 @@ class BFD4TestCase(VppTestCase): udp_sport_rx += 1 # need to compare the hex payload here, otherwise BFD_vpp_echo # gets in way - self.assertEqual(str(p[UDP].payload), - str(echo_packet[UDP].payload), + self.assertEqual(scapy.compat.raw(p[UDP].payload), + scapy.compat.raw(echo_packet[UDP].payload), "Received packet is not the echo packet sent") self.assert_equal(udp_sport_tx, udp_sport_rx, "UDP source port (== " "ECHO packet identifier for test purposes)") @@ -1063,13 +1135,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 +1149,10 @@ class BFD4TestCase(VppTestCase): self.assert_equal(p[BFD].required_min_rx_interval, self.vpp_session.required_min_rx, "BFD required min rx interval") - self.vapi.bfd_udp_set_echo_source(self.loopback0.sw_if_index) + self.test_session.send_packet() + self.vapi.bfd_udp_set_echo_source( + sw_if_index=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 @@ -1095,9 +1170,12 @@ class BFD4TestCase(VppTestCase): 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" @@ -1108,16 +1186,17 @@ 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 - self.vapi.bfd_udp_set_echo_source(self.loopback0.sw_if_index) + self.vapi.bfd_udp_set_echo_source( + sw_if_index=self.loopback0.sw_if_index) # echo function should be used now, but we will drop the echo packets verified_diag = False for dummy in range(3): @@ -1149,13 +1228,13 @@ class BFD4TestCase(VppTestCase): self.assert_equal(events[0].state, BFDState.down, BFDState) self.assertTrue(verified_diag, "Incorrect diagnostics code received") - @unittest.skipUnless(running_extended_tests(), "part of extended tests") 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) + self.vapi.bfd_udp_set_echo_source( + sw_if_index=self.loopback0.sw_if_index) # wait for first echo packet while True: p = self.pg0.wait_for_packet(1) @@ -1181,13 +1260,13 @@ class BFD4TestCase(VppTestCase): events = self.vapi.collect_events() self.assert_equal(len(events), 0, "number of bfd events") - @unittest.skipUnless(running_extended_tests(), "part of extended tests") 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) + self.vapi.bfd_udp_set_echo_source( + sw_if_index=self.loopback0.sw_if_index) # wait for first echo packet while True: p = self.pg0.wait_for_packet(1) @@ -1213,12 +1292,12 @@ class BFD4TestCase(VppTestCase): events = self.vapi.collect_events() self.assert_equal(len(events), 0, "number of bfd events") - @unittest.skipUnless(running_extended_tests(), "part of extended tests") 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.vapi.bfd_udp_set_echo_source(self.loopback0.sw_if_index) + self.test_session.update(required_min_echo_rx=150000) + self.vapi.bfd_udp_set_echo_source( + sw_if_index=self.loopback0.sw_if_index) self.test_session.send_packet() # should be turned on - loopback echo packets echo_packet = None @@ -1266,12 +1345,12 @@ class BFD4TestCase(VppTestCase): self.test_session.send_packet() self.assertTrue(timeout_ok, "Expected timeout event didn't occur") - @unittest.skipUnless(running_extended_tests(), "part of extended tests") 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.vapi.bfd_udp_set_echo_source(self.loopback0.sw_if_index) + self.test_session.update(required_min_echo_rx=150000) + self.vapi.bfd_udp_set_echo_source( + sw_if_index=self.loopback0.sw_if_index) self.test_session.send_packet() # should be turned on - loopback echo packets timeout_at = None @@ -1316,7 +1395,6 @@ class BFD4TestCase(VppTestCase): self.test_session.send_packet() self.assertTrue(timeout_ok, "Expected timeout event didn't occur") - @unittest.skipUnless(running_extended_tests(), "part of extended tests") def test_admin_up_down(self): """ put session admin-up and admin-down """ bfd_session_up(self) @@ -1354,7 +1432,6 @@ class BFD4TestCase(VppTestCase): e = self.vapi.wait_for_event(1, "bfd_udp_session_details") verify_event(self, e, expected_state=BFDState.up) - @unittest.skipUnless(running_extended_tests(), "part of extended tests") def test_config_change_remote_demand(self): """ configuration change while peer in demand mode """ bfd_session_up(self) @@ -1378,7 +1455,7 @@ class BFD4TestCase(VppTestCase): / USEC_IN_SEC count = 0 for dummy in range(self.test_session.detect_mult * 2): - time.sleep(transmit_time) + self.sleep(transmit_time) self.test_session.send_packet(demand) try: p = wait_for_bfd_packet(self, timeout=0) @@ -1392,6 +1469,20 @@ 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()) + class BFD6TestCase(VppTestCase): """Bidirectional Forwarding Detection (BFD) (IPv6) """ @@ -1404,13 +1495,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() @@ -1419,6 +1511,10 @@ class BFD6TestCase(VppTestCase): super(BFD6TestCase, cls).tearDownClass() raise + @classmethod + def tearDownClass(cls): + super(BFD6TestCase, cls).tearDownClass() + def setUp(self): super(BFD6TestCase, self).setUp() self.factory = AuthKeyFactory() @@ -1432,7 +1528,7 @@ class BFD6TestCase(VppTestCase): self.vpp_session.admin_up() self.test_session = BFDTestSession(self, self.pg0, AF_INET6) self.logger.debug(self.vapi.cli("show adj nbr")) - except: + except BaseException: self.vapi.want_bfd_events(enable_disable=0) raise @@ -1472,7 +1568,6 @@ class BFD6TestCase(VppTestCase): self.test_session.send_packet() self.assert_equal(self.vpp_session.state, BFDState.up, BFDState) - @unittest.skipUnless(running_extended_tests(), "part of extended tests") def test_hold_up(self): """ hold BFD session up """ bfd_session_up(self) @@ -1522,8 +1617,8 @@ class BFD6TestCase(VppTestCase): udp_sport_rx += 1 # need to compare the hex payload here, otherwise BFD_vpp_echo # gets in way - self.assertEqual(str(p[UDP].payload), - str(echo_packet[UDP].payload), + self.assertEqual(scapy.compat.raw(p[UDP].payload), + scapy.compat.raw(echo_packet[UDP].payload), "Received packet is not the echo packet sent") self.assert_equal(udp_sport_tx, udp_sport_rx, "UDP source port (== " "ECHO packet identifier for test purposes)") @@ -1531,15 +1626,15 @@ class BFD6TestCase(VppTestCase): "ECHO packet identifier for test purposes)") 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( @@ -1547,7 +1642,10 @@ class BFD6TestCase(VppTestCase): self.assert_equal(p[BFD].required_min_rx_interval, self.vpp_session.required_min_rx, "BFD required min rx interval") - self.vapi.bfd_udp_set_echo_source(self.loopback0.sw_if_index) + self.test_session.send_packet() + self.vapi.bfd_udp_set_echo_source( + sw_if_index=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 @@ -1565,9 +1663,12 @@ class BFD6TestCase(VppTestCase): 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" @@ -1578,6 +1679,208 @@ 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()) + + +class BFDFIBTestCase(VppTestCase): + """ BFD-FIB interactions (IPv6) """ + + vpp_session = None + test_session = None + + @classmethod + def setUpClass(cls): + super(BFDFIBTestCase, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(BFDFIBTestCase, cls).tearDownClass() + + 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=False) + + 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(b'\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(b'\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)]) + ip_2002_s_64 = VppIpRoute(self, "2002::", 64, + [VppRoutePath(self.pg0.remote_ip6, + 0xffffffff)]) + 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 BFDTunTestCase(VppTestCase): + """ BFD over GRE tunnel """ + + vpp_session = None + test_session = None + + @classmethod + def setUpClass(cls): + super(BFDTunTestCase, cls).setUpClass() + + @classmethod + def tearDownClass(cls): + super(BFDTunTestCase, cls).tearDownClass() + + def setUp(self): + super(BFDTunTestCase, 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_ip4() + i.resolve_arp() + + def tearDown(self): + if not self.vpp_dead: + self.vapi.want_bfd_events(enable_disable=0) + + super(BFDTunTestCase, 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_bfd_o_gre(self): + """ BFD-o-GRE """ + + # A GRE interface over which to run a BFD session + gre_if = VppGreInterface(self, + self.pg0.local_ip4, + self.pg0.remote_ip4) + gre_if.add_vpp_config() + gre_if.admin_up() + gre_if.config_ip4() + + # bring the session up now the routes are present + self.vpp_session = VppBFDUDPSession(self, + gre_if, + gre_if.remote_ip4, + is_tunnel=True) + self.vpp_session.add_vpp_config() + self.vpp_session.admin_up() + + self.test_session = BFDTestSession( + self, gre_if, AF_INET, + tunnel_header=(IP(src=self.pg0.remote_ip4, + dst=self.pg0.local_ip4) / + GRE()), + phy_interface=self.pg0) + + # packets to match against both of the routes + p = [(Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) / + IP(src=self.pg0.remote_ip4, dst=gre_if.remote_ip4) / + UDP(sport=1234, dport=1234) / + Raw(b'\xa5' * 100))] + + # session is up - traffic passes + bfd_session_up(self) + + self.send_and_expect(self.pg0, p, self.pg0) + + # bring session down + bfd_session_down(self) class BFDSHA1TestCase(VppTestCase): @@ -1591,6 +1894,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() @@ -1601,6 +1905,10 @@ class BFDSHA1TestCase(VppTestCase): super(BFDSHA1TestCase, cls).tearDownClass() raise + @classmethod + def tearDownClass(cls): + super(BFDSHA1TestCase, cls).tearDownClass() + def setUp(self): super(BFDSHA1TestCase, self).setUp() self.factory = AuthKeyFactory() @@ -1609,7 +1917,7 @@ class BFDSHA1TestCase(VppTestCase): def tearDown(self): if not self.vpp_dead: - self.vapi.want_bfd_events(enable_disable=0) + self.vapi.want_bfd_events(enable_disable=False) self.vapi.collect_events() # clear the event queue super(BFDSHA1TestCase, self).tearDown() @@ -1627,7 +1935,6 @@ class BFDSHA1TestCase(VppTestCase): bfd_key_id=self.vpp_session.bfd_key_id) bfd_session_up(self) - @unittest.skipUnless(running_extended_tests(), "part of extended tests") def test_hold_up(self): """ hold BFD session up """ key = self.factory.create_random_key(self) @@ -1646,7 +1953,6 @@ class BFDSHA1TestCase(VppTestCase): self.test_session.send_packet() self.assert_equal(self.vpp_session.state, BFDState.up, BFDState) - @unittest.skipUnless(running_extended_tests(), "part of extended tests") def test_hold_up_meticulous(self): """ hold BFD session up - meticulous auth """ key = self.factory.create_random_key( @@ -1668,7 +1974,6 @@ class BFDSHA1TestCase(VppTestCase): self.test_session.send_packet() self.assert_equal(self.vpp_session.state, BFDState.up, BFDState) - @unittest.skipUnless(running_extended_tests(), "part of extended tests") def test_send_bad_seq_number(self): """ session is not kept alive by msgs with bad sequence numbers""" key = self.factory.create_random_key( @@ -1731,7 +2036,6 @@ class BFDSHA1TestCase(VppTestCase): wait_for_bfd_packet(self) self.assert_equal(self.vpp_session.state, BFDState.up, BFDState) - @unittest.skipUnless(running_extended_tests(), "part of extended tests") def test_mismatch_auth(self): """ session is not brought down by unauthenticated msg """ key = self.factory.create_random_key(self) @@ -1746,7 +2050,6 @@ class BFDSHA1TestCase(VppTestCase): legitimate_test_session, rogue_test_session) - @unittest.skipUnless(running_extended_tests(), "part of extended tests") def test_mismatch_bfd_key_id(self): """ session is not brought down by msg with non-existent key-id """ key = self.factory.create_random_key(self) @@ -1766,7 +2069,6 @@ class BFDSHA1TestCase(VppTestCase): legitimate_test_session, rogue_test_session) - @unittest.skipUnless(running_extended_tests(), "part of extended tests") def test_mismatched_auth_type(self): """ session is not brought down by msg with wrong auth type """ key = self.factory.create_random_key(self) @@ -1783,7 +2085,6 @@ class BFDSHA1TestCase(VppTestCase): vpp_session, legitimate_test_session, rogue_test_session, {'auth_type': BFDAuthType.keyed_md5}) - @unittest.skipUnless(running_extended_tests(), "part of extended tests") def test_restart(self): """ simulate remote peer restart and resynchronization """ key = self.factory.create_random_key( @@ -1809,10 +2110,10 @@ class BFDSHA1TestCase(VppTestCase): self.test_session.vpp_seq_number = None # now throw away any pending packets self.pg0.enable_capture() + self.test_session.my_discriminator = 0 bfd_session_up(self) -@unittest.skipUnless(running_extended_tests(), "part of extended tests") class BFDAuthOnOffTestCase(VppTestCase): """Bidirectional Forwarding Detection (BFD) (changing auth) """ @@ -1823,6 +2124,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() @@ -1833,6 +2135,10 @@ class BFDAuthOnOffTestCase(VppTestCase): super(BFDAuthOnOffTestCase, cls).tearDownClass() raise + @classmethod + def tearDownClass(cls): + super(BFDAuthOnOffTestCase, cls).tearDownClass() + def setUp(self): super(BFDAuthOnOffTestCase, self).setUp() self.factory = AuthKeyFactory() @@ -1841,7 +2147,7 @@ class BFDAuthOnOffTestCase(VppTestCase): def tearDown(self): if not self.vpp_dead: - self.vapi.want_bfd_events(enable_disable=0) + self.vapi.want_bfd_events(enable_disable=False) self.vapi.collect_events() # clear the event queue super(BFDAuthOnOffTestCase, self).tearDown() @@ -2020,7 +2326,6 @@ class BFDAuthOnOffTestCase(VppTestCase): "number of bfd events") -@unittest.skipUnless(running_extended_tests(), "part of extended tests") class BFDCLITestCase(VppTestCase): """Bidirectional Forwarding Detection (BFD) (CLI) """ pg0 = None @@ -2028,7 +2333,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() @@ -2040,6 +2345,10 @@ class BFDCLITestCase(VppTestCase): super(BFDCLITestCase, cls).tearDownClass() raise + @classmethod + def tearDownClass(cls): + super(BFDCLITestCase, cls).tearDownClass() + def setUp(self): super(BFDCLITestCase, self).setUp() self.factory = AuthKeyFactory() @@ -2047,7 +2356,7 @@ class BFDCLITestCase(VppTestCase): def tearDown(self): try: - self.vapi.want_bfd_events(enable_disable=0) + self.vapi.want_bfd_events(enable_disable=False) except UnexpectedApiReturnValueError: # some tests aren't subscribed, so this is not an issue pass @@ -2062,7 +2371,11 @@ class BFDCLITestCase(VppTestCase): def cli_verify_response(self, cli, expected): """ execute a CLI, asserting that the response matches expectation """ - self.assert_equal(self.vapi.cli(cli).strip(), + try: + reply = self.vapi.cli(cli) + except CliFailedCommandError as cli_error: + reply = str(cli_error) + self.assert_equal(reply.strip(), expected, "CLI command response") @@ -2089,7 +2402,7 @@ class BFDCLITestCase(VppTestCase): self.cli_verify_no_response( "bfd key set conf-key-id %s type keyed-sha1 secret %s" % (k.conf_key_id, - "".join("{:02x}".format(ord(c)) for c in k.key))) + "".join("{:02x}".format(scapy.compat.orb(c)) for c in k.key))) self.assertTrue(k.query_vpp_config()) self.vpp_session = VppBFDUDPSession( self, self.pg0, self.pg0.remote_ip4, sha1_key=k) @@ -2106,7 +2419,7 @@ class BFDCLITestCase(VppTestCase): self.cli_verify_response( "bfd key set conf-key-id %s type keyed-sha1 secret %s" % (k.conf_key_id, - "".join("{:02x}".format(ord(c)) for c in k2.key)), + "".join("{:02x}".format(scapy.compat.orb(c)) for c in k2.key)), "bfd key set: `bfd_auth_set_key' API call failed, " "rv=-103:BFD object in use") # manipulating the session using old secret should still work @@ -2125,7 +2438,7 @@ class BFDCLITestCase(VppTestCase): self.cli_verify_no_response( "bfd key set conf-key-id %s type meticulous-keyed-sha1 secret %s" % (k.conf_key_id, - "".join("{:02x}".format(ord(c)) for c in k.key))) + "".join("{:02x}".format(scapy.compat.orb(c)) for c in k.key))) self.assertTrue(k.query_vpp_config()) self.vpp_session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip6, af=AF_INET6, @@ -2144,7 +2457,7 @@ class BFDCLITestCase(VppTestCase): self.cli_verify_response( "bfd key set conf-key-id %s type keyed-sha1 secret %s" % (k.conf_key_id, - "".join("{:02x}".format(ord(c)) for c in k2.key)), + "".join("{:02x}".format(scapy.compat.orb(c)) for c in k2.key)), "bfd key set: `bfd_auth_set_key' API call failed, " "rv=-103:BFD object in use") # manipulating the session using old secret should still work @@ -2402,7 +2715,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", @@ -2436,5 +2749,6 @@ class BFDCLITestCase(VppTestCase): self.cli_verify_response("show bfd echo-source", "UDP echo source is not set.") + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner)