X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=test%2Ftest_bfd.py;h=e7ebb214d9a0bda4a39c092e95e7366ce8c6036e;hb=refs%2Fchanges%2F36%2F5536%2F4;hp=0ba0b46dee8e13bce2f68ee2190ba25d3085c2dd;hpb=d3ba515d99551ee87096931b34a5b8d4222b385d;p=vpp.git diff --git a/test/test_bfd.py b/test/test_bfd.py index 0ba0b46dee8..e7ebb214d9a 100644 --- a/test/test_bfd.py +++ b/test/test_bfd.py @@ -6,16 +6,19 @@ import unittest import hashlib import binascii import time -from random import randint, shuffle -from socket import AF_INET, AF_INET6 +from struct import pack, unpack +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 scapy.layers.inet import UDP, IP from scapy.layers.inet6 import IPv6 from bfd import VppBFDAuthKey, BFD, BFDAuthType, VppBFDUDPSession, \ - BFDDiagCode, BFDState + BFDDiagCode, BFDState, BFD_vpp_echo from framework import VppTestCase, VppTestRunner from vpp_pg_interface import CaptureTimeoutError from util import ppp +from vpp_papi_provider import UnexpectedApiReturnValueError USEC_IN_SEC = 1000000 @@ -265,6 +268,7 @@ class BFDTestSession(object): self.my_discriminator = 0 self.desired_min_tx = 100000 self.required_min_rx = 100000 + self.required_min_echo_rx = None self.detect_mult = detect_mult self.diag = BFDDiagCode.no_diagnostic self.your_discriminator = None @@ -279,24 +283,27 @@ class BFDTestSession(object): self.our_seq_number += 1 def update(self, my_discriminator=None, your_discriminator=None, - desired_min_tx=None, required_min_rx=None, detect_mult=None, + desired_min_tx=None, required_min_rx=None, + required_min_echo_rx=None, detect_mult=None, diag=None, state=None, auth_type=None): """ update BFD parameters associated with session """ - if my_discriminator: + if my_discriminator is not None: self.my_discriminator = my_discriminator - if your_discriminator: + if your_discriminator is not None: self.your_discriminator = your_discriminator - if required_min_rx: + if required_min_rx is not None: self.required_min_rx = required_min_rx - if desired_min_tx: + if required_min_echo_rx is not None: + self.required_min_echo_rx = required_min_echo_rx + if desired_min_tx is not None: self.desired_min_tx = desired_min_tx - if detect_mult: + if detect_mult is not None: self.detect_mult = detect_mult - if diag: + if diag is not None: self.diag = diag - if state: + if state is not None: self.state = state - if auth_type: + if auth_type is not None: self.auth_type = auth_type def fill_packet_fields(self, packet): @@ -315,6 +322,11 @@ class BFDTestSession(object): "BFD: setting packet.required_min_rx_interval=%s", self.required_min_rx) bfd.required_min_rx_interval = self.required_min_rx + if self.required_min_echo_rx: + self.test.logger.debug( + "BFD: setting packet.required_min_echo_rx=%s", + self.required_min_echo_rx) + bfd.required_min_echo_rx_interval = self.required_min_echo_rx if self.desired_min_tx: self.test.logger.debug( "BFD: setting packet.desired_min_tx_interval=%s", @@ -451,19 +463,25 @@ def bfd_session_up(test): test.vpp_clock_offset) if old_offset: test.assertAlmostEqual( - old_offset, test.vpp_clock_offset, delta=0.1, + old_offset, test.vpp_clock_offset, delta=0.5, msg="vpp clock offset not stable (new: %s, old: %s)" % (test.vpp_clock_offset, old_offset)) test.logger.info("BFD: Sending Init") test.test_session.update(my_discriminator=randint(0, 40000000), your_discriminator=p[BFD].my_discriminator, state=BFDState.init) + if test.test_session.sha1_key and test.test_session.sha1_key.auth_type == \ + BFDAuthType.meticulous_keyed_sha1: + test.test_session.inc_seq_num() test.test_session.send_packet() test.logger.info("BFD: Waiting for event") e = test.vapi.wait_for_event(1, "bfd_udp_session_details") verify_event(test, e, expected_state=BFDState.up) test.logger.info("BFD: Session is Up") test.test_session.update(state=BFDState.up) + if test.test_session.sha1_key and test.test_session.sha1_key.auth_type == \ + BFDAuthType.meticulous_keyed_sha1: + test.test_session.inc_seq_num() test.test_session.send_packet() test.assert_equal(test.vpp_session.state, BFDState.up, BFDState) @@ -472,6 +490,9 @@ def bfd_session_down(test): """ Bring BFD session down """ test.assert_equal(test.vpp_session.state, BFDState.up, BFDState) test.test_session.update(state=BFDState.down) + if test.test_session.sha1_key and test.test_session.sha1_key.auth_type == \ + BFDAuthType.meticulous_keyed_sha1: + test.test_session.inc_seq_num() test.test_session.send_packet() test.logger.info("BFD: Waiting for event") e = test.vapi.wait_for_event(1, "bfd_udp_session_details") @@ -480,6 +501,30 @@ def bfd_session_down(test): test.assert_equal(test.vpp_session.state, BFDState.down, BFDState) +def verify_bfd_session_config(test, session, state=None): + dump = session.get_bfd_udp_session_dump_entry() + test.assertIsNotNone(dump) + # since dump is not none, we have verified that sw_if_index and addresses + # are valid (in get_bfd_udp_session_dump_entry) + if state: + test.assert_equal(dump.state, state, "session state") + test.assert_equal(dump.required_min_rx, session.required_min_rx, + "required min rx interval") + test.assert_equal(dump.desired_min_tx, session.desired_min_tx, + "desired min tx interval") + test.assert_equal(dump.detect_mult, session.detect_mult, + "detect multiplier") + if session.sha1_key is None: + test.assert_equal(dump.is_authenticated, 0, "is_authenticated flag") + else: + test.assert_equal(dump.is_authenticated, 1, "is_authenticated flag") + test.assert_equal(dump.bfd_key_id, session.bfd_key_id, + "bfd key id") + test.assert_equal(dump.conf_key_id, + session.sha1_key.conf_key_id, + "config key id") + + def verify_ip(test, packet): """ Verify correctness of IP layer. """ if test.vpp_session.af == AF_INET6: @@ -578,6 +623,10 @@ class BFD4TestCase(VppTestCase): super(BFD4TestCase, cls).setUpClass() try: cls.create_pg_interfaces([0]) + cls.create_loopback_interfaces([0]) + cls.loopback0 = cls.lo_interfaces[0] + cls.loopback0.config_ip4() + cls.loopback0.admin_up() cls.pg0.config_ip4() cls.pg0.configure_ipv4_neighbors() cls.pg0.admin_up() @@ -612,6 +661,32 @@ class BFD4TestCase(VppTestCase): """ bring BFD session up """ bfd_session_up(self) + def test_session_up_by_ip(self): + """ bring BFD session up - first frame looked up by address pair """ + self.logger.info("BFD: Sending Slow control frame") + self.test_session.update(my_discriminator=randint(0, 40000000)) + self.test_session.send_packet() + self.pg0.enable_capture() + p = self.pg0.wait_for_packet(1) + self.assert_equal(p[BFD].your_discriminator, + self.test_session.my_discriminator, + "BFD - your discriminator") + self.assert_equal(p[BFD].state, BFDState.init, BFDState) + self.test_session.update(your_discriminator=p[BFD].my_discriminator, + state=BFDState.up) + self.logger.info("BFD: Waiting for event") + e = self.vapi.wait_for_event(1, "bfd_udp_session_details") + verify_event(self, e, expected_state=BFDState.init) + self.logger.info("BFD: Sending Up") + self.test_session.send_packet() + self.logger.info("BFD: Waiting for event") + e = self.vapi.wait_for_event(1, "bfd_udp_session_details") + verify_event(self, e, expected_state=BFDState.up) + self.logger.info("BFD: Session is Up") + self.test_session.update(state=BFDState.up) + self.test_session.send_packet() + self.assert_equal(self.vpp_session.state, BFDState.up, BFDState) + def test_session_down(self): """ bring BFD session down """ bfd_session_up(self) @@ -645,35 +720,31 @@ class BFD4TestCase(VppTestCase): bfd_session_up(self) self.test_session.update(required_min_rx=0) self.test_session.send_packet() - cap = 2 * self.vpp_session.desired_min_tx *\ - self.test_session.detect_mult - time_mark = time.time() - count = 0 - # busy wait here, trying to collect a packet or event, vpp is not - # allowed to send packets and the session will timeout first - so the - # Up->Down event must arrive before any packets do - while time.time() < time_mark + cap / USEC_IN_SEC: + for dummy in range(self.test_session.detect_mult): + self.sleep(self.vpp_session.required_min_rx / USEC_IN_SEC, + "sleep before transmitting bfd packet") + self.test_session.send_packet() try: - p = wait_for_bfd_packet( - self, timeout=0, - pcap_time_min=time_mark - self.vpp_clock_offset) + p = wait_for_bfd_packet(self, timeout=0) self.logger.error(ppp("Received unexpected packet:", p)) - count += 1 except CaptureTimeoutError: pass - events = self.vapi.collect_events() - if len(events) > 0: - verify_event(self, events[0], BFDState.down) - break - self.assert_equal(count, 0, "number of packets received") + self.assert_equal( + len(self.vapi.collect_events()), 0, "number of bfd events") + self.test_session.update(required_min_rx=100000) + for dummy in range(3): + self.test_session.send_packet() + wait_for_bfd_packet( + self, timeout=self.test_session.required_min_rx / USEC_IN_SEC) + self.assert_equal( + len(self.vapi.collect_events()), 0, "number of bfd events") def test_conn_down(self): """ verify session goes down after inactivity """ bfd_session_up(self) - for dummy in range(self.test_session.detect_mult): - wait_for_bfd_packet(self) - self.assert_equal(len(self.vapi.collect_events()), 0, - "number of bfd events") + 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") e = self.vapi.wait_for_event(1, "bfd_udp_session_details") verify_event(self, e, expected_state=BFDState.down) @@ -799,7 +870,7 @@ class BFD4TestCase(VppTestCase): before = time.time() e = self.vapi.wait_for_event(1, "bfd_udp_session_details") after = time.time() - detection_time = self.vpp_session.detect_mult *\ + detection_time = self.test_session.detect_mult *\ self.vpp_session.required_min_rx / USEC_IN_SEC self.assert_in_range(after - before, 0.9 * detection_time, @@ -807,10 +878,6 @@ class BFD4TestCase(VppTestCase): "time before bfd session goes down") verify_event(self, e, expected_state=BFDState.down) - def test_modify_des_min_tx(self): - """ modify desired min tx interval """ - pass - def test_modify_detect_mult(self): """ modify detect multiplier """ bfd_session_up(self) @@ -834,9 +901,83 @@ class BFD4TestCase(VppTestCase): self.assertNotIn("P", p.sprintf("%BFD.flags%"), "Poll bit not set in BFD packet") + def test_queued_poll(self): + """ test poll sequence queueing """ + bfd_session_up(self) + p = wait_for_bfd_packet(self) + self.vpp_session.modify_parameters( + required_min_rx=2 * self.vpp_session.required_min_rx) + p = wait_for_bfd_packet(self) + poll_sequence_start = time.time() + poll_sequence_length_min = 0.5 + send_final_after = time.time() + poll_sequence_length_min + # poll bit needs to be set + self.assertIn("P", p.sprintf("%BFD.flags%"), + "Poll bit not set in BFD packet") + self.assert_equal(p[BFD].required_min_rx_interval, + self.vpp_session.required_min_rx, + "BFD required min rx interval") + self.vpp_session.modify_parameters( + required_min_rx=2 * self.vpp_session.required_min_rx) + # 2nd poll sequence should be queued now + # don't send the reply back yet, wait for some time to emulate + # longer round-trip time + packet_count = 0 + while time.time() < send_final_after: + self.test_session.send_packet() + p = wait_for_bfd_packet(self) + self.assert_equal(len(self.vapi.collect_events()), 0, + "number of bfd events") + self.assert_equal(p[BFD].required_min_rx_interval, + self.vpp_session.required_min_rx, + "BFD required min rx interval") + packet_count += 1 + # poll bit must be set + self.assertIn("P", p.sprintf("%BFD.flags%"), + "Poll bit not set in BFD packet") + final = self.test_session.create_packet() + final[BFD].flags = "F" + self.test_session.send_packet(final) + # finish 1st with final + poll_sequence_length = time.time() - poll_sequence_start + # vpp must wait for some time before starting new poll sequence + poll_no_2_started = False + for dummy in range(2 * packet_count): + p = wait_for_bfd_packet(self) + self.assert_equal(len(self.vapi.collect_events()), 0, + "number of bfd events") + if "P" in p.sprintf("%BFD.flags%"): + poll_no_2_started = True + if time.time() < poll_sequence_start + poll_sequence_length: + raise Exception("VPP started 2nd poll sequence too soon") + final = self.test_session.create_packet() + final[BFD].flags = "F" + self.test_session.send_packet(final) + break + else: + self.test_session.send_packet() + self.assertTrue(poll_no_2_started, "2nd poll sequence not performed") + # finish 2nd with final + final = self.test_session.create_packet() + final[BFD].flags = "F" + self.test_session.send_packet(final) + p = wait_for_bfd_packet(self) + # poll bit must not be set + self.assertNotIn("P", p.sprintf("%BFD.flags%"), + "Poll bit set in BFD packet") + + def test_poll_response(self): + """ test correct response to control frame with poll bit set """ + bfd_session_up(self) + poll = self.test_session.create_packet() + poll[BFD].flags = "P" + self.test_session.send_packet(poll) + final = wait_for_bfd_packet( + self, pcap_time_min=time.time() - self.vpp_clock_offset) + self.assertIn("F", final.sprintf("%BFD.flags%")) + def test_no_periodic_if_remote_demand(self): """ no periodic frames outside poll sequence if remote demand set """ - self.test_session.update(detect_mult=10) bfd_session_up(self) demand = self.test_session.create_packet() demand[BFD].flags = "D" @@ -846,7 +987,370 @@ class BFD4TestCase(VppTestCase): self.test_session.desired_min_tx) \ / USEC_IN_SEC count = 0 - for dummy in range(self.test_session.detect_mult): + for dummy in range(self.test_session.detect_mult * 2): + time.sleep(transmit_time) + self.test_session.send_packet(demand) + try: + p = wait_for_bfd_packet(self, timeout=0) + self.logger.error(ppp("Received unexpected packet:", p)) + count += 1 + except CaptureTimeoutError: + pass + events = self.vapi.collect_events() + for e in events: + self.logger.error("Received unexpected event: %s", e) + self.assert_equal(count, 0, "number of packets received") + self.assert_equal(len(events), 0, "number of events received") + + def test_echo_looped_back(self): + """ echo packets looped back """ + # don't need a session in this case.. + self.vpp_session.remove_vpp_config() + self.pg0.enable_capture() + echo_packet_count = 10 + # random source port low enough to increment a few times.. + udp_sport_tx = randint(1, 50000) + udp_sport_rx = udp_sport_tx + echo_packet = (Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IP(src=self.pg0.remote_ip4, + dst=self.pg0.remote_ip4) / + UDP(dport=BFD.udp_dport_echo) / + Raw("this should be looped back")) + for dummy in range(echo_packet_count): + self.sleep(.01, "delay between echo packets") + echo_packet[UDP].sport = udp_sport_tx + udp_sport_tx += 1 + self.logger.debug(ppp("Sending packet:", echo_packet)) + self.pg0.add_stream(echo_packet) + self.pg_start() + for dummy in range(echo_packet_count): + p = self.pg0.wait_for_packet(1) + self.logger.debug(ppp("Got packet:", p)) + ether = p[Ether] + self.assert_equal(self.pg0.remote_mac, + ether.dst, "Destination MAC") + self.assert_equal(self.pg0.local_mac, ether.src, "Source MAC") + ip = p[IP] + self.assert_equal(self.pg0.remote_ip4, ip.dst, "Destination IP") + self.assert_equal(self.pg0.remote_ip4, ip.src, "Destination IP") + udp = p[UDP] + self.assert_equal(udp.dport, BFD.udp_dport_echo, + "UDP destination port") + self.assert_equal(udp.sport, udp_sport_rx, "UDP source port") + 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), + "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)") + + def test_echo(self): + """ echo function """ + bfd_session_up(self) + self.test_session.update(required_min_echo_rx=50000) + 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 + self.sleep(sleep, "delay before sending bfd packet") + self.test_session.send_packet() + p = wait_for_bfd_packet( + self, pcap_time_min=time.time() - self.vpp_clock_offset) + 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) + # should be turned on - loopback echo packets + for dummy in range(3): + loop_until = time.time() + 0.75 * detection_time + while time.time() < loop_until: + p = self.pg0.wait_for_packet(1) + self.logger.debug(ppp("Got packet:", p)) + if p[UDP].dport == BFD.udp_dport_echo: + self.assert_equal( + p[IP].dst, self.pg0.local_ip4, "BFD ECHO dst IP") + 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.pg0.add_stream(p) + self.pg_start() + elif p.haslayer(BFD): + 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" + self.test_session.send_packet(final) + else: + raise Exception(ppp("Received unknown packet:", p)) + + self.assert_equal(len(self.vapi.collect_events()), 0, + "number of bfd events") + self.test_session.send_packet() + + 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.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) + # echo function should be used now, but we will drop the echo packets + verified_diag = False + for dummy in range(3): + loop_until = time.time() + 0.75 * detection_time + while time.time() < loop_until: + p = self.pg0.wait_for_packet(1) + self.logger.debug(ppp("Got packet:", p)) + if p[UDP].dport == BFD.udp_dport_echo: + # dropped + pass + elif p.haslayer(BFD): + if "P" in p.sprintf("%BFD.flags%"): + self.assertGreaterEqual( + p[BFD].required_min_rx_interval, + 1000000) + final = self.test_session.create_packet() + final[BFD].flags = "F" + self.test_session.send_packet(final) + if p[BFD].state == BFDState.down: + self.assert_equal(p[BFD].diag, + BFDDiagCode.echo_function_failed, + BFDDiagCode) + verified_diag = True + else: + raise Exception(ppp("Received unknown packet:", p)) + self.test_session.send_packet() + events = self.vapi.collect_events() + self.assert_equal(len(events), 1, "number of bfd events") + self.assert_equal(events[0].state, BFDState.down, BFDState) + self.assertTrue(verified_diag, "Incorrect diagnostics code received") + + 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.send_packet() + self.vapi.bfd_udp_set_echo_source(self.loopback0.sw_if_index) + # wait for first echo packet + while True: + p = self.pg0.wait_for_packet(1) + self.logger.debug(ppp("Got packet:", p)) + if p[UDP].dport == BFD.udp_dport_echo: + self.logger.debug(ppp("Looping back packet:", p)) + self.pg0.add_stream(p) + self.pg_start() + break + elif p.haslayer(BFD): + # ignore BFD + pass + else: + raise Exception(ppp("Received unknown packet:", p)) + self.test_session.update(required_min_echo_rx=0) + self.test_session.send_packet() + # echo packets shouldn't arrive anymore + for dummy in range(5): + wait_for_bfd_packet( + self, pcap_time_min=time.time() - self.vpp_clock_offset) + self.test_session.send_packet() + events = self.vapi.collect_events() + self.assert_equal(len(events), 0, "number of bfd events") + + 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.send_packet() + self.vapi.bfd_udp_set_echo_source(self.loopback0.sw_if_index) + # wait for first echo packet + while True: + p = self.pg0.wait_for_packet(1) + self.logger.debug(ppp("Got packet:", p)) + if p[UDP].dport == BFD.udp_dport_echo: + self.logger.debug(ppp("Looping back packet:", p)) + self.pg0.add_stream(p) + self.pg_start() + break + elif p.haslayer(BFD): + # ignore BFD + pass + else: + raise Exception(ppp("Received unknown packet:", p)) + self.vapi.bfd_udp_del_echo_source() + self.test_session.send_packet() + # echo packets shouldn't arrive anymore + for dummy in range(5): + wait_for_bfd_packet( + self, pcap_time_min=time.time() - self.vpp_clock_offset) + self.test_session.send_packet() + events = self.vapi.collect_events() + self.assert_equal(len(events), 0, "number of bfd events") + + 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.send_packet() + # should be turned on - loopback echo packets + echo_packet = None + timeout_at = None + timeout_ok = False + for dummy in range(10 * self.vpp_session.detect_mult): + p = self.pg0.wait_for_packet(1) + if p[UDP].dport == BFD.udp_dport_echo: + if echo_packet is None: + self.logger.debug(ppp("Got first echo packet:", p)) + echo_packet = p + timeout_at = time.time() + self.vpp_session.detect_mult * \ + self.test_session.required_min_echo_rx / USEC_IN_SEC + else: + self.logger.debug(ppp("Got followup echo packet:", p)) + self.logger.debug(ppp("Looping back first echo packet:", p)) + self.pg0.add_stream(echo_packet) + self.pg_start() + elif p.haslayer(BFD): + self.logger.debug(ppp("Got packet:", p)) + if "P" in p.sprintf("%BFD.flags%"): + final = self.test_session.create_packet() + final[BFD].flags = "F" + self.test_session.send_packet(final) + if p[BFD].state == BFDState.down: + self.assertIsNotNone( + timeout_at, + "Session went down before first echo packet received") + now = time.time() + self.assertGreaterEqual( + now, timeout_at, + "Session timeout at %s, but is expected at %s" % + (now, timeout_at)) + self.assert_equal(p[BFD].diag, + BFDDiagCode.echo_function_failed, + BFDDiagCode) + events = self.vapi.collect_events() + self.assert_equal(len(events), 1, "number of bfd events") + self.assert_equal(events[0].state, BFDState.down, BFDState) + timeout_ok = True + break + else: + raise Exception(ppp("Received unknown packet:", p)) + self.test_session.send_packet() + self.assertTrue(timeout_ok, "Expected timeout event didn't occur") + + 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.send_packet() + # should be turned on - loopback echo packets + timeout_at = None + timeout_ok = False + for dummy in range(10 * self.vpp_session.detect_mult): + p = self.pg0.wait_for_packet(1) + if p[UDP].dport == BFD.udp_dport_echo: + self.logger.debug(ppp("Got echo packet:", p)) + if timeout_at is None: + 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) + self.logger.debug(ppp("Looping back modified echo packet:", p)) + self.pg0.add_stream(p) + self.pg_start() + elif p.haslayer(BFD): + self.logger.debug(ppp("Got packet:", p)) + if "P" in p.sprintf("%BFD.flags%"): + final = self.test_session.create_packet() + final[BFD].flags = "F" + self.test_session.send_packet(final) + if p[BFD].state == BFDState.down: + self.assertIsNotNone( + timeout_at, + "Session went down before first echo packet received") + now = time.time() + self.assertGreaterEqual( + now, timeout_at, + "Session timeout at %s, but is expected at %s" % + (now, timeout_at)) + self.assert_equal(p[BFD].diag, + BFDDiagCode.echo_function_failed, + BFDDiagCode) + events = self.vapi.collect_events() + self.assert_equal(len(events), 1, "number of bfd events") + self.assert_equal(events[0].state, BFDState.down, BFDState) + timeout_ok = True + break + else: + raise Exception(ppp("Received unknown packet:", p)) + self.test_session.send_packet() + self.assertTrue(timeout_ok, "Expected timeout event didn't occur") + + def test_admin_up_down(self): + """ put session admin-up and admin-down """ + bfd_session_up(self) + self.vpp_session.admin_down() + self.pg0.enable_capture() + e = self.vapi.wait_for_event(1, "bfd_udp_session_details") + verify_event(self, e, expected_state=BFDState.admin_down) + for dummy in range(2): + p = wait_for_bfd_packet(self) + self.assert_equal(p[BFD].state, BFDState.admin_down, BFDState) + # try to bring session up - shouldn't be possible + self.test_session.update(state=BFDState.init) + self.test_session.send_packet() + for dummy in range(2): + p = wait_for_bfd_packet(self) + self.assert_equal(p[BFD].state, BFDState.admin_down, BFDState) + self.vpp_session.admin_up() + self.test_session.update(state=BFDState.down) + e = self.vapi.wait_for_event(1, "bfd_udp_session_details") + verify_event(self, e, expected_state=BFDState.down) + p = wait_for_bfd_packet( + self, pcap_time_min=time.time() - self.vpp_clock_offset) + self.assert_equal(p[BFD].state, BFDState.down, BFDState) + self.test_session.send_packet() + p = wait_for_bfd_packet( + self, pcap_time_min=time.time() - self.vpp_clock_offset) + self.assert_equal(p[BFD].state, BFDState.init, BFDState) + e = self.vapi.wait_for_event(1, "bfd_udp_session_details") + verify_event(self, e, expected_state=BFDState.init) + self.test_session.update(state=BFDState.up) + self.test_session.send_packet() + p = wait_for_bfd_packet( + self, pcap_time_min=time.time() - self.vpp_clock_offset) + self.assert_equal(p[BFD].state, BFDState.up, BFDState) + e = self.vapi.wait_for_event(1, "bfd_udp_session_details") + verify_event(self, e, expected_state=BFDState.up) + + def test_config_change_remote_demand(self): + """ configuration change while peer in demand mode """ + bfd_session_up(self) + demand = self.test_session.create_packet() + demand[BFD].flags = "D" + self.test_session.send_packet(demand) + self.vpp_session.modify_parameters( + required_min_rx=2 * self.vpp_session.required_min_rx) + p = wait_for_bfd_packet( + self, pcap_time_min=time.time() - self.vpp_clock_offset) + # poll bit must be set + self.assertIn("P", p.sprintf("%BFD.flags%"), "Poll bit not set") + # terminate poll sequence + final = self.test_session.create_packet() + final[BFD].flags = "D+F" + self.test_session.send_packet(final) + # vpp should be quiet now again + transmit_time = 0.9 \ + * max(self.vpp_session.required_min_rx, + self.test_session.desired_min_tx) \ + / USEC_IN_SEC + count = 0 + for dummy in range(self.test_session.detect_mult * 2): time.sleep(transmit_time) self.test_session.send_packet(demand) try: @@ -879,6 +1383,10 @@ class BFD6TestCase(VppTestCase): cls.pg0.configure_ipv6_neighbors() cls.pg0.admin_up() cls.pg0.resolve_ndp() + cls.create_loopback_interfaces([0]) + cls.loopback0 = cls.lo_interfaces[0] + cls.loopback0.config_ip6() + cls.loopback0.admin_up() except Exception: super(BFD6TestCase, cls).tearDownClass() @@ -911,16 +1419,135 @@ class BFD6TestCase(VppTestCase): """ bring BFD session up """ bfd_session_up(self) + def test_session_up_by_ip(self): + """ bring BFD session up - first frame looked up by address pair """ + self.logger.info("BFD: Sending Slow control frame") + self.test_session.update(my_discriminator=randint(0, 40000000)) + self.test_session.send_packet() + self.pg0.enable_capture() + p = self.pg0.wait_for_packet(1) + self.assert_equal(p[BFD].your_discriminator, + self.test_session.my_discriminator, + "BFD - your discriminator") + self.assert_equal(p[BFD].state, BFDState.init, BFDState) + self.test_session.update(your_discriminator=p[BFD].my_discriminator, + state=BFDState.up) + self.logger.info("BFD: Waiting for event") + e = self.vapi.wait_for_event(1, "bfd_udp_session_details") + verify_event(self, e, expected_state=BFDState.init) + self.logger.info("BFD: Sending Up") + self.test_session.send_packet() + self.logger.info("BFD: Waiting for event") + e = self.vapi.wait_for_event(1, "bfd_udp_session_details") + verify_event(self, e, expected_state=BFDState.up) + self.logger.info("BFD: Session is Up") + self.test_session.update(state=BFDState.up) + self.test_session.send_packet() + self.assert_equal(self.vpp_session.state, BFDState.up, BFDState) + def test_hold_up(self): """ hold BFD session up """ bfd_session_up(self) - for dummy in range(self.test_session.detect_mult*2): + for dummy in range(self.test_session.detect_mult * 2): wait_for_bfd_packet(self) self.test_session.send_packet() self.assert_equal(len(self.vapi.collect_events()), 0, "number of bfd events") self.assert_equal(self.vpp_session.state, BFDState.up, BFDState) + def test_echo_looped_back(self): + """ echo packets looped back """ + # don't need a session in this case.. + self.vpp_session.remove_vpp_config() + self.pg0.enable_capture() + echo_packet_count = 10 + # random source port low enough to increment a few times.. + udp_sport_tx = randint(1, 50000) + udp_sport_rx = udp_sport_tx + echo_packet = (Ether(src=self.pg0.remote_mac, + dst=self.pg0.local_mac) / + IPv6(src=self.pg0.remote_ip6, + dst=self.pg0.remote_ip6) / + UDP(dport=BFD.udp_dport_echo) / + Raw("this should be looped back")) + for dummy in range(echo_packet_count): + self.sleep(.01, "delay between echo packets") + echo_packet[UDP].sport = udp_sport_tx + udp_sport_tx += 1 + self.logger.debug(ppp("Sending packet:", echo_packet)) + self.pg0.add_stream(echo_packet) + self.pg_start() + for dummy in range(echo_packet_count): + p = self.pg0.wait_for_packet(1) + self.logger.debug(ppp("Got packet:", p)) + ether = p[Ether] + self.assert_equal(self.pg0.remote_mac, + ether.dst, "Destination MAC") + self.assert_equal(self.pg0.local_mac, ether.src, "Source MAC") + ip = p[IPv6] + self.assert_equal(self.pg0.remote_ip6, ip.dst, "Destination IP") + self.assert_equal(self.pg0.remote_ip6, ip.src, "Destination IP") + udp = p[UDP] + self.assert_equal(udp.dport, BFD.udp_dport_echo, + "UDP destination port") + self.assert_equal(udp.sport, udp_sport_rx, "UDP source port") + 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), + "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)") + self.assert_equal(udp_sport_tx, udp_sport_rx, "UDP source port (== " + "ECHO packet identifier for test purposes)") + + def test_echo(self): + """ echo function used """ + bfd_session_up(self) + self.test_session.update(required_min_echo_rx=50000) + 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 + self.sleep(sleep, "delay before sending bfd packet") + self.test_session.send_packet() + p = wait_for_bfd_packet( + self, pcap_time_min=time.time() - self.vpp_clock_offset) + 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) + # should be turned on - loopback echo packets + for dummy in range(3): + loop_until = time.time() + 0.75 * detection_time + while time.time() < loop_until: + p = self.pg0.wait_for_packet(1) + self.logger.debug(ppp("Got packet:", p)) + if p[UDP].dport == BFD.udp_dport_echo: + self.assert_equal( + p[IPv6].dst, self.pg0.local_ip6, "BFD ECHO dst IP") + 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.pg0.add_stream(p) + self.pg_start() + elif p.haslayer(BFD): + 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" + self.test_session.send_packet(final) + else: + raise Exception(ppp("Received unknown packet:", p)) + + self.assert_equal(len(self.vapi.collect_events()), 0, + "number of bfd events") + self.test_session.send_packet() + class BFDSHA1TestCase(VppTestCase): """Bidirectional Forwarding Detection (BFD) (SHA1 auth) """ @@ -982,7 +1609,7 @@ class BFDSHA1TestCase(VppTestCase): self, self.pg0, AF_INET, sha1_key=key, bfd_key_id=self.vpp_session.bfd_key_id) bfd_session_up(self) - for dummy in range(self.test_session.detect_mult*2): + for dummy in range(self.test_session.detect_mult * 2): wait_for_bfd_packet(self) self.test_session.send_packet() self.assert_equal(self.vpp_session.state, BFDState.up, BFDState) @@ -1009,28 +1636,24 @@ class BFDSHA1TestCase(VppTestCase): self.assert_equal(self.vpp_session.state, BFDState.up, BFDState) def test_send_bad_seq_number(self): - """ session is not kept alive by msgs with bad seq numbers""" + """ session is not kept alive by msgs with bad sequence numbers""" key = self.factory.create_random_key( self, BFDAuthType.meticulous_keyed_sha1) key.add_vpp_config() self.vpp_session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4, sha1_key=key) self.vpp_session.add_vpp_config() - self.vpp_session.admin_up() self.test_session = BFDTestSession( self, self.pg0, AF_INET, sha1_key=key, bfd_key_id=self.vpp_session.bfd_key_id) bfd_session_up(self) - detection_time = self.vpp_session.detect_mult *\ + detection_time = self.test_session.detect_mult *\ self.vpp_session.required_min_rx / USEC_IN_SEC - session_timeout = time.time() + detection_time - while time.time() < session_timeout: - self.assert_equal(len(self.vapi.collect_events()), 0, - "number of bfd events") - wait_for_bfd_packet(self) + send_until = time.time() + 2 * detection_time + while time.time() < send_until: self.test_session.send_packet() - wait_for_bfd_packet(self) - self.test_session.send_packet() + self.sleep(0.7 * self.vpp_session.required_min_rx / USEC_IN_SEC, + "time between bfd packets") e = self.vapi.collect_events() # session should be down now, because the sequence numbers weren't # updated @@ -1054,7 +1677,6 @@ class BFDSHA1TestCase(VppTestCase): self.vpp_session = vpp_bfd_udp_session self.vpp_session.add_vpp_config() - self.vpp_session.admin_up() self.test_session = legitimate_test_session # bring vpp session up bfd_session_up(self) @@ -1132,15 +1754,14 @@ class BFDSHA1TestCase(VppTestCase): self.vpp_session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4, sha1_key=key) self.vpp_session.add_vpp_config() - self.vpp_session.admin_up() self.test_session = BFDTestSession( self, self.pg0, AF_INET, sha1_key=key, bfd_key_id=self.vpp_session.bfd_key_id, our_seq_number=0) bfd_session_up(self) # don't send any packets for 2*detection_time - detection_time = self.vpp_session.detect_mult *\ + detection_time = self.test_session.detect_mult *\ self.vpp_session.required_min_rx / USEC_IN_SEC - self.sleep(detection_time, "simulating peer restart") + self.sleep(2*detection_time, "simulating peer restart") events = self.vapi.collect_events() self.assert_equal(len(events), 1, "number of bfd events") verify_event(self, events[0], expected_state=BFDState.down) @@ -1192,17 +1813,16 @@ class BFDAuthOnOffTestCase(VppTestCase): self.vpp_session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4) self.vpp_session.add_vpp_config() - self.vpp_session.admin_up() self.test_session = BFDTestSession(self, self.pg0, AF_INET) bfd_session_up(self) - for dummy in range(self.test_session.detect_mult*2): + for dummy in range(self.test_session.detect_mult * 2): p = wait_for_bfd_packet(self) self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.send_packet() self.vpp_session.activate_auth(key) self.test_session.bfd_key_id = self.vpp_session.bfd_key_id self.test_session.sha1_key = key - for dummy in range(self.test_session.detect_mult*2): + for dummy in range(self.test_session.detect_mult * 2): p = wait_for_bfd_packet(self) self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.send_packet() @@ -1217,13 +1837,12 @@ class BFDAuthOnOffTestCase(VppTestCase): self.vpp_session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4, sha1_key=key) self.vpp_session.add_vpp_config() - self.vpp_session.admin_up() self.test_session = BFDTestSession( self, self.pg0, AF_INET, sha1_key=key, bfd_key_id=self.vpp_session.bfd_key_id) bfd_session_up(self) # self.vapi.want_bfd_events(enable_disable=0) - for dummy in range(self.test_session.detect_mult*2): + for dummy in range(self.test_session.detect_mult * 2): p = wait_for_bfd_packet(self) self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.inc_seq_num() @@ -1231,7 +1850,7 @@ class BFDAuthOnOffTestCase(VppTestCase): self.vpp_session.deactivate_auth() self.test_session.bfd_key_id = None self.test_session.sha1_key = None - for dummy in range(self.test_session.detect_mult*2): + for dummy in range(self.test_session.detect_mult * 2): p = wait_for_bfd_packet(self) self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.inc_seq_num() @@ -1249,19 +1868,18 @@ class BFDAuthOnOffTestCase(VppTestCase): self.vpp_session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4, sha1_key=key1) self.vpp_session.add_vpp_config() - self.vpp_session.admin_up() self.test_session = BFDTestSession( self, self.pg0, AF_INET, sha1_key=key1, bfd_key_id=self.vpp_session.bfd_key_id) bfd_session_up(self) - for dummy in range(self.test_session.detect_mult*2): + for dummy in range(self.test_session.detect_mult * 2): p = wait_for_bfd_packet(self) self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.send_packet() self.vpp_session.activate_auth(key2) self.test_session.bfd_key_id = self.vpp_session.bfd_key_id self.test_session.sha1_key = key2 - for dummy in range(self.test_session.detect_mult*2): + for dummy in range(self.test_session.detect_mult * 2): p = wait_for_bfd_packet(self) self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.send_packet() @@ -1276,21 +1894,20 @@ class BFDAuthOnOffTestCase(VppTestCase): self.vpp_session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4) self.vpp_session.add_vpp_config() - self.vpp_session.admin_up() self.test_session = BFDTestSession(self, self.pg0, AF_INET) bfd_session_up(self) - for dummy in range(self.test_session.detect_mult*2): + for dummy in range(self.test_session.detect_mult * 2): wait_for_bfd_packet(self) self.test_session.send_packet() self.vpp_session.activate_auth(key, delayed=True) - for dummy in range(self.test_session.detect_mult*2): + for dummy in range(self.test_session.detect_mult * 2): p = wait_for_bfd_packet(self) self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.send_packet() self.test_session.bfd_key_id = self.vpp_session.bfd_key_id self.test_session.sha1_key = key self.test_session.send_packet() - for dummy in range(self.test_session.detect_mult*2): + for dummy in range(self.test_session.detect_mult * 2): p = wait_for_bfd_packet(self) self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.send_packet() @@ -1305,24 +1922,23 @@ class BFDAuthOnOffTestCase(VppTestCase): self.vpp_session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4, sha1_key=key) self.vpp_session.add_vpp_config() - self.vpp_session.admin_up() self.test_session = BFDTestSession( self, self.pg0, AF_INET, sha1_key=key, bfd_key_id=self.vpp_session.bfd_key_id) bfd_session_up(self) - for dummy in range(self.test_session.detect_mult*2): + for dummy in range(self.test_session.detect_mult * 2): p = wait_for_bfd_packet(self) self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.send_packet() self.vpp_session.deactivate_auth(delayed=True) - for dummy in range(self.test_session.detect_mult*2): + for dummy in range(self.test_session.detect_mult * 2): p = wait_for_bfd_packet(self) self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.send_packet() self.test_session.bfd_key_id = None self.test_session.sha1_key = None self.test_session.send_packet() - for dummy in range(self.test_session.detect_mult*2): + for dummy in range(self.test_session.detect_mult * 2): p = wait_for_bfd_packet(self) self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.send_packet() @@ -1344,19 +1960,19 @@ class BFDAuthOnOffTestCase(VppTestCase): self, self.pg0, AF_INET, sha1_key=key1, bfd_key_id=self.vpp_session.bfd_key_id) bfd_session_up(self) - for dummy in range(self.test_session.detect_mult*2): + for dummy in range(self.test_session.detect_mult * 2): p = wait_for_bfd_packet(self) self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.send_packet() self.vpp_session.activate_auth(key2, delayed=True) - for dummy in range(self.test_session.detect_mult*2): + for dummy in range(self.test_session.detect_mult * 2): p = wait_for_bfd_packet(self) self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.send_packet() self.test_session.bfd_key_id = self.vpp_session.bfd_key_id self.test_session.sha1_key = key2 self.test_session.send_packet() - for dummy in range(self.test_session.detect_mult*2): + for dummy in range(self.test_session.detect_mult * 2): p = wait_for_bfd_packet(self) self.assert_equal(p[BFD].state, BFDState.up, BFDState) self.test_session.send_packet() @@ -1364,5 +1980,421 @@ class BFDAuthOnOffTestCase(VppTestCase): self.assert_equal(len(self.vapi.collect_events()), 0, "number of bfd events") + +class BFDCLITestCase(VppTestCase): + """Bidirectional Forwarding Detection (BFD) (CLI) """ + pg0 = None + + @classmethod + def setUpClass(cls): + super(BFDCLITestCase, cls).setUpClass() + + try: + cls.create_pg_interfaces((0,)) + cls.pg0.config_ip4() + cls.pg0.config_ip6() + cls.pg0.resolve_arp() + cls.pg0.resolve_ndp() + + except Exception: + super(BFDCLITestCase, cls).tearDownClass() + raise + + def setUp(self): + super(BFDCLITestCase, self).setUp() + self.factory = AuthKeyFactory() + self.pg0.enable_capture() + + def tearDown(self): + try: + self.vapi.want_bfd_events(enable_disable=0) + except UnexpectedApiReturnValueError: + # some tests aren't subscribed, so this is not an issue + pass + self.vapi.collect_events() # clear the event queue + super(BFDCLITestCase, self).tearDown() + + def cli_verify_no_response(self, cli): + """ execute a CLI, asserting that the response is empty """ + self.assert_equal(self.vapi.cli(cli), + "", + "CLI command response") + + def cli_verify_response(self, cli, expected): + """ execute a CLI, asserting that the response matches expectation """ + self.assert_equal(self.vapi.cli(cli).strip(), + expected, + "CLI command response") + + def test_show(self): + """ show commands """ + k1 = self.factory.create_random_key(self) + k1.add_vpp_config() + k2 = self.factory.create_random_key( + self, auth_type=BFDAuthType.meticulous_keyed_sha1) + k2.add_vpp_config() + s1 = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4) + s1.add_vpp_config() + s2 = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip6, af=AF_INET6, + sha1_key=k2) + s2.add_vpp_config() + self.logger.info(self.vapi.ppcli("show bfd keys")) + self.logger.info(self.vapi.ppcli("show bfd sessions")) + self.logger.info(self.vapi.ppcli("show bfd")) + + def test_set_del_sha1_key(self): + """ set/delete SHA1 auth key """ + k = self.factory.create_random_key(self) + self.registry.register(k, self.logger) + 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))) + self.assertTrue(k.query_vpp_config()) + self.vpp_session = VppBFDUDPSession( + self, self.pg0, self.pg0.remote_ip4, sha1_key=k) + self.vpp_session.add_vpp_config() + self.test_session = \ + BFDTestSession(self, self.pg0, AF_INET, sha1_key=k, + bfd_key_id=self.vpp_session.bfd_key_id) + self.vapi.want_bfd_events() + bfd_session_up(self) + bfd_session_down(self) + # try to replace the secret for the key - should fail because the key + # is in-use + k2 = self.factory.create_random_key(self) + 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)), + "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 + bfd_session_up(self) + bfd_session_down(self) + self.vpp_session.remove_vpp_config() + self.cli_verify_no_response( + "bfd key del conf-key-id %s" % k.conf_key_id) + self.assertFalse(k.query_vpp_config()) + + def test_set_del_meticulous_sha1_key(self): + """ set/delete meticulous SHA1 auth key """ + k = self.factory.create_random_key( + self, auth_type=BFDAuthType.meticulous_keyed_sha1) + self.registry.register(k, self.logger) + 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))) + self.assertTrue(k.query_vpp_config()) + self.vpp_session = VppBFDUDPSession(self, self.pg0, + self.pg0.remote_ip6, af=AF_INET6, + sha1_key=k) + self.vpp_session.add_vpp_config() + self.vpp_session.admin_up() + self.test_session = \ + BFDTestSession(self, self.pg0, AF_INET6, sha1_key=k, + bfd_key_id=self.vpp_session.bfd_key_id) + self.vapi.want_bfd_events() + bfd_session_up(self) + bfd_session_down(self) + # try to replace the secret for the key - should fail because the key + # is in-use + k2 = self.factory.create_random_key(self) + 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)), + "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 + bfd_session_up(self) + bfd_session_down(self) + self.vpp_session.remove_vpp_config() + self.cli_verify_no_response( + "bfd key del conf-key-id %s" % k.conf_key_id) + self.assertFalse(k.query_vpp_config()) + + def test_add_mod_del_bfd_udp(self): + """ create/modify/delete IPv4 BFD UDP session """ + vpp_session = VppBFDUDPSession( + self, self.pg0, self.pg0.remote_ip4) + self.registry.register(vpp_session, self.logger) + cli_add_cmd = "bfd udp session add interface %s local-addr %s " \ + "peer-addr %s desired-min-tx %s required-min-rx %s "\ + "detect-mult %s" % (self.pg0.name, self.pg0.local_ip4, + self.pg0.remote_ip4, + vpp_session.desired_min_tx, + vpp_session.required_min_rx, + vpp_session.detect_mult) + self.cli_verify_no_response(cli_add_cmd) + # 2nd add should fail + self.cli_verify_response( + cli_add_cmd, + "bfd udp session add: `bfd_add_add_session' API call" + " failed, rv=-101:Duplicate BFD object") + verify_bfd_session_config(self, vpp_session) + mod_session = VppBFDUDPSession( + self, self.pg0, self.pg0.remote_ip4, + required_min_rx=2 * vpp_session.required_min_rx, + desired_min_tx=3 * vpp_session.desired_min_tx, + detect_mult=4 * vpp_session.detect_mult) + self.cli_verify_no_response( + "bfd udp session mod interface %s local-addr %s peer-addr %s " + "desired-min-tx %s required-min-rx %s detect-mult %s" % + (self.pg0.name, self.pg0.local_ip4, self.pg0.remote_ip4, + mod_session.desired_min_tx, mod_session.required_min_rx, + mod_session.detect_mult)) + verify_bfd_session_config(self, mod_session) + cli_del_cmd = "bfd udp session del interface %s local-addr %s "\ + "peer-addr %s" % (self.pg0.name, + self.pg0.local_ip4, self.pg0.remote_ip4) + self.cli_verify_no_response(cli_del_cmd) + # 2nd del is expected to fail + self.cli_verify_response( + cli_del_cmd, "bfd udp session del: `bfd_udp_del_session' API call" + " failed, rv=-102:No such BFD object") + self.assertFalse(vpp_session.query_vpp_config()) + + def test_add_mod_del_bfd_udp6(self): + """ create/modify/delete IPv6 BFD UDP session """ + vpp_session = VppBFDUDPSession( + self, self.pg0, self.pg0.remote_ip6, af=AF_INET6) + self.registry.register(vpp_session, self.logger) + cli_add_cmd = "bfd udp session add interface %s local-addr %s " \ + "peer-addr %s desired-min-tx %s required-min-rx %s "\ + "detect-mult %s" % (self.pg0.name, self.pg0.local_ip6, + self.pg0.remote_ip6, + vpp_session.desired_min_tx, + vpp_session.required_min_rx, + vpp_session.detect_mult) + self.cli_verify_no_response(cli_add_cmd) + # 2nd add should fail + self.cli_verify_response( + cli_add_cmd, + "bfd udp session add: `bfd_add_add_session' API call" + " failed, rv=-101:Duplicate BFD object") + verify_bfd_session_config(self, vpp_session) + mod_session = VppBFDUDPSession( + self, self.pg0, self.pg0.remote_ip6, af=AF_INET6, + required_min_rx=2 * vpp_session.required_min_rx, + desired_min_tx=3 * vpp_session.desired_min_tx, + detect_mult=4 * vpp_session.detect_mult) + self.cli_verify_no_response( + "bfd udp session mod interface %s local-addr %s peer-addr %s " + "desired-min-tx %s required-min-rx %s detect-mult %s" % + (self.pg0.name, self.pg0.local_ip6, self.pg0.remote_ip6, + mod_session.desired_min_tx, + mod_session.required_min_rx, mod_session.detect_mult)) + verify_bfd_session_config(self, mod_session) + cli_del_cmd = "bfd udp session del interface %s local-addr %s "\ + "peer-addr %s" % (self.pg0.name, + self.pg0.local_ip6, self.pg0.remote_ip6) + self.cli_verify_no_response(cli_del_cmd) + # 2nd del is expected to fail + self.cli_verify_response( + cli_del_cmd, + "bfd udp session del: `bfd_udp_del_session' API call" + " failed, rv=-102:No such BFD object") + self.assertFalse(vpp_session.query_vpp_config()) + + def test_add_mod_del_bfd_udp_auth(self): + """ create/modify/delete IPv4 BFD UDP session (authenticated) """ + key = self.factory.create_random_key(self) + key.add_vpp_config() + vpp_session = VppBFDUDPSession( + self, self.pg0, self.pg0.remote_ip4, sha1_key=key) + self.registry.register(vpp_session, self.logger) + cli_add_cmd = "bfd udp session add interface %s local-addr %s " \ + "peer-addr %s desired-min-tx %s required-min-rx %s "\ + "detect-mult %s conf-key-id %s bfd-key-id %s"\ + % (self.pg0.name, self.pg0.local_ip4, self.pg0.remote_ip4, + vpp_session.desired_min_tx, vpp_session.required_min_rx, + vpp_session.detect_mult, key.conf_key_id, + vpp_session.bfd_key_id) + self.cli_verify_no_response(cli_add_cmd) + # 2nd add should fail + self.cli_verify_response( + cli_add_cmd, + "bfd udp session add: `bfd_add_add_session' API call" + " failed, rv=-101:Duplicate BFD object") + verify_bfd_session_config(self, vpp_session) + mod_session = VppBFDUDPSession( + self, self.pg0, self.pg0.remote_ip4, sha1_key=key, + bfd_key_id=vpp_session.bfd_key_id, + required_min_rx=2 * vpp_session.required_min_rx, + desired_min_tx=3 * vpp_session.desired_min_tx, + detect_mult=4 * vpp_session.detect_mult) + self.cli_verify_no_response( + "bfd udp session mod interface %s local-addr %s peer-addr %s " + "desired-min-tx %s required-min-rx %s detect-mult %s" % + (self.pg0.name, self.pg0.local_ip4, self.pg0.remote_ip4, + mod_session.desired_min_tx, + mod_session.required_min_rx, mod_session.detect_mult)) + verify_bfd_session_config(self, mod_session) + cli_del_cmd = "bfd udp session del interface %s local-addr %s "\ + "peer-addr %s" % (self.pg0.name, + self.pg0.local_ip4, self.pg0.remote_ip4) + self.cli_verify_no_response(cli_del_cmd) + # 2nd del is expected to fail + self.cli_verify_response( + cli_del_cmd, + "bfd udp session del: `bfd_udp_del_session' API call" + " failed, rv=-102:No such BFD object") + self.assertFalse(vpp_session.query_vpp_config()) + + def test_add_mod_del_bfd_udp6_auth(self): + """ create/modify/delete IPv6 BFD UDP session (authenticated) """ + key = self.factory.create_random_key( + self, auth_type=BFDAuthType.meticulous_keyed_sha1) + key.add_vpp_config() + vpp_session = VppBFDUDPSession( + self, self.pg0, self.pg0.remote_ip6, af=AF_INET6, sha1_key=key) + self.registry.register(vpp_session, self.logger) + cli_add_cmd = "bfd udp session add interface %s local-addr %s " \ + "peer-addr %s desired-min-tx %s required-min-rx %s "\ + "detect-mult %s conf-key-id %s bfd-key-id %s" \ + % (self.pg0.name, self.pg0.local_ip6, self.pg0.remote_ip6, + vpp_session.desired_min_tx, vpp_session.required_min_rx, + vpp_session.detect_mult, key.conf_key_id, + vpp_session.bfd_key_id) + self.cli_verify_no_response(cli_add_cmd) + # 2nd add should fail + self.cli_verify_response( + cli_add_cmd, + "bfd udp session add: `bfd_add_add_session' API call" + " failed, rv=-101:Duplicate BFD object") + verify_bfd_session_config(self, vpp_session) + mod_session = VppBFDUDPSession( + self, self.pg0, self.pg0.remote_ip6, af=AF_INET6, sha1_key=key, + bfd_key_id=vpp_session.bfd_key_id, + required_min_rx=2 * vpp_session.required_min_rx, + desired_min_tx=3 * vpp_session.desired_min_tx, + detect_mult=4 * vpp_session.detect_mult) + self.cli_verify_no_response( + "bfd udp session mod interface %s local-addr %s peer-addr %s " + "desired-min-tx %s required-min-rx %s detect-mult %s" % + (self.pg0.name, self.pg0.local_ip6, self.pg0.remote_ip6, + mod_session.desired_min_tx, + mod_session.required_min_rx, mod_session.detect_mult)) + verify_bfd_session_config(self, mod_session) + cli_del_cmd = "bfd udp session del interface %s local-addr %s "\ + "peer-addr %s" % (self.pg0.name, + self.pg0.local_ip6, self.pg0.remote_ip6) + self.cli_verify_no_response(cli_del_cmd) + # 2nd del is expected to fail + self.cli_verify_response( + cli_del_cmd, + "bfd udp session del: `bfd_udp_del_session' API call" + " failed, rv=-102:No such BFD object") + self.assertFalse(vpp_session.query_vpp_config()) + + def test_auth_on_off(self): + """ turn authentication on and off """ + key = self.factory.create_random_key( + self, auth_type=BFDAuthType.meticulous_keyed_sha1) + key.add_vpp_config() + session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4) + auth_session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4, + sha1_key=key) + session.add_vpp_config() + cli_activate = \ + "bfd udp session auth activate interface %s local-addr %s "\ + "peer-addr %s conf-key-id %s bfd-key-id %s"\ + % (self.pg0.name, self.pg0.local_ip4, self.pg0.remote_ip4, + key.conf_key_id, auth_session.bfd_key_id) + self.cli_verify_no_response(cli_activate) + verify_bfd_session_config(self, auth_session) + self.cli_verify_no_response(cli_activate) + verify_bfd_session_config(self, auth_session) + cli_deactivate = \ + "bfd udp session auth deactivate interface %s local-addr %s "\ + "peer-addr %s "\ + % (self.pg0.name, self.pg0.local_ip4, self.pg0.remote_ip4) + self.cli_verify_no_response(cli_deactivate) + verify_bfd_session_config(self, session) + self.cli_verify_no_response(cli_deactivate) + verify_bfd_session_config(self, session) + + def test_auth_on_off_delayed(self): + """ turn authentication on and off (delayed) """ + key = self.factory.create_random_key( + self, auth_type=BFDAuthType.meticulous_keyed_sha1) + key.add_vpp_config() + session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4) + auth_session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4, + sha1_key=key) + session.add_vpp_config() + cli_activate = \ + "bfd udp session auth activate interface %s local-addr %s "\ + "peer-addr %s conf-key-id %s bfd-key-id %s delayed yes"\ + % (self.pg0.name, self.pg0.local_ip4, self.pg0.remote_ip4, + key.conf_key_id, auth_session.bfd_key_id) + self.cli_verify_no_response(cli_activate) + verify_bfd_session_config(self, auth_session) + self.cli_verify_no_response(cli_activate) + verify_bfd_session_config(self, auth_session) + cli_deactivate = \ + "bfd udp session auth deactivate interface %s local-addr %s "\ + "peer-addr %s delayed yes"\ + % (self.pg0.name, self.pg0.local_ip4, self.pg0.remote_ip4) + self.cli_verify_no_response(cli_deactivate) + verify_bfd_session_config(self, session) + self.cli_verify_no_response(cli_deactivate) + verify_bfd_session_config(self, session) + + def test_admin_up_down(self): + """ put session admin-up and admin-down """ + session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4) + session.add_vpp_config() + cli_down = \ + "bfd udp session set-flags admin down interface %s local-addr %s "\ + "peer-addr %s "\ + % (self.pg0.name, self.pg0.local_ip4, self.pg0.remote_ip4) + cli_up = \ + "bfd udp session set-flags admin up interface %s local-addr %s "\ + "peer-addr %s "\ + % (self.pg0.name, self.pg0.local_ip4, self.pg0.remote_ip4) + self.cli_verify_no_response(cli_down) + verify_bfd_session_config(self, session, state=BFDState.admin_down) + self.cli_verify_no_response(cli_up) + verify_bfd_session_config(self, session, state=BFDState.down) + + def test_set_del_udp_echo_source(self): + """ set/del udp echo source """ + self.create_loopback_interfaces([0]) + self.loopback0 = self.lo_interfaces[0] + self.loopback0.admin_up() + self.cli_verify_response("show bfd echo-source", + "UDP echo source is not set.") + cli_set = "bfd udp echo-source set interface %s" % self.loopback0.name + self.cli_verify_no_response(cli_set) + self.cli_verify_response("show bfd echo-source", + "UDP echo source is: %s\n" + "IPv4 address usable as echo source: none\n" + "IPv6 address usable as echo source: none" % + self.loopback0.name) + self.loopback0.config_ip4() + unpacked = unpack("!L", self.loopback0.local_ip4n) + echo_ip4 = inet_ntop(AF_INET, pack("!L", unpacked[0] ^ 1)) + self.cli_verify_response("show bfd echo-source", + "UDP echo source is: %s\n" + "IPv4 address usable as echo source: %s\n" + "IPv6 address usable as echo source: none" % + (self.loopback0.name, echo_ip4)) + unpacked = unpack("!LLLL", self.loopback0.local_ip6n) + echo_ip6 = inet_ntop(AF_INET6, pack("!LLLL", unpacked[0], unpacked[1], + unpacked[2], unpacked[3] ^ 1)) + self.loopback0.config_ip6() + self.cli_verify_response("show bfd echo-source", + "UDP echo source is: %s\n" + "IPv4 address usable as echo source: %s\n" + "IPv6 address usable as echo source: %s" % + (self.loopback0.name, echo_ip4, echo_ip6)) + cli_del = "bfd udp echo-source del" + self.cli_verify_no_response(cli_del) + self.cli_verify_response("show bfd echo-source", + "UDP echo source is not set.") + if __name__ == '__main__': unittest.main(testRunner=VppTestRunner)