diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index e464e47b1f0c..a15ee45496ca 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -158,7 +158,12 @@ static int zebra_vrf_enable(struct vrf *vrf) static int zebra_vrf_disable(struct vrf *vrf) { struct zebra_vrf *zvrf = vrf->info; + struct zebra_vrf *zvrf_default = vrf_info_lookup(VRF_DEFAULT); + struct rib_table_info *info; + struct route_entry *re; + struct route_node *rn; struct interface *ifp; + bool table_delete; afi_t afi; safi_t safi; @@ -216,9 +221,42 @@ 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); + if (!zvrf->table[afi][safi]) { + zebra_router_release_table(zvrf, zvrf->table_id, + afi, safi); + continue; + } + + table_delete = 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 = zvrf_default; + + rn = route_top(zvrf->table[afi][safi]); + if (rn) + table_delete = false; + while (rn) { + if (!rn->info) + continue; + + /* Assign the route entries to the default VRF, + * even though they are not actually owned by it. + */ + RNODE_FOREACH_RE (rn, re) + re->vrf_id = VRF_DEFAULT; + + rn = route_next(rn); + } zvrf->table[afi][safi] = NULL; + + if (table_delete) + /* Only delete the table if it is empty */ + zebra_router_release_table(zvrf, zvrf->table_id, + afi, safi); } } @@ -349,14 +387,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) + re->vrf_id = vrf_id; + } + memset(&p, 0, sizeof(p)); p.family = afi2family(afi);