summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--zebra/zebra_nhg.c62
-rw-r--r--zebra/zebra_nhg.h1
-rw-r--r--zebra/zebra_vrf.c77
3 files changed, 137 insertions, 3 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_vrf.c b/zebra/zebra_vrf.c
index 7bfe07b4cf..7f30067197 100644
--- a/zebra/zebra_vrf.c
+++ b/zebra/zebra_vrf.c
@@ -162,6 +162,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)
{
@@ -224,9 +263,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,14 +400,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);