From b3cc2fa04a2298de7b21a2dcb97406b28d671aa1 Mon Sep 17 00:00:00 2001 From: Louis Scalbert Date: Wed, 20 Nov 2024 16:33:55 +0100 Subject: [PATCH 1/8] zebra: fix table heap-after-free crash Fix a heap-after-free that causes zebra to crash even without address-sanitizer. To reproduce: > echo "100 my_table" | tee -a /etc/iproute2/rt_tables > ip route add blackhole default table 100 > ip route show table 100 > ip l add red type vrf table 100 > ip l del red > ip route del blackhole default table 100 Zebra manages routing tables for all existing Linux RT tables, regardless of whether they are assigned to a VRF interface. When a table is not assigned to any VRF, zebra arbitrarily assigns it to the default VRF, even though this is not strictly accurate (the code expects this behavior). When an RT table is created after a VRF, zebra correctly assigns the table to the VRF. However, if a VRF interface is assigned to an existing RT table, zebra does not update the table owner, which remains as the default VRF. As a result, existing routing entries remain under the default VRF, while new entries are correctly assigned to the VRF. The VRF mismatch is unexpected in the code and creates crashes and memory related issues. Furthermore, Linux does not automatically delete RT tables when they are unassigned from a VRF. It is incorrect to delete these tables from zebra. Instead, at VRF disabling, do not release the table but reassign it to the default VRF. At VRF enabling, change the table owner back to the appropriate VRF. > ==2866266==ERROR: AddressSanitizer: heap-use-after-free on address 0x606000154f54 at pc 0x7fa32474b83f bp 0x7ffe94f67d90 sp 0x7ffe94f67d88 > READ of size 1 at 0x606000154f54 thread T0 > #0 0x7fa32474b83e in rn_hash_node_const_find lib/table.c:28 > #1 0x7fa32474bab1 in rn_hash_node_find lib/table.c:28 > #2 0x7fa32474d783 in route_node_get lib/table.c:283 > #3 0x7fa3247328dd in srcdest_rnode_get lib/srcdest_table.c:231 > #4 0x55b0e4fa8da4 in rib_find_rn_from_ctx zebra/zebra_rib.c:1957 > #5 0x55b0e4fa8e31 in rib_process_result zebra/zebra_rib.c:1988 > #6 0x55b0e4fb9d64 in rib_process_dplane_results zebra/zebra_rib.c:4894 > #7 0x7fa32476689c in event_call lib/event.c:1996 > #8 0x7fa32463b7b2 in frr_run lib/libfrr.c:1232 > #9 0x55b0e4e6c32a in main zebra/main.c:526 > #10 0x7fa32424fd09 in __libc_start_main ../csu/libc-start.c:308 > #11 0x55b0e4e2d649 in _start (/usr/lib/frr/zebra+0x1a1649) > > 0x606000154f54 is located 20 bytes inside of 56-byte region [0x606000154f40,0x606000154f78) > freed by thread T0 here: > #0 0x7fa324ca9b6f in __interceptor_free ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:123 > #1 0x7fa324668d8f in qfree lib/memory.c:130 > #2 0x7fa32474c421 in route_table_free lib/table.c:126 > #3 0x7fa32474bf96 in route_table_finish lib/table.c:46 > #4 0x55b0e4fbca3a in zebra_router_free_table zebra/zebra_router.c:191 > #5 0x55b0e4fbccea in zebra_router_release_table zebra/zebra_router.c:214 > #6 0x55b0e4fd428e in zebra_vrf_disable zebra/zebra_vrf.c:219 > #7 0x7fa32476fabf in vrf_disable lib/vrf.c:326 > #8 0x7fa32476f5d4 in vrf_delete lib/vrf.c:231 > #9 0x55b0e4e4ad36 in interface_vrf_change zebra/interface.c:1478 > #10 0x55b0e4e4d5d2 in zebra_if_dplane_ifp_handling zebra/interface.c:1949 > #11 0x55b0e4e4fb89 in zebra_if_dplane_result zebra/interface.c:2268 > #12 0x55b0e4fb9f26 in rib_process_dplane_results zebra/zebra_rib.c:4954 > #13 0x7fa32476689c in event_call lib/event.c:1996 > #14 0x7fa32463b7b2 in frr_run lib/libfrr.c:1232 > #15 0x55b0e4e6c32a in main zebra/main.c:526 > #16 0x7fa32424fd09 in __libc_start_main ../csu/libc-start.c:308 > > previously allocated by thread T0 here: > #0 0x7fa324caa037 in __interceptor_calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:154 > #1 0x7fa324668c4d in qcalloc lib/memory.c:105 > #2 0x7fa32474bf33 in route_table_init_with_delegate lib/table.c:38 > #3 0x7fa32474e73c in route_table_init lib/table.c:512 > #4 0x55b0e4fbc353 in zebra_router_get_table zebra/zebra_router.c:137 > #5 0x55b0e4fd4da0 in zebra_vrf_table_create zebra/zebra_vrf.c:358 > #6 0x55b0e4fd3d30 in zebra_vrf_enable zebra/zebra_vrf.c:140 > #7 0x7fa32476f9b2 in vrf_enable lib/vrf.c:286 > #8 0x55b0e4e4af76 in interface_vrf_change zebra/interface.c:1533 > #9 0x55b0e4e4d612 in zebra_if_dplane_ifp_handling zebra/interface.c:1968 > #10 0x55b0e4e4fb89 in zebra_if_dplane_result zebra/interface.c:2268 > #11 0x55b0e4fb9f26 in rib_process_dplane_results zebra/zebra_rib.c:4954 > #12 0x7fa32476689c in event_call lib/event.c:1996 > #13 0x7fa32463b7b2 in frr_run lib/libfrr.c:1232 > #14 0x55b0e4e6c32a in main zebra/main.c:526 > #15 0x7fa32424fd09 in __libc_start_main ../csu/libc-start.c:308 Fixes: d8612e6 ("zebra: Track tables allocated by vrf and cleanup") Signed-off-by: Louis Scalbert --- zebra/zebra_nhg.c | 62 ++++++++++++++++++++++++++++++++++++++ zebra/zebra_nhg.h | 1 + zebra/zebra_vrf.c | 77 +++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 137 insertions(+), 3 deletions(-) diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 1519246c179e..e114a9d20e31 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -2923,6 +2923,68 @@ static uint32_t proto_nhg_nexthop_active_update(struct nexthop_group *nhg) return curr_active; } +void nexthop_vrf_update(struct route_node *rn, struct route_entry *re, vrf_id_t vrf_id) +{ + struct nhg_hash_entry *curr_nhe, *new_nhe; + afi_t rt_afi = family2afi(rn->p.family); + struct nexthop *nexthop; + + re->vrf_id = vrf_id; + + /* Make a local copy of the existing nhe, so we don't work on/modify + * the shared nhe. + */ + curr_nhe = zebra_nhe_copy(re->nhe, re->nhe->id); + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: re %p nhe %p (%pNG), curr_nhe %p", __func__, re, re->nhe, re->nhe, + curr_nhe); + + /* Clear the existing id, if any: this will avoid any confusion + * if the id exists, and will also force the creation + * of a new nhe reflecting the changes we may make in this local copy. + */ + curr_nhe->id = 0; + + curr_nhe->vrf_id = vrf_id; + for (ALL_NEXTHOPS(curr_nhe->nhg, nexthop)) { + if (!nexthop->ifindex) + /* change VRF ID of nexthop without interfaces + * (eg. blackhole) + */ + nexthop->vrf_id = vrf_id; + } + + if (zebra_nhg_get_backup_nhg(curr_nhe)) { + for (ALL_NEXTHOPS(curr_nhe->backup_info->nhe->nhg, nexthop)) { + if (!nexthop->ifindex) + /* change VRF ID of nexthop without interfaces + * (eg. blackhole) + */ + nexthop->vrf_id = vrf_id; + } + } + + /* + * Ref or create an nhe that matches the current state of the + * nexthop(s). + */ + new_nhe = zebra_nhg_rib_find_nhe(curr_nhe, rt_afi); + + if (IS_ZEBRA_DEBUG_NHG_DETAIL) + zlog_debug("%s: re %p CHANGED: nhe %p (%pNG) => new_nhe %p (%pNG)", __func__, re, + re->nhe, re->nhe, new_nhe, new_nhe); + + route_entry_update_nhe(re, new_nhe); + + /* + * Do not need the old / copied nhe anymore since it + * was either copied over into a new nhe or not + * used at all. + */ + zebra_nhg_free(curr_nhe); +} + /* * This function takes the start of two comparable nexthops from two different * nexthop groups and walks them to see if they can be considered the same diff --git a/zebra/zebra_nhg.h b/zebra/zebra_nhg.h index 0f90627a0d15..de6f6123aa16 100644 --- a/zebra/zebra_nhg.h +++ b/zebra/zebra_nhg.h @@ -401,6 +401,7 @@ extern void zebra_nhg_mark_keep(void); /* Nexthop resolution processing */ struct route_entry; /* Forward ref to avoid circular includes */ +extern void nexthop_vrf_update(struct route_node *rn, struct route_entry *re, vrf_id_t vrf_id); extern int nexthop_active_update(struct route_node *rn, struct route_entry *re, struct route_entry *old_re); diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 2b3cfc876664..eba019913269 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -154,6 +154,45 @@ static int zebra_vrf_enable(struct vrf *vrf) return 0; } +/* update the VRF ID of a routing table and their routing entries */ +static void zebra_vrf_disable_update_vrfid(struct zebra_vrf *zvrf, afi_t afi, safi_t safi) +{ + struct rib_table_info *info; + struct route_entry *re; + struct route_node *rn; + bool empty_table = true; + + /* Assign the table to the default VRF. + * Although the table is not technically owned by the default VRF, + * the code assumes that unassigned routing tables are + * associated with the default VRF. + */ + info = route_table_get_info(zvrf->table[afi][safi]); + info->zvrf = vrf_info_lookup(VRF_DEFAULT); + + rn = route_top(zvrf->table[afi][safi]); + if (rn) + empty_table = false; + while (rn) { + if (!rn->info) { + rn = route_next(rn); + continue; + } + + /* Assign the route entries to the default VRF, + * even though they are not actually owned by it. + */ + RNODE_FOREACH_RE (rn, re) + nexthop_vrf_update(rn, re, VRF_DEFAULT); + + rn = route_next(rn); + } + + if (empty_table) + zebra_router_release_table(zvrf, zvrf->table_id, afi, safi); + zvrf->table[afi][safi] = NULL; +} + /* Callback upon disabling a VRF. */ static int zebra_vrf_disable(struct vrf *vrf) { @@ -216,9 +255,13 @@ static int zebra_vrf_disable(struct vrf *vrf) * we no-longer need this pointer. */ for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++) { - zebra_router_release_table(zvrf, zvrf->table_id, afi, - safi); - zvrf->table[afi][safi] = NULL; + if (!zvrf->table[afi][safi] || vrf->vrf_id == VRF_DEFAULT) { + zebra_router_release_table(zvrf, zvrf->table_id, afi, safi); + zvrf->table[afi][safi] = NULL; + continue; + } + + zebra_vrf_disable_update_vrfid(zvrf, afi, safi); } } @@ -349,14 +392,42 @@ static void zebra_rnhtable_node_cleanup(struct route_table *table, static void zebra_vrf_table_create(struct zebra_vrf *zvrf, afi_t afi, safi_t safi) { + vrf_id_t vrf_id = zvrf->vrf->vrf_id; + struct rib_table_info *info; + struct route_entry *re; struct route_node *rn; struct prefix p; assert(!zvrf->table[afi][safi]); + /* Attempt to retrieve the Linux routing table using zvrf->table_id. + * If the table was created before the VRF, it will already exist. + * Otherwise, create a new table. + */ zvrf->table[afi][safi] = zebra_router_get_table(zvrf, zvrf->table_id, afi, safi); + /* If the table existed before the VRF was created, info->zvrf was + * referring to the default VRF. + * Assign the table to the new VRF. + * Note: FRR does not allow multiple VRF interfaces to be created with the + * same table ID. + */ + info = route_table_get_info(zvrf->table[afi][safi]); + info->zvrf = zvrf; + + /* If the table existed before the VRF was created, their routing entries + * was owned by the default VRF. + * Re-assign all the routing entries to the new VRF. + */ + for (rn = route_top(zvrf->table[afi][safi]); rn; rn = route_next(rn)) { + if (!rn->info) + continue; + + RNODE_FOREACH_RE (rn, re) + nexthop_vrf_update(rn, re, vrf_id); + } + memset(&p, 0, sizeof(p)); p.family = afi2family(afi); From 2c495ffe6b3c9f1e038bd99c5bcfa2c82a7dfb33 Mon Sep 17 00:00:00 2001 From: Louis Scalbert Date: Thu, 22 Aug 2024 11:12:29 +0200 Subject: [PATCH 2/8] zebra: remove vrf route entries at vrf disabling When a VRF is deleted, the kernel retains only its own routing entries in the former VRF table and removes all others.q This change ensures that routing entries created by FRR daemons are also removed from the former zebra VRF table when the VRF is disabled. To test: > echo "100 my_table" | tee -a /etc/iproute2/rt_tables > ip l add du0 type dummy > ifconfig du0 192.168.0.1/24 up > ip route add blackhole default table 100 > ip route show table 100 > ip l add red type vrf table 100 > ip l set du0 master red > vtysh -c 'configure' -c 'vrf red' -c 'ip route 10.0.0.0/24 192.168.0.254' > vtysh -c 'show ip route table 100' > sleep 0.1 > ip l del red > sleep 0.1 > vtysh -c 'show ip route table 100' > ip l add red type vrf table 100 > ip l set du0 master red > vtysh -c 'configure' -c 'vrf red' -c 'ip route 10.0.0.0/24 192.168.0.254' > vtysh -c 'show ip route table 100' > sleep 0.1 > ip l del red > sleep 0.1 > vtysh -c 'show ip route table 100' Fixes: d8612e6 ("zebra: Track tables allocated by vrf and cleanup") Signed-off-by: Louis Scalbert --- zebra/zebra_rib.c | 5 +++++ zebra/zebra_vrf.c | 32 +++++++++++++++++++++++++------- zebra/zebra_vrf.h | 2 ++ 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index b2543ca0e8b1..8b69ec0e767e 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -963,6 +963,11 @@ void zebra_rtable_node_cleanup(struct route_table *table, rib_unlink(node, re); } + zebra_node_info_cleanup(node); +} + +void zebra_node_info_cleanup(struct route_node *node) +{ if (node->info) { rib_dest_t *dest = node->info; diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index eba019913269..cef5955e11b0 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -158,9 +158,10 @@ static int zebra_vrf_enable(struct vrf *vrf) static void zebra_vrf_disable_update_vrfid(struct zebra_vrf *zvrf, afi_t afi, safi_t safi) { struct rib_table_info *info; - struct route_entry *re; - struct route_node *rn; + struct route_entry *re, *nre; + struct route_node *rn, *nrn; bool empty_table = true; + bool rn_delete; /* Assign the table to the default VRF. * Although the table is not technically owned by the default VRF, @@ -179,13 +180,30 @@ static void zebra_vrf_disable_update_vrfid(struct zebra_vrf *zvrf, afi_t afi, sa continue; } - /* Assign the route entries to the default VRF, + /* Assign the kernel route entries to the default VRF, * even though they are not actually owned by it. + * + * Remove route nodes that have been created by FRR daemons. + * They are not needed if the VRF is disabled. */ - RNODE_FOREACH_RE (rn, re) - nexthop_vrf_update(rn, re, VRF_DEFAULT); - - rn = route_next(rn); + rn_delete = true; + RNODE_FOREACH_RE_SAFE (rn, re, nre) { + if (re->type == ZEBRA_ROUTE_KERNEL) { + nexthop_vrf_update(rn, re, VRF_DEFAULT); + rn_delete = false; + } else + rib_unlink(rn, re); + } + if (rn_delete) { + nrn = route_next(rn); + zebra_node_info_cleanup(rn); + rn->info = NULL; + route_unlock_node(rn); + rn = nrn; + } else { + empty_table = false; + rn = route_next(rn); + } } if (empty_table) diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index f97138c811b1..0f78d8fafd8d 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -260,6 +260,8 @@ extern void zebra_vrf_init(void); extern void zebra_rtable_node_cleanup(struct route_table *table, struct route_node *node); +extern void zebra_node_info_cleanup(struct route_node *node); + #ifdef __cplusplus } From fd10d9dc7d5ed5d6b0e8f223df1dba98e6eb46ec Mon Sep 17 00:00:00 2001 From: Louis Scalbert Date: Thu, 22 Aug 2024 11:29:11 +0200 Subject: [PATCH 3/8] zebra: fix removed default route at vrf enabling When a routing table (RT) already has a default route before being assigned to a VRF, the default route vanishes in zebra after the VRF assignment. > root@router:~# ip route add blackhole default table 100 > root@router:~# ip route show table 100 > blackhole default > root@router:~# vtysh -c 'show ip route table 100' > [...] > VRF default table 100: > K>* 0.0.0.0/0 [0/0] unreachable (blackhole), 00:00:05 > root@router:~# ip l add red type vrf table 100 > root@router:~# vtysh -c 'show ip route table 100' > root@router:~# Do not override the default route if it exists. Signed-off-by: Louis Scalbert --- zebra/zebra_vrf.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index cef5955e11b0..b90618183ae3 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -449,8 +449,11 @@ static void zebra_vrf_table_create(struct zebra_vrf *zvrf, afi_t afi, memset(&p, 0, sizeof(p)); p.family = afi2family(afi); + /* create a fake default route or get the existing one */ rn = srcdest_rnode_get(zvrf->table[afi][safi], &p, NULL); - zebra_rib_create_dest(rn); + if (!rn->info) + /* do not override the existing default route */ + zebra_rib_create_dest(rn); } /* Allocate new zebra VRF. */ From 0b6a312108e9f14b6c94027be1f12e5a239ade67 Mon Sep 17 00:00:00 2001 From: Louis Scalbert Date: Mon, 23 Sep 2024 10:59:06 +0200 Subject: [PATCH 4/8] zebra: fix vanished blackhole route Fix vanished blackhole route when kernel routes are updated. > root@router# echo "100 my_table" | tee -a /etc/iproute2/rt_tables > root@router# ip l add du0 type dummy > root@router# ifconfig du0 192.168.0.1/24 up > root@router# ip route add blackhole default table 100 > root@router# ip route show table 100 > blackhole default > root@router# vtysh -c 'show ip route table 100' > [...] > Table 100: > K>* 0.0.0.0/0 [0/0] unreachable (blackhole), weight 1, 00:00:05 > root@router# ip l add red type vrf table 100 > root@router# vtysh -c 'show ip route table 100' > [...] > Table 100: > K>* 0.0.0.0/0 [0/0] unreachable (blackhole), weight 1, 00:00:16 > root@router# ip l set du0 master red > root@router# vtysh -c 'show ip route table 100' > [...] > Table 100: > C>* 192.168.0.0/24 is directly connected, du0, weight 1, 00:00:02 > L>* 192.168.0.1/32 is directly connected, du0, weight 1, 00:00:02 > root@router# ip route show table 100 > blackhole default > 192.168.0.0/24 dev du0 proto kernel scope link src 192.168.0.1 > local 192.168.0.1 dev du0 proto kernel scope host src 192.168.0.1 > broadcast 192.168.0.255 dev du0 proto kernel scope link src 192.168.0.1 Fixes: d528c02a20 ("zebra: Handle kernel routes appropriately") Signed-off-by: Louis Scalbert --- zebra/zebra_rib.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 8b69ec0e767e..9815d16ee59e 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -4557,6 +4557,12 @@ rib_update_handle_kernel_route_down_possibility(struct route_node *rn, bool alive = false; for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) { + if (!nexthop->ifindex) { + /* blackhole nexthops have no interfaces */ + alive = true; + break; + } + struct interface *ifp = if_lookup_by_index(nexthop->ifindex, nexthop->vrf_id); From f57da00c437558589afa41129021bd0c38df82ad Mon Sep 17 00:00:00 2001 From: Louis Scalbert Date: Mon, 23 Sep 2024 17:57:49 +0200 Subject: [PATCH 5/8] tests: zebra_rib, test vrf change Test table ID move to a VRF and the removal of the VRF. Signed-off-by: Louis Scalbert --- .../zebra_rib/r1/v4_route_table_1_no_vrf.json | 22 +++ .../zebra_rib/r1/v4_route_table_1_no_vrf.txt | 1 + .../r1/v4_route_table_1_vrf_red.json | 82 ++++++++++ .../zebra_rib/r1/v4_route_table_1_vrf_red.txt | 4 + tests/topotests/zebra_rib/test_zebra_rib.py | 140 ++++++++++++++---- 5 files changed, 222 insertions(+), 27 deletions(-) create mode 100644 tests/topotests/zebra_rib/r1/v4_route_table_1_no_vrf.json create mode 100644 tests/topotests/zebra_rib/r1/v4_route_table_1_no_vrf.txt create mode 100644 tests/topotests/zebra_rib/r1/v4_route_table_1_vrf_red.json create mode 100644 tests/topotests/zebra_rib/r1/v4_route_table_1_vrf_red.txt diff --git a/tests/topotests/zebra_rib/r1/v4_route_table_1_no_vrf.json b/tests/topotests/zebra_rib/r1/v4_route_table_1_no_vrf.json new file mode 100644 index 000000000000..5190dc469ce1 --- /dev/null +++ b/tests/topotests/zebra_rib/r1/v4_route_table_1_no_vrf.json @@ -0,0 +1,22 @@ +{ + "0.0.0.0/0": [ + { + "protocol": "kernel", + "vrfName": "default", + "installed": true, + "table": 1, + "nexthops": [ + { + "fib": true, + "unreachable": true, + "blackhole": true, + "active": true + } + ] + } + ], + "10.2.0.0/24": null, + "10.3.0.0/24": null, + "192.168.210.0/24": null, + "192.168.210.1/32": null +} diff --git a/tests/topotests/zebra_rib/r1/v4_route_table_1_no_vrf.txt b/tests/topotests/zebra_rib/r1/v4_route_table_1_no_vrf.txt new file mode 100644 index 000000000000..0664cab2d832 --- /dev/null +++ b/tests/topotests/zebra_rib/r1/v4_route_table_1_no_vrf.txt @@ -0,0 +1 @@ +blackhole default diff --git a/tests/topotests/zebra_rib/r1/v4_route_table_1_vrf_red.json b/tests/topotests/zebra_rib/r1/v4_route_table_1_vrf_red.json new file mode 100644 index 000000000000..f1dad10ab2bb --- /dev/null +++ b/tests/topotests/zebra_rib/r1/v4_route_table_1_vrf_red.json @@ -0,0 +1,82 @@ +{ + "0.0.0.0/0": [ + { + "protocol": "kernel", + "vrfName": "RED", + "installed": true, + "table": 1, + "nexthops": [ + { + "fib": true, + "unreachable": true, + "blackhole": true, + "active": true + } + ] + } + ], + "10.2.0.0/24": [ + { + "protocol": "static", + "vrfName": "RED", + "installed": true, + "table": 1, + "nexthops": [ + { + "fib": true, + "ip": "192.168.210.254", + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ], + "10.3.0.0/24": [ + { + "protocol": "static", + "vrfName": "RED", + "installed": true, + "table": 1, + "nexthops": [ + { + "fib": true, + "ip": "192.168.212.254", + "interfaceName": "r1-eth2", + "vrf": "default", + "active": true + } + ] + } + ], + "192.168.210.0/24": [ + { + "protocol": "connected", + "vrfName": "RED", + "installed": true, + "table": 1, + "nexthops": [ + { + "fib": true, + "directlyConnected": true, + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ], + "192.168.210.1/32": [ + { + "protocol": "local", + "vrfName": "RED", + "installed": true, + "table": 1, + "nexthops": [ + { + "fib": true, + "interfaceName": "r1-eth0", + "active": true + } + ] + } + ] +} diff --git a/tests/topotests/zebra_rib/r1/v4_route_table_1_vrf_red.txt b/tests/topotests/zebra_rib/r1/v4_route_table_1_vrf_red.txt new file mode 100644 index 000000000000..2053fa2bbeb6 --- /dev/null +++ b/tests/topotests/zebra_rib/r1/v4_route_table_1_vrf_red.txt @@ -0,0 +1,4 @@ +blackhole default +10.2.0.0/24 via 192.168.210.254 dev r1-eth0 proto XXXX metric 20 +10.3.0.0/24 via 192.168.212.254 dev r1-eth2 proto XXXX metric 20 +192.168.210.0/24 dev r1-eth0 proto XXXX scope link src 192.168.210.1 diff --git a/tests/topotests/zebra_rib/test_zebra_rib.py b/tests/topotests/zebra_rib/test_zebra_rib.py index d1aee46b404e..810a46bd8b3c 100644 --- a/tests/topotests/zebra_rib/test_zebra_rib.py +++ b/tests/topotests/zebra_rib/test_zebra_rib.py @@ -27,12 +27,13 @@ # pylint: disable=C0413 # Import topogen and topotest helpers from lib import topotest +from lib.common_config import step from lib.topogen import Topogen, TopoRouter, get_topogen from lib.topolog import logger from time import sleep -pytestmark = [pytest.mark.sharpd] +pytestmark = [pytest.mark.sharpd, pytest.mark.staticd] krel = platform.release() @@ -64,6 +65,7 @@ def setup_module(mod): router.load_config( TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname)) ) + router.load_config(TopoRouter.RD_STATIC, "/dev/null") # Macvlan interface for protodown func test */ config_macvlan(tgen, "r1", "r1-eth0", "r1-eth0-macvlan") @@ -77,14 +79,54 @@ def teardown_module(): tgen.stop_topology() +def check_routes_installed(expected, table=None): + tgen = get_topogen() + r1 = tgen.gears["r1"] + + cmd = "ip route show" + if table: + cmd += " table {}".format(table) + actual = r1.run(cmd) + actual = ("\n".join(actual.splitlines()) + "\n").rstrip() + actual = re.sub(r" nhid [0-9][0-9]", "", actual) + actual = re.sub(r" proto sharp", " proto XXXX", actual) + actual = re.sub(r" proto static", " proto XXXX", actual) + actual = re.sub(r" proto 194", " proto XXXX", actual) + actual = re.sub(r" proto 196", " proto XXXX", actual) + actual = re.sub(r" proto kernel", " proto XXXX", actual) + actual = re.sub(r" proto 2", " proto XXXX", actual) + # Some platforms have double spaces? Why?????? + actual = re.sub(r" proto XXXX ", " proto XXXX ", actual) + actual = re.sub(r" metric", " metric", actual) + actual = re.sub(r" link ", " link ", actual) + actual = actual.splitlines() + actual = [ + line.rstrip() + for line in actual + if not line.startswith("broadcast") and not line.startswith("local") + ] + + expected = ("\n".join(expected.splitlines()) + "\n").rstrip() + expected = expected.splitlines() + expected = [line.rstrip() for line in expected] + + return topotest.get_textdiff( + actual, + expected, + title1="Actual ip route show", + title2="Expected ip route show", + ) + + def test_zebra_kernel_route_vrf(): "Test kernel routes should be removed after interface changes vrf" logger.info("Test kernel routes should be removed after interface changes vrf") vrf = "RED" + table_id = 1 tgen = get_topogen() r1 = tgen.gears["r1"] - # Add kernel routes, the interface is initially in default vrf + step("Add kernel routes, the interface is initially in default vrf") r1.run("ip route add 3.5.1.0/24 via 192.168.210.1 dev r1-eth0") json_file = "{}/r1/v4_route_1_vrf_before.json".format(CWD) expected = json.loads(open(json_file).read()) @@ -94,11 +136,59 @@ def test_zebra_kernel_route_vrf(): _, result = topotest.run_and_expect(test_func, None, count=5, wait=1) assert result is None, '"r1" JSON output mismatches' - # Change the interface's vrf - r1.run("ip link add {} type vrf table 1".format(vrf)) + step("Add routes in table 1") + r1.run("ip route add blackhole default table {}".format(table_id)) + + json_file = "{}/r1/v4_route_table_1_no_vrf.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route table 1 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, '"r1" JSON output mismatches' + + ipfile = "{}/r1/v4_route_table_1_no_vrf.txt".format(CWD) + expected = open(ipfile).read().rstrip() + expected = ("\n".join(expected.splitlines()) + "\n").rstrip() + + test_func = partial(check_routes_installed, expected, table=1) + ok, result = topotest.run_and_expect(test_func, "", count=60, wait=0.5) + assert ok, result + + step("Add VRF {} and assign it r1-eth0 interface".format(vrf)) + r1.run("ip link add {} type vrf table {}".format(vrf, table_id)) r1.run("ip link set {} up".format(vrf)) r1.run("ip link set dev r1-eth0 master {}".format(vrf)) + step("Add static routes to VRF {}".format(vrf)) + r1.vtysh_cmd( + """ +configure terminal + vrf {} + ip route 10.2.0.0/24 192.168.210.254 + ip route 10.3.0.0/24 192.168.212.254 nexthop-vrf default +""".format( + vrf + ) + ) + + json_file = "{}/r1/v4_route_table_1_vrf_red.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route table 1 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, '"r1" JSON output mismatches' + + ipfile = "{}/r1/v4_route_table_1_vrf_red.txt".format(CWD) + expected = open(ipfile).read().rstrip() + expected = ("\n".join(expected.splitlines()) + "\n").rstrip() + + test_func = partial(check_routes_installed, expected, table=1) + ok, result = topotest.run_and_expect(test_func, "", count=60, wait=0.5) + assert ok, result + + step("check 3.5.1.0/24 absence on VRF default") expected = "{}" test_func = partial( topotest.router_output_cmp, r1, "show ip route 3.5.1.0/24 json", expected @@ -107,10 +197,26 @@ def test_zebra_kernel_route_vrf(): assertmsg = "{} should not have the kernel route.\n{}".format('"r1"', diff) assert result, assertmsg - # Clean up + step("Remove VRF {}".format(vrf)) r1.run("ip link set dev r1-eth0 nomaster") r1.run("ip link del dev {}".format(vrf)) + json_file = "{}/r1/v4_route_table_1_no_vrf.json".format(CWD) + expected = json.loads(open(json_file).read()) + test_func = partial( + topotest.router_json_cmp, r1, "show ip route table 1 json", expected + ) + _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5) + assert result is None, '"r1" JSON output mismatches' + + ipfile = "{}/r1/v4_route_table_1_no_vrf.txt".format(CWD) + expected = open(ipfile).read().rstrip() + expected = ("\n".join(expected.splitlines()) + "\n").rstrip() + + test_func = partial(check_routes_installed, expected, table=1) + ok, result = topotest.run_and_expect(test_func, "", count=60, wait=0.5) + assert ok, result + def test_zebra_kernel_admin_distance(): "Test some basic kernel routes added that should be accepted" @@ -295,28 +401,8 @@ def check_sharp_map_correct_runs(): expected = open(sharp_ipfile).read().rstrip() expected = ("\n".join(expected.splitlines()) + "\n").rstrip() - def check_routes_installed(): - actual = r1.run("ip route show") - actual = ("\n".join(actual.splitlines()) + "\n").rstrip() - actual = re.sub(r" nhid [0-9][0-9]", "", actual) - actual = re.sub(r" proto sharp", " proto XXXX", actual) - actual = re.sub(r" proto static", " proto XXXX", actual) - actual = re.sub(r" proto 194", " proto XXXX", actual) - actual = re.sub(r" proto 196", " proto XXXX", actual) - actual = re.sub(r" proto kernel", " proto XXXX", actual) - actual = re.sub(r" proto 2", " proto XXXX", actual) - # Some platforms have double spaces? Why?????? - actual = re.sub(r" proto XXXX ", " proto XXXX ", actual) - actual = re.sub(r" metric", " metric", actual) - actual = re.sub(r" link ", " link ", actual) - return topotest.get_textdiff( - actual, - expected, - title1="Actual ip route show", - title2="Expected ip route show", - ) - - ok, result = topotest.run_and_expect(check_routes_installed, "", count=5, wait=1) + test_func = partial(check_routes_installed, expected) + ok, result = topotest.run_and_expect(test_func, "", count=60, wait=0.5) assert ok, result From b2b3b9b2b816d8e7dacf6b2fcb5faa0c84515c16 Mon Sep 17 00:00:00 2001 From: Louis Scalbert Date: Mon, 23 Sep 2024 17:23:51 +0200 Subject: [PATCH 6/8] lib, tests: add table_id route entry flag Add a flag to mean that a route entry is owned by a table ID and not by a VRF. If the VRF associated to the table ID is deleted, the route entry must not be deleted. Update to tests with new flag. 2057 is in hexa 0x809, meaning that the new flag has been to some prefix. Signed-off-by: Louis Scalbert --- lib/zclient.c | 7 +++++++ lib/zclient.h | 6 ++++++ tests/topotests/zebra_rib/r1/import_mrib_table_2.json | 8 ++++---- tests/topotests/zebra_rib/r1/import_mrib_table_3.json | 10 +++++----- tests/topotests/zebra_rib/r1/import_mrib_table_4.json | 8 ++++---- tests/topotests/zebra_rib/r1/import_table_2.json | 8 ++++---- tests/topotests/zebra_rib/r1/import_table_3.json | 10 +++++----- tests/topotests/zebra_rib/r1/import_table_4.json | 8 ++++---- 8 files changed, 39 insertions(+), 26 deletions(-) diff --git a/lib/zclient.c b/lib/zclient.c index 557d9c3eb9b4..deb55a82f4b7 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1236,6 +1236,8 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) stream_putc(s, api->type); stream_putw(s, api->instance); + if (CHECK_FLAG(api->message, ZAPI_MESSAGE_TABLEID)) + SET_FLAG(api->flags, ZEBRA_FLAG_TABLEID); stream_putl(s, api->flags); stream_putl(s, api->message); @@ -1295,6 +1297,8 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) return -1; } + if (CHECK_FLAG(api->message, ZAPI_MESSAGE_TABLEID)) + SET_FLAG(api->flags, ZEBRA_FLAG_TABLEID); if (zapi_nexthop_encode(s, api_nh, api->flags, api->message) != 0) @@ -1334,6 +1338,9 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api) return -1; } + if (CHECK_FLAG(api->message, ZAPI_MESSAGE_TABLEID)) + SET_FLAG(api->flags, ZEBRA_FLAG_TABLEID); + if (zapi_nexthop_encode(s, api_nh, api->flags, api->message) != 0) diff --git a/lib/zclient.h b/lib/zclient.h index 6da9558aa560..5952dbf7d330 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -563,6 +563,12 @@ struct zapi_route { * kernel (NLM_F_APPEND at the very least ) */ #define ZEBRA_FLAG_OUTOFSYNC 0x400 +/* + * This flag lets us know that the route entry is + * associated to the table ID and must remain when the + * table ID is de-associated from a VRF. + */ +#define ZEBRA_FLAG_TABLEID 0x800 /* The older XXX_MESSAGE flags live here */ uint32_t message; diff --git a/tests/topotests/zebra_rib/r1/import_mrib_table_2.json b/tests/topotests/zebra_rib/r1/import_mrib_table_2.json index 61aaaede6e52..03e4163269ef 100644 --- a/tests/topotests/zebra_rib/r1/import_mrib_table_2.json +++ b/tests/topotests/zebra_rib/r1/import_mrib_table_2.json @@ -68,7 +68,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -97,7 +97,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -126,7 +126,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -155,7 +155,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, diff --git a/tests/topotests/zebra_rib/r1/import_mrib_table_3.json b/tests/topotests/zebra_rib/r1/import_mrib_table_3.json index 27a0b9f26470..dd1774cd840b 100644 --- a/tests/topotests/zebra_rib/r1/import_mrib_table_3.json +++ b/tests/topotests/zebra_rib/r1/import_mrib_table_3.json @@ -68,7 +68,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -97,7 +97,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -126,7 +126,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -155,7 +155,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -238,7 +238,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, diff --git a/tests/topotests/zebra_rib/r1/import_mrib_table_4.json b/tests/topotests/zebra_rib/r1/import_mrib_table_4.json index 5a8f0eecd5a4..9a318f9f0097 100644 --- a/tests/topotests/zebra_rib/r1/import_mrib_table_4.json +++ b/tests/topotests/zebra_rib/r1/import_mrib_table_4.json @@ -68,7 +68,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -97,7 +97,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -126,7 +126,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -155,7 +155,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, diff --git a/tests/topotests/zebra_rib/r1/import_table_2.json b/tests/topotests/zebra_rib/r1/import_table_2.json index 61aaaede6e52..03e4163269ef 100644 --- a/tests/topotests/zebra_rib/r1/import_table_2.json +++ b/tests/topotests/zebra_rib/r1/import_table_2.json @@ -68,7 +68,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -97,7 +97,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -126,7 +126,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -155,7 +155,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, diff --git a/tests/topotests/zebra_rib/r1/import_table_3.json b/tests/topotests/zebra_rib/r1/import_table_3.json index 27a0b9f26470..dd1774cd840b 100644 --- a/tests/topotests/zebra_rib/r1/import_table_3.json +++ b/tests/topotests/zebra_rib/r1/import_table_3.json @@ -68,7 +68,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -97,7 +97,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -126,7 +126,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -155,7 +155,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -238,7 +238,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, diff --git a/tests/topotests/zebra_rib/r1/import_table_4.json b/tests/topotests/zebra_rib/r1/import_table_4.json index 5a8f0eecd5a4..9a318f9f0097 100644 --- a/tests/topotests/zebra_rib/r1/import_table_4.json +++ b/tests/topotests/zebra_rib/r1/import_table_4.json @@ -68,7 +68,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -97,7 +97,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -126,7 +126,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, @@ -155,7 +155,7 @@ "installed": true, "table": 254, "internalStatus": 16, - "internalFlags": 9, + "internalFlags": 2057, "nexthops": [ { "flags": 3, From 309b9cccf21b58e8e90682c5fd369e5f53272e6d Mon Sep 17 00:00:00 2001 From: Louis Scalbert Date: Tue, 24 Sep 2024 10:14:21 +0200 Subject: [PATCH 7/8] zebra: keep table route entries at vrf disabling At VRF disabling, keep the route entries that was associated to its table ID but not to the VRF itself. Kernel flushes these entries so we need to reinstall them. Signed-off-by: Louis Scalbert --- zebra/zebra_vrf.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index b90618183ae3..5fedcb5e9d21 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -183,13 +183,19 @@ static void zebra_vrf_disable_update_vrfid(struct zebra_vrf *zvrf, afi_t afi, sa /* Assign the kernel route entries to the default VRF, * even though they are not actually owned by it. * - * Remove route nodes that have been created by FRR daemons. - * They are not needed if the VRF is disabled. + * Remove route nodes that were created by FRR daemons, + * unless they are associated with the table rather than the VRF. + * Routes associated with the VRF are not needed once the VRF is + * disabled. */ rn_delete = true; RNODE_FOREACH_RE_SAFE (rn, re, nre) { - if (re->type == ZEBRA_ROUTE_KERNEL) { + if (re->type == ZEBRA_ROUTE_KERNEL || + CHECK_FLAG(re->flags, ZEBRA_FLAG_TABLEID)) { nexthop_vrf_update(rn, re, VRF_DEFAULT); + if (CHECK_FLAG(re->flags, ZEBRA_FLAG_TABLEID)) + /* reinstall routes */ + rib_install_kernel(rn, re, NULL); rn_delete = false; } else rib_unlink(rn, re); From 736a1c7ac646b4517ca623b2f2b6421405f8621f Mon Sep 17 00:00:00 2001 From: Louis Scalbert Date: Mon, 23 Sep 2024 17:57:49 +0200 Subject: [PATCH 8/8] tests: zebra_rib, test vrf change Test table ID move to a VRF and the removal of the VRF. Signed-off-by: Louis Scalbert --- .../zebra_rib/r1/v4_route_table_1_no_vrf.json | 32 ++++++++++++++++++ .../zebra_rib/r1/v4_route_table_1_no_vrf.txt | 2 ++ .../r1/v4_route_table_1_vrf_red.json | 33 +++++++++++++++++++ .../zebra_rib/r1/v4_route_table_1_vrf_red.txt | 2 ++ tests/topotests/zebra_rib/test_zebra_rib.py | 10 ++++++ 5 files changed, 79 insertions(+) diff --git a/tests/topotests/zebra_rib/r1/v4_route_table_1_no_vrf.json b/tests/topotests/zebra_rib/r1/v4_route_table_1_no_vrf.json index 5190dc469ce1..70398d80b1a1 100644 --- a/tests/topotests/zebra_rib/r1/v4_route_table_1_no_vrf.json +++ b/tests/topotests/zebra_rib/r1/v4_route_table_1_no_vrf.json @@ -15,6 +15,38 @@ ] } ], + "10.0.0.0/24": [ + { + "protocol": "static", + "vrfName": "default", + "installed": true, + "table": 1, + "nexthops": [ + { + "fib": true, + "unreachable": true, + "blackhole": true, + "active": true + } + ] + } + ], + "10.1.0.0/24": [ + { + "protocol": "static", + "vrfName": "default", + "installed": true, + "table": 1, + "nexthops": [ + { + "fib": true, + "ip": "192.168.211.254", + "interfaceName": "r1-eth1", + "active": true + } + ] + } + ], "10.2.0.0/24": null, "10.3.0.0/24": null, "192.168.210.0/24": null, diff --git a/tests/topotests/zebra_rib/r1/v4_route_table_1_no_vrf.txt b/tests/topotests/zebra_rib/r1/v4_route_table_1_no_vrf.txt index 0664cab2d832..f0fcb73d4d68 100644 --- a/tests/topotests/zebra_rib/r1/v4_route_table_1_no_vrf.txt +++ b/tests/topotests/zebra_rib/r1/v4_route_table_1_no_vrf.txt @@ -1 +1,3 @@ blackhole default +blackhole 10.0.0.0/24 proto XXXX metric 20 +10.1.0.0/24 via 192.168.211.254 dev r1-eth1 proto XXXX metric 20 diff --git a/tests/topotests/zebra_rib/r1/v4_route_table_1_vrf_red.json b/tests/topotests/zebra_rib/r1/v4_route_table_1_vrf_red.json index f1dad10ab2bb..5949a69a7353 100644 --- a/tests/topotests/zebra_rib/r1/v4_route_table_1_vrf_red.json +++ b/tests/topotests/zebra_rib/r1/v4_route_table_1_vrf_red.json @@ -15,6 +15,39 @@ ] } ], + "10.0.0.0/24": [ + { + "protocol": "static", + "vrfName": "RED", + "installed": true, + "table": 1, + "nexthops": [ + { + "fib": true, + "unreachable": true, + "blackhole": true, + "active": true + } + ] + } + ], + "10.1.0.0/24": [ + { + "protocol": "static", + "vrfName": "RED", + "installed": true, + "table": 1, + "nexthops": [ + { + "fib": true, + "ip": "192.168.211.254", + "interfaceName": "r1-eth1", + "vrf": "default", + "active": true + } + ] + } + ], "10.2.0.0/24": [ { "protocol": "static", diff --git a/tests/topotests/zebra_rib/r1/v4_route_table_1_vrf_red.txt b/tests/topotests/zebra_rib/r1/v4_route_table_1_vrf_red.txt index 2053fa2bbeb6..fb3d0346894d 100644 --- a/tests/topotests/zebra_rib/r1/v4_route_table_1_vrf_red.txt +++ b/tests/topotests/zebra_rib/r1/v4_route_table_1_vrf_red.txt @@ -1,4 +1,6 @@ blackhole default +blackhole 10.0.0.0/24 proto XXXX metric 20 +10.1.0.0/24 via 192.168.211.254 dev r1-eth1 proto XXXX metric 20 10.2.0.0/24 via 192.168.210.254 dev r1-eth0 proto XXXX metric 20 10.3.0.0/24 via 192.168.212.254 dev r1-eth2 proto XXXX metric 20 192.168.210.0/24 dev r1-eth0 proto XXXX scope link src 192.168.210.1 diff --git a/tests/topotests/zebra_rib/test_zebra_rib.py b/tests/topotests/zebra_rib/test_zebra_rib.py index 810a46bd8b3c..6dc316752abd 100644 --- a/tests/topotests/zebra_rib/test_zebra_rib.py +++ b/tests/topotests/zebra_rib/test_zebra_rib.py @@ -139,6 +139,16 @@ def test_zebra_kernel_route_vrf(): step("Add routes in table 1") r1.run("ip route add blackhole default table {}".format(table_id)) + r1.vtysh_cmd( + """ +configure terminal + ip route 10.0.0.0/24 blackhole table {} + ip route 10.1.0.0/24 192.168.211.254 nexthop-vrf default table {} +""".format( + table_id, table_id + ) + ) + json_file = "{}/r1/v4_route_table_1_no_vrf.json".format(CWD) expected = json.loads(open(json_file).read()) test_func = partial(