Add test coverage for the multi-VRF IPv4 FIB API. The app/test-fib functional harness exercises VRF-aware routes with nexthop encoding, bulk VRF lookup, and nexthop validation across multiple VRFs (enabled with the -V option).
The app/test unit tests cover VRF table creation, per-VRF route add and delete, lookup correctness, inter-VRF isolation, and all supported nexthop sizes. Signed-off-by: Vladimir Medvedkin <[email protected]> --- app/test-fib/main.c | 167 +++++++++++++++++++++++-- app/test/test_fib.c | 298 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 454 insertions(+), 11 deletions(-) diff --git a/app/test-fib/main.c b/app/test-fib/main.c index dd1a6d7297..5593fdd47e 100644 --- a/app/test-fib/main.c +++ b/app/test-fib/main.c @@ -82,6 +82,7 @@ static struct { uint32_t nb_routes_per_depth[128 + 1]; uint32_t flags; uint32_t tbl8; + uint32_t nb_vrfs; uint8_t ent_sz; uint8_t rnd_lookup_ips_ratio; uint8_t print_fract; @@ -95,6 +96,7 @@ static struct { .nb_routes_per_depth = {0}, .flags = FIB_V4_DIR_TYPE, .tbl8 = DEFAULT_LPM_TBL8, + .nb_vrfs = 1, .ent_sz = 4, .rnd_lookup_ips_ratio = 0, .print_fract = 10, @@ -136,6 +138,45 @@ get_max_nh(uint8_t nh_sz) (1ULL << 21) - 1); } +/* Get number of bits needed to encode VRF IDs */ +static __rte_always_inline __rte_pure uint8_t +get_vrf_bits(uint32_t nb_vrfs) +{ + uint8_t bits = 0; + uint32_t vrfs = nb_vrfs > 1 ? nb_vrfs : 2; /* At least 1 bit */ + + /* Round up to next power of 2 */ + vrfs--; + while (vrfs > 0) { + bits++; + vrfs >>= 1; + } + return bits; +} + +/* Encode VRF ID into nexthop (high bits) */ +static __rte_always_inline uint64_t +encode_vrf_nh(uint16_t vrf_id, uint64_t nh, uint8_t nh_sz) +{ + uint8_t vrf_bits = get_vrf_bits(config.nb_vrfs); + /* +1 to account for ext bit in nexthop */ + uint8_t vrf_shift = bits_in_nh(nh_sz) - (vrf_bits + 1); + uint64_t nh_mask = (1ULL << vrf_shift) - 1; + + return (nh & nh_mask) | ((uint64_t)vrf_id << vrf_shift); +} + +/* Decode VRF ID from nexthop (high bits) */ +static __rte_always_inline uint16_t +decode_vrf_nh(uint64_t nh, uint8_t nh_sz) +{ + uint8_t vrf_bits = get_vrf_bits(config.nb_vrfs); + /* +1 to account for ext bit in nexthop */ + uint8_t vrf_shift = bits_in_nh(nh_sz) - (vrf_bits + 1); + + return (uint16_t)(nh >> vrf_shift); +} + static int get_fib_type(void) { @@ -629,7 +670,8 @@ print_usage(void) "[-v <type of lookup function:" "\ts1, s2, s3 (3 types of scalar), v (vector) -" " for DIR24_8 based FIB\n" - "\ts, v - for TRIE based ipv6 FIB>]\n", + "\ts, v - for TRIE based ipv6 FIB>]\n" + "[-V <number of VRFs (default 1)>]\n", config.prgname); } @@ -652,6 +694,11 @@ check_config(void) return -1; } + if ((config.flags & CMP_FLAG) && (config.nb_vrfs > 1)) { + printf("-c option can not be used with -V > 1\n"); + return -1; + } + if (!((config.ent_sz == 1) || (config.ent_sz == 2) || (config.ent_sz == 4) || (config.ent_sz == 8))) { printf("wrong -e option %d, can be 1 or 2 or 4 or 8\n", @@ -663,6 +710,24 @@ check_config(void) printf("-e 1 is valid only for ipv4\n"); return -1; } + + /* + * For multi-VRF mode, VRF IDs are packed into the high bits of the + * nexthop for validation. Ensure there are enough bits: + * get_vrf_bits(nb_vrfs) must be strictly less than + * the total nexthop width. + */ + if ((config.nb_vrfs > 1) && !(config.flags & IPV6_FLAG)) { + uint8_t nh_sz = rte_ctz32(config.ent_sz); + uint8_t vrf_bits = get_vrf_bits(config.nb_vrfs); + /* - 2 to leave at least 1 bit for nexthop and 1 bit for ext_ent flag */ + if (vrf_bits >= bits_in_nh(nh_sz) - 2) { + printf("%u VRFs cannot be encoded in a %u-byte nexthop; " + "use a wider -e entry size\n", + config.nb_vrfs, config.ent_sz); + return -1; + } + } return 0; } @@ -672,7 +737,7 @@ parse_opts(int argc, char **argv) int opt; char *endptr; - while ((opt = getopt(argc, argv, "f:t:n:d:l:r:c6ab:e:g:w:u:sv:")) != + while ((opt = getopt(argc, argv, "f:t:n:d:l:r:c6ab:e:g:w:u:sv:V:")) != -1) { switch (opt) { case 'f': @@ -781,6 +846,16 @@ parse_opts(int argc, char **argv) } print_usage(); rte_exit(-EINVAL, "Invalid option -v %s\n", optarg); + case 'V': + errno = 0; + config.nb_vrfs = strtoul(optarg, &endptr, 10); + /* VRF IDs are uint16_t, max valid VRF is 65535 */ + if ((errno != 0) || (config.nb_vrfs == 0) || + (config.nb_vrfs > UINT16_MAX)) { + print_usage(); + rte_exit(-EINVAL, "Invalid option -V: must be 1..65535\n"); + } + break; default: print_usage(); rte_exit(-EINVAL, "Invalid options\n"); @@ -820,6 +895,7 @@ run_v4(void) { uint64_t start, acc; uint64_t def_nh = 0; + uint8_t nh_sz = rte_ctz32(config.ent_sz); struct rte_fib *fib; struct rte_fib_conf conf = {0}; struct rt_rule_4 *rt; @@ -830,6 +906,7 @@ run_v4(void) uint32_t *tbl4 = config.lookup_tbl; uint64_t fib_nh[BURST_SZ]; uint32_t lpm_nh[BURST_SZ]; + uint16_t *vrf_ids = NULL; rt = (struct rt_rule_4 *)config.rt; @@ -843,16 +920,38 @@ run_v4(void) return ret; } + /* Allocate VRF IDs array for lookups if using multiple VRFs */ + if (config.nb_vrfs > 1) { + vrf_ids = rte_malloc(NULL, sizeof(uint16_t) * config.nb_lookup_ips, 0); + if (vrf_ids == NULL) { + printf("Can not alloc VRF IDs array\n"); + return -ENOMEM; + } + /* Generate random VRF IDs for each lookup */ + for (i = 0; i < config.nb_lookup_ips; i++) + vrf_ids[i] = rte_rand() % config.nb_vrfs; + } + conf.type = get_fib_type(); conf.default_nh = def_nh; conf.max_routes = config.nb_routes * 2; conf.rib_ext_sz = 0; + conf.max_vrfs = config.nb_vrfs; + conf.vrf_default_nh = NULL; /* Use global default for single VRF */ if (conf.type == RTE_FIB_DIR24_8) { conf.dir24_8.nh_sz = rte_ctz32(config.ent_sz); conf.dir24_8.num_tbl8 = RTE_MIN(config.tbl8, get_max_nh(conf.dir24_8.nh_sz)); } + conf.vrf_default_nh = rte_malloc(NULL, conf.max_vrfs * sizeof(uint64_t), 0); + if (conf.vrf_default_nh == NULL) { + printf("Can not alloc VRF default nexthops array\n"); + return -ENOMEM; + } + for (i = 0; i < conf.max_vrfs; i++) + conf.vrf_default_nh[i] = encode_vrf_nh(i, def_nh, nh_sz); + fib = rte_fib_create("test", -1, &conf); if (fib == NULL) { printf("Can not alloc FIB, err %d\n", rte_errno); @@ -883,12 +982,27 @@ run_v4(void) for (k = config.print_fract, i = 0; k > 0; k--) { start = rte_rdtsc_precise(); for (j = 0; j < (config.nb_routes - i) / k; j++) { - ret = rte_fib_add(fib, rt[i + j].addr, rt[i + j].depth, - rt[i + j].nh); - if (unlikely(ret != 0)) { - printf("Can not add a route to FIB, err %d\n", - ret); - return -ret; + uint32_t idx = i + j; + if (config.nb_vrfs > 1) { + uint16_t vrf_id; + for (vrf_id = 0; vrf_id < config.nb_vrfs; vrf_id++) { + uint64_t nh = encode_vrf_nh(vrf_id, rt[idx].nh, nh_sz); + ret = rte_fib_vrf_add(fib, vrf_id, rt[idx].addr, + rt[idx].depth, nh); + if (unlikely(ret != 0)) { + printf("Can not add a route to FIB, err %d\n", + ret); + return -ret; + } + } + } else { + ret = rte_fib_add(fib, rt[idx].addr, rt[idx].depth, + rt[idx].nh); + if (unlikely(ret != 0)) { + printf("Can not add a route to FIB, err %d\n", + ret); + return -ret; + } } } printf("AVG FIB add %"PRIu64"\n", @@ -928,14 +1042,33 @@ run_v4(void) acc = 0; for (i = 0; i < config.nb_lookup_ips; i += BURST_SZ) { start = rte_rdtsc_precise(); - ret = rte_fib_lookup_bulk(fib, tbl4 + i, fib_nh, BURST_SZ); + if (config.nb_vrfs > 1) + ret = rte_fib_vrf_lookup_bulk(fib, vrf_ids + i, + tbl4 + i, fib_nh, BURST_SZ); + else + ret = rte_fib_lookup_bulk(fib, tbl4 + i, fib_nh, + BURST_SZ); acc += rte_rdtsc_precise() - start; if (ret != 0) { printf("FIB lookup fails, err %d\n", ret); return -ret; } + /* Validate VRF IDs in returned nexthops */ + if (config.nb_vrfs > 1) { + for (j = 0; j < BURST_SZ; j++) { + uint16_t returned_vrf = decode_vrf_nh(fib_nh[j], nh_sz); + if (returned_vrf != vrf_ids[i + j]) { + printf("VRF validation failed: " + "expected VRF %u, got %u\n", + vrf_ids[i + j], returned_vrf); + return -1; + } + } + } } printf("AVG FIB lookup %.1f\n", (double)acc / (double)i); + if (config.nb_vrfs > 1) + printf("VRF validation passed\n"); if (config.flags & CMP_FLAG) { acc = 0; @@ -970,8 +1103,17 @@ run_v4(void) for (k = config.print_fract, i = 0; k > 0; k--) { start = rte_rdtsc_precise(); - for (j = 0; j < (config.nb_routes - i) / k; j++) - rte_fib_delete(fib, rt[i + j].addr, rt[i + j].depth); + for (j = 0; j < (config.nb_routes - i) / k; j++) { + uint32_t idx = i + j; + if (config.nb_vrfs > 1) { + uint16_t vrf_id; + for (vrf_id = 0; vrf_id < config.nb_vrfs; vrf_id++) + rte_fib_vrf_delete(fib, vrf_id, rt[idx].addr, + rt[idx].depth); + } else { + rte_fib_delete(fib, rt[idx].addr, rt[idx].depth); + } + } printf("AVG FIB delete %"PRIu64"\n", (rte_rdtsc_precise() - start) / j); @@ -991,6 +1133,9 @@ run_v4(void) } } + if (vrf_ids != NULL) + rte_free(vrf_ids); + return 0; } diff --git a/app/test/test_fib.c b/app/test/test_fib.c index bd73399d56..6a5d9836de 100644 --- a/app/test/test_fib.c +++ b/app/test/test_fib.c @@ -24,6 +24,11 @@ static int32_t test_get_invalid(void); static int32_t test_lookup(void); static int32_t test_invalid_rcu(void); static int32_t test_fib_rcu_sync_rw(void); +static int32_t test_create_vrf(void); +static int32_t test_vrf_add_del(void); +static int32_t test_vrf_lookup(void); +static int32_t test_vrf_isolation(void); +static int32_t test_vrf_all_nh_sizes(void); #define MAX_ROUTES (1 << 16) #define MAX_TBL8 (1 << 15) @@ -588,6 +593,294 @@ test_fib_rcu_sync_rw(void) return status == 0 ? TEST_SUCCESS : TEST_FAILED; } +/* + * Test VRF creation and basic operations + */ +static int32_t +test_create_vrf(void) +{ + struct rte_fib *fib = NULL; + struct rte_fib_conf config = { 0 }; + uint64_t def_nh = 100; + uint64_t vrf_def_nh[4] = {100, 200, 300, 400}; + + config.max_routes = MAX_ROUTES; + config.rib_ext_sz = 0; + config.default_nh = def_nh; + config.type = RTE_FIB_DIR24_8; + config.dir24_8.nh_sz = RTE_FIB_DIR24_8_4B; + config.dir24_8.num_tbl8 = MAX_TBL8; + + /* Test single VRF (backward compat) */ + config.max_vrfs = 0; + config.vrf_default_nh = NULL; + fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config); + RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB with max_vrfs=0\n"); + rte_fib_free(fib); + + /* Test single VRF explicitly */ + config.max_vrfs = 1; + fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config); + RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB with max_vrfs=1\n"); + rte_fib_free(fib); + + /* Test multi-VRF with per-VRF defaults */ + config.max_vrfs = 4; + config.vrf_default_nh = vrf_def_nh; + fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config); + RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB with max_vrfs=4\n"); + rte_fib_free(fib); + + return TEST_SUCCESS; +} + +/* + * Test VRF route add/delete operations + */ +static int32_t +test_vrf_add_del(void) +{ + struct rte_fib *fib = NULL; + struct rte_fib_conf config = { 0 }; + uint64_t def_nh = 100; + uint64_t vrf_def_nh[4] = {100, 200, 300, 400}; + uint32_t ip = RTE_IPV4(192, 168, 1, 0); + uint8_t depth = 24; + uint64_t nh = 1000; + int ret; + + config.max_routes = MAX_ROUTES; + config.rib_ext_sz = 0; + config.default_nh = def_nh; + config.type = RTE_FIB_DIR24_8; + config.dir24_8.nh_sz = RTE_FIB_DIR24_8_4B; + config.dir24_8.num_tbl8 = MAX_TBL8; + config.max_vrfs = 4; + config.vrf_default_nh = vrf_def_nh; + + fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config); + RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n"); + + /* Add route to VRF 0 */ + ret = rte_fib_vrf_add(fib, 0, ip, depth, nh); + RTE_TEST_ASSERT(ret == 0, "Failed to add route to VRF 0\n"); + + /* Add route to VRF 1 with different nexthop */ + ret = rte_fib_vrf_add(fib, 1, ip, depth, nh + 1); + RTE_TEST_ASSERT(ret == 0, "Failed to add route to VRF 1\n"); + + /* Add route to VRF 2 */ + ret = rte_fib_vrf_add(fib, 2, ip, depth, nh + 2); + RTE_TEST_ASSERT(ret == 0, "Failed to add route to VRF 2\n"); + + /* Test invalid VRF ID */ + ret = rte_fib_vrf_add(fib, 10, ip, depth, nh); + RTE_TEST_ASSERT(ret != 0, "Should fail with invalid VRF ID\n"); + + /* Delete route from VRF 1 */ + ret = rte_fib_vrf_delete(fib, 1, ip, depth); + RTE_TEST_ASSERT(ret == 0, "Failed to delete route from VRF 1\n"); + + /* Delete non-existent route - implementation may return error */ + ret = rte_fib_vrf_delete(fib, 3, ip, depth); + (void)ret; /* Accept any return value */ + + rte_fib_free(fib); + return TEST_SUCCESS; +} + +/* + * Test VRF lookup functionality + */ +static int32_t +test_vrf_lookup(void) +{ + struct rte_fib *fib = NULL; + struct rte_fib_conf config = { 0 }; + uint64_t def_nh = 100; + uint64_t vrf_def_nh[4] = {1000, 2000, 3000, 4000}; + uint32_t ip_base = RTE_IPV4(10, 0, 0, 0); + uint16_t vrf_ids[8]; + uint32_t ips[8]; + uint64_t next_hops[8]; + int ret; + uint32_t i; + + config.max_routes = MAX_ROUTES; + config.rib_ext_sz = 0; + config.default_nh = def_nh; + config.type = RTE_FIB_DIR24_8; + config.dir24_8.nh_sz = RTE_FIB_DIR24_8_4B; + config.dir24_8.num_tbl8 = MAX_TBL8; + config.max_vrfs = 4; + config.vrf_default_nh = vrf_def_nh; + + fib = rte_fib_create(__func__, SOCKET_ID_ANY, &config); + RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n"); + + /* Add routes to different VRFs with VRF-specific nexthops */ + for (i = 0; i < 4; i++) { + ret = rte_fib_vrf_add(fib, i, ip_base + (i << 16), 16, 100 + i); + RTE_TEST_ASSERT(ret == 0, "Failed to add route to VRF %u\n", i); + } + + /* Prepare lookup: each IP should match its VRF-specific route */ + for (i = 0; i < 4; i++) { + vrf_ids[i] = i; + ips[i] = ip_base + (i << 16) + 0x1234; /* Within the /16 */ + } + + /* Lookup should return VRF-specific nexthops */ + ret = rte_fib_vrf_lookup_bulk(fib, vrf_ids, ips, next_hops, 4); + RTE_TEST_ASSERT(ret == 0, "VRF lookup failed\n"); + + for (i = 0; i < 4; i++) { + RTE_TEST_ASSERT(next_hops[i] == 100 + i, + "Wrong nexthop for VRF %u: expected %"PRIu64", got %"PRIu64"\n", + i, (uint64_t)(100 + i), next_hops[i]); + } + + /* Test default nexthops for unmatched IPs */ + for (i = 0; i < 4; i++) { + vrf_ids[i] = i; + ips[i] = RTE_IPV4(192, 168, i, 1); /* No route for these */ + } + + ret = rte_fib_vrf_lookup_bulk(fib, vrf_ids, ips, next_hops, 4); + RTE_TEST_ASSERT(ret == 0, "VRF lookup failed\n"); + + for (i = 0; i < 4; i++) { + RTE_TEST_ASSERT(next_hops[i] == vrf_def_nh[i], + "Wrong default nexthop for VRF %u: expected %"PRIu64", got %"PRIu64"\n", + i, vrf_def_nh[i], next_hops[i]); + } + + rte_fib_free(fib); + return TEST_SUCCESS; +} + +/* + * Test VRF isolation - routes in one VRF shouldn't affect others + */ +static int32_t +test_vrf_isolation(void) +{ + struct rte_fib *fib = NULL; + struct rte_fib_conf config = { 0 }; + uint64_t vrf_def_nh[3] = {100, 200, 300}; + uint32_t ip = RTE_IPV4(10, 10, 10, 0); + uint16_t vrf_ids[3] = {0, 1, 2}; + uint32_t ips[3]; + uint64_t next_hops[3]; + int ret; + uint32_t i; + + config.max_routes = MAX_ROUTES; + config.rib_ext_sz = 0; + config.default_nh = 0; + config.type = RTE_FIB_DIR24_8; + config.dir24_8.nh_sz = RTE_FIB_DIR24_8_4B; + config.dir24_8.num_tbl8 = MAX_TBL8; + config.max_vrfs = 3; + config.vrf_default_nh = vrf_def_nh; + + fib = rte_fib_create("test_vrfisol", SOCKET_ID_ANY, &config); + RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n"); + + /* Add route only to VRF 1 */ + ret = rte_fib_vrf_add(fib, 1, ip, 24, 777); + RTE_TEST_ASSERT(ret == 0, "Failed to add route to VRF 1\n"); + + /* Lookup same IP in all three VRFs */ + for (i = 0; i < 3; i++) + ips[i] = ip + 15; /* Within /24 */ + + ret = rte_fib_vrf_lookup_bulk(fib, vrf_ids, ips, next_hops, 3); + RTE_TEST_ASSERT(ret == 0, "VRF lookup failed\n"); + + /* VRF 0 should get default */ + RTE_TEST_ASSERT(next_hops[0] == vrf_def_nh[0], + "VRF 0 should return default nexthop\n"); + + /* VRF 1 should get the route */ + RTE_TEST_ASSERT(next_hops[1] == 777, + "VRF 1 should return route nexthop 777, got %"PRIu64"\n", next_hops[1]); + + /* VRF 2 should get default */ + RTE_TEST_ASSERT(next_hops[2] == vrf_def_nh[2], + "VRF 2 should return default nexthop\n"); + + rte_fib_free(fib); + return TEST_SUCCESS; +} + +/* + * Test multi-VRF with all nexthop sizes + */ +static int32_t +test_vrf_all_nh_sizes(void) +{ + struct rte_fib *fib = NULL; + struct rte_fib_conf config = { 0 }; + uint64_t vrf_def_nh[2] = {10, 20}; + uint32_t ip = RTE_IPV4(172, 16, 0, 0); + uint16_t vrf_ids[2] = {0, 1}; + uint32_t ips[2]; + uint64_t next_hops[2]; + int ret; + enum rte_fib_dir24_8_nh_sz nh_sizes[] = { + RTE_FIB_DIR24_8_1B, + RTE_FIB_DIR24_8_2B, + RTE_FIB_DIR24_8_4B, + RTE_FIB_DIR24_8_8B + }; + uint64_t max_nhs[] = {127, 32767, 2147483647ULL, 9223372036854775807ULL}; + int i; + + config.max_routes = MAX_ROUTES; + config.rib_ext_sz = 0; + config.default_nh = 0; + config.type = RTE_FIB_DIR24_8; + config.dir24_8.num_tbl8 = 127; + config.max_vrfs = 2; + config.vrf_default_nh = vrf_def_nh; + + for (i = 0; i < (int)RTE_DIM(nh_sizes); i++) { + char name[32]; + config.dir24_8.nh_sz = nh_sizes[i]; + snprintf(name, sizeof(name), "vrf_nh%d", i); + + fib = rte_fib_create(name, SOCKET_ID_ANY, &config); + RTE_TEST_ASSERT(fib != NULL, "Failed to create FIB\n"); + + /* Add routes with max nexthop for this size */ + ret = rte_fib_vrf_add(fib, 0, ip, 16, max_nhs[i]); + RTE_TEST_ASSERT(ret == 0, + "Failed to add route to VRF 0 with nh_sz=%d\n", nh_sizes[i]); + + ret = rte_fib_vrf_add(fib, 1, ip, 16, max_nhs[i] - 1); + RTE_TEST_ASSERT(ret == 0, + "Failed to add route to VRF 1 with nh_sz=%d\n", nh_sizes[i]); + + /* Lookup */ + ips[0] = ip + 0x100; + ips[1] = ip + 0x200; + + ret = rte_fib_vrf_lookup_bulk(fib, vrf_ids, ips, next_hops, 2); + RTE_TEST_ASSERT(ret == 0, "VRF lookup failed with nh_sz=%d\n", nh_sizes[i]); + + RTE_TEST_ASSERT(next_hops[0] == max_nhs[i], + "Wrong nexthop for VRF 0 with nh_sz=%d\n", nh_sizes[i]); + RTE_TEST_ASSERT(next_hops[1] == max_nhs[i] - 1, + "Wrong nexthop for VRF 1 with nh_sz=%d\n", nh_sizes[i]); + + rte_fib_free(fib); + fib = NULL; + } + + return TEST_SUCCESS; +} + static struct unit_test_suite fib_fast_tests = { .suite_name = "fib autotest", .setup = NULL, @@ -600,6 +893,11 @@ static struct unit_test_suite fib_fast_tests = { TEST_CASE(test_lookup), TEST_CASE(test_invalid_rcu), TEST_CASE(test_fib_rcu_sync_rw), + TEST_CASE(test_create_vrf), + TEST_CASE(test_vrf_add_del), + TEST_CASE(test_vrf_lookup), + TEST_CASE(test_vrf_isolation), + TEST_CASE(test_vrf_all_nh_sizes), TEST_CASES_END() } }; -- 2.43.0

