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")