diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-09 13:16:35 +0000 |
commit | e2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch) | |
tree | f0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /tests/bgpd | |
parent | Initial commit. (diff) | |
download | frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip |
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests/bgpd')
-rw-r--r-- | tests/bgpd/subdir.am | 82 | ||||
-rw-r--r-- | tests/bgpd/test_aspath.c | 1463 | ||||
-rw-r--r-- | tests/bgpd/test_aspath.py | 84 | ||||
-rw-r--r-- | tests/bgpd/test_bgp_table.c | 167 | ||||
-rw-r--r-- | tests/bgpd/test_bgp_table.py | 9 | ||||
-rw-r--r-- | tests/bgpd/test_capability.c | 984 | ||||
-rw-r--r-- | tests/bgpd/test_capability.py | 56 | ||||
-rw-r--r-- | tests/bgpd/test_ecommunity.c | 134 | ||||
-rw-r--r-- | tests/bgpd/test_ecommunity.py | 11 | ||||
-rw-r--r-- | tests/bgpd/test_mp_attr.c | 1117 | ||||
-rw-r--r-- | tests/bgpd/test_mp_attr.py | 49 | ||||
-rw-r--r-- | tests/bgpd/test_mpath.c | 482 | ||||
-rw-r--r-- | tests/bgpd/test_mpath.py | 10 | ||||
-rw-r--r-- | tests/bgpd/test_packet.c | 75 | ||||
-rw-r--r-- | tests/bgpd/test_peer_attr.c | 1491 | ||||
-rw-r--r-- | tests/bgpd/test_peer_attr.py | 200 |
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") |