nat: improve icmp type detection performance
[vpp.git] / test / test_bfd.py
index 9a37eb8..d1c7b92 100644 (file)
@@ -1,33 +1,40 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 """ BFD tests """
 
 from __future__ import division
 
 import binascii
 """ BFD tests """
 
 from __future__ import division
 
 import binascii
+from collections import namedtuple
 import hashlib
 import hashlib
+import ipaddress
+import reprlib
 import time
 import unittest
 from random import randint, shuffle, getrandbits
 from socket import AF_INET, AF_INET6, inet_ntop
 from struct import pack, unpack
 
 import time
 import unittest
 from random import randint, shuffle, getrandbits
 from socket import AF_INET, AF_INET6, inet_ntop
 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
 
 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 config import config
 from bfd import VppBFDAuthKey, BFD, BFDAuthType, VppBFDUDPSession, \
     BFDDiagCode, BFDState, BFD_vpp_echo
 from bfd import VppBFDAuthKey, BFD, BFDAuthType, VppBFDUDPSession, \
     BFDDiagCode, BFDState, BFD_vpp_echo
-from framework import VppTestCase, VppTestRunner, running_extended_tests
+from framework import tag_fixme_vpp_workers
+from framework import VppTestCase, VppTestRunner
+from framework import tag_run_solo
 from util import ppp
 from vpp_ip import DpoProto
 from vpp_ip_route import VppIpRoute, VppRoutePath
 from vpp_lo_interface import VppLoInterface
 from util import ppp
 from vpp_ip import DpoProto
 from vpp_ip_route import VppIpRoute, VppRoutePath
 from vpp_lo_interface import VppLoInterface
-from vpp_papi_provider import UnexpectedApiReturnValueError
+from vpp_papi_provider import UnexpectedApiReturnValueError, \
+    CliFailedCommandError
 from vpp_pg_interface import CaptureTimeoutError, is_ipv6_misc
 from vpp_gre_interface import VppGreInterface
 from vpp_pg_interface import CaptureTimeoutError, is_ipv6_misc
 from vpp_gre_interface import VppGreInterface
+from vpp_papi import VppEnum
 
 USEC_IN_SEC = 1000000
 
 
 USEC_IN_SEC = 1000000
 
@@ -50,7 +57,6 @@ class AuthKeyFactory(object):
                              conf_key_id=conf_key_id, key=key)
 
 
                              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"""
 
 class BFDAPITestCase(VppTestCase):
     """Bidirectional Forwarding Detection (BFD) - API"""
 
@@ -138,6 +144,34 @@ class BFDAPITestCase(VppTestCase):
                           "required min receive interval")
         self.assert_equal(session.detect_mult, s.detect_mult, "detect mult")
 
                           "required min receive interval")
         self.assert_equal(session.detect_mult, s.detect_mult, "detect mult")
 
+    def test_upd_bfd(self):
+        """ Create/Modify w/ Update BFD session parameters """
+        session = VppBFDUDPSession(self, self.pg0, self.pg0.remote_ip4,
+                                   desired_min_tx=50000,
+                                   required_min_rx=10000,
+                                   detect_mult=1)
+        session.upd_vpp_config()
+        s = session.get_bfd_udp_session_dump_entry()
+        self.assert_equal(session.desired_min_tx,
+                          s.desired_min_tx,
+                          "desired min transmit interval")
+        self.assert_equal(session.required_min_rx,
+                          s.required_min_rx,
+                          "required min receive interval")
+
+        self.assert_equal(session.detect_mult, s.detect_mult, "detect mult")
+        session.upd_vpp_config(desired_min_tx=session.desired_min_tx * 2,
+                               required_min_rx=session.required_min_rx * 2,
+                               detect_mult=session.detect_mult * 2)
+        s = session.get_bfd_udp_session_dump_entry()
+        self.assert_equal(session.desired_min_tx,
+                          s.desired_min_tx,
+                          "desired min transmit interval")
+        self.assert_equal(session.required_min_rx,
+                          s.required_min_rx,
+                          "required min receive interval")
+        self.assert_equal(session.detect_mult, s.detect_mult, "detect mult")
+
     def test_add_sha1_keys(self):
         """ add SHA1 keys """
         key_count = 10
     def test_add_sha1_keys(self):
         """ add SHA1 keys """
         key_count = 10
@@ -150,7 +184,7 @@ class BFDAPITestCase(VppTestCase):
         for key in keys:
             self.assertTrue(key.query_vpp_config())
         # remove randomly
         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:
         shuffle(indexes)
         removed = []
         for i in indexes:
@@ -208,7 +242,7 @@ class BFDAPITestCase(VppTestCase):
             session.add_vpp_config()
 
     def test_shared_sha1_key(self):
             session.add_vpp_config()
 
     def test_shared_sha1_key(self):
-        """ share single SHA1 key between multiple BFD sessions """
+        """ single SHA1 key shared by multiple BFD sessions """
         key = self.factory.create_random_key(self)
         key.add_vpp_config()
         sessions = [
         key = self.factory.create_random_key(self)
         key.add_vpp_config()
         sessions = [
@@ -273,7 +307,8 @@ class BFDAPITestCase(VppTestCase):
         self.assertFalse(echo_source.have_usable_ip4)
         self.assertFalse(echo_source.have_usable_ip6)
 
         self.assertFalse(echo_source.have_usable_ip4)
         self.assertFalse(echo_source.have_usable_ip6)
 
-        self.vapi.bfd_udp_set_echo_source(self.loopback0.sw_if_index)
+        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)
         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)
@@ -281,26 +316,26 @@ class BFDAPITestCase(VppTestCase):
         self.assertFalse(echo_source.have_usable_ip6)
 
         self.loopback0.config_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_ip4 = ipaddress.IPv4Address(int(ipaddress.IPv4Address(
+            self.loopback0.local_ip4)) ^ 1).packed
         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)
         echo_source = self.vapi.bfd_udp_get_echo_source()
         self.assertTrue(echo_source.is_set)
         self.assertEqual(echo_source.sw_if_index, self.loopback0.sw_if_index)
         self.assertTrue(echo_source.have_usable_ip4)
-        self.assertEqual(echo_source.ip4_addr, echo_ip4)
+        self.assertEqual(echo_source.ip4_addr.packed, echo_ip4)
         self.assertFalse(echo_source.have_usable_ip6)
 
         self.loopback0.config_ip6()
         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_ip6 = ipaddress.IPv6Address(int(ipaddress.IPv6Address(
+            self.loopback0.local_ip6)) ^ 1).packed
+
         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)
         echo_source = self.vapi.bfd_udp_get_echo_source()
         self.assertTrue(echo_source.is_set)
         self.assertEqual(echo_source.sw_if_index, self.loopback0.sw_if_index)
         self.assertTrue(echo_source.have_usable_ip4)
-        self.assertEqual(echo_source.ip4_addr, echo_ip4)
+        self.assertEqual(echo_source.ip4_addr.packed, echo_ip4)
         self.assertTrue(echo_source.have_usable_ip6)
         self.assertTrue(echo_source.have_usable_ip6)
-        self.assertEqual(echo_source.ip6_addr, echo_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.vapi.bfd_udp_del_echo_source()
         echo_source = self.vapi.bfd_udp_get_echo_source()
@@ -340,6 +375,10 @@ class BFDTestSession(object):
         self.state = BFDState.down
         self.auth_type = BFDAuthType.no_auth
         self.tunnel_header = tunnel_header
         self.state = BFDState.down
         self.auth_type = BFDAuthType.no_auth
         self.tunnel_header = tunnel_header
+        self.tx_packets = 0
+        self.rx_packets = 0
+        self.tx_packets_echo = 0
+        self.rx_packets_echo = 0
 
     def inc_seq_num(self):
         """ increment sequence number, wrapping if needed """
 
     def inc_seq_num(self):
         """ increment sequence number, wrapping if needed """
@@ -448,7 +487,7 @@ class BFDTestSession(object):
         if self.sha1_key:
             hash_material = scapy.compat.raw(
                 packet[BFD])[:32] + self.sha1_key.key + \
         if self.sha1_key:
             hash_material = scapy.compat.raw(
                 packet[BFD])[:32] + self.sha1_key.key + \
-                "\0" * (20 - len(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()
             self.test.logger.debug("BFD: Calculated SHA1 hash: %s" %
                                    hashlib.sha1(hash_material).hexdigest())
             packet[BFD].auth_key_hash = hashlib.sha1(hash_material).digest()
@@ -462,6 +501,7 @@ class BFDTestSession(object):
             interface = self.phy_interface
         self.test.logger.debug(ppp("Sending packet:", packet))
         interface.add_stream(packet)
             interface = self.phy_interface
         self.test.logger.debug(ppp("Sending packet:", packet))
         interface.add_stream(packet)
+        self.tx_packets += 1
         self.test.pg_start()
 
     def verify_sha1_auth(self, packet):
         self.test.pg_start()
 
     def verify_sha1_auth(self, packet):
@@ -507,7 +547,7 @@ class BFDTestSession(object):
             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),
             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. """
 
     def verify_bfd(self, packet):
         """ Verify correctness of BFD layer. """
@@ -527,7 +567,7 @@ def bfd_session_up(test):
     old_offset = None
     if hasattr(test, 'vpp_clock_offset'):
         old_offset = test.vpp_clock_offset
     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:
     test.logger.debug("BFD: Calculated vpp clock offset: %s",
                       test.vpp_clock_offset)
     if old_offset:
@@ -544,7 +584,7 @@ def bfd_session_up(test):
         test.test_session.inc_seq_num()
     test.test_session.send_packet()
     test.logger.info("BFD: Waiting for event")
         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")
+    e = test.vapi.wait_for_event(1, "bfd_udp_session_event")
     verify_event(test, e, expected_state=BFDState.up)
     test.logger.info("BFD: Session is Up")
     test.test_session.update(state=BFDState.up)
     verify_event(test, e, expected_state=BFDState.up)
     test.logger.info("BFD: Session is Up")
     test.test_session.update(state=BFDState.up)
@@ -564,7 +604,7 @@ def bfd_session_down(test):
         test.test_session.inc_seq_num()
     test.test_session.send_packet()
     test.logger.info("BFD: Waiting for event")
         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")
+    e = test.vapi.wait_for_event(1, "bfd_udp_session_event")
     verify_event(test, e, expected_state=BFDState.down)
     test.logger.info("BFD: Session is Down")
     test.assert_equal(test.vpp_session.state, BFDState.down, BFDState)
     verify_event(test, e, expected_state=BFDState.down)
     test.logger.info("BFD: Session is Down")
     test.assert_equal(test.vpp_session.state, BFDState.down, BFDState)
@@ -621,24 +661,15 @@ def verify_udp(test, packet):
 def verify_event(test, event, expected_state):
     """ Verify correctness of event values. """
     e = event
 def verify_event(test, event, expected_state):
     """ Verify correctness of event values. """
     e = event
-    test.logger.debug("BFD: Event: %s" % moves.reprlib.repr(e))
+    test.logger.debug("BFD: Event: %s" % reprlib.repr(e))
     test.assert_equal(e.sw_if_index,
                       test.vpp_session.interface.sw_if_index,
                       "BFD interface index")
     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)
 
 
     test.assert_equal(e.state, expected_state, BFDState)
 
 
@@ -661,6 +692,7 @@ def wait_for_bfd_packet(test, timeout=1, pcap_time_min=None, is_tunnel=False):
         if time_left < 0:
             raise CaptureTimeoutError("Packet did not arrive within timeout")
         p = test.pg0.wait_for_packet(timeout=time_left)
         if time_left < 0:
             raise CaptureTimeoutError("Packet did not arrive within timeout")
         p = test.pg0.wait_for_packet(timeout=time_left)
+        test.test_session.rx_packets += 1
         test.logger.debug(ppp("BFD: Got packet:", p))
         if pcap_time_min is not None and p.time < pcap_time_min:
             test.logger.debug(ppp("BFD: ignoring packet (pcap time %s < "
         test.logger.debug(ppp("BFD: Got packet:", p))
         if pcap_time_min is not None and p.time < pcap_time_min:
             test.logger.debug(ppp("BFD: ignoring packet (pcap time %s < "
@@ -683,7 +715,34 @@ def wait_for_bfd_packet(test, timeout=1, pcap_time_min=None, is_tunnel=False):
     return p
 
 
     return p
 
 
-@unittest.skipUnless(running_extended_tests, "part of extended tests")
+BFDStats = namedtuple("BFDStats", "rx rx_echo tx tx_echo")
+
+
+def bfd_grab_stats_snapshot(test, bs_idx=0, thread_index=None):
+    s = test.statistics
+    ti = thread_index
+    if ti is None:
+        rx = s['/bfd/rx-session-counters'][:, bs_idx].sum_packets()
+        rx_echo = s['/bfd/rx-session-echo-counters'][:, bs_idx].sum_packets()
+        tx = s['/bfd/tx-session-counters'][:, bs_idx].sum_packets()
+        tx_echo = s['/bfd/tx-session-echo-counters'][:, bs_idx].sum_packets()
+    else:
+        rx = s['/bfd/rx-session-counters'][ti, bs_idx].sum_packets()
+        rx_echo = s['/bfd/rx-session-echo-counters'][ti, bs_idx].sum_packets()
+        tx = s['/bfd/tx-session-counters'][ti, bs_idx].sum_packets()
+        tx_echo = s['/bfd/tx-session-echo-counters'][ti, bs_idx].sum_packets()
+    return BFDStats(rx, rx_echo, tx, tx_echo)
+
+
+def bfd_stats_diff(stats_before, stats_after):
+    rx = stats_after.rx - stats_before.rx
+    rx_echo = stats_after.rx_echo - stats_before.rx_echo
+    tx = stats_after.tx - stats_before.tx
+    tx_echo = stats_after.tx_echo - stats_before.tx_echo
+    return BFDStats(rx, rx_echo, tx, tx_echo)
+
+
+@tag_run_solo
 class BFD4TestCase(VppTestCase):
     """Bidirectional Forwarding Detection (BFD)"""
 
 class BFD4TestCase(VppTestCase):
     """Bidirectional Forwarding Detection (BFD)"""
 
@@ -721,12 +780,14 @@ class BFD4TestCase(VppTestCase):
         self.vapi.want_bfd_events()
         self.pg0.enable_capture()
         try:
         self.vapi.want_bfd_events()
         self.pg0.enable_capture()
         try:
+            self.bfd_udp4_sessions = self.statistics['/bfd/udp4/sessions']
+            self.bfd_udp6_sessions = self.statistics['/bfd/udp6/sessions']
             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)
             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)
-        except:
+        except BaseException:
             self.vapi.want_bfd_events(enable_disable=0)
             raise
 
             self.vapi.want_bfd_events(enable_disable=0)
             raise
 
@@ -739,6 +800,10 @@ class BFD4TestCase(VppTestCase):
     def test_session_up(self):
         """ bring BFD session up """
         bfd_session_up(self)
     def test_session_up(self):
         """ bring BFD session up """
         bfd_session_up(self)
+        bfd_udp4_sessions = self.statistics['/bfd/udp4/sessions']
+        bfd_udp6_sessions = self.statistics['/bfd/udp6/sessions']
+        self.assert_equal(bfd_udp4_sessions - self.bfd_udp4_sessions, 1)
+        self.assert_equal(bfd_udp6_sessions, self.bfd_udp6_sessions)
 
     def test_session_up_by_ip(self):
         """ bring BFD session up - first frame looked up by address pair """
 
     def test_session_up_by_ip(self):
         """ bring BFD session up - first frame looked up by address pair """
@@ -754,12 +819,12 @@ class BFD4TestCase(VppTestCase):
         self.test_session.update(your_discriminator=p[BFD].my_discriminator,
                                  state=BFDState.up)
         self.logger.info("BFD: Waiting for event")
         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")
+        e = self.vapi.wait_for_event(1, "bfd_udp_session_event")
         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")
         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")
+        e = self.vapi.wait_for_event(1, "bfd_udp_session_event")
         verify_event(self, e, expected_state=BFDState.up)
         self.logger.info("BFD: Session is Up")
         self.test_session.update(state=BFDState.up)
         verify_event(self, e, expected_state=BFDState.up)
         self.logger.info("BFD: Session is Up")
         self.test_session.update(state=BFDState.up)
@@ -771,7 +836,6 @@ class BFD4TestCase(VppTestCase):
         bfd_session_up(self)
         bfd_session_down(self)
 
         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)
     def test_hold_up(self):
         """ hold BFD session up """
         bfd_session_up(self)
@@ -781,7 +845,6 @@ class BFD4TestCase(VppTestCase):
         self.assert_equal(len(self.vapi.collect_events()), 0,
                           "number of bfd events")
 
         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
     def test_slow_timer(self):
         """ verify slow periodic control frames while session down """
         packet_count = 3
@@ -796,7 +859,6 @@ class BFD4TestCase(VppTestCase):
                 time_diff, 0.70, 1.05, "time between slow packets")
             prev_packet = next_packet
 
                 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)
     def test_zero_remote_min_rx(self):
         """ no packets when zero remote required min rx interval """
         bfd_session_up(self)
@@ -821,17 +883,25 @@ class BFD4TestCase(VppTestCase):
         self.assert_equal(
             len(self.vapi.collect_events()), 0, "number of bfd events")
 
         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)
         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")
     def test_conn_down(self):
         """ verify session goes down after inactivity """
         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")
-        e = self.vapi.wait_for_event(1, "bfd_udp_session_details")
+        e = self.vapi.wait_for_event(1, "bfd_udp_session_event")
         verify_event(self, e, expected_state=BFDState.down)
 
         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)
     def test_large_required_min_rx(self):
         """ large remote required min rx interval """
         bfd_session_up(self)
@@ -861,7 +931,6 @@ class BFD4TestCase(VppTestCase):
                 break
         self.assert_equal(count, 0, "number of packets received")
 
                 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()
     def test_immediate_remote_min_rx_reduction(self):
         """ immediately honor remote required min rx reduction """
         self.vpp_session.remove_vpp_config()
@@ -894,7 +963,6 @@ class BFD4TestCase(VppTestCase):
                                  "time between BFD packets")
             reference_packet = p
 
                                  "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)
     def test_modify_req_min_rx_double(self):
         """ modify session - double required min rx """
         bfd_session_up(self)
@@ -918,13 +986,12 @@ class BFD4TestCase(VppTestCase):
                 self.vpp_session.required_min_rx) / USEC_IN_SEC
         self.test_session.send_packet(final)
         time_mark = time.time()
                 self.vpp_session.required_min_rx) / USEC_IN_SEC
         self.test_session.send_packet(final)
         time_mark = time.time()
-        e = self.vapi.wait_for_event(2 * timeout, "bfd_udp_session_details")
+        e = self.vapi.wait_for_event(2 * timeout, "bfd_udp_session_event")
         verify_event(self, e, expected_state=BFDState.down)
         time_to_event = time.time() - time_mark
         self.assert_in_range(time_to_event, .9 * timeout,
                              1.1 * timeout, "session timeout")
 
         verify_event(self, e, expected_state=BFDState.down)
         time_to_event = time.time() - time_mark
         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(
     def test_modify_req_min_rx_halve(self):
         """ modify session - halve required min rx """
         self.vpp_session.modify_parameters(
@@ -959,7 +1026,7 @@ class BFD4TestCase(VppTestCase):
             self.vpp_session.required_min_rx / USEC_IN_SEC
         before = time.time()
         e = self.vapi.wait_for_event(
             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")
+            2 * detection_time, "bfd_udp_session_event")
         after = time.time()
         self.assert_in_range(after - before,
                              0.9 * detection_time,
         after = time.time()
         self.assert_in_range(after - before,
                              0.9 * detection_time,
@@ -967,7 +1034,6 @@ class BFD4TestCase(VppTestCase):
                              "time before bfd session goes down")
         verify_event(self, e, expected_state=BFDState.down)
 
                              "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)
     def test_modify_detect_mult(self):
         """ modify detect multiplier """
         bfd_session_up(self)
@@ -991,7 +1057,6 @@ class BFD4TestCase(VppTestCase):
         self.assertNotIn("P", p.sprintf("%BFD.flags%"),
                          "Poll bit not set in BFD packet")
 
         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)
     def test_queued_poll(self):
         """ test poll sequence queueing """
         bfd_session_up(self)
@@ -1057,7 +1122,8 @@ class BFD4TestCase(VppTestCase):
         self.assertNotIn("P", p.sprintf("%BFD.flags%"),
                          "Poll bit set in BFD packet")
 
         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(config.extended, "part of extended tests")
     def test_poll_response(self):
         """ test correct response to control frame with poll bit set """
         bfd_session_up(self)
     def test_poll_response(self):
         """ test correct response to control frame with poll bit set """
         bfd_session_up(self)
@@ -1068,7 +1134,6 @@ class BFD4TestCase(VppTestCase):
             self, pcap_time_min=time.time() - self.vpp_clock_offset)
         self.assertIn("F", final.sprintf("%BFD.flags%"))
 
             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)
     def test_no_periodic_if_remote_demand(self):
         """ no periodic frames outside poll sequence if remote demand set """
         bfd_session_up(self)
@@ -1097,8 +1162,8 @@ class BFD4TestCase(VppTestCase):
 
     def test_echo_looped_back(self):
         """ echo packets looped back """
 
     def test_echo_looped_back(self):
         """ echo packets looped back """
-        # don't need a session in this case..
-        self.vpp_session.remove_vpp_config()
+        bfd_session_up(self)
+        stats_before = bfd_grab_stats_snapshot(self)
         self.pg0.enable_capture()
         echo_packet_count = 10
         # random source port low enough to increment a few times..
         self.pg0.enable_capture()
         echo_packet_count = 10
         # random source port low enough to increment a few times..
@@ -1117,7 +1182,10 @@ class BFD4TestCase(VppTestCase):
             self.logger.debug(ppp("Sending packet:", echo_packet))
             self.pg0.add_stream(echo_packet)
             self.pg_start()
             self.logger.debug(ppp("Sending packet:", echo_packet))
             self.pg0.add_stream(echo_packet)
             self.pg_start()
-        for dummy in range(echo_packet_count):
+            self.logger.debug(self.vapi.ppcli("show trace"))
+        counter = 0
+        bfd_control_packets_rx = 0
+        while counter < echo_packet_count:
             p = self.pg0.wait_for_packet(1)
             self.logger.debug(ppp("Got packet:", p))
             ether = p[Ether]
             p = self.pg0.wait_for_packet(1)
             self.logger.debug(ppp("Got packet:", p))
             ether = p[Ether]
@@ -1126,8 +1194,11 @@ class BFD4TestCase(VppTestCase):
             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.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]
             udp = p[UDP]
+            if udp.dport == BFD.udp_dport:
+                bfd_control_packets_rx += 1
+                continue
+            self.assert_equal(self.pg0.remote_ip4, ip.src, "Source IP")
             self.assert_equal(udp.dport, BFD.udp_dport_echo,
                               "UDP destination port")
             self.assert_equal(udp.sport, udp_sport_rx, "UDP source port")
             self.assert_equal(udp.dport, BFD.udp_dport_echo,
                               "UDP destination port")
             self.assert_equal(udp.sport, udp_sport_rx, "UDP source port")
@@ -1137,11 +1208,23 @@ class BFD4TestCase(VppTestCase):
             self.assertEqual(scapy.compat.raw(p[UDP].payload),
                              scapy.compat.raw(echo_packet[UDP].payload),
                              "Received packet is not the echo packet sent")
             self.assertEqual(scapy.compat.raw(p[UDP].payload),
                              scapy.compat.raw(echo_packet[UDP].payload),
                              "Received packet is not the echo packet sent")
+            counter += 1
         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)")
+        stats_after = bfd_grab_stats_snapshot(self)
+        diff = bfd_stats_diff(stats_before, stats_after)
+        self.assertEqual(
+            0, diff.rx, "RX counter bumped but no BFD packets sent")
+        self.assertEqual(
+            bfd_control_packets_rx, diff.tx, "TX counter incorrect")
+        self.assertEqual(0, diff.rx_echo,
+                         "RX echo counter bumped but no BFD session exists")
+        self.assertEqual(0, diff.tx_echo,
+                         "TX echo counter bumped but no BFD session exists")
 
     def test_echo(self):
         """ echo function """
 
     def test_echo(self):
         """ echo function """
+        stats_before = bfd_grab_stats_snapshot(self)
         bfd_session_up(self)
         self.test_session.update(required_min_echo_rx=150000)
         self.test_session.send_packet()
         bfd_session_up(self)
         self.test_session.update(required_min_echo_rx=150000)
         self.test_session.send_packet()
@@ -1158,7 +1241,8 @@ class BFD4TestCase(VppTestCase):
                           self.vpp_session.required_min_rx,
                           "BFD required min rx interval")
         self.test_session.send_packet()
                           self.vpp_session.required_min_rx,
                           "BFD required min rx interval")
         self.test_session.send_packet()
-        self.vapi.bfd_udp_set_echo_source(self.loopback0.sw_if_index)
+        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):
         echo_seen = False
         # should be turned on - loopback echo packets
         for dummy in range(3):
@@ -1176,9 +1260,12 @@ class BFD4TestCase(VppTestCase):
                                       "ECHO packet destination MAC address")
                     p[Ether].dst = self.pg0.local_mac
                     self.pg0.add_stream(p)
                                       "ECHO packet destination MAC address")
                     p[Ether].dst = self.pg0.local_mac
                     self.pg0.add_stream(p)
+                    self.test_session.rx_packets_echo += 1
+                    self.test_session.tx_packets_echo += 1
                     self.pg_start()
                     echo_seen = True
                 elif p.haslayer(BFD):
                     self.pg_start()
                     echo_seen = True
                 elif p.haslayer(BFD):
+                    self.test_session.rx_packets += 1
                     if echo_seen:
                         self.assertGreaterEqual(
                             p[BFD].required_min_rx_interval,
                     if echo_seen:
                         self.assertGreaterEqual(
                             p[BFD].required_min_rx_interval,
@@ -1195,7 +1282,20 @@ class BFD4TestCase(VppTestCase):
             self.test_session.send_packet()
         self.assertTrue(echo_seen, "No echo packets received")
 
             self.test_session.send_packet()
         self.assertTrue(echo_seen, "No echo packets received")
 
-    @unittest.skipUnless(running_extended_tests, "part of extended tests")
+        stats_after = bfd_grab_stats_snapshot(self)
+        diff = bfd_stats_diff(stats_before, stats_after)
+        # our rx is vpp tx and vice versa, also tolerate one packet off
+        self.assert_in_range(self.test_session.tx_packets,
+                             diff.rx - 1, diff.rx + 1, "RX counter")
+        self.assert_in_range(self.test_session.rx_packets,
+                             diff.tx - 1, diff.tx + 1, "TX counter")
+        self.assert_in_range(self.test_session.tx_packets_echo,
+                             diff.rx_echo - 1, diff.rx_echo + 1,
+                             "RX echo counter")
+        self.assert_in_range(self.test_session.rx_packets_echo,
+                             diff.tx_echo - 1, diff.tx_echo + 1,
+                             "TX echo counter")
+
     def test_echo_fail(self):
         """ session goes down if echo function fails """
         bfd_session_up(self)
     def test_echo_fail(self):
         """ session goes down if echo function fails """
         bfd_session_up(self)
@@ -1203,7 +1303,8 @@ class BFD4TestCase(VppTestCase):
         self.test_session.send_packet()
         detection_time = self.test_session.detect_mult *\
             self.vpp_session.required_min_rx / USEC_IN_SEC
         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):
         # echo function should be used now, but we will drop the echo packets
         verified_diag = False
         for dummy in range(3):
@@ -1235,13 +1336,13 @@ class BFD4TestCase(VppTestCase):
         self.assert_equal(events[0].state, BFDState.down, BFDState)
         self.assertTrue(verified_diag, "Incorrect diagnostics code received")
 
         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=150000)
         self.test_session.send_packet()
     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=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)
         # wait for first echo packet
         while True:
             p = self.pg0.wait_for_packet(1)
@@ -1267,13 +1368,13 @@ class BFD4TestCase(VppTestCase):
             events = self.vapi.collect_events()
             self.assert_equal(len(events), 0, "number of bfd events")
 
             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=150000)
         self.test_session.send_packet()
     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=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)
         # wait for first echo packet
         while True:
             p = self.pg0.wait_for_packet(1)
@@ -1299,12 +1400,12 @@ class BFD4TestCase(VppTestCase):
             events = self.vapi.collect_events()
             self.assert_equal(len(events), 0, "number of bfd events")
 
             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=150000)
     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=150000)
-        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)
         self.test_session.send_packet()
         # should be turned on - loopback echo packets
         echo_packet = None
         self.test_session.send_packet()
         # should be turned on - loopback echo packets
         echo_packet = None
@@ -1352,12 +1453,12 @@ class BFD4TestCase(VppTestCase):
             self.test_session.send_packet()
         self.assertTrue(timeout_ok, "Expected timeout event didn't occur")
 
             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=150000)
     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=150000)
-        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)
         self.test_session.send_packet()
         # should be turned on - loopback echo packets
         timeout_at = None
         self.test_session.send_packet()
         # should be turned on - loopback echo packets
         timeout_at = None
@@ -1402,13 +1503,12 @@ class BFD4TestCase(VppTestCase):
             self.test_session.send_packet()
         self.assertTrue(timeout_ok, "Expected timeout event didn't occur")
 
             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)
         self.vpp_session.admin_down()
         self.pg0.enable_capture()
     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")
+        e = self.vapi.wait_for_event(1, "bfd_udp_session_event")
         verify_event(self, e, expected_state=BFDState.admin_down)
         for dummy in range(2):
             p = wait_for_bfd_packet(self)
         verify_event(self, e, expected_state=BFDState.admin_down)
         for dummy in range(2):
             p = wait_for_bfd_packet(self)
@@ -1421,7 +1521,7 @@ class BFD4TestCase(VppTestCase):
             self.assert_equal(p[BFD].state, BFDState.admin_down, BFDState)
         self.vpp_session.admin_up()
         self.test_session.update(state=BFDState.down)
             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")
+        e = self.vapi.wait_for_event(1, "bfd_udp_session_event")
         verify_event(self, e, expected_state=BFDState.down)
         p = wait_for_bfd_packet(
             self, pcap_time_min=time.time() - self.vpp_clock_offset)
         verify_event(self, e, expected_state=BFDState.down)
         p = wait_for_bfd_packet(
             self, pcap_time_min=time.time() - self.vpp_clock_offset)
@@ -1430,17 +1530,16 @@ class BFD4TestCase(VppTestCase):
         p = wait_for_bfd_packet(
             self, pcap_time_min=time.time() - self.vpp_clock_offset)
         self.assert_equal(p[BFD].state, BFDState.init, BFDState)
         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")
+        e = self.vapi.wait_for_event(1, "bfd_udp_session_event")
         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)
         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")
+        e = self.vapi.wait_for_event(1, "bfd_udp_session_event")
         verify_event(self, e, expected_state=BFDState.up)
 
         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)
     def test_config_change_remote_demand(self):
         """ configuration change while peer in demand mode """
         bfd_session_up(self)
@@ -1488,12 +1587,13 @@ class BFD4TestCase(VppTestCase):
         vpp_session.add_vpp_config()
         vpp_session.admin_up()
         intf.remove_vpp_config()
         vpp_session.add_vpp_config()
         vpp_session.admin_up()
         intf.remove_vpp_config()
-        e = self.vapi.wait_for_event(1, "bfd_udp_session_details")
+        e = self.vapi.wait_for_event(1, "bfd_udp_session_event")
         self.assert_equal(e.sw_if_index, sw_if_index, "sw_if_index")
         self.assertFalse(vpp_session.query_vpp_config())
 
 
         self.assert_equal(e.sw_if_index, sw_if_index, "sw_if_index")
         self.assertFalse(vpp_session.query_vpp_config())
 
 
-@unittest.skipUnless(running_extended_tests, "part of extended tests")
+@tag_run_solo
+@tag_fixme_vpp_workers
 class BFD6TestCase(VppTestCase):
     """Bidirectional Forwarding Detection (BFD) (IPv6) """
 
 class BFD6TestCase(VppTestCase):
     """Bidirectional Forwarding Detection (BFD) (IPv6) """
 
@@ -1531,6 +1631,8 @@ class BFD6TestCase(VppTestCase):
         self.vapi.want_bfd_events()
         self.pg0.enable_capture()
         try:
         self.vapi.want_bfd_events()
         self.pg0.enable_capture()
         try:
+            self.bfd_udp4_sessions = self.statistics['/bfd/udp4/sessions']
+            self.bfd_udp6_sessions = self.statistics['/bfd/udp6/sessions']
             self.vpp_session = VppBFDUDPSession(self, self.pg0,
                                                 self.pg0.remote_ip6,
                                                 af=AF_INET6)
             self.vpp_session = VppBFDUDPSession(self, self.pg0,
                                                 self.pg0.remote_ip6,
                                                 af=AF_INET6)
@@ -1538,7 +1640,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"))
             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
 
             self.vapi.want_bfd_events(enable_disable=0)
             raise
 
@@ -1551,6 +1653,10 @@ class BFD6TestCase(VppTestCase):
     def test_session_up(self):
         """ bring BFD session up """
         bfd_session_up(self)
     def test_session_up(self):
         """ bring BFD session up """
         bfd_session_up(self)
+        bfd_udp4_sessions = self.statistics['/bfd/udp4/sessions']
+        bfd_udp6_sessions = self.statistics['/bfd/udp6/sessions']
+        self.assert_equal(bfd_udp4_sessions, self.bfd_udp4_sessions)
+        self.assert_equal(bfd_udp6_sessions - self.bfd_udp6_sessions, 1)
 
     def test_session_up_by_ip(self):
         """ bring BFD session up - first frame looked up by address pair """
 
     def test_session_up_by_ip(self):
         """ bring BFD session up - first frame looked up by address pair """
@@ -1566,19 +1672,18 @@ class BFD6TestCase(VppTestCase):
         self.test_session.update(your_discriminator=p[BFD].my_discriminator,
                                  state=BFDState.up)
         self.logger.info("BFD: Waiting for event")
         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")
+        e = self.vapi.wait_for_event(1, "bfd_udp_session_event")
         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")
         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")
+        e = self.vapi.wait_for_event(1, "bfd_udp_session_event")
         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)
 
         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)
 
-    @unittest.skipUnless(running_extended_tests, "part of extended tests")
     def test_hold_up(self):
         """ hold BFD session up """
         bfd_session_up(self)
     def test_hold_up(self):
         """ hold BFD session up """
         bfd_session_up(self)
@@ -1591,8 +1696,8 @@ class BFD6TestCase(VppTestCase):
 
     def test_echo_looped_back(self):
         """ echo packets looped back """
 
     def test_echo_looped_back(self):
         """ echo packets looped back """
-        # don't need a session in this case..
-        self.vpp_session.remove_vpp_config()
+        bfd_session_up(self)
+        stats_before = bfd_grab_stats_snapshot(self)
         self.pg0.enable_capture()
         echo_packet_count = 10
         # random source port low enough to increment a few times..
         self.pg0.enable_capture()
         echo_packet_count = 10
         # random source port low enough to increment a few times..
@@ -1611,7 +1716,9 @@ class BFD6TestCase(VppTestCase):
             self.logger.debug(ppp("Sending packet:", echo_packet))
             self.pg0.add_stream(echo_packet)
             self.pg_start()
             self.logger.debug(ppp("Sending packet:", echo_packet))
             self.pg0.add_stream(echo_packet)
             self.pg_start()
-        for dummy in range(echo_packet_count):
+        counter = 0
+        bfd_control_packets_rx = 0
+        while counter < echo_packet_count:
             p = self.pg0.wait_for_packet(1)
             self.logger.debug(ppp("Got packet:", p))
             ether = p[Ether]
             p = self.pg0.wait_for_packet(1)
             self.logger.debug(ppp("Got packet:", p))
             ether = p[Ether]
@@ -1620,8 +1727,11 @@ class BFD6TestCase(VppTestCase):
             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.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]
             udp = p[UDP]
+            if udp.dport == BFD.udp_dport:
+                bfd_control_packets_rx += 1
+                continue
+            self.assert_equal(self.pg0.remote_ip6, ip.src, "Source IP")
             self.assert_equal(udp.dport, BFD.udp_dport_echo,
                               "UDP destination port")
             self.assert_equal(udp.sport, udp_sport_rx, "UDP source port")
             self.assert_equal(udp.dport, BFD.udp_dport_echo,
                               "UDP destination port")
             self.assert_equal(udp.sport, udp_sport_rx, "UDP source port")
@@ -1631,13 +1741,23 @@ class BFD6TestCase(VppTestCase):
             self.assertEqual(scapy.compat.raw(p[UDP].payload),
                              scapy.compat.raw(echo_packet[UDP].payload),
                              "Received packet is not the echo packet sent")
             self.assertEqual(scapy.compat.raw(p[UDP].payload),
                              scapy.compat.raw(echo_packet[UDP].payload),
                              "Received packet is not the echo packet sent")
+            counter += 1
         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)")
-        self.assert_equal(udp_sport_tx, udp_sport_rx, "UDP source port (== "
-                          "ECHO packet identifier for test purposes)")
+        stats_after = bfd_grab_stats_snapshot(self)
+        diff = bfd_stats_diff(stats_before, stats_after)
+        self.assertEqual(
+            0, diff.rx, "RX counter bumped but no BFD packets sent")
+        self.assertEqual(bfd_control_packets_rx,
+                         diff.tx, "TX counter incorrect")
+        self.assertEqual(0, diff.rx_echo,
+                         "RX echo counter bumped but no BFD session exists")
+        self.assertEqual(0, diff.tx_echo,
+                         "TX echo counter bumped but no BFD session exists")
 
     def test_echo(self):
         """ echo function """
 
     def test_echo(self):
         """ echo function """
+        stats_before = bfd_grab_stats_snapshot(self)
         bfd_session_up(self)
         self.test_session.update(required_min_echo_rx=150000)
         self.test_session.send_packet()
         bfd_session_up(self)
         self.test_session.update(required_min_echo_rx=150000)
         self.test_session.send_packet()
@@ -1654,7 +1774,8 @@ class BFD6TestCase(VppTestCase):
                           self.vpp_session.required_min_rx,
                           "BFD required min rx interval")
         self.test_session.send_packet()
                           self.vpp_session.required_min_rx,
                           "BFD required min rx interval")
         self.test_session.send_packet()
-        self.vapi.bfd_udp_set_echo_source(self.loopback0.sw_if_index)
+        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):
         echo_seen = False
         # should be turned on - loopback echo packets
         for dummy in range(3):
@@ -1670,11 +1791,14 @@ class BFD6TestCase(VppTestCase):
                     self.logger.debug(ppp("Looping back packet:", p))
                     self.assert_equal(p[Ether].dst, self.pg0.remote_mac,
                                       "ECHO packet destination MAC address")
                     self.logger.debug(ppp("Looping back packet:", p))
                     self.assert_equal(p[Ether].dst, self.pg0.remote_mac,
                                       "ECHO packet destination MAC address")
+                    self.test_session.rx_packets_echo += 1
+                    self.test_session.tx_packets_echo += 1
                     p[Ether].dst = self.pg0.local_mac
                     self.pg0.add_stream(p)
                     self.pg_start()
                     echo_seen = True
                 elif p.haslayer(BFD):
                     p[Ether].dst = self.pg0.local_mac
                     self.pg0.add_stream(p)
                     self.pg_start()
                     echo_seen = True
                 elif p.haslayer(BFD):
+                    self.test_session.rx_packets += 1
                     if echo_seen:
                         self.assertGreaterEqual(
                             p[BFD].required_min_rx_interval,
                     if echo_seen:
                         self.assertGreaterEqual(
                             p[BFD].required_min_rx_interval,
@@ -1691,6 +1815,20 @@ class BFD6TestCase(VppTestCase):
             self.test_session.send_packet()
         self.assertTrue(echo_seen, "No echo packets received")
 
             self.test_session.send_packet()
         self.assertTrue(echo_seen, "No echo packets received")
 
+        stats_after = bfd_grab_stats_snapshot(self)
+        diff = bfd_stats_diff(stats_before, stats_after)
+        # our rx is vpp tx and vice versa, also tolerate one packet off
+        self.assert_in_range(self.test_session.tx_packets,
+                             diff.rx - 1, diff.rx + 1, "RX counter")
+        self.assert_in_range(self.test_session.rx_packets,
+                             diff.tx - 1, diff.tx + 1, "TX counter")
+        self.assert_in_range(self.test_session.tx_packets_echo,
+                             diff.rx_echo - 1, diff.rx_echo + 1,
+                             "RX echo counter")
+        self.assert_in_range(self.test_session.rx_packets_echo,
+                             diff.tx_echo - 1, diff.tx_echo + 1,
+                             "TX echo counter")
+
     def test_intf_deleted(self):
         """ interface with bfd session deleted """
         intf = VppLoInterface(self)
     def test_intf_deleted(self):
         """ interface with bfd session deleted """
         intf = VppLoInterface(self)
@@ -1702,12 +1840,12 @@ class BFD6TestCase(VppTestCase):
         vpp_session.add_vpp_config()
         vpp_session.admin_up()
         intf.remove_vpp_config()
         vpp_session.add_vpp_config()
         vpp_session.admin_up()
         intf.remove_vpp_config()
-        e = self.vapi.wait_for_event(1, "bfd_udp_session_details")
+        e = self.vapi.wait_for_event(1, "bfd_udp_session_event")
         self.assert_equal(e.sw_if_index, sw_if_index, "sw_if_index")
         self.assertFalse(vpp_session.query_vpp_config())
 
 
         self.assert_equal(e.sw_if_index, sw_if_index, "sw_if_index")
         self.assertFalse(vpp_session.query_vpp_config())
 
 
-@unittest.skipUnless(running_extended_tests, "part of extended tests")
+@tag_run_solo
 class BFDFIBTestCase(VppTestCase):
     """ BFD-FIB interactions (IPv6) """
 
 class BFDFIBTestCase(VppTestCase):
     """ BFD-FIB interactions (IPv6) """
 
@@ -1736,7 +1874,7 @@ class BFDFIBTestCase(VppTestCase):
 
     def tearDown(self):
         if not self.vpp_dead:
 
     def tearDown(self):
         if not self.vpp_dead:
-            self.vapi.want_bfd_events(enable_disable=0)
+            self.vapi.want_bfd_events(enable_disable=False)
 
         super(BFDFIBTestCase, self).tearDown()
 
 
         super(BFDFIBTestCase, self).tearDown()
 
@@ -1754,11 +1892,11 @@ class BFDFIBTestCase(VppTestCase):
         p = [(Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IPv6(src="3001::1", dst="2001::1") /
               UDP(sport=1234, dport=1234) /
         p = [(Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IPv6(src="3001::1", dst="2001::1") /
               UDP(sport=1234, dport=1234) /
-              Raw('\xa5' * 100)),
+              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) /
              (Ether(dst=self.pg0.local_mac, src=self.pg0.remote_mac) /
               IPv6(src="3001::1", dst="2002::1") /
               UDP(sport=1234, dport=1234) /
-              Raw('\xa5' * 100))]
+              Raw(b'\xa5' * 100))]
 
         # A recursive and a non-recursive route via a next-hop that
         # will have a BFD session
 
         # A recursive and a non-recursive route via a next-hop that
         # will have a BFD session
@@ -1813,7 +1951,7 @@ class BFDFIBTestCase(VppTestCase):
                              packet[IPv6].dst)
 
 
                              packet[IPv6].dst)
 
 
-@unittest.skipUnless(running_extended_tests, "part of extended tests")
+@unittest.skipUnless(config.extended, "part of extended tests")
 class BFDTunTestCase(VppTestCase):
     """ BFD over GRE tunnel """
 
 class BFDTunTestCase(VppTestCase):
     """ BFD over GRE tunnel """
 
@@ -1883,7 +2021,7 @@ class BFDTunTestCase(VppTestCase):
         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) /
         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('\xa5' * 100))]
+              Raw(b'\xa5' * 100))]
 
         # session is up - traffic passes
         bfd_session_up(self)
 
         # session is up - traffic passes
         bfd_session_up(self)
@@ -1894,7 +2032,7 @@ class BFDTunTestCase(VppTestCase):
         bfd_session_down(self)
 
 
         bfd_session_down(self)
 
 
-@unittest.skipUnless(running_extended_tests, "part of extended tests")
+@tag_run_solo
 class BFDSHA1TestCase(VppTestCase):
     """Bidirectional Forwarding Detection (BFD) (SHA1 auth) """
 
 class BFDSHA1TestCase(VppTestCase):
     """Bidirectional Forwarding Detection (BFD) (SHA1 auth) """
 
@@ -1929,7 +2067,7 @@ class BFDSHA1TestCase(VppTestCase):
 
     def tearDown(self):
         if not self.vpp_dead:
 
     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()
 
         self.vapi.collect_events()  # clear the event queue
         super(BFDSHA1TestCase, self).tearDown()
 
@@ -1947,7 +2085,6 @@ class BFDSHA1TestCase(VppTestCase):
             bfd_key_id=self.vpp_session.bfd_key_id)
         bfd_session_up(self)
 
             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)
     def test_hold_up(self):
         """ hold BFD session up """
         key = self.factory.create_random_key(self)
@@ -1966,7 +2103,6 @@ class BFDSHA1TestCase(VppTestCase):
             self.test_session.send_packet()
         self.assert_equal(self.vpp_session.state, BFDState.up, BFDState)
 
             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(
     def test_hold_up_meticulous(self):
         """ hold BFD session up - meticulous auth """
         key = self.factory.create_random_key(
@@ -1988,7 +2124,6 @@ class BFDSHA1TestCase(VppTestCase):
             self.test_session.send_packet()
         self.assert_equal(self.vpp_session.state, BFDState.up, BFDState)
 
             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(
     def test_send_bad_seq_number(self):
         """ session is not kept alive by msgs with bad sequence numbers"""
         key = self.factory.create_random_key(
@@ -2051,7 +2186,6 @@ class BFDSHA1TestCase(VppTestCase):
         wait_for_bfd_packet(self)
         self.assert_equal(self.vpp_session.state, BFDState.up, BFDState)
 
         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)
     def test_mismatch_auth(self):
         """ session is not brought down by unauthenticated msg """
         key = self.factory.create_random_key(self)
@@ -2066,7 +2200,6 @@ class BFDSHA1TestCase(VppTestCase):
                                             legitimate_test_session,
                                             rogue_test_session)
 
                                             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)
     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)
@@ -2086,7 +2219,6 @@ class BFDSHA1TestCase(VppTestCase):
                                             legitimate_test_session,
                                             rogue_test_session)
 
                                             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)
     def test_mismatched_auth_type(self):
         """ session is not brought down by msg with wrong auth type """
         key = self.factory.create_random_key(self)
@@ -2103,7 +2235,6 @@ class BFDSHA1TestCase(VppTestCase):
             vpp_session, legitimate_test_session, rogue_test_session,
             {'auth_type': BFDAuthType.keyed_md5})
 
             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(
     def test_restart(self):
         """ simulate remote peer restart and resynchronization """
         key = self.factory.create_random_key(
@@ -2129,10 +2260,11 @@ class BFDSHA1TestCase(VppTestCase):
         self.test_session.vpp_seq_number = None
         # now throw away any pending packets
         self.pg0.enable_capture()
         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)
 
 
         bfd_session_up(self)
 
 
-@unittest.skipUnless(running_extended_tests, "part of extended tests")
+@tag_run_solo
 class BFDAuthOnOffTestCase(VppTestCase):
     """Bidirectional Forwarding Detection (BFD) (changing auth) """
 
 class BFDAuthOnOffTestCase(VppTestCase):
     """Bidirectional Forwarding Detection (BFD) (changing auth) """
 
@@ -2166,7 +2298,7 @@ class BFDAuthOnOffTestCase(VppTestCase):
 
     def tearDown(self):
         if not self.vpp_dead:
 
     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()
 
         self.vapi.collect_events()  # clear the event queue
         super(BFDAuthOnOffTestCase, self).tearDown()
 
@@ -2345,7 +2477,7 @@ class BFDAuthOnOffTestCase(VppTestCase):
                           "number of bfd events")
 
 
                           "number of bfd events")
 
 
-@unittest.skipUnless(running_extended_tests, "part of extended tests")
+@tag_run_solo
 class BFDCLITestCase(VppTestCase):
     """Bidirectional Forwarding Detection (BFD) (CLI) """
     pg0 = None
 class BFDCLITestCase(VppTestCase):
     """Bidirectional Forwarding Detection (BFD) (CLI) """
     pg0 = None
@@ -2376,7 +2508,7 @@ class BFDCLITestCase(VppTestCase):
 
     def tearDown(self):
         try:
 
     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
         except UnexpectedApiReturnValueError:
             # some tests aren't subscribed, so this is not an issue
             pass
@@ -2386,12 +2518,16 @@ class BFDCLITestCase(VppTestCase):
     def cli_verify_no_response(self, cli):
         """ execute a CLI, asserting that the response is empty """
         self.assert_equal(self.vapi.cli(cli),
     def cli_verify_no_response(self, cli):
         """ execute a CLI, asserting that the response is empty """
         self.assert_equal(self.vapi.cli(cli),
-                          b"",
+                          "",
                           "CLI command response")
 
     def cli_verify_response(self, cli, expected):
         """ execute a CLI, asserting that the response matches expectation """
                           "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(),
+        try:
+            reply = self.vapi.cli(cli)
+        except CliFailedCommandError as cli_error:
+            reply = str(cli_error)
+        self.assert_equal(reply.strip(),
                           expected,
                           "CLI command response")
 
                           expected,
                           "CLI command response")
 
@@ -2744,16 +2880,15 @@ class BFDCLITestCase(VppTestCase):
                                  "IPv6 address usable as echo source: none" %
                                  self.loopback0.name)
         self.loopback0.config_ip4()
                                  "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))
+        echo_ip4 = str(ipaddress.IPv4Address(int(ipaddress.IPv4Address(
+            self.loopback0.local_ip4)) ^ 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))
         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))
+        echo_ip6 = str(ipaddress.IPv6Address(int(ipaddress.IPv6Address(
+            self.loopback0.local_ip6)) ^ 1))
         self.loopback0.config_ip6()
         self.cli_verify_response("show bfd echo-source",
                                  "UDP echo source is: %s\n"
         self.loopback0.config_ip6()
         self.cli_verify_response("show bfd echo-source",
                                  "UDP echo source is: %s\n"
@@ -2765,5 +2900,6 @@ class BFDCLITestCase(VppTestCase):
         self.cli_verify_response("show bfd echo-source",
                                  "UDP echo source is not set.")
 
         self.cli_verify_response("show bfd echo-source",
                                  "UDP echo source is not set.")
 
+
 if __name__ == '__main__':
     unittest.main(testRunner=VppTestRunner)
 if __name__ == '__main__':
     unittest.main(testRunner=VppTestRunner)