]> git.proxmox.com Git - mirror_frr.git/blobdiff - tests/bgpd/test_peer_attr.c
lib: add support for confirmed commits
[mirror_frr.git] / tests / bgpd / test_peer_attr.c
index 67a6db849f8ad4c9828db6bb2af517fcaf626dcc..78016dc9cefe544dfde53ab6bdb3ac386e95e640 100644 (file)
 #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 = str_printf("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 = str_printf(                                       \
+                       "assertion failed: %s[%d] == [%d]%s (%s:%d)", (#A),    \
+                       (A), (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 thread_master *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;
@@ -56,12 +144,18 @@ struct test {
        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;
 };
 
@@ -75,9 +169,7 @@ struct test_peer_attr {
        const char *peer_cmd;
        const char *group_cmd;
 
-       enum { PEER_AT_AF_FLAG = 0,
-              PEER_AT_AF_FILTER = 1,
-              PEER_AT_GLOBAL_FLAG = 2 } type;
+       enum test_peer_attr_type type;
        union {
                uint32_t flag;
                struct {
@@ -86,33 +178,29 @@ struct test_peer_attr {
                } filter;
        } u;
        struct {
-               bool invert;
+               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];
-};
-
-#define OUT_SYMBOL_INFO "\u25ba"
-#define OUT_SYMBOL_OK "\u2714"
-#define OUT_SYMBOL_NOK "\u2716"
 
-#define TEST_ASSERT_EQ(T, A, B)                                                \
-       do {                                                                   \
-               if ((T)->state != TEST_SUCCESS || ((A) == (B)))                \
-                       break;                                                 \
-               (T)->state = TEST_ASSERT_ERROR;                                \
-               (T)->error = str_printf(                                       \
-                       "assertion failed: %s[%d] == [%d]%s (%s:%d)", (#A),    \
-                       (A), (B), (#B), __FILE__, __LINE__);                   \
-       } while (0)
+       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",
 };
 
@@ -123,21 +211,223 @@ static struct test_peer_family test_default_families[] = {
        {.afi = AFI_IP6, .safi = SAFI_MULTICAST},
 };
 
+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;
+}
+
+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 = "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 = "addpath-tx-all-paths",
-               .u.flag = PEER_FLAG_ADDPATH_TX_ALL_PATHS,
+               .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 = "addpath-tx-bestpath-per-AS",
-               .u.flag = PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS,
+               .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",
@@ -196,6 +486,25 @@ static struct test_peer_attr test_peer_attrs[] = {
                .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",
@@ -204,6 +513,7 @@ static struct test_peer_attr test_peer_attrs[] = {
                .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",
@@ -212,36 +522,46 @@ static struct test_peer_attr test_peer_attrs[] = {
                .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",
@@ -258,6 +578,7 @@ static struct test_peer_attr test_peer_attrs[] = {
                .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",
@@ -266,6 +587,7 @@ static struct test_peer_attr test_peer_attrs[] = {
                .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",
@@ -292,6 +614,7 @@ static struct test_peer_attr test_peer_attrs[] = {
                .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",
@@ -300,11 +623,13 @@ static struct test_peer_attr test_peer_attrs[] = {
                .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",
@@ -313,17 +638,20 @@ static struct test_peer_attr test_peer_attrs[] = {
        {
                .cmd = "send-community",
                .u.flag = PEER_FLAG_SEND_COMMUNITY,
-               .o.invert = true,
+               .o.invert_peer = true,
+               .o.invert_group = true,
        },
        {
                .cmd = "send-community extended",
                .u.flag = PEER_FLAG_SEND_EXT_COMMUNITY,
-               .o.invert = true,
+               .o.invert_peer = true,
+               .o.invert_group = true,
        },
        {
                .cmd = "send-community large",
                .u.flag = PEER_FLAG_SEND_LARGE_COMMUNITY,
-               .o.invert = true,
+               .o.invert_peer = true,
+               .o.invert_group = true,
        },
        {
                .cmd = "soft-reconfiguration inbound",
@@ -336,55 +664,19 @@ static struct test_peer_attr test_peer_attrs[] = {
                .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),
        },
        {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) {
@@ -409,6 +701,42 @@ static const char *str_from_safi(safi_t safi)
        }
 }
 
+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;
+}
+
+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_execute(struct test *test, const char *fmt, ...)
 {
        int ret;
@@ -520,19 +848,17 @@ static void test_config_absent(struct test *test, const char *fmt, ...)
        va_end(ap);
 }
 
-static struct test *test_new(const char *desc, bool use_ibgp)
+static void test_initialize(struct test *test)
 {
-       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();
+       /* Skip execution if test instance has previously failed. */
+       if (test->state != TEST_SUCCESS)
+               return;
 
-       test->vty = vty_new();
-       test->vty->type = VTY_TERM;
-       test->vty->node = CONFIG_NODE;
+       /* 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");
@@ -542,10 +868,18 @@ static struct test *test_new(const char *desc, bool use_ibgp)
        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->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 test;
+               return;
 
        /* Fetch default BGP instance. */
        test->bgp = bgp_get_default();
@@ -553,18 +887,23 @@ static struct test *test_new(const char *desc, bool use_ibgp)
                test->state = TEST_INTERNAL_ERROR;
                test->error =
                        str_printf("could not retrieve default bgp instance");
-               return test;
+               return;
        }
 
        /* Fetch peer instance. */
-       str2sockunion(cfg.peer_address, &su);
-       test->peer = peer_lookup(test->bgp, &su);
+       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 = str_printf(
                        "could not retrieve instance of bgp peer [%s]",
                        cfg.peer_address);
-               return test;
+               return;
        }
 
        /* Fetch peer-group instance. */
@@ -574,25 +913,30 @@ static struct test *test_new(const char *desc, bool use_ibgp)
                test->error = str_printf(
                        "could not retrieve instance of bgp peer-group [%s]",
                        cfg.peer_group);
-               return test;
+               return;
        }
+}
 
-       return test;
-};
-
-static void test_log(struct test *test, const char *fmt, ...)
+static struct test *test_new(const char *desc, bool use_ibgp,
+                            bool use_iface_peer)
 {
-       va_list ap;
+       struct test *test;
 
-       /* Skip logging if test instance has previously failed. */
-       if (test->state != TEST_SUCCESS)
-               return;
+       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;
 
-       /* Store formatted log message. */
-       va_start(ap, fmt);
-       listnode_add(test->log, str_vprintf(fmt, ap));
-       va_end(ap);
-}
+       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)
 {
@@ -623,7 +967,7 @@ static void test_finish(struct test *test)
                test->vty = NULL;
        }
        if (test->log)
-               list_delete_and_null(&test->log);
+               list_delete(&test->log);
        if (test->desc)
                XFREE(MTYPE_TMP, test->desc);
        if (test->error)
@@ -631,23 +975,37 @@ static void test_finish(struct test *test)
        XFREE(MTYPE_TMP, test);
 }
 
-static void test_af_flags(struct test *test, struct peer *peer,
-                         struct test_peer_attr *attr, bool exp_val,
-                         bool exp_ovrd)
+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;
 
-       /* Flip expected values for inverted flags. */
-       exp_inv = attr->o.invert;
+       /* 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. */
-       cur_val = !!CHECK_FLAG(peer->af_flags[attr->afi][attr->safi],
-                              attr->u.flag);
-       cur_ovrd = !!CHECK_FLAG(peer->af_flags_override[attr->afi][attr->safi],
-                               attr->u.flag);
-       cur_inv = !!CHECK_FLAG(peer->af_flags_invert[attr->afi][attr->safi],
-                              attr->u.flag);
+       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);
@@ -655,42 +1013,44 @@ static void test_af_flags(struct test *test, struct peer *peer,
        TEST_ASSERT_EQ(test, cur_inv, exp_inv);
 }
 
-static void test_af_filter(struct test *test, struct peer *peer,
-                          struct test_peer_attr *attr, bool exp_state,
-                          bool exp_ovrd)
+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[attr->afi][attr->safi]
-                                                    [attr->u.filter.direct],
-                               attr->u.filter.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[attr->afi][attr->safi];
+       filter = &peer->filter[pa->afi][pa->safi];
 
-       switch (attr->u.filter.flag) {
+       switch (pa->u.filter.flag) {
        case PEER_FT_DISTRIBUTE_LIST:
                TEST_ASSERT_EQ(test,
-                              !!(filter->dlist[attr->u.filter.direct].name),
+                              !!(filter->dlist[pa->u.filter.direct].name),
                               exp_state);
                break;
        case PEER_FT_FILTER_LIST:
                TEST_ASSERT_EQ(test,
-                              !!(filter->aslist[attr->u.filter.direct].name),
+                              !!(filter->aslist[pa->u.filter.direct].name),
                               exp_state);
                break;
        case PEER_FT_PREFIX_LIST:
                TEST_ASSERT_EQ(test,
-                              !!(filter->plist[attr->u.filter.direct].name),
+                              !!(filter->plist[pa->u.filter.direct].name),
                               exp_state);
                break;
        case PEER_FT_ROUTE_MAP:
-               TEST_ASSERT_EQ(test,
-                              !!(filter->map[attr->u.filter.direct].name),
+               TEST_ASSERT_EQ(test, !!(filter->map[pa->u.filter.direct].name),
                               exp_state);
                break;
        case PEER_FT_UNSUPPRESS_MAP:
@@ -699,173 +1059,322 @@ static void test_af_filter(struct test *test, struct peer *peer,
        }
 }
 
+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 = str_printf("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 =
+                       str_printf("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 *ec = pa->o.invert ? "no " : "";
-       const char *dc = pa->o.invert ? "" : "no ";
+       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;
 
-       if (pa->type == PEER_AT_AF_FLAG)
-               type = "af-flag";
-       else /* if (pa->type == PEER_AT_AF_FILTER) */
-               type = "af-filter";
+       /* 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 =
+                       str_printf("invalid attribute type: %d", pa->type);
+               return;
+       }
 
-       /* Test Case: Switch active address-family. */
-       if (pa->type == PEER_AT_AF_FLAG || pa->type == PEER_AT_AF_FILTER) {
+       /*
+        * =====================================================================
+        * 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]",
                         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_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", ec, p->host, peer_cmd);
-       test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd);
+       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);
-       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_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 peer-group %s", p->host,
-                           g->name);
-       test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd);
+       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);
-       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_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]",
+                        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_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]",
+                        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_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 peer-group %s", p->host,
-                           g->name);
-       test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd);
+       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);
-       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_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", 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_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", dc, g->name, group_cmd);
-       test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd);
+       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);
-       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_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", 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_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", 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_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", dc, p->host, peer_cmd);
+       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", 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_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", dc, g->name, group_cmd);
+       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);
-       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_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", ec, p->host, peer_cmd);
-       test_config_present(test, "%sneighbor %s %s", ec, p->host, peer_cmd);
+       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);
-       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_process(test, pa, p, g->conf, true, false);
 }
 
 static void bgp_startup(void)
@@ -877,10 +1386,12 @@ static void bgp_startup(void)
        zprivs_init(&bgpd_privs);
 
        master = thread_master_create(NULL);
+       yang_init();
+       nb_init(master, NULL, 0);
        bgp_master_init(master);
        bgp_option_set(BGP_OPT_NO_LISTEN);
-       vrf_init(NULL, NULL, NULL, NULL);
-       bgp_init();
+       vrf_init(NULL, NULL, NULL, NULL, NULL);
+       bgp_init(0);
        bgp_pthreads_run();
 }
 
@@ -915,11 +1426,13 @@ static void bgp_shutdown(void)
        bgp_zebra_destroy();
 
        bf_free(bm->rd_idspace);
-       list_delete_and_null(&bm->bgp);
+       list_delete(&bm->bgp);
        memset(bm, 0, sizeof(*bm));
 
        vty_terminate();
        cmd_terminate();
+       nb_terminate();
+       yang_terminate();
        zprivs_terminate(&bgpd_privs);
        thread_master_free(master);
        master = NULL;
@@ -941,7 +1454,7 @@ int main(void)
                pa = &test_peer_attrs[i++];
 
                /* Just copy the peer attribute structure for global flags. */
-               if (pa->type == PEER_AT_GLOBAL_FLAG) {
+               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);
@@ -980,7 +1493,7 @@ int main(void)
                        desc = str_printf("peer\\%s", pa->cmd);
 
                /* Initialize new test instance. */
-               test = test_new(desc, pa->o.use_ibgp);
+               test = test_new(desc, pa->o.use_ibgp, pa->o.use_iface_peer);
                XFREE(MTYPE_TMP, desc);
 
                /* Execute tests and finish test instance. */
@@ -994,7 +1507,7 @@ int main(void)
                XFREE(MTYPE_TMP, pa);
        }
 
-       list_delete_and_null(&pa_list);
+       list_delete(&pa_list);
        bgp_shutdown();
 
        return 0;