summaryrefslogtreecommitdiffstats
path: root/zebra/zebra_vrf.c
diff options
context:
space:
mode:
Diffstat (limited to 'zebra/zebra_vrf.c')
-rw-r--r--zebra/zebra_vrf.c688
1 files changed, 688 insertions, 0 deletions
diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c
new file mode 100644
index 0000000..25e6513
--- /dev/null
+++ b/zebra/zebra_vrf.c
@@ -0,0 +1,688 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2016 CumulusNetworks
+ * Donald Sharp
+ *
+ * This file is part of Quagga
+ */
+#include <zebra.h>
+
+/* for basename */
+#include <libgen.h>
+
+#include "log.h"
+#include "linklist.h"
+#include "command.h"
+#include "memory.h"
+#include "srcdest_table.h"
+#include "vrf.h"
+#include "vty.h"
+
+#include "zebra/zebra_router.h"
+#include "zebra/rtadv.h"
+#include "zebra/debug.h"
+#include "zebra/zapi_msg.h"
+#include "zebra/rib.h"
+#include "zebra/zebra_vrf.h"
+#include "zebra/zebra_rnh.h"
+#include "zebra/router-id.h"
+#include "zebra/interface.h"
+#include "zebra/zebra_mpls.h"
+#include "zebra/zebra_vxlan.h"
+#include "zebra/zebra_netns_notify.h"
+#include "zebra/zebra_routemap.h"
+#include "zebra/zebra_vrf_clippy.c"
+#include "zebra/table_manager.h"
+
+static void zebra_vrf_table_create(struct zebra_vrf *zvrf, afi_t afi,
+ safi_t safi);
+static void zebra_rnhtable_node_cleanup(struct route_table *table,
+ struct route_node *node);
+
+DEFINE_MTYPE_STATIC(ZEBRA, ZEBRA_VRF, "ZEBRA VRF");
+DEFINE_MTYPE_STATIC(ZEBRA, OTHER_TABLE, "Other Table");
+
+/* VRF information update. */
+static void zebra_vrf_add_update(struct zebra_vrf *zvrf)
+{
+ struct listnode *node, *nnode;
+ struct zserv *client;
+
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug("MESSAGE: ZEBRA_VRF_ADD %s", zvrf_name(zvrf));
+
+ for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) {
+ /* Do not send unsolicited messages to synchronous clients. */
+ if (client->synchronous)
+ continue;
+
+ zsend_vrf_add(client, zvrf);
+ }
+}
+
+static void zebra_vrf_delete_update(struct zebra_vrf *zvrf)
+{
+ struct listnode *node, *nnode;
+ struct zserv *client;
+
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug("MESSAGE: ZEBRA_VRF_DELETE %s", zvrf_name(zvrf));
+
+ for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) {
+ /* Do not send unsolicited messages to synchronous clients. */
+ if (client->synchronous)
+ continue;
+
+ zsend_vrf_delete(client, zvrf);
+ }
+}
+
+void zebra_vrf_update_all(struct zserv *client)
+{
+ struct vrf *vrf;
+
+ RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) {
+ if (vrf->vrf_id != VRF_UNKNOWN)
+ zsend_vrf_add(client, vrf_info_lookup(vrf->vrf_id));
+ }
+}
+
+/* Callback upon creating a new VRF. */
+static int zebra_vrf_new(struct vrf *vrf)
+{
+ struct zebra_vrf *zvrf;
+
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug("VRF %s created, id %u", vrf->name, vrf->vrf_id);
+
+ zvrf = zebra_vrf_alloc(vrf);
+ if (!vrf_is_backend_netns())
+ zvrf->zns = zebra_ns_lookup(NS_DEFAULT);
+
+ otable_init(&zvrf->other_tables);
+
+ router_id_init(zvrf);
+
+ /* Initiate Table Manager per ZNS */
+ table_manager_enable(zvrf);
+
+ return 0;
+}
+
+/* Callback upon enabling a VRF. */
+static int zebra_vrf_enable(struct vrf *vrf)
+{
+ struct zebra_vrf *zvrf = vrf->info;
+ struct route_table *table;
+ afi_t afi;
+ safi_t safi;
+
+ assert(zvrf);
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug("VRF %s id %u is now active", zvrf_name(zvrf),
+ zvrf_id(zvrf));
+
+ if (vrf_is_backend_netns())
+ zvrf->zns = zebra_ns_lookup((ns_id_t)vrf->vrf_id);
+ else
+ zvrf->zns = zebra_ns_lookup(NS_DEFAULT);
+
+ rtadv_vrf_init(zvrf);
+
+ /* Inform clients that the VRF is now active. This is an
+ * add for the clients.
+ */
+
+ zebra_vrf_add_update(zvrf);
+ /* Allocate tables */
+ for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
+ for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++)
+ zebra_vrf_table_create(zvrf, afi, safi);
+
+ table = route_table_init();
+ table->cleanup = zebra_rnhtable_node_cleanup;
+ zvrf->rnh_table[afi] = table;
+
+ table = route_table_init();
+ table->cleanup = zebra_rnhtable_node_cleanup;
+ zvrf->rnh_table_multicast[afi] = table;
+ }
+
+ /* Kick off any VxLAN-EVPN processing. */
+ zebra_vxlan_vrf_enable(zvrf);
+
+ return 0;
+}
+
+/* Callback upon disabling a VRF. */
+static int zebra_vrf_disable(struct vrf *vrf)
+{
+ struct zebra_vrf *zvrf = vrf->info;
+ struct interface *ifp;
+ afi_t afi;
+ safi_t safi;
+
+ assert(zvrf);
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug("VRF %s id %u is now inactive", zvrf_name(zvrf),
+ zvrf_id(zvrf));
+
+ /* Stop any VxLAN-EVPN processing. */
+ zebra_vxlan_vrf_disable(zvrf);
+
+ rtadv_vrf_terminate(zvrf);
+
+ /* Inform clients that the VRF is now inactive. This is a
+ * delete for the clients.
+ */
+ zebra_vrf_delete_update(zvrf);
+
+ /* If asked to retain routes, there's nothing more to do. */
+ if (CHECK_FLAG(zvrf->flags, ZEBRA_VRF_RETAIN))
+ return 0;
+
+ /* Remove all routes. */
+ for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
+ route_table_finish(zvrf->rnh_table[afi]);
+ zvrf->rnh_table[afi] = NULL;
+ route_table_finish(zvrf->rnh_table_multicast[afi]);
+ zvrf->rnh_table_multicast[afi] = NULL;
+
+ for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++)
+ rib_close_table(zvrf->table[afi][safi]);
+ }
+
+ /* Cleanup Vxlan, MPLS and PW tables. */
+ zebra_vxlan_cleanup_tables(zvrf);
+ zebra_mpls_cleanup_tables(zvrf);
+ zebra_pw_exit(zvrf);
+
+ /* Remove link-local IPv4 addresses created for BGP unnumbered peering.
+ */
+ FOR_ALL_INTERFACES (vrf, ifp)
+ if_nbr_ipv6ll_to_ipv4ll_neigh_del_all(ifp);
+
+ /* clean-up work queues */
+ meta_queue_free(zrouter.mq, zvrf);
+
+ /* Cleanup (free) routing tables and NHT tables. */
+ for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
+ /*
+ * Set the table pointer to NULL as that
+ * we no-longer need a copy of it, nor do we
+ * own this data, the zebra_router structure
+ * owns these tables. Once we've cleaned up the
+ * table, see rib_close_table above
+ * 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;
+ }
+ }
+
+ return 0;
+}
+
+static int zebra_vrf_delete(struct vrf *vrf)
+{
+ struct zebra_vrf *zvrf = vrf->info;
+ struct other_route_table *otable;
+
+ assert(zvrf);
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug("VRF %s id %u deleted", zvrf_name(zvrf),
+ zvrf_id(zvrf));
+
+ table_manager_disable(zvrf);
+
+ /* clean-up work queues */
+ meta_queue_free(zrouter.mq, zvrf);
+
+ /* Free Vxlan and MPLS. */
+ zebra_vxlan_close_tables(zvrf);
+ zebra_mpls_close_tables(zvrf);
+
+ otable = otable_pop(&zvrf->other_tables);
+ while (otable) {
+ zebra_router_release_table(zvrf, otable->table_id,
+ otable->afi, otable->safi);
+ XFREE(MTYPE_OTHER_TABLE, otable);
+
+ otable = otable_pop(&zvrf->other_tables);
+ }
+
+ /* Cleanup EVPN states for vrf */
+ zebra_vxlan_vrf_delete(zvrf);
+ zebra_routemap_vrf_delete(zvrf);
+
+ list_delete_all_node(zvrf->rid_all_sorted_list);
+ list_delete_all_node(zvrf->rid_lo_sorted_list);
+
+ list_delete_all_node(zvrf->rid6_all_sorted_list);
+ list_delete_all_node(zvrf->rid6_lo_sorted_list);
+
+ otable_fini(&zvrf->other_tables);
+ XFREE(MTYPE_ZEBRA_VRF, zvrf);
+ vrf->info = NULL;
+
+ return 0;
+}
+
+/* Lookup the routing table in a VRF based on both VRF-Id and table-id.
+ * NOTE: Table-id is relevant on two modes:
+ * - case VRF backend is default : on default VRF only
+ * - case VRF backend is netns : on all VRFs
+ */
+struct route_table *zebra_vrf_lookup_table_with_table_id(afi_t afi, safi_t safi,
+ vrf_id_t vrf_id,
+ uint32_t table_id)
+{
+ struct zebra_vrf *zvrf = vrf_info_lookup(vrf_id);
+ struct other_route_table ort, *otable;
+
+ if (!zvrf)
+ return NULL;
+
+ if (afi >= AFI_MAX || safi >= SAFI_MAX)
+ return NULL;
+
+ if (table_id == zvrf->table_id)
+ return zebra_vrf_table(afi, safi, vrf_id);
+
+ ort.afi = afi;
+ ort.safi = safi;
+ ort.table_id = table_id;
+ otable = otable_find(&zvrf->other_tables, &ort);
+
+ if (otable)
+ return otable->table;
+
+ return NULL;
+}
+
+struct route_table *zebra_vrf_get_table_with_table_id(afi_t afi, safi_t safi,
+ vrf_id_t vrf_id,
+ uint32_t table_id)
+{
+ struct zebra_vrf *zvrf = vrf_info_lookup(vrf_id);
+ struct other_route_table *otable;
+ struct route_table *table;
+
+ table = zebra_vrf_lookup_table_with_table_id(afi, safi, vrf_id,
+ table_id);
+
+ if (table)
+ goto done;
+
+ /* Create it as an `other` table */
+ table = zebra_router_get_table(zvrf, table_id, afi, safi);
+
+ otable = XCALLOC(MTYPE_OTHER_TABLE, sizeof(*otable));
+ otable->afi = afi;
+ otable->safi = safi;
+ otable->table_id = table_id;
+ otable->table = table;
+ otable_add(&zvrf->other_tables, otable);
+
+done:
+ return table;
+}
+
+static void zebra_rnhtable_node_cleanup(struct route_table *table,
+ struct route_node *node)
+{
+ if (node->info)
+ zebra_free_rnh(node->info);
+}
+
+/*
+ * Create a routing table for the specific AFI/SAFI in the given VRF.
+ */
+static void zebra_vrf_table_create(struct zebra_vrf *zvrf, afi_t afi,
+ safi_t safi)
+{
+ struct route_node *rn;
+ struct prefix p;
+
+ assert(!zvrf->table[afi][safi]);
+
+ zvrf->table[afi][safi] =
+ zebra_router_get_table(zvrf, zvrf->table_id, afi, safi);
+
+ memset(&p, 0, sizeof(p));
+ p.family = afi2family(afi);
+
+ rn = srcdest_rnode_get(zvrf->table[afi][safi], &p, NULL);
+ zebra_rib_create_dest(rn);
+}
+
+/* Allocate new zebra VRF. */
+struct zebra_vrf *zebra_vrf_alloc(struct vrf *vrf)
+{
+ struct zebra_vrf *zvrf;
+
+ zvrf = XCALLOC(MTYPE_ZEBRA_VRF, sizeof(struct zebra_vrf));
+
+ zvrf->vrf = vrf;
+ vrf->info = zvrf;
+
+ zebra_vxlan_init_tables(zvrf);
+ zebra_mpls_init_tables(zvrf);
+ zebra_pw_init(zvrf);
+ zvrf->table_id = rt_table_main_id;
+ /* by default table ID is default one */
+
+ if (DFLT_ZEBRA_IP_NHT_RESOLVE_VIA_DEFAULT) {
+ zvrf->zebra_rnh_ip_default_route = true;
+ zvrf->zebra_rnh_ipv6_default_route = true;
+ }
+
+ return zvrf;
+}
+
+/*
+ * Pending: create an efficient table_id (in a tree/hash) based lookup)
+ */
+vrf_id_t zebra_vrf_lookup_by_table(uint32_t table_id, ns_id_t ns_id)
+{
+ struct vrf *vrf;
+ struct zebra_vrf *zvrf;
+
+ RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) {
+ zvrf = vrf->info;
+ if (zvrf == NULL)
+ continue;
+ /* case vrf with netns : match the netnsid */
+ if (vrf_is_backend_netns()) {
+ if (ns_id == zvrf_id(zvrf))
+ return zvrf_id(zvrf);
+ } else {
+ /* VRF is VRF_BACKEND_VRF_LITE */
+ if (zvrf->table_id != table_id)
+ continue;
+ return zvrf_id(zvrf);
+ }
+ }
+
+ return VRF_DEFAULT;
+}
+
+/* Lookup VRF by identifier. */
+struct zebra_vrf *zebra_vrf_lookup_by_id(vrf_id_t vrf_id)
+{
+ return vrf_info_lookup(vrf_id);
+}
+
+/* Lookup VRF by name. */
+struct zebra_vrf *zebra_vrf_lookup_by_name(const char *name)
+{
+ struct vrf *vrf;
+
+ if (!name)
+ name = VRF_DEFAULT_NAME;
+
+ vrf = vrf_lookup_by_name(name);
+ if (vrf)
+ return ((struct zebra_vrf *)vrf->info);
+
+ return NULL;
+}
+
+/* Lookup the routing table in an enabled VRF. */
+struct route_table *zebra_vrf_table(afi_t afi, safi_t safi, vrf_id_t vrf_id)
+{
+ struct zebra_vrf *zvrf = vrf_info_lookup(vrf_id);
+
+ if (!zvrf)
+ return NULL;
+
+ if (afi >= AFI_MAX || safi >= SAFI_MAX)
+ return NULL;
+
+ return zvrf->table[afi][safi];
+}
+
+static int vrf_config_write(struct vty *vty)
+{
+ struct vrf *vrf;
+ struct zebra_vrf *zvrf;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ zvrf = vrf->info;
+
+ if (!zvrf)
+ continue;
+
+ if (zvrf_id(zvrf) == VRF_DEFAULT) {
+ if (zvrf->l3vni)
+ vty_out(vty, "vni %u%s\n", zvrf->l3vni,
+ is_l3vni_for_prefix_routes_only(
+ zvrf->l3vni)
+ ? " prefix-routes-only"
+ : "");
+
+ if (zvrf->zebra_rnh_ip_default_route !=
+ SAVE_ZEBRA_IP_NHT_RESOLVE_VIA_DEFAULT)
+ vty_out(vty, "%sip nht resolve-via-default\n",
+ zvrf->zebra_rnh_ip_default_route
+ ? ""
+ : "no ");
+
+ if (zvrf->zebra_rnh_ipv6_default_route !=
+ SAVE_ZEBRA_IP_NHT_RESOLVE_VIA_DEFAULT)
+ vty_out(vty, "%sipv6 nht resolve-via-default\n",
+ zvrf->zebra_rnh_ipv6_default_route
+ ? ""
+ : "no ");
+
+ if (zvrf->tbl_mgr
+ && (zvrf->tbl_mgr->start || zvrf->tbl_mgr->end))
+ vty_out(vty, "ip table range %u %u\n",
+ zvrf->tbl_mgr->start,
+ zvrf->tbl_mgr->end);
+ } else {
+ vty_frame(vty, "vrf %s\n", zvrf_name(zvrf));
+ if (zvrf->l3vni)
+ vty_out(vty, " vni %u%s\n", zvrf->l3vni,
+ is_l3vni_for_prefix_routes_only(
+ zvrf->l3vni)
+ ? " prefix-routes-only"
+ : "");
+ zebra_ns_config_write(vty, (struct ns *)vrf->ns_ctxt);
+ if (zvrf->zebra_rnh_ip_default_route !=
+ SAVE_ZEBRA_IP_NHT_RESOLVE_VIA_DEFAULT)
+ vty_out(vty, " %sip nht resolve-via-default\n",
+ zvrf->zebra_rnh_ip_default_route
+ ? ""
+ : "no ");
+
+ if (zvrf->zebra_rnh_ipv6_default_route !=
+ SAVE_ZEBRA_IP_NHT_RESOLVE_VIA_DEFAULT)
+ vty_out(vty, " %sipv6 nht resolve-via-default\n",
+ zvrf->zebra_rnh_ipv6_default_route
+ ? ""
+ : "no ");
+
+ if (zvrf->tbl_mgr && vrf_is_backend_netns()
+ && (zvrf->tbl_mgr->start || zvrf->tbl_mgr->end))
+ vty_out(vty, " ip table range %u %u\n",
+ zvrf->tbl_mgr->start,
+ zvrf->tbl_mgr->end);
+ }
+
+
+ zebra_routemap_config_write_protocol(vty, zvrf);
+ router_id_write(vty, zvrf);
+
+ if (zvrf_id(zvrf) != VRF_DEFAULT)
+ vty_endframe(vty, "exit-vrf\n!\n");
+ else
+ vty_out(vty, "!\n");
+ }
+ return 0;
+}
+
+DEFPY (vrf_netns,
+ vrf_netns_cmd,
+ "netns NAME$netns_name",
+ "Attach VRF to a Namespace\n"
+ "The file name in " NS_RUN_DIR ", or a full pathname\n")
+{
+ char *pathname = ns_netns_pathname(vty, netns_name);
+ int ret;
+
+ VTY_DECLVAR_CONTEXT(vrf, vrf);
+
+ if (!pathname)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ frr_with_privs(&zserv_privs) {
+ ret = zebra_vrf_netns_handler_create(
+ vty, vrf, pathname, NS_UNKNOWN, NS_UNKNOWN, NS_UNKNOWN);
+ }
+
+ return ret;
+}
+
+DEFUN (no_vrf_netns,
+ no_vrf_netns_cmd,
+ "no netns [NAME]",
+ NO_STR
+ "Detach VRF from a Namespace\n"
+ "The file name in " NS_RUN_DIR ", or a full pathname\n")
+{
+ struct ns *ns = NULL;
+
+ VTY_DECLVAR_CONTEXT(vrf, vrf);
+
+ if (!vrf_is_backend_netns()) {
+ vty_out(vty, "VRF backend is not Netns. Aborting\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (!vrf->ns_ctxt) {
+ vty_out(vty, "VRF %s(%u) is not configured with NetNS\n",
+ vrf->name, vrf->vrf_id);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ ns = (struct ns *)vrf->ns_ctxt;
+
+ ns->vrf_ctxt = NULL;
+ vrf_disable(vrf);
+ /* vrf ID from VRF is necessary for Zebra
+ * so that propagate to other clients is done
+ */
+ ns_delete(ns);
+ vrf->ns_ctxt = NULL;
+ return CMD_SUCCESS;
+}
+
+/* if ns_id is different and not VRF_UNKNOWN,
+ * then update vrf identifier, and enable VRF
+ */
+static void vrf_update_vrf_id(ns_id_t ns_id, void *opaqueptr)
+{
+ ns_id_t vrf_id = (vrf_id_t)ns_id;
+ vrf_id_t old_vrf_id;
+ struct vrf *vrf = (struct vrf *)opaqueptr;
+
+ if (!vrf)
+ return;
+ old_vrf_id = vrf->vrf_id;
+ if (vrf_id == vrf->vrf_id)
+ return;
+ if (vrf->vrf_id != VRF_UNKNOWN)
+ RB_REMOVE(vrf_id_head, &vrfs_by_id, vrf);
+ vrf->vrf_id = vrf_id;
+ RB_INSERT(vrf_id_head, &vrfs_by_id, vrf);
+ if (old_vrf_id == VRF_UNKNOWN)
+ vrf_enable(vrf);
+}
+
+int zebra_vrf_netns_handler_create(struct vty *vty, struct vrf *vrf,
+ char *pathname, ns_id_t ns_id,
+ ns_id_t internal_ns_id,
+ ns_id_t rel_def_ns_id)
+{
+ struct ns *ns = NULL;
+
+ if (!vrf)
+ return CMD_WARNING_CONFIG_FAILED;
+ if (vrf->vrf_id != VRF_UNKNOWN && vrf->ns_ctxt == NULL) {
+ if (vty)
+ vty_out(vty,
+ "VRF %u is already configured with VRF %s\n",
+ vrf->vrf_id, vrf->name);
+ else
+ zlog_info("VRF %u is already configured with VRF %s",
+ vrf->vrf_id, vrf->name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (vrf->ns_ctxt != NULL) {
+ ns = (struct ns *)vrf->ns_ctxt;
+ if (!strcmp(ns->name, pathname)) {
+ if (vty)
+ vty_out(vty,
+ "VRF %u already configured with NETNS %s\n",
+ vrf->vrf_id, ns->name);
+ else
+ zlog_info(
+ "VRF %u already configured with NETNS %s",
+ vrf->vrf_id, ns->name);
+ return CMD_WARNING;
+ }
+ }
+ ns = ns_lookup_name(pathname);
+ if (ns && ns->vrf_ctxt) {
+ struct vrf *vrf2 = (struct vrf *)ns->vrf_ctxt;
+
+ if (vrf2 == vrf)
+ return CMD_SUCCESS;
+ if (vty)
+ vty_out(vty,
+ "NS %s is already configured with VRF %u(%s)\n",
+ ns->name, vrf2->vrf_id, vrf2->name);
+ else
+ zlog_info("NS %s is already configured with VRF %u(%s)",
+ ns->name, vrf2->vrf_id, vrf2->name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ ns = ns_get_created(ns, pathname, ns_id);
+ ns->internal_ns_id = internal_ns_id;
+ ns->relative_default_ns = rel_def_ns_id;
+ ns->vrf_ctxt = (void *)vrf;
+ vrf->ns_ctxt = (void *)ns;
+ /* update VRF netns NAME */
+ strlcpy(vrf->data.l.netns_name, basename(pathname), NS_NAMSIZ);
+
+ if (!ns_enable(ns, vrf_update_vrf_id)) {
+ if (vty)
+ vty_out(vty, "Can not associate NS %u with NETNS %s\n",
+ ns->ns_id, ns->name);
+ else
+ zlog_info("Can not associate NS %u with NETNS %s",
+ ns->ns_id, ns->name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* Zebra VRF initialization. */
+void zebra_vrf_init(void)
+{
+ vrf_init(zebra_vrf_new, zebra_vrf_enable, zebra_vrf_disable,
+ zebra_vrf_delete);
+
+ hook_register(zserv_client_close, release_daemon_table_chunks);
+
+ vrf_cmd_init(vrf_config_write);
+
+ if (vrf_is_backend_netns() && ns_have_netns()) {
+ /* Install NS commands. */
+ install_element(VRF_NODE, &vrf_netns_cmd);
+ install_element(VRF_NODE, &no_vrf_netns_cmd);
+ }
+}