bfd: add per session counters 27/35327/1
authorKlement Sekera <klement.sekera@gmail.com>
Mon, 14 Feb 2022 20:20:22 +0000 (20:20 +0000)
committerKlement Sekera <klement.sekera@gmail.com>
Mon, 14 Feb 2022 20:21:01 +0000 (20:21 +0000)
Add udp total session counts - as stat segment entries:

/bfd/udp4/sessions
/bfd/udp6/sessions

and per session packet/byte counters:

/bfd/rx-session-counters
/bfd/rx-session-echo-counters
/bfd/tx-session-counters
/bfd/tx-session-echo-counters

These counters are per-thread and per-session id.

Adjust tests to verify proper function.

Type: refactor
Signed-off-by: Klement Sekera <klement.sekera@gmail.com>
Change-Id: Ie597928022b6ac74c2220019b9e8e1714295f170

src/vnet/bfd/bfd_main.c
src/vnet/bfd/bfd_main.h
src/vnet/bfd/bfd_udp.c
src/vnet/bfd/bfd_udp.h
test/test_bfd.py

index 23e3f0f..a9dd60e 100644 (file)
 #include <vlib/log.h>
 #include <vnet/crypto/crypto.h>
 
+static void
+bfd_validate_counters (bfd_main_t *bm)
+{
+  vlib_validate_combined_counter (&bm->rx_counter, pool_elts (bm->sessions));
+  vlib_validate_combined_counter (&bm->rx_echo_counter,
+                                 pool_elts (bm->sessions));
+  vlib_validate_combined_counter (&bm->tx_counter, pool_elts (bm->sessions));
+  vlib_validate_combined_counter (&bm->tx_echo_counter,
+                                 pool_elts (bm->sessions));
+}
+
 static u64
 bfd_calc_echo_checksum (u32 discriminator, u64 expire_time, u32 secret)
 {
@@ -727,11 +738,11 @@ bfd_transport_control_frame (vlib_main_t * vm, u32 bi, bfd_session_t * bs)
     {
     case BFD_TRANSPORT_UDP4:
       BFD_DBG ("Transport bfd via udp4, bs_idx=%u", bs->bs_idx);
-      return bfd_transport_udp4 (vm, bi, bs);
+      return bfd_transport_udp4 (vm, bi, bs, 0 /* is_echo */);
       break;
     case BFD_TRANSPORT_UDP6:
       BFD_DBG ("Transport bfd via udp6, bs_idx=%u", bs->bs_idx);
-      return bfd_transport_udp6 (vm, bi, bs);
+      return bfd_transport_udp6 (vm, bi, bs, 0 /* is_echo */);
       break;
     }
   return 0;
@@ -761,11 +772,11 @@ bfd_transport_echo (vlib_main_t * vm, u32 bi, bfd_session_t * bs)
     {
     case BFD_TRANSPORT_UDP4:
       BFD_DBG ("Transport bfd echo via udp4, bs_idx=%u", bs->bs_idx);
-      return bfd_transport_udp4 (vm, bi, bs);
+      return bfd_transport_udp4 (vm, bi, bs, 1 /* is_echo */);
       break;
     case BFD_TRANSPORT_UDP6:
       BFD_DBG ("Transport bfd echo via udp6, bs_idx=%u", bs->bs_idx);
-      return bfd_transport_udp6 (vm, bi, bs);
+      return bfd_transport_udp6 (vm, bi, bs, 1 /* is_echo */);
       break;
     }
   return 0;
@@ -1336,6 +1347,14 @@ bfd_main_init (vlib_main_t * vm)
   bm->owner_thread_index = ~0;
   if (n_vlib_mains > 1)
     clib_spinlock_init (&bm->lock);
+  bm->rx_counter.name = "bfd rx session counters";
+  bm->rx_counter.stat_segment_name = "/bfd/rx-session-counters";
+  bm->rx_echo_counter.name = "bfd rx session echo counters";
+  bm->rx_echo_counter.stat_segment_name = "/bfd/rx-session-echo-counters";
+  bm->tx_counter.name = "bfd tx session counters";
+  bm->tx_counter.stat_segment_name = "/bfd/tx-session-counters";
+  bm->tx_echo_counter.name = "bfd tx session echo counters";
+  bm->tx_echo_counter.stat_segment_name = "/bfd/tx-session-echo-counters";
   return 0;
 }
 
@@ -1371,6 +1390,11 @@ bfd_get_session (bfd_main_t * bm, bfd_transport_e t)
   while (hash_get (bm->session_by_disc, result->local_discr));
   bfd_set_defaults (bm, result);
   hash_set (bm->session_by_disc, result->local_discr, result->bs_idx);
+  bfd_validate_counters (bm);
+  vlib_zero_combined_counter (&bm->rx_counter, result->bs_idx);
+  vlib_zero_combined_counter (&bm->rx_echo_counter, result->bs_idx);
+  vlib_zero_combined_counter (&bm->tx_counter, result->bs_idx);
+  vlib_zero_combined_counter (&bm->tx_echo_counter, result->bs_idx);
   bfd_unlock (bm);
   return result;
 }
@@ -1392,6 +1416,10 @@ bfd_put_session (bfd_main_t * bm, bfd_session_t * bs)
       --bs->auth.next_key->use_count;
     }
   hash_unset (bm->session_by_disc, bs->local_discr);
+  vlib_zero_combined_counter (&bm->rx_counter, bs->bs_idx);
+  vlib_zero_combined_counter (&bm->rx_echo_counter, bs->bs_idx);
+  vlib_zero_combined_counter (&bm->tx_counter, bs->bs_idx);
+  vlib_zero_combined_counter (&bm->tx_echo_counter, bs->bs_idx);
   pool_put (bm->sessions, bs);
   bfd_unlock (bm);
 }
@@ -1905,8 +1933,8 @@ bfd_consume_pkt (vlib_main_t * vm, bfd_main_t * bm, const bfd_pkt_t * pkt,
     }
 }
 
-int
-bfd_consume_echo_pkt (vlib_main_t * vm, bfd_main_t * bm, vlib_buffer_t * b)
+bfd_session_t *
+bfd_consume_echo_pkt (vlib_main_t *vm, bfd_main_t *bm, vlib_buffer_t *b)
 {
   bfd_echo_pkt_t *pkt = NULL;
   if (b->current_length != sizeof (*pkt))
@@ -1926,7 +1954,7 @@ bfd_consume_echo_pkt (vlib_main_t * vm, bfd_main_t * bm, vlib_buffer_t * b)
   if (checksum != pkt->checksum)
     {
       BFD_DBG ("Invalid echo packet, checksum mismatch");
-      return 1;
+      return 0;
     }
   u64 now = bfd_time_now_nsec (vm, NULL);
   if (pkt->expire_time_nsec < now)
@@ -1938,7 +1966,7 @@ bfd_consume_echo_pkt (vlib_main_t * vm, bfd_main_t * bm, vlib_buffer_t * b)
     {
       bs->echo_last_rx_nsec = now;
     }
-  return 1;
+  return bs;
 }
 
 u8 *
index 5e9f025..2d91e68 100644 (file)
@@ -258,7 +258,7 @@ typedef enum
 } bfd_listen_event_e;
 
 /**
- * session nitification call back function type
+ * session notification call back function type
  */
 typedef void (*bfd_notify_fn_t) (bfd_listen_event_e, const bfd_session_t *);
 
@@ -322,6 +322,11 @@ typedef struct
   vlib_log_class_t log_class;
 
   u16 msg_id_base;
+
+  vlib_combined_counter_main_t rx_counter;
+  vlib_combined_counter_main_t rx_echo_counter;
+  vlib_combined_counter_main_t tx_counter;
+  vlib_combined_counter_main_t tx_echo_counter;
 } bfd_main_t;
 
 extern bfd_main_t bfd_main;
@@ -412,10 +417,11 @@ void bfd_put_session (bfd_main_t * bm, bfd_session_t * bs);
 bfd_session_t *bfd_find_session_by_idx (bfd_main_t * bm, uword bs_idx);
 bfd_session_t *bfd_find_session_by_disc (bfd_main_t * bm, u32 disc);
 void bfd_session_start (bfd_main_t * bm, bfd_session_t * bs);
+void bfd_session_stop (bfd_main_t *bm, bfd_session_t *bs);
 void bfd_consume_pkt (vlib_main_t * vm, bfd_main_t * bm,
                      const bfd_pkt_t * bfd, u32 bs_idx);
-int bfd_consume_echo_pkt (vlib_main_t * vm, bfd_main_t * bm,
-                         vlib_buffer_t * b);
+bfd_session_t *bfd_consume_echo_pkt (vlib_main_t *vm, bfd_main_t *bm,
+                                    vlib_buffer_t *b);
 int bfd_verify_pkt_common (const bfd_pkt_t * pkt);
 int bfd_verify_pkt_auth (vlib_main_t * vm, const bfd_pkt_t * pkt,
                         u16 pkt_size, bfd_session_t * bs);
index d8fd4a1..97b1a73 100644 (file)
@@ -35,6 +35,7 @@
 #include <vnet/dpo/receive_dpo.h>
 #include <vnet/fib/fib_entry.h>
 #include <vnet/fib/fib_table.h>
+#include <vpp/stats/stat_segment.h>
 #include <vnet/bfd/bfd_debug.h>
 #include <vnet/bfd/bfd_udp.h>
 #include <vnet/bfd/bfd_main.h>
@@ -68,8 +69,10 @@ typedef struct
   vlib_log_class_t log_class;
   /* number of active udp4 sessions */
   u32 udp4_sessions_count;
+  u32 udp4_sessions_count_stat_seg_entry;
   /* number of active udp6 sessions */
   u32 udp6_sessions_count;
+  u32 udp6_sessions_count_stat_seg_entry;
 } bfd_udp_main_t;
 
 static vlib_node_registration_t bfd_udp4_input_node;
@@ -79,6 +82,14 @@ static vlib_node_registration_t bfd_udp_echo6_input_node;
 
 bfd_udp_main_t bfd_udp_main;
 
+void
+bfd_udp_update_stat_segment_entry (u32 entry, u64 value)
+{
+  vlib_stat_segment_lock ();
+  stat_segment_set_state_counter (entry, value);
+  vlib_stat_segment_unlock ();
+}
+
 vnet_api_error_t
 bfd_udp_set_echo_source (u32 sw_if_index)
 {
@@ -94,7 +105,7 @@ bfd_udp_set_echo_source (u32 sw_if_index)
 }
 
 vnet_api_error_t
-bfd_udp_del_echo_source (u32 sw_if_index)
+bfd_udp_del_echo_source ()
 {
   bfd_udp_main.echo_source_sw_if_index = ~0;
   bfd_udp_main.echo_source_is_set = 0;
@@ -372,13 +383,18 @@ bfd_add_udp6_transport (vlib_main_t * vm, u32 bi, const bfd_session_t * bs,
 }
 
 static void
-bfd_create_frame_to_next_node (vlib_main_t * vm, u32 bi, u32 next_node)
+bfd_create_frame_to_next_node (vlib_main_t *vm, bfd_main_t *bm,
+                              const bfd_session_t *bs, u32 bi, u32 next_node,
+                              vlib_combined_counter_main_t *tx_counter)
 {
   vlib_frame_t *f = vlib_get_frame_to_node (vm, next_node);
   u32 *to_next = vlib_frame_vector_args (f);
   to_next[0] = bi;
   f->n_vectors = 1;
   vlib_put_frame_to_node (vm, next_node, f);
+  vlib_buffer_t *b = vlib_get_buffer (vm, bi);
+  vlib_increment_combined_counter (tx_counter, vm->thread_index, bs->bs_idx, 1,
+                                  vlib_buffer_length_in_chain (vm, b));
 }
 
 int
@@ -435,25 +451,33 @@ bfd_udp_calc_next_node (const struct bfd_session_s *bs, u32 * next_node)
 }
 
 int
-bfd_transport_udp4 (vlib_main_t * vm, u32 bi, const struct bfd_session_s *bs)
+bfd_transport_udp4 (vlib_main_t *vm, u32 bi, const struct bfd_session_s *bs,
+                   int is_echo)
 {
   u32 next_node;
   int rv = bfd_udp_calc_next_node (bs, &next_node);
+  bfd_main_t *bm = bfd_udp_main.bfd_main;
   if (rv)
     {
-      bfd_create_frame_to_next_node (vm, bi, next_node);
+      bfd_create_frame_to_next_node (vm, bm, bs, bi, next_node,
+                                    is_echo ? &bm->tx_echo_counter :
+                                              &bm->tx_counter);
     }
   return rv;
 }
 
 int
-bfd_transport_udp6 (vlib_main_t * vm, u32 bi, const struct bfd_session_s *bs)
+bfd_transport_udp6 (vlib_main_t *vm, u32 bi, const struct bfd_session_s *bs,
+                   int is_echo)
 {
   u32 next_node;
   int rv = bfd_udp_calc_next_node (bs, &next_node);
+  bfd_main_t *bm = bfd_udp_main.bfd_main;
   if (rv)
     {
-      bfd_create_frame_to_next_node (vm, bi, next_node);
+      bfd_create_frame_to_next_node (
+       vm, bfd_udp_main.bfd_main, bs, bi, next_node,
+       is_echo ? &bm->tx_echo_counter : &bm->tx_counter);
     }
   return 1;
 }
@@ -530,6 +554,8 @@ bfd_udp_add_session_internal (vlib_main_t * vm, bfd_udp_main_t * bum,
               "returns %d", format_ip46_address, &key->peer_addr,
               IP46_TYPE_ANY, key->sw_if_index, bus->adj_index);
       ++bum->udp4_sessions_count;
+      bfd_udp_update_stat_segment_entry (
+       bum->udp4_sessions_count_stat_seg_entry, bum->udp4_sessions_count);
       if (1 == bum->udp4_sessions_count)
        {
          udp_register_dst_port (vm, UDP_DST_PORT_bfd4,
@@ -547,6 +573,8 @@ bfd_udp_add_session_internal (vlib_main_t * vm, bfd_udp_main_t * bum,
               "returns %d", format_ip46_address, &key->peer_addr,
               IP46_TYPE_ANY, key->sw_if_index, bus->adj_index);
       ++bum->udp6_sessions_count;
+      bfd_udp_update_stat_segment_entry (
+       bum->udp6_sessions_count_stat_seg_entry, bum->udp6_sessions_count);
       if (1 == bum->udp6_sessions_count)
        {
          udp_register_dst_port (vm, UDP_DST_PORT_bfd6,
@@ -719,6 +747,8 @@ bfd_udp_del_session_internal (vlib_main_t * vm, bfd_session_t * bs)
     {
     case BFD_TRANSPORT_UDP4:
       --bum->udp4_sessions_count;
+      bfd_udp_update_stat_segment_entry (
+       bum->udp4_sessions_count_stat_seg_entry, bum->udp4_sessions_count);
       if (!bum->udp4_sessions_count)
        {
          udp_unregister_dst_port (vm, UDP_DST_PORT_bfd4, 1);
@@ -727,6 +757,8 @@ bfd_udp_del_session_internal (vlib_main_t * vm, bfd_session_t * bs)
       break;
     case BFD_TRANSPORT_UDP6:
       --bum->udp6_sessions_count;
+      bfd_udp_update_stat_segment_entry (
+       bum->udp6_sessions_count_stat_seg_entry, bum->udp6_sessions_count);
       if (!bum->udp6_sessions_count)
        {
          udp_unregister_dst_port (vm, UDP_DST_PORT_bfd6, 0);
@@ -1342,6 +1374,9 @@ bfd_udp_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
       next0 = BFD_UDP_INPUT_NEXT_NORMAL;
       if (BFD_UDP_ERROR_NONE == error0)
        {
+         vlib_increment_combined_counter (
+           &bm->rx_counter, vm->thread_index, bs->bs_idx, 1,
+           vlib_buffer_length_in_chain (vm, b0));
          /*
           *  if everything went fine, check for poll bit, if present, re-use
           *  the buffer and based on (now updated) session parameters, send
@@ -1488,8 +1523,9 @@ bfd_udp_echo_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
          clib_memcpy_fast (t0->data, vlib_buffer_get_current (b0), len);
        }
 
+      bfd_session_t *bs = NULL;
       bfd_lock (bm);
-      if (bfd_consume_echo_pkt (vm, bfd_udp_main.bfd_main, b0))
+      if ((bs = bfd_consume_echo_pkt (vm, bfd_udp_main.bfd_main, b0)))
        {
          b0->error = rt->errors[BFD_UDP_ERROR_NONE];
          next0 = BFD_UDP_ECHO_INPUT_NEXT_NORMAL;
@@ -1512,6 +1548,14 @@ bfd_udp_echo_input (vlib_main_t * vm, vlib_node_runtime_t * rt,
        }
 
       bfd_unlock (bm);
+
+      if (bs)
+       {
+         vlib_increment_combined_counter (
+           &bm->rx_echo_counter, vm->thread_index, bs->bs_idx, 1,
+           vlib_buffer_length_in_chain (vm, b0));
+       }
+
       vlib_set_next_frame_buffer (vm, rt, next0, bi0);
 
       from += 1;
@@ -1640,6 +1684,32 @@ bfd_udp_sw_if_add_del (CLIB_UNUSED (vnet_main_t *vnm), u32 sw_if_index,
 
 VNET_SW_INTERFACE_ADD_DEL_FUNCTION (bfd_udp_sw_if_add_del);
 
+clib_error_t *
+bfd_udp_stats_init (bfd_udp_main_t *bum)
+{
+  const char *name4 = "/bfd/udp4/sessions";
+  bum->udp4_sessions_count_stat_seg_entry =
+    stat_segment_new_entry ((u8 *) name4, STAT_DIR_TYPE_SCALAR_INDEX);
+
+  stat_segment_set_state_counter (bum->udp4_sessions_count_stat_seg_entry, 0);
+  if (~0 == bum->udp4_sessions_count_stat_seg_entry)
+    {
+      return clib_error_return (
+       0, "Could not create stat segment entry for %s", name4);
+    }
+  const char *name6 = "/bfd/udp6/sessions";
+  bum->udp6_sessions_count_stat_seg_entry =
+    stat_segment_new_entry ((u8 *) name6, STAT_DIR_TYPE_SCALAR_INDEX);
+
+  if (~0 == bum->udp6_sessions_count_stat_seg_entry)
+    {
+      return clib_error_return (
+       0, "Could not create stat segment entry for %s", name6);
+    }
+
+  return 0;
+}
+
 /*
  * setup function
  */
@@ -1671,6 +1741,8 @@ bfd_udp_init (vlib_main_t * vm)
   ASSERT (node);
   bfd_udp_main.ip6_midchain_idx = node->index;
 
+  bfd_udp_stats_init (&bfd_udp_main);
+
   bfd_udp_main.log_class = vlib_log_register_class ("bfd", "udp");
   vlib_log_debug (bfd_udp_main.log_class, "initialized");
   return 0;
index 8786810..866b586 100644 (file)
@@ -24,7 +24,6 @@
 #include <vnet/ip/ip6_packet.h>
 #include <vnet/bfd/bfd_api.h>
 
-/* *INDENT-OFF* */
 /** identifier of BFD session based on UDP transport only */
 typedef CLIB_PACKED (struct {
   union {
@@ -38,7 +37,6 @@ typedef CLIB_PACKED (struct {
   /** peer address */
   ip46_address_t peer_addr;
 }) bfd_udp_key_t;
-/* *INDENT-ON* */
 
 /** UDP transport specific data embedded in bfd_session's union */
 typedef struct
@@ -82,22 +80,18 @@ int bfd_add_udp6_transport (vlib_main_t * vm, u32 bi,
 /**
  * @brief transport packet over udpv4
  *
- * @param is_echo 1 if this is echo packet, 0 if control frame
- *
  * @return 1 on success, 0 on failure
  */
-int bfd_transport_udp4 (vlib_main_t * vm, u32 bi,
-                       const struct bfd_session_s *bs);
+int bfd_transport_udp4 (vlib_main_t *vm, u32 bi,
+                       const struct bfd_session_s *bs, int is_echo);
 
 /**
  * @brief transport packet over udpv6
  *
- * @param is_echo 1 if this is echo packet, 0 if control frame
- *
  * @return 1 on success, 0 on failure
  */
-int bfd_transport_udp6 (vlib_main_t * vm, u32 bi,
-                       const struct bfd_session_s *bs);
+int bfd_transport_udp6 (vlib_main_t *vm, u32 bi,
+                       const struct bfd_session_s *bs, int is_echo);
 
 /**
  * @brief check if the bfd udp layer is echo-capable at this time
index 7a444cb..174db3e 100644 (file)
@@ -4,6 +4,7 @@
 from __future__ import division
 
 import binascii
+from collections import namedtuple
 import hashlib
 import ipaddress
 import reprlib
@@ -373,6 +374,10 @@ class BFDTestSession(object):
         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 """
@@ -495,6 +500,7 @@ class BFDTestSession(object):
             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):
@@ -685,6 +691,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)
+        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 < "
@@ -707,6 +714,33 @@ def wait_for_bfd_packet(test, timeout=1, pcap_time_min=None, is_tunnel=False):
     return p
 
 
+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)"""
@@ -745,6 +779,8 @@ class BFD4TestCase(VppTestCase):
         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()
@@ -763,6 +799,10 @@ class BFD4TestCase(VppTestCase):
     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 """
@@ -1121,8 +1161,8 @@ class BFD4TestCase(VppTestCase):
 
     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..
@@ -1141,7 +1181,10 @@ class BFD4TestCase(VppTestCase):
             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]
@@ -1150,8 +1193,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.remote_ip4, ip.src, "Destination IP")
             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")
@@ -1161,11 +1207,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")
+            counter += 1
         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 """
+        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()
@@ -1201,9 +1259,12 @@ class BFD4TestCase(VppTestCase):
                                       "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.test_session.rx_packets += 1
                     if echo_seen:
                         self.assertGreaterEqual(
                             p[BFD].required_min_rx_interval,
@@ -1220,6 +1281,20 @@ class BFD4TestCase(VppTestCase):
             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_echo_fail(self):
         """ session goes down if echo function fails """
         bfd_session_up(self)
@@ -1555,6 +1630,8 @@ class BFD6TestCase(VppTestCase):
         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)
@@ -1575,6 +1652,10 @@ class BFD6TestCase(VppTestCase):
     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 """
@@ -1614,8 +1695,8 @@ class BFD6TestCase(VppTestCase):
 
     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..
@@ -1634,7 +1715,9 @@ class BFD6TestCase(VppTestCase):
             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]
@@ -1643,8 +1726,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.remote_ip6, ip.src, "Destination IP")
             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")
@@ -1654,13 +1740,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")
+            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 """
+        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()
@@ -1694,11 +1790,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.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):
+                    self.test_session.rx_packets += 1
                     if echo_seen:
                         self.assertGreaterEqual(
                             p[BFD].required_min_rx_interval,
@@ -1715,6 +1814,20 @@ class BFD6TestCase(VppTestCase):
             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)