diff --git a/tests/.gitignore b/tests/.gitignore index 1708a4b7b0..d136cae482 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -25,6 +25,7 @@ __pycache__ /bgpd/test_mp_attr /bgpd/test_mpath /bgpd/test_packet +/bgpd/test_peer_attr /isisd/test_fuzz_isis_tlv /isisd/test_fuzz_isis_tlv_tests.h /isisd/test_isis_vertex_queue diff --git a/tests/Makefile.am b/tests/Makefile.am index 6a19325927..aefe0d06ac 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -18,6 +18,7 @@ TESTS_BGPD = \ bgpd/test_aspath \ bgpd/test_capability \ bgpd/test_packet \ + bgpd/test_peer_attr \ bgpd/test_ecommunity \ bgpd/test_mp_attr \ bgpd/test_mpath @@ -140,6 +141,7 @@ lib_cli_test_commands_SOURCES = lib/cli/test_commands_defun.c \ bgpd_test_aspath_SOURCES = bgpd/test_aspath.c bgpd_test_capability_SOURCES = bgpd/test_capability.c bgpd_test_packet_SOURCES = bgpd/test_packet.c +bgpd_test_peer_attr_SOURCES = bgpd/test_peer_attr.c bgpd_test_ecommunity_SOURCES = bgpd/test_ecommunity.c bgpd_test_mp_attr_SOURCES = bgpd/test_mp_attr.c bgpd_test_mpath_SOURCES = bgpd/test_mpath.c @@ -179,6 +181,7 @@ lib_cli_test_commands_LDADD = $(ALL_TESTS_LDADD) bgpd_test_aspath_LDADD = $(BGP_TEST_LDADD) bgpd_test_capability_LDADD = $(BGP_TEST_LDADD) bgpd_test_packet_LDADD = $(BGP_TEST_LDADD) +bgpd_test_peer_attr_LDADD = $(BGP_TEST_LDADD) bgpd_test_ecommunity_LDADD = $(BGP_TEST_LDADD) bgpd_test_mp_attr_LDADD = $(BGP_TEST_LDADD) bgpd_test_mpath_LDADD = $(BGP_TEST_LDADD) @@ -193,6 +196,7 @@ EXTRA_DIST = \ bgpd/test_ecommunity.py \ bgpd/test_mp_attr.py \ bgpd/test_mpath.py \ + bgpd/test_peer_attr.py \ helpers/python/frrsix.py \ helpers/python/frrtest.py \ isisd/test_fuzz_isis_tlv.py \ diff --git a/tests/bgpd/test_peer_attr.c b/tests/bgpd/test_peer_attr.c new file mode 100644 index 0000000000..bb965a3344 --- /dev/null +++ b/tests/bgpd/test_peer_attr.c @@ -0,0 +1,1144 @@ +/* + * BGP Peer Attribute Unit Tests + * Copyright (C) 2018 Pascal Mathis + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#include + +#include "memory.h" +#include "plist.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" + +#ifdef ENABLE_BGP_VNC +#include "bgpd/rfapi/rfapi_backend.h" +#endif + +/* Required variables to link in libbgp */ +struct zebra_privs_t bgpd_privs = {0}; +struct thread_master *master = NULL; + +enum test_state { + TEST_SUCCESS, + TEST_COMMAND_ERROR, + TEST_CONFIG_ERROR, + TEST_ASSERT_ERROR, + TEST_INTERNAL_ERROR, +}; + +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 test_config { + int local_asn; + int peer_asn; + const char *peer_address; + 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 { PEER_AT_AF_FLAG = 0, + PEER_AT_AF_FILTER = 1, + } type; + union { + uint32_t flag; + struct { + uint32_t flag; + size_t direct; + } filter; + } u; + struct { + bool invert; + bool use_ibgp; + } o; + + afi_t afi; + safi_t safi; + struct test_peer_family families[AFI_MAX * SAFI_MAX]; +}; + +#define OUT_SYMBOL_INFO "\u25ba" +#define OUT_SYMBOL_OK "\u2714" +#define OUT_SYMBOL_NOK "\u2716" + +/* clang-format off */ +#define TEST_ASSERT(T, C) \ + do { \ + if ((T)->state != TEST_SUCCESS || (C)) \ + break; \ + \ + (T)->state = TEST_ASSERT_ERROR; \ + (T)->error = str_printf("assertion failed: %s", (#C)); \ + } while (0) + +#define TEST_AF_FLAGS(T, P, A, V, O) \ + do { \ + if ((T)->state != TEST_SUCCESS) \ + break; \ + \ + TEST_ASSERT((T), !!CHECK_FLAG((P)->af_flags[(A)->afi][(A)->safi], (A)->u.flag) == ((V) ^ (A)->o.invert)); \ + TEST_ASSERT((T), !!CHECK_FLAG((P)->af_flags_override[(A)->afi][(A)->safi], (A)->u.flag) == (O)); \ + TEST_ASSERT((T), !!CHECK_FLAG((P)->af_flags_invert[(A)->afi][(A)->safi], (A)->u.flag) == (A)->o.invert); \ + } while (0) + +#define TEST_AF_FILTER(T, P, A, S, O) \ + do { \ + if ((T)->state != TEST_SUCCESS) \ + break; \ + \ + TEST_ASSERT((T), !!CHECK_FLAG((P)->filter_override[(A)->afi][(A)->safi][(A)->u.filter.direct], (A)->u.filter.flag) == (O)); \ + switch ((A)->u.filter.flag) { \ + case PEER_FT_DISTRIBUTE_LIST: \ + TEST_ASSERT((T), !!((P)->filter[(A)->afi][(A)->safi].dlist[(A)->u.filter.direct].name) == (S)); \ + break; \ + case PEER_FT_FILTER_LIST: \ + TEST_ASSERT((T), !!((P)->filter[(A)->afi][(A)->safi].aslist[(A)->u.filter.direct].name) == (S)); \ + break; \ + case PEER_FT_PREFIX_LIST: \ + TEST_ASSERT((T), !!((P)->filter[(A)->afi][(A)->safi].plist[(A)->u.filter.direct].name) == (S)); \ + break; \ + case PEER_FT_ROUTE_MAP: \ + TEST_ASSERT((T), !!((P)->filter[(A)->afi][(A)->safi].map[(A)->u.filter.direct].name) == (S)); \ + break; \ + case PEER_FT_UNSUPPRESS_MAP: \ + TEST_ASSERT((T), !!((P)->filter[(A)->afi][(A)->safi].usmap.name) == (S)); \ + break; \ + } \ + } while (0) +/* clang-format on */ + +static struct test_config cfg = { + .local_asn = 100, + .peer_asn = 200, + .peer_address = "1.1.1.1", + .peer_group = "PG-TEST", +}; + +/* clang-format off */ +static struct test_peer_attr test_peer_attrs[] = { + { + .cmd = "addpath-tx-all-paths", + .u.flag = PEER_FLAG_ADDPATH_TX_ALL_PATHS, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "addpath-tx-bestpath-per-AS", + .u.flag = PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "allowas-in", + .peer_cmd = "allowas-in 1", + .group_cmd = "allowas-in 2", + .u.flag = PEER_FLAG_ALLOWAS_IN, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "allowas-in origin", + .u.flag = PEER_FLAG_ALLOWAS_IN_ORIGIN, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "as-override", + .u.flag = PEER_FLAG_AS_OVERRIDE, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "attribute-unchanged as-path", + .u.flag = PEER_FLAG_AS_PATH_UNCHANGED, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "attribute-unchanged next-hop", + .u.flag = PEER_FLAG_NEXTHOP_UNCHANGED, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "attribute-unchanged med", + .u.flag = PEER_FLAG_MED_UNCHANGED, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "attribute-unchanged as-path next-hop", + .u.flag = PEER_FLAG_AS_PATH_UNCHANGED + | PEER_FLAG_NEXTHOP_UNCHANGED, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "attribute-unchanged as-path med", + .u.flag = PEER_FLAG_AS_PATH_UNCHANGED + | PEER_FLAG_MED_UNCHANGED, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "attribute-unchanged as-path next-hop med", + .u.flag = PEER_FLAG_AS_PATH_UNCHANGED + | PEER_FLAG_NEXTHOP_UNCHANGED + | PEER_FLAG_MED_UNCHANGED, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "capability orf prefix-list send", + .u.flag = PEER_FLAG_ORF_PREFIX_SM, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "capability orf prefix-list receive", + .u.flag = PEER_FLAG_ORF_PREFIX_RM, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "capability orf prefix-list both", + .u.flag = PEER_FLAG_ORF_PREFIX_SM | PEER_FLAG_ORF_PREFIX_RM, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "default-originate", + .u.flag = PEER_FLAG_DEFAULT_ORIGINATE, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .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, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .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, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .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, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .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, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .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, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "maximum-prefix", + .peer_cmd = "maximum-prefix 10", + .group_cmd = "maximum-prefix 20", + .u.flag = PEER_FLAG_MAX_PREFIX, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "maximum-prefix", + .peer_cmd = "maximum-prefix 10 restart 100", + .group_cmd = "maximum-prefix 20 restart 200", + .u.flag = PEER_FLAG_MAX_PREFIX, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .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, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .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, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .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, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "next-hop-self", + .u.flag = PEER_FLAG_NEXTHOP_SELF, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "next-hop-self force", + .u.flag = PEER_FLAG_FORCE_NEXTHOP_SELF, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .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, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .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, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "remove-private-AS", + .u.flag = PEER_FLAG_REMOVE_PRIVATE_AS, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "remove-private-AS all", + .u.flag = PEER_FLAG_REMOVE_PRIVATE_AS + | PEER_FLAG_REMOVE_PRIVATE_AS_ALL, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "remove-private-AS replace-AS", + .u.flag = PEER_FLAG_REMOVE_PRIVATE_AS + | PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "remove-private-AS all replace-AS", + .u.flag = PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .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, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .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, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "route-reflector-client", + .u.flag = PEER_FLAG_REFLECTOR_CLIENT, + .o.use_ibgp = true, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "route-server-client", + .u.flag = PEER_FLAG_RSERVER_CLIENT, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "send-community", + .u.flag = PEER_FLAG_SEND_COMMUNITY, + .o.invert = true, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "send-community extended", + .u.flag = PEER_FLAG_SEND_EXT_COMMUNITY, + .o.invert = true, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "send-community large", + .u.flag = PEER_FLAG_SEND_LARGE_COMMUNITY, + .o.invert = true, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "soft-reconfiguration inbound", + .u.flag = PEER_FLAG_SOFT_RECONFIG, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .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, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + { + .cmd = "weight", + .peer_cmd = "weight 100", + .group_cmd = "weight 200", + .u.flag = PEER_FLAG_WEIGHT, + .families = { + { .afi = AFI_IP, .safi = SAFI_UNICAST }, + { .afi = AFI_IP6, .safi = SAFI_UNICAST }, + } + }, + {NULL} +}; +/* clang-format on */ + +static char *str_vprintf(const char *fmt, va_list ap) +{ + int ret; + int buf_size = 0; + char *buf = NULL; + va_list apc; + + while (1) { + va_copy(apc, ap); + ret = vsnprintf(buf, buf_size, fmt, apc); + va_end(apc); + + if (ret >= 0 && ret < buf_size) + break; + + if (ret >= 0) + buf_size = ret + 1; + else + buf_size *= 2; + + buf = XREALLOC(MTYPE_TMP, buf, buf_size); + } + + return buf; +} + +static char *str_printf(const char *fmt, ...) +{ + char *buf; + va_list ap; + + va_start(ap, fmt); + buf = str_vprintf(fmt, ap); + va_end(ap); + + return buf; +} + +static const char *str_from_afi(afi_t afi) +{ + switch (afi) { + case AFI_IP: + return "ipv4"; + case AFI_IP6: + return "ipv6"; + default: + return ""; + } +} + +static const char *str_from_safi(safi_t safi) +{ + switch (safi) { + case SAFI_UNICAST: + return "unicast"; + case SAFI_MULTICAST: + return "multicast"; + case SAFI_MPLS_VPN: + return "labeled-unicast"; + case SAFI_FLOWSPEC: + return "flowspec"; + default: + return ""; + } +} + +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 = str_vprintf(fmt, ap); + va_end(ap); + if (!cmd) { + test->state = TEST_INTERNAL_ERROR; + test->error = + str_printf("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 = str_printf( + "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 = str_printf( + "execution of command [%s] has failed with code [%d]", + cmd, ret); + } + + /* Free memory and return. */ + cmd_free_strvec(vline); + XFREE(MTYPE_TMP, cmd); + return; +} + +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 = str_vprintf(fmt, apc); + va_end(apc); + if (!matcher) { + test->state = TEST_INTERNAL_ERROR; + test->error = + str_printf("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 = str_printf("expected config [%s] to be present", + matcher); + } else if (matched && invert) { + test->state = TEST_CONFIG_ERROR; + test->error = str_printf("expected config [%s] to be absent", + matcher); + } + + /* Free memory and return. */ + XFREE(MTYPE_TMP, matcher); + XFREE(MTYPE_TMP, config); + return; +} + +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); +} + +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 struct test *test_new(const char *desc, bool use_ibgp) +{ + struct test *test; + union sockunion su; + + test = XCALLOC(MTYPE_TMP, sizeof(struct test)); + test->state = TEST_SUCCESS; + test->desc = XSTRDUP(MTYPE_TMP, desc); + test->log = list_new(); + + test->vty = vty_new(); + test->vty->type = VTY_TERM; + test->vty->node = CONFIG_NODE; + + /* 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); + test_execute(test, "neighbor %s remote-as %d", cfg.peer_address, + use_ibgp ? cfg.local_asn : cfg.peer_asn); + if (test->state != TEST_SUCCESS) + return test; + + /* Fetch default BGP instance. */ + test->bgp = bgp_get_default(); + if (!test->bgp) { + test->state = TEST_INTERNAL_ERROR; + test->error = + str_printf("could not retrieve default bgp instance"); + return test; + } + + /* Fetch peer instance. */ + str2sockunion(cfg.peer_address, &su); + test->peer = peer_lookup(test->bgp, &su); + if (!test->peer) { + test->state = TEST_INTERNAL_ERROR; + test->error = str_printf( + "could not retrieve instance of bgp peer [%s]", + cfg.peer_address); + return test; + } + + /* Fetch peer-group instance. */ + test->group = peer_group_lookup(test->bgp, cfg.peer_group); + if (!test->group) { + test->state = TEST_INTERNAL_ERROR; + test->error = str_printf( + "could not retrieve instance of bgp peer-group [%s]", + cfg.peer_group); + return test; + } + + return test; +}; + +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, str_vprintf(fmt, ap)); + va_end(ap); +} + +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_and_null(&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_attr(struct test *test, struct test_peer_attr *pa) +{ + int tc = 1; + const char *ec = pa->o.invert ? "no " : ""; + const char *dc = pa->o.invert ? "" : "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; + + /* Test Case: Switch active address-family. */ + if (pa->type == PEER_AT_AF_FLAG || pa->type == PEER_AT_AF_FILTER) { + test_log(test, "prepare: switch address-family to [%s]", + afi_safi_print(pa->afi, pa->safi)); + test_execute(test, "address-family %s %s", + str_from_afi(pa->afi), str_from_safi(pa->safi)); + } + + /* Test Case: Set flag on BGP peer. */ + test_log(test, "case %02d: set af-flag [%s] on [%s]", tc++, peer_cmd, + p->host); + test_execute(test, "%sneighbor %s %s", ec, p->host, peer_cmd); + test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd); + test_config_absent(test, "neighbor %s %s", g->name, pa->cmd); + if (pa->type == PEER_AT_AF_FLAG) { + TEST_AF_FLAGS(test, p, pa, true, true); + TEST_AF_FLAGS(test, g->conf, pa, false, false); + } else if (pa->type == PEER_AT_AF_FILTER) { + TEST_AF_FILTER(test, p, pa, true, true); + TEST_AF_FILTER(test, g->conf, pa, false, 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 peer-group %s", p->host, + g->name); + test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd); + test_config_absent(test, "neighbor %s %s", g->name, pa->cmd); + if (pa->type == PEER_AT_AF_FLAG) { + TEST_AF_FLAGS(test, p, pa, true, true); + TEST_AF_FLAGS(test, g->conf, pa, false, false); + } else if (pa->type == PEER_AT_AF_FILTER) { + TEST_AF_FILTER(test, p, pa, true, true); + TEST_AF_FILTER(test, g->conf, pa, false, 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 peer-group %s", p->host, + g->name); + test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd); + test_config_absent(test, "neighbor %s %s", g->name, pa->cmd); + if (pa->type == PEER_AT_AF_FLAG) { + TEST_AF_FLAGS(test, p, pa, true, true); + TEST_AF_FLAGS(test, g->conf, pa, false, false); + } else if (pa->type == PEER_AT_AF_FILTER) { + TEST_AF_FILTER(test, p, pa, true, true); + TEST_AF_FILTER(test, g->conf, pa, false, false); + } + + /* Test Case: Set flag on BGP peer-group. */ + test_log(test, "case %02d: set af-flag [%s] on [%s]", tc++, group_cmd, + g->name); + test_execute(test, "%sneighbor %s %s", ec, g->name, group_cmd); + test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd); + test_config_present(test, "%sneighbor %s %s", ec, g->name, group_cmd); + if (pa->type == PEER_AT_AF_FLAG) { + TEST_AF_FLAGS(test, p, pa, true, true); + TEST_AF_FLAGS(test, g->conf, pa, true, false); + } else if (pa->type == PEER_AT_AF_FILTER) { + TEST_AF_FILTER(test, p, pa, true, true); + TEST_AF_FILTER(test, g->conf, pa, true, false); + } + + /* Test Case: Unset flag on BGP peer-group. */ + test_log(test, "case %02d: unset af-flag [%s] on [%s]", tc++, group_cmd, + g->name); + test_execute(test, "%sneighbor %s %s", dc, g->name, group_cmd); + test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd); + test_config_absent(test, "neighbor %s %s", g->name, pa->cmd); + if (pa->type == PEER_AT_AF_FLAG) { + TEST_AF_FLAGS(test, p, pa, true, true); + TEST_AF_FLAGS(test, g->conf, pa, false, false); + } else if (pa->type == PEER_AT_AF_FILTER) { + TEST_AF_FILTER(test, p, pa, true, true); + TEST_AF_FILTER(test, g->conf, pa, false, false); + } + + /* Test Case: Set flag on BGP peer-group. */ + test_log(test, "case %02d: set af-flag [%s] on [%s]", tc++, group_cmd, + g->name); + test_execute(test, "%sneighbor %s %s", ec, g->name, group_cmd); + test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd); + test_config_present(test, "%sneighbor %s %s", ec, g->name, group_cmd); + if (pa->type == PEER_AT_AF_FLAG) { + TEST_AF_FLAGS(test, p, pa, true, true); + TEST_AF_FLAGS(test, g->conf, pa, true, false); + } else if (pa->type == PEER_AT_AF_FILTER) { + TEST_AF_FILTER(test, p, pa, true, true); + TEST_AF_FILTER(test, g->conf, pa, true, false); + } + + /* Test Case: Re-set flag on BGP peer. */ + test_log(test, "case %02d: re-set af-flag [%s] on [%s]", tc++, peer_cmd, + p->host); + test_execute(test, "%sneighbor %s %s", ec, p->host, peer_cmd); + test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd); + test_config_present(test, "%sneighbor %s %s", ec, g->name, group_cmd); + if (pa->type == PEER_AT_AF_FLAG) { + TEST_AF_FLAGS(test, p, pa, true, true); + TEST_AF_FLAGS(test, g->conf, pa, true, false); + } else if (pa->type == PEER_AT_AF_FILTER) { + TEST_AF_FILTER(test, p, pa, true, true); + TEST_AF_FILTER(test, g->conf, pa, true, false); + } + + /* Test Case: Unset flag on BGP peer. */ + test_log(test, "case %02d: unset af-flag [%s] on [%s]", tc++, peer_cmd, + p->host); + test_execute(test, "%sneighbor %s %s", dc, p->host, peer_cmd); + test_config_absent(test, "neighbor %s %s", p->host, pa->cmd); + test_config_present(test, "%sneighbor %s %s", ec, g->name, group_cmd); + if (pa->type == PEER_AT_AF_FLAG) { + TEST_AF_FLAGS(test, p, pa, true, false); + TEST_AF_FLAGS(test, g->conf, pa, true, false); + } else if (pa->type == PEER_AT_AF_FILTER) { + TEST_AF_FILTER(test, p, pa, true, false); + TEST_AF_FILTER(test, g->conf, pa, true, false); + } + + /* Test Case: Unset flag on BGP peer-group. */ + test_log(test, "case %02d: unset af-flag [%s] on [%s]", tc++, group_cmd, + g->name); + test_execute(test, "%sneighbor %s %s", dc, 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); + if (pa->type == PEER_AT_AF_FLAG) { + TEST_AF_FLAGS(test, p, pa, false, false); + TEST_AF_FLAGS(test, g->conf, pa, false, false); + } else if (pa->type == PEER_AT_AF_FILTER) { + TEST_AF_FILTER(test, p, pa, false, false); + TEST_AF_FILTER(test, g->conf, pa, false, false); + } + + /* Test Case: Set flag on BGP peer. */ + test_log(test, "case %02d: set af-flag [%s] on [%s]", tc++, peer_cmd, + p->host); + test_execute(test, "%sneighbor %s %s", ec, p->host, peer_cmd); + test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd); + test_config_absent(test, "neighbor %s %s", g->name, pa->cmd); + if (pa->type == PEER_AT_AF_FLAG) { + TEST_AF_FLAGS(test, p, pa, true, true); + TEST_AF_FLAGS(test, g->conf, pa, false, false); + } else if (pa->type == PEER_AT_AF_FILTER) { + TEST_AF_FILTER(test, p, pa, true, true); + TEST_AF_FILTER(test, g->conf, pa, false, false); + } +} + +static void bgp_startup() +{ + cmd_init(1); + openzlog("testbgpd", "NONE", 0, LOG_CONS | LOG_NDELAY | LOG_PID, + LOG_DAEMON); + zprivs_preinit(&bgpd_privs); + zprivs_init(&bgpd_privs); + + master = thread_master_create(NULL); + bgp_master_init(master); + bgp_option_set(BGP_OPT_NO_LISTEN); + vrf_init(NULL, NULL, NULL, NULL); + bgp_init(); + bgp_pthreads_run(); +} + +static void bgp_shutdown() +{ + 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_and_null(&bm->bgp); + memset(bm, 0, sizeof(*bm)); + + vty_terminate(); + cmd_terminate(); + zprivs_terminate(&bgpd_privs); + thread_master_free(master); + master = NULL; + closezlog(); +} + +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++]; + + if (pa->families[0].afi && pa->families[0].safi) { + 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++; + } + } else { + pac = XMALLOC(MTYPE_TMP, sizeof(struct test_peer_attr)); + memcpy(pac, pa, sizeof(struct test_peer_attr)); + listnode_add(pa_list, pac); + } + } + + for (ALL_LIST_ELEMENTS(pa_list, node, nnode, pa)) { + char *desc; + struct test *test; + + /* Build test description string. */ + if (pa->afi && pa->safi) + desc = str_printf("peer\\%s-%s\\%s", + str_from_afi(pa->afi), + str_from_safi(pa->safi), pa->cmd); + else + desc = str_printf("peer\\%s", pa->cmd); + + /* Initialize new test instance. */ + test = test_new(desc, pa->o.use_ibgp); + 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_and_null(&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 0000000000..17c6598382 --- /dev/null +++ b/tests/bgpd/test_peer_attr.py @@ -0,0 +1,94 @@ +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\\ipv4-unicast\\addpath-tx-all-paths') +TestFlag.okfail('peer\\ipv6-unicast\\addpath-tx-all-paths') +TestFlag.okfail('peer\\ipv4-unicast\\addpath-tx-bestpath-per-AS') +TestFlag.okfail('peer\\ipv6-unicast\\addpath-tx-bestpath-per-AS') +TestFlag.okfail('peer\\ipv4-unicast\\allowas-in') +TestFlag.okfail('peer\\ipv6-unicast\\allowas-in') +TestFlag.okfail('peer\\ipv4-unicast\\allowas-in origin') +TestFlag.okfail('peer\\ipv6-unicast\\allowas-in origin') +TestFlag.okfail('peer\\ipv4-unicast\\as-override') +TestFlag.okfail('peer\\ipv6-unicast\\as-override') +TestFlag.okfail('peer\\ipv4-unicast\\attribute-unchanged as-path') +TestFlag.okfail('peer\\ipv6-unicast\\attribute-unchanged as-path') +TestFlag.okfail('peer\\ipv4-unicast\\attribute-unchanged next-hop') +TestFlag.okfail('peer\\ipv6-unicast\\attribute-unchanged next-hop') +TestFlag.okfail('peer\\ipv4-unicast\\attribute-unchanged med') +TestFlag.okfail('peer\\ipv6-unicast\\attribute-unchanged med') +TestFlag.okfail('peer\\ipv4-unicast\\attribute-unchanged as-path next-hop') +TestFlag.okfail('peer\\ipv6-unicast\\attribute-unchanged as-path next-hop') +TestFlag.okfail('peer\\ipv4-unicast\\attribute-unchanged as-path med') +TestFlag.okfail('peer\\ipv6-unicast\\attribute-unchanged as-path med') +TestFlag.okfail('peer\\ipv4-unicast\\attribute-unchanged as-path next-hop med') +TestFlag.okfail('peer\\ipv6-unicast\\attribute-unchanged as-path next-hop med') +TestFlag.okfail('peer\\ipv4-unicast\\capability orf prefix-list send') +TestFlag.okfail('peer\\ipv6-unicast\\capability orf prefix-list send') +TestFlag.okfail('peer\\ipv4-unicast\\capability orf prefix-list receive') +TestFlag.okfail('peer\\ipv6-unicast\\capability orf prefix-list receive') +TestFlag.okfail('peer\\ipv4-unicast\\capability orf prefix-list both') +TestFlag.okfail('peer\\ipv6-unicast\\capability orf prefix-list both') +TestFlag.okfail('peer\\ipv4-unicast\\default-originate') +TestFlag.okfail('peer\\ipv6-unicast\\default-originate') +TestFlag.okfail('peer\\ipv4-unicast\\default-originate route-map') +TestFlag.okfail('peer\\ipv6-unicast\\default-originate route-map') +TestFlag.okfail('peer\\ipv4-unicast\\distribute-list') +TestFlag.okfail('peer\\ipv6-unicast\\distribute-list') +TestFlag.okfail('peer\\ipv4-unicast\\distribute-list') +TestFlag.okfail('peer\\ipv6-unicast\\distribute-list') +TestFlag.okfail('peer\\ipv4-unicast\\filter-list') +TestFlag.okfail('peer\\ipv6-unicast\\filter-list') +TestFlag.okfail('peer\\ipv4-unicast\\filter-list') +TestFlag.okfail('peer\\ipv6-unicast\\filter-list') +TestFlag.okfail('peer\\ipv4-unicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv6-unicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv4-unicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv6-unicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv4-unicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv6-unicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv4-unicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv6-unicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv4-unicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv6-unicast\\maximum-prefix') +TestFlag.okfail('peer\\ipv4-unicast\\next-hop-self') +TestFlag.okfail('peer\\ipv6-unicast\\next-hop-self') +TestFlag.okfail('peer\\ipv4-unicast\\next-hop-self force') +TestFlag.okfail('peer\\ipv6-unicast\\next-hop-self force') +TestFlag.okfail('peer\\ipv4-unicast\\prefix-list') +TestFlag.okfail('peer\\ipv6-unicast\\prefix-list') +TestFlag.okfail('peer\\ipv4-unicast\\prefix-list') +TestFlag.okfail('peer\\ipv6-unicast\\prefix-list') +TestFlag.okfail('peer\\ipv4-unicast\\remove-private-AS') +TestFlag.okfail('peer\\ipv6-unicast\\remove-private-AS') +TestFlag.okfail('peer\\ipv4-unicast\\remove-private-AS all') +TestFlag.okfail('peer\\ipv6-unicast\\remove-private-AS all') +TestFlag.okfail('peer\\ipv4-unicast\\remove-private-AS replace-AS') +TestFlag.okfail('peer\\ipv6-unicast\\remove-private-AS replace-AS') +TestFlag.okfail('peer\\ipv4-unicast\\remove-private-AS all replace-AS') +TestFlag.okfail('peer\\ipv6-unicast\\remove-private-AS all replace-AS') +TestFlag.okfail('peer\\ipv4-unicast\\route-map') +TestFlag.okfail('peer\\ipv6-unicast\\route-map') +TestFlag.okfail('peer\\ipv4-unicast\\route-map') +TestFlag.okfail('peer\\ipv6-unicast\\route-map') +TestFlag.okfail('peer\\ipv4-unicast\\route-reflector-client') +TestFlag.okfail('peer\\ipv6-unicast\\route-reflector-client') +TestFlag.okfail('peer\\ipv4-unicast\\route-server-client') +TestFlag.okfail('peer\\ipv6-unicast\\route-server-client') +TestFlag.okfail('peer\\ipv4-unicast\\send-community') +TestFlag.okfail('peer\\ipv6-unicast\\send-community') +TestFlag.okfail('peer\\ipv4-unicast\\send-community extended') +TestFlag.okfail('peer\\ipv6-unicast\\send-community extended') +TestFlag.okfail('peer\\ipv4-unicast\\send-community large') +TestFlag.okfail('peer\\ipv6-unicast\\send-community large') +TestFlag.okfail('peer\\ipv4-unicast\\soft-reconfiguration inbound') +TestFlag.okfail('peer\\ipv6-unicast\\soft-reconfiguration inbound') +TestFlag.okfail('peer\\ipv4-unicast\\unsuppress-map') +TestFlag.okfail('peer\\ipv6-unicast\\unsuppress-map') +TestFlag.okfail('peer\\ipv4-unicast\\weight') +TestFlag.okfail('peer\\ipv6-unicast\\weight')