summaryrefslogtreecommitdiff
path: root/zebra
diff options
context:
space:
mode:
Diffstat (limited to 'zebra')
-rw-r--r--zebra/zebra_nhg.c62
-rw-r--r--zebra/zebra_nhg.h1
-rw-r--r--zebra/zebra_rib.c11
-rw-r--r--zebra/zebra_vrf.c106
-rw-r--r--zebra/zebra_vrf.h2
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
}