summaryrefslogtreecommitdiffstats
path: root/tests/bgpd
diff options
context:
space:
mode:
Diffstat (limited to 'tests/bgpd')
-rw-r--r--tests/bgpd/subdir.am82
-rw-r--r--tests/bgpd/test_aspath.c1463
-rw-r--r--tests/bgpd/test_aspath.py84
-rw-r--r--tests/bgpd/test_bgp_table.c167
-rw-r--r--tests/bgpd/test_bgp_table.py9
-rw-r--r--tests/bgpd/test_capability.c984
-rw-r--r--tests/bgpd/test_capability.py56
-rw-r--r--tests/bgpd/test_ecommunity.c134
-rw-r--r--tests/bgpd/test_ecommunity.py11
-rw-r--r--tests/bgpd/test_mp_attr.c1117
-rw-r--r--tests/bgpd/test_mp_attr.py49
-rw-r--r--tests/bgpd/test_mpath.c482
-rw-r--r--tests/bgpd/test_mpath.py10
-rw-r--r--tests/bgpd/test_packet.c75
-rw-r--r--tests/bgpd/test_peer_attr.c1491
-rw-r--r--tests/bgpd/test_peer_attr.py200
16 files changed, 6414 insertions, 0 deletions
diff --git a/tests/bgpd/subdir.am b/tests/bgpd/subdir.am
new file mode 100644
index 0000000..5148e7e
--- /dev/null
+++ b/tests/bgpd/subdir.am
@@ -0,0 +1,82 @@
+if !BGPD
+PYTEST_IGNORE += --ignore=bgpd/
+endif
+BGP_TEST_LDADD = bgpd/libbgp.a $(RFPLDADD) $(ALL_TESTS_LDADD) $(LIBYANG_LIBS) $(UST_LIBS) -lm
+
+
+if BGPD
+check_PROGRAMS += tests/bgpd/test_aspath
+endif
+tests_bgpd_test_aspath_CFLAGS = $(TESTS_CFLAGS)
+tests_bgpd_test_aspath_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_bgpd_test_aspath_LDADD = $(BGP_TEST_LDADD)
+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_table
+endif
+tests_bgpd_test_bgp_table_CFLAGS = $(TESTS_CFLAGS)
+tests_bgpd_test_bgp_table_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_bgpd_test_bgp_table_LDADD = $(BGP_TEST_LDADD)
+tests_bgpd_test_bgp_table_SOURCES = tests/bgpd/test_bgp_table.c
+
+
+if BGPD
+check_PROGRAMS += tests/bgpd/test_capability
+endif
+tests_bgpd_test_capability_CFLAGS = $(TESTS_CFLAGS)
+tests_bgpd_test_capability_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_bgpd_test_capability_LDADD = $(BGP_TEST_LDADD)
+tests_bgpd_test_capability_SOURCES = tests/bgpd/test_capability.c
+EXTRA_DIST += tests/bgpd/test_capability.py
+
+
+if BGPD
+check_PROGRAMS += tests/bgpd/test_ecommunity
+endif
+tests_bgpd_test_ecommunity_CFLAGS = $(TESTS_CFLAGS)
+tests_bgpd_test_ecommunity_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_bgpd_test_ecommunity_LDADD = $(BGP_TEST_LDADD)
+tests_bgpd_test_ecommunity_SOURCES = tests/bgpd/test_ecommunity.c
+EXTRA_DIST += tests/bgpd/test_ecommunity.py
+
+
+if BGPD
+check_PROGRAMS += tests/bgpd/test_mp_attr
+endif
+tests_bgpd_test_mp_attr_CFLAGS = $(TESTS_CFLAGS)
+tests_bgpd_test_mp_attr_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_bgpd_test_mp_attr_LDADD = $(BGP_TEST_LDADD)
+tests_bgpd_test_mp_attr_SOURCES = tests/bgpd/test_mp_attr.c
+EXTRA_DIST += tests/bgpd/test_mp_attr.py
+
+
+if BGPD
+check_PROGRAMS += tests/bgpd/test_mpath
+endif
+tests_bgpd_test_mpath_CFLAGS = $(TESTS_CFLAGS)
+tests_bgpd_test_mpath_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_bgpd_test_mpath_LDADD = $(BGP_TEST_LDADD)
+tests_bgpd_test_mpath_SOURCES = tests/bgpd/test_mpath.c
+EXTRA_DIST += tests/bgpd/test_mpath.py
+
+
+if BGPD
+check_PROGRAMS += tests/bgpd/test_packet
+endif
+tests_bgpd_test_packet_CFLAGS = $(TESTS_CFLAGS)
+tests_bgpd_test_packet_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_bgpd_test_packet_LDADD = $(BGP_TEST_LDADD)
+tests_bgpd_test_packet_SOURCES = tests/bgpd/test_packet.c
+
+
+if BGPD
+check_PROGRAMS += tests/bgpd/test_peer_attr
+endif
+tests_bgpd_test_peer_attr_CFLAGS = $(TESTS_CFLAGS)
+tests_bgpd_test_peer_attr_CPPFLAGS = $(TESTS_CPPFLAGS)
+tests_bgpd_test_peer_attr_LDADD = $(BGP_TEST_LDADD)
+tests_bgpd_test_peer_attr_SOURCES = tests/bgpd/test_peer_attr.c
+EXTRA_DIST += tests/bgpd/test_peer_attr.py
diff --git a/tests/bgpd/test_aspath.c b/tests/bgpd/test_aspath.c
new file mode 100644
index 0000000..799733b
--- /dev/null
+++ b/tests/bgpd/test_aspath.c
@@ -0,0 +1,1463 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2005 Sun Microsystems, Inc.
+ */
+
+#include <zebra.h>
+
+#include "vty.h"
+#include "stream.h"
+#include "privs.h"
+#include "queue.h"
+#include "filter.h"
+#include "frr_pthread.h"
+
+#include "bgpd/bgpd.c"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_packet.h"
+
+#define VT100_RESET "\x1b[0m"
+#define VT100_RED "\x1b[31m"
+#define VT100_GREEN "\x1b[32m"
+#define VT100_YELLOW "\x1b[33m"
+#define OK VT100_GREEN "OK" VT100_RESET
+#define FAILED VT100_RED "failed" VT100_RESET
+
+/* need these to link in libbgp */
+struct zebra_privs_t bgpd_privs = {};
+struct event_loop *master = NULL;
+
+static int failed = 0;
+
+/* specification for a test - what the results should be */
+struct test_spec {
+ const char *shouldbe; /* the string the path should parse to */
+ const char *shouldbe_delete_confed; /* ditto, but once confeds are
+ deleted */
+ const unsigned int hops; /* aspath_count_hops result */
+ const unsigned int confeds; /* aspath_count_confeds */
+ const int private_as; /* whether the private_as check should pass or
+ fail */
+#define NOT_ALL_PRIVATE 0
+#define ALL_PRIVATE 1
+ const as_t does_loop; /* an ASN which should trigger loop-check */
+ const as_t doesnt_loop; /* one which should not */
+ const as_t first; /* the first ASN, if there is one */
+#define NULL_ASN 0
+};
+
+
+/* test segments to parse and validate, and use for other tests */
+static struct test_segment {
+ const char *name;
+ const char *desc;
+ const uint8_t asdata[1024];
+ int len;
+ struct test_spec sp;
+ enum asnotation_mode asnotation;
+} test_segments[] = {
+ {
+ /* 0 */
+ "seq1",
+ "seq(8466,3,52737,4096)",
+ {0x2, 0x4, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00},
+ 10,
+ {"8466 3 52737 4096", "8466 3 52737 4096", 4, 0,
+ NOT_ALL_PRIVATE, 4096, 4, 8466},
+ 0,
+ },
+ {
+ /* 1 */
+ "seq2",
+ "seq(8722) seq(4)",
+ {0x2, 0x1, 0x22, 0x12, 0x2, 0x1, 0x00, 0x04},
+ 8,
+ {
+ "8722 4",
+ "8722 4",
+ 2,
+ 0,
+ NOT_ALL_PRIVATE,
+ 4,
+ 5,
+ 8722,
+ },
+ 0,
+ },
+ {
+ /* 2 */
+ "seq3",
+ "seq(8466,3,52737,4096,8722,4)",
+ {0x2, 0x6, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x22,
+ 0x12, 0x00, 0x04},
+ 14,
+ {"8466 3 52737 4096 8722 4", "8466 3 52737 4096 8722 4", 6, 0,
+ NOT_ALL_PRIVATE, 3, 5, 8466},
+ 0,
+ },
+ {
+ /* 3 */
+ "seqset",
+ "seq(8482,51457) set(5204)",
+ {0x2, 0x2, 0x21, 0x22, 0xc9, 0x01, 0x1, 0x1, 0x14, 0x54},
+ 10,
+ {"8482 51457 {5204}", "8482 51457 {5204}", 3, 0,
+ NOT_ALL_PRIVATE, 5204, 51456, 8482},
+ 0,
+ },
+ {
+ /* 4 */
+ "seqset2",
+ "seq(8467, 59649) set(4196,48658) set(17322,30745)",
+ {0x2, 0x2, 0x21, 0x13, 0xe9, 0x01, 0x1, 0x2, 0x10, 0x64, 0xbe,
+ 0x12, 0x1, 0x2, 0x43, 0xaa, 0x78, 0x19},
+ 18,
+ {"8467 59649 {4196,48658} {17322,30745}",
+ "8467 59649 {4196,48658} {17322,30745}", 4, 0, NOT_ALL_PRIVATE,
+ 48658, 1, 8467},
+ 0,
+ },
+ {
+ /* 5 */
+ "multi",
+ "seq(6435,59408,21665) set(2457,61697,4369), seq(1842,41590,51793)",
+ {0x2, 0x3, 0x19, 0x23, 0xe8, 0x10, 0x54, 0xa1,
+ 0x1, 0x3, 0x09, 0x99, 0xf1, 0x01, 0x11, 0x11,
+ 0x2, 0x3, 0x07, 0x32, 0xa2, 0x76, 0xca, 0x51},
+ 24,
+ {"6435 59408 21665 {2457,4369,61697} 1842 41590 51793",
+ "6435 59408 21665 {2457,4369,61697} 1842 41590 51793", 7, 0,
+ NOT_ALL_PRIVATE, 51793, 1, 6435},
+ 0,
+ },
+ {
+ /* 6 */
+ "confed",
+ "confseq(123,456,789)",
+ {0x3, 0x3, 0x00, 0x7b, 0x01, 0xc8, 0x03, 0x15},
+ 8,
+ {"(123 456 789)", "", 0, 3, NOT_ALL_PRIVATE, 789, 1, NULL_ASN},
+ 0,
+ },
+ {
+ /* 7 */
+ "confed2",
+ "confseq(123,456,789) confseq(111,222)",
+ {0x3, 0x3, 0x00, 0x7b, 0x01, 0xc8, 0x03, 0x15, 0x3, 0x2, 0x00,
+ 0x6f, 0x00, 0xde},
+ 14,
+ {"(123 456 789) (111 222)", "", 0, 5, NOT_ALL_PRIVATE, 111, 1,
+ NULL_ASN},
+ 0,
+ },
+ {
+ /* 8 */
+ "confset",
+ "confset(456,123,789)",
+ {0x4, 0x3, 0x01, 0xc8, 0x00, 0x7b, 0x03, 0x15},
+ 8,
+ {"[123,456,789]", "", 0, 1, NOT_ALL_PRIVATE, 123, 1, NULL_ASN},
+ 0,
+ },
+ {
+ /* 9 */
+ "confmulti",
+ "confseq(123,456,789) confset(222,111) seq(8722) set(4196,48658)",
+ {0x3, 0x3, 0x00, 0x7b, 0x01, 0xc8, 0x03, 0x15,
+ 0x4, 0x2, 0x00, 0xde, 0x00, 0x6f, 0x2, 0x1,
+ 0x22, 0x12, 0x1, 0x2, 0x10, 0x64, 0xbe, 0x12},
+ 24,
+ {"(123 456 789) [111,222] 8722 {4196,48658}",
+ "8722 {4196,48658}", 2, 4, NOT_ALL_PRIVATE, 123, 1, NULL_ASN},
+ 0,
+ },
+ {
+ /* 10 */
+ "seq4",
+ "seq(8466,2,52737,4096,8722,4)",
+ {0x2, 0x6, 0x21, 0x12, 0x00, 0x02, 0xce, 0x01, 0x10, 0x00, 0x22,
+ 0x12, 0x00, 0x04},
+ 14,
+ {"8466 2 52737 4096 8722 4", "8466 2 52737 4096 8722 4", 6, 0,
+ NOT_ALL_PRIVATE, 4096, 1, 8466},
+ 0,
+ },
+ {
+ /* 11 */
+ "tripleseq1",
+ "seq(8466,2,52737) seq(4096,8722,4) seq(8722)",
+ {0x2, 0x3, 0x21, 0x12, 0x00, 0x02, 0xce, 0x01, 0x2, 0x3,
+ 0x10, 0x00, 0x22, 0x12, 0x00, 0x04, 0x2, 0x1, 0x22, 0x12},
+ 20,
+ {"8466 2 52737 4096 8722 4 8722",
+ "8466 2 52737 4096 8722 4 8722", 7, 0, NOT_ALL_PRIVATE, 4096,
+ 1, 8466},
+ 0,
+ },
+ {
+ /* 12 */
+ "someprivate",
+ "seq(8466,64512,52737,65535)",
+ {0x2, 0x4, 0x21, 0x12, 0xfc, 0x00, 0xce, 0x01, 0xff, 0xff},
+ 10,
+ {"8466 64512 52737 65535", "8466 64512 52737 65535", 4, 0,
+ NOT_ALL_PRIVATE, 65535, 4, 8466},
+ 0,
+ },
+ {
+ /* 13 */
+ "allprivate",
+ "seq(65534,64512,64513,65535)",
+ {0x2, 0x4, 0xff, 0xfe, 0xfc, 0x00, 0xfc, 0x01, 0xff, 0xff},
+ 10,
+ {"65534 64512 64513 65535", "65534 64512 64513 65535", 4, 0,
+ ALL_PRIVATE, 65534, 4, 65534},
+ 0,
+ },
+ {
+ /* 14 */
+ "long",
+ "seq(8466,3,52737,4096,34285,<repeated 49 more times>)",
+ {
+ 0x2, 0xfa, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10,
+ 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01,
+ 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce,
+ 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03,
+ 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00,
+ 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12,
+ 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21,
+ 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed,
+ 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85,
+ 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00,
+ 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10,
+ 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01,
+ 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce,
+ 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03,
+ 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00,
+ 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12,
+ 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21,
+ 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed,
+ 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85,
+ 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00,
+ 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10,
+ 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01,
+ 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce,
+ 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03,
+ 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00,
+ 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12,
+ 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21,
+ 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed,
+ 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85,
+ 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00,
+ 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10,
+ 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01,
+ 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce,
+ 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03,
+ 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00,
+ 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12,
+ 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21,
+ 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed,
+ 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85,
+ 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00,
+ 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10,
+ 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01,
+ 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce,
+ 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03,
+ 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00,
+ 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12,
+ 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21,
+ 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed,
+ 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x85,
+ 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00,
+ 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10,
+ 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01,
+ 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03, 0xce,
+ 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00, 0x03,
+ 0xce, 0x01, 0x10, 0x00, 0x85, 0xed, 0x21, 0x12, 0x00,
+ 0x03, 0xce, 0x01, 0x10, 0x00, 0x85, 0xed,
+ },
+ 502,
+ {"8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285",
+
+ "8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285",
+ 250, 0, NOT_ALL_PRIVATE, 4096, 4, 8466},
+ 0,
+ },
+ {
+ /* 15 */
+ "seq1extra",
+ "seq(8466,3,52737,4096,3456)",
+ {0x2, 0x5, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x0d,
+ 0x80},
+ 12,
+ {"8466 3 52737 4096 3456", "8466 3 52737 4096 3456", 5, 0,
+ NOT_ALL_PRIVATE, 4096, 4, 8466},
+ 0,
+ },
+ {
+ /* 16 */
+ "empty",
+ "<empty>",
+ {},
+ 0,
+ {"", "", 0, 0, 0, 0, 0, 0},
+ 0,
+ },
+ {
+ /* 17 */
+ "redundantset",
+ "seq(8466,3,52737,4096,3456) set(7099,8153,8153,8153)",
+ {0x2, 0x5, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01,
+ 0x10, 0x00, 0x0d, 0x80, 0x1, 0x4, 0x1b, 0xbb,
+ 0x1f, 0xd9, 0x1f, 0xd9, 0x1f, 0xd9},
+ 22,
+ {/* We shouldn't ever /generate/ such paths. However, we should
+ * cope with them fine.
+ */
+ "8466 3 52737 4096 3456 {7099,8153}",
+ "8466 3 52737 4096 3456 {7099,8153}", 6, 0, NOT_ALL_PRIVATE,
+ 4096, 4, 8466},
+ 0,
+ },
+ {
+ /* 18 */
+ "reconcile_lead_asp",
+ "seq(6435,59408,21665) set(23456,23456,23456), seq(23456,23456,23456)",
+ {0x2, 0x3, 0x19, 0x23, 0xe8, 0x10, 0x54, 0xa1,
+ 0x1, 0x3, 0x5b, 0xa0, 0x5b, 0xa0, 0x5b, 0xa0,
+ 0x2, 0x3, 0x5b, 0xa0, 0x5b, 0xa0, 0x5b, 0xa0},
+ 24,
+ {"6435 59408 21665 {23456} 23456 23456 23456",
+ "6435 59408 21665 {23456} 23456 23456 23456", 7, 0,
+ NOT_ALL_PRIVATE, 23456, 1, 6435},
+ 0,
+ },
+ {
+ /* 19 */
+ "reconcile_new_asp",
+ "set(2457,61697,4369), seq(1842,41591,51793)",
+ {0x1, 0x3, 0x09, 0x99, 0xf1, 0x01, 0x11, 0x11, 0x2, 0x3, 0x07,
+ 0x32, 0xa2, 0x77, 0xca, 0x51},
+ 16,
+ {"{2457,4369,61697} 1842 41591 51793",
+ "{2457,4369,61697} 1842 41591 51793", 4, 0, NOT_ALL_PRIVATE,
+ 51793, 1, 2457},
+ 0,
+ },
+ {
+ /* 20 */
+ "reconcile_confed",
+ "confseq(123,456,789) confset(456,124,788) seq(6435,59408,21665) set(23456,23456,23456), seq(23456,23456,23456)",
+ {0x3, 0x3, 0x00, 0x7b, 0x01, 0xc8, 0x03, 0x15, 0x4, 0x3,
+ 0x01, 0xc8, 0x00, 0x7c, 0x03, 0x14, 0x2, 0x3, 0x19, 0x23,
+ 0xe8, 0x10, 0x54, 0xa1, 0x1, 0x3, 0x5b, 0xa0, 0x5b, 0xa0,
+ 0x5b, 0xa0, 0x2, 0x3, 0x5b, 0xa0, 0x5b, 0xa0, 0x5b, 0xa0},
+ 40,
+ {"(123 456 789) [124,456,788] 6435 59408 21665 {23456} 23456 23456 23456",
+ "6435 59408 21665 {23456} 23456 23456 23456", 7, 4,
+ NOT_ALL_PRIVATE, 23456, 1, 6435},
+ 0,
+ },
+ {
+ /* 21 */
+ "reconcile_start_trans",
+ "seq(23456,23456,23456) seq(6435,59408,21665)",
+ {
+ 0x2,
+ 0x3,
+ 0x5b,
+ 0xa0,
+ 0x5b,
+ 0xa0,
+ 0x5b,
+ 0xa0,
+ 0x2,
+ 0x3,
+ 0x19,
+ 0x23,
+ 0xe8,
+ 0x10,
+ 0x54,
+ 0xa1,
+ },
+ 16,
+ {"23456 23456 23456 6435 59408 21665",
+ "23456 23456 23456 6435 59408 21665", 6, 0, NOT_ALL_PRIVATE,
+ 21665, 1, 23456},
+ 0,
+ },
+ {
+ /* 22 */
+ "reconcile_start_trans4",
+ "seq(1842,41591,51793) seq(6435,59408,21665)",
+ {
+ 0x2,
+ 0x3,
+ 0x07,
+ 0x32,
+ 0xa2,
+ 0x77,
+ 0xca,
+ 0x51,
+ 0x2,
+ 0x3,
+ 0x19,
+ 0x23,
+ 0xe8,
+ 0x10,
+ 0x54,
+ 0xa1,
+ },
+ 16,
+ {"1842 41591 51793 6435 59408 21665",
+ "1842 41591 51793 6435 59408 21665", 6, 0, NOT_ALL_PRIVATE,
+ 41591, 1, 1842},
+ 0,
+ },
+ {
+ /* 23 */
+ "reconcile_start_trans_error",
+ "seq(23456,23456,23456) seq(6435,59408)",
+ {
+ 0x2,
+ 0x3,
+ 0x5b,
+ 0xa0,
+ 0x5b,
+ 0xa0,
+ 0x5b,
+ 0xa0,
+ 0x2,
+ 0x2,
+ 0x19,
+ 0x23,
+ 0xe8,
+ 0x10,
+ },
+ 14,
+ {"23456 23456 23456 6435 59408", "23456 23456 23456 6435 59408",
+ 5, 0, NOT_ALL_PRIVATE, 59408, 1, 23456},
+ 0,
+ },
+ {
+ /* 24 */
+ "redundantset2",
+ "seq(8466,3,52737,4096,3456) set(7099,8153,8153,8153,7099)",
+ {
+ 0x2, 0x5, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01,
+ 0x10, 0x00, 0x0d, 0x80, 0x1, 0x5, 0x1b, 0xbb,
+ 0x1f, 0xd9, 0x1f, 0xd9, 0x1f, 0xd9, 0x1b, 0xbb,
+ },
+ 24,
+ {/* We should weed out duplicate set members. */
+ "8466 3 52737 4096 3456 {7099,8153}",
+ "8466 3 52737 4096 3456 {7099,8153}", 6, 0, NOT_ALL_PRIVATE,
+ 4096, 4, 8466},
+ 0,
+ },
+ {
+ /* 25 */
+ "zero-size overflow",
+ "#ASNs = 0, data = seq(8466 3 52737 4096 3456)",
+ {0x2, 0x0, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x0d,
+ 0x80},
+ 12,
+ {NULL, NULL, 0, 0, 0, 0, 0, 0},
+ 0,
+ },
+ {
+ /* 26 */
+ "zero-size overflow + valid segment",
+ "seq(#AS=0:8466 3 52737),seq(4096 3456)",
+ {0x2, 0x0, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x2, 0x2, 0x10,
+ 0x00, 0x0d, 0x80},
+ 14,
+ {NULL, NULL, 0, 0, 0, 0, 0, 0},
+ 0,
+ },
+ {
+ /* 27 */
+ "invalid segment type",
+ "type=8(4096 3456)",
+ {0x8, 0x2, 0x10, 0x00, 0x0d, 0x80},
+ 14,
+ {NULL, NULL, 0, 0, 0, 0, 0, 0},
+ 0,
+ },
+ {
+ /* 28 */
+ "BGP_AS_ZERO",
+ "seq(8466,3,52737,0,4096)",
+ {0x2, 0x5, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x00, 0x00, 0x10,
+ 0x00},
+ 12,
+ {"8466 3 52737 0 4096", "8466 3 52737 0 4096", 5, 0,
+ NOT_ALL_PRIVATE, 4096, 4, 8466},
+ 0,
+ },
+ {
+ /* 29 */
+ "seq3_asdot+",
+ "seq(0.8466,0.3,0.52737,0.4096,0.8722,0.4)",
+ {0x2, 0x6, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x10, 0x00, 0x22,
+ 0x12, 0x00, 0x04},
+ 14,
+ {"0.8466 0.3 0.52737 0.4096 0.8722 0.4",
+ "0.8466 0.3 0.52737 0.4096 0.8722 0.4", 6, 0, NOT_ALL_PRIVATE,
+ 3, 5, 8466},
+ ASNOTATION_DOTPLUS,
+ },
+ {
+ /* 30 */
+ "confmulti_asdot+",
+ "confseq(0.123,0.456,0.789) confset(0.222,0.111) seq(0.8722) set(0.4196,0.48658)",
+ {0x3, 0x3, 0x00, 0x7b, 0x01, 0xc8, 0x03, 0x15,
+ 0x4, 0x2, 0x00, 0xde, 0x00, 0x6f, 0x2, 0x1,
+ 0x22, 0x12, 0x1, 0x2, 0x10, 0x64, 0xbe, 0x12},
+ 24,
+ {"(0.123 0.456 0.789) [0.111,0.222] 0.8722 {0.4196,0.48658}",
+ "0.8722 {0.4196,0.48658}", 2, 4, NOT_ALL_PRIVATE, 123, 1,
+ NULL_ASN},
+ ASNOTATION_DOTPLUS,
+ },
+ {
+ /* 31 */
+ "someprivate asdot+",
+ "seq(0.8466,0.64512,0.52737,0.65535)",
+ {0x2, 0x4, 0x21, 0x12, 0xfc, 0x00, 0xce, 0x01, 0xff, 0xff},
+ 10,
+ {"0.8466 0.64512 0.52737 0.65535",
+ "0.8466 0.64512 0.52737 0.65535", 4, 0, NOT_ALL_PRIVATE, 65535,
+ 4, 8466},
+ ASNOTATION_DOTPLUS,
+ },
+ {
+ /* 32 */
+ "BGP_AS_ZERO asdot+",
+ "seq(0.8466,0.3,0.52737,0.0,0.4096)",
+ {0x2, 0x5, 0x21, 0x12, 0x00, 0x03, 0xce, 0x01, 0x00, 0x00, 0x10,
+ 0x00},
+ 12,
+ {"0.8466 0.3 0.52737 0.0 0.4096",
+ "0.8466 0.3 0.52737 0.0 0.4096", 5, 0, NOT_ALL_PRIVATE, 4096,
+ 4, 8466},
+ ASNOTATION_DOTPLUS,
+ },
+ {NULL, NULL, {0}, 0, {NULL, 0, 0}}};
+
+#define COMMON_ATTRS \
+ BGP_ATTR_FLAG_TRANS, BGP_ATTR_ORIGIN, 1, BGP_ORIGIN_EGP, \
+ BGP_ATTR_FLAG_TRANS, BGP_ATTR_NEXT_HOP, 4, 192, 0, 2, 0
+#define COMMON_ATTR_SIZE 11
+
+/* */
+static struct aspath_tests {
+ const char *desc;
+ const struct test_segment *segment;
+ const char *shouldbe; /* String it should evaluate to */
+ const enum as4 {
+ AS4_DATA,
+ AS2_DATA
+ } as4; /* whether data should be as4 or not (ie as2) */
+ const int result; /* expected result for bgp_attr_parse */
+ const int cap; /* capabilities to set for peer */
+ const char attrheader[1024];
+ size_t len;
+ const struct test_segment *old_segment;
+} aspath_tests[] = {
+ /* 0 */
+ {
+ "basic test",
+ &test_segments[0],
+ "8466 3 52737 4096",
+ AS2_DATA,
+ 0,
+ 0,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS,
+ BGP_ATTR_AS_PATH,
+ 10,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ /* 1 */
+ {
+ "length too short",
+ &test_segments[0],
+ "8466 3 52737 4096",
+ AS2_DATA,
+ -1,
+ 0,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS,
+ BGP_ATTR_AS_PATH,
+ 8,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ /* 2 */
+ {
+ "length too long",
+ &test_segments[0],
+ "8466 3 52737 4096",
+ AS2_DATA,
+ -1,
+ 0,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS,
+ BGP_ATTR_AS_PATH,
+ 12,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ /* 3 */
+ {
+ "incorrect flag",
+ &test_segments[0],
+ "8466 3 52737 4096",
+ AS2_DATA,
+ -1,
+ 0,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
+ BGP_ATTR_AS_PATH,
+ 10,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ /* 4 */
+ {
+ "as4_path, with as2 format data",
+ &test_segments[0],
+ "8466 3 52737 4096",
+ AS2_DATA,
+ -1,
+ 0,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
+ BGP_ATTR_AS4_PATH,
+ 10,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ /* 5 */
+ {
+ "as4, with incorrect attr length",
+ &test_segments[0],
+ "8466 3 52737 4096",
+ AS4_DATA,
+ -1,
+ PEER_CAP_AS4_RCV,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
+ BGP_ATTR_AS4_PATH,
+ 10,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ /* 6 */
+ {
+ "basic 4-byte as-path",
+ &test_segments[0],
+ "8466 3 52737 4096",
+ AS4_DATA,
+ 0,
+ PEER_CAP_AS4_RCV | PEER_CAP_AS4_ADV,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS,
+ BGP_ATTR_AS_PATH,
+ 18,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ /* 7 */
+ {
+ "4b AS_PATH: too short",
+ &test_segments[0],
+ "8466 3 52737 4096",
+ AS4_DATA,
+ -1,
+ PEER_CAP_AS4_RCV | PEER_CAP_AS4_ADV,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS,
+ BGP_ATTR_AS_PATH,
+ 16,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ /* 8 */
+ {
+ "4b AS_PATH: too long",
+ &test_segments[0],
+ "8466 3 52737 4096",
+ AS4_DATA,
+ -1,
+ PEER_CAP_AS4_RCV | PEER_CAP_AS4_ADV,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS,
+ BGP_ATTR_AS_PATH,
+ 20,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ /* 9 */
+ {
+ "4b AS_PATH: too long2",
+ &test_segments[0],
+ "8466 3 52737 4096",
+ AS4_DATA,
+ -1,
+ PEER_CAP_AS4_RCV | PEER_CAP_AS4_ADV,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS,
+ BGP_ATTR_AS_PATH,
+ 22,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ /* 10 */
+ {
+ "4b AS_PATH: bad flags",
+ &test_segments[0],
+ "8466 3 52737 4096",
+ AS4_DATA,
+ -1,
+ PEER_CAP_AS4_RCV | PEER_CAP_AS4_ADV,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
+ BGP_ATTR_AS_PATH,
+ 18,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ /* 11 */
+ {
+ "4b AS4_PATH w/o AS_PATH",
+ &test_segments[6],
+ NULL,
+ AS4_DATA,
+ -2,
+ PEER_CAP_AS4_ADV,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
+ BGP_ATTR_AS4_PATH,
+ 14,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ /* 12 */
+ {
+ "4b AS4_PATH: confed",
+ &test_segments[6],
+ "8466 3 52737 4096 (123 456 789)",
+ AS4_DATA,
+ 0,
+ PEER_CAP_AS4_ADV,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
+ BGP_ATTR_AS4_PATH,
+ 14,
+ },
+ COMMON_ATTR_SIZE + 3,
+ &test_segments[0],
+ },
+ /* 13 */
+ {
+ "4b AS4_PATH: BGP_AS_ZERO",
+ &test_segments[28],
+ "8466 3 52737 0 4096",
+ AS4_DATA,
+ -2,
+ PEER_CAP_AS4_RCV | PEER_CAP_AS4_ADV,
+ {
+ COMMON_ATTRS,
+ BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
+ BGP_ATTR_AS4_PATH,
+ 22,
+ },
+ COMMON_ATTR_SIZE + 3,
+ },
+ {NULL, NULL, NULL, 0, 0, 0, {0}, 0},
+};
+
+/* prepending tests */
+static struct tests {
+ const struct test_segment *test1;
+ const struct test_segment *test2;
+ struct test_spec sp;
+} prepend_tests[] = {
+ /* 0 */
+ {
+ &test_segments[0],
+ &test_segments[1],
+ {"8466 3 52737 4096 8722 4", "8466 3 52737 4096 8722 4", 6, 0,
+ NOT_ALL_PRIVATE, 4096, 1, 8466},
+ },
+ /* 1 */
+ {&test_segments[1],
+ &test_segments[3],
+ {"8722 4 8482 51457 {5204}", "8722 4 8482 51457 {5204}", 5, 0,
+ NOT_ALL_PRIVATE, 5204, 1, 8722}},
+ /* 2 */
+ {
+ &test_segments[3],
+ &test_segments[4],
+ {"8482 51457 {5204} 8467 59649 {4196,48658} {17322,30745}",
+ "8482 51457 {5204} 8467 59649 {4196,48658} {17322,30745}", 7,
+ 0, NOT_ALL_PRIVATE, 5204, 1, 8482},
+ },
+ /* 3 */
+ {&test_segments[4],
+ &test_segments[5],
+ {"8467 59649 {4196,48658} {17322,30745} 6435 59408 21665 {2457,4369,61697} 1842 41590 51793",
+ "8467 59649 {4196,48658} {17322,30745} 6435 59408 21665 {2457,4369,61697} 1842 41590 51793",
+ 11, 0, NOT_ALL_PRIVATE, 61697, 1, 8467}},
+ /* 4 */
+ {
+ &test_segments[5],
+ &test_segments[6],
+ {"6435 59408 21665 {2457,4369,61697} 1842 41590 51793",
+ "6435 59408 21665 {2457,4369,61697} 1842 41590 51793", 7, 0,
+ NOT_ALL_PRIVATE, 1842, 1, 6435},
+ },
+ /* 5 */
+ {&test_segments[6],
+ &test_segments[7],
+ {"(123 456 789) (123 456 789) (111 222)", "", 0, 8, NOT_ALL_PRIVATE,
+ 111, 1, 0}},
+ {&test_segments[7],
+ &test_segments[8],
+ {"(123 456 789) (111 222) [123,456,789]", "", 0, 6, NOT_ALL_PRIVATE,
+ 111, 1, 0}},
+ {
+ &test_segments[8],
+ &test_segments[9],
+ {"[123,456,789] (123 456 789) [111,222] 8722 {4196,48658}",
+ "8722 {4196,48658}", 2, 5, NOT_ALL_PRIVATE, 456, 1, NULL_ASN},
+ },
+ {
+ &test_segments[9],
+ &test_segments[8],
+ {"(123 456 789) [111,222] 8722 {4196,48658} [123,456,789]",
+ "8722 {4196,48658}", 2, 5, NOT_ALL_PRIVATE, 48658, 1,
+ NULL_ASN},
+ },
+ {
+ &test_segments[14],
+ &test_segments[11],
+ {"8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 2 52737 4096 8722 4 8722",
+
+ "8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 3 52737 4096 34285 8466 2 52737 4096 8722 4 8722",
+ 257, 0, NOT_ALL_PRIVATE, 4096, 1000, 8466},
+ },
+ {NULL,
+ NULL,
+ {
+ NULL, 0, 0, 0, 0, 0, 0,
+ }},
+};
+
+struct tests reconcile_tests[] = {
+ {
+ &test_segments[18],
+ &test_segments[19],
+ {"6435 59408 21665 {2457,4369,61697} 1842 41591 51793",
+ "6435 59408 21665 {2457,4369,61697} 1842 41591 51793", 7, 0,
+ NOT_ALL_PRIVATE, 51793, 1, 6435},
+ },
+ {
+ &test_segments[19],
+ &test_segments[18],
+ /* AS_PATH (19) has more hops than NEW_AS_PATH,
+ * so just AS_PATH should be used (though, this practice
+ * is bad imho).
+ */
+ {"{2457,4369,61697} 1842 41591 51793 6435 59408 21665 {23456} 23456 23456 23456",
+ "{2457,4369,61697} 1842 41591 51793 6435 59408 21665 {23456} 23456 23456 23456",
+ 11, 0, NOT_ALL_PRIVATE, 51793, 1, 6435},
+ },
+ {
+ &test_segments[20],
+ &test_segments[19],
+ {"(123 456 789) [124,456,788] 6435 59408 21665 {2457,4369,61697} 1842 41591 51793",
+ "6435 59408 21665 {2457,4369,61697} 1842 41591 51793", 7, 4,
+ NOT_ALL_PRIVATE, 51793, 1, 6435},
+ },
+ {
+ &test_segments[21],
+ &test_segments[22],
+ {"1842 41591 51793 6435 59408 21665",
+ "1842 41591 51793 6435 59408 21665", 6, 0, NOT_ALL_PRIVATE,
+ 51793, 1, 1842},
+ },
+ {
+ &test_segments[23],
+ &test_segments[22],
+ {"23456 23456 23456 6435 59408 1842 41591 51793 6435 59408 21665",
+ "23456 23456 23456 6435 59408 1842 41591 51793 6435 59408 21665",
+ 11, 0, NOT_ALL_PRIVATE, 51793, 1, 1842},
+ },
+ {NULL,
+ NULL,
+ {
+ NULL, 0, 0, 0, 0, 0, 0,
+ }},
+};
+
+struct tests aggregate_tests[] = {
+ {
+ &test_segments[0],
+ &test_segments[2],
+ {"8466 3 52737 4096 {4,8722}", "8466 3 52737 4096 {4,8722}", 5,
+ 0, NOT_ALL_PRIVATE, 4, 1, 8466},
+ },
+ {
+ &test_segments[2],
+ &test_segments[0],
+ {"8466 3 52737 4096 {4,8722}", "8466 3 52737 4096 {4,8722}", 5,
+ 0, NOT_ALL_PRIVATE, 8722, 1, 8466},
+ },
+ {
+ &test_segments[2],
+ &test_segments[10],
+ {"8466 {2,3,4,4096,8722,52737}", "8466 {2,3,4,4096,8722,52737}",
+ 2, 0, NOT_ALL_PRIVATE, 8722, 5, 8466},
+ },
+ {
+ &test_segments[10],
+ &test_segments[2],
+ {"8466 {2,3,4,4096,8722,52737}", "8466 {2,3,4,4096,8722,52737}",
+ 2, 0, NOT_ALL_PRIVATE, 2, 20000, 8466},
+ },
+
+ {
+ &test_segments[5],
+ &test_segments[18],
+ {"6435 59408 21665 {1842,2457,4369,23456,41590,51793,61697}",
+ "6435 59408 21665 {1842,2457,4369,23456,41590,51793,61697}", 4,
+ 0, NOT_ALL_PRIVATE, 41590, 1, 6435},
+ },
+
+ {NULL, NULL, {NULL, 0, 0}},
+};
+
+struct compare_tests {
+ int test_index1;
+ int test_index2;
+#define CMP_RES_YES 1
+#define CMP_RES_NO 0
+ char shouldbe_cmp;
+ char shouldbe_confed;
+} left_compare[] = {
+ {0, 1, CMP_RES_NO, CMP_RES_NO}, {0, 2, CMP_RES_YES, CMP_RES_NO},
+ {0, 11, CMP_RES_YES, CMP_RES_NO}, {0, 15, CMP_RES_YES, CMP_RES_NO},
+ {0, 16, CMP_RES_NO, CMP_RES_NO}, {1, 11, CMP_RES_NO, CMP_RES_NO},
+ {6, 7, CMP_RES_NO, CMP_RES_YES}, {6, 8, CMP_RES_NO, CMP_RES_NO},
+ {7, 8, CMP_RES_NO, CMP_RES_NO}, {1, 9, CMP_RES_YES, CMP_RES_NO},
+ {0, 9, CMP_RES_NO, CMP_RES_NO}, {3, 9, CMP_RES_NO, CMP_RES_NO},
+ {0, 6, CMP_RES_NO, CMP_RES_NO}, {1, 6, CMP_RES_NO, CMP_RES_NO},
+ {0, 8, CMP_RES_NO, CMP_RES_NO}, {1, 8, CMP_RES_NO, CMP_RES_NO},
+ {11, 6, CMP_RES_NO, CMP_RES_NO}, {11, 7, CMP_RES_NO, CMP_RES_NO},
+ {11, 8, CMP_RES_NO, CMP_RES_NO}, {9, 6, CMP_RES_NO, CMP_RES_YES},
+ {9, 7, CMP_RES_NO, CMP_RES_YES}, {9, 8, CMP_RES_NO, CMP_RES_NO},
+};
+
+/* make an aspath from a data stream */
+static struct aspath *make_aspath(const uint8_t *data, size_t len, int use32bit,
+ enum asnotation_mode asnotation)
+{
+ struct stream *s = NULL;
+ struct aspath *as;
+ if (len) {
+ s = stream_new(len);
+ stream_put(s, data, len);
+ }
+ as = aspath_parse(s, len, use32bit, asnotation);
+
+ if (s)
+ stream_free(s);
+
+ return as;
+}
+
+static void printbytes(const uint8_t *bytes, int len)
+{
+ int i = 0;
+ while (i < len) {
+ if (i % 2)
+ printf("%02hhx%s", bytes[i], " ");
+ else
+ printf("0x%02hhx", bytes[i]);
+ i++;
+ }
+ printf("\n");
+}
+
+/* validate the given aspath */
+static int validate(struct aspath *as, const struct test_spec *sp)
+{
+ size_t bytes, bytes4;
+ int fails = 0;
+ const uint8_t *out;
+ static struct stream *s;
+ struct aspath *asinout, *asconfeddel, *asstr, *as4;
+
+ if (as == NULL && sp->shouldbe == NULL) {
+ printf("Correctly failed to parse\n");
+ return fails;
+ }
+
+ out = aspath_snmp_pathseg(as, &bytes);
+ asinout = make_aspath(out, bytes, 0, as->asnotation);
+ /* Excercise AS4 parsing a bit, with a dogfood test */
+ if (!s)
+ s = stream_new(BGP_MAX_PACKET_SIZE);
+ bytes4 = aspath_put(s, as, 1);
+ as4 = make_aspath(STREAM_DATA(s), bytes4, 1, as->asnotation);
+
+ asn_relax_as_zero(true);
+ asstr = aspath_str2aspath(sp->shouldbe, as->asnotation);
+ asn_relax_as_zero(false);
+
+ asconfeddel = aspath_delete_confed_seq(aspath_dup(asinout));
+
+ printf("got: %s\n", aspath_print(as));
+
+ /* the parsed path should match the specified 'shouldbe' string.
+ * We should pass the "eat our own dog food" test, be able to output
+ * this path and then input it again. Ie the path resulting from:
+ *
+ * aspath_parse(aspath_put(as))
+ *
+ * should:
+ *
+ * - also match the specified 'shouldbe' value
+ * - hash to same value as original path
+ * - have same hops and confed counts as original, and as the
+ * the specified counts
+ *
+ * aspath_str2aspath() and shouldbe should match
+ *
+ * We do the same for:
+ *
+ * aspath_parse(aspath_put(as,USE32BIT))
+ *
+ * Confederation related tests:
+ * - aspath_delete_confed_seq(aspath) should match shouldbe_confed
+ * - aspath_delete_confed_seq should be idempotent.
+ */
+ if (strcmp(aspath_print(as), sp->shouldbe)
+ /* hash validation */
+ || (aspath_key_make(as) != aspath_key_make(asinout))
+ /* by string */
+ || strcmp(aspath_print(asinout), sp->shouldbe)
+ /* By 4-byte parsing */
+ || strcmp(aspath_print(as4), sp->shouldbe)
+ /* by various path counts */
+ || (aspath_count_hops(as) != sp->hops)
+ || (aspath_count_confeds(as) != sp->confeds)
+ || (aspath_count_hops(asinout) != sp->hops)
+ || (aspath_count_confeds(asinout) != sp->confeds)) {
+ failed++;
+ fails++;
+ printf("shouldbe:\n%s\n", sp->shouldbe);
+ printf("as4:\n%s\n", aspath_print(as4));
+ printf("hash keys: in: %d out->in: %d\n", aspath_key_make(as),
+ aspath_key_make(asinout));
+ printf("hops: %d, counted %d %d\n", sp->hops,
+ aspath_count_hops(as), aspath_count_hops(asinout));
+ printf("confeds: %d, counted %d %d\n", sp->confeds,
+ aspath_count_confeds(as), aspath_count_confeds(asinout));
+ printf("out->in:\n%s\nbytes: ", aspath_print(asinout));
+ printbytes(out, bytes);
+ }
+ /* basic confed related tests */
+ if ((aspath_print(asconfeddel) == NULL
+ && sp->shouldbe_delete_confed != NULL)
+ || (aspath_print(asconfeddel) != NULL
+ && sp->shouldbe_delete_confed == NULL)
+ || strcmp(aspath_print(asconfeddel), sp->shouldbe_delete_confed)
+ /* delete_confed_seq should be idempotent */
+ || (aspath_key_make(asconfeddel)
+ != aspath_key_make(aspath_delete_confed_seq(asconfeddel)))) {
+ failed++;
+ fails++;
+ printf("as-path minus confeds is: %s\n",
+ aspath_print(asconfeddel));
+ printf("as-path minus confeds should be: %s\n",
+ sp->shouldbe_delete_confed);
+ }
+ /* aspath_str2aspath test */
+ if ((aspath_print(asstr) == NULL && sp->shouldbe != NULL)
+ || (aspath_print(asstr) != NULL && sp->shouldbe == NULL)
+ || strcmp(aspath_print(asstr), sp->shouldbe)) {
+ failed++;
+ fails++;
+ printf("asstr: %s\n", aspath_print(asstr));
+ }
+
+ /* loop, private and first as checks */
+ if ((sp->does_loop && aspath_loop_check(as, sp->does_loop) == 0)
+ || (sp->doesnt_loop && aspath_loop_check(as, sp->doesnt_loop) != 0)
+ || (aspath_private_as_check(as) != sp->private_as)
+ || (aspath_firstas_check(as, sp->first) && sp->first == 0)) {
+ failed++;
+ fails++;
+ printf("firstas: %d, got %d\n", sp->first,
+ aspath_firstas_check(as, sp->first));
+ printf("loop does: %d %d, doesn't: %d %d\n", sp->does_loop,
+ aspath_loop_check(as, sp->does_loop), sp->doesnt_loop,
+ aspath_loop_check(as, sp->doesnt_loop));
+ printf("private check: %d %d\n", sp->private_as,
+ aspath_private_as_check(as));
+ }
+ aspath_unintern(&asinout);
+ aspath_unintern(&as4);
+
+ aspath_free(asconfeddel);
+ aspath_free(asstr);
+ stream_reset(s);
+
+ return fails;
+}
+
+static void empty_get_test(void)
+{
+ struct aspath *as = aspath_empty_get();
+ struct test_spec sp = {"", "", 0, 0, 0, 0, 0, 0};
+
+ printf("empty_get_test, as: %s\n", aspath_print(as));
+ if (!validate(as, &sp))
+ printf("%s\n", OK);
+ else
+ printf("%s!\n", FAILED);
+
+ printf("\n");
+
+ aspath_free(as);
+}
+
+/* basic parsing test */
+static void parse_test(struct test_segment *t)
+{
+ struct aspath *asp;
+
+ printf("%s: %s\n", t->name, t->desc);
+
+ asp = make_aspath(t->asdata, t->len, 0, t->asnotation);
+
+ printf("aspath: %s\nvalidating...:\n", aspath_print(asp));
+
+ if (!validate(asp, &t->sp))
+ printf(OK "\n");
+ else
+ printf(FAILED "\n");
+
+ printf("\n");
+
+ aspath_unintern(&asp);
+}
+
+/* prepend testing */
+static void prepend_test(struct tests *t)
+{
+ struct aspath *asp1, *asp2, *ascratch;
+
+ printf("prepend %s: %s\n", t->test1->name, t->test1->desc);
+ printf("to %s: %s\n", t->test2->name, t->test2->desc);
+
+ asp1 = make_aspath(t->test1->asdata, t->test1->len, 0,
+ ASNOTATION_PLAIN);
+ asp2 = make_aspath(t->test2->asdata, t->test2->len, 0,
+ ASNOTATION_PLAIN);
+
+ ascratch = aspath_dup(asp2);
+ aspath_unintern(&asp2);
+
+ asp2 = aspath_prepend(asp1, ascratch);
+
+ printf("aspath: %s\n", aspath_print(asp2));
+
+ if (!validate(asp2, &t->sp))
+ printf("%s\n", OK);
+ else
+ printf("%s!\n", FAILED);
+
+ printf("\n");
+ aspath_unintern(&asp1);
+ aspath_free(asp2);
+}
+
+/* empty-prepend testing */
+static void empty_prepend_test(struct test_segment *t)
+{
+ struct aspath *asp1, *asp2, *ascratch;
+
+ printf("empty prepend %s: %s\n", t->name, t->desc);
+
+ asp1 = make_aspath(t->asdata, t->len, 0, t->asnotation);
+ asp2 = aspath_empty(t->asnotation);
+
+ ascratch = aspath_dup(asp2);
+ aspath_unintern(&asp2);
+
+ asp2 = aspath_prepend(asp1, ascratch);
+
+ printf("aspath: %s\n", aspath_print(asp2));
+
+ if (!validate(asp2, &t->sp))
+ printf(OK "\n");
+ else
+ printf(FAILED "!\n");
+
+ printf("\n");
+ aspath_unintern(&asp1);
+ aspath_free(asp2);
+}
+
+/* as2+as4 reconciliation testing */
+static void as4_reconcile_test(struct tests *t)
+{
+ struct aspath *asp1, *asp2, *ascratch;
+
+ printf("reconciling %s:\n %s\n", t->test1->name, t->test1->desc);
+ printf("with %s:\n %s\n", t->test2->name, t->test2->desc);
+
+ asp1 = make_aspath(t->test1->asdata, t->test1->len, 0,
+ ASNOTATION_PLAIN);
+ asp2 = make_aspath(t->test2->asdata, t->test2->len, 0,
+ ASNOTATION_PLAIN);
+
+ ascratch = aspath_reconcile_as4(asp1, asp2);
+
+ if (!validate(ascratch, &t->sp))
+ printf(OK "\n");
+ else
+ printf(FAILED "!\n");
+
+ printf("\n");
+ aspath_unintern(&asp1);
+ aspath_unintern(&asp2);
+ aspath_free(ascratch);
+}
+
+
+/* aggregation testing */
+static void aggregate_test(struct tests *t)
+{
+ struct aspath *asp1, *asp2, *ascratch;
+
+ printf("aggregate %s: %s\n", t->test1->name, t->test1->desc);
+ printf("with %s: %s\n", t->test2->name, t->test2->desc);
+
+ asp1 = make_aspath(t->test1->asdata, t->test1->len, 0,
+ ASNOTATION_PLAIN);
+ asp2 = make_aspath(t->test2->asdata, t->test2->len, 0,
+ ASNOTATION_PLAIN);
+
+ ascratch = aspath_aggregate(asp1, asp2);
+
+ if (!validate(ascratch, &t->sp))
+ printf(OK "\n");
+ else
+ printf(FAILED "!\n");
+
+ printf("\n");
+ aspath_unintern(&asp1);
+ aspath_unintern(&asp2);
+ aspath_free(ascratch);
+ /* aspath_unintern (ascratch);*/
+}
+
+/* cmp_left tests */
+static void cmp_test(void)
+{
+ unsigned int i;
+#define CMP_TESTS_MAX (sizeof(left_compare) / sizeof(struct compare_tests))
+
+ for (i = 0; i < CMP_TESTS_MAX; i++) {
+ struct test_segment *t1 =
+ &test_segments[left_compare[i].test_index1];
+ struct test_segment *t2 =
+ &test_segments[left_compare[i].test_index2];
+ struct aspath *asp1, *asp2;
+
+ printf("left cmp %s: %s\n", t1->name, t1->desc);
+ printf("and %s: %s\n", t2->name, t2->desc);
+
+ asp1 = make_aspath(t1->asdata, t1->len, 0, ASNOTATION_PLAIN);
+ asp2 = make_aspath(t2->asdata, t2->len, 0, ASNOTATION_PLAIN);
+
+ if (aspath_cmp_left(asp1, asp2) != left_compare[i].shouldbe_cmp
+ || aspath_cmp_left(asp2, asp1)
+ != left_compare[i].shouldbe_cmp
+ || aspath_cmp_left_confed(asp1, asp2)
+ != left_compare[i].shouldbe_confed
+ || aspath_cmp_left_confed(asp2, asp1)
+ != left_compare[i].shouldbe_confed) {
+ failed++;
+ printf(FAILED "\n");
+ printf("result should be: cmp: %d, confed: %d\n",
+ left_compare[i].shouldbe_cmp,
+ left_compare[i].shouldbe_confed);
+ printf("got: cmp %d, cmp_confed: %d\n",
+ aspath_cmp_left(asp1, asp2),
+ aspath_cmp_left_confed(asp1, asp2));
+ printf("path1: %s\npath2: %s\n", aspath_print(asp1),
+ aspath_print(asp2));
+ } else
+ printf(OK "\n");
+
+ printf("\n");
+ aspath_unintern(&asp1);
+ aspath_unintern(&asp2);
+ }
+}
+
+static int handle_attr_test(struct aspath_tests *t)
+{
+ struct bgp bgp = {0};
+ struct peer peer = {0};
+ struct attr attr = {0};
+ int ret;
+ int initfail = failed;
+ struct aspath *asp;
+ size_t datalen;
+
+ asp = make_aspath(t->segment->asdata, t->segment->len, 0,
+ t->segment->asnotation);
+ bgp.asnotation = t->segment->asnotation;
+
+ peer.curr = stream_new(BGP_MAX_PACKET_SIZE);
+ peer.connection = bgp_peer_connection_new(&peer);
+ peer.connection->obuf = stream_fifo_new();
+ peer.bgp = &bgp;
+ peer.host = (char *)"none";
+ peer.connection->fd = -1;
+ peer.cap = t->cap;
+ peer.max_packet_size = BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE;
+
+ stream_write(peer.curr, t->attrheader, t->len);
+ datalen = aspath_put(peer.curr, asp, t->as4 == AS4_DATA);
+ if (t->old_segment) {
+ char dummyaspath[] = {BGP_ATTR_FLAG_TRANS, BGP_ATTR_AS_PATH,
+ t->old_segment->len};
+ stream_write(peer.curr, dummyaspath, sizeof(dummyaspath));
+ stream_write(peer.curr, t->old_segment->asdata,
+ t->old_segment->len);
+ datalen += sizeof(dummyaspath) + t->old_segment->len;
+ }
+
+ ret = bgp_attr_parse(&peer, &attr, t->len + datalen, NULL, NULL);
+
+ if (ret != t->result) {
+ printf("bgp_attr_parse returned %d, expected %d\n", ret,
+ t->result);
+ printf("datalen %zd\n", datalen);
+ failed++;
+ }
+ if (ret != 0)
+ goto out;
+
+ if (t->shouldbe && attr.aspath == NULL) {
+ printf("aspath is NULL, but should be: %s\n", t->shouldbe);
+ failed++;
+ }
+ if (t->shouldbe && attr.aspath
+ && strcmp(attr.aspath->str, t->shouldbe)) {
+ printf("attr str and 'shouldbe' mismatched!\n"
+ "attr str: %s\n"
+ "shouldbe: %s\n",
+ attr.aspath->str, t->shouldbe);
+ failed++;
+ }
+ if (!t->shouldbe && attr.aspath) {
+ printf("aspath should be NULL, but is: %s\n", attr.aspath->str);
+ failed++;
+ }
+
+out:
+ aspath_unintern(&attr.aspath);
+ aspath_unintern(&asp);
+ return failed - initfail;
+}
+
+static void attr_test(struct aspath_tests *t)
+{
+ printf("%s\n", t->desc);
+ printf("%s\n\n", handle_attr_test(t) ? FAILED : OK);
+}
+
+int main(void)
+{
+ int i = 0;
+ qobj_init();
+ bgp_master_init(event_master_create(NULL), BGP_SOCKET_SNDBUF_SIZE,
+ list_new());
+ master = bm->master;
+ bgp_option_set(BGP_OPT_NO_LISTEN);
+ bgp_attr_init();
+
+ while (test_segments[i].name) {
+ printf("test %u\n", i);
+ parse_test(&test_segments[i]);
+ empty_prepend_test(&test_segments[i++]);
+ }
+ i = 0;
+
+ while (prepend_tests[i].test1) {
+ printf("prepend test %u\n", i);
+ prepend_test(&prepend_tests[i++]);
+ }
+
+ i = 0;
+ while (aggregate_tests[i].test1) {
+ printf("aggregate test %u\n", i);
+ aggregate_test(&aggregate_tests[i++]);
+ }
+
+ i = 0;
+
+ while (reconcile_tests[i].test1) {
+ printf("reconcile test %u\n", i);
+ as4_reconcile_test(&reconcile_tests[i++]);
+ }
+
+ i = 0;
+
+ cmp_test();
+
+ i = 0;
+
+ empty_get_test();
+
+ i = 0;
+
+ frr_pthread_init();
+ bgp_pthreads_init();
+ bgp_pth_ka->running = true;
+
+ while (aspath_tests[i].desc) {
+ printf("aspath_attr test %d\n", i);
+ attr_test(&aspath_tests[i++]);
+ }
+
+ printf("failures: %d\n", failed);
+ printf("aspath count: %ld\n", aspath_count());
+
+ return (failed + aspath_count());
+}
diff --git a/tests/bgpd/test_aspath.py b/tests/bgpd/test_aspath.py
new file mode 100644
index 0000000..88579ad
--- /dev/null
+++ b/tests/bgpd/test_aspath.py
@@ -0,0 +1,84 @@
+import frrtest
+import re
+
+re_okfail = re.compile(
+ r"^(?:\x1b\[3[12]m)?(?P<ret>OK|failed)".encode("utf8"), re.MULTILINE
+)
+
+
+class TestAspath(frrtest.TestMultiOut):
+ program = "./test_aspath"
+
+ def _parsertest(self, line):
+ if not hasattr(self, "parserno"):
+ self.parserno = -1
+ self.parserno += 1
+
+ self._onesimple("test %d" % self.parserno)
+ self._okfail("%s:" % line, okfail=re_okfail)
+ self._okfail("empty prepend %s:" % line, okfail=re_okfail)
+
+ def _attrtest(self, line):
+ if not hasattr(self, "attrno"):
+ self.attrno = -1
+ self.attrno += 1
+
+ self._onesimple("aspath_attr test %d" % self.attrno)
+ self._okfail(line, okfail=re_okfail)
+
+
+TestAspath.parsertest("seq1")
+TestAspath.parsertest("seq2")
+TestAspath.parsertest("seq3")
+TestAspath.parsertest("seqset")
+TestAspath.parsertest("seqset2")
+TestAspath.parsertest("multi")
+TestAspath.parsertest("confed")
+TestAspath.parsertest("confed2")
+TestAspath.parsertest("confset")
+TestAspath.parsertest("confmulti")
+TestAspath.parsertest("seq4")
+TestAspath.parsertest("tripleseq1")
+TestAspath.parsertest("someprivate")
+TestAspath.parsertest("allprivate")
+TestAspath.parsertest("long")
+TestAspath.parsertest("seq1extra")
+TestAspath.parsertest("empty")
+TestAspath.parsertest("redundantset")
+TestAspath.parsertest("reconcile_lead_asp")
+TestAspath.parsertest("reconcile_new_asp")
+TestAspath.parsertest("reconcile_confed")
+TestAspath.parsertest("reconcile_start_trans")
+TestAspath.parsertest("reconcile_start_trans4")
+TestAspath.parsertest("reconcile_start_trans_error")
+TestAspath.parsertest("redundantset2")
+TestAspath.parsertest("zero-size overflow")
+TestAspath.parsertest("zero-size overflow + valid segment")
+TestAspath.parsertest("invalid segment type")
+TestAspath.parsertest("BGP_AS_ZERO")
+
+for i in range(10):
+ TestAspath.okfail("prepend test %d" % i)
+for i in range(5):
+ TestAspath.okfail("aggregate test %d" % i)
+for i in range(5):
+ TestAspath.okfail("reconcile test %d" % i)
+for _ in range(22):
+ TestAspath.okfail("left cmp ")
+
+TestAspath.okfail("empty_get_test")
+
+TestAspath.attrtest("basic test")
+TestAspath.attrtest("length too short")
+TestAspath.attrtest("length too long")
+TestAspath.attrtest("incorrect flag")
+TestAspath.attrtest("as4_path, with as2 format data")
+TestAspath.attrtest("as4, with incorrect attr length")
+TestAspath.attrtest("basic 4-byte as-path")
+TestAspath.attrtest("4b AS_PATH: too short")
+TestAspath.attrtest("4b AS_PATH: too long")
+TestAspath.attrtest("4b AS_PATH: too long2")
+TestAspath.attrtest("4b AS_PATH: bad flags")
+TestAspath.attrtest("4b AS4_PATH w/o AS_PATH")
+TestAspath.attrtest("4b AS4_PATH: confed")
+TestAspath.attrtest("4b AS4_PATH: BGP_AS_ZERO")
diff --git a/tests/bgpd/test_bgp_table.c b/tests/bgpd/test_bgp_table.c
new file mode 100644
index 0000000..bed86f5
--- /dev/null
+++ b/tests/bgpd/test_bgp_table.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * BGP Routing table range lookup test
+ * Copyright (C) 2012 OSR.
+ * Copyright (C) 2018 Marcel Röthke (marcel.roethke@haw-hamburg.de), for HAW
+ * Hamburg
+ *
+ * This file is part of FRRouting
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "table.h"
+#include "bgpd/bgp_table.h"
+#include "linklist.h"
+
+/* Satisfy link requirements from including bgpd.h */
+struct zebra_privs_t bgpd_privs = {0};
+/*
+ * test_node_t
+ *
+ * Information that is kept for each node in the radix tree.
+ */
+struct test_node_t {
+
+ /*
+ * Human readable representation of the string. Allocated using
+ * malloc()/dup().
+ */
+ char *prefix_str;
+};
+
+/*
+ * add_node
+ *
+ * Add the given prefix (passed in as a string) to the given table.
+ */
+static void add_node(struct bgp_table *table, const char *prefix_str)
+{
+ struct prefix_ipv4 p;
+ struct test_node_t *node;
+ struct bgp_dest *dest;
+
+ assert(prefix_str);
+
+ if (str2prefix_ipv4(prefix_str, &p) <= 0)
+ assert(0);
+
+ dest = bgp_node_get(table, (struct prefix *)&p);
+ if (dest->info) {
+ assert(0);
+ return;
+ }
+
+ node = malloc(sizeof(struct test_node_t));
+ assert(node);
+ node->prefix_str = strdup(prefix_str);
+ assert(node->prefix_str);
+ dest->info = node;
+}
+
+static bool prefix_in_array(const struct prefix *p, struct prefix *prefix_array,
+ size_t prefix_array_size)
+{
+ for (size_t i = 0; i < prefix_array_size; ++i) {
+ if (prefix_same(p, &prefix_array[i]))
+ return true;
+ }
+ return false;
+}
+
+static void check_lookup_result(struct bgp_dest *match, va_list arglist)
+{
+ char *prefix_str;
+ struct prefix *prefixes = NULL;
+ size_t prefix_count = 0;
+
+ while ((prefix_str = va_arg(arglist, char *))) {
+ ++prefix_count;
+ prefixes = realloc(prefixes, sizeof(*prefixes) * prefix_count);
+
+ if (str2prefix(prefix_str, &prefixes[prefix_count - 1]) <= 0)
+ assert(0);
+ }
+
+ /* check if the result is empty and if it is allowd to be empty */
+ assert((prefix_count == 0 && !match) || prefix_count > 0);
+ if (!match)
+ return;
+
+ struct bgp_dest *dest = match;
+
+ while ((dest = bgp_route_next_until(dest, match))) {
+ const struct prefix *dest_p = bgp_dest_get_prefix(dest);
+
+ if (bgp_dest_has_bgp_path_info_data(dest)
+ && !prefix_in_array(dest_p, prefixes, prefix_count)) {
+ printf("prefix %pFX was not expected!\n", dest_p);
+ assert(0);
+ }
+ }
+}
+
+static void do_test(struct bgp_table *table, const char *prefix, ...)
+{
+ va_list arglist;
+ struct prefix p;
+
+
+ va_start(arglist, prefix);
+ printf("\nDoing lookup for %s\n", prefix);
+ if (str2prefix(prefix, &p) <= 0)
+ assert(0);
+ struct bgp_dest *dest = bgp_table_subtree_lookup(table, &p);
+
+ check_lookup_result(dest, arglist);
+
+ va_end(arglist);
+
+ printf("Checks successfull\n");
+}
+
+/*
+ * test_range_lookup
+ */
+static void test_range_lookup(void)
+{
+ struct bgp_table *table = bgp_table_init(NULL, AFI_IP, SAFI_UNICAST);
+
+ printf("Testing bgp_table_range_lookup\n");
+
+ printf("Setup bgp_table");
+ const char *prefixes[] = {"1.16.0.0/16", "1.16.128.0/18",
+ "1.16.192.0/18", "1.16.64.0/19",
+ "1.16.160.0/19", "1.16.32.0/20",
+ "1.16.32.0/21", "16.0.0.0/16"};
+
+ int num_prefixes = array_size(prefixes);
+
+ for (int i = 0; i < num_prefixes; i++)
+ add_node(table, prefixes[i]);
+
+ do_test(table, "1.16.0.0/17", "1.16.64.0/19", "1.16.32.0/20",
+ "1.16.32.0/20", "1.16.32.0/21", NULL);
+ do_test(table, "1.16.128.0/17", "1.16.128.0/18", "1.16.192.0/18",
+ "1.16.160.0/19", NULL);
+
+ do_test(table, "1.16.0.0/16", "1.16.0.0/16", "1.16.128.0/18",
+ "1.16.192.0/18", "1.16.64.0/19", "1.16.160.0/19",
+ "1.16.32.0/20", "1.16.32.0/21", NULL);
+
+ do_test(table, "1.17.0.0/16", NULL);
+
+ do_test(table, "128.0.0.0/8", NULL);
+
+ do_test(table, "16.0.0.0/8", "16.0.0.0/16", NULL);
+
+ do_test(table, "0.0.0.0/2", "1.16.0.0/16", "1.16.128.0/18",
+ "1.16.192.0/18", "1.16.64.0/19", "1.16.160.0/19",
+ "1.16.32.0/20", "1.16.32.0/21", "16.0.0.0/16", NULL);
+}
+
+int main(void)
+{
+ test_range_lookup();
+}
diff --git a/tests/bgpd/test_bgp_table.py b/tests/bgpd/test_bgp_table.py
new file mode 100644
index 0000000..8f05442
--- /dev/null
+++ b/tests/bgpd/test_bgp_table.py
@@ -0,0 +1,9 @@
+import frrtest
+
+
+class TestTable(frrtest.TestMultiOut):
+ program = "./test_bgp_table"
+
+
+for i in range(7):
+ TestTable.onesimple("Checks successfull")
diff --git a/tests/bgpd/test_capability.c b/tests/bgpd/test_capability.c
new file mode 100644
index 0000000..9d3d0ec
--- /dev/null
+++ b/tests/bgpd/test_capability.c
@@ -0,0 +1,984 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2007 Sun Microsystems, Inc.
+ */
+
+#include <zebra.h>
+
+#include "qobj.h"
+#include "vty.h"
+#include "stream.h"
+#include "privs.h"
+#include "memory.h"
+#include "queue.h"
+#include "filter.h"
+#include "frr_pthread.h"
+
+#include "bgpd/bgpd.c"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_packet.h"
+
+#define VT100_RESET "\x1b[0m"
+#define VT100_RED "\x1b[31m"
+#define VT100_GREEN "\x1b[32m"
+#define VT100_YELLOW "\x1b[33m"
+
+#define CAPABILITY 0
+#define DYNCAP 1
+#define OPT_PARAM 2
+
+/* need these to link in libbgp */
+struct zebra_privs_t bgpd_privs = {};
+struct event_loop *master = NULL;
+
+static int failed = 0;
+static int tty = 0;
+
+/* test segments to parse and validate, and use for other tests */
+static struct test_segment {
+ const char *name;
+ const char *desc;
+ const uint8_t data[1024];
+ int len;
+#define SHOULD_PARSE 0
+#define SHOULD_ERR -1
+ int parses; /* whether it should parse or not */
+ as_t peek_for; /* what peek_for_as4_capability should say */
+
+ /* AFI/SAFI validation */
+ int validate_afi;
+ iana_afi_t afi;
+ iana_safi_t safi;
+#define VALID_AFI 1
+#define INVALID_AFI 0
+ int afi_valid;
+} test_segments[] = {
+ /* 0 */
+ {
+ "caphdr",
+ "capability header, and no more",
+ {CAPABILITY_CODE_REFRESH, 0x0},
+ 2,
+ SHOULD_PARSE,
+ },
+ /* 1 */
+ {
+ "nodata",
+ "header, no data but length says there is",
+ {0x1, 0xa},
+ 2,
+ SHOULD_ERR,
+ },
+ /* 2 */
+ {
+ "padded",
+ "valid, with padding",
+ {CAPABILITY_CODE_REFRESH, 0x2, 0x0, 0x0},
+ 4,
+ SHOULD_PARSE,
+ },
+ /* 3 */
+ {
+ "minsize",
+ "violates minsize requirement",
+ {CAPABILITY_CODE_ORF, 0x2, 0x0, 0x0},
+ 4,
+ SHOULD_ERR,
+ },
+ {NULL, NULL, {0}, 0, 0},
+};
+
+static struct test_segment mp_segments[] = {
+ {
+ "MP4",
+ "MP IP/Uni",
+ {0x1, 0x4, 0x0, 0x1, 0x0, 0x1},
+ 6,
+ SHOULD_PARSE,
+ 0,
+ 1,
+ IANA_AFI_IPV4,
+ IANA_SAFI_UNICAST,
+ VALID_AFI,
+ },
+ {
+ "MPv6",
+ "MP IPv6/Uni",
+ {0x1, 0x4, 0x0, 0x2, 0x0, 0x1},
+ 6,
+ SHOULD_PARSE,
+ 0,
+ 1,
+ IANA_AFI_IPV6,
+ IANA_SAFI_UNICAST,
+ VALID_AFI,
+ },
+ /* 5 */
+ {
+ "MP2",
+ "MP IP/Multicast",
+ {CAPABILITY_CODE_MP, 0x4, 0x0, 0x1, 0x0, 0x2},
+ 6,
+ SHOULD_PARSE,
+ 0,
+ 1,
+ IANA_AFI_IPV4,
+ IANA_SAFI_MULTICAST,
+ VALID_AFI,
+ },
+ /* 6 */
+ {
+ "MP3",
+ "MP IP6/MPLS-labeled VPN",
+ {CAPABILITY_CODE_MP, 0x4, 0x0, 0x2, 0x0, 0x80},
+ 6,
+ SHOULD_PARSE,
+ 0,
+ 1,
+ IANA_AFI_IPV6,
+ IANA_SAFI_MPLS_VPN,
+ VALID_AFI,
+ },
+ /* 7 */
+ {
+ "MP5",
+ "MP IP6/MPLS-VPN",
+ {CAPABILITY_CODE_MP, 0x4, 0x0, 0x2, 0x0, 0x4},
+ 6,
+ SHOULD_PARSE,
+ 0,
+ 1,
+ IANA_AFI_IPV6,
+ IANA_SAFI_MPLS_VPN,
+ VALID_AFI,
+ },
+ /* 8 */
+ {
+ "MP6",
+ "MP IP4/MPLS-labeled VPN",
+ {CAPABILITY_CODE_MP, 0x4, 0x0, 0x1, 0x0, 0x80},
+ 6,
+ SHOULD_PARSE,
+ 0,
+ 1,
+ IANA_AFI_IPV4,
+ IANA_SAFI_MPLS_VPN,
+ VALID_AFI,
+ },
+ /* 10 */
+ {
+ "MP8",
+ "MP unknown AFI/SAFI",
+ {CAPABILITY_CODE_MP, 0x4, 0x0, 0xa, 0x0, 0x81},
+ 6,
+ SHOULD_PARSE,
+ 0,
+ 1,
+ 0xa,
+ 0x81,
+ INVALID_AFI, /* parses, but unknown */
+ },
+ /* 11 */
+ {
+ "MP-short",
+ "MP IP4/Unicast, length too short (< minimum)",
+ {CAPABILITY_CODE_MP, 0x2, 0x0, 0x1, 0x0, 0x1},
+ 6,
+ SHOULD_ERR,
+ },
+ /* 12 */
+ {
+ "MP-overflow",
+ "MP IP4/Unicast, length too long",
+ {CAPABILITY_CODE_MP, 0x6, 0x0, 0x1, 0x0, 0x1},
+ 6,
+ SHOULD_ERR,
+ 0,
+ 1,
+ IANA_AFI_IPV4,
+ IANA_SAFI_UNICAST,
+ VALID_AFI,
+ },
+ {NULL, NULL, {0}, 0, 0}};
+
+static struct test_segment misc_segments[] =
+ {
+ /* 13 */
+ {
+ "ORF",
+ "ORF, simple, single entry, single tuple",
+ {/* hdr */ CAPABILITY_CODE_ORF, 0x7,
+ /* mpc */ 0x0, 0x1, 0x0, 0x1,
+ /* num */ 0x1,
+ /* tuples */ 0x40, 0x3},
+ 9,
+ SHOULD_PARSE,
+ },
+ /* 14 */
+ {
+ "ORF-many",
+ "ORF, multi entry/tuple",
+ {
+ /* hdr */ CAPABILITY_CODE_ORF,
+ 0x21,
+ /* mpc */ 0x0,
+ 0x1,
+ 0x0,
+ 0x1,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ ORF_MODE_BOTH,
+ 0x80,
+ ORF_MODE_RECEIVE,
+ 0x80,
+ ORF_MODE_SEND,
+ /* mpc */ 0x0,
+ 0x2,
+ 0x0,
+ 0x1,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ ORF_MODE_BOTH,
+ 0x80,
+ ORF_MODE_RECEIVE,
+ 0x80,
+ ORF_MODE_SEND,
+ /* mpc */ 0x0,
+ 0x2,
+ 0x0,
+ 0x2,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ ORF_MODE_RECEIVE,
+ 0x80,
+ ORF_MODE_SEND,
+ 0x80,
+ ORF_MODE_BOTH,
+ },
+ 35,
+ SHOULD_PARSE,
+ },
+ /* 15 */
+ {
+ "ORFlo",
+ "ORF, multi entry/tuple, hdr length too short",
+ {
+ /* hdr */ CAPABILITY_CODE_ORF,
+ 0x15,
+ /* mpc */ 0x0,
+ 0x1,
+ 0x0,
+ 0x1,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ /* mpc */ 0x0,
+ 0x1,
+ 0x0,
+ 0x1,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ /* mpc */ 0x0,
+ 0x2,
+ 0x0,
+ 0x2,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ },
+ 35,
+ SHOULD_ERR, /* It should error on invalid
+ Route-Refresh.. */
+ },
+ /* 16 */
+ {"ORFlu",
+ "ORF, multi entry/tuple, length too long",
+ {
+ /* hdr */ 0x3,
+ 0x22,
+ /* mpc */ 0x0,
+ 0x1,
+ 0x0,
+ 0x1,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ /* mpc */ 0x0,
+ 0x2,
+ 0x0,
+ 0x1,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ /* mpc */ 0x0,
+ 0x2,
+ 0x0,
+ 0x2,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ },
+ 35,
+ SHOULD_ERR},
+ /* 17 */
+ {
+ "ORFnu",
+ "ORF, multi entry/tuple, entry number too long",
+ {
+ /* hdr */ 0x3,
+ 0x21,
+ /* mpc */ 0x0,
+ 0x1,
+ 0x0,
+ 0x1,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ /* mpc */ 0x0,
+ 0x2,
+ 0x0,
+ 0x1,
+ /* num */ 0x4,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ /* mpc */ 0x0,
+ 0x2,
+ 0x0,
+ 0x2,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ },
+ 35,
+ SHOULD_PARSE, /* parses, but last few tuples should be
+ gibberish */
+ },
+ /* 18 */
+ {
+ "ORFno",
+ "ORF, multi entry/tuple, entry number too short",
+ {
+ /* hdr */ 0x3,
+ 0x21,
+ /* mpc */ 0x0,
+ 0x1,
+ 0x0,
+ 0x1,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ /* mpc */ 0x0,
+ 0x2,
+ 0x0,
+ 0x1,
+ /* num */ 0x1,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ /* mpc */ 0x0,
+ 0x2,
+ 0x0,
+ 0x2,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ },
+ 35,
+ SHOULD_PARSE, /* Parses, but should get gibberish
+ afi/safis */
+ },
+ /* 17 */
+ {
+ "ORFpad",
+ "ORF, multi entry/tuple, padded to align",
+ {
+ /* hdr */ 0x3,
+ 0x22,
+ /* mpc */ 0x0,
+ 0x1,
+ 0x0,
+ 0x1,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ /* mpc */ 0x0,
+ 0x2,
+ 0x0,
+ 0x1,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ /* mpc */ 0x0,
+ 0x2,
+ 0x0,
+ 0x2,
+ /* num */ 0x3,
+ /* tuples */ 0x40,
+ 0x3,
+ 0x80,
+ 0x1,
+ 0x80,
+ 0x2,
+ 0x00,
+ },
+ 36,
+ SHOULD_PARSE,
+ },
+ /* 19 */
+ {
+ "AS4",
+ "AS4 capability",
+ {0x41, 0x4, 0xab, 0xcd, 0xef,
+ 0x12}, /* AS: 2882400018 */
+ 6,
+ SHOULD_PARSE,
+ 2882400018,
+ },
+ {
+ "AS4",
+ "AS4 capability: short",
+ {0x41, 0x4, 0xab, 0xcd, 0xef}, /* AS: 2882400018 */
+ 5,
+ SHOULD_ERR,
+ },
+ {
+ "AS4",
+ "AS4 capability: long",
+ {0x41, 0x4, 0xab, 0xcd, 0xef, 0x12, 0x12},
+ 7,
+ SHOULD_ERR,
+ 2882400018,
+ },
+ {
+ "GR",
+ "GR capability",
+ {
+ /* hdr */ CAPABILITY_CODE_RESTART, 0xe,
+ /* R-bit, time */ 0xf1, 0x12,
+ /* afi */ 0x0, 0x1,
+ /* safi */ 0x1,
+ /* flags */ 0xf,
+ /* afi */ 0x0, 0x2,
+ /* safi */ 0x1,
+ /* flags */ 0x0,
+ /* afi */ 0x0, 0x2,
+ /* safi */ 0x2,
+ /* flags */ 0x1,
+ },
+ 16,
+ SHOULD_PARSE,
+ },
+ {
+ "GR-short",
+ "GR capability, but header length too short",
+ {
+ /* hdr */ 0x40, 0xa,
+ /* R-bit, time */ 0xf1, 0x12,
+ /* afi */ 0x0, 0x1,
+ /* safi */ 0x1,
+ /* flags */ 0xf,
+ /* afi */ 0x0, 0x2,
+ /* safi */ 0x1,
+ /* flags */ 0x0,
+ /* afi */ 0x0, 0x2,
+ /* safi */ 0x2,
+ /* flags */ 0x1,
+ },
+ 15 /* array is 16 though */,
+ SHOULD_ERR,
+ },
+ {
+ "GR-long",
+ "GR capability, but header length too long",
+ {
+ /* hdr */ 0x40, 0xf,
+ /* R-bit, time */ 0xf1, 0x12,
+ /* afi */ 0x0, 0x1,
+ /* safi */ 0x1,
+ /* flags */ 0xf,
+ /* afi */ 0x0, 0x2,
+ /* safi */ 0x1,
+ /* flags */ 0x0,
+ /* afi */ 0x0, 0x2,
+ /* safi */ 0x2,
+ /* flags */ 0x01,
+ },
+ 16,
+ SHOULD_ERR,
+ },
+ {
+ "GR-trunc",
+ "GR capability, but truncated",
+ {
+ /* hdr */ 0x40, 0xf,
+ /* R-bit, time */ 0xf1, 0x12,
+ /* afi */ 0x0, 0x1,
+ /* safi */ 0x1,
+ /* flags */ 0xf,
+ /* afi */ 0x0, 0x2,
+ /* safi */ 0x1,
+ /* flags */ 0x0,
+ /* afi */ 0x0, 0x2,
+ /* safi */ 0x2,
+ /* flags */ 0x1,
+ },
+ 15,
+ SHOULD_ERR,
+ },
+ {
+ "GR-empty",
+ "GR capability, but empty.",
+ {
+ /* hdr */ 0x40, 0x0,
+ },
+ 2,
+ SHOULD_ERR,
+ },
+ {
+ "MP-empty",
+ "MP capability, but empty.",
+ {
+ /* hdr */ 0x1, 0x0,
+ },
+ 2,
+ SHOULD_ERR,
+ },
+ {
+ "ORF-empty",
+ "ORF capability, but empty.",
+ {
+ /* hdr */ 0x3, 0x0,
+ },
+ 2,
+ SHOULD_ERR,
+ },
+ {
+ "AS4-empty",
+ "AS4 capability, but empty.",
+ {
+ /* hdr */ 0x41, 0x0,
+ },
+ 2,
+ SHOULD_ERR,
+ },
+ {
+ "dyn-empty",
+ "Dynamic capability, but empty.",
+ {
+ /* hdr */ 0x42, 0x0,
+ },
+ 2,
+ SHOULD_PARSE,
+ },
+ {
+ "dyn-old",
+ "Dynamic capability (deprecated version)",
+ {CAPABILITY_CODE_DYNAMIC, 0x0},
+ 2,
+ SHOULD_PARSE,
+ },
+ {
+ "Role",
+ "Role capability",
+ {
+ /* hdr */ 0x9, 0x1,
+ 0x1,
+ },
+ 3,
+ SHOULD_PARSE,
+ },
+ {
+ "Role-long",
+ "Role capability, but too long",
+ {
+ /* hdr */ 0x9, 0x4,
+ 0x0, 0x0, 0x0, 0x1,
+ },
+ 6,
+ SHOULD_ERR,
+ },
+ {
+ "Role-empty",
+ "Role capability, but empty.",
+ {
+ /* hdr */ 0x9, 0x0,
+ },
+ 2,
+ SHOULD_ERR,
+ },
+ {NULL, NULL, {0}, 0, 0}};
+
+/* DYNAMIC message */
+struct test_segment dynamic_cap_msgs[] = {
+ {
+ "DynCap",
+ "Dynamic Capability Message, IP/Multicast",
+ {0x0, 0x1, 0x4, 0x0, 0x1, 0x0, 0x2},
+ 7,
+ SHOULD_PARSE, /* horrible alignment, just as with ORF */
+ },
+ {
+ "DynCapLong",
+ "Dynamic Capability Message, IP/Multicast, truncated",
+ {0x0, 0x1, 0x4, 0x0, 0x1, 0x0, 0x2},
+ 5,
+ SHOULD_ERR,
+ },
+ {
+ "DynCapPadded",
+ "Dynamic Capability Message, IP/Multicast, padded",
+ {0x0, 0x1, 0x4, 0x0, 0x1, 0x0, 0x2, 0x0},
+ 8,
+ SHOULD_ERR, /* No way to tell padding from data.. */
+ },
+ {
+ "DynCapMPCpadded",
+ "Dynamic Capability Message, IP/Multicast, cap data padded",
+ {0x0, 0x1, 0x5, 0x0, 0x1, 0x0, 0x2, 0x0},
+ 8,
+ SHOULD_PARSE, /* You can though add padding to the capability
+ data */
+ },
+ {
+ "DynCapMPCoverflow",
+ "Dynamic Capability Message, IP/Multicast, cap data != length",
+ {0x0, 0x1, 0x3, 0x0, 0x1, 0x0, 0x2, 0x0},
+ 8,
+ SHOULD_ERR,
+ },
+ {NULL, NULL, {0}, 0, 0}};
+
+/* Entire Optional-Parameters block */
+struct test_segment opt_params[] = {
+ {
+ "Cap-singlets",
+ "One capability per Optional-Param",
+ {
+ 0x02, 0x06, 0x01, 0x04,
+ 0x00, 0x01, 0x00, 0x01, /* MP IPv4/Uni */
+ 0x02, 0x06, 0x01, 0x04,
+ 0x00, 0x02, 0x00, 0x01, /* MP IPv6/Uni */
+ 0x02, 0x02, 0x80, 0x00, /* RR (old) */
+ 0x02, 0x02, 0x02, 0x00, /* RR */
+ },
+ 24,
+ SHOULD_PARSE,
+ },
+ {
+ "Cap-series",
+ "Series of capability, one Optional-Param",
+ {
+ 0x02, 0x10, 0x01, 0x04, 0x00, 0x01, 0x00,
+ 0x01, /* MP IPv4/Uni */
+ 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, /* MP IPv6/Uni */
+ 0x80, 0x00, /* RR (old) */
+ 0x02, 0x00, /* RR */
+ },
+ 18,
+ SHOULD_PARSE,
+ },
+ {
+ "AS4more",
+ "AS4 capability after other caps (singlets)",
+ {
+ 0x02, 0x06, 0x01, 0x04,
+ 0x00, 0x01, 0x00, 0x01, /* MP IPv4/Uni */
+ 0x02, 0x06, 0x01, 0x04,
+ 0x00, 0x02, 0x00, 0x01, /* MP IPv6/Uni */
+ 0x02, 0x02, 0x80, 0x00, /* RR (old) */
+ 0x02, 0x02, 0x02, 0x00, /* RR */
+ 0x02, 0x06, 0x41, 0x04,
+ 0x00, 0x03, 0x00, 0x06 /* AS4: 1996614 */
+ },
+ 32,
+ SHOULD_PARSE,
+ 196614,
+ },
+ {
+ "AS4series",
+ "AS4 capability, in series of capabilities",
+ {
+ 0x02, 0x16, 0x01, 0x04, 0x00, 0x01,
+ 0x00, 0x01, /* MP IPv4/Uni */
+ 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, /* MP IPv6/Uni */
+ 0x80, 0x00, /* RR (old) */
+ 0x02, 0x00, /* RR */
+ 0x41, 0x04, 0x00, 0x03, 0x00, 0x06 /* AS4: 1996614 */
+ },
+ 24,
+ SHOULD_PARSE,
+ 196614,
+ },
+ {
+ "AS4real",
+ "AS4 capability, in series of capabilities",
+ {
+ 0x02, 0x06, 0x01, 0x04,
+ 0x00, 0x01, 0x00, 0x01, /* MP IPv4/uni */
+ 0x02, 0x06, 0x01, 0x04,
+ 0x00, 0x02, 0x00, 0x01, /* MP IPv6/uni */
+ 0x02, 0x02, 0x80, 0x00, /* RR old */
+ 0x02, 0x02, 0x02, 0x00, /* RR */
+ 0x02, 0x06, 0x41, 0x04,
+ 0x00, 0x03, 0x00, 0x06, /* AS4 */
+ },
+ 32,
+ SHOULD_PARSE,
+ 196614,
+ },
+ {
+ "AS4real2",
+ "AS4 capability, in series of capabilities",
+ {
+ 0x02, 0x06, 0x01, 0x04, 0x00, 0x01, 0x00, 0x01, 0x02,
+ 0x06, 0x01, 0x04, 0x00, 0x02, 0x00, 0x01, 0x02, 0x02,
+ 0x80, 0x00, 0x02, 0x02, 0x02, 0x00, 0x02, 0x06, 0x41,
+ 0x04, 0x00, 0x00, 0xfc, 0x03, 0x02, 0x09, 0x82, 0x07,
+ 0x00, 0x01, 0x00, 0x01, 0x01, 0x80, 0x03, 0x02, 0x09,
+ 0x03, 0x07, 0x00, 0x01, 0x00, 0x01, 0x01, 0x40, 0x03,
+ 0x02, 0x02, 0x42, 0x00,
+ },
+ 58,
+ SHOULD_PARSE,
+ 64515,
+ },
+
+ {NULL, NULL, {0}, 0, 0}};
+
+/* basic parsing test */
+static void parse_test(struct peer *peer, struct test_segment *t, int type)
+{
+ int ret;
+ int capability = 0;
+ as_t as4 = 0;
+ int oldfailed = failed;
+ int len = t->len;
+#define RANDOM_FUZZ 35
+
+ stream_reset(peer->curr);
+ stream_put(peer->curr, NULL, RANDOM_FUZZ);
+ stream_set_getp(peer->curr, RANDOM_FUZZ);
+
+ switch (type) {
+ case CAPABILITY:
+ stream_putc(peer->curr, BGP_OPEN_OPT_CAP);
+ stream_putc(peer->curr, t->len);
+ break;
+ case DYNCAP:
+ /* for (i = 0; i < BGP_MARKER_SIZE; i++)
+ stream_putc (peer->, 0xff);
+ stream_putw (s, 0);
+ stream_putc (s, BGP_MSG_CAPABILITY);*/
+ break;
+ }
+ stream_write(peer->curr, t->data, t->len);
+
+ printf("%s: %s\n", t->name, t->desc);
+
+ switch (type) {
+ case CAPABILITY:
+ len += 2; /* to cover the OPT-Param header */
+ _FALLTHROUGH
+ case OPT_PARAM:
+ printf("len: %u\n", len);
+ /* peek_for_as4 wants getp at capibility*/
+ as4 = peek_for_as4_capability(peer, len);
+ printf("peek_for_as4: as4 is %u\n", as4);
+ /* and it should leave getp as it found it */
+ assert(stream_get_getp(peer->curr) == RANDOM_FUZZ);
+
+ ret = bgp_open_option_parse(peer, len, &capability);
+ break;
+ case DYNCAP:
+ ret = bgp_capability_receive(peer->connection, peer, t->len);
+ break;
+ default:
+ printf("unknown type %u\n", type);
+ exit(1);
+ }
+
+ if (ret != BGP_Stop && t->validate_afi) {
+ afi_t afi;
+ safi_t safi;
+
+ /* Convert AFI, SAFI to internal values, check. */
+ if (bgp_map_afi_safi_iana2int(t->afi, t->safi, &afi, &safi)) {
+ if (t->afi_valid == VALID_AFI)
+ failed++;
+ }
+ printf("MP: %u(%u)/%u(%u): recv %u, nego %u\n", t->afi, afi,
+ t->safi, safi, peer->afc_recv[afi][safi],
+ peer->afc_nego[afi][safi]);
+
+ if (t->afi_valid == VALID_AFI) {
+
+ if (!peer->afc_recv[afi][safi])
+ failed++;
+ if (!peer->afc_nego[afi][safi])
+ failed++;
+ }
+ }
+
+ if (as4 != t->peek_for) {
+ printf("as4 %u != %u\n", as4, t->peek_for);
+ failed++;
+ }
+
+ /*
+ * Some of the functions used return BGP_Stop on error and some return
+ * -1. If we have -1, keep it; if we have BGP_Stop, transform it to the
+ * correct pass/fail code
+ */
+ if (ret != -1)
+ ret = (ret == BGP_Stop) ? -1 : 0;
+
+ printf("parsed?: %s\n", ret ? "no" : "yes");
+
+ if (ret != t->parses) {
+ printf("t->parses: %d\nret: %d\n", t->parses, ret);
+ failed++;
+ }
+
+ if (tty)
+ printf("%s",
+ (failed > oldfailed) ? VT100_RED "failed!" VT100_RESET
+ : VT100_GREEN "OK" VT100_RESET);
+ else
+ printf("%s", (failed > oldfailed) ? "failed!" : "OK");
+
+ if (failed)
+ printf(" (%u)", failed);
+
+ printf("\n\n");
+}
+
+static struct bgp *bgp;
+static as_t asn = 100;
+
+int main(void)
+{
+ struct peer *peer;
+ int i, j;
+
+ conf_bgp_debug_neighbor_events = -1UL;
+ conf_bgp_debug_packet = -1UL;
+ conf_bgp_debug_as4 = -1UL;
+ term_bgp_debug_neighbor_events = -1UL;
+ term_bgp_debug_packet = -1UL;
+ term_bgp_debug_as4 = -1UL;
+
+ qobj_init();
+ master = event_master_create(NULL);
+ bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
+ vrf_init(NULL, NULL, NULL, NULL);
+ bgp_option_set(BGP_OPT_NO_LISTEN);
+
+ frr_pthread_init();
+ bgp_pthreads_init();
+ bgp_pth_ka->running = true;
+
+ if (fileno(stdout) >= 0)
+ tty = isatty(fileno(stdout));
+
+ if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT, NULL,
+ ASNOTATION_PLAIN) < 0)
+ return -1;
+
+ peer = peer_create_accept(bgp);
+ peer->host = (char *)"foo";
+
+ for (i = AFI_IP; i < AFI_MAX; i++)
+ for (j = SAFI_UNICAST; j < SAFI_MAX; j++) {
+ peer->afc[i][j] = 1;
+ peer->afc_adv[i][j] = 1;
+ }
+
+ peer->curr = stream_new(BGP_MAX_PACKET_SIZE);
+
+ i = 0;
+ while (mp_segments[i].name)
+ parse_test(peer, &mp_segments[i++], CAPABILITY);
+
+ /* These tests assume mp_segments tests set at least
+ * one of the afc_nego's
+ */
+ i = 0;
+ while (test_segments[i].name)
+ parse_test(peer, &test_segments[i++], CAPABILITY);
+
+ i = 0;
+ while (misc_segments[i].name)
+ parse_test(peer, &misc_segments[i++], CAPABILITY);
+
+ i = 0;
+ while (opt_params[i].name)
+ parse_test(peer, &opt_params[i++], OPT_PARAM);
+
+ SET_FLAG(peer->cap, PEER_CAP_DYNAMIC_ADV);
+ peer->connection = bgp_peer_connection_new(peer);
+ peer->connection->status = Established;
+
+ i = 0;
+ while (dynamic_cap_msgs[i].name)
+ parse_test(peer, &dynamic_cap_msgs[i++], DYNCAP);
+
+ printf("failures: %d\n", failed);
+ return failed;
+}
diff --git a/tests/bgpd/test_capability.py b/tests/bgpd/test_capability.py
new file mode 100644
index 0000000..da9245b
--- /dev/null
+++ b/tests/bgpd/test_capability.py
@@ -0,0 +1,56 @@
+import frrtest
+
+
+class TestCapability(frrtest.TestMultiOut):
+ program = "./test_capability"
+
+
+TestCapability.okfail("MP4: MP IP/Uni")
+TestCapability.okfail("MPv6: MP IPv6/Uni")
+TestCapability.okfail("MP2: MP IP/Multicast")
+TestCapability.okfail("MP3: MP IP6/MPLS-labeled VPN")
+TestCapability.okfail("MP5: MP IP6/MPLS-VPN")
+TestCapability.okfail("MP6: MP IP4/MPLS-labeled VPN")
+TestCapability.okfail("MP8: MP unknown AFI/SAFI")
+TestCapability.okfail("MP-short: MP IP4/Unicast, length too short (< minimum)")
+TestCapability.okfail("MP-overflow: MP IP4/Unicast, length too long")
+TestCapability.okfail("caphdr: capability header, and no more")
+TestCapability.okfail("nodata: header, no data but length says there is")
+TestCapability.okfail("padded: valid, with padding")
+TestCapability.okfail("minsize: violates minsize requirement")
+TestCapability.okfail("ORF: ORF, simple, single entry, single tuple")
+TestCapability.okfail("ORF-many: ORF, multi entry/tuple")
+TestCapability.okfail("ORFlo: ORF, multi entry/tuple, hdr length too short")
+TestCapability.okfail("ORFlu: ORF, multi entry/tuple, length too long")
+TestCapability.okfail("ORFnu: ORF, multi entry/tuple, entry number too long")
+TestCapability.okfail("ORFno: ORF, multi entry/tuple, entry number too short")
+TestCapability.okfail("ORFpad: ORF, multi entry/tuple, padded to align")
+TestCapability.okfail("AS4: AS4 capability")
+TestCapability.okfail("GR: GR capability")
+TestCapability.okfail("GR-short: GR capability, but header length too short")
+TestCapability.okfail("GR-long: GR capability, but header length too long")
+TestCapability.okfail("GR-trunc: GR capability, but truncated")
+TestCapability.okfail("GR-empty: GR capability, but empty.")
+TestCapability.okfail("MP-empty: MP capability, but empty.")
+TestCapability.okfail("ORF-empty: ORF capability, but empty.")
+TestCapability.okfail("AS4-empty: AS4 capability, but empty.")
+TestCapability.okfail("dyn-empty: Dynamic capability, but empty.")
+TestCapability.okfail("dyn-old: Dynamic capability (deprecated version)")
+TestCapability.okfail("Role: Role capability")
+TestCapability.okfail("Role-long: Role capability, but too long")
+TestCapability.okfail("Role-empty: Role capability, but empty.")
+TestCapability.okfail("Cap-singlets: One capability per Optional-Param")
+TestCapability.okfail("Cap-series: Series of capability, one Optional-Param")
+TestCapability.okfail("AS4more: AS4 capability after other caps (singlets)")
+TestCapability.okfail("AS4series: AS4 capability, in series of capabilities")
+TestCapability.okfail("AS4real: AS4 capability, in series of capabilities")
+TestCapability.okfail("AS4real2: AS4 capability, in series of capabilities")
+TestCapability.okfail("DynCap: Dynamic Capability Message, IP/Multicast")
+TestCapability.okfail("DynCapLong: Dynamic Capability Message, IP/Multicast, truncated")
+TestCapability.okfail("DynCapPadded: Dynamic Capability Message, IP/Multicast, padded")
+TestCapability.okfail(
+ "DynCapMPCpadded: Dynamic Capability Message, IP/Multicast, cap data padded"
+)
+TestCapability.okfail(
+ "DynCapMPCoverflow: Dynamic Capability Message, IP/Multicast, cap data != length"
+)
diff --git a/tests/bgpd/test_ecommunity.c b/tests/bgpd/test_ecommunity.c
new file mode 100644
index 0000000..49322d3
--- /dev/null
+++ b/tests/bgpd/test_ecommunity.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2007 Sun Microsystems, Inc.
+ */
+#include <zebra.h>
+
+#include "vty.h"
+#include "stream.h"
+#include "privs.h"
+#include "memory.h"
+#include "queue.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_ecommunity.h"
+
+/* need these to link in libbgp */
+struct zebra_privs_t bgpd_privs = {};
+struct event_loop *master = NULL;
+
+static int failed = 0;
+
+/* specification for a test - what the results should be */
+struct test_spec {
+ const char *shouldbe; /* the string the path should parse to */
+};
+
+
+/* test segments to parse and validate, and use for other tests */
+static struct test_segment {
+ const char *name;
+ const char *desc;
+ const uint8_t data[1024];
+ int len;
+ struct test_spec sp;
+} test_segments[] = {{/* 0 */
+ "ipaddr",
+ "rt 1.2.3.4:257",
+ {ECOMMUNITY_ENCODE_IP, ECOMMUNITY_ROUTE_TARGET, 0x1, 0x2,
+ 0x3, 0x4, 0x1, 0x1},
+ 8,
+ {"rt 1.2.3.4:257"}},
+ {/* 1 */
+ "ipaddr-so",
+ "soo 1.2.3.4:257",
+ {ECOMMUNITY_ENCODE_IP, ECOMMUNITY_SITE_ORIGIN, 0x1, 0x2,
+ 0x3, 0x4, 0x1, 0x1},
+ 8,
+ {"soo 1.2.3.4:257"}},
+ {/* 2 */
+ "asn",
+ "rt 23456:987654321",
+ {ECOMMUNITY_ENCODE_AS, ECOMMUNITY_SITE_ORIGIN, 0x5b, 0xa0,
+ 0x3a, 0xde, 0x68, 0xb1},
+ 8,
+ {"soo 23456:987654321"}},
+ {/* 3 */
+ "asn4",
+ "rt 168450976:4321",
+ {ECOMMUNITY_ENCODE_AS4, ECOMMUNITY_SITE_ORIGIN, 0xa, 0xa,
+ 0x5b, 0xa0, 0x10, 0xe1},
+ 8,
+ {"soo 168450976:4321"}},
+ {NULL, NULL, {0}, 0, {NULL}}};
+
+
+/* validate the given aspath */
+static int validate(struct ecommunity *ecom, const struct test_spec *sp)
+{
+ int fails = 0;
+ struct ecommunity *etmp;
+ char *str1, *str2;
+
+ printf("got:\n %s\n", ecommunity_str(ecom));
+ str1 = ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_COMMUNITY_LIST, 0);
+ etmp = ecommunity_str2com(str1, 0, 1);
+ if (etmp)
+ str2 = ecommunity_ecom2str(etmp,
+ ECOMMUNITY_FORMAT_COMMUNITY_LIST, 0);
+ else
+ str2 = NULL;
+
+ if (strcmp(sp->shouldbe, str1)) {
+ failed++;
+ fails++;
+ printf("shouldbe: %s\n%s\n", str1, sp->shouldbe);
+ }
+ if (!etmp || strcmp(str1, str2)) {
+ failed++;
+ fails++;
+ printf("dogfood: in %s\n"
+ " in->out %s\n",
+ str1, (etmp && str2) ? str2 : "NULL");
+ }
+ ecommunity_free(&etmp);
+ XFREE(MTYPE_ECOMMUNITY_STR, str1);
+ XFREE(MTYPE_ECOMMUNITY_STR, str2);
+
+ return fails;
+}
+
+/* basic parsing test */
+static void parse_test(struct test_segment *t)
+{
+ struct ecommunity *ecom;
+
+ printf("%s: %s\n", t->name, t->desc);
+
+ ecom = ecommunity_parse((uint8_t *)t->data, t->len, 0);
+
+ printf("ecom: %s\nvalidating...:\n", ecommunity_str(ecom));
+
+ if (!validate(ecom, &t->sp))
+ printf("OK\n");
+ else
+ printf("failed\n");
+
+ printf("\n");
+ ecommunity_unintern(&ecom);
+}
+
+
+int main(void)
+{
+ int i = 0;
+ ecommunity_init();
+ while (test_segments[i].name)
+ parse_test(&test_segments[i++]);
+
+ printf("failures: %d\n", failed);
+ // printf ("aspath count: %ld\n", aspath_count());
+ return failed;
+ // return (failed + aspath_count());
+}
diff --git a/tests/bgpd/test_ecommunity.py b/tests/bgpd/test_ecommunity.py
new file mode 100644
index 0000000..1499294
--- /dev/null
+++ b/tests/bgpd/test_ecommunity.py
@@ -0,0 +1,11 @@
+import frrtest
+
+
+class TestEcommunity(frrtest.TestMultiOut):
+ program = "./test_ecommunity"
+
+
+TestEcommunity.okfail("ipaddr")
+TestEcommunity.okfail("ipaddr-so")
+TestEcommunity.okfail("asn")
+TestEcommunity.okfail("asn4")
diff --git a/tests/bgpd/test_mp_attr.c b/tests/bgpd/test_mp_attr.c
new file mode 100644
index 0000000..cebdda9
--- /dev/null
+++ b/tests/bgpd/test_mp_attr.c
@@ -0,0 +1,1117 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2008 Sun Microsystems, Inc.
+ */
+
+#include <zebra.h>
+
+#include "qobj.h"
+#include "vty.h"
+#include "stream.h"
+#include "privs.h"
+#include "memory.h"
+#include "queue.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_network.h"
+
+#define VT100_RESET "\x1b[0m"
+#define VT100_RED "\x1b[31m"
+#define VT100_GREEN "\x1b[32m"
+#define VT100_YELLOW "\x1b[33m"
+
+#define CAPABILITY 0
+#define DYNCAP 1
+#define OPT_PARAM 2
+
+/* need these to link in libbgp */
+struct zebra_privs_t bgpd_privs = {};
+struct event_loop *master = NULL;
+
+static int failed = 0;
+static int tty = 0;
+
+/* test segments to parse and validate, and use for other tests */
+static struct test_segment {
+ const char *name;
+ const char *desc;
+ const uint8_t data[1024];
+ int len;
+#define SHOULD_PARSE 0
+#define SHOULD_ERR -1
+ int parses; /* whether it should parse or not */
+} mp_reach_segments[] = {
+ {
+ "IPv6",
+ "IPV6 MP Reach, global nexthop, 1 NLRI",
+ {
+ /* AFI / SAFI */ 0x0,
+ AFI_IP6,
+ SAFI_UNICAST,
+ /* nexthop bytes */ 16,
+ /* Nexthop (global) */ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2,
+ 0xaa,
+ 0xbb,
+ 0xcc,
+ 0xdd,
+ 0x3,
+ 0x4,
+ 0x5,
+ 0x6,
+ 0xa1,
+ 0xa2,
+ 0xa3,
+ 0xa4,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 32,
+ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102::/32 */
+ },
+ (4 + 16 + 1 + 5),
+ SHOULD_PARSE,
+ },
+ {
+ "IPv6-2",
+ "IPV6 MP Reach, global nexthop, 2 NLRIs",
+ {
+ /* AFI / SAFI */ 0x0,
+ AFI_IP6,
+ SAFI_UNICAST,
+ /* nexthop bytes */ 16,
+ /* Nexthop (global) */ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* ffee:102:... */
+ 0xaa,
+ 0xbb,
+ 0xcc,
+ 0xdd,
+ 0x3,
+ 0x4,
+ 0x5,
+ 0x6,
+ 0xa1,
+ 0xa2,
+ 0xa3,
+ 0xa4,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 32,
+ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102::/32 */
+ 64,
+ 0xff,
+ 0xfe,
+ 0x0,
+ 0x1, /* fffe:1:2:3::/64 */
+ 0x0,
+ 0x2,
+ 0x0,
+ 0x3,
+ },
+ (4 + 16 + 1 + 5 + 9),
+ SHOULD_PARSE,
+ },
+ {
+ "IPv6-default",
+ "IPV6 MP Reach, global nexthop, 2 NLRIs + default",
+ {
+ /* AFI / SAFI */ 0x0,
+ AFI_IP6,
+ SAFI_UNICAST,
+ /* nexthop bytes */ 16,
+ /* Nexthop (global) */ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2,
+ 0xaa,
+ 0xbb,
+ 0xcc,
+ 0xdd,
+ 0x3,
+ 0x4,
+ 0x5,
+ 0x6,
+ 0xa1,
+ 0xa2,
+ 0xa3,
+ 0xa4,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 32,
+ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102::/32 */
+ 64,
+ 0xff,
+ 0xfe,
+ 0x0,
+ 0x1, /* fffe:1:2:3::/64 */
+ 0x0,
+ 0x2,
+ 0x0,
+ 0x3,
+ 0x0, /* ::/0 */
+ },
+ (4 + 16 + 1 + 5 + 9 + 1),
+ SHOULD_PARSE,
+ },
+ {
+ "IPv6-lnh",
+ "IPV6 MP Reach, global+local nexthops, 2 NLRIs + default",
+ {
+ /* AFI / SAFI */ 0x0,
+ AFI_IP6,
+ SAFI_UNICAST,
+ /* nexthop bytes */ 32,
+ /* Nexthop (global) */ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102:... */
+ 0xaa,
+ 0xbb,
+ 0xcc,
+ 0xdd,
+ 0x3,
+ 0x4,
+ 0x5,
+ 0x6,
+ 0xa1,
+ 0xa2,
+ 0xa3,
+ 0xa4,
+ /* Nexthop (local) */ 0xfe,
+ 0x80,
+ 0x0,
+ 0x0, /* fe80::210:2ff:.. */
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x2,
+ 0x10,
+ 0x2,
+ 0xff,
+ 0x1,
+ 0x2,
+ 0x3,
+ 0x4,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 32,
+ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102::/32 */
+ 64,
+ 0xff,
+ 0xfe,
+ 0x0,
+ 0x1, /* fffe:1:2:3::/64 */
+ 0x0,
+ 0x2,
+ 0x0,
+ 0x3,
+ 0x0, /* ::/0 */
+ },
+ (4 + 32 + 1 + 5 + 9 + 1),
+ SHOULD_PARSE,
+ },
+ {
+ "IPv6-nhlen",
+ "IPV6 MP Reach, inappropriate nexthop length",
+ {
+ /* AFI / SAFI */ 0x0,
+ AFI_IP6,
+ SAFI_UNICAST,
+ /* nexthop bytes */ 4,
+ /* Nexthop (global) */ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102:... */
+ 0xaa,
+ 0xbb,
+ 0xcc,
+ 0xdd,
+ 0x3,
+ 0x4,
+ 0x5,
+ 0x6,
+ 0xa1,
+ 0xa2,
+ 0xa3,
+ 0xa4,
+ /* Nexthop (local) */ 0xfe,
+ 0x80,
+ 0x0,
+ 0x0, /* fe80::210:2ff:.. */
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x2,
+ 0x10,
+ 0x2,
+ 0xff,
+ 0x1,
+ 0x2,
+ 0x3,
+ 0x4,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 32,
+ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102::/32 */
+ 64,
+ 0xff,
+ 0xfe,
+ 0x0,
+ 0x1, /* fffe:1:2:3::/64 */
+ 0x0,
+ 0x2,
+ 0x0,
+ 0x3,
+ 0x0, /* ::/0 */
+ },
+ (4 + 32 + 1 + 5 + 9 + 1),
+ SHOULD_ERR,
+ },
+ {
+ "IPv6-nhlen2",
+ "IPV6 MP Reach, invalid nexthop length",
+ {
+ /* AFI / SAFI */ 0x0,
+ AFI_IP6,
+ SAFI_UNICAST,
+ /* nexthop bytes */ 5,
+ /* Nexthop (global) */ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102:... */
+ 0xaa,
+ 0xbb,
+ 0xcc,
+ 0xdd,
+ 0x3,
+ 0x4,
+ 0x5,
+ 0x6,
+ 0xa1,
+ 0xa2,
+ 0xa3,
+ 0xa4,
+ /* Nexthop (local) */ 0xfe,
+ 0x80,
+ 0x0,
+ 0x0, /* fe80::210:2ff:.. */
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x2,
+ 0x10,
+ 0x2,
+ 0xff,
+ 0x1,
+ 0x2,
+ 0x3,
+ 0x4,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 32,
+ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102::/32 */
+ 64,
+ 0xff,
+ 0xfe,
+ 0x0,
+ 0x1, /* fffe:1:2:3::/64 */
+ 0x0,
+ 0x2,
+ 0x0,
+ 0x3,
+ 0x0, /* ::/0 */
+ },
+ (4 + 32 + 1 + 5 + 9 + 1),
+ SHOULD_ERR,
+ },
+ {
+ "IPv6-nhlen3",
+ "IPV6 MP Reach, nexthop length overflow",
+ {
+ /* AFI / SAFI */ 0x0,
+ AFI_IP6,
+ SAFI_UNICAST,
+ /* nexthop bytes */ 32,
+ /* Nexthop (global) */ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102:... */
+ 0xaa,
+ 0xbb,
+ 0xcc,
+ 0xdd,
+ 0x3,
+ 0x4,
+ 0x5,
+ 0x6,
+ 0xa1,
+ 0xa2,
+ 0xa3,
+ 0xa4,
+ },
+ (4 + 16),
+ SHOULD_ERR,
+ },
+ {
+ "IPv6-nhlen4",
+ "IPV6 MP Reach, nexthop length short",
+ {
+ /* AFI / SAFI */ 0x0,
+ AFI_IP6,
+ SAFI_UNICAST,
+ /* nexthop bytes */ 16,
+ /* Nexthop (global) */ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102:... */
+ 0xaa,
+ 0xbb,
+ 0xcc,
+ 0xdd,
+ 0x3,
+ 0x4,
+ 0x5,
+ 0x6,
+ 0xa1,
+ 0xa2,
+ 0xa3,
+ 0xa4,
+ /* Nexthop (local) */ 0xfe,
+ 0x80,
+ 0x0,
+ 0x0, /* fe80::210:2ff:.. */
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x2,
+ 0x10,
+ 0x2,
+ 0xff,
+ 0x1,
+ 0x2,
+ 0x3,
+ 0x4,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 32,
+ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102::/32 */
+ 64,
+ 0xff,
+ 0xfe,
+ 0x0,
+ 0x1, /* fffe:1:2:3::/64 */
+ 0x0,
+ 0x2,
+ 0x0,
+ 0x3,
+ 0x0, /* ::/0 */
+ },
+ (4 + 32 + 1 + 5 + 9 + 1),
+ SHOULD_ERR,
+ },
+ {
+ "IPv6-nlri",
+ "IPV6 MP Reach, NLRI bitlen overflow",
+ {
+ /* AFI / SAFI */ 0x0,
+ AFI_IP6,
+ SAFI_UNICAST,
+ /* nexthop bytes */ 32,
+ /* Nexthop (global) */ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102:... */
+ 0xaa,
+ 0xbb,
+ 0xcc,
+ 0xdd,
+ 0x3,
+ 0x4,
+ 0x5,
+ 0x6,
+ 0xa1,
+ 0xa2,
+ 0xa3,
+ 0xa4,
+ /* Nexthop (local) */ 0xfe,
+ 0x80,
+ 0x0,
+ 0x0, /* fe80::210:2ff:.. */
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x0,
+ 0x2,
+ 0x10,
+ 0x2,
+ 0xff,
+ 0x1,
+ 0x2,
+ 0x3,
+ 0x4,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 120,
+ 0xff,
+ 0xfe,
+ 0x1,
+ 0x2, /* fffe:102::/32 */
+ 64,
+ 0xff,
+ 0xfe,
+ 0x0,
+ 0x1, /* fffe:1:2:3::/64 */
+ 0x0,
+ 0x2,
+ 0x0,
+ 0x3,
+ 0, /* ::/0 */
+ },
+ (4 + 32 + 1 + 5 + 9 + 1),
+ SHOULD_ERR,
+ },
+ {
+ "IPv4",
+ "IPv4 MP Reach, 2 NLRIs + default",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST,
+ /* nexthop bytes */ 4,
+ /* Nexthop */ 192, 168, 0, 1,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 16, 10, 1, /* 10.1/16 */
+ 17, 10, 2, 3, /* 10.2.3/17 */
+ 0, /* 0/0 */
+ },
+ (4 + 4 + 1 + 3 + 4 + 1),
+ SHOULD_PARSE,
+ },
+ {
+ "IPv4-nhlen",
+ "IPv4 MP Reach, nexthop lenth overflow",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST,
+ /* nexthop bytes */ 32,
+ /* Nexthop */ 192, 168, 0, 1,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 16, 10, 1, /* 10.1/16 */
+ 17, 10, 2, 3, /* 10.2.3/17 */
+ 0, /* 0/0 */
+ },
+ (4 + 4 + 1 + 3 + 4 + 1),
+ SHOULD_ERR,
+ },
+ {
+ "IPv4-nlrilen",
+ "IPv4 MP Reach, nlri lenth overflow",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST,
+ /* nexthop bytes */ 4,
+ /* Nexthop */ 192, 168, 0, 1,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 16, 10, 1, /* 10.1/16 */
+ 30, 10, 0, /* 0/0 */
+ },
+ (4 + 4 + 1 + 3 + 2 + 1),
+ SHOULD_ERR,
+ },
+ {
+ "IPv4-VPNv4",
+ "IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, IANA_SAFI_MPLS_VPN,
+ /* nexthop bytes */ 12,
+ /* RD */ 0, 0, 0, 0, /* RD defined to be 0 */
+ 0, 0, 0, 0,
+ /* Nexthop */ 192, 168, 0, 1,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 88 + 16, 0, 1, 2, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_AS */
+ 0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */
+ 10, 1, /* 10.1/16 */
+ 88 + 17, 0xff, 0, 0, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_IP */
+ 192, 168, 0, 1, /* IPv4 */
+ 10, 2, 3, /* 10.2.3/17 */
+ },
+ (4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)),
+ SHOULD_PARSE,
+ },
+ {
+ "IPv4-VPNv4-bogus-plen",
+ "IPv4/MPLS-labeled VPN MP Reach, RD, Nexthop, NLRI / bogus p'len",
+ {
+ /* AFI / SAFI */ 0x0,
+ AFI_IP,
+ IANA_SAFI_MPLS_VPN,
+ /* nexthop bytes */ 12,
+ /* RD */ 0,
+ 0,
+ 1,
+ 2,
+ 0,
+ 0xff,
+ 3,
+ 4,
+ /* Nexthop */ 192,
+ 168,
+ 0,
+ 1,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 16,
+ 10,
+ 1, /* 10.1/16 */
+ 17,
+ 10,
+ 2,
+ 3, /* 10.2.3/17 */
+ 0, /* 0/0 */
+ },
+ (3 + 1 + 3 * 4 + 1 + 3 + 4 + 1),
+ SHOULD_ERR,
+ },
+ {
+ "IPv4-VPNv4-plen1-short",
+ "IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs, 1st plen short",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, IANA_SAFI_MPLS_VPN,
+ /* nexthop bytes */ 12,
+ /* RD */ 0, 0, 0, 0, /* RD defined to be 0 */
+ 0, 0, 0, 0,
+ /* Nexthop */ 192, 168, 0, 1,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 88 + 1, 0, 1, 2, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_AS */
+ 0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */
+ 10, 1, /* 10.1/16 */
+ 88 + 17, 0xff, 0, 0, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_IP */
+ 192, 168, 0, 1, /* IPv4 */
+ 10, 2, 3, /* 10.2.3/17 */
+ },
+ (4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)),
+ SHOULD_ERR,
+ },
+ {
+ "IPv4-VPNv4-plen1-long",
+ "IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs, 1st plen long",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, IANA_SAFI_MPLS_VPN,
+ /* nexthop bytes */ 12,
+ /* RD */ 0, 0, 0, 0, /* RD defined to be 0 */
+ 0, 0, 0, 0,
+ /* Nexthop */ 192, 168, 0, 1,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 88 + 32, 0, 1, 2, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_AS */
+ 0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */
+ 10, 1, /* 10.1/16 */
+ 88 + 17, 0xff, 0, 0, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_IP */
+ 192, 168, 0, 1, /* IPv4 */
+ 10, 2, 3, /* 10.2.3/17 */
+ },
+ (4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)),
+ SHOULD_ERR,
+ },
+ {
+ "IPv4-VPNv4-plenn-long",
+ "IPv4/VPNv4 MP Reach, RD, Nexthop, 3 NLRIs, last plen long",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, IANA_SAFI_MPLS_VPN,
+ /* nexthop bytes */ 12,
+ /* RD */ 0, 0, 0, 0, /* RD defined to be 0 */
+ 0, 0, 0, 0,
+ /* Nexthop */ 192, 168, 0, 1,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 88 + 16, 0, 1, 2, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_AS */
+ 0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */
+ 10, 1, /* 10.1/16 */
+ 88 + 17, 0xff, 0, 0, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_IP */
+ 192, 168, 0, 1, /* IPv4 */
+ 10, 2, 3, /* 10.2.3/17 */
+ 88 + 1, /* bogus */
+ },
+ (4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3) + 1),
+ SHOULD_ERR,
+ },
+ {
+ "IPv4-VPNv4-plenn-short",
+ "IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs, last plen short",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, IANA_SAFI_MPLS_VPN,
+ /* nexthop bytes */ 12,
+ /* RD */ 0, 0, 0, 0, /* RD defined to be 0 */
+ 0, 0, 0, 0,
+ /* Nexthop */ 192, 168, 0, 1,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 88 + 16, 0, 1, 2, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_AS */
+ 0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */
+ 10, 1, /* 10.1/16 */
+ 88 + 2, 0xff, 0, 0, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_IP */
+ 192, 168, 0, 1, /* IPv4 */
+ 10, 2, 3, /* 10.2.3/17 */
+ },
+ (4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)),
+ SHOULD_ERR,
+ },
+ {
+ "IPv4-VPNv4-bogus-rd-type",
+ "IPv4/VPNv4 MP Reach, RD, NH, 2 NLRI, unknown RD in 1st (log, but parse)",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, IANA_SAFI_MPLS_VPN,
+ /* nexthop bytes */ 12,
+ /* RD */ 0, 0, 0, 0, /* RD defined to be 0 */
+ 0, 0, 0, 0,
+ /* Nexthop */ 192, 168, 0, 1,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 88 + 16, 0, 1, 2, /* tag */
+ /* rd, 8 octets */
+ 0xff, 0, /* Bogus RD */
+ 0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */
+ 10, 1, /* 10.1/16 */
+ 88 + 17, 0xff, 0, 0, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_IP */
+ 192, 168, 0, 1, /* IPv4 */
+ 10, 2, 3, /* 10.2.3/17 */
+ },
+ (4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)),
+ SHOULD_PARSE,
+ },
+ {
+ "IPv4-VPNv4-0-nlri",
+ "IPv4/VPNv4 MP Reach, RD, Nexthop, 3 NLRI, 3rd 0 bogus",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, IANA_SAFI_MPLS_VPN,
+ /* nexthop bytes */ 12,
+ /* RD */ 0, 0, 0, 0, /* RD defined to be 0 */
+ 0, 0, 0, 0,
+ /* Nexthop */ 192, 168, 0, 1,
+ /* SNPA (defunct, MBZ) */ 0x0,
+ /* NLRI tuples */ 88 + 16, 0, 1, 2, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_AS */
+ 0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */
+ 10, 1, /* 10.1/16 */
+ 88 + 17, 0xff, 0, 0, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_IP */
+ 192, 168, 0, 1, /* IPv4 */
+ 10, 2, 3, /* 10.2.3/17 */
+ 0 /* 0/0, bogus for vpnv4 ?? */
+ },
+ (4 + 12 + 1 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3) + 1),
+ SHOULD_ERR,
+ },
+
+ /* From bug #385 */
+ {
+ "IPv6-bug",
+ "IPv6, global nexthop, 1 default NLRI",
+ {
+ /* AFI / SAFI */ 0x0,
+ 0x2,
+ 0x1,
+ /* nexthop bytes */ 0x20,
+ /* Nexthop (global) */ 0x20,
+ 0x01,
+ 0x04,
+ 0x70,
+ 0x00,
+ 0x01,
+ 0x00,
+ 0x06,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x01,
+ /* Nexthop (local) */ 0xfe,
+ 0x80,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x02,
+ 0x0c,
+ 0xdb,
+ 0xff,
+ 0xfe,
+ 0xfe,
+ 0xeb,
+ 0x00,
+ /* SNPA (defunct, MBZ) */ 0,
+ /* NLRI tuples */ /* Should have 0 here for ::/0, but
+ dont */
+ },
+ 37,
+ SHOULD_ERR,
+ },
+ {
+ .name = "IPv4",
+ .desc = "IPV4 MP Reach, flowspec, 1 NLRI",
+ .data = {
+ /* AFI / SAFI */ 0x0,
+ AFI_IP,
+ IANA_SAFI_FLOWSPEC,
+ 0x00, /* no NH */
+ 0x00,
+ 0x06, /* FS Length */
+ 0x01, /* FS dest prefix ID */
+ 0x1e, /* IP */
+ 0x1e,
+ 0x28,
+ 0x28,
+ 0x0
+ },
+ .len = 12,
+ .parses = SHOULD_PARSE,
+ },
+ {NULL, NULL, {0}, 0, 0}};
+
+/* MP_UNREACH_NLRI tests */
+static struct test_segment mp_unreach_segments[] = {
+ {
+ "IPv6-unreach",
+ "IPV6 MP Unreach, 1 NLRI",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST,
+ /* NLRI tuples */ 32, 0xff, 0xfe, 0x1,
+ 0x2, /* fffe:102::/32 */
+ },
+ (3 + 5),
+ SHOULD_PARSE,
+ },
+ {
+ "IPv6-unreach2",
+ "IPV6 MP Unreach, 2 NLRIs",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST,
+ /* NLRI tuples */ 32, 0xff, 0xfe, 0x1,
+ 0x2, /* fffe:102::/32 */
+ 64, 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
+ 0x0, 0x2, 0x0, 0x3,
+ },
+ (3 + 5 + 9),
+ SHOULD_PARSE,
+ },
+ {
+ "IPv6-unreach-default",
+ "IPV6 MP Unreach, 2 NLRIs + default",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST,
+ /* NLRI tuples */ 32, 0xff, 0xfe, 0x1,
+ 0x2, /* fffe:102::/32 */
+ 64, 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
+ 0x0, 0x2, 0x0, 0x3, 0x0, /* ::/0 */
+ },
+ (3 + 5 + 9 + 1),
+ SHOULD_PARSE,
+ },
+ {
+ "IPv6-unreach-nlri",
+ "IPV6 MP Unreach, NLRI bitlen overflow",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP6, SAFI_UNICAST,
+ /* NLRI tuples */ 120, 0xff, 0xfe, 0x1,
+ 0x2, /* fffe:102::/32 */
+ 64, 0xff, 0xfe, 0x0, 0x1, /* fffe:1:2:3::/64 */
+ 0x0, 0x2, 0x0, 0x3, 0, /* ::/0 */
+ },
+ (3 + 5 + 9 + 1),
+ SHOULD_ERR,
+ },
+ {
+ "IPv4-unreach",
+ "IPv4 MP Unreach, 2 NLRIs + default",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST,
+ /* NLRI tuples */ 16, 10, 1, /* 10.1/16 */
+ 17, 10, 2, 3, /* 10.2.3/17 */
+ 0, /* 0/0 */
+ },
+ (3 + 3 + 4 + 1),
+ SHOULD_PARSE,
+ },
+ {
+ "IPv4-unreach-nlrilen",
+ "IPv4 MP Unreach, nlri length overflow",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, SAFI_UNICAST,
+ /* NLRI tuples */ 16, 10, 1, /* 10.1/16 */
+ 30, 10, 0, /* 0/0 */
+ },
+ (3 + 3 + 2 + 1),
+ SHOULD_ERR,
+ },
+ {
+ "IPv4-unreach-VPNv4",
+ "IPv4/MPLS-labeled VPN MP Unreach, RD, 3 NLRIs",
+ {
+ /* AFI / SAFI */ 0x0, AFI_IP, IANA_SAFI_MPLS_VPN,
+ /* NLRI tuples */ 88 + 16, 0, 1, 2, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_AS */
+ 0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */
+ 10, 1, /* 10.1/16 */
+ 88 + 17, 0xff, 0, 0, /* tag */
+ /* rd, 8 octets */
+ 0, 0, /* RD_TYPE_IP */
+ 192, 168, 0, 1, /* IPv4 */
+ 10, 2, 3, /* 10.2.3/17 */
+ },
+ (3 + (1 + 3 + 8 + 2) + (1 + 3 + 8 + 3)),
+ SHOULD_PARSE,
+ },
+ {
+ .name = "IPv4",
+ .desc = "IPV4 MP Unreach, flowspec, 1 NLRI",
+ .data = {
+ /* AFI / SAFI */ 0x0,
+ AFI_IP,
+ IANA_SAFI_FLOWSPEC,
+ 0x06, /* FS Length */
+ 0x01, /* FS dest prefix ID */
+ 0x1e, /* IP */
+ 0x1e,
+ 0x28,
+ 0x28,
+ 0x0
+ },
+ .len = 10,
+ .parses = SHOULD_PARSE,
+ },
+ {NULL, NULL, {0}, 0, 0}};
+
+static struct test_segment mp_prefix_sid[] = {
+ {
+ "PREFIX-SID",
+ "PREFIX-SID Test 1",
+ {
+ /* TLV[0] Latel-Index TLV */
+ 0x01, /* Type 0x01:Label-Index */
+ 0x00, 0x07, /* Length */
+ 0x00, /* RESERVED */
+ 0x00, 0x00, /* Flags */
+ 0x00, 0x00, 0x00, 0x02, /* Label Index */
+
+ /* TLV[1] SRGB TLV */
+ 0x03, /* Type 0x03:SRGB */
+ 0x00, 0x08, /* Length */
+ 0x00, 0x00, /* Flags */
+ 0x0a, 0x1b, 0xfe, /* SRGB[0] first label */
+ 0x00, 0x00, 0x0a /* SRBG[0] nb-labels in range */
+ },
+ .len = 21,
+ .parses = SHOULD_PARSE,
+ },
+ {NULL, NULL, { 0 }, 0, 0},
+};
+
+/* nlri_parse indicates 0 on successful parse, and -1 otherwise.
+ * attr_parse indicates BGP_ATTR_PARSE_PROCEED/0 on success,
+ * and BGP_ATTR_PARSE_ERROR/-1 or lower negative ret on err.
+ */
+static void handle_result(struct peer *peer, struct test_segment *t,
+ int parse_ret, int nlri_ret)
+{
+ int oldfailed = failed;
+
+ printf("mp attr parsed?: %s\n", parse_ret ? "no" : "yes");
+ if (!parse_ret)
+ printf("nrli parsed?: %s\n", nlri_ret ? "no" : "yes");
+ printf("should parse?: %s\n", t->parses ? "no" : "yes");
+
+ if ((parse_ret != 0 || nlri_ret != 0) != (t->parses != 0))
+ failed++;
+
+
+ if (tty)
+ printf("%s",
+ (failed > oldfailed) ? VT100_RED "failed!" VT100_RESET
+ : VT100_GREEN "OK" VT100_RESET);
+ else
+ printf("%s", (failed > oldfailed) ? "failed!" : "OK");
+
+ if (failed)
+ printf(" (%u)", failed);
+
+ printf("\n\n");
+}
+
+/* basic parsing test */
+static void parse_test(struct peer *peer, struct test_segment *t, int type)
+{
+ int parse_ret = 0, nlri_ret = 0;
+ struct attr attr = {};
+ struct bgp_nlri nlri = {};
+ struct bgp_attr_parser_args attr_args = {
+ .peer = peer,
+ .length = t->len,
+ .total = 1,
+ .attr = &attr,
+ .type = type,
+ .flags = BGP_ATTR_FLAG_OPTIONAL,
+ .startp = BGP_INPUT_PNT(peer),
+ };
+#define RANDOM_FUZZ 35
+ stream_reset(peer->curr);
+ stream_put(peer->curr, NULL, RANDOM_FUZZ);
+ stream_set_getp(peer->curr, RANDOM_FUZZ);
+
+ stream_write(peer->curr, t->data, t->len);
+
+ printf("%s: %s\n", t->name, t->desc);
+
+ switch (type) {
+ case BGP_ATTR_MP_REACH_NLRI:
+ parse_ret = bgp_mp_reach_parse(&attr_args, &nlri);
+ break;
+ case BGP_ATTR_MP_UNREACH_NLRI:
+ parse_ret = bgp_mp_unreach_parse(&attr_args, &nlri);
+ break;
+ case BGP_ATTR_PREFIX_SID:
+ parse_ret = bgp_attr_prefix_sid(&attr_args);
+ break;
+ default:
+ printf("unknown type");
+ return;
+ }
+ if (!parse_ret) {
+ iana_afi_t pkt_afi;
+ iana_safi_t pkt_safi;
+
+ /* Convert AFI, SAFI to internal values, check. */
+ if (bgp_map_afi_safi_int2iana(nlri.afi, nlri.safi, &pkt_afi,
+ &pkt_safi))
+ assert(0);
+
+ printf("MP: %u(%u)/%u(%u): recv %u, nego %u\n", nlri.afi,
+ pkt_afi, nlri.safi, pkt_safi,
+ peer->afc_recv[nlri.afi][nlri.safi],
+ peer->afc_nego[nlri.afi][nlri.safi]);
+ }
+
+ if (!parse_ret) {
+ if (type == BGP_ATTR_MP_REACH_NLRI)
+ nlri_ret = bgp_nlri_parse(peer, &attr, &nlri, false);
+ else if (type == BGP_ATTR_MP_UNREACH_NLRI)
+ nlri_ret = bgp_nlri_parse(peer, &attr, &nlri, true);
+ }
+ handle_result(peer, t, parse_ret, nlri_ret);
+}
+
+static struct bgp *bgp;
+static as_t asn = 100;
+
+int main(void)
+{
+ struct interface ifp;
+ struct peer *peer;
+ int i, j;
+
+ conf_bgp_debug_neighbor_events = -1UL;
+ conf_bgp_debug_packet = -1UL;
+ conf_bgp_debug_as4 = -1UL;
+ conf_bgp_debug_flowspec = -1UL;
+ term_bgp_debug_neighbor_events = -1UL;
+ term_bgp_debug_packet = -1UL;
+ term_bgp_debug_as4 = -1UL;
+ term_bgp_debug_flowspec = -1UL;
+
+ qobj_init();
+ cmd_init(0);
+ bgp_vty_init();
+ master = event_master_create("test mp attr");
+ bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
+ vrf_init(NULL, NULL, NULL, NULL);
+ bgp_option_set(BGP_OPT_NO_LISTEN);
+ bgp_attr_init();
+
+ if (fileno(stdout) >= 0)
+ tty = isatty(fileno(stdout));
+
+ if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT, NULL,
+ ASNOTATION_PLAIN) < 0)
+ return -1;
+
+ peer = peer_create_accept(bgp);
+ peer->host = (char *)"foo";
+ peer->connection = bgp_peer_connection_new(peer);
+ peer->connection->status = Established;
+ peer->curr = stream_new(BGP_MAX_PACKET_SIZE);
+
+ ifp.ifindex = 0;
+ peer->nexthop.ifp = &ifp;
+
+ for (i = AFI_IP; i < AFI_MAX; i++)
+ for (j = SAFI_UNICAST; j < SAFI_MAX; j++) {
+ peer->afc[i][j] = 1;
+ peer->afc_adv[i][j] = 1;
+ }
+
+ i = 0;
+ while (mp_reach_segments[i].name)
+ parse_test(peer, &mp_reach_segments[i++],
+ BGP_ATTR_MP_REACH_NLRI);
+
+ i = 0;
+ while (mp_unreach_segments[i].name)
+ parse_test(peer, &mp_unreach_segments[i++],
+ BGP_ATTR_MP_UNREACH_NLRI);
+
+ i = 0;
+ while (mp_prefix_sid[i].name)
+ parse_test(peer, &mp_prefix_sid[i++],
+ BGP_ATTR_PREFIX_SID);
+ printf("failures: %d\n", failed);
+ return failed;
+}
diff --git a/tests/bgpd/test_mp_attr.py b/tests/bgpd/test_mp_attr.py
new file mode 100644
index 0000000..d9612bb
--- /dev/null
+++ b/tests/bgpd/test_mp_attr.py
@@ -0,0 +1,49 @@
+import frrtest
+
+
+class TestMpAttr(frrtest.TestMultiOut):
+ program = "./test_mp_attr"
+
+
+TestMpAttr.okfail("IPv6: IPV6 MP Reach, global nexthop, 1 NLRI")
+TestMpAttr.okfail("IPv6-2: IPV6 MP Reach, global nexthop, 2 NLRIs")
+TestMpAttr.okfail("IPv6-default: IPV6 MP Reach, global nexthop, 2 NLRIs + default")
+TestMpAttr.okfail("IPv6-lnh: IPV6 MP Reach, global+local nexthops, 2 NLRIs + default")
+TestMpAttr.okfail("IPv6-nhlen: IPV6 MP Reach, inappropriate nexthop length")
+TestMpAttr.okfail("IPv6-nhlen2: IPV6 MP Reach, invalid nexthop length")
+TestMpAttr.okfail("IPv6-nhlen3: IPV6 MP Reach, nexthop length overflow")
+TestMpAttr.okfail("IPv6-nhlen4: IPV6 MP Reach, nexthop length short")
+TestMpAttr.okfail("IPv6-nlri: IPV6 MP Reach, NLRI bitlen overflow")
+TestMpAttr.okfail("IPv4: IPv4 MP Reach, 2 NLRIs + default")
+TestMpAttr.okfail("IPv4-nhlen: IPv4 MP Reach, nexthop lenth overflow")
+TestMpAttr.okfail("IPv4-nlrilen: IPv4 MP Reach, nlri lenth overflow")
+TestMpAttr.okfail("IPv4-VPNv4: IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs")
+TestMpAttr.okfail(
+ "IPv4-VPNv4-bogus-plen: IPv4/MPLS-labeled VPN MP Reach, RD, Nexthop, NLRI / bogus p'len"
+)
+TestMpAttr.okfail(
+ "IPv4-VPNv4-plen1-short: IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs, 1st plen short"
+)
+TestMpAttr.okfail(
+ "IPv4-VPNv4-plen1-long: IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs, 1st plen long"
+)
+TestMpAttr.okfail(
+ "IPv4-VPNv4-plenn-long: IPv4/VPNv4 MP Reach, RD, Nexthop, 3 NLRIs, last plen long"
+)
+TestMpAttr.okfail(
+ "IPv4-VPNv4-plenn-short: IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs, last plen short"
+)
+TestMpAttr.okfail(
+ "IPv4-VPNv4-bogus-rd-type: IPv4/VPNv4 MP Reach, RD, NH, 2 NLRI, unknown RD in 1st (log, but parse)"
+)
+TestMpAttr.okfail(
+ "IPv4-VPNv4-0-nlri: IPv4/VPNv4 MP Reach, RD, Nexthop, 3 NLRI, 3rd 0 bogus"
+)
+TestMpAttr.okfail("IPv6-bug: IPv6, global nexthop, 1 default NLRI")
+TestMpAttr.okfail("IPv6-unreach: IPV6 MP Unreach, 1 NLRI")
+TestMpAttr.okfail("IPv6-unreach2: IPV6 MP Unreach, 2 NLRIs")
+TestMpAttr.okfail("IPv6-unreach-default: IPV6 MP Unreach, 2 NLRIs + default")
+TestMpAttr.okfail("IPv6-unreach-nlri: IPV6 MP Unreach, NLRI bitlen overflow")
+TestMpAttr.okfail("IPv4-unreach: IPv4 MP Unreach, 2 NLRIs + default")
+TestMpAttr.okfail("IPv4-unreach-nlrilen: IPv4 MP Unreach, nlri length overflow")
+TestMpAttr.okfail("IPv4-unreach-VPNv4: IPv4/MPLS-labeled VPN MP Unreach, RD, 3 NLRIs")
diff --git a/tests/bgpd/test_mpath.c b/tests/bgpd/test_mpath.c
new file mode 100644
index 0000000..ebbe3ac
--- /dev/null
+++ b/tests/bgpd/test_mpath.c
@@ -0,0 +1,482 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * BGP Multipath Unit Test
+ * Copyright (C) 2010 Google Inc.
+ *
+ * This file is part of Quagga
+ */
+
+#include <zebra.h>
+
+#include "qobj.h"
+#include "vty.h"
+#include "stream.h"
+#include "privs.h"
+#include "linklist.h"
+#include "memory.h"
+#include "zclient.h"
+#include "queue.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_mpath.h"
+#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_network.h"
+
+#define VT100_RESET "\x1b[0m"
+#define VT100_RED "\x1b[31m"
+#define VT100_GREEN "\x1b[32m"
+#define VT100_YELLOW "\x1b[33m"
+#define OK VT100_GREEN "OK" VT100_RESET
+#define FAILED VT100_RED "failed" VT100_RESET
+
+#define TEST_PASSED 0
+#define TEST_FAILED -1
+
+#define EXPECT_TRUE(expr, res) \
+ if (!(expr)) { \
+ printf("Test failure in %s line %u: %s\n", __func__, __LINE__, \
+ #expr); \
+ (res) = TEST_FAILED; \
+ }
+
+typedef struct testcase_t__ testcase_t;
+
+typedef int (*test_setup_func)(testcase_t *);
+typedef int (*test_run_func)(testcase_t *);
+typedef int (*test_cleanup_func)(testcase_t *);
+
+struct testcase_t__ {
+ const char *desc;
+ void *test_data;
+ void *verify_data;
+ void *tmp_data;
+ test_setup_func setup;
+ test_run_func run;
+ test_cleanup_func cleanup;
+};
+
+/* need these to link in libbgp */
+struct event_loop *master = NULL;
+extern struct zclient *zclient;
+struct zebra_privs_t bgpd_privs = {
+ .user = NULL,
+ .group = NULL,
+ .vty_group = NULL,
+};
+
+static int tty = 0;
+
+/* Create fake bgp instance */
+static struct bgp *bgp_create_fake(as_t *as, const char *name)
+{
+ struct bgp *bgp;
+ afi_t afi;
+ safi_t safi;
+
+ if ((bgp = XCALLOC(MTYPE_BGP, sizeof(struct bgp))) == NULL)
+ return NULL;
+
+ bgp_lock(bgp);
+ // bgp->peer_self = peer_new (bgp);
+ // bgp->peer_self->host = XSTRDUP (MTYPE_BGP_PEER_HOST, "Static
+ // announcement");
+
+ bgp->peer = list_new();
+ // bgp->peer->cmp = (int (*)(void *, void *)) peer_cmp;
+
+ bgp->group = list_new();
+ // bgp->group->cmp = (int (*)(void *, void *)) peer_group_cmp;
+
+ bgp_evpn_init(bgp);
+ FOREACH_AFI_SAFI (afi, safi) {
+ bgp->route[afi][safi] = bgp_table_init(bgp, afi, safi);
+ bgp->aggregate[afi][safi] = bgp_table_init(bgp, afi, safi);
+ bgp->rib[afi][safi] = bgp_table_init(bgp, afi, safi);
+ bgp->maxpaths[afi][safi].maxpaths_ebgp = MULTIPATH_NUM;
+ bgp->maxpaths[afi][safi].maxpaths_ibgp = MULTIPATH_NUM;
+ }
+
+ bgp_scan_init(bgp);
+ bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF;
+ bgp->default_holdtime = BGP_DEFAULT_HOLDTIME;
+ bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE;
+ bgp->restart_time = BGP_DEFAULT_RESTART_TIME;
+ bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME;
+
+ bgp->as = *as;
+
+ if (name)
+ bgp->name = strdup(name);
+
+ return bgp;
+}
+
+/*=========================================================
+ * Testcase for maximum-paths configuration
+ */
+static int setup_bgp_cfg_maximum_paths(testcase_t *t)
+{
+ as_t asn = 1;
+ t->tmp_data = bgp_create_fake(&asn, NULL);
+ if (!t->tmp_data)
+ return -1;
+ return 0;
+}
+
+static int run_bgp_cfg_maximum_paths(testcase_t *t)
+{
+ afi_t afi;
+ safi_t safi;
+ struct bgp *bgp;
+ int api_result;
+ int test_result = TEST_PASSED;
+
+ bgp = t->tmp_data;
+ FOREACH_AFI_SAFI (afi, safi) {
+ /* test bgp_maximum_paths_set */
+ api_result = bgp_maximum_paths_set(bgp, afi, safi,
+ BGP_PEER_EBGP, 10, 0);
+ EXPECT_TRUE(api_result == 0, test_result);
+ api_result = bgp_maximum_paths_set(bgp, afi, safi,
+ BGP_PEER_IBGP, 10, 0);
+ EXPECT_TRUE(api_result == 0, test_result);
+ EXPECT_TRUE(bgp->maxpaths[afi][safi].maxpaths_ebgp == 10,
+ test_result);
+ EXPECT_TRUE(bgp->maxpaths[afi][safi].maxpaths_ibgp == 10,
+ test_result);
+
+ /* test bgp_maximum_paths_unset */
+ api_result =
+ bgp_maximum_paths_unset(bgp, afi, safi, BGP_PEER_EBGP);
+ EXPECT_TRUE(api_result == 0, test_result);
+ api_result =
+ bgp_maximum_paths_unset(bgp, afi, safi, BGP_PEER_IBGP);
+ EXPECT_TRUE(api_result == 0, test_result);
+ EXPECT_TRUE((bgp->maxpaths[afi][safi].maxpaths_ebgp
+ == MULTIPATH_NUM),
+ test_result);
+ EXPECT_TRUE((bgp->maxpaths[afi][safi].maxpaths_ibgp
+ == MULTIPATH_NUM),
+ test_result);
+ }
+
+ return test_result;
+}
+
+static int cleanup_bgp_cfg_maximum_paths(testcase_t *t)
+{
+ return bgp_delete((struct bgp *)t->tmp_data);
+}
+
+testcase_t test_bgp_cfg_maximum_paths = {
+ .desc = "Test bgp maximum-paths config",
+ .setup = setup_bgp_cfg_maximum_paths,
+ .run = run_bgp_cfg_maximum_paths,
+ .cleanup = cleanup_bgp_cfg_maximum_paths,
+};
+
+/*=========================================================
+ * Testcase for bgp_mp_list
+ */
+struct peer test_mp_list_peer[] = {
+ {.local_as = 1, .as = 2}, {.local_as = 1, .as = 2},
+ {.local_as = 1, .as = 2}, {.local_as = 1, .as = 2},
+ {.local_as = 1, .as = 2},
+};
+int test_mp_list_peer_count = array_size(test_mp_list_peer);
+struct attr test_mp_list_attr[4];
+struct bgp_path_info test_mp_list_info[] = {
+ {.peer = &test_mp_list_peer[0], .attr = &test_mp_list_attr[0]},
+ {.peer = &test_mp_list_peer[1], .attr = &test_mp_list_attr[1]},
+ {.peer = &test_mp_list_peer[2], .attr = &test_mp_list_attr[1]},
+ {.peer = &test_mp_list_peer[3], .attr = &test_mp_list_attr[2]},
+ {.peer = &test_mp_list_peer[4], .attr = &test_mp_list_attr[3]},
+};
+int test_mp_list_info_count = array_size(test_mp_list_info);
+
+static int setup_bgp_mp_list(testcase_t *t)
+{
+ test_mp_list_attr[0].nexthop.s_addr = 0x01010101;
+ test_mp_list_attr[1].nexthop.s_addr = 0x02020202;
+ test_mp_list_attr[2].nexthop.s_addr = 0x03030303;
+ test_mp_list_attr[3].nexthop.s_addr = 0x04040404;
+
+ if ((test_mp_list_peer[0].su_remote = sockunion_str2su("1.1.1.1"))
+ == NULL)
+ return -1;
+ if ((test_mp_list_peer[1].su_remote = sockunion_str2su("2.2.2.2"))
+ == NULL)
+ return -1;
+ if ((test_mp_list_peer[2].su_remote = sockunion_str2su("3.3.3.3"))
+ == NULL)
+ return -1;
+ if ((test_mp_list_peer[3].su_remote = sockunion_str2su("4.4.4.4"))
+ == NULL)
+ return -1;
+ if ((test_mp_list_peer[4].su_remote = sockunion_str2su("5.5.5.5"))
+ == NULL)
+ return -1;
+
+ return 0;
+}
+
+static int run_bgp_mp_list(testcase_t *t)
+{
+ struct list mp_list;
+ struct listnode *mp_node;
+ struct bgp_path_info *info;
+ int i;
+ int test_result = TEST_PASSED;
+ bgp_mp_list_init(&mp_list);
+ EXPECT_TRUE(listcount(&mp_list) == 0, test_result);
+
+ bgp_mp_list_add(&mp_list, &test_mp_list_info[1]);
+ bgp_mp_list_add(&mp_list, &test_mp_list_info[4]);
+ bgp_mp_list_add(&mp_list, &test_mp_list_info[2]);
+ bgp_mp_list_add(&mp_list, &test_mp_list_info[3]);
+ bgp_mp_list_add(&mp_list, &test_mp_list_info[0]);
+
+ for (i = 0, mp_node = mp_list.head; i < test_mp_list_info_count;
+ i++, mp_node = listnextnode(mp_node)) {
+ info = listgetdata(mp_node);
+ info->lock++;
+ EXPECT_TRUE(info == &test_mp_list_info[i], test_result);
+ }
+
+ bgp_mp_list_clear(&mp_list);
+ EXPECT_TRUE(listcount(&mp_list) == 0, test_result);
+
+ return test_result;
+}
+
+static int cleanup_bgp_mp_list(testcase_t *t)
+{
+ int i;
+
+ for (i = 0; i < test_mp_list_peer_count; i++)
+ sockunion_free(test_mp_list_peer[i].su_remote);
+
+ return 0;
+}
+
+testcase_t test_bgp_mp_list = {
+ .desc = "Test bgp_mp_list",
+ .setup = setup_bgp_mp_list,
+ .run = run_bgp_mp_list,
+ .cleanup = cleanup_bgp_mp_list,
+};
+
+/*=========================================================
+ * Testcase for bgp_path_info_mpath_update
+ */
+
+static struct bgp_dest *dest;
+
+static int setup_bgp_path_info_mpath_update(testcase_t *t)
+{
+ int i;
+ struct bgp *bgp;
+ struct bgp_table *rt;
+ struct prefix p;
+ as_t asn = 1;
+
+ t->tmp_data = bgp_create_fake(&asn, NULL);
+ if (!t->tmp_data)
+ return -1;
+
+ bgp = t->tmp_data;
+ rt = bgp->rib[AFI_IP][SAFI_UNICAST];
+
+ if (!rt)
+ return -1;
+
+ str2prefix("42.1.1.0/24", &p);
+ dest = bgp_node_get(rt, &p);
+
+ setup_bgp_mp_list(t);
+ for (i = 0; i < test_mp_list_info_count; i++)
+ bgp_path_info_add(dest, &test_mp_list_info[i]);
+ return 0;
+}
+
+static int run_bgp_path_info_mpath_update(testcase_t *t)
+{
+ struct bgp_path_info *new_best, *old_best, *mpath;
+ struct list mp_list;
+ struct bgp_maxpaths_cfg mp_cfg = {3, 3};
+
+ int test_result = TEST_PASSED;
+ bgp_mp_list_init(&mp_list);
+ bgp_mp_list_add(&mp_list, &test_mp_list_info[4]);
+ bgp_mp_list_add(&mp_list, &test_mp_list_info[3]);
+ bgp_mp_list_add(&mp_list, &test_mp_list_info[0]);
+ bgp_mp_list_add(&mp_list, &test_mp_list_info[1]);
+ new_best = &test_mp_list_info[3];
+ old_best = NULL;
+ bgp_path_info_mpath_update(NULL, dest, new_best, old_best, &mp_list,
+ &mp_cfg);
+ bgp_mp_list_clear(&mp_list);
+ EXPECT_TRUE(bgp_path_info_mpath_count(new_best) == 2, test_result);
+ mpath = bgp_path_info_mpath_first(new_best);
+ EXPECT_TRUE(mpath == &test_mp_list_info[0], test_result);
+ EXPECT_TRUE(CHECK_FLAG(mpath->flags, BGP_PATH_MULTIPATH), test_result);
+ mpath = bgp_path_info_mpath_next(mpath);
+ EXPECT_TRUE(mpath == &test_mp_list_info[1], test_result);
+ EXPECT_TRUE(CHECK_FLAG(mpath->flags, BGP_PATH_MULTIPATH), test_result);
+
+ bgp_mp_list_add(&mp_list, &test_mp_list_info[0]);
+ bgp_mp_list_add(&mp_list, &test_mp_list_info[1]);
+ new_best = &test_mp_list_info[0];
+ old_best = &test_mp_list_info[3];
+ bgp_path_info_mpath_update(NULL, dest, new_best, old_best, &mp_list,
+ &mp_cfg);
+ bgp_mp_list_clear(&mp_list);
+ EXPECT_TRUE(bgp_path_info_mpath_count(new_best) == 1, test_result);
+ mpath = bgp_path_info_mpath_first(new_best);
+ EXPECT_TRUE(mpath == &test_mp_list_info[1], test_result);
+ EXPECT_TRUE(CHECK_FLAG(mpath->flags, BGP_PATH_MULTIPATH), test_result);
+ EXPECT_TRUE(!CHECK_FLAG(test_mp_list_info[0].flags, BGP_PATH_MULTIPATH),
+ test_result);
+
+ return test_result;
+}
+
+static int cleanup_bgp_path_info_mpath_update(testcase_t *t)
+{
+ int i;
+
+ for (i = 0; i < test_mp_list_peer_count; i++)
+ sockunion_free(test_mp_list_peer[i].su_remote);
+
+ return bgp_delete((struct bgp *)t->tmp_data);
+}
+
+testcase_t test_bgp_path_info_mpath_update = {
+ .desc = "Test bgp_path_info_mpath_update",
+ .setup = setup_bgp_path_info_mpath_update,
+ .run = run_bgp_path_info_mpath_update,
+ .cleanup = cleanup_bgp_path_info_mpath_update,
+};
+
+/*=========================================================
+ * Set up testcase vector
+ */
+testcase_t *all_tests[] = {
+ &test_bgp_cfg_maximum_paths, &test_bgp_mp_list,
+ &test_bgp_path_info_mpath_update,
+};
+
+int all_tests_count = array_size(all_tests);
+
+/*=========================================================
+ * Test Driver Functions
+ */
+static int global_test_init(void)
+{
+ qobj_init();
+ master = event_master_create(NULL);
+ zclient = zclient_new(master, &zclient_options_default, NULL, 0);
+ bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
+ vrf_init(NULL, NULL, NULL, NULL);
+ bgp_option_set(BGP_OPT_NO_LISTEN);
+
+ if (fileno(stdout) >= 0)
+ tty = isatty(fileno(stdout));
+ return 0;
+}
+
+static int global_test_cleanup(void)
+{
+ if (zclient != NULL)
+ zclient_free(zclient);
+ event_master_free(master);
+ return 0;
+}
+
+static void display_result(testcase_t *test, int result)
+{
+ if (tty)
+ printf("%s: %s\n", test->desc,
+ result == TEST_PASSED ? OK : FAILED);
+ else
+ printf("%s: %s\n", test->desc,
+ result == TEST_PASSED ? "OK" : "FAILED");
+}
+
+static int setup_test(testcase_t *t)
+{
+ int res = 0;
+ if (t->setup)
+ res = t->setup(t);
+ return res;
+}
+
+static int cleanup_test(testcase_t *t)
+{
+ int res = 0;
+ if (t->cleanup)
+ res = t->cleanup(t);
+ return res;
+}
+
+static void run_tests(testcase_t *tests[], int num_tests, int *pass_count,
+ int *fail_count)
+{
+ int test_index, result;
+ testcase_t *cur_test;
+
+ *pass_count = *fail_count = 0;
+
+ for (test_index = 0; test_index < num_tests; test_index++) {
+ cur_test = tests[test_index];
+ if (!cur_test->desc) {
+ printf("error: test %d has no description!\n",
+ test_index);
+ continue;
+ }
+ if (!cur_test->run) {
+ printf("error: test %s has no run function!\n",
+ cur_test->desc);
+ continue;
+ }
+ if (setup_test(cur_test) != 0) {
+ printf("error: setup failed for test %s\n",
+ cur_test->desc);
+ continue;
+ }
+ result = cur_test->run(cur_test);
+ if (result == TEST_PASSED)
+ *pass_count += 1;
+ else
+ *fail_count += 1;
+ display_result(cur_test, result);
+ if (cleanup_test(cur_test) != 0) {
+ printf("error: cleanup failed for test %s\n",
+ cur_test->desc);
+ continue;
+ }
+ }
+}
+
+int main(void)
+{
+ int pass_count, fail_count;
+ time_t cur_time;
+ char buf[32];
+
+ time(&cur_time);
+ printf("BGP Multipath Tests Run at %s", ctime_r(&cur_time, buf));
+ if (global_test_init() != 0) {
+ printf("Global init failed. Terminating.\n");
+ exit(1);
+ }
+ run_tests(all_tests, all_tests_count, &pass_count, &fail_count);
+ global_test_cleanup();
+ printf("Total pass/fail: %d/%d\n", pass_count, fail_count);
+ return fail_count;
+}
diff --git a/tests/bgpd/test_mpath.py b/tests/bgpd/test_mpath.py
new file mode 100644
index 0000000..582fd25
--- /dev/null
+++ b/tests/bgpd/test_mpath.py
@@ -0,0 +1,10 @@
+import frrtest
+
+
+class TestMpath(frrtest.TestMultiOut):
+ program = "./test_mpath"
+
+
+TestMpath.okfail("bgp maximum-paths config")
+TestMpath.okfail("bgp_mp_list")
+TestMpath.okfail("bgp_path_info_mpath_update")
diff --git a/tests/bgpd/test_packet.c b/tests/bgpd/test_packet.c
new file mode 100644
index 0000000..a83276b
--- /dev/null
+++ b/tests/bgpd/test_packet.c
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2017 Cumulus Networks Inc.
+ * Donald Sharp
+ *
+ * This file is part of FRR
+ */
+
+#include <zebra.h>
+
+#include "qobj.h"
+#include "vty.h"
+#include "stream.h"
+#include "privs.h"
+#include "memory.h"
+#include "queue.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_network.h"
+
+/* need these to link in libbgp */
+struct zebra_privs_t bgpd_privs = {};
+struct event_loop *master = NULL;
+
+static struct bgp *bgp;
+static as_t asn = 100;
+
+extern int bgp_read_packet(struct peer *peer);
+
+/*
+ * This file is intended to be used as input for some sort of
+ * fuzzer. Specifically I had afl in mind when I wrote
+ * this code.
+ */
+int main(int argc, char *argv[])
+{
+ struct peer *peer;
+ int i, j;
+ struct event t;
+
+ qobj_init();
+ bgp_attr_init();
+ master = event_master_create(NULL);
+ bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
+ vrf_init(NULL, NULL, NULL, NULL);
+ bgp_option_set(BGP_OPT_NO_LISTEN);
+
+ if (bgp_get(&bgp, &asn, NULL, BGP_INSTANCE_TYPE_DEFAULT, NULL,
+ ASNOTATION_PLAIN) < 0)
+ return -1;
+
+ peer = peer_create_accept(bgp);
+ peer->host = (char *)"foo";
+
+ for (i = AFI_IP; i < AFI_MAX; i++)
+ for (j = SAFI_UNICAST; j < SAFI_MAX; j++) {
+ peer->afc[i][j] = 1;
+ peer->afc_adv[i][j] = 1;
+ }
+
+ SET_FLAG(peer->cap, PEER_CAP_DYNAMIC_ADV);
+ peer->connection = bgp_peer_connection_new(peer);
+ peer->connection->status = Established;
+
+ peer->connection->fd = open(argv[1], O_RDONLY | O_NONBLOCK);
+ t.arg = peer;
+ peer->connection->t_read = &t;
+
+ // printf("bgp_read_packet returns: %d\n", bgp_read(&t));
+}
diff --git a/tests/bgpd/test_peer_attr.c b/tests/bgpd/test_peer_attr.c
new file mode 100644
index 0000000..bc6eba9
--- /dev/null
+++ b/tests/bgpd/test_peer_attr.c
@@ -0,0 +1,1491 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * BGP Peer Attribute Unit Tests
+ * Copyright (C) 2018 Pascal Mathis
+ */
+#include <zebra.h>
+
+#include "memory.h"
+#include "plist.h"
+#include "printfrr.h"
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_regex.h"
+#include "bgpd/bgp_clist.h"
+#include "bgpd/bgp_dump.h"
+#include "bgpd/bgp_filter.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_network.h"
+
+#ifdef ENABLE_BGP_VNC
+#include "bgpd/rfapi/rfapi_backend.h"
+#endif
+
+#define OUT_SYMBOL_INFO "\u25ba"
+#define OUT_SYMBOL_OK "\u2714"
+#define OUT_SYMBOL_NOK "\u2716"
+
+#define TEST_ASSERT(T, C) \
+ do { \
+ if ((T)->state != TEST_SUCCESS || (C)) \
+ break; \
+ (T)->state = TEST_ASSERT_ERROR; \
+ (T)->error = \
+ asprintfrr(MTYPE_TMP, "assertion failed: %s (%s:%d)", \
+ (#C), __FILE__, __LINE__); \
+ } while (0)
+
+#define TEST_ASSERT_EQ(T, A, B) \
+ do { \
+ if ((T)->state != TEST_SUCCESS || ((A) == (B))) \
+ break; \
+ (T)->state = TEST_ASSERT_ERROR; \
+ (T)->error = asprintfrr( \
+ MTYPE_TMP, \
+ "assertion failed: %s[%lld] == [%lld]%s (%s:%d)", \
+ (#A), (long long)(A), (long long)(B), (#B), __FILE__, \
+ __LINE__); \
+ } while (0)
+
+#define TEST_HANDLER_MAX 5
+#define TEST_HANDLER(name) _test_handler_##name
+#define TEST_HANDLER_DECL(name) \
+ static void _test_handler_##name( \
+ struct test *test, struct test_peer_attr *pa, \
+ struct peer *peer, struct peer *group, bool peer_set, \
+ bool group_set)
+
+#define TEST_ATTR_HANDLER_DECL(name, attr, pval, gval) \
+ TEST_HANDLER_DECL(name) \
+ { \
+ if (peer_set) \
+ TEST_ASSERT_EQ(test, peer->attr, (pval)); \
+ else if (peer_group_active(peer) && group_set) \
+ TEST_ASSERT_EQ(test, peer->attr, (gval)); \
+ if (group_set) \
+ TEST_ASSERT_EQ(test, group->attr, (gval)); \
+ } \
+ TEST_HANDLER_DECL(name)
+
+#define TEST_STR_ATTR_HANDLER_DECL(name, attr, pval, gval) \
+ TEST_HANDLER_DECL(name) \
+ { \
+ if (peer_set) { \
+ TEST_ASSERT(test, peer->attr != NULL); \
+ TEST_ASSERT(test, !strcmp(peer->attr, (pval))); \
+ } else if (peer_group_active(peer) && group_set) { \
+ TEST_ASSERT(test, peer->attr != NULL); \
+ TEST_ASSERT(test, !strcmp(peer->attr, (gval))); \
+ } \
+ if (group_set) { \
+ TEST_ASSERT(test, group->attr != NULL); \
+ TEST_ASSERT(test, !strcmp(group->attr, (gval))); \
+ } \
+ } \
+ TEST_HANDLER_DECL(name)
+
+#define TEST_SU_ATTR_HANDLER_DECL(name, attr, pval, gval) \
+ TEST_HANDLER_DECL(name) \
+ { \
+ union sockunion su; \
+ if (peer_set) { \
+ str2sockunion(pval, &su); \
+ TEST_ASSERT(test, !sockunion_cmp(peer->attr, &su)); \
+ } else if (peer_group_active(peer) && group_set) { \
+ str2sockunion(gval, &su); \
+ TEST_ASSERT(test, !sockunion_cmp(group->attr, &su)); \
+ } \
+ if (group_set) { \
+ str2sockunion(gval, &su); \
+ TEST_ASSERT(test, !sockunion_cmp(group->attr, &su)); \
+ } \
+ } \
+ TEST_HANDLER_DECL(name)
+
+/* Required variables to link in libbgp */
+struct zebra_privs_t bgpd_privs = {0};
+struct event_loop *master;
+
+enum test_state {
+ TEST_SUCCESS,
+ TEST_SKIPPING,
+ TEST_COMMAND_ERROR,
+ TEST_CONFIG_ERROR,
+ TEST_ASSERT_ERROR,
+ TEST_CUSTOM_ERROR,
+ TEST_INTERNAL_ERROR,
+};
+
+enum test_peer_attr_type {
+ PEER_AT_AF_FLAG = 0,
+ PEER_AT_AF_FILTER = 1,
+ PEER_AT_AF_CUSTOM = 2,
+ PEER_AT_GLOBAL_FLAG = 3,
+ PEER_AT_GLOBAL_CUSTOM = 4
+};
+
+struct test {
+ enum test_state state;
+ char *desc;
+ char *error;
+ struct list *log;
+
+ struct vty *vty;
+ struct bgp *bgp;
+ struct peer *peer;
+ struct peer_group *group;
+
+ struct {
+ bool use_ibgp;
+ bool use_iface_peer;
+ } o;
+};
+
+struct test_config {
+ int local_asn;
+ int peer_asn;
+ const char *peer_address;
+ const char *peer_interface;
+ const char *peer_group;
+};
+
+struct test_peer_family {
+ afi_t afi;
+ safi_t safi;
+};
+
+struct test_peer_attr {
+ const char *cmd;
+ const char *peer_cmd;
+ const char *group_cmd;
+
+ enum test_peer_attr_type type;
+ union {
+ uint64_t flag;
+ struct {
+ uint64_t flag;
+ size_t direct;
+ } filter;
+ } u;
+ struct {
+ bool invert_peer;
+ bool invert_group;
+ bool use_ibgp;
+ bool use_iface_peer;
+ bool skip_xfer_cases;
+ } o;
+
+ afi_t afi;
+ safi_t safi;
+ struct test_peer_family families[AFI_MAX * SAFI_MAX];
+
+ void (*handlers[TEST_HANDLER_MAX])(struct test *test,
+ struct test_peer_attr *pa,
+ struct peer *peer,
+ struct peer *group, bool peer_set,
+ bool group_set);
+};
+
+static struct test_config cfg = {
+ .local_asn = 100,
+ .peer_asn = 200,
+ .peer_address = "1.1.1.1",
+ .peer_interface = "IP-TEST",
+ .peer_group = "PG-TEST",
+};
+
+static struct test_peer_family test_default_families[] = {
+ {.afi = AFI_IP, .safi = SAFI_UNICAST},
+ {.afi = AFI_IP, .safi = SAFI_MULTICAST},
+ {.afi = AFI_IP6, .safi = SAFI_UNICAST},
+ {.afi = AFI_IP6, .safi = SAFI_MULTICAST},
+};
+
+TEST_ATTR_HANDLER_DECL(advertisement_interval, v_routeadv, 10, 20);
+TEST_STR_ATTR_HANDLER_DECL(password, password, "FRR-Peer", "FRR-Group");
+TEST_ATTR_HANDLER_DECL(local_as, change_local_as, 1, 2);
+TEST_ATTR_HANDLER_DECL(timers_1, keepalive, 10, 20);
+TEST_ATTR_HANDLER_DECL(timers_2, holdtime, 30, 60);
+TEST_ATTR_HANDLER_DECL(addpath_types, addpath_type[pa->afi][pa->safi],
+ BGP_ADDPATH_ALL, BGP_ADDPATH_BEST_PER_AS);
+TEST_SU_ATTR_HANDLER_DECL(update_source_su, update_source, "255.255.255.1",
+ "255.255.255.2");
+TEST_STR_ATTR_HANDLER_DECL(update_source_if, update_if, "IF-PEER", "IF-GROUP");
+
+TEST_ATTR_HANDLER_DECL(allowas_in, allowas_in[pa->afi][pa->safi], 1, 2);
+TEST_STR_ATTR_HANDLER_DECL(default_originate_route_map,
+ default_rmap[pa->afi][pa->safi].name, "RM-PEER",
+ "RM-GROUP");
+TEST_STR_ATTR_HANDLER_DECL(
+ distribute_list,
+ filter[pa->afi][pa->safi].dlist[pa->u.filter.direct].name, "DL-PEER",
+ "DL-GROUP");
+TEST_STR_ATTR_HANDLER_DECL(
+ filter_list, filter[pa->afi][pa->safi].aslist[pa->u.filter.direct].name,
+ "FL-PEER", "FL-GROUP");
+TEST_ATTR_HANDLER_DECL(maximum_prefix, pmax[pa->afi][pa->safi], 10, 20);
+TEST_ATTR_HANDLER_DECL(maximum_prefix_threshold,
+ pmax_threshold[pa->afi][pa->safi], 1, 2);
+TEST_ATTR_HANDLER_DECL(maximum_prefix_restart, pmax_restart[pa->afi][pa->safi],
+ 100, 200);
+TEST_STR_ATTR_HANDLER_DECL(
+ prefix_list, filter[pa->afi][pa->safi].plist[pa->u.filter.direct].name,
+ "PL-PEER", "PL-GROUP");
+TEST_STR_ATTR_HANDLER_DECL(
+ route_map, filter[pa->afi][pa->safi].map[pa->u.filter.direct].name,
+ "RM-PEER", "RM-GROUP");
+TEST_STR_ATTR_HANDLER_DECL(unsuppress_map, filter[pa->afi][pa->safi].usmap.name,
+ "UM-PEER", "UM-GROUP");
+TEST_ATTR_HANDLER_DECL(weight, weight[pa->afi][pa->safi], 100, 200);
+
+/* clang-format off */
+static struct test_peer_attr test_peer_attrs[] = {
+ /* Peer Attributes */
+ {
+ .cmd = "advertisement-interval",
+ .peer_cmd = "advertisement-interval 10",
+ .group_cmd = "advertisement-interval 20",
+ .u.flag = PEER_FLAG_ROUTEADV,
+ .type = PEER_AT_GLOBAL_FLAG,
+ .handlers[0] = TEST_HANDLER(advertisement_interval),
+ },
+ {
+ .cmd = "capability dynamic",
+ .u.flag = PEER_FLAG_DYNAMIC_CAPABILITY,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "capability extended-nexthop",
+ .u.flag = PEER_FLAG_CAPABILITY_ENHE,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "capability extended-nexthop",
+ .u.flag = PEER_FLAG_CAPABILITY_ENHE,
+ .type = PEER_AT_GLOBAL_FLAG,
+ .o.invert_peer = true,
+ .o.use_iface_peer = true,
+ },
+ {
+ .cmd = "capability software-version",
+ .u.flag = PEER_FLAG_CAPABILITY_SOFT_VERSION,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "capability software-version",
+ .u.flag = PEER_FLAG_CAPABILITY_SOFT_VERSION,
+ .type = PEER_AT_GLOBAL_FLAG,
+ .o.invert_peer = true,
+ .o.use_iface_peer = true,
+ },
+ {
+ .cmd = "description",
+ .peer_cmd = "description FRR Peer",
+ .group_cmd = "description FRR Group",
+ .type = PEER_AT_GLOBAL_CUSTOM,
+ },
+ {
+ .cmd = "disable-connected-check",
+ .u.flag = PEER_FLAG_DISABLE_CONNECTED_CHECK,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "dont-capability-negotiate",
+ .u.flag = PEER_FLAG_DONT_CAPABILITY,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "enforce-first-as",
+ .u.flag = PEER_FLAG_ENFORCE_FIRST_AS,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "local-as",
+ .peer_cmd = "local-as 1",
+ .group_cmd = "local-as 2",
+ .u.flag = PEER_FLAG_LOCAL_AS,
+ .type = PEER_AT_GLOBAL_FLAG,
+ .handlers[0] = TEST_HANDLER(local_as),
+ },
+ {
+ .cmd = "local-as 1 no-prepend",
+ .u.flag = PEER_FLAG_LOCAL_AS | PEER_FLAG_LOCAL_AS_NO_PREPEND,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "local-as 1 no-prepend replace-as",
+ .u.flag = PEER_FLAG_LOCAL_AS | PEER_FLAG_LOCAL_AS_REPLACE_AS,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "override-capability",
+ .u.flag = PEER_FLAG_OVERRIDE_CAPABILITY,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "passive",
+ .u.flag = PEER_FLAG_PASSIVE,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "password",
+ .peer_cmd = "password FRR-Peer",
+ .group_cmd = "password FRR-Group",
+ .u.flag = PEER_FLAG_PASSWORD,
+ .type = PEER_AT_GLOBAL_FLAG,
+ .handlers[0] = TEST_HANDLER(password),
+ },
+ {
+ .cmd = "shutdown",
+ .u.flag = PEER_FLAG_SHUTDOWN,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "strict-capability-match",
+ .u.flag = PEER_FLAG_STRICT_CAP_MATCH,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "timers",
+ .peer_cmd = "timers 10 30",
+ .group_cmd = "timers 20 60",
+ .u.flag = PEER_FLAG_TIMER,
+ .type = PEER_AT_GLOBAL_FLAG,
+ .handlers[0] = TEST_HANDLER(timers_1),
+ .handlers[1] = TEST_HANDLER(timers_2),
+ },
+ {
+ .cmd = "timers connect",
+ .peer_cmd = "timers connect 10",
+ .group_cmd = "timers connect 20",
+ .u.flag = PEER_FLAG_TIMER_CONNECT,
+ .type = PEER_AT_GLOBAL_FLAG,
+ },
+ {
+ .cmd = "update-source",
+ .peer_cmd = "update-source 255.255.255.1",
+ .group_cmd = "update-source 255.255.255.2",
+ .u.flag = PEER_FLAG_UPDATE_SOURCE,
+ .type = PEER_AT_GLOBAL_FLAG,
+ .handlers[0] = TEST_HANDLER(update_source_su),
+ },
+ {
+ .cmd = "update-source",
+ .peer_cmd = "update-source IF-PEER",
+ .group_cmd = "update-source IF-GROUP",
+ .u.flag = PEER_FLAG_UPDATE_SOURCE,
+ .type = PEER_AT_GLOBAL_FLAG,
+ .handlers[0] = TEST_HANDLER(update_source_if),
+ },
+
+ /* Address Family Attributes */
+ {
+ .cmd = "addpath",
+ .peer_cmd = "addpath-tx-all-paths",
+ .group_cmd = "addpath-tx-bestpath-per-AS",
+ .type = PEER_AT_AF_CUSTOM,
+ .handlers[0] = TEST_HANDLER(addpath_types),
+ },
+ {
+ .cmd = "allowas-in",
+ .peer_cmd = "allowas-in 1",
+ .group_cmd = "allowas-in 2",
+ .u.flag = PEER_FLAG_ALLOWAS_IN,
+ .handlers[0] = TEST_HANDLER(allowas_in),
+ },
+ {
+ .cmd = "allowas-in origin",
+ .u.flag = PEER_FLAG_ALLOWAS_IN_ORIGIN,
+ },
+ {
+ .cmd = "as-override",
+ .u.flag = PEER_FLAG_AS_OVERRIDE,
+ },
+ {
+ .cmd = "attribute-unchanged as-path",
+ .u.flag = PEER_FLAG_AS_PATH_UNCHANGED,
+ },
+ {
+ .cmd = "attribute-unchanged next-hop",
+ .u.flag = PEER_FLAG_NEXTHOP_UNCHANGED,
+ },
+ {
+ .cmd = "attribute-unchanged med",
+ .u.flag = PEER_FLAG_MED_UNCHANGED,
+ },
+ {
+ .cmd = "attribute-unchanged as-path next-hop",
+ .u.flag = PEER_FLAG_AS_PATH_UNCHANGED
+ | PEER_FLAG_NEXTHOP_UNCHANGED,
+ },
+ {
+ .cmd = "attribute-unchanged as-path med",
+ .u.flag = PEER_FLAG_AS_PATH_UNCHANGED
+ | PEER_FLAG_MED_UNCHANGED,
+ },
+ {
+ .cmd = "attribute-unchanged as-path next-hop med",
+ .u.flag = PEER_FLAG_AS_PATH_UNCHANGED
+ | PEER_FLAG_NEXTHOP_UNCHANGED
+ | PEER_FLAG_MED_UNCHANGED,
+ },
+ {
+ .cmd = "capability orf prefix-list send",
+ .u.flag = PEER_FLAG_ORF_PREFIX_SM,
+ },
+ {
+ .cmd = "capability orf prefix-list receive",
+ .u.flag = PEER_FLAG_ORF_PREFIX_RM,
+ },
+ {
+ .cmd = "capability orf prefix-list both",
+ .u.flag = PEER_FLAG_ORF_PREFIX_SM | PEER_FLAG_ORF_PREFIX_RM,
+ },
+ {
+ .cmd = "default-originate",
+ .u.flag = PEER_FLAG_DEFAULT_ORIGINATE,
+ },
+ {
+ .cmd = "default-originate route-map",
+ .peer_cmd = "default-originate route-map RM-PEER",
+ .group_cmd = "default-originate route-map RM-GROUP",
+ .u.flag = PEER_FLAG_DEFAULT_ORIGINATE,
+ .handlers[0] = TEST_HANDLER(default_originate_route_map),
+ },
+ {
+ .cmd = "distribute-list",
+ .peer_cmd = "distribute-list DL-PEER in",
+ .group_cmd = "distribute-list DL-GROUP in",
+ .type = PEER_AT_AF_FILTER,
+ .u.filter.flag = PEER_FT_DISTRIBUTE_LIST,
+ .u.filter.direct = FILTER_IN,
+ .handlers[0] = TEST_HANDLER(distribute_list),
+ },
+ {
+ .cmd = "distribute-list",
+ .peer_cmd = "distribute-list DL-PEER out",
+ .group_cmd = "distribute-list DL-GROUP out",
+ .type = PEER_AT_AF_FILTER,
+ .u.filter.flag = PEER_FT_DISTRIBUTE_LIST,
+ .u.filter.direct = FILTER_OUT,
+ .handlers[0] = TEST_HANDLER(distribute_list),
+ },
+ {
+ .cmd = "filter-list",
+ .peer_cmd = "filter-list FL-PEER in",
+ .group_cmd = "filter-list FL-GROUP in",
+ .type = PEER_AT_AF_FILTER,
+ .u.filter.flag = PEER_FT_FILTER_LIST,
+ .u.filter.direct = FILTER_IN,
+ .handlers[0] = TEST_HANDLER(filter_list),
+ },
+ {
+ .cmd = "filter-list",
+ .peer_cmd = "filter-list FL-PEER out",
+ .group_cmd = "filter-list FL-GROUP out",
+ .type = PEER_AT_AF_FILTER,
+ .u.filter.flag = PEER_FT_FILTER_LIST,
+ .u.filter.direct = FILTER_OUT,
+ .handlers[0] = TEST_HANDLER(filter_list),
+ },
+ {
+ .cmd = "maximum-prefix",
+ .peer_cmd = "maximum-prefix 10",
+ .group_cmd = "maximum-prefix 20",
+ .u.flag = PEER_FLAG_MAX_PREFIX,
+ .handlers[0] = TEST_HANDLER(maximum_prefix),
+ },
+ {
+ .cmd = "maximum-prefix",
+ .peer_cmd = "maximum-prefix 10 restart 100",
+ .group_cmd = "maximum-prefix 20 restart 200",
+ .u.flag = PEER_FLAG_MAX_PREFIX,
+ .handlers[0] = TEST_HANDLER(maximum_prefix),
+ .handlers[1] = TEST_HANDLER(maximum_prefix_restart),
+ },
+ {
+ .cmd = "maximum-prefix",
+ .peer_cmd = "maximum-prefix 10 1 restart 100",
+ .group_cmd = "maximum-prefix 20 2 restart 200",
+ .u.flag = PEER_FLAG_MAX_PREFIX,
+ .handlers[0] = TEST_HANDLER(maximum_prefix),
+ .handlers[1] = TEST_HANDLER(maximum_prefix_threshold),
+ .handlers[2] = TEST_HANDLER(maximum_prefix_restart),
+ },
+ {
+ .cmd = "maximum-prefix",
+ .peer_cmd = "maximum-prefix 10 warning-only",
+ .group_cmd = "maximum-prefix 20 warning-only",
+ .u.flag = PEER_FLAG_MAX_PREFIX | PEER_FLAG_MAX_PREFIX_WARNING,
+ .handlers[0] = TEST_HANDLER(maximum_prefix),
+ },
+ {
+ .cmd = "maximum-prefix",
+ .peer_cmd = "maximum-prefix 10 1 warning-only",
+ .group_cmd = "maximum-prefix 20 2 warning-only",
+ .u.flag = PEER_FLAG_MAX_PREFIX | PEER_FLAG_MAX_PREFIX_WARNING,
+ .handlers[0] = TEST_HANDLER(maximum_prefix),
+ .handlers[1] = TEST_HANDLER(maximum_prefix_threshold),
+ },
+ {
+ .cmd = "next-hop-self",
+ .u.flag = PEER_FLAG_NEXTHOP_SELF,
+ },
+ {
+ .cmd = "next-hop-self force",
+ .u.flag = PEER_FLAG_FORCE_NEXTHOP_SELF,
+ },
+ {
+ .cmd = "prefix-list",
+ .peer_cmd = "prefix-list PL-PEER in",
+ .group_cmd = "prefix-list PL-GROUP in",
+ .type = PEER_AT_AF_FILTER,
+ .u.filter.flag = PEER_FT_PREFIX_LIST,
+ .u.filter.direct = FILTER_IN,
+ .handlers[0] = TEST_HANDLER(prefix_list),
+ },
+ {
+ .cmd = "prefix-list",
+ .peer_cmd = "prefix-list PL-PEER out",
+ .group_cmd = "prefix-list PL-GROUP out",
+ .type = PEER_AT_AF_FILTER,
+ .u.filter.flag = PEER_FT_PREFIX_LIST,
+ .u.filter.direct = FILTER_OUT,
+ .handlers[0] = TEST_HANDLER(prefix_list),
+ },
+ {
+ .cmd = "remove-private-AS",
+ .u.flag = PEER_FLAG_REMOVE_PRIVATE_AS,
+ },
+ {
+ .cmd = "remove-private-AS all",
+ .u.flag = PEER_FLAG_REMOVE_PRIVATE_AS
+ | PEER_FLAG_REMOVE_PRIVATE_AS_ALL,
+ },
+ {
+ .cmd = "remove-private-AS replace-AS",
+ .u.flag = PEER_FLAG_REMOVE_PRIVATE_AS
+ | PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE,
+ },
+ {
+ .cmd = "remove-private-AS all replace-AS",
+ .u.flag = PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE,
+ },
+ {
+ .cmd = "route-map",
+ .peer_cmd = "route-map RM-PEER in",
+ .group_cmd = "route-map RM-GROUP in",
+ .type = PEER_AT_AF_FILTER,
+ .u.filter.flag = PEER_FT_ROUTE_MAP,
+ .u.filter.direct = FILTER_IN,
+ .handlers[0] = TEST_HANDLER(route_map),
+ },
+ {
+ .cmd = "route-map",
+ .peer_cmd = "route-map RM-PEER out",
+ .group_cmd = "route-map RM-GROUP out",
+ .type = PEER_AT_AF_FILTER,
+ .u.filter.flag = PEER_FT_ROUTE_MAP,
+ .u.filter.direct = FILTER_OUT,
+ .handlers[0] = TEST_HANDLER(route_map),
+ },
+ {
+ .cmd = "route-reflector-client",
+ .u.flag = PEER_FLAG_REFLECTOR_CLIENT,
+ .o.use_ibgp = true,
+ .o.skip_xfer_cases = true,
+ },
+ {
+ .cmd = "route-server-client",
+ .u.flag = PEER_FLAG_RSERVER_CLIENT,
+ },
+ {
+ .cmd = "send-community",
+ .u.flag = PEER_FLAG_SEND_COMMUNITY,
+ .o.invert_peer = true,
+ .o.invert_group = true,
+ },
+ {
+ .cmd = "send-community extended",
+ .u.flag = PEER_FLAG_SEND_EXT_COMMUNITY,
+ .o.invert_peer = true,
+ .o.invert_group = true,
+ },
+ {
+ .cmd = "send-community large",
+ .u.flag = PEER_FLAG_SEND_LARGE_COMMUNITY,
+ .o.invert_peer = true,
+ .o.invert_group = true,
+ },
+ {
+ .cmd = "soft-reconfiguration inbound",
+ .u.flag = PEER_FLAG_SOFT_RECONFIG,
+ },
+ {
+ .cmd = "unsuppress-map",
+ .peer_cmd = "unsuppress-map UM-PEER",
+ .group_cmd = "unsuppress-map UM-GROUP",
+ .type = PEER_AT_AF_FILTER,
+ .u.filter.flag = PEER_FT_UNSUPPRESS_MAP,
+ .u.filter.direct = 0,
+ .handlers[0] = TEST_HANDLER(unsuppress_map),
+ },
+ {
+ .cmd = "weight",
+ .peer_cmd = "weight 100",
+ .group_cmd = "weight 200",
+ .u.flag = PEER_FLAG_WEIGHT,
+ .handlers[0] = TEST_HANDLER(weight),
+ },
+ {
+ .cmd = "accept-own",
+ .peer_cmd = "accept-own",
+ .group_cmd = "accept-own",
+ .families[0] = {.afi = AFI_IP, .safi = SAFI_MPLS_VPN},
+ .families[1] = {.afi = AFI_IP6, .safi = SAFI_MPLS_VPN},
+ .u.flag = PEER_FLAG_ACCEPT_OWN,
+ },
+ {NULL}
+};
+/* clang-format on */
+
+static const char *str_from_afi(afi_t afi)
+{
+ switch (afi) {
+ case AFI_IP:
+ return "ipv4";
+ case AFI_IP6:
+ return "ipv6";
+ case AFI_L2VPN:
+ return "l2vpn";
+ case AFI_MAX:
+ case AFI_UNSPEC:
+ return "bad-value";
+ }
+
+ assert(!"Reached end of function we should never reach");
+}
+
+static const char *str_from_attr_type(enum test_peer_attr_type at)
+{
+ switch (at) {
+ case PEER_AT_GLOBAL_FLAG:
+ return "peer-flag";
+ case PEER_AT_AF_FLAG:
+ return "af-flag";
+ case PEER_AT_AF_FILTER:
+ return "af-filter";
+ case PEER_AT_GLOBAL_CUSTOM:
+ case PEER_AT_AF_CUSTOM:
+ return "custom";
+ default:
+ return NULL;
+ }
+}
+
+static bool is_attr_type_global(enum test_peer_attr_type at)
+{
+ return at == PEER_AT_GLOBAL_FLAG || at == PEER_AT_GLOBAL_CUSTOM;
+}
+
+PRINTFRR(2, 3)
+static void test_log(struct test *test, const char *fmt, ...)
+{
+ va_list ap;
+
+ /* Skip logging if test instance has previously failed. */
+ if (test->state != TEST_SUCCESS)
+ return;
+
+ /* Store formatted log message. */
+ va_start(ap, fmt);
+ listnode_add(test->log, vasprintfrr(MTYPE_TMP, fmt, ap));
+ va_end(ap);
+}
+
+PRINTFRR(2, 3)
+static void test_execute(struct test *test, const char *fmt, ...)
+{
+ int ret;
+ char *cmd;
+ va_list ap;
+ vector vline;
+
+ /* Skip execution if test instance has previously failed. */
+ if (test->state != TEST_SUCCESS)
+ return;
+
+ /* Format command string with variadic arguments. */
+ va_start(ap, fmt);
+ cmd = vasprintfrr(MTYPE_TMP, fmt, ap);
+ va_end(ap);
+ if (!cmd) {
+ test->state = TEST_INTERNAL_ERROR;
+ test->error = asprintfrr(
+ MTYPE_TMP, "could not format command string [%s]", fmt);
+ return;
+ }
+
+ /* Tokenize formatted command string. */
+ vline = cmd_make_strvec(cmd);
+ if (vline == NULL) {
+ test->state = TEST_INTERNAL_ERROR;
+ test->error = asprintfrr(
+ MTYPE_TMP,
+ "tokenizing command string [%s] returned empty result",
+ cmd);
+ XFREE(MTYPE_TMP, cmd);
+
+ return;
+ }
+
+ /* Execute command (non-strict). */
+ ret = cmd_execute_command(vline, test->vty, NULL, 0);
+ if (ret != CMD_SUCCESS) {
+ test->state = TEST_COMMAND_ERROR;
+ test->error = asprintfrr(
+ MTYPE_TMP,
+ "execution of command [%s] has failed with code [%d]",
+ cmd, ret);
+ }
+
+ /* Free memory. */
+ cmd_free_strvec(vline);
+ XFREE(MTYPE_TMP, cmd);
+}
+
+PRINTFRR(2, 0)
+static void test_config(struct test *test, const char *fmt, bool invert,
+ va_list ap)
+{
+ char *matcher;
+ char *config;
+ bool matched;
+ va_list apc;
+
+ /* Skip execution if test instance has previously failed. */
+ if (test->state != TEST_SUCCESS)
+ return;
+
+ /* Format matcher string with variadic arguments. */
+ va_copy(apc, ap);
+ matcher = vasprintfrr(MTYPE_TMP, fmt, apc);
+ va_end(apc);
+ if (!matcher) {
+ test->state = TEST_INTERNAL_ERROR;
+ test->error = asprintfrr(
+ MTYPE_TMP, "could not format matcher string [%s]", fmt);
+ return;
+ }
+
+ /* Fetch BGP configuration into buffer. */
+ bgp_config_write(test->vty);
+ config = buffer_getstr(test->vty->obuf);
+ buffer_reset(test->vty->obuf);
+
+ /* Match config against matcher. */
+ matched = !!strstr(config, matcher);
+ if (!matched && !invert) {
+ test->state = TEST_CONFIG_ERROR;
+ test->error = asprintfrr(MTYPE_TMP,
+ "expected config [%s] to be present",
+ matcher);
+ } else if (matched && invert) {
+ test->state = TEST_CONFIG_ERROR;
+ test->error = asprintfrr(MTYPE_TMP,
+ "expected config [%s] to be absent",
+ matcher);
+ }
+
+ /* Free memory and return. */
+ XFREE(MTYPE_TMP, matcher);
+ XFREE(MTYPE_TMP, config);
+}
+
+PRINTFRR(2, 3)
+static void test_config_present(struct test *test, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ test_config(test, fmt, false, ap);
+ va_end(ap);
+}
+
+PRINTFRR(2, 3)
+static void test_config_absent(struct test *test, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ test_config(test, fmt, true, ap);
+ va_end(ap);
+}
+
+static void test_initialize(struct test *test)
+{
+ union sockunion su;
+
+ /* Skip execution if test instance has previously failed. */
+ if (test->state != TEST_SUCCESS)
+ return;
+
+ /* Log message about (re)-initialization */
+ test_log(test, "prepare: %sinitialize bgp test environment",
+ test->bgp ? "re-" : "");
+
+ /* Attempt gracefully to purge previous BGP configuration. */
+ test_execute(test, "no router bgp");
+ test->state = TEST_SUCCESS;
+
+ /* Initialize BGP test environment. */
+ test_execute(test, "router bgp %d", cfg.local_asn);
+ test_execute(test, "no bgp default ipv4-unicast");
+ test_execute(test, "neighbor %s peer-group", cfg.peer_group);
+ if (test->o.use_iface_peer) {
+ test_execute(test, "neighbor %s interface", cfg.peer_interface);
+ test_execute(test, "neighbor %s remote-as %d",
+ cfg.peer_interface,
+ test->o.use_ibgp ? cfg.local_asn : cfg.peer_asn);
+ } else {
+ test_execute(test, "neighbor %s remote-as %d", cfg.peer_address,
+ test->o.use_ibgp ? cfg.local_asn : cfg.peer_asn);
+ }
+
+ if (test->state != TEST_SUCCESS)
+ return;
+
+ /* Fetch default BGP instance. */
+ test->bgp = bgp_get_default();
+ if (!test->bgp) {
+ test->state = TEST_INTERNAL_ERROR;
+ test->error = asprintfrr(
+ MTYPE_TMP, "could not retrieve default bgp instance");
+ return;
+ }
+
+ /* Fetch peer instance. */
+ if (test->o.use_iface_peer) {
+ test->peer =
+ peer_lookup_by_conf_if(test->bgp, cfg.peer_interface);
+ } else {
+ str2sockunion(cfg.peer_address, &su);
+ test->peer = peer_lookup(test->bgp, &su);
+ }
+ if (!test->peer) {
+ test->state = TEST_INTERNAL_ERROR;
+ test->error = asprintfrr(
+ MTYPE_TMP,
+ "could not retrieve instance of bgp peer [%s]",
+ cfg.peer_address);
+ return;
+ }
+
+ /* Fetch peer-group instance. */
+ test->group = peer_group_lookup(test->bgp, cfg.peer_group);
+ if (!test->group) {
+ test->state = TEST_INTERNAL_ERROR;
+ test->error = asprintfrr(
+ MTYPE_TMP,
+ "could not retrieve instance of bgp peer-group [%s]",
+ cfg.peer_group);
+ return;
+ }
+}
+
+static struct test *test_new(const char *desc, bool use_ibgp,
+ bool use_iface_peer)
+{
+ struct test *test;
+
+ test = XCALLOC(MTYPE_TMP, sizeof(struct test));
+ test->state = TEST_SUCCESS;
+ test->desc = XSTRDUP(MTYPE_TMP, desc);
+ test->log = list_new();
+ test->o.use_ibgp = use_ibgp;
+ test->o.use_iface_peer = use_iface_peer;
+
+ test->vty = vty_new();
+ test->vty->type = VTY_TERM;
+ test->vty->node = CONFIG_NODE;
+
+ test_initialize(test);
+
+ return test;
+};
+
+static void test_finish(struct test *test)
+{
+ char *msg;
+ struct listnode *node, *nnode;
+
+ /* Print test output header. */
+ printf("%s [test] %s\n",
+ (test->state == TEST_SUCCESS) ? OUT_SYMBOL_OK : OUT_SYMBOL_NOK,
+ test->desc);
+
+ /* Print test log messages. */
+ for (ALL_LIST_ELEMENTS(test->log, node, nnode, msg)) {
+ printf("%s %s\n", OUT_SYMBOL_INFO, msg);
+ XFREE(MTYPE_TMP, msg);
+ }
+
+ /* Print test error message if available. */
+ if (test->state != TEST_SUCCESS && test->error)
+ printf("%s error: %s\n", OUT_SYMBOL_INFO, test->error);
+
+ /* Print machine-readable result of test. */
+ printf("%s\n", test->state == TEST_SUCCESS ? "OK" : "failed");
+
+ /* Cleanup allocated memory. */
+ if (test->vty) {
+ vty_close(test->vty);
+ test->vty = NULL;
+ }
+ if (test->log)
+ list_delete(&test->log);
+ if (test->desc)
+ XFREE(MTYPE_TMP, test->desc);
+ if (test->error)
+ XFREE(MTYPE_TMP, test->error);
+ XFREE(MTYPE_TMP, test);
+}
+
+static void test_peer_flags(struct test *test, struct test_peer_attr *pa,
+ struct peer *peer, bool exp_val, bool exp_ovrd)
+{
+ bool exp_inv, cur_val, cur_ovrd, cur_inv;
+
+ /* Skip execution if test instance has previously failed. */
+ if (test->state != TEST_SUCCESS)
+ return;
+
+ /* Detect if flag is meant to be inverted. */
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ exp_inv = pa->o.invert_group;
+ else
+ exp_inv = pa->o.invert_peer;
+
+ /* Flip expected value if flag is inverted. */
+ exp_val ^= exp_inv;
+
+ /* Fetch current state of value, override and invert flags. */
+ if (pa->type == PEER_AT_GLOBAL_FLAG) {
+ cur_val = !!CHECK_FLAG(peer->flags, pa->u.flag);
+ cur_ovrd = !!CHECK_FLAG(peer->flags_override, pa->u.flag);
+ cur_inv = !!CHECK_FLAG(peer->flags_invert, pa->u.flag);
+ } else /* if (pa->type == PEER_AT_AF_FLAG) */ {
+ cur_val = !!CHECK_FLAG(peer->af_flags[pa->afi][pa->safi],
+ pa->u.flag);
+ cur_ovrd = !!CHECK_FLAG(
+ peer->af_flags_override[pa->afi][pa->safi], pa->u.flag);
+ cur_inv = !!CHECK_FLAG(peer->af_flags_invert[pa->afi][pa->safi],
+ pa->u.flag);
+ }
+
+ /* Assert expected flag states. */
+ TEST_ASSERT_EQ(test, cur_val, exp_val);
+ TEST_ASSERT_EQ(test, cur_ovrd, exp_ovrd);
+ TEST_ASSERT_EQ(test, cur_inv, exp_inv);
+}
+
+static void test_af_filter(struct test *test, struct test_peer_attr *pa,
+ struct peer *peer, bool exp_state, bool exp_ovrd)
+{
+ bool cur_ovrd;
+ struct bgp_filter *filter;
+
+ /* Skip execution if test instance has previously failed. */
+ if (test->state != TEST_SUCCESS)
+ return;
+
+ /* Fetch and assert current state of override flag. */
+ cur_ovrd = !!CHECK_FLAG(
+ peer->filter_override[pa->afi][pa->safi][pa->u.filter.direct],
+ pa->u.filter.flag);
+
+ TEST_ASSERT_EQ(test, cur_ovrd, exp_ovrd);
+
+ /* Assert that map/list matches expected state (set/unset). */
+ filter = &peer->filter[pa->afi][pa->safi];
+
+ switch (pa->u.filter.flag) {
+ case PEER_FT_DISTRIBUTE_LIST:
+ TEST_ASSERT_EQ(test,
+ !!(filter->dlist[pa->u.filter.direct].name),
+ exp_state);
+ break;
+ case PEER_FT_FILTER_LIST:
+ TEST_ASSERT_EQ(test,
+ !!(filter->aslist[pa->u.filter.direct].name),
+ exp_state);
+ break;
+ case PEER_FT_PREFIX_LIST:
+ TEST_ASSERT_EQ(test,
+ !!(filter->plist[pa->u.filter.direct].name),
+ exp_state);
+ break;
+ case PEER_FT_ROUTE_MAP:
+ TEST_ASSERT_EQ(test, !!(filter->map[pa->u.filter.direct].name),
+ exp_state);
+ break;
+ case PEER_FT_UNSUPPRESS_MAP:
+ TEST_ASSERT_EQ(test, !!(filter->usmap.name), exp_state);
+ break;
+ }
+}
+
+static void test_custom(struct test *test, struct test_peer_attr *pa,
+ struct peer *peer, struct peer *group, bool peer_set,
+ bool group_set)
+{
+ int i;
+ char *handler_error;
+
+ for (i = 0; i < TEST_HANDLER_MAX; i++) {
+ /* Skip execution if test instance has previously failed. */
+ if (test->state != TEST_SUCCESS)
+ return;
+
+ /* Skip further execution if handler is undefined. */
+ if (!pa->handlers[i])
+ return;
+
+ /* Execute custom handler. */
+ pa->handlers[i](test, pa, peer, group, peer_set, group_set);
+ if (test->state != TEST_SUCCESS) {
+ test->state = TEST_CUSTOM_ERROR;
+ handler_error = test->error;
+ test->error = asprintfrr(MTYPE_TMP,
+ "custom handler failed: %s",
+ handler_error);
+ XFREE(MTYPE_TMP, handler_error);
+ }
+ }
+}
+
+
+static void test_process(struct test *test, struct test_peer_attr *pa,
+ struct peer *peer, struct peer *group, bool peer_set,
+ bool group_set)
+{
+ switch (pa->type) {
+ case PEER_AT_GLOBAL_FLAG:
+ case PEER_AT_AF_FLAG:
+ test_peer_flags(
+ test, pa, peer,
+ peer_set || (peer_group_active(peer) && group_set),
+ peer_set);
+ test_peer_flags(test, pa, group, group_set, false);
+ break;
+
+ case PEER_AT_AF_FILTER:
+ test_af_filter(
+ test, pa, peer,
+ peer_set || (peer_group_active(peer) && group_set),
+ peer_set);
+ test_af_filter(test, pa, group, group_set, false);
+ break;
+
+ case PEER_AT_GLOBAL_CUSTOM:
+ case PEER_AT_AF_CUSTOM:
+ /*
+ * Do nothing here - a custom handler can be executed, but this
+ * is not required. This will allow defining peer attributes
+ * which shall not be checked for flag/filter/other internal
+ * states.
+ */
+ break;
+
+ default:
+ test->state = TEST_INTERNAL_ERROR;
+ test->error = asprintfrr(
+ MTYPE_TMP, "invalid attribute type: %d", pa->type);
+ break;
+ }
+
+ /* Attempt to call a custom handler if set for further processing. */
+ test_custom(test, pa, peer, group, peer_set, group_set);
+}
+
+static void test_peer_attr(struct test *test, struct test_peer_attr *pa)
+{
+ int tc = 1;
+ const char *type;
+ const char *ecp = pa->o.invert_peer ? "no " : "";
+ const char *dcp = pa->o.invert_peer ? "" : "no ";
+ const char *ecg = pa->o.invert_group ? "no " : "";
+ const char *dcg = pa->o.invert_group ? "" : "no ";
+ const char *peer_cmd = pa->peer_cmd ?: pa->cmd;
+ const char *group_cmd = pa->group_cmd ?: pa->cmd;
+ struct peer *p = test->peer;
+ struct peer_group *g = test->group;
+
+ /* Determine type and if test is address-family relevant */
+ type = str_from_attr_type(pa->type);
+ if (!type) {
+ test->state = TEST_INTERNAL_ERROR;
+ test->error = asprintfrr(
+ MTYPE_TMP, "invalid attribute type: %d", pa->type);
+ return;
+ }
+
+ /*
+ * =====================================================================
+ * Test Case Suite 1: Config persistence after adding peer to group
+ *
+ * Example: If a peer attribute has value [1] and a group attribute has
+ * value [2], the peer attribute value should be persisted when the peer
+ * gets added to the peer-group.
+ *
+ * This test suite is meant to test the group2peer functions which can
+ * be found inside bgpd/bgpd.c, which are related to initial peer-group
+ * inheritance.
+ * =====================================================================
+ */
+
+ /* Test Preparation: Switch and activate address-family. */
+ if (!is_attr_type_global(pa->type)) {
+ test_log(test, "prepare: switch address-family to [%s]",
+ get_afi_safi_str(pa->afi, pa->safi, false));
+ test_execute(test, "address-family %s %s",
+ str_from_afi(pa->afi), safi2str(pa->safi));
+ test_execute(test, "neighbor %s activate", g->name);
+ test_execute(test, "neighbor %s activate", p->host);
+ }
+
+ /* Skip peer-group to peer transfer test cases if requested. */
+ if (pa->o.skip_xfer_cases && test->state == TEST_SUCCESS)
+ test->state = TEST_SKIPPING;
+
+ /* Test Case: Set flag on BGP peer. */
+ test_log(test, "case %02d: set %s [%s] on [%s]", tc++, type, peer_cmd,
+ p->host);
+ test_execute(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
+ test_process(test, pa, p, g->conf, true, false);
+
+ /* Test Case: Set flag on BGP peer-group. */
+ test_log(test, "case %02d: set %s [%s] on [%s]", tc++, type, group_cmd,
+ g->name);
+ test_execute(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
+ test_process(test, pa, p, g->conf, true, true);
+
+ /* Test Case: Add BGP peer to peer-group. */
+ test_log(test, "case %02d: add peer [%s] to group [%s]", tc++, p->host,
+ g->name);
+ test_execute(test, "neighbor %s peer-group %s", p->host, g->name);
+ test_config_present(test, "neighbor %s %speer-group %s", p->host,
+ p->conf_if ? "interface " : "", g->name);
+ test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
+ test_process(test, pa, p, g->conf, true, true);
+
+ /* Test Case: Unset flag on BGP peer-group. */
+ test_log(test, "case %02d: unset %s [%s] on [%s]", tc++, type,
+ group_cmd, g->name);
+ test_execute(test, "%sneighbor %s %s", dcg, g->name, group_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
+ test_process(test, pa, p, g->conf, true, false);
+
+ /*
+ * =====================================================================
+ * Test Case Suite 2: Config inheritance after adding peer to group
+ *
+ * Example: If a peer attribute has not been set and a group attribute
+ * has a value of [2], the group attribute should be inherited to the
+ * peer without flagging the newly set value as overridden.
+ *
+ * This test suite is meant to test the group2peer functions which can
+ * be found inside bgpd/bgpd.c, which are related to initial peer-group
+ * inheritance.
+ * =====================================================================
+ */
+
+ /* Test Preparation: Re-initialize test environment. */
+ test_initialize(test);
+ p = test->peer;
+ g = test->group;
+
+ /* Test Preparation: Switch and activate address-family. */
+ if (!is_attr_type_global(pa->type)) {
+ test_log(test, "prepare: switch address-family to [%s]",
+ get_afi_safi_str(pa->afi, pa->safi, false));
+ test_execute(test, "address-family %s %s",
+ str_from_afi(pa->afi), safi2str(pa->safi));
+ test_execute(test, "neighbor %s activate", g->name);
+ test_execute(test, "neighbor %s activate", p->host);
+ }
+
+ /* Test Case: Set flag on BGP peer-group. */
+ test_log(test, "case %02d: set %s [%s] on [%s]", tc++, type, group_cmd,
+ g->name);
+ test_execute(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
+ test_config_absent(test, "neighbor %s %s", p->host, pa->cmd);
+ test_config_present(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
+ test_process(test, pa, p, g->conf, false, true);
+
+ /* Test Case: Add BGP peer to peer-group. */
+ test_log(test, "case %02d: add peer [%s] to group [%s]", tc++, p->host,
+ g->name);
+ test_execute(test, "neighbor %s peer-group %s", p->host, g->name);
+ test_config_present(test, "neighbor %s %speer-group %s", p->host,
+ p->conf_if ? "interface " : "", g->name);
+ test_config_absent(test, "neighbor %s %s", p->host, pa->cmd);
+ test_config_present(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
+ test_process(test, pa, p, g->conf, false, true);
+
+ /* Stop skipping test cases if previously enabled. */
+ if (pa->o.skip_xfer_cases && test->state == TEST_SKIPPING)
+ test->state = TEST_SUCCESS;
+
+ /*
+ * =====================================================================
+ * Test Case Suite 3: Miscellaneous flag checks
+ *
+ * This test suite does not focus on initial peer-group inheritance and
+ * instead executes various different commands to set/unset attributes
+ * on both peer- and group-level. These checks should always be executed
+ * and must pass.
+ * =====================================================================
+ */
+
+ /* Test Preparation: Re-initialize test environment. */
+ test_initialize(test);
+ p = test->peer;
+ g = test->group;
+
+ /* Test Preparation: Switch and activate address-family. */
+ if (!is_attr_type_global(pa->type)) {
+ test_log(test, "prepare: switch address-family to [%s]",
+ get_afi_safi_str(pa->afi, pa->safi, false));
+ test_execute(test, "address-family %s %s",
+ str_from_afi(pa->afi), safi2str(pa->safi));
+ test_execute(test, "neighbor %s activate", g->name);
+ test_execute(test, "neighbor %s activate", p->host);
+ }
+
+ /* Test Case: Set flag on BGP peer. */
+ test_log(test, "case %02d: set %s [%s] on [%s]", tc++, type, peer_cmd,
+ p->host);
+ test_execute(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
+ test_process(test, pa, p, g->conf, true, false);
+
+ /* Test Case: Add BGP peer to peer-group. */
+ test_log(test, "case %02d: add peer [%s] to group [%s]", tc++, p->host,
+ g->name);
+ test_execute(test, "neighbor %s peer-group %s", p->host, g->name);
+ test_config_present(test, "neighbor %s %speer-group %s", p->host,
+ p->conf_if ? "interface " : "", g->name);
+ test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
+ test_process(test, pa, p, g->conf, true, false);
+
+ /* Test Case: Re-add BGP peer to peer-group. */
+ test_log(test, "case %02d: re-add peer [%s] to group [%s]", tc++,
+ p->host, g->name);
+ test_execute(test, "neighbor %s peer-group %s", p->host, g->name);
+ test_config_present(test, "neighbor %s %speer-group %s", p->host,
+ p->conf_if ? "interface " : "", g->name);
+ test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
+ test_process(test, pa, p, g->conf, true, false);
+
+ /* Test Case: Set flag on BGP peer-group. */
+ test_log(test, "case %02d: set %s [%s] on [%s]", tc++, type, group_cmd,
+ g->name);
+ test_execute(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
+ test_process(test, pa, p, g->conf, true, true);
+
+ /* Test Case: Unset flag on BGP peer-group. */
+ test_log(test, "case %02d: unset %s [%s] on [%s]", tc++, type,
+ group_cmd, g->name);
+ test_execute(test, "%sneighbor %s %s", dcg, g->name, group_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
+ test_process(test, pa, p, g->conf, true, false);
+
+ /* Test Case: Set flag on BGP peer-group. */
+ test_log(test, "case %02d: set %s [%s] on [%s]", tc++, type, group_cmd,
+ g->name);
+ test_execute(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
+ test_process(test, pa, p, g->conf, true, true);
+
+ /* Test Case: Re-set flag on BGP peer. */
+ test_log(test, "case %02d: re-set %s [%s] on [%s]", tc++, type,
+ peer_cmd, p->host);
+ test_execute(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
+ test_process(test, pa, p, g->conf, true, true);
+
+ /* Test Case: Unset flag on BGP peer. */
+ test_log(test, "case %02d: unset %s [%s] on [%s]", tc++, type, peer_cmd,
+ p->host);
+ test_execute(test, "%sneighbor %s %s", dcp, p->host, peer_cmd);
+ test_config_absent(test, "neighbor %s %s", p->host, pa->cmd);
+ test_config_present(test, "%sneighbor %s %s", ecg, g->name, group_cmd);
+ test_process(test, pa, p, g->conf, false, true);
+
+ /* Test Case: Unset flag on BGP peer-group. */
+ test_log(test, "case %02d: unset %s [%s] on [%s]", tc++, type,
+ group_cmd, g->name);
+ test_execute(test, "%sneighbor %s %s", dcg, g->name, group_cmd);
+ test_config_absent(test, "neighbor %s %s", p->host, pa->cmd);
+ test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
+ test_process(test, pa, p, g->conf, false, false);
+
+ /* Test Case: Set flag on BGP peer. */
+ test_log(test, "case %02d: set %s [%s] on [%s]", tc++, type, peer_cmd,
+ p->host);
+ test_execute(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_present(test, "%sneighbor %s %s", ecp, p->host, peer_cmd);
+ test_config_absent(test, "neighbor %s %s", g->name, pa->cmd);
+ test_process(test, pa, p, g->conf, true, false);
+}
+
+static void bgp_startup(void)
+{
+ cmd_init(1);
+ zlog_aux_init("NONE: ", LOG_DEBUG);
+ zprivs_preinit(&bgpd_privs);
+ zprivs_init(&bgpd_privs);
+
+ master = event_master_create(NULL);
+ nb_init(master, NULL, 0, false);
+ bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
+ bgp_option_set(BGP_OPT_NO_LISTEN);
+ vrf_init(NULL, NULL, NULL, NULL);
+ frr_pthread_init();
+ bgp_init(0);
+ bgp_pthreads_run();
+}
+
+static void bgp_shutdown(void)
+{
+ struct bgp *bgp;
+ struct listnode *node, *nnode;
+
+ bgp_terminate();
+ bgp_close();
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
+ bgp_delete(bgp);
+ bgp_dump_finish();
+ bgp_route_finish();
+ bgp_route_map_terminate();
+ bgp_attr_finish();
+ bgp_pthreads_finish();
+ access_list_add_hook(NULL);
+ access_list_delete_hook(NULL);
+ access_list_reset();
+ as_list_add_hook(NULL);
+ as_list_delete_hook(NULL);
+ bgp_filter_reset();
+ prefix_list_add_hook(NULL);
+ prefix_list_delete_hook(NULL);
+ prefix_list_reset();
+ community_list_terminate(bgp_clist);
+ vrf_terminate();
+#ifdef ENABLE_BGP_VNC
+ vnc_zebra_destroy();
+#endif
+ bgp_zebra_destroy();
+
+ bf_free(bm->rd_idspace);
+ list_delete(&bm->bgp);
+ memset(bm, 0, sizeof(*bm));
+
+ vty_terminate();
+ cmd_terminate();
+ nb_terminate();
+ yang_terminate();
+ zprivs_terminate(&bgpd_privs);
+ event_master_free(master);
+ master = NULL;
+}
+
+int main(void)
+{
+ int i, ii;
+ struct list *pa_list;
+ struct test_peer_attr *pa, *pac;
+ struct listnode *node, *nnode;
+
+ bgp_startup();
+
+ pa_list = list_new();
+ i = 0;
+ while (test_peer_attrs[i].cmd) {
+ pa = &test_peer_attrs[i++];
+
+ /* Just copy the peer attribute structure for global flags. */
+ if (is_attr_type_global(pa->type)) {
+ pac = XMALLOC(MTYPE_TMP, sizeof(struct test_peer_attr));
+ memcpy(pac, pa, sizeof(struct test_peer_attr));
+ listnode_add(pa_list, pac);
+ continue;
+ }
+
+ /* Fallback to default families if not specified. */
+ if (!pa->families[0].afi && !pa->families[0].safi)
+ memcpy(&pa->families, test_default_families,
+ sizeof(test_default_families));
+
+ /* Add peer attribute definition for each address family. */
+ ii = 0;
+ while (pa->families[ii].afi && pa->families[ii].safi) {
+ pac = XMALLOC(MTYPE_TMP, sizeof(struct test_peer_attr));
+ memcpy(pac, pa, sizeof(struct test_peer_attr));
+
+ pac->afi = pa->families[ii].afi;
+ pac->safi = pa->families[ii].safi;
+ listnode_add(pa_list, pac);
+
+ ii++;
+ }
+ }
+
+ for (ALL_LIST_ELEMENTS(pa_list, node, nnode, pa)) {
+ char *desc;
+ struct test *test;
+
+ /* Build test description string. */
+ if (pa->afi && pa->safi)
+ desc = asprintfrr(MTYPE_TMP, "peer\\%s-%s\\%s",
+ str_from_afi(pa->afi),
+ safi2str(pa->safi), pa->cmd);
+ else
+ desc = asprintfrr(MTYPE_TMP, "peer\\%s", pa->cmd);
+
+ /* Initialize new test instance. */
+ test = test_new(desc, pa->o.use_ibgp, pa->o.use_iface_peer);
+ XFREE(MTYPE_TMP, desc);
+
+ /* Execute tests and finish test instance. */
+ test_peer_attr(test, pa);
+ test_finish(test);
+
+ /* Print empty line as spacer. */
+ printf("\n");
+
+ /* Free memory used for peer-attr declaration. */
+ XFREE(MTYPE_TMP, pa);
+ }
+
+ list_delete(&pa_list);
+ bgp_shutdown();
+
+ return 0;
+}
diff --git a/tests/bgpd/test_peer_attr.py b/tests/bgpd/test_peer_attr.py
new file mode 100644
index 0000000..eb57618
--- /dev/null
+++ b/tests/bgpd/test_peer_attr.py
@@ -0,0 +1,200 @@
+import frrtest
+
+
+class TestFlag(frrtest.TestMultiOut):
+ program = "./test_peer_attr"
+
+
+# List of tests can be generated by executing:
+# $> ./test_peer_attr 2>&1 | sed -n 's/\\/\\\\/g; s/\S\+ \[test\] \(.\+\)/TestFlag.okfail(\x27\1\x27)/pg'
+#
+TestFlag.okfail("peer\\advertisement-interval")
+TestFlag.okfail("peer\\capability dynamic")
+TestFlag.okfail("peer\\capability extended-nexthop")
+# TestFlag.okfail('peer\\capability extended-nexthop')
+TestFlag.okfail("peer\\description")
+TestFlag.okfail("peer\\disable-connected-check")
+TestFlag.okfail("peer\\dont-capability-negotiate")
+TestFlag.okfail("peer\\enforce-first-as")
+TestFlag.okfail("peer\\local-as")
+TestFlag.okfail("peer\\local-as 1 no-prepend")
+TestFlag.okfail("peer\\local-as 1 no-prepend replace-as")
+TestFlag.okfail("peer\\override-capability")
+TestFlag.okfail("peer\\passive")
+TestFlag.okfail("peer\\password")
+TestFlag.okfail("peer\\shutdown")
+TestFlag.okfail("peer\\strict-capability-match")
+TestFlag.okfail("peer\\timers")
+TestFlag.okfail("peer\\timers connect")
+TestFlag.okfail("peer\\update-source")
+TestFlag.okfail("peer\\update-source")
+TestFlag.okfail("peer\\ipv4-unicast\\addpath")
+TestFlag.okfail("peer\\ipv4-multicast\\addpath")
+TestFlag.okfail("peer\\ipv6-unicast\\addpath")
+TestFlag.okfail("peer\\ipv6-multicast\\addpath")
+TestFlag.okfail("peer\\ipv4-unicast\\allowas-in")
+TestFlag.okfail("peer\\ipv4-multicast\\allowas-in")
+TestFlag.okfail("peer\\ipv6-unicast\\allowas-in")
+TestFlag.okfail("peer\\ipv6-multicast\\allowas-in")
+TestFlag.okfail("peer\\ipv4-unicast\\allowas-in origin")
+TestFlag.okfail("peer\\ipv4-multicast\\allowas-in origin")
+TestFlag.okfail("peer\\ipv6-unicast\\allowas-in origin")
+TestFlag.okfail("peer\\ipv6-multicast\\allowas-in origin")
+TestFlag.okfail("peer\\ipv4-unicast\\as-override")
+TestFlag.okfail("peer\\ipv4-multicast\\as-override")
+TestFlag.okfail("peer\\ipv6-unicast\\as-override")
+TestFlag.okfail("peer\\ipv6-multicast\\as-override")
+TestFlag.okfail("peer\\ipv4-unicast\\attribute-unchanged as-path")
+TestFlag.okfail("peer\\ipv4-multicast\\attribute-unchanged as-path")
+TestFlag.okfail("peer\\ipv6-unicast\\attribute-unchanged as-path")
+TestFlag.okfail("peer\\ipv6-multicast\\attribute-unchanged as-path")
+TestFlag.okfail("peer\\ipv4-unicast\\attribute-unchanged next-hop")
+TestFlag.okfail("peer\\ipv4-multicast\\attribute-unchanged next-hop")
+TestFlag.okfail("peer\\ipv6-unicast\\attribute-unchanged next-hop")
+TestFlag.okfail("peer\\ipv6-multicast\\attribute-unchanged next-hop")
+TestFlag.okfail("peer\\ipv4-unicast\\attribute-unchanged med")
+TestFlag.okfail("peer\\ipv4-multicast\\attribute-unchanged med")
+TestFlag.okfail("peer\\ipv6-unicast\\attribute-unchanged med")
+TestFlag.okfail("peer\\ipv6-multicast\\attribute-unchanged med")
+TestFlag.okfail("peer\\ipv4-unicast\\attribute-unchanged as-path next-hop")
+TestFlag.okfail("peer\\ipv4-multicast\\attribute-unchanged as-path next-hop")
+TestFlag.okfail("peer\\ipv6-unicast\\attribute-unchanged as-path next-hop")
+TestFlag.okfail("peer\\ipv6-multicast\\attribute-unchanged as-path next-hop")
+TestFlag.okfail("peer\\ipv4-unicast\\attribute-unchanged as-path med")
+TestFlag.okfail("peer\\ipv4-multicast\\attribute-unchanged as-path med")
+TestFlag.okfail("peer\\ipv6-unicast\\attribute-unchanged as-path med")
+TestFlag.okfail("peer\\ipv6-multicast\\attribute-unchanged as-path med")
+TestFlag.okfail("peer\\ipv4-unicast\\attribute-unchanged as-path next-hop med")
+TestFlag.okfail("peer\\ipv4-multicast\\attribute-unchanged as-path next-hop med")
+TestFlag.okfail("peer\\ipv6-unicast\\attribute-unchanged as-path next-hop med")
+TestFlag.okfail("peer\\ipv6-multicast\\attribute-unchanged as-path next-hop med")
+TestFlag.okfail("peer\\ipv4-unicast\\capability orf prefix-list send")
+TestFlag.okfail("peer\\ipv4-multicast\\capability orf prefix-list send")
+TestFlag.okfail("peer\\ipv6-unicast\\capability orf prefix-list send")
+TestFlag.okfail("peer\\ipv6-multicast\\capability orf prefix-list send")
+TestFlag.okfail("peer\\ipv4-unicast\\capability orf prefix-list receive")
+TestFlag.okfail("peer\\ipv4-multicast\\capability orf prefix-list receive")
+TestFlag.okfail("peer\\ipv6-unicast\\capability orf prefix-list receive")
+TestFlag.okfail("peer\\ipv6-multicast\\capability orf prefix-list receive")
+TestFlag.okfail("peer\\ipv4-unicast\\capability orf prefix-list both")
+TestFlag.okfail("peer\\ipv4-multicast\\capability orf prefix-list both")
+TestFlag.okfail("peer\\ipv6-unicast\\capability orf prefix-list both")
+TestFlag.okfail("peer\\ipv6-multicast\\capability orf prefix-list both")
+TestFlag.okfail("peer\\ipv4-unicast\\default-originate")
+TestFlag.okfail("peer\\ipv4-multicast\\default-originate")
+TestFlag.okfail("peer\\ipv6-unicast\\default-originate")
+TestFlag.okfail("peer\\ipv6-multicast\\default-originate")
+TestFlag.okfail("peer\\ipv4-unicast\\default-originate route-map")
+TestFlag.okfail("peer\\ipv4-multicast\\default-originate route-map")
+TestFlag.okfail("peer\\ipv6-unicast\\default-originate route-map")
+TestFlag.okfail("peer\\ipv6-multicast\\default-originate route-map")
+TestFlag.okfail("peer\\ipv4-unicast\\distribute-list")
+TestFlag.okfail("peer\\ipv4-multicast\\distribute-list")
+TestFlag.okfail("peer\\ipv6-unicast\\distribute-list")
+TestFlag.okfail("peer\\ipv6-multicast\\distribute-list")
+TestFlag.okfail("peer\\ipv4-unicast\\distribute-list")
+TestFlag.okfail("peer\\ipv4-multicast\\distribute-list")
+TestFlag.okfail("peer\\ipv6-unicast\\distribute-list")
+TestFlag.okfail("peer\\ipv6-multicast\\distribute-list")
+TestFlag.okfail("peer\\ipv4-unicast\\filter-list")
+TestFlag.okfail("peer\\ipv4-multicast\\filter-list")
+TestFlag.okfail("peer\\ipv6-unicast\\filter-list")
+TestFlag.okfail("peer\\ipv6-multicast\\filter-list")
+TestFlag.okfail("peer\\ipv4-unicast\\filter-list")
+TestFlag.okfail("peer\\ipv4-multicast\\filter-list")
+TestFlag.okfail("peer\\ipv6-unicast\\filter-list")
+TestFlag.okfail("peer\\ipv6-multicast\\filter-list")
+TestFlag.okfail("peer\\ipv4-unicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv4-multicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv6-unicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv6-multicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv4-unicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv4-multicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv6-unicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv6-multicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv4-unicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv4-multicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv6-unicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv6-multicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv4-unicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv4-multicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv6-unicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv6-multicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv4-unicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv4-multicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv6-unicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv6-multicast\\maximum-prefix")
+TestFlag.okfail("peer\\ipv4-unicast\\next-hop-self")
+TestFlag.okfail("peer\\ipv4-multicast\\next-hop-self")
+TestFlag.okfail("peer\\ipv6-unicast\\next-hop-self")
+TestFlag.okfail("peer\\ipv6-multicast\\next-hop-self")
+TestFlag.okfail("peer\\ipv4-unicast\\next-hop-self force")
+TestFlag.okfail("peer\\ipv4-multicast\\next-hop-self force")
+TestFlag.okfail("peer\\ipv6-unicast\\next-hop-self force")
+TestFlag.okfail("peer\\ipv6-multicast\\next-hop-self force")
+TestFlag.okfail("peer\\ipv4-unicast\\prefix-list")
+TestFlag.okfail("peer\\ipv4-multicast\\prefix-list")
+TestFlag.okfail("peer\\ipv6-unicast\\prefix-list")
+TestFlag.okfail("peer\\ipv6-multicast\\prefix-list")
+TestFlag.okfail("peer\\ipv4-unicast\\prefix-list")
+TestFlag.okfail("peer\\ipv4-multicast\\prefix-list")
+TestFlag.okfail("peer\\ipv6-unicast\\prefix-list")
+TestFlag.okfail("peer\\ipv6-multicast\\prefix-list")
+TestFlag.okfail("peer\\ipv4-unicast\\remove-private-AS")
+TestFlag.okfail("peer\\ipv4-multicast\\remove-private-AS")
+TestFlag.okfail("peer\\ipv6-unicast\\remove-private-AS")
+TestFlag.okfail("peer\\ipv6-multicast\\remove-private-AS")
+TestFlag.okfail("peer\\ipv4-unicast\\remove-private-AS all")
+TestFlag.okfail("peer\\ipv4-multicast\\remove-private-AS all")
+TestFlag.okfail("peer\\ipv6-unicast\\remove-private-AS all")
+TestFlag.okfail("peer\\ipv6-multicast\\remove-private-AS all")
+TestFlag.okfail("peer\\ipv4-unicast\\remove-private-AS replace-AS")
+TestFlag.okfail("peer\\ipv4-multicast\\remove-private-AS replace-AS")
+TestFlag.okfail("peer\\ipv6-unicast\\remove-private-AS replace-AS")
+TestFlag.okfail("peer\\ipv6-multicast\\remove-private-AS replace-AS")
+TestFlag.okfail("peer\\ipv4-unicast\\remove-private-AS all replace-AS")
+TestFlag.okfail("peer\\ipv4-multicast\\remove-private-AS all replace-AS")
+TestFlag.okfail("peer\\ipv6-unicast\\remove-private-AS all replace-AS")
+TestFlag.okfail("peer\\ipv6-multicast\\remove-private-AS all replace-AS")
+TestFlag.okfail("peer\\ipv4-unicast\\route-map")
+TestFlag.okfail("peer\\ipv4-multicast\\route-map")
+TestFlag.okfail("peer\\ipv6-unicast\\route-map")
+TestFlag.okfail("peer\\ipv6-multicast\\route-map")
+TestFlag.okfail("peer\\ipv4-unicast\\route-map")
+TestFlag.okfail("peer\\ipv4-multicast\\route-map")
+TestFlag.okfail("peer\\ipv6-unicast\\route-map")
+TestFlag.okfail("peer\\ipv6-multicast\\route-map")
+TestFlag.okfail("peer\\ipv4-unicast\\route-reflector-client")
+TestFlag.okfail("peer\\ipv4-multicast\\route-reflector-client")
+TestFlag.okfail("peer\\ipv6-unicast\\route-reflector-client")
+TestFlag.okfail("peer\\ipv6-multicast\\route-reflector-client")
+TestFlag.okfail("peer\\ipv4-unicast\\route-server-client")
+TestFlag.okfail("peer\\ipv4-multicast\\route-server-client")
+TestFlag.okfail("peer\\ipv6-unicast\\route-server-client")
+TestFlag.okfail("peer\\ipv6-multicast\\route-server-client")
+TestFlag.okfail("peer\\ipv4-unicast\\send-community")
+TestFlag.okfail("peer\\ipv4-multicast\\send-community")
+TestFlag.okfail("peer\\ipv6-unicast\\send-community")
+TestFlag.okfail("peer\\ipv6-multicast\\send-community")
+TestFlag.okfail("peer\\ipv4-unicast\\send-community extended")
+TestFlag.okfail("peer\\ipv4-multicast\\send-community extended")
+TestFlag.okfail("peer\\ipv6-unicast\\send-community extended")
+TestFlag.okfail("peer\\ipv6-multicast\\send-community extended")
+TestFlag.okfail("peer\\ipv4-unicast\\send-community large")
+TestFlag.okfail("peer\\ipv4-multicast\\send-community large")
+TestFlag.okfail("peer\\ipv6-unicast\\send-community large")
+TestFlag.okfail("peer\\ipv6-multicast\\send-community large")
+TestFlag.okfail("peer\\ipv4-unicast\\soft-reconfiguration inbound")
+TestFlag.okfail("peer\\ipv4-multicast\\soft-reconfiguration inbound")
+TestFlag.okfail("peer\\ipv6-unicast\\soft-reconfiguration inbound")
+TestFlag.okfail("peer\\ipv6-multicast\\soft-reconfiguration inbound")
+TestFlag.okfail("peer\\ipv4-unicast\\unsuppress-map")
+TestFlag.okfail("peer\\ipv4-multicast\\unsuppress-map")
+TestFlag.okfail("peer\\ipv6-unicast\\unsuppress-map")
+TestFlag.okfail("peer\\ipv6-multicast\\unsuppress-map")
+TestFlag.okfail("peer\\ipv4-unicast\\weight")
+TestFlag.okfail("peer\\ipv4-multicast\\weight")
+TestFlag.okfail("peer\\ipv6-unicast\\weight")
+TestFlag.okfail("peer\\ipv6-multicast\\weight")
+TestFlag.okfail("peer\\ipv4-vpn\\accept-own")
+TestFlag.okfail("peer\\ipv6-vpn\\accept-own")