bfd: add API to configure TOS for IP of BFD packets 39/43439/9
authorMihut Aronovici <[email protected]>
Mon, 14 Jul 2025 19:32:22 +0000 (15:32 -0400)
committerAndrew Yourtchenko <[email protected]>
Fri, 19 Sep 2025 21:29:27 +0000 (21:29 +0000)
Change-Id: I2753b5b200791130b83fa07b0fa731e636f79252
Signed-off-by: Mihut Aronovici <[email protected]>
Type: improvement

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

index cf14455..73ae308 100644 (file)
@@ -369,6 +369,37 @@ autoreply define bfd_udp_enable_multihop
   u32 context;
 };
 
+/** \brief BFD UDP - set TOS for the IP part of the BFD packet
+    @param client_index - opaque cookie to identify the sender
+    @param context - sender context, to match reply w/ request
+    @param tos - TOS for the IP portion of the BFD packet
+*/
+autoreply define bfd_udp_set_tos
+{
+  u32 client_index;
+  u32 context;
+  u8 tos;
+};
+
+
+define bfd_udp_get_tos
+{
+  u32 client_index;
+  u32 context;
+};
+
+/** \brief Reply to get the TOS value
+    @param context - returned sender context, to match reply w/ request
+    @param tos - The TOS value taht was set
+*/
+
+define bfd_udp_get_tos_reply
+{
+  u32 context;
+  i32 retval;
+  u8 tos;
+};
+
 /* must be compatible with bfd_error_t */
 counters bfd_udp {
   none {
index bccf58b..44fb8ef 100644 (file)
@@ -464,6 +464,26 @@ vl_api_bfd_udp_enable_multihop_t_handler (vl_api_bfd_udp_enable_multihop_t *mp)
   REPLY_MACRO (VL_API_BFD_UDP_ENABLE_MULTIHOP_REPLY);
 }
 
+static void
+vl_api_bfd_udp_set_tos_t_handler (vl_api_bfd_udp_set_tos_t *mp)
+{
+  vl_api_bfd_udp_set_tos_reply_t *rmp;
+  int rv = 0;
+
+  bfd_main.tos = mp->tos;
+
+  REPLY_MACRO (VL_API_BFD_UDP_SET_TOS_REPLY);
+}
+
+static void
+vl_api_bfd_udp_get_tos_t_handler (vl_api_bfd_udp_get_tos_t *mp)
+{
+  vl_api_bfd_udp_get_tos_reply_t *rmp;
+  int rv = 0;
+
+  REPLY_MACRO2 (VL_API_BFD_UDP_GET_TOS_REPLY, ({ rmp->tos = bfd_main.tos; }));
+}
+
 #include <vnet/bfd/bfd.api.c>
 static clib_error_t *
 bfd_api_hookup (vlib_main_t * vm)
index 7d92539..538bf9d 100644 (file)
@@ -324,6 +324,9 @@ typedef struct
    */
   bool multihop_enabled;
 
+  /** TOS field value of the IP part of the BFD packet */
+  u8 tos;
+
   /** log class */
   vlib_log_class_t log_class;
 
index 6d3202c..f4b9682 100644 (file)
@@ -283,6 +283,7 @@ bfd_add_udp4_transport (vlib_main_t * vm, u32 bi, const bfd_session_t * bs,
   headers->ip4.ip_version_and_header_length = 0x45;
   headers->ip4.ttl = 255;
   headers->ip4.protocol = IP_PROTOCOL_UDP;
+  headers->ip4.tos = bfd_main.tos;
   headers->udp.src_port =
     clib_host_to_net_u16 (bfd_udp_bs_idx_to_sport (bs->bs_idx));
   if (is_echo)
@@ -346,7 +347,8 @@ bfd_add_udp6_transport (vlib_main_t * vm, u32 bi, const bfd_session_t * bs,
   headers = vlib_buffer_get_current (b);
   clib_memset (headers, 0, sizeof (*headers));
   headers->ip6.ip_version_traffic_class_and_flow_label =
-    clib_host_to_net_u32 (0x6 << 28);
+    clib_host_to_net_u32 ((0x6 << 28) + (bfd_main.tos << 20));
+
   headers->ip6.hop_limit = 255;
   headers->ip6.protocol = IP_PROTOCOL_UDP;
   headers->udp.src_port =
index ebe4e5f..2047e04 100644 (file)
@@ -19,6 +19,7 @@ from vpp_papi import VppEnum
 
 BFD_UDP_SH_PORT = 3784
 BFD_UDP_MH_PORT = 4784
+BFD_UDP_DEFAULT_TOS = 192
 
 
 class BFDDiagCode(NumericConstant):
index ebc815c..a6b21f2 100644 (file)
@@ -31,6 +31,7 @@ from bfd import (
     BFD_vpp_echo,
     BFD_UDP_SH_PORT,
     BFD_UDP_MH_PORT,
+    BFD_UDP_DEFAULT_TOS,
 )
 from framework import VppTestCase
 from asfframework import (
@@ -574,6 +575,20 @@ class BFDAPITestCase(VppTestCase):
         self.assertFalse(echo_source.have_usable_ip4)
         self.assertFalse(echo_source.have_usable_ip6)
 
+    def test_set_udp_tos(self):
+        """test udp tos configuration"""
+        if self.multihop:
+            return
+        self.vapi.bfd_udp_set_tos(BFD_UDP_DEFAULT_TOS)
+        my_tos = self.vapi.bfd_udp_get_tos()
+        self.logger.debug("MY_TOS is %s", my_tos)
+        self.assertTrue(my_tos.tos == BFD_UDP_DEFAULT_TOS)
+        new_tos_value = 112
+        self.vapi.bfd_udp_set_tos(new_tos_value)
+        my_tos = self.vapi.bfd_udp_get_tos()
+        self.assertTrue(my_tos.tos == new_tos_value)
+        self.vapi.bfd_udp_set_tos(BFD_UDP_DEFAULT_TOS)
+
 
 class BFDTestSession(object):
     """BFD session as seen from test framework side"""
@@ -936,18 +951,22 @@ def verify_bfd_session_config(test, session, state=None):
         )
 
 
-def verify_ip(test, packet):
+def verify_ip(test, packet, expected_tos=None):
     """Verify correctness of IP layer."""
     if test.vpp_session.af == AF_INET6:
         ip = packet[IPv6]
         local_ip = test.vpp_session.local_addr
         remote_ip = test.vpp_session.peer_addr
         test.assert_equal(ip.hlim, 255, "IPv6 hop limit")
+        if expected_tos is not None:
+            test.assert_equal(ip.tc, expected_tos, "IPv6 TOS")
     else:
         ip = packet[IP]
         local_ip = test.vpp_session.local_addr
         remote_ip = test.vpp_session.peer_addr
         test.assert_equal(ip.ttl, 255, "IPv4 TTL")
+        if expected_tos is not None:
+            test.assert_equal(ip.tos, expected_tos, "IPv4 TOS")
     test.assert_equal(ip.src, local_ip, "IP source address")
     test.assert_equal(ip.dst, remote_ip, "IP destination address")
 
@@ -976,11 +995,14 @@ def verify_event(test, event, expected_state):
     test.assert_equal(e.state, expected_state, BFDState)
 
 
-def wait_for_bfd_packet(test, timeout=1, pcap_time_min=None, is_tunnel=False):
+def wait_for_bfd_packet(
+    test, timeout=1, pcap_time_min=None, is_tunnel=False, expected_tos=None
+):
     """wait for BFD packet and verify its correctness
 
     :param timeout: how long to wait
     :param pcap_time_min: ignore packets with pcap timestamp lower than this
+    :param expected_tos: expected TOS/TC value for IP verification
 
     :returns: tuple (packet, time spent waiting for packet)
     """
@@ -1017,7 +1039,7 @@ def wait_for_bfd_packet(test, timeout=1, pcap_time_min=None, is_tunnel=False):
         raise Exception(ppp("Unexpected or invalid BFD packet:", p))
     if bfd.payload:
         raise Exception(ppp("Unexpected payload in BFD packet:", bfd))
-    verify_ip(test, p)
+    verify_ip(test, p, expected_tos)
     verify_udp(test, p)
     test.test_session.verify_bfd(p)
     return p
@@ -2053,6 +2075,49 @@ class BFD4TestCase(VppTestCase):
             self.assert_equal(e.sw_if_index, sw_if_index, "sw_if_index")
             self.assertFalse(vpp_session.query_vpp_config())
 
+    def test_bfd_traffic_with_tos_configured(self):
+        """test BFD traffic with TOS values configured"""
+        # Test different TOS values
+        test_tos_values = [0, 112, 192, 255]
+
+        for tos_value in test_tos_values:
+            with self.subTest(tos=tos_value):
+                self.logger.info(f"Testing BFD traffic with TOS value: {tos_value}")
+                # Set the TOS value
+                self.vapi.bfd_udp_set_tos(tos_value)
+                # Verify the TOS value was set
+                current_tos = self.vapi.bfd_udp_get_tos()
+                self.assertEqual(
+                    current_tos.tos, tos_value, f"TOS should be set to {tos_value}"
+                )
+                # Start the BFD session and wait for the first packet
+                self.logger.info("BFD: Waiting for packet with custom TOS")
+                p = wait_for_bfd_packet(
+                    self,
+                    timeout=2,
+                    is_tunnel=self.vpp_session.is_tunnel,
+                    expected_tos=tos_value,
+                )
+                # Verify the packet has the expected TOS value
+                if self.vpp_session.af == AF_INET6:
+                    actual_tos = p[IPv6].tc
+                    field_name = "IPv6 TC"
+                else:
+                    actual_tos = p[IP].tos
+                    field_name = "IPv4 TOS"
+                self.assertEqual(
+                    actual_tos,
+                    tos_value,
+                    f"{field_name} should be {tos_value}, got {actual_tos}",
+                )
+                self.logger.info(
+                    f"Successfully verified BFD packet with TOS {tos_value}"
+                )
+                # Send a response packet to keep the session alive
+                self.test_session.send_packet()
+                # Brief pause between tests
+                self.sleep(0.2, "brief pause between TOS tests")
+
 
 @parameterized_class(
     [