diff options
Diffstat (limited to 'zebra')
| -rw-r--r-- | zebra/zebra_nhg.c | 62 | ||||
| -rw-r--r-- | zebra/zebra_nhg.h | 1 | ||||
| -rw-r--r-- | zebra/zebra_rib.c | 11 | ||||
| -rw-r--r-- | zebra/zebra_vrf.c | 106 | ||||
| -rw-r--r-- | zebra/zebra_vrf.h | 2 |
5 files changed, 178 insertions, 4 deletions
diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index f5141c8f23..5b7452a79e 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -2930,6 +2930,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 0f90627a0d..de6f6123aa 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_rib.c b/zebra/zebra_rib.c index 8cea605f41..20ec25a431 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -903,6 +903,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; @@ -4498,6 +4503,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); diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 7bfe07b4cf..d652c57388 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -162,6 +162,69 @@ 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, *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, + * 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 kernel route entries to the default VRF, + * even though they are not actually owned by it. + * + * 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 || + 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); + } + 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) + 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) { @@ -224,9 +287,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); } } @@ -357,19 +424,50 @@ 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); + /* 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. */ diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index 334bb93684..289a8fcc47 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -263,6 +263,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 } |
