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/test_peer_attr.c | |
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/test_peer_attr.c')
-rw-r--r-- | tests/bgpd/test_peer_attr.c | 1491 |
1 files changed, 1491 insertions, 0 deletions
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; +} |