BFD: loop back echo packets

Change-Id: I772b63ac25ebfccaff9ab9d8d0b1445e85f21df7
Signed-off-by: Klement Sekera <ksekera@cisco.com>
diff --git a/src/vnet/bfd/bfd_main.c b/src/vnet/bfd/bfd_main.c
index 8bb8de3..0959d0e 100644
--- a/src/vnet/bfd/bfd_main.c
+++ b/src/vnet/bfd/bfd_main.c
@@ -264,8 +264,7 @@
 static void
 bfd_set_effective_desired_min_tx (bfd_main_t * bm,
 				  bfd_session_t * bs, u64 now,
-				  u64 desired_min_tx_clocks,
-				  int handling_wakeup)
+				  u64 desired_min_tx_clocks)
 {
   bs->effective_desired_min_tx_clocks = desired_min_tx_clocks;
   BFD_DBG ("Set effective desired min tx to " BFD_CLK_FMT,
@@ -273,20 +272,17 @@
   bfd_recalc_detection_time (bm, bs);
   bfd_recalc_tx_interval (bm, bs);
   bfd_calc_next_tx (bm, bs, now);
-  bfd_set_timer (bm, bs, now, handling_wakeup);
 }
 
 static void
 bfd_set_effective_required_min_rx (bfd_main_t * bm,
 				   bfd_session_t * bs, u64 now,
-				   u64 required_min_rx_clocks,
-				   int handling_wakeup)
+				   u64 required_min_rx_clocks)
 {
   bs->effective_required_min_rx_clocks = required_min_rx_clocks;
   BFD_DBG ("Set effective required min rx to " BFD_CLK_FMT,
 	   BFD_CLK_PRN (bs->effective_required_min_rx_clocks));
   bfd_recalc_detection_time (bm, bs);
-  bfd_set_timer (bm, bs, now, handling_wakeup);
 }
 
 static void
@@ -424,42 +420,39 @@
   switch (bs->local_state)
     {
     case BFD_STATE_admin_down:
-      bfd_set_effective_required_min_rx (bm, bs, now,
-					 bs->config_required_min_rx_clocks,
-					 handling_wakeup);
       bfd_set_effective_desired_min_tx (bm, bs, now,
 					clib_max
 					(bs->config_desired_min_tx_clocks,
-					 bm->default_desired_min_tx_clocks),
-					handling_wakeup);
+					 bm->default_desired_min_tx_clocks));
+      bfd_set_effective_required_min_rx (bm, bs, now,
+					 bs->config_required_min_rx_clocks);
+      bfd_set_timer (bm, bs, now, handling_wakeup);
       break;
     case BFD_STATE_down:
-      bfd_set_effective_required_min_rx (bm, bs, now,
-					 bs->config_required_min_rx_clocks,
-					 handling_wakeup);
       bfd_set_effective_desired_min_tx (bm, bs, now,
 					clib_max
 					(bs->config_desired_min_tx_clocks,
-					 bm->default_desired_min_tx_clocks),
-					handling_wakeup);
+					 bm->default_desired_min_tx_clocks));
+      bfd_set_effective_required_min_rx (bm, bs, now,
+					 bs->config_required_min_rx_clocks);
+      bfd_set_timer (bm, bs, now, handling_wakeup);
       break;
     case BFD_STATE_init:
       bfd_set_effective_desired_min_tx (bm, bs, now,
 					clib_max
 					(bs->config_desired_min_tx_clocks,
-					 bm->default_desired_min_tx_clocks),
-					handling_wakeup);
+					 bm->default_desired_min_tx_clocks));
+      bfd_set_timer (bm, bs, now, handling_wakeup);
       break;
     case BFD_STATE_up:
+      bfd_set_effective_desired_min_tx (bm, bs, now,
+					bs->config_desired_min_tx_clocks);
       if (POLL_NOT_NEEDED == bs->poll_state)
 	{
 	  bfd_set_effective_required_min_rx (bm, bs, now,
-					     bs->config_required_min_rx_clocks,
-					     handling_wakeup);
+					     bs->config_required_min_rx_clocks);
 	}
-      bfd_set_effective_desired_min_tx (bm, bs, now,
-					bs->config_desired_min_tx_clocks,
-					handling_wakeup);
+      bfd_set_timer (bm, bs, now, handling_wakeup);
       break;
     }
 }
@@ -1401,9 +1394,9 @@
       if (BFD_STATE_up == bs->local_state)
 	{
 	  bfd_set_effective_required_min_rx (bm, bs, now,
-					     bs->config_required_min_rx_clocks,
-					     0);
+					     bs->config_required_min_rx_clocks);
 	  bfd_recalc_detection_time (bm, bs);
+	  bfd_set_timer (bm, bs, now, 0);
 	}
     }
   if (BFD_STATE_admin_down == bs->local_state)
diff --git a/src/vnet/bfd/bfd_udp.c b/src/vnet/bfd/bfd_udp.c
index 75b3597..8519009 100644
--- a/src/vnet/bfd/bfd_udp.c
+++ b/src/vnet/bfd/bfd_udp.c
@@ -42,6 +42,8 @@
 
 static vlib_node_registration_t bfd_udp4_input_node;
 static vlib_node_registration_t bfd_udp6_input_node;
+static vlib_node_registration_t bfd_udp_echo4_input_node;
+static vlib_node_registration_t bfd_udp_echo6_input_node;
 
 bfd_udp_main_t bfd_udp_main;
 
@@ -594,11 +596,10 @@
   BFD_UDP_INPUT_N_NEXT,
 } bfd_udp_input_next_t;
 
-/* Packet counters */
+/* Packet counters - BFD control frames */
 #define foreach_bfd_udp_error(F)           \
   F (NONE, "good bfd packets (processed)") \
-  F (BAD, "invalid bfd packets")           \
-  F (DISABLED, "bfd packets received on disabled interfaces")
+  F (BAD, "invalid bfd packets")
 
 #define F(sym, string) static char BFD_UDP_ERR_##sym##_STR[] = string;
 foreach_bfd_udp_error (F);
@@ -618,9 +619,32 @@
     BFD_UDP_N_ERROR,
 } bfd_udp_error_t;
 
+/* Packet counters - BFD ECHO packets */
+#define foreach_bfd_udp_echo_error(F)           \
+  F (NONE, "good bfd echo packets (processed)") \
+  F (BAD, "invalid bfd echo packets")
+
+#define F(sym, string) static char BFD_UDP_ECHO_ERR_##sym##_STR[] = string;
+foreach_bfd_udp_echo_error (F);
+#undef F
+
+static char *bfd_udp_echo_error_strings[] = {
+#define F(sym, string) BFD_UDP_ECHO_ERR_##sym##_STR,
+  foreach_bfd_udp_echo_error (F)
+#undef F
+};
+
+typedef enum
+{
+#define F(sym, str) BFD_UDP_ECHO_ERROR_##sym,
+  foreach_bfd_udp_echo_error (F)
+#undef F
+    BFD_UDP_ECHO_N_ERROR,
+} bfd_udp_echo_error_t;
+
 static void
-bfd_udp4_find_headers (vlib_buffer_t * b, const ip4_header_t ** ip4,
-		       const udp_header_t ** udp)
+bfd_udp4_find_headers (vlib_buffer_t * b, ip4_header_t ** ip4,
+		       udp_header_t ** udp)
 {
   /* sanity check first */
   const i32 start = vnet_buffer (b)->ip.start_of_ip_header;
@@ -714,8 +738,8 @@
 	 b->current_length, sizeof (*pkt));
       return BFD_UDP_ERROR_BAD;
     }
-  const ip4_header_t *ip4;
-  const udp_header_t *udp;
+  ip4_header_t *ip4;
+  udp_header_t *udp;
   bfd_udp4_find_headers (b, &ip4, &udp);
   if (!ip4 || !udp)
     {
@@ -776,8 +800,8 @@
 }
 
 static void
-bfd_udp6_find_headers (vlib_buffer_t * b, const ip6_header_t ** ip6,
-		       const udp_header_t ** udp)
+bfd_udp6_find_headers (vlib_buffer_t * b, ip6_header_t ** ip6,
+		       udp_header_t ** udp)
 {
   /* sanity check first */
   const i32 start = vnet_buffer (b)->ip.start_of_ip_header;
@@ -856,8 +880,8 @@
 	 b->current_length, sizeof (*pkt));
       return BFD_UDP_ERROR_BAD;
     }
-  const ip6_header_t *ip6;
-  const udp_header_t *udp;
+  ip6_header_t *ip6;
+  udp_header_t *udp;
   bfd_udp6_find_headers (b, &ip6, &udp);
   if (!ip6 || !udp)
     {
@@ -1057,6 +1081,185 @@
 };
 /* *INDENT-ON* */
 
+/**
+ * @brief swap the source and destination IP addresses in the packet
+ */
+static int
+bfd_echo_address_swap (vlib_buffer_t * b, int is_ipv6)
+{
+  udp_header_t *dummy = NULL;
+  if (is_ipv6)
+    {
+      ip6_header_t *ip6 = NULL;
+      bfd_udp6_find_headers (b, &ip6, &dummy);
+      if (!ip6)
+	{
+	  return 0;
+	}
+      ip6_address_t tmp = ip6->dst_address;
+      ip6->dst_address = ip6->src_address;
+      ip6->src_address = tmp;
+      vlib_buffer_advance (b,
+			   (u8 *) ip6 - (u8 *) vlib_buffer_get_current (b));
+    }
+  else
+    {
+      ip4_header_t *ip4 = NULL;
+      bfd_udp4_find_headers (b, &ip4, &dummy);
+      if (!ip4)
+	{
+	  return 0;
+	}
+      ip4_address_t tmp = ip4->dst_address;
+      ip4->dst_address = ip4->src_address;
+      ip4->src_address = tmp;
+      vlib_buffer_advance (b,
+			   (u8 *) ip4 - (u8 *) vlib_buffer_get_current (b));
+    }
+  return 1;
+}
+
+/*
+ * Process a frame of bfd echo packets
+ * Expect 1 packet / frame
+ */
+static uword
+bfd_udp_echo_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
+		    vlib_frame_t * f, int is_ipv6)
+{
+  u32 n_left_from, *from;
+  bfd_input_trace_t *t0;
+
+  from = vlib_frame_vector_args (f);	/* array of buffer indices */
+  n_left_from = f->n_vectors;	/* number of buffer indices */
+
+  while (n_left_from > 0)
+    {
+      u32 bi0;
+      vlib_buffer_t *b0;
+      u32 next0;
+
+      bi0 = from[0];
+      b0 = vlib_get_buffer (vm, bi0);
+
+      /* If this pkt is traced, snapshot the data */
+      if (b0->flags & VLIB_BUFFER_IS_TRACED)
+	{
+	  int len;
+	  t0 = vlib_add_trace (vm, rt, b0, sizeof (*t0));
+	  len = (b0->current_length < sizeof (t0->data)) ? b0->current_length
+	    : sizeof (t0->data);
+	  t0->len = len;
+	  clib_memcpy (t0->data, vlib_buffer_get_current (b0), len);
+	}
+
+      if (bfd_echo_address_swap (b0, is_ipv6))
+	{
+	  /* loop back the packet */
+	  b0->error = rt->errors[BFD_UDP_ERROR_NONE];
+	  if (is_ipv6)
+	    {
+	      vlib_node_increment_counter (vm, bfd_udp_echo6_input_node.index,
+					   b0->error, 1);
+	    }
+	  else
+	    {
+	      vlib_node_increment_counter (vm, bfd_udp_echo4_input_node.index,
+					   b0->error, 1);
+	    }
+	  next0 = BFD_UDP_INPUT_NEXT_REPLY;
+	}
+      else
+	{
+	  b0->error = rt->errors[BFD_UDP_ERROR_BAD];
+	  next0 = BFD_UDP_INPUT_NEXT_NORMAL;
+	}
+
+      vlib_set_next_frame_buffer (vm, rt, next0, bi0);
+
+      from += 1;
+      n_left_from -= 1;
+    }
+
+  return f->n_vectors;
+}
+
+static uword
+bfd_udp_echo4_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
+		     vlib_frame_t * f)
+{
+  return bfd_udp_echo_input (vm, rt, f, 0);
+}
+
+u8 *
+bfd_echo_input_format_trace (u8 * s, va_list * args)
+{
+  CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
+  CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
+  const bfd_udp_echo_input_trace_t *t =
+    va_arg (*args, bfd_udp_echo_input_trace_t *);
+  if (t->len > STRUCT_SIZE_OF (bfd_pkt_t, head))
+    {
+      s = format (s, "BFD ECHO:\n");
+      s = format (s, "    data: %U", format_hexdump, t->data, t->len);
+    }
+
+  return s;
+}
+
+/*
+ * bfd input graph node declaration
+ */
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (bfd_udp_echo4_input_node, static) = {
+  .function = bfd_udp_echo4_input,
+  .name = "bfd-udp-echo4-input",
+  .vector_size = sizeof (u32),
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = BFD_UDP_ECHO_N_ERROR,
+  .error_strings = bfd_udp_error_strings,
+
+  .format_trace = bfd_echo_input_format_trace,
+
+  .n_next_nodes = BFD_UDP_INPUT_N_NEXT,
+  .next_nodes =
+      {
+              [BFD_UDP_INPUT_NEXT_NORMAL] = "error-drop",
+              [BFD_UDP_INPUT_NEXT_REPLY] = "ip4-lookup",
+      },
+};
+/* *INDENT-ON* */
+
+static uword
+bfd_udp_echo6_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
+		     vlib_frame_t * f)
+{
+  return bfd_udp_echo_input (vm, rt, f, 1);
+}
+
+/* *INDENT-OFF* */
+VLIB_REGISTER_NODE (bfd_udp_echo6_input_node, static) = {
+  .function = bfd_udp_echo6_input,
+  .name = "bfd-udp-echo6-input",
+  .vector_size = sizeof (u32),
+  .type = VLIB_NODE_TYPE_INTERNAL,
+
+  .n_errors = BFD_UDP_ECHO_N_ERROR,
+  .error_strings = bfd_udp_echo_error_strings,
+
+  .format_trace = bfd_echo_input_format_trace,
+
+  .n_next_nodes = BFD_UDP_INPUT_N_NEXT,
+  .next_nodes =
+      {
+              [BFD_UDP_INPUT_NEXT_NORMAL] = "error-drop",
+              [BFD_UDP_INPUT_NEXT_REPLY] = "ip6-lookup",
+      },
+};
+
+/* *INDENT-ON* */
+
 static clib_error_t *
 bfd_sw_interface_up_down (vnet_main_t * vnm, u32 sw_if_index, u32 flags)
 {
@@ -1093,6 +1296,10 @@
   bfd_udp_main.bfd_main = &bfd_main;
   udp_register_dst_port (vm, UDP_DST_PORT_bfd4, bfd_udp4_input_node.index, 1);
   udp_register_dst_port (vm, UDP_DST_PORT_bfd6, bfd_udp6_input_node.index, 0);
+  udp_register_dst_port (vm, UDP_DST_PORT_bfd_echo4,
+			 bfd_udp_echo4_input_node.index, 1);
+  udp_register_dst_port (vm, UDP_DST_PORT_bfd_echo6,
+			 bfd_udp_echo6_input_node.index, 0);
   return 0;
 }
 
diff --git a/src/vnet/bfd/bfd_udp.h b/src/vnet/bfd/bfd_udp.h
index 26e8985..502e231 100644
--- a/src/vnet/bfd/bfd_udp.h
+++ b/src/vnet/bfd/bfd_udp.h
@@ -40,6 +40,13 @@
   adj_index_t adj_index;
 } bfd_udp_session_t;
 
+/* bfd udp echo packet trace capture */
+typedef struct
+{
+  u32 len;
+  u8 data[400];
+} bfd_udp_echo_input_trace_t;
+
 struct bfd_session_s;
 
 void bfd_add_udp4_transport (vlib_main_t * vm, vlib_buffer_t * b,
diff --git a/src/vnet/ip/udp.h b/src/vnet/ip/udp.h
index 03c62e0..bad58b5 100644
--- a/src/vnet/ip/udp.h
+++ b/src/vnet/ip/udp.h
@@ -38,7 +38,8 @@
 _ (67, dhcp_to_server)                          \
 _ (68, dhcp_to_client)                          \
 _ (500, ikev2)                                  \
-_ (3784, bfd4)                                   \
+_ (3784, bfd4)                                  \
+_ (3785, bfd_echo4)                             \
 _ (4341, lisp_gpe)                              \
 _ (4342, lisp_cp)                          	\
 _ (4739, ipfix)                                 \
@@ -51,7 +52,8 @@
 #define foreach_udp6_dst_port                   \
 _ (547, dhcpv6_to_server)                       \
 _ (546, dhcpv6_to_client)			\
-_ (3784, bfd6)                                   \
+_ (3784, bfd6)                                  \
+_ (3785, bfd_echo6)                             \
 _ (4341, lisp_gpe6)                             \
 _ (4342, lisp_cp6)                          	\
 _ (4790, vxlan6_gpe)      \
diff --git a/test/bfd.py b/test/bfd.py
index 09a7681..8eb3b36 100644
--- a/test/bfd.py
+++ b/test/bfd.py
@@ -6,7 +6,7 @@
 from scapy.layers.inet import UDP
 from scapy.packet import Packet
 from scapy.fields import BitField, BitEnumField, XByteField, FlagsField,\
-        ConditionalField, StrField
+    ConditionalField, StrField
 from vpp_object import VppObject
 from util import NumericConstant
 
@@ -110,6 +110,7 @@
     """ BFD protocol layer for scapy """
 
     udp_dport = 3784  #: BFD destination port per RFC 5881
+    udp_dport_echo = 3785  # : BFD destination port for ECHO per RFC 5881
     udp_sport_min = 49152  #: BFD source port min value per RFC 5881
     udp_sport_max = 65535  #: BFD source port max value per RFC 5881
     bfd_pkt_len = 24  # : length of BFD pkt without authentication section
diff --git a/test/framework.py b/test/framework.py
index 8dd61aa..beed180 100644
--- a/test/framework.py
+++ b/test/framework.py
@@ -236,6 +236,7 @@
             cls.pump_thread_stop_flag = Event()
             cls.pump_thread_wakeup_pipe = os.pipe()
             cls.pump_thread = Thread(target=pump_output, args=(cls,))
+            cls.pump_thread.daemon = True
             cls.pump_thread.start()
             cls.vapi = VppPapiProvider(cls.shm_prefix, cls.shm_prefix, cls)
             if cls.step:
diff --git a/test/test_bfd.py b/test/test_bfd.py
index 0ba0b46..64e9301 100644
--- a/test/test_bfd.py
+++ b/test/test_bfd.py
@@ -8,6 +8,7 @@
 import time
 from random import randint, shuffle
 from socket import AF_INET, AF_INET6
+from scapy.packet import Raw
 from scapy.layers.l2 import Ether
 from scapy.layers.inet import UDP, IP
 from scapy.layers.inet6 import IPv6
@@ -836,7 +837,6 @@
 
     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 +846,7 @@
                   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:
@@ -861,6 +861,48 @@
         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.local_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.local_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
+            self.assertTrue(p.haslayer(Raw) and p[Raw] == echo_packet[Raw],
+                            "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)")
+
 
 class BFD6TestCase(VppTestCase):
     """Bidirectional Forwarding Detection (BFD) (IPv6) """
@@ -914,13 +956,55 @@
     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.local_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.local_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
+            self.assertTrue(p.haslayer(Raw) and p[Raw] == echo_packet[Raw],
+                            "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)")
+
 
 class BFDSHA1TestCase(VppTestCase):
     """Bidirectional Forwarding Detection (BFD) (SHA1 auth) """
@@ -982,7 +1066,7 @@
             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)
@@ -1195,14 +1279,14 @@
         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()
@@ -1223,7 +1307,7 @@
             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 +1315,7 @@
         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()
@@ -1254,14 +1338,14 @@
             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()
@@ -1279,18 +1363,18 @@
         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()
@@ -1310,19 +1394,19 @@
             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 +1428,19 @@
             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()