From: Mihut Aronovici Date: Mon, 14 Jul 2025 19:32:22 +0000 (-0400) Subject: bfd: add API to configure TOS for IP of BFD packets X-Git-Tag: v26.02-rc0~10 X-Git-Url: https://gerrit.fd.io/r/gitweb?a=commitdiff_plain;h=refs%2Fchanges%2F39%2F43439%2F9;p=vpp.git bfd: add API to configure TOS for IP of BFD packets Change-Id: I2753b5b200791130b83fa07b0fa731e636f79252 Signed-off-by: Mihut Aronovici Type: improvement --- diff --git a/src/vnet/bfd/bfd.api b/src/vnet/bfd/bfd.api index cf14455f391..73ae308b4f8 100644 --- a/src/vnet/bfd/bfd.api +++ b/src/vnet/bfd/bfd.api @@ -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 { diff --git a/src/vnet/bfd/bfd_api.c b/src/vnet/bfd/bfd_api.c index bccf58ba4bb..44fb8ef5915 100644 --- a/src/vnet/bfd/bfd_api.c +++ b/src/vnet/bfd/bfd_api.c @@ -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 static clib_error_t * bfd_api_hookup (vlib_main_t * vm) diff --git a/src/vnet/bfd/bfd_main.h b/src/vnet/bfd/bfd_main.h index 7d9253983ce..538bf9dfb58 100644 --- a/src/vnet/bfd/bfd_main.h +++ b/src/vnet/bfd/bfd_main.h @@ -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; diff --git a/src/vnet/bfd/bfd_udp.c b/src/vnet/bfd/bfd_udp.c index 6d3202cc55c..f4b96828959 100644 --- a/src/vnet/bfd/bfd_udp.c +++ b/src/vnet/bfd/bfd_udp.c @@ -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 = diff --git a/test/bfd.py b/test/bfd.py index ebe4e5f95f2..2047e04fb4f 100644 --- a/test/bfd.py +++ b/test/bfd.py @@ -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): diff --git a/test/test_bfd.py b/test/test_bfd.py index ebc815cb701..a6b21f2810e 100644 --- a/test/test_bfd.py +++ b/test/test_bfd.py @@ -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( [