summaryrefslogtreecommitdiff
path: root/zebra/zebra_vrf.c
diff options
context:
space:
mode:
Diffstat (limited to 'zebra/zebra_vrf.c')
-rw-r--r--zebra/zebra_vrf.c106
1 files changed, 102 insertions, 4 deletions
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. */