Commit ab6a259233 for frr

commit ab6a259233add181a620feb0490d98a15d951d3a
Author: Amr Shadid <amr.shadid.2016@gmail.com>
Date:   Tue Sep 15 06:48:59 2026 +0300

    tests: isisd: cover trailing bytes in extended-reach sub-TLVs

    Feed the unpacker a TLV 22 whose first item declares 8 sub-TLV bytes but
    only 6 form an Admin Group sub-TLV, leaving 2 stray bytes, followed by a
    second item.  Before the fix the parser left those 2 bytes and read the
    second neighbor from the wrong offset; the test checks that the second
    neighbor still parses correctly.

    Signed-off-by: Amr Shadid <amr.shadid.2016@gmail.com>

diff --git a/tests/isisd/subdir.am b/tests/isisd/subdir.am
index 74b6291cce..8daecf847c 100644
--- a/tests/isisd/subdir.am
+++ b/tests/isisd/subdir.am
@@ -74,3 +74,13 @@ tests_isisd_test_isis_vertex_queue_CPPFLAGS = $(TESTS_CPPFLAGS)
 tests_isisd_test_isis_vertex_queue_LDADD = $(ISISD_TEST_LDADD)
 tests_isisd_test_isis_vertex_queue_SOURCES = tests/isisd/test_isis_vertex_queue.c tests/isisd/test_common.c
 EXTRA_DIST += tests/isisd/test_isis_vertex_queue.py
+
+
+if ISISD
+check_PROGRAMS += tests/isisd/test_isis_tlv_ext_reach
+endif
+tests_isisd_test_isis_tlv_ext_reach_CFLAGS = $(TESTS_CFLAGS)
+tests_isisd_test_isis_tlv_ext_reach_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_isisd_test_isis_tlv_ext_reach_LDADD = $(ISISD_TEST_LDADD)
+tests_isisd_test_isis_tlv_ext_reach_SOURCES = tests/isisd/test_isis_tlv_ext_reach.c tests/isisd/test_common.c
+EXTRA_DIST += tests/isisd/test_isis_tlv_ext_reach.py
diff --git a/tests/isisd/test_isis_tlv_ext_reach.c b/tests/isisd/test_isis_tlv_ext_reach.c
new file mode 100644
index 0000000000..1cf812529a
--- /dev/null
+++ b/tests/isisd/test_isis_tlv_ext_reach.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <zebra.h>
+
+#include "memory.h"
+#include "sbuf.h"
+#include "stream.h"
+#include "frrevent.h"
+
+#include "isisd/isis_circuit.h"
+#include "isisd/isis_tlvs.h"
+
+#include "test_common.h"
+
+/*
+ * #22820: a first extended-reach item whose sub-TLV area ends in two stray
+ * bytes must not shift the parse of the following item.
+ */
+/* clang-format off */
+static const uint8_t lsp_body[] = {
+	0x16, 0x1e,			/* TLV type 22 (Ext Reach), length 30 */
+
+	/* item 1 */
+	0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00,	/* neighbor id */
+	0x00, 0x00, 0x0a,				/* metric 10 */
+	0x08,						/* 8 bytes of sub-TLVs */
+	0x03, 0x04, 0xde, 0xad, 0xbe, 0xef,		/* Admin Group sub-TLV (6 bytes) */
+	0xff, 0xfe,					/* 2 bytes declared but not a sub-TLV */
+
+	/* item 2 - must still parse as this exact neighbor */
+	0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00,	/* neighbor id */
+	0x00, 0x00, 0x14,				/* metric 20 */
+	0x00,						/* no sub-TLVs */
+};
+/* clang-format on */
+
+int main(int argc, char **argv)
+{
+	static const uint8_t want_id[7] = {
+		0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00,
+	};
+	struct stream *s = stream_new(sizeof(lsp_body));
+	struct isis_extended_reach *first, *second;
+	struct isis_tlvs *tlvs;
+	const char *log;
+	int rv;
+
+	stream_put(s, lsp_body, sizeof(lsp_body));
+	stream_set_getp(s, 0);
+
+	rv = isis_unpack_tlvs(STREAM_READABLE(s), s, &tlvs, &log);
+	printf("unpack rv=%d\nlog:\n%s\n", rv, log);
+	assert(rv == 0);
+
+	assert(tlvs->extended_reach.count == 2);
+	first = (struct isis_extended_reach *)tlvs->extended_reach.head;
+	second = first->next;
+	printf("second neighbor id %02x%02x.%02x%02x.%02x%02x.%02x\n", second->id[0], second->id[1],
+	       second->id[2], second->id[3], second->id[4], second->id[5], second->id[6]);
+	assert(memcmp(second->id, want_id, sizeof(want_id)) == 0);
+
+	isis_free_tlvs(tlvs);
+	stream_free(s);
+	printf("OK\n");
+	return 0;
+}
diff --git a/tests/isisd/test_isis_tlv_ext_reach.py b/tests/isisd/test_isis_tlv_ext_reach.py
new file mode 100644
index 0000000000..63bf7c5003
--- /dev/null
+++ b/tests/isisd/test_isis_tlv_ext_reach.py
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+import frrtest
+
+
+class TestIsisTlvExtReach(frrtest.TestMultiOut):
+    program = "./test_isis_tlv_ext_reach"
+
+
+TestIsisTlvExtReach.exit_cleanly()