Commit 1e107e7fad for frr
commit 1e107e7fadfe533a193f62ddb38eeed7d9b7d565
Author: Minghan Chan <mhchann082@gmail.com>
Date: Fri Oct 16 14:35:10 2026 +0800
tests: check the BGP-LS IGP Metric TLV length
Add a bgpd unit test that fills the BGP-LS attribute of a Link State
edge, as ospfd and isisd put it in the TED, and checks the IGP Metric
TLV that bgpd encodes for it: 2 octets for OSPFv2 and 3 octets for
IS-IS, with small and large metric values.
Without the bgpd fix the cases with OSPFv2 metrics 1 and 100 and with
IS-IS metrics 10 and 1000 fail.
Signed-off-by: Minghan Chan <mhchann082@gmail.com>
diff --git a/tests/.gitignore b/tests/.gitignore
index a03f79904c..fec450a833 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -7,6 +7,7 @@ frr_northbound*
/bgpd/test_allowas_rmap
/bgpd/test_aspath
/bgpd/test_attr_parse
+/bgpd/test_bgp_ls_attr
/bgpd/test_bgp_table
/bgpd/test_capability
/bgpd/test_community
diff --git a/tests/bgpd/subdir.am b/tests/bgpd/subdir.am
index 0fbe709ef0..1780a7c5b5 100644
--- a/tests/bgpd/subdir.am
+++ b/tests/bgpd/subdir.am
@@ -14,6 +14,16 @@ tests_bgpd_test_aspath_SOURCES = tests/bgpd/test_aspath.c
EXTRA_DIST += tests/bgpd/test_aspath.py
+if BGPD
+check_PROGRAMS += tests/bgpd/test_bgp_ls_attr
+endif
+tests_bgpd_test_bgp_ls_attr_CFLAGS = $(TESTS_CFLAGS)
+tests_bgpd_test_bgp_ls_attr_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_bgpd_test_bgp_ls_attr_LDADD = $(BGP_TEST_LDADD)
+tests_bgpd_test_bgp_ls_attr_SOURCES = tests/bgpd/test_bgp_ls_attr.c
+EXTRA_DIST += tests/bgpd/test_bgp_ls_attr.py
+
+
if BGPD
check_PROGRAMS += tests/bgpd/test_bgp_table
endif
diff --git a/tests/bgpd/test_bgp_ls_attr.c b/tests/bgpd/test_bgp_ls_attr.c
new file mode 100644
index 0000000000..2c9fc41cd1
--- /dev/null
+++ b/tests/bgpd/test_bgp_ls_attr.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * BGP-LS attribute encoding test
+ * Copyright (C) 2026 Minghan Chan <mhchann082@gmail.com>
+ *
+ * Checks that a Link State edge from the TED is encoded with the IGP Metric
+ * TLV (1095) length of its IGP (RFC 9552 section 5.3.2.4), whatever the
+ * metric value.
+ */
+
+#include <zebra.h>
+
+#include "privs.h"
+#include "stream.h"
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_ls_nlri.h"
+#include "bgpd/bgp_ls_ted.h"
+
+/* Satisfy link requirements from including bgpd.h */
+struct zebra_privs_t bgpd_privs = {};
+
+static int failed;
+
+#define TLV_MAX 8
+
+struct test_case {
+ const char *name;
+ const char *desc;
+ enum ls_origin origin;
+ uint32_t metric;
+ uint8_t tlv[TLV_MAX];
+ size_t tlv_len;
+};
+
+static const struct test_case tests[] = {
+ {
+ "OSPFv2-1",
+ "OSPFv2 metric 1 is 2 octets",
+ OSPFv2,
+ 1,
+ { 0x04, 0x47, 0x00, 0x02, 0x00, 0x01 },
+ 6,
+ },
+ {
+ "OSPFv2-100",
+ "OSPFv2 metric 100 is 2 octets",
+ OSPFv2,
+ 100,
+ { 0x04, 0x47, 0x00, 0x02, 0x00, 0x64 },
+ 6,
+ },
+ {
+ "OSPFv2-1000",
+ "OSPFv2 metric 1000 is 2 octets",
+ OSPFv2,
+ 1000,
+ { 0x04, 0x47, 0x00, 0x02, 0x03, 0xe8 },
+ 6,
+ },
+ {
+ "OSPFv2-65535",
+ "OSPFv2 metric 65535 is 2 octets",
+ OSPFv2,
+ 65535,
+ { 0x04, 0x47, 0x00, 0x02, 0xff, 0xff },
+ 6,
+ },
+ {
+ "ISIS-L2-10",
+ "IS-IS L2 wide metric 10 is 3 octets",
+ ISIS_L2,
+ 10,
+ { 0x04, 0x47, 0x00, 0x03, 0x00, 0x00, 0x0a },
+ 7,
+ },
+ {
+ "ISIS-L1-1000",
+ "IS-IS L1 wide metric 1000 is 3 octets",
+ ISIS_L1,
+ 1000,
+ { 0x04, 0x47, 0x00, 0x03, 0x00, 0x03, 0xe8 },
+ 7,
+ },
+ {
+ "ISIS-L2-16777215",
+ "IS-IS L2 wide metric 16777215 is 3 octets",
+ ISIS_L2,
+ 16777215,
+ { 0x04, 0x47, 0x00, 0x03, 0xff, 0xff, 0xff },
+ 7,
+ },
+};
+
+static void dump(const char *what, const uint8_t *buf, size_t len)
+{
+ printf(" %s:", what);
+ for (size_t i = 0; i < len; i++)
+ printf(" %02x", buf[i]);
+ printf("\n");
+}
+
+static void run_test(const struct test_case *t)
+{
+ struct ls_attributes ls_attr = {};
+ struct bgp_ls_attr *attr;
+ struct stream *s;
+ int len;
+ bool ok;
+
+ printf("%s: %s\n", t->name, t->desc);
+
+ ls_attr.adv.origin = t->origin;
+ ls_attr.metric = t->metric;
+ SET_FLAG(ls_attr.flags, LS_ATTR_METRIC);
+
+ attr = bgp_ls_attr_alloc();
+ s = stream_new(64);
+
+ ok = bgp_ls_populate_link_attr(&ls_attr, attr) == 0;
+ len = ok ? bgp_ls_encode_attr(s, attr) : -1;
+ ok = len == (int)t->tlv_len && memcmp(STREAM_DATA(s), t->tlv, t->tlv_len) == 0;
+
+ if (!ok) {
+ dump("expected", t->tlv, t->tlv_len);
+ dump("got ", STREAM_DATA(s), stream_get_endp(s));
+ failed++;
+ }
+ printf("%s\n", ok ? "OK" : "failed");
+
+ stream_free(s);
+ bgp_ls_attr_free(attr);
+}
+
+int main(void)
+{
+ for (size_t i = 0; i < array_size(tests); i++)
+ run_test(&tests[i]);
+
+ printf("failures: %d\n", failed);
+ return failed;
+}
diff --git a/tests/bgpd/test_bgp_ls_attr.py b/tests/bgpd/test_bgp_ls_attr.py
new file mode 100644
index 0000000000..4e2d57f7e8
--- /dev/null
+++ b/tests/bgpd/test_bgp_ls_attr.py
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+import frrtest
+
+
+class TestBgpLsAttr(frrtest.TestMultiOut):
+ program = "./test_bgp_ls_attr"
+
+
+TestBgpLsAttr.okfail("OSPFv2-1: OSPFv2 metric 1 is 2 octets")
+TestBgpLsAttr.okfail("OSPFv2-100: OSPFv2 metric 100 is 2 octets")
+TestBgpLsAttr.okfail("OSPFv2-1000: OSPFv2 metric 1000 is 2 octets")
+TestBgpLsAttr.okfail("OSPFv2-65535: OSPFv2 metric 65535 is 2 octets")
+TestBgpLsAttr.okfail("ISIS-L2-10: IS-IS L2 wide metric 10 is 3 octets")
+TestBgpLsAttr.okfail("ISIS-L1-1000: IS-IS L1 wide metric 1000 is 3 octets")
+TestBgpLsAttr.okfail("ISIS-L2-16777215: IS-IS L2 wide metric 16777215 is 3 octets")