From 87c616675f43baa239b0ab9bd7c583028b82c3c6 Mon Sep 17 00:00:00 2001 From: Baorong Liu <96146196+baorliu@users.noreply.github.com> Date: Mon, 23 Feb 2026 14:34:50 -0800 Subject: [PATCH 01/14] bfdsyncd, recover pr 3267 Signed-off-by: Baorong Liu <96146196+baorliu@users.noreply.github.com> --- Makefile.am | 4 +- bfdsyncd/Makefile.am | 26 + bfdsyncd/bfdd/bfddp_packet.h | 387 +++++++++++ bfdsyncd/bfdlink.cpp | 722 +++++++++++++++++++++ bfdsyncd/bfdlink.h | 112 ++++ bfdsyncd/bfdsyncd.cpp | 148 +++++ configure.ac | 1 + orchagent/bfdorch.cpp | 74 ++- tests/mock_tests/Makefile.am | 22 +- tests/mock_tests/bfdsyncd/test_bfdlink.cpp | 178 +++++ 10 files changed, 1668 insertions(+), 6 deletions(-) create mode 100644 bfdsyncd/Makefile.am create mode 100644 bfdsyncd/bfdd/bfddp_packet.h create mode 100644 bfdsyncd/bfdlink.cpp create mode 100644 bfdsyncd/bfdlink.h create mode 100644 bfdsyncd/bfdsyncd.cpp create mode 100644 tests/mock_tests/bfdsyncd/test_bfdlink.cpp diff --git a/Makefile.am b/Makefile.am index 757db0d8d63..7dc156eb1be 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,7 +1,7 @@ if GCOV_ENABLED -SUBDIRS = gcovpreload fpmsyncd neighsyncd portsyncd mclagsyncd natsyncd fdbsyncd orchagent swssconfig cfgmgr tests gearsyncd +SUBDIRS = gcovpreload fpmsyncd bfdsyncd neighsyncd portsyncd mclagsyncd natsyncd fdbsyncd orchagent swssconfig cfgmgr tests gearsyncd else -SUBDIRS = fpmsyncd neighsyncd portsyncd mclagsyncd natsyncd fdbsyncd orchagent swssconfig cfgmgr tests gearsyncd +SUBDIRS = fpmsyncd bfdsyncd neighsyncd portsyncd mclagsyncd natsyncd fdbsyncd orchagent swssconfig cfgmgr tests gearsyncd endif diff --git a/bfdsyncd/Makefile.am b/bfdsyncd/Makefile.am new file mode 100644 index 00000000000..08bcdccc215 --- /dev/null +++ b/bfdsyncd/Makefile.am @@ -0,0 +1,26 @@ +INCLUDES = -I $(top_srcdir) -I $(top_srcdir)/warmrestart -I /usr/include/sai + +bin_PROGRAMS = bfdsyncd + +CFLAGS_SAI = -I /usr/include/sai + +if DEBUG +DBGFLAGS = -ggdb -DDEBUG +else +DBGFLAGS = -g +endif + +bfdsyncd_SOURCES = bfdsyncd.cpp bfdlink.cpp $(top_srcdir)/warmrestart/warmRestartHelper.cpp + +bfdsyncd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_ASAN) +bfdsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_ASAN) +bfdsyncd_LDADD = $(LDFLAGS_ASAN) -lnl-3 -lnl-route-3 -lswsscommon + +if GCOV_ENABLED +bfdsyncd_LDADD += -lgcovpreload +endif + +if ASAN_ENABLED +bfdsyncd_SOURCES += $(top_srcdir)/lib/asan.cpp +endif + diff --git a/bfdsyncd/bfdd/bfddp_packet.h b/bfdsyncd/bfdd/bfddp_packet.h new file mode 100644 index 00000000000..fbfe89deddc --- /dev/null +++ b/bfdsyncd/bfdd/bfddp_packet.h @@ -0,0 +1,387 @@ +/* + * BFD Data Plane protocol messages header. + * + * Copyright (C) 2020 Network Device Education Foundation, Inc. ("NetDEF") + * Rafael F. Zalamena + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the ?Software?), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED ?AS IS?, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/** + * \file bfddp_packet.h + */ +#ifndef BFD_DP_PACKET_H +#define BFD_DP_PACKET_H + +#include + +#include + +/* + * Protocol definitions. + */ + +/** + * BFD protocol version as defined in RFC5880 Section 4.1 Generic BFD Control + * Packet Format. + */ +#define BFD_PROTOCOL_VERSION 1 + +/** Default data plane port. */ +#define BFD_DATA_PLANE_DEFAULT_PORT 50700 + +/** BFD single hop UDP port, as defined in RFC 5881 Section 4. Encapsulation. */ +#define BFD_SINGLE_HOP_PORT 3784 + +/** BFD multi hop UDP port, as defined in RFC 5883 Section 5. Encapsulation. */ +#define BFD_MULTI_HOP_PORT 4784 + +/** Default slow start multiplier. */ +#define SLOWSTART_DMULT 3 +/** Default slow start transmission speed. */ +#define SLOWSTART_TX 1000000u +/** Default slow start receive speed. */ +#define SLOWSTART_RX 1000000u +/** Default slow start echo receive speed. */ +#define SLOWSTART_ERX 0u + +/* + * BFD single hop source UDP ports. As defined in RFC 5881 Section 4. + * Encapsulation. + */ +#define BFD_SOURCE_PORT_BEGIN 49152 +#define BFD_SOURCE_PORT_END 65535 + +/** BFD data plane protocol version. */ +#define BFD_DP_VERSION 1 + +/** BFD data plane message types. */ +enum bfddp_message_type { + /** Ask for BFD daemon or data plane for echo packet. */ + ECHO_REQUEST = 0, + /** Answer a ECHO_REQUEST packet. */ + ECHO_REPLY = 1, + /** Add or update BFD peer session. */ + DP_ADD_SESSION = 2, + /** Delete BFD peer session. */ + DP_DELETE_SESSION = 3, + /** Tell BFD daemon state changed: timer expired or session down. */ + BFD_STATE_CHANGE = 4, + + /** Ask for BFD session counters. */ + DP_REQUEST_SESSION_COUNTERS = 5, + /** Tell BFD daemon about counters values. */ + BFD_SESSION_COUNTERS = 6, +}; + +/** + * `ECHO_REQUEST`/`ECHO_REPLY` data payload. + * + * Data plane might use whatever precision it wants for `dp_time` + * field, however if you want to be able to tell the delay between + * data plane packet send and BFD daemon packet processing you should + * use `gettimeofday()` and have the data plane clock synchronized with + * BFD daemon (not a problem if data plane runs in the same system). + * + * Normally data plane will only check the time stamp it sent to determine + * the whole packet trip time. + */ +struct bfddp_echo { + /** Filled by data plane. */ + uint64_t dp_time; + /** Filled by BFD daemon. */ + uint64_t bfdd_time; +}; + + +/** BFD session flags. */ +enum bfddp_session_flag { + /** Set when using multi hop. */ + SESSION_MULTIHOP = (1 << 0), + /** Set when using demand mode. */ + SESSION_DEMAND = (1 << 1), + /** Set when using cbit (Control Plane Independent). */ + SESSION_CBIT = (1 << 2), + /** Set when using echo mode. */ + SESSION_ECHO = (1 << 3), + /** Set when using IPv6. */ + SESSION_IPV6 = (1 << 4), + /** Set when using passive mode. */ + SESSION_PASSIVE = (1 << 5), + /** Set when session is administrative down. */ + SESSION_SHUTDOWN = (1 << 6), +}; + +/** + * `DP_ADD_SESSION`/`DP_DELETE_SESSION` data payload. + * + * `lid` is unique in BFD daemon so it might be used as key for data + * structures lookup. + */ +struct bfddp_session { + /** Important session flags. \see bfddp_session_flag. */ + uint32_t flags; + /** + * Session source address. + * + * Check `flags` field for `SESSION_IPV6` before using as IPv6. + */ + struct in6_addr src; + /** + * Session destination address. + * + * Check `flags` field for `SESSION_IPV6` before using as IPv6. + */ + struct in6_addr dst; + + /** Local discriminator. */ + uint32_t lid; + /** + * Minimum desired transmission interval (in microseconds) without + * jitter. + */ + uint32_t min_tx; + /** + * Required minimum receive interval rate (in microseconds) without + * jitter. + */ + uint32_t min_rx; + /** + * Minimum desired echo transmission interval (in microseconds) + * without jitter. + */ + uint32_t min_echo_tx; + /** + * Required minimum echo receive interval rate (in microseconds) + * without jitter. + */ + uint32_t min_echo_rx; + /** Amount of milliseconds to wait before starting the session */ + uint32_t hold_time; + + /** Minimum TTL. */ + uint8_t ttl; + /** Detection multiplier. */ + uint8_t detect_mult; + /** Reserved / zeroed. */ + uint16_t zero; + + /** Interface index (set to `0` when unavailable). */ + uint32_t ifindex; + /** Interface name (empty when unavailable). */ + char ifname[64]; + + /* TODO: missing authentication. */ +}; + +/** BFD packet state values as defined in RFC 5880, Section 4.1. */ +enum bfd_state_value { + /** Session is administratively down. */ + STATE_ADMINDOWN = 0, + /** Session is down or went down. */ + STATE_DOWN = 1, + /** Session is initializing. */ + STATE_INIT = 2, + /** Session is up. */ + STATE_UP = 3, +}; + +/** BFD diagnostic field values as defined in RFC 5880, Section 4.1. */ +enum bfd_diagnostic_value { + /** Nothing was diagnosed. */ + DIAG_NOTHING = 0, + /** Control detection time expired. */ + DIAG_CONTROL_EXPIRED = 1, + /** Echo function failed. */ + DIAG_ECHO_FAILED = 2, + /** Neighbor signaled down. */ + DIAG_DOWN = 3, + /** Forwarding plane reset. */ + DIAG_FP_RESET = 4, + /** Path down. */ + DIAG_PATH_DOWN = 5, + /** Concatenated path down. */ + DIAG_CONCAT_PATH_DOWN = 6, + /** Administratively down. */ + DIAG_ADMIN_DOWN = 7, + /** Reverse concatenated path down. */ + DIAG_REV_CONCAT_PATH_DOWN = 8, +}; + +/** BFD remote state flags. */ +enum bfd_remote_flags { + /** Control Plane Independent bit. */ + RBIT_CPI = (1 << 0), + /** Demand mode bit. */ + RBIT_DEMAND = (1 << 1), + /** Multipoint bit. */ + RBIT_MP = (1 << 2), +}; + +/** + * `BFD_STATE_CHANGE` data payload. + */ +struct bfddp_state_change { + /** Local discriminator. */ + uint32_t lid; + /** Remote discriminator. */ + uint32_t rid; + /** Remote configurations/bits set. \see bfd_remote_flags. */ + uint32_t remote_flags; + /** Remote minimum desired transmission interval. */ + uint32_t desired_tx; + /** Remote minimum receive interval. */ + uint32_t required_rx; + /** Remote minimum echo receive interval. */ + uint32_t required_echo_rx; + /** Remote state. \see bfd_state_values.*/ + uint8_t state; + /** Remote diagnostics (if any) */ + uint8_t diagnostics; + /** Remote detection multiplier. */ + uint8_t detection_multiplier; +}; + +/** + * BFD control packet state bits definition. + */ +enum bfddp_control_state_bits { + /** Used to request connection establishment signal. */ + STATE_POLL_BIT = (1 << 5), + /** Finalizes the connection establishment signal. */ + STATE_FINAL_BIT = (1 << 4), + /** Signalizes that forward plane doesn't depend on control plane. */ + STATE_CPI_BIT = (1 << 3), + /** Signalizes the use of authentication. */ + STATE_AUTH_BIT = (1 << 2), + /** Signalizes that peer is using demand mode. */ + STATE_DEMAND_BIT = (1 << 1), + /** Used in RFC 8562 implementation. */ + STATE_MULTI_BIT = (1 << 0), +}; + +/** + * BFD control packet. + * + * As defined in 'RFC 5880 Section 4.1 Generic BFD Control Packet Format'. + */ +struct bfddp_control_packet { + /** (3 bits version << 5) | (5 bits diag). */ + uint8_t version_diag; + /** + * (2 bits state << 6) | (6 bits flags) + * + * \see bfd_state_value, bfddp_control_state_bits. + */ + uint8_t state_bits; + /** Detection multiplier. */ + uint8_t detection_multiplier; + /** Packet length in bytes. */ + uint8_t length; + /** Our discriminator. */ + uint32_t local_id; + /** Remote system discriminator. */ + uint32_t remote_id; + /** Desired minimum send interval in microseconds. */ + uint32_t desired_tx; + /** Desired minimum receive interval in microseconds. */ + uint32_t required_rx; + /** Desired minimum echo receive interval in microseconds. */ + uint32_t required_echo_rx; +}; + +/** + * The protocol wire message header structure. + */ +struct bfddp_message_header { + /** Protocol version format. \see BFD_DP_VERSION. */ + uint8_t version; + /** Reserved / zero field. */ + uint8_t zero; + /** Message contents type. \see bfddp_message_type. */ + uint16_t type; + /** + * Message identification (to pair request/response). + * + * The ID `0` is reserved for asynchronous messages (e.g. unrequested + * messages). + */ + uint16_t id; + /** Message length. */ + uint16_t length; +}; + +/** + * Data plane session counters request. + * + * Message type: `DP_REQUEST_SESSION_COUNTERS`. + */ +struct bfddp_request_counters { + /** Session local discriminator. */ + uint32_t lid; +}; + +/** + * BFD session counters reply. + * + * Message type: `BFD_SESSION_COUNTERS`. + */ +struct bfddp_session_counters { + /** Session local discriminator. */ + uint32_t lid; + + /** Control packet bytes input. */ + uint64_t control_input_bytes; + /** Control packets input. */ + uint64_t control_input_packets; + /** Control packet bytes output. */ + uint64_t control_output_bytes; + /** Control packets output. */ + uint64_t control_output_packets; + + /** Echo packet bytes input. */ + uint64_t echo_input_bytes; + /** Echo packets input. */ + uint64_t echo_input_packets; + /** Echo packet bytes output. */ + uint64_t echo_output_bytes; + /** Echo packets output. */ + uint64_t echo_output_packets; +}; + +/** + * The protocol wire messages structure. + */ +struct bfddp_message { + /** Message header. \see bfddp_message_header. */ + struct bfddp_message_header header; + + /** Message payload. \see bfddp_message_type. */ + union { + struct bfddp_echo echo; + struct bfddp_session session; + struct bfddp_state_change state; + struct bfddp_control_packet control; + struct bfddp_request_counters counters_req; + struct bfddp_session_counters session_counters; + } data; +}; + +#endif /* BFD_DP_PACKET_H */ + diff --git a/bfdsyncd/bfdlink.cpp b/bfdsyncd/bfdlink.cpp new file mode 100644 index 00000000000..475a690fdeb --- /dev/null +++ b/bfdsyncd/bfdlink.cpp @@ -0,0 +1,722 @@ +#include +#include +#include +#include "logger.h" +#include "netmsg.h" +#include "netdispatcher.h" +#include "bfdsyncd/bfdlink.h" +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + + +extern "C" { +#include "sai.h" +#include "saistatus.h" +} + +using namespace std; +using namespace swss; + +static const char *bfd_dplane_messagetype2str(enum bfddp_message_type bmt) +{ + switch (bmt) { + case ECHO_REQUEST: + return "ECHO_REQUEST"; + case ECHO_REPLY: + return "ECHO_REPLY"; + case DP_ADD_SESSION: + return "DP_ADD_SESSION"; + case DP_DELETE_SESSION: + return "DP_DELETE_SESSION"; + case BFD_STATE_CHANGE: + return "BFD_STATE_CHANGE"; + case DP_REQUEST_SESSION_COUNTERS: + return "DP_REQUEST_SESSION_COUNTERS"; + case BFD_SESSION_COUNTERS: + return "BFD_SESSION_COUNTERS"; + default: + return "UNKNOWN"; + } +} + +BfdLink::BfdLink(DBConnector *db, DBConnector *stateDb, unsigned short port, int debug) : + m_debug(debug), + m_bufSize(BFD_MAX_MSG_LEN * 10), + m_messageBuffer(NULL), + m_pos(0), + m_connected(false), + m_server_up(false), + m_bfdTable(db, APP_BFD_SESSION_TABLE_NAME), + m_bfdStateTable(stateDb, STATE_BFD_SESSION_TABLE_NAME) +{ + struct sockaddr_in addr; + int true_val = 1; + + m_server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_server_socket < 0) + throw system_error(errno, system_category()); + + if (setsockopt(m_server_socket, SOL_SOCKET, SO_REUSEADDR, &true_val, + sizeof(true_val)) < 0) + { + close(m_server_socket); + throw system_error(errno, system_category()); + } + + if (setsockopt(m_server_socket, SOL_SOCKET, SO_KEEPALIVE, &true_val, + sizeof(true_val)) < 0) + { + close(m_server_socket); + throw system_error(errno, system_category()); + } + + memset (&addr, 0, sizeof (addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if (bind(m_server_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0) + { + close(m_server_socket); + throw system_error(errno, system_category()); + } + + if (listen(m_server_socket, 2) != 0) + { + close(m_server_socket); + throw system_error(errno, system_category()); + } + + m_server_up = true; + m_messageBuffer = new char[m_bufSize]; + m_sendBuffer = new char[m_bufSize]; +} + +BfdLink::~BfdLink() +{ + delete[] m_messageBuffer; + if (m_connected) + close(m_connection_socket); + if (m_server_up) + close(m_server_socket); +} + +std::string BfdLink::get_intf_mac(const char* intf) +{ + std::string mac; + std::string path; + std::ifstream netfile; + path = "/sys/class/net/" + string(intf) + "/address"; + netfile.open(path); + std::getline(netfile, mac); + netfile.close(); + return mac; +} + +std::string BfdLink::exec(const char* cmd) { + std::array buffer; + std::string result; + std::unique_ptr pipe(popen(cmd, "r"), pclose); + if (!pipe) { + throw std::runtime_error("popen() failed!"); + } + while (fgets(buffer.data(), static_cast(buffer.size()), pipe.get()) != nullptr) { + result += buffer.data(); + } + return result; +} + + +bool BfdLink::sendmsg(uint16_t msglen) { + size_t sent = 0; + while (sent != msglen) + { + auto rc = ::send(m_connection_socket, m_sendBuffer + sent, msglen - sent, 0); + if (rc == -1) + { + SWSS_LOG_ERROR("Failed to send BFD state or counter message: %s", strerror(errno)); + return false; + } + sent += rc; + } + return true; +} + +void BfdLink::accept() +{ + struct sockaddr_in client_addr; + + /* Ref: man 3 accept */ + /* address_len argument, on input, specifies the length of the supplied sockaddr structure */ + socklen_t client_len = sizeof(struct sockaddr_in); + + m_connection_socket = ::accept(m_server_socket, (struct sockaddr *)&client_addr, + &client_len); + if (m_connection_socket < 0) + throw system_error(errno, system_category()); + + SWSS_LOG_WARN("New connection accepted from: %s\n", inet_ntoa(client_addr.sin_addr)); +} + +int BfdLink::getFd() +{ + return m_connection_socket; +} + +void BfdLink::hexdump(void *ptr, int buflen) +{ + char str[100]; + m_printbuf[0]=0; + unsigned char *buf = (unsigned char*)ptr; + int i, j; + for (i=0; ihexdump(m_messageBuffer, (int)read); + } + + m_pos+= (uint32_t)read; + SWSS_LOG_INFO("updated pos %d", m_pos); + + /* Check for complete messages */ + while (true) + { + hdr = reinterpret_cast(static_cast(m_messageBuffer + start)); + left = m_pos - start; + if (left < BFD_MSG_HDR_LEN) + break; + + msg_len = bfd_msg_len(hdr); + if (left < msg_len) + { + break; + } + + if (!bfd_msg_ok(hdr)) + { + break; + } + + this->handleBfdDpMessage(start); + + start += msg_len; + } + + memmove(m_messageBuffer, m_messageBuffer + start, m_pos - start); + if (m_pos > start) + { + m_pos = m_pos - (uint32_t)start; + } + else + { + m_pos = 0; + } + return 0; +} + +void BfdLink::handleBfdDpMessage(size_t start) +{ + bfddp_message *bmp; + bfddp_message bm ={}; + size_t msg_len; + uint32_t flags; + uint32_t lid; + uint32_t ifindex; + uint32_t rx_int; + uint32_t tx_int; + string bfdkey = ""; + string bfdkey_map = ""; + bool add = true; + bool multihop = true; + bool is_linklocal = false; + char dst_addr[INET6_ADDRSTRLEN]; + char src_addr[INET6_ADDRSTRLEN]; + char ifname[IFNAME_LEN]; + string dst_mac; + string src_mac; + string cmd, dst_str; + + bmp = reinterpret_cast(static_cast(m_messageBuffer+start)); + + auto type = ntohs(bmp->header.type); + msg_len = bfd_msg_len(&bmp->header); + + if (!bfd_msg_ok(&bmp->header)) + { + SWSS_LOG_ERROR("received an invalid BFD DP message, ver %d, type %s, msg_len %ld", bmp->header.version, bfd_dplane_messagetype2str((bfddp_message_type)type), msg_len); + return; + } + + SWSS_LOG_INFO("bfd dp message, ver %d, type %s, msg_len %ld", bmp->header.version, bfd_dplane_messagetype2str((bfddp_message_type)type), msg_len); + + if ((type != DP_ADD_SESSION) && (type != DP_DELETE_SESSION) && (type != DP_REQUEST_SESSION_COUNTERS)) + { + SWSS_LOG_ERROR("BFD_DP supports DP_ADD_SESSION/DP_DELETE_SESSION/DP_REQUEST_SESSION_COUNTERS type only, received message type %s", bfd_dplane_messagetype2str((bfddp_message_type)type)); + return; + } + + memcpy(&bm, bmp, sizeof(bm)); + + /* HW offload does not support counters, return 0 for counters here */ + if (type == DP_REQUEST_SESSION_COUNTERS) + { + struct bfddp_message msg = {}; + uint16_t msglen = sizeof(msg.header) + sizeof(msg.data.session_counters); + + /* Message header. don't need to do hton for the data from bm. for the counters, need htobe64*/ + msg.header.version = BFD_DP_VERSION; + msg.header.length = htons(msglen); + msg.header.type = htons(BFD_SESSION_COUNTERS); + msg.header.id = bm.header.id; + msg.data.session_counters.lid = bm.data.counters_req.lid; + msg.data.session_counters.control_input_packets = htobe64(0); + msg.data.session_counters.control_output_packets = htobe64(0); + msg.data.session_counters.echo_input_packets = htobe64(0); + msg.data.session_counters.echo_output_packets = htobe64(0); + + memcpy(m_sendBuffer, &msg, msglen); + + SWSS_LOG_INFO("BFD_SESSION_COUNTERS send counters to bfdd, id %d, lid %u", ntohs(msg.header.id), ntohl(msg.data.session_counters.lid)); + + sendmsg(msglen); + return; + } + + bm.header.type = type; + bm.header.length = (uint16_t)msg_len; + + flags=ntohl(bmp->data.session.flags); + bm.data.session.flags = flags; + + multihop = flags & SESSION_MULTIHOP; + if (flags & SESSION_IPV6) + { + struct in6_addr v6; + v6 = bm.data.session.src; + if (inet_ntop(AF_INET6, &v6, src_addr, sizeof(src_addr)) == NULL) { + SWSS_LOG_ERROR("Invalid src ip6 address"); + return; + } + v6 = bm.data.session.dst; + if (inet_ntop(AF_INET6, &v6, dst_addr, sizeof(dst_addr)) == NULL) { + SWSS_LOG_ERROR("Invalid dst ip6 address"); + return; + } + } + else + { + struct in_addr v4; + sprintf(src_addr, "%s", inet_ntoa(*(struct in_addr *)&bmp->data.session.src)); + sprintf(dst_addr, "%s", inet_ntoa(*(struct in_addr *)&bmp->data.session.dst)); + /* check link local ip address 169.254.0.0/16 0xa9fe0000 */ + if ((inet_pton(AF_INET, dst_addr, &v4) == 1) && ((v4.s_addr & 0x0000ffff) == 0x0000fea9)) { + is_linklocal = true; + SWSS_LOG_INFO("dst_addr %s is a link local ip address", dst_addr); + } + } + + bfdkey = string("default:default:")+string(dst_addr); + bfdkey_map = string("default|default|")+string(dst_addr); + + ifindex = ntohl(bm.data.session.ifindex); + memcpy(&ifname, bm.data.session.ifname, IFNAME_LEN); + + /* for link-local address only */ + if (ifindex != 0) { + bfdkey = string("default:")+string(ifname)+string(":")+string(dst_addr); + bfdkey_map = string("default|")+string(ifname)+string("|")+string(dst_addr); + } + + /* mac address is not needed for deletion, neighbor entry might be deleted already */ + if ((ifindex != 0) && (bm.header.type == DP_ADD_SESSION)) { + /* get src mac address */ + src_mac = get_intf_mac(ifname); + + if (flags & SESSION_IPV6) + { + /* update ndp table */ + cmd = string("ping6 -c 3 ") + string(dst_addr) + string(" -I ") + string(ifname); + SWSS_LOG_INFO("CMD: %s", cmd.c_str()); + exec(cmd.c_str()); + + /* get dst mac address */ + cmd = string("ip -6 neighbor get ") + string(dst_addr) + string(" dev ") + string(ifname) + string(" | grep -o -E ..:..:..:..:..:.."); + SWSS_LOG_INFO("CMD: %s", cmd.c_str()); + dst_str = exec(cmd.c_str()); + if (dst_str.length() < 17) { + SWSS_LOG_ERROR("mac address length is not correct: dst_mac %s ", dst_str.c_str()); + return; + } + dst_mac = dst_str.substr(0,17); + } + else + { + /* update arp table */ + if (is_linklocal) { + SWSS_LOG_ERROR("IPv4 link-local is not supported!"); + return; + } else { + cmd = string("ping -c 3 ") + string(dst_addr) + string(" -I ") + string(ifname); + } + SWSS_LOG_INFO("CMD: %s", cmd.c_str()); + exec(cmd.c_str()); + + /* get dst mac address */ + cmd = string("arp ") + string(dst_addr) + string(" | grep -o -E ..:..:..:..:..:.."); + SWSS_LOG_INFO("CMD: %s", cmd.c_str()); + dst_str = exec(cmd.c_str()); + if (dst_str.length() < 17) { + SWSS_LOG_ERROR("mac address length is not correct: ip_address %s, dst_mac %s", dst_addr, dst_str.c_str()); + return; + } + dst_mac = dst_str.substr(0,17); + } + SWSS_LOG_INFO("dst_mac %s , src_mac %s", dst_mac.c_str(), src_mac.c_str()); + } + + if (bm.header.type == DP_ADD_SESSION) { + std::map::iterator it; + SWSS_LOG_INFO("bfd session lookup key %s ", bfdkey_map.c_str()); + it = m_key2bfd.find(bfdkey_map); + if (it != m_key2bfd.end()) + { + /* check if there is any timing parameter change. return if not */ + bool changed = false; + if (it->second.data.session.min_rx != ntohl(bmp->data.session.min_rx)) + { + changed = true; + } + if (it->second.data.session.min_tx != ntohl(bmp->data.session.min_tx)) + { + changed = true; + }; + if (it->second.data.session.detect_mult != bmp->data.session.detect_mult) + { + changed = true; + }; + if (changed) { + SWSS_LOG_WARN("bfd session key %s is already created, parameter changed, delete and recreate it.", bfdkey_map.c_str()); + m_bfdTable.del(bfdkey); + m_key2bfd.erase(bfdkey_map); + /* the symptom observed that redis eliminates consecutive del and add transaction sometime, get wrong result. need to wait to make sure deletion done */ + usleep(100000); + } + else + { + SWSS_LOG_WARN("bfd session key %s is already created, ignore duplicated creation.", bfdkey_map.c_str()); + /* in the case of duplicated creation, update lid here and update bfd state from redis state db to bfdd */ + it->second.data.session.lid = bm.data.session.lid; + bfdStateUpdate(bfdkey_map); + return; + } + } + } + + lid = bm.data.session.lid; + SWSS_LOG_INFO("add key %s local discriminator 0x%08x to lookup table", bfdkey.c_str(), lid); + + bm.data.session.min_rx = ntohl(bmp->data.session.min_rx); + bm.data.session.min_tx = ntohl(bmp->data.session.min_tx); + + rx_int = ntohl(bmp->data.session.min_rx)/1000; + tx_int = ntohl(bmp->data.session.min_tx)/1000; + + vector fvVector; + FieldValueTuple mh("multihop", multihop?"true":"false"); + fvVector.push_back(mh); + + FieldValueTuple la("local_addr", src_addr); + fvVector.push_back(la); + + /* Specify both dst_mac and src_mac for inject-down */ + if (ifindex != 0) { + FieldValueTuple d_mac("dst_mac", dst_mac.c_str()); + fvVector.push_back(d_mac); + FieldValueTuple s_mac("src_mac", src_mac.c_str()); + fvVector.push_back(s_mac); + } + + /* let bfdorch use default value if the following parameters are not provided */ + if (rx_int != 0) + { + FieldValueTuple rx("rx_interval", to_string(rx_int)); + fvVector.push_back(rx); + } + if (tx_int != 0) + { + FieldValueTuple tx("tx_interval", to_string(tx_int)); + fvVector.push_back(tx); + } + if (bm.data.session.detect_mult != 0) + { + FieldValueTuple detect_mult("multiplier", to_string(bm.data.session.detect_mult)); + fvVector.push_back(detect_mult); + } + + + if (bm.header.type == DP_ADD_SESSION) + { + m_key2bfd[bfdkey_map] = bm; + + /* in the case of bgp container restart, + * bgp creates bfd session again but bfdorch does not create bfd session again, + * update bfd state from redis state db to bfdd here + */ + bfdStateUpdate(bfdkey_map); + + m_bfdTable.set(bfdkey, fvVector); + SWSS_LOG_INFO("add key %s to appl DB", bfdkey.c_str()); + } + else if (bm.header.type == DP_DELETE_SESSION) + { + m_bfdTable.del(bfdkey); + m_key2bfd.erase(bfdkey_map); + add = false; + SWSS_LOG_INFO("delete key %s from appl DB", bfdkey.c_str()); + } + + SWSS_LOG_NOTICE("BfdTable op %s key: %s local_addr:%s multihop:%s rx_interval:%d tx_interval:%d ifindex:%d ifname:%s ", + add?"add":"del", bfdkey.c_str(), src_addr, multihop?"true":"false", rx_int, tx_int, ifindex, ifname); + if (m_debug) + { + this->bfdDebugMessage(&bm); + } + +} + +void BfdLink::bfdDebugMessage(struct bfddp_message *bm) +{ + uint32_t flags; + + SWSS_LOG_INFO("ver %d", bm->header.version); + SWSS_LOG_INFO("type %s", bfd_dplane_messagetype2str((bfddp_message_type)bm->header.type)); + + flags=bm->data.session.flags; + SWSS_LOG_INFO("flag 0x%08x", flags); + SWSS_LOG_INFO("local discriminator 0x%08x", bm->data.session.lid); + SWSS_LOG_INFO("ttl %d", bm->data.session.ttl); + SWSS_LOG_INFO("detect_mult %d", bm->data.session.detect_mult); + SWSS_LOG_INFO("rx_interval %d", bm->data.session.min_rx); + SWSS_LOG_INFO("tx_interval %d", bm->data.session.min_tx); + SWSS_LOG_INFO("multihop %d", flags & SESSION_MULTIHOP); + + if (flags & SESSION_IPV6) + { + struct in6_addr v6; + char str[INET6_ADDRSTRLEN]; + + v6 = bm->data.session.src; + if (inet_ntop(AF_INET6, &v6, str, sizeof(str)) != NULL) { + SWSS_LOG_INFO("src %s", str); + } + else + { + SWSS_LOG_ERROR("Invalid src ip6 address"); + } + v6 = bm->data.session.dst; + if (inet_ntop(AF_INET6, &v6, str, sizeof(str)) != NULL) { + SWSS_LOG_INFO("dst %s", str); + } + else + { + SWSS_LOG_ERROR("Invalid dst ip6 address"); + } + } + else + { + SWSS_LOG_INFO("src %s", inet_ntoa(*(struct in_addr *)&bm->data.session.src)); + SWSS_LOG_INFO("dst %s", inet_ntoa(*(struct in_addr *)&bm->data.session.dst)); + } + +} + +void BfdLink::bfdStateUpdate(std::string key) +{ + std::vector fvs; + m_bfdStateTable.get(key, fvs); + for (auto fv: fvs) + { + if (fvField(fv) == "state") + { + SWSS_LOG_INFO("key %s found in state db, update state %s to bfdd", key.c_str(), string(fvValue(fv)).c_str()); + handleBfdStateUpdate(key, fvs); + break; + } + } +} + +bool BfdLink::handleBfdStateUpdate(std::string k, const std::vector &fvs) +{ + struct in_addr inaddr; + struct in6_addr in6addr; + char buf6[INET6_ADDRSTRLEN]; + + std::string key; + std::string s = k; + size_t pos = s.find("|"); + std::string vrf = s.substr(0, pos); + s.erase(0, pos+1); + pos = s.find("|"); + std::string intf = s.substr(0, pos); + s.erase(0, pos+1); + std::string ip = s; + + if (inet_pton(AF_INET6, ip.c_str(), &in6addr) == 1) /* success! */ + { + if (inet_ntop(AF_INET6, &in6addr, buf6, sizeof(buf6)) != NULL) + { + key = vrf + string("|") + intf+string("|") + string(buf6); + } + else + { + SWSS_LOG_ERROR("inet_ntop error, ipv6 address %s ", ip.c_str()); + return false; + } + } + else if (inet_pton(AF_INET, ip.c_str(), &inaddr) == 1) /* success! */ + { + key = k; + } + else + { + SWSS_LOG_ERROR("invalid ip address: %s ", ip.c_str()); + return false; + }; + std::map::iterator it; + SWSS_LOG_INFO("lookup key %s ", key.c_str()); + it = m_key2bfd.find(key); + if (it == m_key2bfd.end()) + { + SWSS_LOG_INFO("key %s not found", key.c_str()); + return false; + } + auto session = it->second.data.session; + + struct bfddp_message msg = {}; + uint16_t msglen = sizeof(msg.header) + sizeof(msg.data.state); + uint8_t state = STATE_DOWN; + + /* Message header. */ + msg.header.version = BFD_DP_VERSION; + msg.header.length = ntohs(msglen); + msg.header.type = ntohs(BFD_STATE_CHANGE); + + /* Message payload. */ + + /* HW local_discriminator is different from frr/bfd local discriminator, do not use local discriminator from state db */ + msg.data.state.lid = session.lid; + + for (const auto& fv: fvs) + { + const auto& field = fvField(fv); + const auto& value = fvValue(fv); + if (field == "state") + { + if (value == "Up") + { + state = STATE_UP; + } + else if (value == "Down") + { + state = STATE_DOWN; + } + } + /* Remote discriminator. */ + if (field == "remote_discriminator") + { + uint32_t rid = (uint32_t)strtoll(string(value).c_str(), NULL, 10); + msg.data.state.rid = htonl(rid); + SWSS_LOG_INFO("remote_discriminator %u ", rid); + } + /* Remote minimum receive interval. */ + if (field == "remote_min_rx") + { + uint32_t rx = (uint32_t)strtoll(string(value).c_str(), NULL, 10); + msg.data.state.required_rx = htonl(rx); + SWSS_LOG_INFO("remote_min_rx %u ", rx); + } + /* Remote minimum desired transmission interval. */ + if (field == "remote_min_tx") + { + uint32_t tx = (uint32_t)strtoll(string(value).c_str(), NULL, 10); + msg.data.state.desired_tx = htonl(tx); + SWSS_LOG_INFO("remote_min_tx %u ", tx); + } + /* Remote detection multiplier. */ + if (field == "remote_multiplier") + { + msg.data.state.detection_multiplier = (uint8_t)strtoll(string(value).c_str(), NULL, 10); + SWSS_LOG_INFO("remote_multiplier %u", msg.data.state.detection_multiplier ); + } + + } + msg.data.state.state = state; + SWSS_LOG_INFO("lookup key %s, state %d ", key.c_str(), state); + + if (msglen > m_bufSize) + { + /* should no be reached here */ + SWSS_LOG_THROW("Message length %d is greater than the send buffer size %d", msglen, m_bufSize); + } + + memcpy(m_sendBuffer, &msg, msglen); + + return sendmsg(msglen); + +} diff --git a/bfdsyncd/bfdlink.h b/bfdsyncd/bfdlink.h new file mode 100644 index 00000000000..17f438aee0a --- /dev/null +++ b/bfdsyncd/bfdlink.h @@ -0,0 +1,112 @@ +#ifndef __BFDLINK__ +#define __BFDLINK__ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "selectable.h" +#include "bfdd/bfddp_packet.h" +#include "dbconnector.h" +#include "producerstatetable.h" + +// if name in bfddp_session struct: char ifname[64]; +#define IFNAME_LEN 64 +#define BFD_MAX_MSG_LEN 256 + +typedef bfddp_message_header bfd_msg_hdr_t; + +#define BFD_MSG_HDR_LEN (sizeof (bfd_msg_hdr_t)) + + +/* + * bfd_msg_len + */ +static inline size_t +bfd_msg_len (const bfd_msg_hdr_t *hdr) +{ + //return ntohs (hdr->msg_len); + return ntohs (hdr->length); +} + +/* + * bfd_msg_ok + * + * Returns TRUE if a message looks well-formed. + * + * @param len The length in bytes from 'hdr' to the end of the buffer. + */ +static inline int +bfd_msg_ok (const bfd_msg_hdr_t *hdr) +{ + size_t msg_len; + + if ((ntohs(hdr->type) != DP_ADD_SESSION) && (ntohs(hdr->type) != DP_DELETE_SESSION) && (ntohs(hdr->type) != DP_REQUEST_SESSION_COUNTERS)) + { + return 0; + } + + msg_len = bfd_msg_len (hdr); + if (msg_len < BFD_MSG_HDR_LEN || msg_len > BFD_MAX_MSG_LEN) + return 0; + + return 1; +} + +using namespace std; + +namespace swss { + +class BfdLink : public Selectable { +public: + BfdLink(DBConnector *db, DBConnector *stateDb, unsigned short port = BFD_DATA_PLANE_DEFAULT_PORT, int debug = 0); + virtual ~BfdLink(); + + virtual std::string exec(const char* cmd); + virtual std::string get_intf_mac(const char* intf); + virtual bool sendmsg(uint16_t msglen); + + /* Wait for connection (blocking) */ + void accept(); + + int getFd() override; + uint64_t readData() override; + void bfdDebugMessage(struct bfddp_message *bm); + void handleBfdDpMessage(size_t start); + void hexdump(void *ptr, int buflen); + + /* readMe throws BfdConnectionClosedException when connection is lost */ + class BfdConnectionClosedException : public std::exception + { + }; + bool handleBfdStateUpdate(std::string key, const std::vector &fvs); + void bfdStateUpdate(std::string key); + char *m_messageBuffer; + +private: + /* bfd table */ + ProducerStateTable m_bfdTable; + Table m_bfdStateTable; + + unsigned int m_bufSize; + char *m_sendBuffer; + char m_printbuf[1024]; + unsigned int m_pos; + std::map m_key2bfd; + + bool m_connected; + bool m_server_up; + int m_debug; + int m_server_socket; + int m_connection_socket; +}; + +} + +#endif diff --git a/bfdsyncd/bfdsyncd.cpp b/bfdsyncd/bfdsyncd.cpp new file mode 100644 index 00000000000..5172c8f812b --- /dev/null +++ b/bfdsyncd/bfdsyncd.cpp @@ -0,0 +1,148 @@ +#include +#include +#include "logger.h" +#include "select.h" +#include "selectabletimer.h" +#include "netdispatcher.h" +#include "warmRestartHelper.h" +#include "bfdsyncd/bfdlink.h" +#include "subscriberstatetable.h" + + +using namespace std; +using namespace swss; + +int main(int argc, char **argv) +{ + int dflag = 0; + char *port_str = NULL; + int index; + int c; + unsigned short port = 0; + + opterr = 0; + + while ((c = getopt (argc, argv, "hdp:")) != -1) + switch (c) + { + case 'h': + cout << "Usage: bfdsyncd -d -p " << endl; + break; + case 'd': + dflag = 1; + break; + case 'p': + port_str = optarg; + sscanf(port_str, "%hd", &port); + break; + case '?': + if (optopt == 'p') + fprintf (stderr, "Option -%c requires a TCP port number.\n", optopt); + else if (isprint (optopt)) + { + fprintf (stderr, "Unknown option `-%c'.\n", optopt); + SWSS_LOG_ERROR("Unknown option `-%c'.\n", optopt); + } + else + { + fprintf (stderr, "Unknown option character `\\x%x'.\n", optopt); + SWSS_LOG_ERROR( "Unknown option character `\\x%x'.\n", optopt); + } + return 1; + default: + break; + } + + if (port == 0) + { + port = BFD_DATA_PLANE_DEFAULT_PORT; + } + cout << "debug flag " << dflag << ", port = " << port << endl; + + for (index = optind; index < argc; index++) + cout << "Non-option argument " << argv[index] << endl; + + swss::Logger::linkToDbNative("bfdsyncd"); + DBConnector db("APPL_DB", 0); + RedisPipeline pipeline(&db); + + DBConnector stateDb("STATE_DB", 0); + + SubscriberStateTable bfdstateTableSubscriber(&stateDb, STATE_BFD_SESSION_TABLE_NAME); + + while (true) + { + try + { + BfdLink bfd(&db, &stateDb, port, dflag); + Select s; + + /* + * Pipeline should be flushed right away to deal with state pending + * from previous try/catch iterations. + */ + pipeline.flush(); + + SWSS_LOG_INFO("Waiting for bfd-client connection... "); + cout << "Waiting for bfd-client connection... " << endl; + bfd.accept(); + SWSS_LOG_INFO("bfd-client connected!"); + cout << "bfd-client connected" << endl; + + s.addSelectable(&bfd); + s.addSelectable(&bfdstateTableSubscriber); + + while (true) + { + Selectable *temps; + + s.select(&temps); + + if (temps == &bfdstateTableSubscriber) + { + std::deque keyOpFvsQueue; + bfdstateTableSubscriber.pops(keyOpFvsQueue); + + for (const auto& keyOpFvs: keyOpFvsQueue) + { + const auto& key = kfvKey(keyOpFvs); + const auto& op = kfvOp(keyOpFvs); + const auto& fvs = kfvFieldsValues(keyOpFvs); + + SWSS_LOG_DEBUG("Received bfd state update for key %s, op %s", key.c_str(), op.c_str()); + + //Does not support DEL_COMMAND for state update + if (op != SET_COMMAND) + { + SWSS_LOG_INFO("bfdsyncd support SET_OP only, get key %s, op %s for state_db, ignored", key.c_str(), op.c_str()); + continue; + } + + bfd.handleBfdStateUpdate(key, fvs); + } + } + else if (temps == &bfd) { + SWSS_LOG_DEBUG("Received bfd message (select)"); + } + else + { + pipeline.flush(); + SWSS_LOG_DEBUG("Pipeline flushed"); + } + } + } + catch (BfdLink::BfdConnectionClosedException &e) + { + cout << "Connection lost \"" << e.what() << "\" reconnecting..." << endl; + SWSS_LOG_ERROR("Bfdd connection closed exception had been thrown in daemon."); + } + catch (const exception& e) + { + cout << "Exception \"" << e.what() << "\" had been thrown in daemon" << endl; + SWSS_LOG_ERROR("Exception had been thrown in daemon."); + return 1; + } + } + + return 1; +} diff --git a/configure.ac b/configure.ac index 145231749ce..03db318af03 100644 --- a/configure.ac +++ b/configure.ac @@ -154,6 +154,7 @@ AC_CONFIG_FILES([ Makefile orchagent/Makefile fpmsyncd/Makefile + bfdsyncd/Makefile neighsyncd/Makefile gearsyncd/Makefile fdbsyncd/Makefile diff --git a/orchagent/bfdorch.cpp b/orchagent/bfdorch.cpp index 095655c3040..aca9edbf1a0 100644 --- a/orchagent/bfdorch.cpp +++ b/orchagent/bfdorch.cpp @@ -246,10 +246,66 @@ void BfdOrch::doTask(NotificationConsumer &consumer) SWSS_LOG_INFO("Get BFD session state change notification id:%" PRIx64 " state: %s", id, session_state_lookup.at(state).c_str()); + if (bfd_session_lookup.find(id) == bfd_session_lookup.end()) + { + SWSS_LOG_NOTICE("BFD session missing at state change id:%" PRIx64 " state: %s", id, session_state_lookup.at(state).c_str()); + continue; + } + if (state != bfd_session_lookup[id].state) { auto key = bfd_session_lookup[id].peer; - m_stateBfdSessionTable.hset(key, "state", session_state_lookup.at(state)); + vector fvVector; + m_stateBfdSessionTable.get(key, fvVector); + + fvVector.emplace_back("state", session_state_lookup.at(state)); + + // Update remote parameter after each state change + sai_attribute_t attr; + vector attrs; + + attr.id = SAI_BFD_SESSION_ATTR_REMOTE_DISCRIMINATOR; + attr.value.u32 = 0; + attrs.emplace_back(attr); + + attr.id = SAI_BFD_SESSION_ATTR_REMOTE_MULTIPLIER; + attr.value.u32 = 0; + attrs.emplace_back(attr); + + attr.id = SAI_BFD_SESSION_ATTR_REMOTE_MIN_RX; + attrs.emplace_back(attr); + + attr.id = SAI_BFD_SESSION_ATTR_REMOTE_MIN_TX; + attrs.emplace_back(attr); + + sai_status_t status = sai_bfd_api->get_bfd_session_attribute(id, (uint32_t)attrs.size(), attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + // Non-critical information, skip the debug info if the attributes are not available + SWSS_LOG_WARN("BFD session id:%" PRIx64 " get attributes failed", id); + } + else + { + for (uint32_t i = 0; i < attrs.size(); ++i) { + sai_attribute_t attr = attrs[i]; + auto attr_id = attr.id; + switch (attr_id) { + case SAI_BFD_SESSION_ATTR_REMOTE_DISCRIMINATOR: + fvVector.emplace_back("remote_discriminator", to_string((uint32_t)attr.value.u32)); + break; + case SAI_BFD_SESSION_ATTR_REMOTE_MULTIPLIER: + fvVector.emplace_back("remote_multiplier", to_string((uint32_t)attr.value.u32)); + break; + case SAI_BFD_SESSION_ATTR_REMOTE_MIN_RX: + fvVector.emplace_back("remote_min_rx", to_string((uint32_t)attr.value.u32)); + break; + case SAI_BFD_SESSION_ATTR_REMOTE_MIN_TX: + fvVector.emplace_back("remote_min_tx", to_string((uint32_t)attr.value.u32)); + break; + } + } + } + m_stateBfdSessionTable.set(key, fvVector); SWSS_LOG_NOTICE("BFD session state for %s changed from %s to %s", key.c_str(), session_state_lookup.at(bfd_session_lookup[id].state).c_str(), session_state_lookup.at(state).c_str()); @@ -346,7 +402,9 @@ bool BfdOrch::create_bfd_session(const string& key, const vector(value); @@ -511,7 +574,14 @@ bool BfdOrch::create_bfd_session(const string& key, const vector + +#include +#include + +using namespace swss; +using namespace testing; + +using ::testing::_; +using ::testing::NiceMock; +using ::testing::Return; + +#define STATE_UPDATE_MSG_LEN 36 +#define COUNTER_MSG_LEN 80 + +class MockBfdLink : public BfdLink +{ +public: + MockBfdLink(DBConnector *db, DBConnector *stateDb, unsigned short port = BFD_DATA_PLANE_DEFAULT_PORT, int debug = 0):BfdLink(db, stateDb, port, debug){} + MOCK_METHOD(bool, sendmsg, (uint16_t msglen), ()); + MOCK_METHOD(string, exec, (const char* cmd), (override)); + MOCK_METHOD(string, get_intf_mac, (const char* intf), (override)); +}; + + +class BfdSyncdTest : public ::testing::Test +{ +public: + void SetUp() override + { + } + + void TearDown() override + { + } + + DBConnector m_appl_db{"APPL_DB", 0}; + DBConnector m_state_db{"STATE_DB", 0}; + NiceMock m_bfd{&m_appl_db, &m_state_db, BFD_DATA_PLANE_DEFAULT_PORT, 1}; + +}; + +TEST_F(BfdSyncdTest, SingleMessageInBfdMessage) +{ + shared_ptr app_db; + app_db = make_shared("APPL_DB", 0); + Table app_bfd_session_table(app_db.get(), APP_BFD_SESSION_TABLE_NAME); + + //Create BFD session + unsigned char s[] = { + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x10, 0xfe, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7a, 0xa4, 0x3e, 0xff, 0xfe, 0x72, 0xac, 0x00, 0xfe, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7a, 0x11, 0x08, 0xff, 0xfe, 0x55, 0xd4, 0x00, 0x24, 0x08, 0xc7, 0x9e, + 0x00, 0x04, 0x93, 0xe0, 0x00, 0x04, 0x93, 0xe0, 0x00, 0x00, 0xc3, 0x50, 0x00, 0x00, 0xc3, 0x50, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x45, 0x74, 0x68, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x31, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + ON_CALL(m_bfd, get_intf_mac(_)).WillByDefault(Return("78:12:83:58:08:00")); + ON_CALL(m_bfd, exec(_)).WillByDefault(Return("78:12:83:58:08:01")); + + memcpy(m_bfd.m_messageBuffer, static_cast(s), sizeof(s)); + + m_bfd.handleBfdDpMessage(0); + + //Check APPL DB + { + vector keys; + vector fieldValues; + string multihop; + string local_addr; + string dst_mac; + string src_mac; + string rx_interval; + string tx_interval; + string multiplier; + + app_bfd_session_table.getKeys(keys); + ASSERT_EQ(keys.size(), 1); + + app_bfd_session_table.get(keys[0], fieldValues); + for (const auto& fv: fieldValues) + { + const auto& field = fvField(fv); + const auto& value = fvValue(fv); + + if (field == "multihop") multihop = value; + if (field == "local_addr") local_addr = value; + if (field == "dst_mac") dst_mac = value; + if (field == "src_mac") src_mac = value; + if (field == "rx_interval") rx_interval = value; + if (field == "tx_interval") tx_interval = value; + if (field == "multiplier" ) multiplier = value; + } + ASSERT_EQ( multihop, "false"); + ASSERT_EQ( local_addr, "fe80::7aa4:3eff:fe72:ac00"); + ASSERT_EQ( dst_mac, "78:12:83:58:08:01"); + ASSERT_EQ( src_mac, "78:12:83:58:08:00"); + ASSERT_EQ( rx_interval, "300"); + ASSERT_EQ( tx_interval, "300"); + ASSERT_EQ( multiplier, "3"); + } + + //Update BFD session state, call sendmsg + { + EXPECT_CALL(m_bfd, sendmsg(STATE_UPDATE_MSG_LEN)).Times(1); + + std::vector fieldValues = { + {"state", "Up"}, + }; + auto key = string("default|Ethernet1_1|fe80::7a11:8ff:fe55:d400"); + m_bfd.handleBfdStateUpdate(key, fieldValues); + + } + + //handle counter request + { + EXPECT_CALL(m_bfd, sendmsg(COUNTER_MSG_LEN)).Times(1); + + unsigned char s[] = { + 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x10, 0xfe, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7a, 0xa4, 0x3e, 0xff, 0xfe, 0x72, 0xac, 0x00, 0xfe, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7a, 0x11, 0x08, 0xff, 0xfe, 0x55, 0xd4, 0x00, 0x24, 0x08, 0xc7, 0x9e, + 0x00, 0x04, 0x93, 0xe0, 0x00, 0x04, 0x93, 0xe0, 0x00, 0x00, 0xc3, 0x50, 0x00, 0x00, 0xc3, 0x50, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x45, 0x74, 0x68, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x31, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + memcpy(m_bfd.m_messageBuffer, static_cast(s), sizeof(s)); + + m_bfd.handleBfdDpMessage(0); + } + + //Delete BFD session and Check APPL DB + { + unsigned char s[] = { + 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x10, 0xfe, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7a, 0xa4, 0x3e, 0xff, 0xfe, 0x72, 0xac, 0x00, 0xfe, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7a, 0x11, 0x08, 0xff, 0xfe, 0x55, 0xd4, 0x00, 0x24, 0x08, 0xc7, 0x9e, + 0x00, 0x04, 0x93, 0xe0, 0x00, 0x04, 0x93, 0xe0, 0x00, 0x00, 0xc3, 0x50, 0x00, 0x00, 0xc3, 0x50, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x45, 0x74, 0x68, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x31, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + memcpy(m_bfd.m_messageBuffer, static_cast(s), sizeof(s)); + + m_bfd.handleBfdDpMessage(0); + + vector keys; + vector fieldValues; + + app_bfd_session_table.getKeys(keys); + ASSERT_EQ(keys.size(), 0); + } + + //Update BFD session state, expecting session is not found, no message sent + { + EXPECT_CALL(m_bfd, sendmsg(STATE_UPDATE_MSG_LEN)).Times(0); + + std::vector fieldValues = { + {"state", "Up"}, + }; + auto key = string("default|Ethernet1_1|fe80::7a11:8ff:fe55:d400"); + m_bfd.handleBfdStateUpdate(key, fieldValues); + } +} + From fa9d78d0a7515ed4e86e9ca65336bcacc9af3c28 Mon Sep 17 00:00:00 2001 From: Baorong Liu <96146196+baorliu@users.noreply.github.com> Date: Mon, 23 Feb 2026 16:19:33 -0800 Subject: [PATCH 02/14] fix tests Signed-off-by: Baorong Liu <96146196+baorliu@users.noreply.github.com> --- tests/mock_tests/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index eb3cc848b25..f6cccfa0e12 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -9,9 +9,9 @@ CXXFLAGS = -g -O0 CFLAGS_SAI = -I /usr/include/sai -TESTS = tests tests_intfmgrd tests_teammgrd tests_portsyncd tests_fpmsyncd test_bfdsyncd tests_response_publisher +TESTS = tests tests_intfmgrd tests_teammgrd tests_portsyncd tests_fpmsyncd tests_bfdsyncd tests_response_publisher -noinst_PROGRAMS = tests tests_intfmgrd tests_teammgrd tests_portsyncd tests_fpmsyncd test_bfdsyncd tests_response_publisher +noinst_PROGRAMS = tests tests_intfmgrd tests_teammgrd tests_portsyncd tests_fpmsyncd tests_bfdsyncd tests_response_publisher LDADD_SAI = -lsaimeta -lsaimetadata -lsaivs -lsairedis From 9152e81dd95a2a354fc70a4899a09b27bf6bb374 Mon Sep 17 00:00:00 2001 From: Baorong Liu <96146196+baorliu@users.noreply.github.com> Date: Mon, 23 Feb 2026 16:56:16 -0800 Subject: [PATCH 03/14] fix tests Signed-off-by: Baorong Liu <96146196+baorliu@users.noreply.github.com> --- tests/mock_tests/Makefile.am | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index f6cccfa0e12..5d11adc128b 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -296,8 +296,7 @@ tests_fpmsyncd_LDADD = $(LDADD_GTEST) $(LDADD_SAI) -lnl-genl-3 -lhiredis -lhired ## bfdsyncd unit tests -tests_bfdsyncd_SOURCES = swss_ut_main.cpp \ - bfdsyncd/test_bfdlink.cpp \ +tests_bfdsyncd_SOURCES = bfdsyncd/test_bfdlink.cpp \ fake_warmstarthelper.cpp \ fake_producerstatetable.cpp \ mock_dbconnector.cpp \ From c9c67ac0a40219ffbbb1ffb7a0ef88e0f982f69b Mon Sep 17 00:00:00 2001 From: Baorong Liu <96146196+baorliu@users.noreply.github.com> Date: Mon, 23 Feb 2026 18:53:23 -0800 Subject: [PATCH 04/14] fix tests Signed-off-by: Baorong Liu <96146196+baorliu@users.noreply.github.com> --- tests/mock_tests/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index 5d11adc128b..bd71dceb952 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -309,7 +309,7 @@ tests_bfdsyncd_INCLUDES = $(tests_INCLUDES) -I$(top_srcdir)/tests_bfdsyncd -I$(t tests_bfdsyncd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_GTEST) $(CFLAGS_SAI) tests_bfdsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_GTEST) $(CFLAGS_SAI) $(tests_bfdsyncd_INCLUDES) tests_bfdsyncd_LDADD = $(LDADD_GTEST) $(LDADD_SAI) -lnl-genl-3 -lhiredis -lhiredis \ - -lswsscommon -lswsscommon -lgtest -lzmq -lnl-3 -lnl-route-3 -lpthread -lgmock -lboost_program_options + -lswsscommon -lswsscommon -lgtest -lgtest_main -lzmq -lnl-3 -lnl-route-3 -lpthread -lgmock -lboost_program_options ## response publisher unit tests From cb9bca0a04c1a2c2ebf6f87b04831212dedc6cc0 Mon Sep 17 00:00:00 2001 From: Baorong Liu Date: Wed, 15 Apr 2026 10:51:51 -0700 Subject: [PATCH 05/14] resolve conflicts in mock_test Makefile.am Signed-off-by: Baorong Liu --- tests/mock_tests/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index bd71dceb952..ea081686e2f 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -9,9 +9,9 @@ CXXFLAGS = -g -O0 CFLAGS_SAI = -I /usr/include/sai -TESTS = tests tests_intfmgrd tests_teammgrd tests_portsyncd tests_fpmsyncd tests_bfdsyncd tests_response_publisher +TESTS = tests tests_intfmgrd tests_teammgrd tests_portsyncd tests_fpmsyncd tests_response_publisher tests_nbrmgrd tests_teamsyncd tests_bfdsyncd -noinst_PROGRAMS = tests tests_intfmgrd tests_teammgrd tests_portsyncd tests_fpmsyncd tests_bfdsyncd tests_response_publisher +noinst_PROGRAMS = tests tests_intfmgrd tests_teammgrd tests_portsyncd tests_fpmsyncd tests_response_publisher tests_nbrmgrd tests_teamsyncd tests_bfdsyncd LDADD_SAI = -lsaimeta -lsaimetadata -lsaivs -lsairedis From be5068b85160aa01d3a357f2a0b5322ebfb626c3 Mon Sep 17 00:00:00 2001 From: Sridhar talari Date: Thu, 25 Jun 2026 11:59:15 -0700 Subject: [PATCH 06/14] Fix tests_bfdsyncd fixture failure on ProducerStateTable SCRIPT LOAD Link mock_redisreply.cpp so RedisReply::checkReplyType is stubbed in the mock Redis environment, and reset the mock DB in SetUp like other mock test suites. Signed-off-by: Sridhar talari --- tests/mock_tests/Makefile.am | 1 + tests/mock_tests/bfdsyncd/test_bfdlink.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index 6553ce6e026..ac8e3fd48e3 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -360,6 +360,7 @@ tests_bfdsyncd_SOURCES = bfdsyncd/test_bfdlink.cpp \ mock_dbconnector.cpp \ mock_table.cpp \ mock_hiredis.cpp \ + mock_redisreply.cpp \ $(top_srcdir)/warmrestart/ \ $(top_srcdir)/bfdsyncd/bfdlink.cpp diff --git a/tests/mock_tests/bfdsyncd/test_bfdlink.cpp b/tests/mock_tests/bfdsyncd/test_bfdlink.cpp index c700c3377a6..35b1d1537e0 100644 --- a/tests/mock_tests/bfdsyncd/test_bfdlink.cpp +++ b/tests/mock_tests/bfdsyncd/test_bfdlink.cpp @@ -1,6 +1,7 @@ #include "bfdsyncd/bfdlink.h" #include +#include "mock_table.h" #include #include @@ -34,6 +35,7 @@ class BfdSyncdTest : public ::testing::Test public: void SetUp() override { + ::testing_db::reset(); } void TearDown() override From aac7e7e0e3c663241411c88ef725bb4d36de4520 Mon Sep 17 00:00:00 2001 From: Sridhar talari Date: Mon, 29 Jun 2026 16:26:43 -0700 Subject: [PATCH 07/14] Fix 32-bit CI failures in bfdsyncd build and unit tests. Use %zu for size_t msg_len log arguments to satisfy -Werror=format on 32-bit targets, and derive STATE_UPDATE_MSG_LEN / COUNTER_MSG_LEN from BFDDP struct sizes so tests_bfdsyncd passes on both i386 and amd64. Signed-off-by: Sridhar talari --- bfdsyncd/bfdlink.cpp | 4 ++-- tests/mock_tests/bfdsyncd/test_bfdlink.cpp | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/bfdsyncd/bfdlink.cpp b/bfdsyncd/bfdlink.cpp index f98fbae5d4f..e0843fed7bb 100644 --- a/bfdsyncd/bfdlink.cpp +++ b/bfdsyncd/bfdlink.cpp @@ -331,11 +331,11 @@ void BfdLink::handleBfdDpMessage(size_t start) if (!bfd_msg_ok(&bmp->header)) { - SWSS_LOG_ERROR("received an invalid BFD DP message, ver %d, type %s, msg_len %ld", bmp->header.version, bfd_dplane_messagetype2str((bfddp_message_type)type), msg_len); + SWSS_LOG_ERROR("received an invalid BFD DP message, ver %d, type %s, msg_len %zu", bmp->header.version, bfd_dplane_messagetype2str((bfddp_message_type)type), msg_len); return; } - SWSS_LOG_INFO("bfd dp message, ver %d, type %s, msg_len %ld", bmp->header.version, bfd_dplane_messagetype2str((bfddp_message_type)type), msg_len); + SWSS_LOG_INFO("bfd dp message, ver %d, type %s, msg_len %zu", bmp->header.version, bfd_dplane_messagetype2str((bfddp_message_type)type), msg_len); if ((type != DP_ADD_SESSION) && (type != DP_DELETE_SESSION) && (type != DP_REQUEST_SESSION_COUNTERS)) { diff --git a/tests/mock_tests/bfdsyncd/test_bfdlink.cpp b/tests/mock_tests/bfdsyncd/test_bfdlink.cpp index 35b1d1537e0..06db7510fe4 100644 --- a/tests/mock_tests/bfdsyncd/test_bfdlink.cpp +++ b/tests/mock_tests/bfdsyncd/test_bfdlink.cpp @@ -1,4 +1,5 @@ #include "bfdsyncd/bfdlink.h" +#include "bfdsyncd/bfdd/bfddp_packet.h" #include #include "mock_table.h" @@ -13,8 +14,8 @@ using ::testing::_; using ::testing::NiceMock; using ::testing::Return; -#define STATE_UPDATE_MSG_LEN 36 -#define COUNTER_MSG_LEN 80 +#define STATE_UPDATE_MSG_LEN (sizeof(bfddp_message_header) + sizeof(bfddp_state_change)) +#define COUNTER_MSG_LEN (sizeof(bfddp_message_header) + sizeof(bfddp_session_counters)) /* Use port 0 (kernel-assigned ephemeral) so parallel test runs and CI * environments where 50700 may be in use don't clash on bind(). */ From 3b6b342ba7f89e8b70b2a39dfde24f3ca078588d Mon Sep 17 00:00:00 2001 From: Sridhar talari Date: Tue, 30 Jun 2026 09:46:10 -0700 Subject: [PATCH 08/14] Add bfd unit tests to improve PR diff coverage. Extend bfdsyncd mock tests for IPv4 sessions, duplicate handling, state updates, and invalid messages; add pytest cases for src_mac and unknown BFD session state notifications in bfdorch. Signed-off-by: Sridhar talari --- tests/mock_tests/bfdsyncd/test_bfdlink.cpp | 237 +++++++++++++++++++-- tests/test_bfd.py | 44 ++++ 2 files changed, 269 insertions(+), 12 deletions(-) diff --git a/tests/mock_tests/bfdsyncd/test_bfdlink.cpp b/tests/mock_tests/bfdsyncd/test_bfdlink.cpp index 06db7510fe4..a04f564f400 100644 --- a/tests/mock_tests/bfdsyncd/test_bfdlink.cpp +++ b/tests/mock_tests/bfdsyncd/test_bfdlink.cpp @@ -4,6 +4,10 @@ #include #include "mock_table.h" +#include +#include +#include + #include #include @@ -21,6 +25,72 @@ using ::testing::Return; * environments where 50700 may be in use don't clash on bind(). */ static constexpr unsigned short BFD_TEST_PORT = 0; +static const size_t BFD_WIRE_MSG_LEN = sizeof(bfddp_message_header) + sizeof(bfddp_session); + +static void copyDefaultIpv6AddBuffer(unsigned char *buf, size_t bufSize) +{ + static const unsigned char s[BFD_WIRE_MSG_LEN] = { + 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x10, 0xfe, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7a, 0xa4, 0x3e, 0xff, 0xfe, 0x72, 0xac, 0x00, 0xfe, 0x80, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7a, 0x11, 0x08, 0xff, 0xfe, 0x55, 0xd4, 0x00, 0x24, 0x08, 0xc7, 0x9e, + 0x00, 0x04, 0x93, 0xe0, 0x00, 0x04, 0x93, 0xe0, 0x00, 0x00, 0xc3, 0x50, 0x00, 0x00, 0xc3, 0x50, + 0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x45, 0x74, 0x68, 0x65, + 0x72, 0x6e, 0x65, 0x74, 0x31, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + ASSERT_GE(bufSize, BFD_WIRE_MSG_LEN); + memcpy(buf, s, BFD_WIRE_MSG_LEN); +} + +static void buildIpv4SessionWire(unsigned char *buf, + size_t bufSize, + uint16_t dpType, + const char *srcIp, + const char *dstIp, + uint32_t ifindex, + const char *ifname, + uint32_t minRxUs, + uint32_t minTxUs, + uint8_t detectMult) +{ + bfddp_message msg = {}; + struct in_addr src4 = {}; + struct in_addr dst4 = {}; + + ASSERT_GE(bufSize, BFD_WIRE_MSG_LEN); + ASSERT_EQ(inet_pton(AF_INET, srcIp, &src4), 1); + ASSERT_EQ(inet_pton(AF_INET, dstIp, &dst4), 1); + + msg.header.version = BFD_DP_VERSION; + msg.header.type = htons(dpType); + msg.header.length = htons(static_cast(BFD_WIRE_MSG_LEN)); + msg.data.session.flags = htonl(0); + memcpy(&msg.data.session.src, &src4, sizeof(src4)); + memcpy(&msg.data.session.dst, &dst4, sizeof(dst4)); + msg.data.session.lid = htonl(0x20); + msg.data.session.min_rx = htonl(minRxUs); + msg.data.session.min_tx = htonl(minTxUs); + msg.data.session.detect_mult = detectMult; + msg.data.session.ifindex = htonl(ifindex); + if (ifname != nullptr) + { + strncpy(msg.data.session.ifname, ifname, IFNAME_LEN - 1); + } + + memcpy(buf, &msg, BFD_WIRE_MSG_LEN); +} + +static void addDefaultIpv6Session(MockBfdLink &bfd) +{ + unsigned char buf[BFD_WIRE_MSG_LEN]; + copyDefaultIpv6AddBuffer(buf, sizeof(buf)); + memcpy(bfd.m_messageBuffer, buf, BFD_WIRE_MSG_LEN); + bfd.handleBfdDpMessage(0); +} + class MockBfdLink : public BfdLink { public: @@ -56,22 +126,13 @@ TEST_F(BfdSyncdTest, SingleMessageInBfdMessage) Table app_bfd_session_table(app_db.get(), APP_BFD_SESSION_TABLE_NAME); //Create BFD session - unsigned char s[] = { - 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x10, 0xfe, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7a, 0xa4, 0x3e, 0xff, 0xfe, 0x72, 0xac, 0x00, 0xfe, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7a, 0x11, 0x08, 0xff, 0xfe, 0x55, 0xd4, 0x00, 0x24, 0x08, 0xc7, 0x9e, - 0x00, 0x04, 0x93, 0xe0, 0x00, 0x04, 0x93, 0xe0, 0x00, 0x00, 0xc3, 0x50, 0x00, 0x00, 0xc3, 0x50, - 0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x45, 0x74, 0x68, 0x65, - 0x72, 0x6e, 0x65, 0x74, 0x31, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; + unsigned char s[BFD_WIRE_MSG_LEN]; + copyDefaultIpv6AddBuffer(s, sizeof(s)); ON_CALL(m_bfd, get_intf_mac(_)).WillByDefault(Return("78:12:83:58:08:00")); ON_CALL(m_bfd, exec(_)).WillByDefault(Return("78:12:83:58:08:01")); - memcpy(m_bfd.m_messageBuffer, static_cast(s), sizeof(s)); + memcpy(m_bfd.m_messageBuffer, static_cast(s), BFD_WIRE_MSG_LEN); m_bfd.handleBfdDpMessage(0); @@ -183,3 +244,155 @@ TEST_F(BfdSyncdTest, SingleMessageInBfdMessage) } } +TEST_F(BfdSyncdTest, InvalidBfdDpMessage) +{ + unsigned char buf[BFD_WIRE_MSG_LEN]; + copyDefaultIpv6AddBuffer(buf, sizeof(buf)); + buf[6] = 0; + buf[7] = 4; + + EXPECT_CALL(m_bfd, sendmsg(_)).Times(0); + memcpy(m_bfd.m_messageBuffer, buf, BFD_WIRE_MSG_LEN); + m_bfd.handleBfdDpMessage(0); +} + +TEST_F(BfdSyncdTest, StateUpdateWithRemoteFields) +{ + ON_CALL(m_bfd, get_intf_mac(_)).WillByDefault(Return("78:12:83:58:08:00")); + ON_CALL(m_bfd, exec(_)).WillByDefault(Return("78:12:83:58:08:01")); + addDefaultIpv6Session(m_bfd); + + EXPECT_CALL(m_bfd, sendmsg(STATE_UPDATE_MSG_LEN)).Times(1); + + std::vector fieldValues = { + {"state", "Down"}, + {"remote_discriminator", "42"}, + {"remote_min_rx", "100000"}, + {"remote_min_tx", "200000"}, + {"remote_multiplier", "3"}, + }; + auto key = string("default|Ethernet1_1|fe80::7a11:8ff:fe55:d400"); + ASSERT_TRUE(m_bfd.handleBfdStateUpdate(key, fieldValues)); +} + +TEST_F(BfdSyncdTest, StateUpdateInvalidRemoteField) +{ + ON_CALL(m_bfd, get_intf_mac(_)).WillByDefault(Return("78:12:83:58:08:00")); + ON_CALL(m_bfd, exec(_)).WillByDefault(Return("78:12:83:58:08:01")); + addDefaultIpv6Session(m_bfd); + + EXPECT_CALL(m_bfd, sendmsg(_)).Times(0); + + std::vector fieldValues = { + {"state", "Up"}, + {"remote_discriminator", "not-a-number"}, + }; + auto key = string("default|Ethernet1_1|fe80::7a11:8ff:fe55:d400"); + ASSERT_FALSE(m_bfd.handleBfdStateUpdate(key, fieldValues)); +} + +TEST_F(BfdSyncdTest, StateUpdateInvalidKey) +{ + EXPECT_CALL(m_bfd, sendmsg(_)).Times(0); + + std::vector fieldValues = {{"state", "Up"}}; + ASSERT_FALSE(m_bfd.handleBfdStateUpdate("default|Ethernet1|not-an-ip", fieldValues)); +} + +TEST_F(BfdSyncdTest, DuplicateAddIgnore) +{ + ON_CALL(m_bfd, get_intf_mac(_)).WillByDefault(Return("78:12:83:58:08:00")); + ON_CALL(m_bfd, exec(_)).WillByDefault(Return("78:12:83:58:08:01")); + + addDefaultIpv6Session(m_bfd); + + Table stateTable(&m_state_db, STATE_BFD_SESSION_TABLE_NAME); + stateTable.set("default|Ethernet1_1|fe80::7a11:8ff:fe55:d400", {{"state", "Up"}}); + + EXPECT_CALL(m_bfd, sendmsg(STATE_UPDATE_MSG_LEN)).Times(1); + addDefaultIpv6Session(m_bfd); + + shared_ptr app_db = make_shared("APPL_DB", 0); + Table app_bfd_session_table(app_db.get(), APP_BFD_SESSION_TABLE_NAME); + vector keys; + app_bfd_session_table.getKeys(keys); + ASSERT_EQ(keys.size(), 1u); +} + +TEST_F(BfdSyncdTest, DuplicateAddRecreate) +{ + ON_CALL(m_bfd, get_intf_mac(_)).WillByDefault(Return("78:12:83:58:08:00")); + ON_CALL(m_bfd, exec(_)).WillByDefault(Return("78:12:83:58:08:01")); + + unsigned char buf[BFD_WIRE_MSG_LEN]; + copyDefaultIpv6AddBuffer(buf, sizeof(buf)); + memcpy(m_bfd.m_messageBuffer, buf, BFD_WIRE_MSG_LEN); + m_bfd.handleBfdDpMessage(0); + + const size_t minRxOffset = sizeof(bfddp_message_header) + offsetof(bfddp_session, min_rx); + buf[minRxOffset] = 0x00; + buf[minRxOffset + 1] = 0x05; + buf[minRxOffset + 2] = 0x93; + buf[minRxOffset + 3] = 0xe0; + memcpy(m_bfd.m_messageBuffer, buf, BFD_WIRE_MSG_LEN); + m_bfd.handleBfdDpMessage(0); + + shared_ptr app_db = make_shared("APPL_DB", 0); + Table app_bfd_session_table(app_db.get(), APP_BFD_SESSION_TABLE_NAME); + vector keys; + app_bfd_session_table.getKeys(keys); + ASSERT_EQ(keys.size(), 1u); +} + +TEST_F(BfdSyncdTest, Ipv4AddSession) +{ + ON_CALL(m_bfd, get_intf_mac(_)).WillByDefault(Return("00:11:22:33:44:55")); + ON_CALL(m_bfd, exec(_)).WillByDefault(Return("00:22:33:44:55:66")); + + unsigned char buf[BFD_WIRE_MSG_LEN]; + buildIpv4SessionWire(buf, sizeof(buf), DP_ADD_SESSION, + "10.0.0.1", "10.0.0.2", 5, "Ethernet0", + 300000, 300000, 3); + memcpy(m_bfd.m_messageBuffer, buf, BFD_WIRE_MSG_LEN); + m_bfd.handleBfdDpMessage(0); + + shared_ptr app_db = make_shared("APPL_DB", 0); + Table app_bfd_session_table(app_db.get(), APP_BFD_SESSION_TABLE_NAME); + vector keys; + app_bfd_session_table.getKeys(keys); + ASSERT_EQ(keys.size(), 1u); + ASSERT_EQ(keys[0], "default:Ethernet0:10.0.0.2"); +} + +TEST_F(BfdSyncdTest, Ipv4LinkLocalRejected) +{ + ON_CALL(m_bfd, get_intf_mac(_)).WillByDefault(Return("00:11:22:33:44:55")); + ON_CALL(m_bfd, exec(_)).WillByDefault(Return("00:22:33:44:55:66")); + + unsigned char buf[BFD_WIRE_MSG_LEN]; + buildIpv4SessionWire(buf, sizeof(buf), DP_ADD_SESSION, + "10.0.0.1", "169.254.1.1", 5, "Ethernet0", + 300000, 300000, 3); + memcpy(m_bfd.m_messageBuffer, buf, BFD_WIRE_MSG_LEN); + m_bfd.handleBfdDpMessage(0); + + shared_ptr app_db = make_shared("APPL_DB", 0); + Table app_bfd_session_table(app_db.get(), APP_BFD_SESSION_TABLE_NAME); + vector keys; + app_bfd_session_table.getKeys(keys); + ASSERT_EQ(keys.size(), 0u); +} + +TEST_F(BfdSyncdTest, BfdStateUpdateFromStateDb) +{ + ON_CALL(m_bfd, get_intf_mac(_)).WillByDefault(Return("78:12:83:58:08:00")); + ON_CALL(m_bfd, exec(_)).WillByDefault(Return("78:12:83:58:08:01")); + addDefaultIpv6Session(m_bfd); + + Table stateTable(&m_state_db, STATE_BFD_SESSION_TABLE_NAME); + stateTable.set("default|Ethernet1_1|fe80::7a11:8ff:fe55:d400", {{"state", "Up"}}); + + EXPECT_CALL(m_bfd, sendmsg(STATE_UPDATE_MSG_LEN)).Times(1); + m_bfd.bfdStateUpdate("default|Ethernet1_1|fe80::7a11:8ff:fe55:d400"); +} + diff --git a/tests/test_bfd.py b/tests/test_bfd.py index 5cd18bbe05c..ce461a5bd10 100644 --- a/tests/test_bfd.py +++ b/tests/test_bfd.py @@ -637,3 +637,47 @@ def test_bfd_state_db_clear(self, dvs): time.sleep(5) keys = self.sdb.get_keys("BFD_SESSION_TABLE") assert len(keys) == 0 + + def test_addRemoveBfdSession_src_mac(self, dvs): + self.setup_db(dvs) + + bfdSessions = self.get_exist_bfd_session() + + fieldValues = { + "local_addr": "10.0.0.1", + "dst_mac": "00:02:03:04:05:06", + "src_mac": "00:11:22:33:44:55", + } + self.create_bfd_session("default:Ethernet0:10.0.0.2", fieldValues) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", len(bfdSessions) + 1) + + createdSessions = self.get_exist_bfd_session() - bfdSessions + assert len(createdSessions) == 1 + + session = createdSessions.pop() + expected_adb_values = { + "SAI_BFD_SESSION_ATTR_SRC_IP_ADDRESS": "10.0.0.1", + "SAI_BFD_SESSION_ATTR_DST_IP_ADDRESS": "10.0.0.2", + "SAI_BFD_SESSION_ATTR_TYPE": "SAI_BFD_SESSION_TYPE_ASYNC_ACTIVE", + "SAI_BFD_SESSION_ATTR_TOS": "192", + "SAI_BFD_SESSION_ATTR_IPHDR_VERSION": "4", + "SAI_BFD_SESSION_ATTR_HW_LOOKUP_VALID": "false", + "SAI_BFD_SESSION_ATTR_DST_MAC_ADDRESS": "00:02:03:04:05:06", + "SAI_BFD_SESSION_ATTR_SRC_MAC_ADDRESS": "00:11:22:33:44:55", + } + self.check_asic_bfd_session_value(session, expected_adb_values) + + self.remove_bfd_session("default:Ethernet0:10.0.0.2") + self.adb.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", session) + + def test_bfd_state_change_unknown_session(self, dvs): + self.setup_db(dvs) + + bfdSessions = self.get_exist_bfd_session() + ntf = swsscommon.NotificationProducer(dvs.adb, "NOTIFICATIONS") + fvp = swsscommon.FieldValuePairs() + ntf_data = '[{"bfd_session_id":"oid:0xdeadbeef","session_state":"SAI_BFD_SESSION_STATE_UP"}]' + ntf.send("bfd_session_state_change", ntf_data, fvp) + time.sleep(1) + + assert len(self.get_exist_bfd_session() - bfdSessions) == 0 From bf3913464ced30529eed7fbec393c34da538b892 Mon Sep 17 00:00:00 2001 From: Sridhar talari Date: Tue, 30 Jun 2026 10:23:07 -0700 Subject: [PATCH 09/14] Fix tests_bfdsyncd compile: define helper after MockBfdLink. addDefaultIpv6Session() referenced MockBfdLink before the class was declared, breaking the amd64 build. Signed-off-by: Sridhar talari --- tests/mock_tests/bfdsyncd/test_bfdlink.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/mock_tests/bfdsyncd/test_bfdlink.cpp b/tests/mock_tests/bfdsyncd/test_bfdlink.cpp index a04f564f400..9d23816c976 100644 --- a/tests/mock_tests/bfdsyncd/test_bfdlink.cpp +++ b/tests/mock_tests/bfdsyncd/test_bfdlink.cpp @@ -83,14 +83,6 @@ static void buildIpv4SessionWire(unsigned char *buf, memcpy(buf, &msg, BFD_WIRE_MSG_LEN); } -static void addDefaultIpv6Session(MockBfdLink &bfd) -{ - unsigned char buf[BFD_WIRE_MSG_LEN]; - copyDefaultIpv6AddBuffer(buf, sizeof(buf)); - memcpy(bfd.m_messageBuffer, buf, BFD_WIRE_MSG_LEN); - bfd.handleBfdDpMessage(0); -} - class MockBfdLink : public BfdLink { public: @@ -100,6 +92,14 @@ class MockBfdLink : public BfdLink MOCK_METHOD(string, get_intf_mac, (const char* intf), (override)); }; +static void addDefaultIpv6Session(MockBfdLink &bfd) +{ + unsigned char buf[BFD_WIRE_MSG_LEN]; + copyDefaultIpv6AddBuffer(buf, sizeof(buf)); + memcpy(bfd.m_messageBuffer, buf, BFD_WIRE_MSG_LEN); + bfd.handleBfdDpMessage(0); +} + class BfdSyncdTest : public ::testing::Test { From 3dc808dc2ff89d8ffec0e4e893d5f1c2279c6989 Mon Sep 17 00:00:00 2001 From: Sridhar talari Date: Tue, 30 Jun 2026 11:15:35 -0700 Subject: [PATCH 10/14] Fix StateUpdateWithRemoteFields mock sendmsg return value. handleBfdStateUpdate returns sendmsg()'s bool result; without WillOnce(Return(true)) the gmock default false fails ASSERT_TRUE. Signed-off-by: Sridhar talari --- tests/mock_tests/bfdsyncd/test_bfdlink.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/mock_tests/bfdsyncd/test_bfdlink.cpp b/tests/mock_tests/bfdsyncd/test_bfdlink.cpp index 9d23816c976..e55d97ef1ab 100644 --- a/tests/mock_tests/bfdsyncd/test_bfdlink.cpp +++ b/tests/mock_tests/bfdsyncd/test_bfdlink.cpp @@ -262,7 +262,7 @@ TEST_F(BfdSyncdTest, StateUpdateWithRemoteFields) ON_CALL(m_bfd, exec(_)).WillByDefault(Return("78:12:83:58:08:01")); addDefaultIpv6Session(m_bfd); - EXPECT_CALL(m_bfd, sendmsg(STATE_UPDATE_MSG_LEN)).Times(1); + EXPECT_CALL(m_bfd, sendmsg(STATE_UPDATE_MSG_LEN)).Times(1).WillOnce(Return(true)); std::vector fieldValues = { {"state", "Down"}, From b416b8f550405a843ddda4a360a5df56dfbf0bdd Mon Sep 17 00:00:00 2001 From: Sridhar talari Date: Tue, 30 Jun 2026 16:35:29 -0700 Subject: [PATCH 11/14] Address Copilot review comments and expand bfdsyncd test coverage. Fix bfdorch state update to avoid duplicate STATE_DB fields, harden bfdsyncd/bfdlink key parsing and invalid-message handling, validate port and remote_multiplier inputs, and add unit/VS tests for malformed keys, overflow values, unsupported message types, and IPv4 paths. Signed-off-by: Sridhar talari --- bfdsyncd/bfdlink.cpp | 53 +++++++++--- bfdsyncd/bfdsyncd.cpp | 18 +++- orchagent/bfdorch.cpp | 2 - tests/mock_tests/bfdsyncd/test_bfdlink.cpp | 98 ++++++++++++++++++++++ tests/test_bfd.py | 23 +++++ 5 files changed, 179 insertions(+), 15 deletions(-) diff --git a/bfdsyncd/bfdlink.cpp b/bfdsyncd/bfdlink.cpp index e0843fed7bb..d7174c0f621 100644 --- a/bfdsyncd/bfdlink.cpp +++ b/bfdsyncd/bfdlink.cpp @@ -9,6 +9,8 @@ #include #include +#include +#include #include #include #include @@ -54,6 +56,27 @@ static bool parseUint32Field(const std::string &field, const std::string &value, } } +static bool parseStateDbKey(const std::string &k, std::string &vrf, std::string &intf, std::string &ip) +{ + size_t pos1 = k.find('|'); + if (pos1 == std::string::npos) + { + return false; + } + + vrf = k.substr(0, pos1); + std::string rest = k.substr(pos1 + 1); + size_t pos2 = rest.find('|'); + if (pos2 == std::string::npos) + { + return false; + } + + intf = rest.substr(0, pos2); + ip = rest.substr(pos2 + 1); + return !vrf.empty() && !intf.empty() && !ip.empty(); +} + } // namespace static const char *bfd_dplane_messagetype2str(enum bfddp_message_type bmt) @@ -282,6 +305,8 @@ uint64_t BfdLink::readData() if (!bfd_msg_ok(hdr)) { + SWSS_LOG_ERROR("Invalid BFD DP message header, dropping %zu buffered bytes", m_pos - start); + start = m_pos; break; } @@ -407,7 +432,8 @@ void BfdLink::handleBfdDpMessage(size_t start) bfdkey_map = string("default|default|")+string(dst_addr); ifindex = ntohl(bm.data.session.ifindex); - memcpy(&ifname, bm.data.session.ifname, IFNAME_LEN); + memcpy(ifname, bm.data.session.ifname, IFNAME_LEN); + ifname[IFNAME_LEN - 1] = '\0'; /* for link-local address only */ if (ifindex != 0) { @@ -640,20 +666,20 @@ bool BfdLink::handleBfdStateUpdate(std::string k, const std::vector::iterator it; SWSS_LOG_INFO("lookup key %s ", key.c_str()); it = m_key2bfd.find(key); @@ -750,6 +776,11 @@ bool BfdLink::handleBfdStateUpdate(std::string k, const std::vector UINT8_MAX) + { + SWSS_LOG_ERROR("remote_multiplier value %u exceeds uint8_t range", multiplier); + return false; + } msg.data.state.detection_multiplier = static_cast(multiplier); SWSS_LOG_INFO("remote_multiplier %u", msg.data.state.detection_multiplier ); } diff --git a/bfdsyncd/bfdsyncd.cpp b/bfdsyncd/bfdsyncd.cpp index c5163db7bf8..352e1b18257 100644 --- a/bfdsyncd/bfdsyncd.cpp +++ b/bfdsyncd/bfdsyncd.cpp @@ -1,5 +1,10 @@ -#include +#include +#include +#include +#include +#include #include +#include #include "logger.h" #include "select.h" #include "selectabletimer.h" @@ -33,7 +38,16 @@ int main(int argc, char **argv) break; case 'p': port_str = optarg; - sscanf(port_str, "%hd", &port); + { + unsigned int parsed = 0; + char extra = '\0'; + if ((sscanf(port_str, "%u%c", &parsed, &extra) != 1) || (parsed > 65535)) + { + fprintf(stderr, "Invalid TCP port number: %s\n", port_str); + return 1; + } + port = static_cast(parsed); + } break; case '?': if (optopt == 'p') diff --git a/orchagent/bfdorch.cpp b/orchagent/bfdorch.cpp index 3f97bacc711..cc2fc1aa30b 100644 --- a/orchagent/bfdorch.cpp +++ b/orchagent/bfdorch.cpp @@ -261,8 +261,6 @@ void BfdOrch::doTask(NotificationConsumer &consumer) { auto key = bfd_session_lookup[id].peer; vector fvVector; - m_stateBfdSessionTable.get(key, fvVector); - fvVector.emplace_back("state", session_state_lookup.at(state)); // Update remote parameter after each state change diff --git a/tests/mock_tests/bfdsyncd/test_bfdlink.cpp b/tests/mock_tests/bfdsyncd/test_bfdlink.cpp index e55d97ef1ab..ba430576e6f 100644 --- a/tests/mock_tests/bfdsyncd/test_bfdlink.cpp +++ b/tests/mock_tests/bfdsyncd/test_bfdlink.cpp @@ -396,3 +396,101 @@ TEST_F(BfdSyncdTest, BfdStateUpdateFromStateDb) m_bfd.bfdStateUpdate("default|Ethernet1_1|fe80::7a11:8ff:fe55:d400"); } +TEST_F(BfdSyncdTest, StateUpdateMalformedKeyFormat) +{ + EXPECT_CALL(m_bfd, sendmsg(_)).Times(0); + + std::vector fieldValues = {{"state", "Up"}}; + ASSERT_FALSE(m_bfd.handleBfdStateUpdate("default|Ethernet0", fieldValues)); +} + +TEST_F(BfdSyncdTest, StateUpdateRemoteMultiplierOverflow) +{ + ON_CALL(m_bfd, get_intf_mac(_)).WillByDefault(Return("78:12:83:58:08:00")); + ON_CALL(m_bfd, exec(_)).WillByDefault(Return("78:12:83:58:08:01")); + addDefaultIpv6Session(m_bfd); + + EXPECT_CALL(m_bfd, sendmsg(_)).Times(0); + + std::vector fieldValues = { + {"state", "Up"}, + {"remote_multiplier", "300"}, + }; + auto key = string("default|Ethernet1_1|fe80::7a11:8ff:fe55:d400"); + ASSERT_FALSE(m_bfd.handleBfdStateUpdate(key, fieldValues)); +} + +TEST_F(BfdSyncdTest, UnsupportedMessageType) +{ + unsigned char buf[BFD_WIRE_MSG_LEN]; + copyDefaultIpv6AddBuffer(buf, sizeof(buf)); + buf[2] = 0; + buf[3] = ECHO_REQUEST; + + EXPECT_CALL(m_bfd, sendmsg(_)).Times(0); + memcpy(m_bfd.m_messageBuffer, buf, BFD_WIRE_MSG_LEN); + m_bfd.handleBfdDpMessage(0); +} + +TEST_F(BfdSyncdTest, Ipv4MultihopNoInterface) +{ + unsigned char buf[BFD_WIRE_MSG_LEN]; + buildIpv4SessionWire(buf, sizeof(buf), DP_ADD_SESSION, + "10.0.0.1", "10.0.0.2", 0, nullptr, + 300000, 300000, 3); + memcpy(m_bfd.m_messageBuffer, buf, BFD_WIRE_MSG_LEN); + m_bfd.handleBfdDpMessage(0); + + shared_ptr app_db = make_shared("APPL_DB", 0); + Table app_bfd_session_table(app_db.get(), APP_BFD_SESSION_TABLE_NAME); + vector keys; + app_bfd_session_table.getKeys(keys); + ASSERT_EQ(keys.size(), 1u); + ASSERT_EQ(keys[0], "default:default:10.0.0.2"); + + vector fieldValues; + app_bfd_session_table.get(keys[0], fieldValues); + bool hasDstMac = false; + for (const auto &fv : fieldValues) + { + if (fvField(fv) == "dst_mac") + { + hasDstMac = true; + } + } + ASSERT_FALSE(hasDstMac); +} + +TEST_F(BfdSyncdTest, MacLookupFailureShortResponse) +{ + ON_CALL(m_bfd, get_intf_mac(_)).WillByDefault(Return("78:12:83:58:08:00")); + ON_CALL(m_bfd, exec(_)).WillByDefault(Return("short")); + + unsigned char buf[BFD_WIRE_MSG_LEN]; + copyDefaultIpv6AddBuffer(buf, sizeof(buf)); + memcpy(m_bfd.m_messageBuffer, buf, BFD_WIRE_MSG_LEN); + m_bfd.handleBfdDpMessage(0); + + shared_ptr app_db = make_shared("APPL_DB", 0); + Table app_bfd_session_table(app_db.get(), APP_BFD_SESSION_TABLE_NAME); + vector keys; + app_bfd_session_table.getKeys(keys); + ASSERT_EQ(keys.size(), 0u); +} + +TEST_F(BfdSyncdTest, StateUpdateInvalidRemoteMinRx) +{ + ON_CALL(m_bfd, get_intf_mac(_)).WillByDefault(Return("78:12:83:58:08:00")); + ON_CALL(m_bfd, exec(_)).WillByDefault(Return("78:12:83:58:08:01")); + addDefaultIpv6Session(m_bfd); + + EXPECT_CALL(m_bfd, sendmsg(_)).Times(0); + + std::vector fieldValues = { + {"state", "Up"}, + {"remote_min_rx", "bad-value"}, + }; + auto key = string("default|Ethernet1_1|fe80::7a11:8ff:fe55:d400"); + ASSERT_FALSE(m_bfd.handleBfdStateUpdate(key, fieldValues)); +} + diff --git a/tests/test_bfd.py b/tests/test_bfd.py index ce461a5bd10..677f0c2b376 100644 --- a/tests/test_bfd.py +++ b/tests/test_bfd.py @@ -681,3 +681,26 @@ def test_bfd_state_change_unknown_session(self, dvs): time.sleep(1) assert len(self.get_exist_bfd_session() - bfdSessions) == 0 + + def test_bfd_state_change_remote_fields(self, dvs): + self.setup_db(dvs) + + bfdSessions = self.get_exist_bfd_session() + + fieldValues = {"local_addr": "10.0.0.1"} + self.create_bfd_session("default:default:10.0.0.2", fieldValues) + self.adb.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_BFD_SESSION", len(bfdSessions) + 1) + + createdSessions = self.get_exist_bfd_session() - bfdSessions + assert len(createdSessions) == 1 + session = createdSessions.pop() + + self.update_bfd_session_state(dvs, session, "Up") + time.sleep(2) + + state = self.sdb.get_entry("BFD_SESSION_TABLE", "default|default|10.0.0.2") + assert state["state"] == "Up" + for remote_field in ("remote_discriminator", "remote_multiplier", + "remote_min_rx", "remote_min_tx"): + if remote_field in state: + assert state[remote_field].isdigit() From 77b5358bea7c21fae13497bd682ca69712215c09 Mon Sep 17 00:00:00 2001 From: stalarir Date: Thu, 6 Aug 2026 14:47:56 -0700 Subject: [PATCH 12/14] [bfdsyncd]: Remove single-hop BFD session support from handleBfdDpMessage Remove link-local address detection, interface-based key construction, MAC address resolution (ping/ARP/NDP), and dst_mac/src_mac fields from APP_DB entries. HW offload now handles multihop sessions only. Signed-off-by: stalarir --- bfdsyncd/bfdlink.cpp | 66 -------------------------------------------- 1 file changed, 66 deletions(-) diff --git a/bfdsyncd/bfdlink.cpp b/bfdsyncd/bfdlink.cpp index d7174c0f621..29fa5d370be 100644 --- a/bfdsyncd/bfdlink.cpp +++ b/bfdsyncd/bfdlink.cpp @@ -421,11 +421,6 @@ void BfdLink::handleBfdDpMessage(size_t start) struct in_addr v4; sprintf(src_addr, "%s", inet_ntoa(*(struct in_addr *)&bmp->data.session.src)); sprintf(dst_addr, "%s", inet_ntoa(*(struct in_addr *)&bmp->data.session.dst)); - /* check link local ip address 169.254.0.0/16 0xa9fe0000 */ - if ((inet_pton(AF_INET, dst_addr, &v4) == 1) && ((ntohl(v4.s_addr) >> 16) == 0xA9FE)) { - is_linklocal = true; - SWSS_LOG_INFO("dst_addr %s is a link local ip address", dst_addr); - } } bfdkey = string("default:default:")+string(dst_addr); @@ -435,59 +430,6 @@ void BfdLink::handleBfdDpMessage(size_t start) memcpy(ifname, bm.data.session.ifname, IFNAME_LEN); ifname[IFNAME_LEN - 1] = '\0'; - /* for link-local address only */ - if (ifindex != 0) { - bfdkey = string("default:")+string(ifname)+string(":")+string(dst_addr); - bfdkey_map = string("default|")+string(ifname)+string("|")+string(dst_addr); - } - - /* mac address is not needed for deletion, neighbor entry might be deleted already */ - if ((ifindex != 0) && (bm.header.type == DP_ADD_SESSION)) { - /* get src mac address */ - src_mac = get_intf_mac(ifname); - - if (flags & SESSION_IPV6) - { - /* update ndp table */ - cmd = string("ping6 -c 3 ") + string(dst_addr) + string(" -I ") + string(ifname); - SWSS_LOG_INFO("CMD: %s", cmd.c_str()); - exec(cmd.c_str()); - - /* get dst mac address */ - cmd = string("ip -6 neighbor get ") + string(dst_addr) + string(" dev ") + string(ifname) + string(" | grep -o -E ..:..:..:..:..:.."); - SWSS_LOG_INFO("CMD: %s", cmd.c_str()); - dst_str = exec(cmd.c_str()); - if (dst_str.length() < 17) { - SWSS_LOG_ERROR("mac address length is not correct: dst_mac %s ", dst_str.c_str()); - return; - } - dst_mac = dst_str.substr(0,17); - } - else - { - /* update arp table */ - if (is_linklocal) { - SWSS_LOG_ERROR("IPv4 link-local is not supported!"); - return; - } else { - cmd = string("ping -c 3 ") + string(dst_addr) + string(" -I ") + string(ifname); - } - SWSS_LOG_INFO("CMD: %s", cmd.c_str()); - exec(cmd.c_str()); - - /* get dst mac address */ - cmd = string("arp ") + string(dst_addr) + string(" | grep -o -E ..:..:..:..:..:.."); - SWSS_LOG_INFO("CMD: %s", cmd.c_str()); - dst_str = exec(cmd.c_str()); - if (dst_str.length() < 17) { - SWSS_LOG_ERROR("mac address length is not correct: ip_address %s, dst_mac %s", dst_addr, dst_str.c_str()); - return; - } - dst_mac = dst_str.substr(0,17); - } - SWSS_LOG_INFO("dst_mac %s , src_mac %s", dst_mac.c_str(), src_mac.c_str()); - } - if (bm.header.type == DP_ADD_SESSION) { std::map::iterator it; SWSS_LOG_INFO("bfd session lookup key %s ", bfdkey_map.c_str()); @@ -542,14 +484,6 @@ void BfdLink::handleBfdDpMessage(size_t start) FieldValueTuple la("local_addr", src_addr); fvVector.push_back(la); - /* Specify both dst_mac and src_mac for inject-down */ - if (ifindex != 0) { - FieldValueTuple d_mac("dst_mac", dst_mac.c_str()); - fvVector.push_back(d_mac); - FieldValueTuple s_mac("src_mac", src_mac.c_str()); - fvVector.push_back(s_mac); - } - /* let bfdorch use default value if the following parameters are not provided */ if (rx_int != 0) { From 3964ac5009d94aec1eeabc9b2c11391fa6c24d50 Mon Sep 17 00:00:00 2001 From: stalarir Date: Thu, 6 Aug 2026 17:20:33 -0700 Subject: [PATCH 13/14] [bfdsyncd]: address PR 4676 follow-ups and drop link-local tests Signed-off-by: stalarir --- bfdsyncd/bfdlink.cpp | 15 +- tests/mock_tests/bfdsyncd/test_bfdlink.cpp | 156 ++++++++------------- 2 files changed, 63 insertions(+), 108 deletions(-) diff --git a/bfdsyncd/bfdlink.cpp b/bfdsyncd/bfdlink.cpp index 29fa5d370be..4afadb50160 100644 --- a/bfdsyncd/bfdlink.cpp +++ b/bfdsyncd/bfdlink.cpp @@ -78,7 +78,6 @@ static bool parseStateDbKey(const std::string &k, std::string &vrf, std::string } } // namespace - static const char *bfd_dplane_messagetype2str(enum bfddp_message_type bmt) { switch (bmt) { @@ -334,20 +333,14 @@ void BfdLink::handleBfdDpMessage(size_t start) size_t msg_len; uint32_t flags; uint32_t lid; - uint32_t ifindex; uint32_t rx_int; uint32_t tx_int; string bfdkey = ""; string bfdkey_map = ""; bool add = true; bool multihop = true; - bool is_linklocal = false; char dst_addr[INET6_ADDRSTRLEN]; char src_addr[INET6_ADDRSTRLEN]; - char ifname[IFNAME_LEN]; - string dst_mac; - string src_mac; - string cmd, dst_str; bmp = reinterpret_cast(static_cast(m_messageBuffer+start)); @@ -426,10 +419,6 @@ void BfdLink::handleBfdDpMessage(size_t start) bfdkey = string("default:default:")+string(dst_addr); bfdkey_map = string("default|default|")+string(dst_addr); - ifindex = ntohl(bm.data.session.ifindex); - memcpy(ifname, bm.data.session.ifname, IFNAME_LEN); - ifname[IFNAME_LEN - 1] = '\0'; - if (bm.header.type == DP_ADD_SESSION) { std::map::iterator it; SWSS_LOG_INFO("bfd session lookup key %s ", bfdkey_map.c_str()); @@ -523,8 +512,8 @@ void BfdLink::handleBfdDpMessage(size_t start) SWSS_LOG_INFO("delete key %s from appl DB", bfdkey.c_str()); } - SWSS_LOG_NOTICE("BfdTable op %s key: %s local_addr:%s multihop:%s rx_interval:%d tx_interval:%d ifindex:%d ifname:%s ", - add?"add":"del", bfdkey.c_str(), src_addr, multihop?"true":"false", rx_int, tx_int, ifindex, ifname); + SWSS_LOG_NOTICE("BfdTable op %s key: %s local_addr:%s multihop:%s rx_interval:%d tx_interval:%d", + add?"add":"del", bfdkey.c_str(), src_addr, multihop?"true":"false", rx_int, tx_int); if (m_debug) { this->bfdDebugMessage(&bm); diff --git a/tests/mock_tests/bfdsyncd/test_bfdlink.cpp b/tests/mock_tests/bfdsyncd/test_bfdlink.cpp index ba430576e6f..9c2d591dbeb 100644 --- a/tests/mock_tests/bfdsyncd/test_bfdlink.cpp +++ b/tests/mock_tests/bfdsyncd/test_bfdlink.cpp @@ -27,22 +27,49 @@ static constexpr unsigned short BFD_TEST_PORT = 0; static const size_t BFD_WIRE_MSG_LEN = sizeof(bfddp_message_header) + sizeof(bfddp_session); -static void copyDefaultIpv6AddBuffer(unsigned char *buf, size_t bufSize) +static void buildIpv6SessionWire(unsigned char *buf, + size_t bufSize, + uint16_t dpType, + const char *srcIp, + const char *dstIp, + uint32_t ifindex, + const char *ifname, + uint32_t minRxUs, + uint32_t minTxUs, + uint8_t detectMult) { - static const unsigned char s[BFD_WIRE_MSG_LEN] = { - 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x10, 0xfe, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7a, 0xa4, 0x3e, 0xff, 0xfe, 0x72, 0xac, 0x00, 0xfe, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7a, 0x11, 0x08, 0xff, 0xfe, 0x55, 0xd4, 0x00, 0x24, 0x08, 0xc7, 0x9e, - 0x00, 0x04, 0x93, 0xe0, 0x00, 0x04, 0x93, 0xe0, 0x00, 0x00, 0xc3, 0x50, 0x00, 0x00, 0xc3, 0x50, - 0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x45, 0x74, 0x68, 0x65, - 0x72, 0x6e, 0x65, 0x74, 0x31, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; + bfddp_message msg = {}; + struct in6_addr src6 = {}; + struct in6_addr dst6 = {}; ASSERT_GE(bufSize, BFD_WIRE_MSG_LEN); - memcpy(buf, s, BFD_WIRE_MSG_LEN); + ASSERT_EQ(inet_pton(AF_INET6, srcIp, &src6), 1); + ASSERT_EQ(inet_pton(AF_INET6, dstIp, &dst6), 1); + + msg.header.version = BFD_DP_VERSION; + msg.header.type = htons(dpType); + msg.header.length = htons(static_cast(BFD_WIRE_MSG_LEN)); + msg.data.session.flags = htonl(SESSION_IPV6); + memcpy(&msg.data.session.src, &src6, sizeof(src6)); + memcpy(&msg.data.session.dst, &dst6, sizeof(dst6)); + msg.data.session.lid = htonl(0x20); + msg.data.session.min_rx = htonl(minRxUs); + msg.data.session.min_tx = htonl(minTxUs); + msg.data.session.detect_mult = detectMult; + msg.data.session.ifindex = htonl(ifindex); + if (ifname != nullptr) + { + strncpy(msg.data.session.ifname, ifname, IFNAME_LEN - 1); + } + + memcpy(buf, &msg, BFD_WIRE_MSG_LEN); +} + +static void copyDefaultIpv6AddBuffer(unsigned char *buf, size_t bufSize) +{ + buildIpv6SessionWire(buf, bufSize, DP_ADD_SESSION, + "2000::1", "2000::2", 0, nullptr, + 300000, 300000, 3); } static void buildIpv4SessionWire(unsigned char *buf, @@ -129,9 +156,6 @@ TEST_F(BfdSyncdTest, SingleMessageInBfdMessage) unsigned char s[BFD_WIRE_MSG_LEN]; copyDefaultIpv6AddBuffer(s, sizeof(s)); - ON_CALL(m_bfd, get_intf_mac(_)).WillByDefault(Return("78:12:83:58:08:00")); - ON_CALL(m_bfd, exec(_)).WillByDefault(Return("78:12:83:58:08:01")); - memcpy(m_bfd.m_messageBuffer, static_cast(s), BFD_WIRE_MSG_LEN); m_bfd.handleBfdDpMessage(0); @@ -142,8 +166,6 @@ TEST_F(BfdSyncdTest, SingleMessageInBfdMessage) vector fieldValues; string multihop; string local_addr; - string dst_mac; - string src_mac; string rx_interval; string tx_interval; string multiplier; @@ -159,16 +181,12 @@ TEST_F(BfdSyncdTest, SingleMessageInBfdMessage) if (field == "multihop") multihop = value; if (field == "local_addr") local_addr = value; - if (field == "dst_mac") dst_mac = value; - if (field == "src_mac") src_mac = value; if (field == "rx_interval") rx_interval = value; if (field == "tx_interval") tx_interval = value; if (field == "multiplier" ) multiplier = value; } ASSERT_EQ( multihop, "false"); - ASSERT_EQ( local_addr, "fe80::7aa4:3eff:fe72:ac00"); - ASSERT_EQ( dst_mac, "78:12:83:58:08:01"); - ASSERT_EQ( src_mac, "78:12:83:58:08:00"); + ASSERT_EQ( local_addr, "2000::1"); ASSERT_EQ( rx_interval, "300"); ASSERT_EQ( tx_interval, "300"); ASSERT_EQ( multiplier, "3"); @@ -181,7 +199,7 @@ TEST_F(BfdSyncdTest, SingleMessageInBfdMessage) std::vector fieldValues = { {"state", "Up"}, }; - auto key = string("default|Ethernet1_1|fe80::7a11:8ff:fe55:d400"); + auto key = string("default|default|2000::2"); m_bfd.handleBfdStateUpdate(key, fieldValues); } @@ -190,38 +208,22 @@ TEST_F(BfdSyncdTest, SingleMessageInBfdMessage) { EXPECT_CALL(m_bfd, sendmsg(COUNTER_MSG_LEN)).Times(1); - unsigned char s[] = { - 0x01, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x10, 0xfe, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7a, 0xa4, 0x3e, 0xff, 0xfe, 0x72, 0xac, 0x00, 0xfe, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7a, 0x11, 0x08, 0xff, 0xfe, 0x55, 0xd4, 0x00, 0x24, 0x08, 0xc7, 0x9e, - 0x00, 0x04, 0x93, 0xe0, 0x00, 0x04, 0x93, 0xe0, 0x00, 0x00, 0xc3, 0x50, 0x00, 0x00, 0xc3, 0x50, - 0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x45, 0x74, 0x68, 0x65, - 0x72, 0x6e, 0x65, 0x74, 0x31, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - - memcpy(m_bfd.m_messageBuffer, static_cast(s), sizeof(s)); + unsigned char s[BFD_WIRE_MSG_LEN]; + copyDefaultIpv6AddBuffer(s, sizeof(s)); + s[2] = 0; + s[3] = DP_REQUEST_SESSION_COUNTERS; + memcpy(m_bfd.m_messageBuffer, static_cast(s), BFD_WIRE_MSG_LEN); m_bfd.handleBfdDpMessage(0); } //Delete BFD session and Check APPL DB { - unsigned char s[] = { - 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x10, 0xfe, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7a, 0xa4, 0x3e, 0xff, 0xfe, 0x72, 0xac, 0x00, 0xfe, 0x80, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7a, 0x11, 0x08, 0xff, 0xfe, 0x55, 0xd4, 0x00, 0x24, 0x08, 0xc7, 0x9e, - 0x00, 0x04, 0x93, 0xe0, 0x00, 0x04, 0x93, 0xe0, 0x00, 0x00, 0xc3, 0x50, 0x00, 0x00, 0xc3, 0x50, - 0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x45, 0x74, 0x68, 0x65, - 0x72, 0x6e, 0x65, 0x74, 0x31, 0x5f, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - - memcpy(m_bfd.m_messageBuffer, static_cast(s), sizeof(s)); + unsigned char s[BFD_WIRE_MSG_LEN]; + copyDefaultIpv6AddBuffer(s, sizeof(s)); + s[2] = 0; + s[3] = DP_DELETE_SESSION; + memcpy(m_bfd.m_messageBuffer, static_cast(s), BFD_WIRE_MSG_LEN); m_bfd.handleBfdDpMessage(0); @@ -239,7 +241,7 @@ TEST_F(BfdSyncdTest, SingleMessageInBfdMessage) std::vector fieldValues = { {"state", "Up"}, }; - auto key = string("default|Ethernet1_1|fe80::7a11:8ff:fe55:d400"); + auto key = string("default|default|2000::2"); m_bfd.handleBfdStateUpdate(key, fieldValues); } } @@ -271,7 +273,7 @@ TEST_F(BfdSyncdTest, StateUpdateWithRemoteFields) {"remote_min_tx", "200000"}, {"remote_multiplier", "3"}, }; - auto key = string("default|Ethernet1_1|fe80::7a11:8ff:fe55:d400"); + auto key = string("default|default|2000::2"); ASSERT_TRUE(m_bfd.handleBfdStateUpdate(key, fieldValues)); } @@ -287,7 +289,7 @@ TEST_F(BfdSyncdTest, StateUpdateInvalidRemoteField) {"state", "Up"}, {"remote_discriminator", "not-a-number"}, }; - auto key = string("default|Ethernet1_1|fe80::7a11:8ff:fe55:d400"); + auto key = string("default|default|2000::2"); ASSERT_FALSE(m_bfd.handleBfdStateUpdate(key, fieldValues)); } @@ -307,7 +309,7 @@ TEST_F(BfdSyncdTest, DuplicateAddIgnore) addDefaultIpv6Session(m_bfd); Table stateTable(&m_state_db, STATE_BFD_SESSION_TABLE_NAME); - stateTable.set("default|Ethernet1_1|fe80::7a11:8ff:fe55:d400", {{"state", "Up"}}); + stateTable.set("default|default|2000::2", {{"state", "Up"}}); EXPECT_CALL(m_bfd, sendmsg(STATE_UPDATE_MSG_LEN)).Times(1); addDefaultIpv6Session(m_bfd); @@ -361,26 +363,7 @@ TEST_F(BfdSyncdTest, Ipv4AddSession) vector keys; app_bfd_session_table.getKeys(keys); ASSERT_EQ(keys.size(), 1u); - ASSERT_EQ(keys[0], "default:Ethernet0:10.0.0.2"); -} - -TEST_F(BfdSyncdTest, Ipv4LinkLocalRejected) -{ - ON_CALL(m_bfd, get_intf_mac(_)).WillByDefault(Return("00:11:22:33:44:55")); - ON_CALL(m_bfd, exec(_)).WillByDefault(Return("00:22:33:44:55:66")); - - unsigned char buf[BFD_WIRE_MSG_LEN]; - buildIpv4SessionWire(buf, sizeof(buf), DP_ADD_SESSION, - "10.0.0.1", "169.254.1.1", 5, "Ethernet0", - 300000, 300000, 3); - memcpy(m_bfd.m_messageBuffer, buf, BFD_WIRE_MSG_LEN); - m_bfd.handleBfdDpMessage(0); - - shared_ptr app_db = make_shared("APPL_DB", 0); - Table app_bfd_session_table(app_db.get(), APP_BFD_SESSION_TABLE_NAME); - vector keys; - app_bfd_session_table.getKeys(keys); - ASSERT_EQ(keys.size(), 0u); + ASSERT_EQ(keys[0], "default:default:10.0.0.2"); } TEST_F(BfdSyncdTest, BfdStateUpdateFromStateDb) @@ -390,10 +373,10 @@ TEST_F(BfdSyncdTest, BfdStateUpdateFromStateDb) addDefaultIpv6Session(m_bfd); Table stateTable(&m_state_db, STATE_BFD_SESSION_TABLE_NAME); - stateTable.set("default|Ethernet1_1|fe80::7a11:8ff:fe55:d400", {{"state", "Up"}}); + stateTable.set("default|default|2000::2", {{"state", "Up"}}); EXPECT_CALL(m_bfd, sendmsg(STATE_UPDATE_MSG_LEN)).Times(1); - m_bfd.bfdStateUpdate("default|Ethernet1_1|fe80::7a11:8ff:fe55:d400"); + m_bfd.bfdStateUpdate("default|default|2000::2"); } TEST_F(BfdSyncdTest, StateUpdateMalformedKeyFormat) @@ -416,7 +399,7 @@ TEST_F(BfdSyncdTest, StateUpdateRemoteMultiplierOverflow) {"state", "Up"}, {"remote_multiplier", "300"}, }; - auto key = string("default|Ethernet1_1|fe80::7a11:8ff:fe55:d400"); + auto key = string("default|default|2000::2"); ASSERT_FALSE(m_bfd.handleBfdStateUpdate(key, fieldValues)); } @@ -461,23 +444,6 @@ TEST_F(BfdSyncdTest, Ipv4MultihopNoInterface) ASSERT_FALSE(hasDstMac); } -TEST_F(BfdSyncdTest, MacLookupFailureShortResponse) -{ - ON_CALL(m_bfd, get_intf_mac(_)).WillByDefault(Return("78:12:83:58:08:00")); - ON_CALL(m_bfd, exec(_)).WillByDefault(Return("short")); - - unsigned char buf[BFD_WIRE_MSG_LEN]; - copyDefaultIpv6AddBuffer(buf, sizeof(buf)); - memcpy(m_bfd.m_messageBuffer, buf, BFD_WIRE_MSG_LEN); - m_bfd.handleBfdDpMessage(0); - - shared_ptr app_db = make_shared("APPL_DB", 0); - Table app_bfd_session_table(app_db.get(), APP_BFD_SESSION_TABLE_NAME); - vector keys; - app_bfd_session_table.getKeys(keys); - ASSERT_EQ(keys.size(), 0u); -} - TEST_F(BfdSyncdTest, StateUpdateInvalidRemoteMinRx) { ON_CALL(m_bfd, get_intf_mac(_)).WillByDefault(Return("78:12:83:58:08:00")); @@ -490,7 +456,7 @@ TEST_F(BfdSyncdTest, StateUpdateInvalidRemoteMinRx) {"state", "Up"}, {"remote_min_rx", "bad-value"}, }; - auto key = string("default|Ethernet1_1|fe80::7a11:8ff:fe55:d400"); + auto key = string("default|default|2000::2"); ASSERT_FALSE(m_bfd.handleBfdStateUpdate(key, fieldValues)); } From 726242e76a833241f8e19005473a4b835e0153ba Mon Sep 17 00:00:00 2001 From: stalarir Date: Thu, 6 Aug 2026 20:18:05 -0700 Subject: [PATCH 14/14] Fix bfdsyncd unused IPv4 variable build failure Signed-off-by: stalarir --- bfdsyncd/bfdlink.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/bfdsyncd/bfdlink.cpp b/bfdsyncd/bfdlink.cpp index 4afadb50160..639b5b71ce8 100644 --- a/bfdsyncd/bfdlink.cpp +++ b/bfdsyncd/bfdlink.cpp @@ -411,7 +411,6 @@ void BfdLink::handleBfdDpMessage(size_t start) } else { - struct in_addr v4; sprintf(src_addr, "%s", inet_ntoa(*(struct in_addr *)&bmp->data.session.src)); sprintf(dst_addr, "%s", inet_ntoa(*(struct in_addr *)&bmp->data.session.dst)); }