summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/command.c6
-rw-r--r--lib/command.h2
-rw-r--r--lib/if.c52
-rw-r--r--lib/logicalrouter.c159
-rw-r--r--lib/logicalrouter.h41
-rw-r--r--lib/netns_linux.c539
-rw-r--r--lib/netns_other.c165
-rw-r--r--lib/ns.c453
-rw-r--r--lib/ns.h88
-rw-r--r--lib/subdir.am5
-rw-r--r--lib/vrf.c381
-rw-r--r--lib/vrf.h91
-rw-r--r--lib/zclient.c4
13 files changed, 1465 insertions, 521 deletions
diff --git a/lib/command.c b/lib/command.c
index d17f2c3d48..10996d5dd4 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -62,7 +62,7 @@ const char *node_names[] = {
"aaa", // AAA_NODE,
"keychain", // KEYCHAIN_NODE,
"keychain key", // KEYCHAIN_KEY_NODE,
- "logical-router", // NS_NODE,
+ "logical-router", // LOGICALROUTER_NODE,
"vrf", // VRF_NODE,
"interface", // INTERFACE_NODE,
"zebra", // ZEBRA_NODE,
@@ -1291,7 +1291,7 @@ void cmd_exit(struct vty *vty)
break;
case INTERFACE_NODE:
case PW_NODE:
- case NS_NODE:
+ case LOGICALROUTER_NODE:
case VRF_NODE:
case ZEBRA_NODE:
case BGP_NODE:
@@ -1376,7 +1376,7 @@ DEFUN (config_end,
case CONFIG_NODE:
case INTERFACE_NODE:
case PW_NODE:
- case NS_NODE:
+ case LOGICALROUTER_NODE:
case VRF_NODE:
case ZEBRA_NODE:
case RIP_NODE:
diff --git a/lib/command.h b/lib/command.h
index e1edc1ef32..269318989f 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -85,7 +85,7 @@ enum node_type {
AAA_NODE, /* AAA node. */
KEYCHAIN_NODE, /* Key-chain node. */
KEYCHAIN_KEY_NODE, /* Key-chain key node. */
- NS_NODE, /* Logical-Router node. */
+ LOGICALROUTER_NODE, /* Logical-Router node. */
VRF_NODE, /* VRF mode node. */
INTERFACE_NODE, /* Interface mode node. */
ZEBRA_NODE, /* zebra connection node. */
diff --git a/lib/if.c b/lib/if.c
index 7866ddb8c4..3a83de46ae 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -384,29 +384,35 @@ struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id, int vty)
{
struct interface *ifp;
+ ifp = if_lookup_by_name(name, vrf_id);
+ if (ifp)
+ return ifp;
+ /* Not Found on same VRF. If the interface command
+ * was entered in vty without a VRF (passed as VRF_DEFAULT),
+ * accept the ifp we found. If a vrf was entered and there is
+ * a mismatch, reject it if from vty.
+ */
ifp = if_lookup_by_name_all_vrf(name);
- if (ifp) {
- if (ifp->vrf_id == vrf_id)
+ if (!ifp)
+ return if_create(name, vrf_id);
+ if (vty) {
+ if (vrf_id == VRF_DEFAULT)
return ifp;
-
- /* Found a match on a different VRF. If the interface command
- * was entered in vty without a VRF (passed as VRF_DEFAULT),
- * accept the ifp we found. If a vrf was entered and there is
- * a mismatch, reject it if from vty. If it came from the kernel
- * or by way of zclient, believe it and update the ifp
- * accordingly.
- */
- if (vty) {
- if (vrf_id == VRF_DEFAULT)
- return ifp;
- return NULL;
- } else {
- if_update_to_new_vrf(ifp, vrf_id);
- return ifp;
- }
+ return NULL;
}
-
- return if_create(name, vrf_id);
+ /* if vrf backend uses NETNS, then
+ * this should not be considered as an update
+ * then create the new interface
+ */
+ if (ifp->vrf_id != vrf_id &&
+ vrf_is_mapped_on_netns(vrf_id))
+ return if_create(name, vrf_id);
+ /* If it came from the kernel
+ * or by way of zclient, believe it and update
+ * the ifp accordingly.
+ */
+ if_update_to_new_vrf(ifp, vrf_id);
+ return ifp;
}
void if_set_index(struct interface *ifp, ifindex_t ifindex)
@@ -1064,7 +1070,7 @@ ifaddr_ipv4_lookup (struct in_addr *addr, ifindex_t ifindex)
rn = route_node_lookup (ifaddr_ipv4_table, (struct prefix *) &p);
if (! rn)
return NULL;
-
+
ifp = rn->info;
route_unlock_node (rn);
return ifp;
@@ -1078,7 +1084,9 @@ void if_terminate(struct vrf *vrf)
{
struct interface *ifp;
- while ((ifp = RB_ROOT(if_name_head, &vrf->ifaces_by_name)) != NULL) {
+ while (!RB_EMPTY(if_name_head, &vrf->ifaces_by_name)) {
+ ifp = RB_ROOT(if_name_head, &vrf->ifaces_by_name);
+
if (ifp->node) {
ifp->node->info = NULL;
route_unlock_node(ifp->node);
diff --git a/lib/logicalrouter.c b/lib/logicalrouter.c
new file mode 100644
index 0000000000..4dc99d304f
--- /dev/null
+++ b/lib/logicalrouter.c
@@ -0,0 +1,159 @@
+/*
+ * Logical Router functions.
+ * Copyright (C) 2018 6WIND S.A.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "ns.h"
+#include "log.h"
+#include "memory.h"
+
+#include "command.h"
+#include "vty.h"
+#include "logicalrouter.h"
+
+/* Comment that useless define to avoid compilation error
+ * in order to use it, one could provide the kind of NETNS to NS backend
+ * so that the allocation will match the logical router
+ * DEFINE_MTYPE_STATIC(LIB, LOGICALROUTER, "LogicalRouter Context")
+ */
+DEFINE_MTYPE_STATIC(LIB, LOGICALROUTER_NAME, "Logical Router Name")
+
+/* Logical Router node has no interface. */
+static struct cmd_node logicalrouter_node = {LOGICALROUTER_NODE, "",
+ 1};
+
+static int logicalrouter_backend;
+
+/* Get a NS. If not found, create one. */
+static struct ns *logicalrouter_get(ns_id_t ns_id)
+{
+ struct ns *ns;
+
+ ns = ns_lookup(ns_id);
+ if (ns)
+ return (ns);
+ ns = ns_get_created(ns, NULL, ns_id);
+ return ns;
+}
+
+static int logicalrouter_is_backend_netns(void)
+{
+ return (logicalrouter_backend == LOGICALROUTER_BACKEND_NETNS);
+}
+
+
+DEFUN_NOSH (logicalrouter,
+ logicalrouter_cmd,
+ "logical-router (1-65535) ns NAME",
+ "Enable a logical-router\n"
+ "Specify the logical-router indentifier\n"
+ "The Name Space\n"
+ "The file name in " NS_RUN_DIR ", or a full pathname\n")
+{
+ int idx_number = 1;
+ int idx_name = 3;
+ ns_id_t ns_id;
+ struct ns *ns = NULL;
+ char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg);
+
+ if (!pathname)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ns_id = strtoul(argv[idx_number]->arg, NULL, 10);
+ ns = logicalrouter_get(ns_id);
+
+ if (ns->name && strcmp(ns->name, pathname) != 0) {
+ vty_out(vty, "NS %u is already configured with NETNS %s\n",
+ ns->ns_id, ns->name);
+ return CMD_WARNING;
+ }
+
+ if (!ns->name)
+ ns->name = XSTRDUP(MTYPE_LOGICALROUTER_NAME, pathname);
+
+ if (!ns_enable(ns, NULL)) {
+ vty_out(vty, "Can not associate NS %u with NETNS %s\n",
+ ns->ns_id, ns->name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_logicalrouter,
+ no_logicalrouter_cmd,
+ "no logical-router (1-65535) ns NAME",
+ NO_STR
+ "Enable a Logical-Router\n"
+ "Specify the Logical-Router identifier\n"
+ "The Name Space\n"
+ "The file name in " NS_RUN_DIR ", or a full pathname\n")
+{
+ int idx_number = 2;
+ int idx_name = 4;
+ ns_id_t ns_id;
+ struct ns *ns = NULL;
+ char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg);
+
+ if (!pathname)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ns_id = strtoul(argv[idx_number]->arg, NULL, 10);
+ ns = ns_lookup(ns_id);
+
+ if (!ns) {
+ vty_out(vty, "NS %u is not found\n", ns_id);
+ return CMD_SUCCESS;
+ }
+
+ if (ns->name && strcmp(ns->name, pathname) != 0) {
+ vty_out(vty, "Incorrect NETNS file name\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ ns_disable(ns);
+
+ if (ns->name) {
+ XFREE(MTYPE_LOGICALROUTER_NAME, ns->name);
+ ns->name = NULL;
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* Initialize NS module. */
+void logicalrouter_init(int (*writefunc)(struct vty *vty))
+{
+ if (ns_have_netns() && logicalrouter_is_backend_netns()) {
+ /* Install LogicalRouter commands. */
+ install_node(&logicalrouter_node, writefunc);
+ install_element(CONFIG_NODE, &logicalrouter_cmd);
+ install_element(CONFIG_NODE, &no_logicalrouter_cmd);
+ }
+}
+
+void logicalrouter_terminate(void)
+{
+ ns_terminate();
+}
+
+void logicalrouter_configure_backend(int backend_netns)
+{
+ logicalrouter_backend = backend_netns;
+}
diff --git a/lib/logicalrouter.h b/lib/logicalrouter.h
new file mode 100644
index 0000000000..5a0780c009
--- /dev/null
+++ b/lib/logicalrouter.h
@@ -0,0 +1,41 @@
+/*
+ * Logical Router related header.
+ * Copyright (C) 2018 6WIND S.A.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _ZEBRA_LOGICAL_ROUTER_H
+#define _ZEBRA_LOGICAL_ROUTER_H
+
+/* Logical Router Backend defines */
+#define LOGICALROUTER_BACKEND_OFF 0
+#define LOGICALROUTER_BACKEND_NETNS 1
+
+/*
+ * Logical Router initializer/destructor
+ */
+extern void logicalrouter_init(int (*writefunc)(struct vty *vty));
+extern void logicalrouter_terminate(void);
+
+/* used to configure backend for logical router
+ * Currently, the whole NETNS feature is exclusively shared
+ * between logical router and VRF backend NETNS
+ * However, when logical router feature will be available,
+ * one can think of having exclusivity only per NETNS
+ */
+extern void logicalrouter_configure_backend(int backend_netns);
+
+#endif /*_ZEBRA_LOGICAL_ROUTER_H*/
diff --git a/lib/netns_linux.c b/lib/netns_linux.c
new file mode 100644
index 0000000000..0e955bade9
--- /dev/null
+++ b/lib/netns_linux.c
@@ -0,0 +1,539 @@
+/*
+ * NS functions.
+ * Copyright (C) 2014 6WIND S.A.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#ifdef HAVE_NETNS
+#undef _GNU_SOURCE
+#define _GNU_SOURCE
+
+#include <sched.h>
+#endif
+
+/* for basename */
+#include <libgen.h>
+
+#include "if.h"
+#include "ns.h"
+#include "log.h"
+#include "memory.h"
+
+#include "command.h"
+#include "vty.h"
+#include "vrf.h"
+
+DEFINE_MTYPE_STATIC(LIB, NS, "NetNS Context")
+DEFINE_MTYPE_STATIC(LIB, NS_NAME, "NetNS Name")
+
+static inline int ns_compare(const struct ns *ns, const struct ns *ns2);
+static struct ns *ns_lookup_name_internal(const char *name);
+
+RB_GENERATE(ns_head, ns, entry, ns_compare)
+
+struct ns_head ns_tree = RB_INITIALIZER(&ns_tree);
+
+static struct ns *default_ns;
+static int ns_current_ns_fd;
+static int ns_default_ns_fd;
+
+static int ns_debug;
+
+#ifndef CLONE_NEWNET
+#define CLONE_NEWNET 0x40000000
+/* New network namespace (lo, device, names sockets, etc) */
+#endif
+
+#ifndef HAVE_SETNS
+static inline int setns(int fd, int nstype)
+{
+#ifdef __NR_setns
+ return syscall(__NR_setns, fd, nstype);
+#else
+ errno = EINVAL;
+ return -1;
+#endif
+}
+#endif /* !HAVE_SETNS */
+
+#ifdef HAVE_NETNS
+static int have_netns_enabled = -1;
+#endif /* HAVE_NETNS */
+
+/* default NS ID value used when VRF backend is not NETNS */
+#define NS_DEFAULT_INTERNAL 0
+
+static int have_netns(void)
+{
+#ifdef HAVE_NETNS
+ if (have_netns_enabled < 0) {
+ int fd = open(NS_DEFAULT_NAME, O_RDONLY);
+
+ if (fd < 0)
+ have_netns_enabled = 0;
+ else {
+ have_netns_enabled = 1;
+ close(fd);
+ }
+ }
+ return have_netns_enabled;
+#else
+ return 0;
+#endif
+}
+
+/* Holding NS hooks */
+struct ns_master {
+ int (*ns_new_hook)(struct ns *ns);
+ int (*ns_delete_hook)(struct ns *ns);
+ int (*ns_enable_hook)(struct ns *ns);
+ int (*ns_disable_hook)(struct ns *ns);
+} ns_master = {
+ 0,
+};
+
+static int ns_is_enabled(struct ns *ns);
+
+static inline int ns_compare(const struct ns *a, const struct ns *b)
+{
+ return (a->ns_id - b->ns_id);
+}
+
+/* Look up a NS by identifier. */
+static struct ns *ns_lookup_internal(ns_id_t ns_id)
+{
+ struct ns ns;
+
+ ns.ns_id = ns_id;
+ return RB_FIND(ns_head, &ns_tree, &ns);
+}
+
+/* Look up a NS by name */
+static struct ns *ns_lookup_name_internal(const char *name)
+{
+ struct ns *ns = NULL;
+
+ RB_FOREACH (ns, ns_head, &ns_tree) {
+ if (ns->name != NULL) {
+ if (strcmp(name, ns->name) == 0)
+ return ns;
+ }
+ }
+ return NULL;
+}
+
+static struct ns *ns_get_created_internal(struct ns *ns, char *name,
+ ns_id_t ns_id)
+{
+ int created = 0;
+ /*
+ * Initialize interfaces.
+ */
+ if (!ns && !name && ns_id != NS_UNKNOWN)
+ ns = ns_lookup_internal(ns_id);
+ if (!ns && name)
+ ns = ns_lookup_name_internal(name);
+ if (!ns) {
+ ns = XCALLOC(MTYPE_NS, sizeof(struct ns));
+ ns->ns_id = ns_id;
+ if (name)
+ ns->name = XSTRDUP(MTYPE_NS_NAME, name);
+ ns->fd = -1;
+ RB_INSERT(ns_head, &ns_tree, ns);
+ created = 1;
+ }
+ if (ns_id != ns->ns_id) {
+ RB_REMOVE(ns_head, &ns_tree, ns);
+ ns->ns_id = ns_id;
+ RB_INSERT(ns_head, &ns_tree, ns);
+ }
+ if (!created)
+ return ns;
+ if (ns_debug) {
+ if (ns->ns_id != NS_UNKNOWN)
+ zlog_info("NS %u is created.", ns->ns_id);
+ else
+ zlog_info("NS %s is created.", ns->name);
+ }
+ if (ns_master.ns_new_hook)
+ (*ns_master.ns_new_hook) (ns);
+ return ns;
+}
+
+/*
+ * Enable a NS - that is, let the NS be ready to use.
+ * The NS_ENABLE_HOOK callback will be called to inform
+ * that they can allocate resources in this NS.
+ *
+ * RETURN: 1 - enabled successfully; otherwise, 0.
+ */
+static int ns_enable_internal(struct ns *ns, void (*func)(ns_id_t, void *))
+{
+ if (!ns_is_enabled(ns)) {
+ if (have_netns()) {
+ ns->fd = open(ns->name, O_RDONLY);
+ } else {
+ ns->fd = -2;
+ /* Remember ns_enable_hook has been called */
+ errno = -ENOTSUP;
+ }
+
+ if (!ns_is_enabled(ns)) {
+ zlog_err("Can not enable NS %u: %s!", ns->ns_id,
+ safe_strerror(errno));
+ return 0;
+ }
+
+ /* Non default NS. leave */
+ if (ns->ns_id == NS_UNKNOWN) {
+ zlog_err("Can not enable NS %s %u: Invalid NSID",
+ ns->name, ns->ns_id);
+ return 0;
+ }
+ if (func)
+ func(ns->ns_id, (void *)ns->vrf_ctxt);
+ if (ns_debug) {
+ if (have_netns())
+ zlog_info("NS %u is associated with NETNS %s.",
+ ns->ns_id, ns->name);
+ zlog_info("NS %u is enabled.", ns->ns_id);
+ }
+ /* zebra first receives NS enable event,
+ * then VRF enable event
+ */
+ if (ns_master.ns_enable_hook)
+ (*ns_master.ns_enable_hook)(ns);
+ }
+
+ return 1;
+}
+
+/*
+ * Check whether the NS is enabled - that is, whether the NS
+ * is ready to allocate resources. Currently there's only one
+ * type of resource: socket.
+ */
+static int ns_is_enabled(struct ns *ns)
+{
+ if (have_netns())
+ return ns && ns->fd >= 0;
+ else
+ return ns && ns->fd == -2 && ns->ns_id == NS_DEFAULT;
+}
+
+/*
+ * Disable a NS - that is, let the NS be unusable.
+ * The NS_DELETE_HOOK callback will be called to inform
+ * that they must release the resources in the NS.
+ */
+static void ns_disable_internal(struct ns *ns)
+{
+ if (ns_is_enabled(ns)) {
+ if (ns_debug)
+ zlog_info("NS %u is to be disabled.",
+ ns->ns_id);
+
+ if (ns_master.ns_disable_hook)
+ (*ns_master.ns_disable_hook)(ns);
+
+ if (have_netns())
+ close(ns->fd);
+
+ ns->fd = -1;
+ }
+}
+
+struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id)
+{
+ return ns_get_created_internal(ns, name, ns_id);
+}
+
+int ns_have_netns(void)
+{
+ return have_netns();
+}
+
+/* Delete a NS. This is called in ns_terminate(). */
+void ns_delete(struct ns *ns)
+{
+ if (ns_debug)
+ zlog_info("NS %u is to be deleted.", ns->ns_id);
+
+ ns_disable(ns);
+
+ if (ns_master.ns_delete_hook)
+ (*ns_master.ns_delete_hook)(ns);
+
+ /*
+ * I'm not entirely sure if the vrf->iflist
+ * needs to be moved into here or not.
+ */
+ // if_terminate (&ns->iflist);
+
+ RB_REMOVE(ns_head, &ns_tree, ns);
+ if (ns->name)
+ XFREE(MTYPE_NS_NAME, ns->name);
+
+ XFREE(MTYPE_NS, ns);
+}
+
+/* Look up the data pointer of the specified VRF. */
+void *
+ns_info_lookup(ns_id_t ns_id)
+{
+ struct ns *ns = ns_lookup_internal(ns_id);
+
+ return ns ? ns->info : NULL;
+}
+
+/* Look up a NS by name */
+struct ns *ns_lookup_name(const char *name)
+{
+ return ns_lookup_name_internal(name);
+}
+
+int ns_enable(struct ns *ns, void (*func)(ns_id_t, void *))
+{
+ return ns_enable_internal(ns, func);
+}
+
+void ns_disable(struct ns *ns)
+{
+ return ns_disable_internal(ns);
+}
+
+struct ns *ns_lookup(ns_id_t ns_id)
+{
+ return ns_lookup_internal(ns_id);
+}
+
+void ns_walk_func(int (*func)(struct ns *))
+{
+ struct ns *ns = NULL;
+
+ RB_FOREACH (ns, ns_head, &ns_tree)
+ func(ns);
+}
+
+const char *ns_get_name(struct ns *ns)
+{
+ if (!ns)
+ return NULL;
+ return ns->name;
+}
+
+/* Add a NS hook. Please add hooks before calling ns_init(). */
+void ns_add_hook(int type, int (*func)(struct ns *))
+{
+ switch (type) {
+ case NS_NEW_HOOK:
+ ns_master.ns_new_hook = func;
+ break;
+ case NS_DELETE_HOOK:
+ ns_master.ns_delete_hook = func;
+ break;
+ case NS_ENABLE_HOOK:
+ ns_master.ns_enable_hook = func;
+ break;
+ case NS_DISABLE_HOOK:
+ ns_master.ns_disable_hook = func;
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * NS realization with NETNS
+ */
+
+char *ns_netns_pathname(struct vty *vty, const char *name)
+{
+ static char pathname[PATH_MAX];
+ char *result;
+ char *check_base;
+
+ if (name[0] == '/') /* absolute pathname */
+ result = realpath(name, pathname);
+ else {
+ /* relevant pathname */
+ char tmp_name[PATH_MAX];
+
+ snprintf(tmp_name, PATH_MAX, "%s/%s", NS_RUN_DIR, name);
+ result = realpath(tmp_name, pathname);
+ }
+
+ if (!result) {
+ if (vty)
+ vty_out(vty, "Invalid pathname: %s\n",
+ safe_strerror(errno));
+ else
+ zlog_warn("Invalid pathname: %s",
+ safe_strerror(errno));
+ return NULL;
+ }
+ check_base = basename(pathname);
+ if (check_base != NULL && strlen(check_base) + 1 > NS_NAMSIZ) {
+ if (vty)
+ vty_out(vty, "NS name (%s) invalid: too long (>%d)\n",
+ check_base, NS_NAMSIZ-1);
+ else
+ zlog_warn("NS name (%s) invalid: too long (>%d)",
+ check_base, NS_NAMSIZ-1);
+ return NULL;
+ }
+ return pathname;
+}
+
+void ns_init(void)
+{
+ static int ns_initialised;
+
+ ns_debug = 0;
+ /* silently return as initialisation done */
+ if (ns_initialised == 1)
+ return;
+ errno = 0;
+#ifdef HAVE_NETNS
+ if (have_netns_enabled < 0)
+ ns_default_ns_fd = open(NS_DEFAULT_NAME, O_RDONLY);
+ else
+ ns_default_ns_fd = -1;
+#else
+ ns_default_ns_fd = -1;
+ default_ns = NULL;
+#endif /* HAVE_NETNS */
+ if (ns_default_ns_fd == -1)
+ zlog_err("NS initialisation failure (%s)",
+ safe_strerror(errno));
+ ns_current_ns_fd = -1;
+ ns_initialised = 1;
+}
+
+/* Initialize NS module. */
+void ns_init_management(ns_id_t default_ns_id)
+{
+ int fd;
+
+ ns_init();
+ default_ns = ns_get_created_internal(NULL, NULL, default_ns_id);
+ if (!default_ns) {
+ zlog_err("%s: failed to create the default NS!",
+ __func__);
+ exit(1);
+ }
+ if (have_netns()) {
+ fd = open(NS_DEFAULT_NAME, O_RDONLY);
+ default_ns->fd = fd;
+ }
+ /* Set the default NS name. */
+ default_ns->name = XSTRDUP(MTYPE_NS_NAME, NS_DEFAULT_NAME);
+ if (ns_debug)
+ zlog_info("%s: default NSID is %u",
+ __func__, default_ns->ns_id);
+
+ /* Enable the default NS. */
+ if (!ns_enable(default_ns, NULL)) {
+ zlog_err("%s: failed to enable the default NS!",
+ __func__);
+ exit(1);
+ }
+}
+
+/* Terminate NS module. */
+void ns_terminate(void)
+{
+ struct ns *ns;
+
+ while (!RB_EMPTY(ns_head, &ns_tree)) {
+ ns = RB_ROOT(ns_head, &ns_tree);
+
+ ns_delete(ns);
+ }
+}
+
+int ns_switch_to_netns(const char *name)
+{
+ int ret;
+ int fd;
+
+ if (name == NULL)
+ return -1;
+ if (ns_default_ns_fd == -1)
+ return -1;
+ fd = open(name, O_RDONLY);
+ if (fd == -1) {
+ errno = EINVAL;
+ return -1;
+ }
+ ret = setns(fd, CLONE_NEWNET);
+ ns_current_ns_fd = fd;
+ close(fd);
+ return ret;
+}
+
+/* returns 1 if switch() was not called before
+ * return status of setns() otherwise
+ */
+int ns_switchback_to_initial(void)
+{
+ if (ns_current_ns_fd != -1 && ns_default_ns_fd != -1) {
+ int ret;
+
+ ret = setns(ns_default_ns_fd, CLONE_NEWNET);
+ ns_current_ns_fd = -1;
+ return ret;
+ }
+ /* silently ignore if setns() is not called */
+ return 1;
+}
+
+/* Create a socket for the NS. */
+int ns_socket(int domain, int type, int protocol, ns_id_t ns_id)
+{
+ struct ns *ns = ns_lookup(ns_id);
+ int ret;
+
+ if (!ns || !ns_is_enabled(ns)) {
+ errno = EINVAL;
+ return -1;
+ }
+ if (have_netns()) {
+ ret = (ns_id != NS_DEFAULT) ? setns(ns->fd, CLONE_NEWNET) : 0;
+ if (ret >= 0) {
+ ret = socket(domain, type, protocol);
+ if (ns_id != NS_DEFAULT) {
+ setns(ns_lookup(NS_DEFAULT)->fd, CLONE_NEWNET);
+ ns_current_ns_fd = ns_id;
+ }
+ }
+ } else
+ ret = socket(domain, type, protocol);
+
+ return ret;
+}
+
+ns_id_t ns_get_default_id(void)
+{
+ if (default_ns)
+ return default_ns->ns_id;
+ return NS_UNKNOWN;
+}
+
diff --git a/lib/netns_other.c b/lib/netns_other.c
new file mode 100644
index 0000000000..2402dd17d6
--- /dev/null
+++ b/lib/netns_other.c
@@ -0,0 +1,165 @@
+/*
+ * NetNS backend for non Linux systems
+ * Copyright (C) 2018 6WIND S.A.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#if !defined(GNU_LINUX) && (defined(SUNOS_5) || defined(OPEN_BSD))
+/* SUNOS_5 or OPEN_BSD */
+
+#include <zebra.h>
+#include "ns.h"
+#include "log.h"
+#include "memory.h"
+
+DEFINE_MTYPE_STATIC(LIB, NS, "NetNS Context")
+DEFINE_MTYPE_STATIC(LIB, NS_NAME, "NetNS Name")
+
+
+static inline int ns_compare(const struct ns *ns, const struct ns *ns2);
+
+RB_GENERATE(ns_head, ns, entry, ns_compare)
+
+struct ns_head ns_tree = RB_INITIALIZER(&ns_tree);
+
+static inline int ns_compare(const struct ns *a, const struct ns *b)
+{
+ return (a->ns_id - b->ns_id);
+}
+
+void ns_terminate(void)
+{
+}
+
+/* API to initialize NETNS managerment
+ * parameter is the default ns_id
+ */
+void ns_init_management(ns_id_t ns_id)
+{
+}
+
+
+/*
+ * NS utilities
+ */
+
+/* Create a socket serving for the given NS
+ */
+int ns_socket(int domain, int type, int protocol, ns_id_t ns_id)
+{
+ return -1;
+}
+
+/* return the path of the NETNS */
+char *ns_netns_pathname(struct vty *vty, const char *name)
+{
+ return NULL;
+}
+
+/* Parse and execute a function on all the NETNS */
+void ns_walk_func(int (*func)(struct ns *))
+{
+}
+
+/* API to get the NETNS name, from the ns pointer */
+const char *ns_get_name(struct ns *ns)
+{
+ return NULL;
+}
+
+/* only called from vrf ( when removing netns from vrf)
+ * or at VRF or logical router termination
+ */
+void ns_delete(struct ns *ns)
+{
+}
+
+/* return > 0 if netns is available
+ * called by VRF to check netns backend is available for VRF
+ */
+int ns_have_netns(void)
+{
+ return 0;
+}
+
+/* API to get context information of a NS */
+void *ns_info_lookup(ns_id_t ns_id)
+{
+ return NULL;
+}
+
+/*
+ * NS init routine
+ * should be called from backendx
+ */
+void ns_init(void)
+{
+}
+
+/* API to retrieve default NS */
+ns_id_t ns_get_default_id(void)
+{
+ return NS_UNKNOWN;
+}
+
+
+/* API that can be used to change from NS */
+int ns_switchback_to_initial(void)
+{
+ return 0;
+}
+int ns_switch_to_netns(const char *netns_name)
+{
+ return 0;
+}
+
+/*
+ * NS handling routines.
+ * called by modules that use NS backend
+ */
+
+/* API to search for already present NETNS */
+struct ns *ns_lookup(ns_id_t ns_id)
+{
+ return NULL;
+}
+
+struct ns *ns_lookup_name(const char *name)
+{
+ return NULL;
+}
+
+/* API to handle NS : creation, enable, disable
+ * for enable, a callback function is passed as parameter
+ * the callback belongs to the module that uses NS as backend
+ * upon enabling the NETNS, the upper layer is informed
+ */
+int ns_enable(struct ns *ns, int (*func)(ns_id_t, void *))
+{
+ return 0;
+}
+
+struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id)
+{
+ return NULL;
+}
+
+void ns_disable(struct ns *ns)
+{
+}
+
+#endif /* !GNU_LINUX */
diff --git a/lib/ns.c b/lib/ns.c
deleted file mode 100644
index fdf93d0742..0000000000
--- a/lib/ns.c
+++ /dev/null
@@ -1,453 +0,0 @@
-/*
- * NS functions.
- * Copyright (C) 2014 6WIND S.A.
- *
- * This file is part of GNU Zebra.
- *
- * GNU Zebra is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * GNU Zebra is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; see the file COPYING; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <zebra.h>
-
-#ifdef HAVE_NETNS
-#undef _GNU_SOURCE
-#define _GNU_SOURCE
-
-#include <sched.h>
-#endif
-
-#include "if.h"
-#include "ns.h"
-#include "log.h"
-#include "memory.h"
-
-#include "command.h"
-#include "vty.h"
-
-DEFINE_MTYPE_STATIC(LIB, NS, "Logical-Router")
-DEFINE_MTYPE_STATIC(LIB, NS_NAME, "Logical-Router Name")
-
-static __inline int ns_compare(const struct ns *, const struct ns *);
-static struct ns *ns_lookup(ns_id_t);
-
-RB_GENERATE(ns_head, ns, entry, ns_compare)
-
-struct ns_head ns_tree = RB_INITIALIZER(&ns_tree);
-
-#ifndef CLONE_NEWNET
-#define CLONE_NEWNET 0x40000000 /* New network namespace (lo, device, names sockets, etc) */
-#endif
-
-#ifndef HAVE_SETNS
-static inline int setns(int fd, int nstype)
-{
-#ifdef __NR_setns
- return syscall(__NR_setns, fd, nstype);
-#else
- errno = ENOSYS;
- return -1;
-#endif
-}
-#endif /* HAVE_SETNS */
-
-#ifdef HAVE_NETNS
-
-#define NS_DEFAULT_NAME "/proc/self/ns/net"
-static int have_netns_enabled = -1;
-
-#else /* !HAVE_NETNS */
-
-#define NS_DEFAULT_NAME "Default-logical-router"
-
-#endif /* HAVE_NETNS */
-
-static int have_netns(void)
-{
-#ifdef HAVE_NETNS
- if (have_netns_enabled < 0) {
- int fd = open(NS_DEFAULT_NAME, O_RDONLY);
-
- if (fd < 0)
- have_netns_enabled = 0;
- else {
- have_netns_enabled = 1;
- close(fd);
- }
- }
- return have_netns_enabled;
-#else
- return 0;
-#endif
-}
-
-/* Holding NS hooks */
-struct ns_master {
- int (*ns_new_hook)(ns_id_t, void **);
- int (*ns_delete_hook)(ns_id_t, void **);
- int (*ns_enable_hook)(ns_id_t, void **);
- int (*ns_disable_hook)(ns_id_t, void **);
-} ns_master = {
- 0,
-};
-
-static int ns_is_enabled(struct ns *ns);
-static int ns_enable(struct ns *ns);
-static void ns_disable(struct ns *ns);
-
-static __inline int ns_compare(const struct ns *a, const struct ns *b)
-{
- return (a->ns_id - b->ns_id);
-}
-
-/* Get a NS. If not found, create one. */
-static struct ns *ns_get(ns_id_t ns_id)
-{
- struct ns *ns;
-
- ns = ns_lookup(ns_id);
- if (ns)
- return (ns);
-
- ns = XCALLOC(MTYPE_NS, sizeof(struct ns));
- ns->ns_id = ns_id;
- ns->fd = -1;
- RB_INSERT(ns_head, &ns_tree, ns);
-
- /*
- * Initialize interfaces.
- *
- * I'm not sure if this belongs here or in
- * the vrf code.
- */
- // if_init (&ns->iflist);
-
- zlog_info("NS %u is created.", ns_id);
-
- if (ns_master.ns_new_hook)
- (*ns_master.ns_new_hook)(ns_id, &ns->info);
-
- return ns;
-}
-
-/* Delete a NS. This is called in ns_terminate(). */
-static void ns_delete(struct ns *ns)
-{
- zlog_info("NS %u is to be deleted.", ns->ns_id);
-
- ns_disable(ns);
-
- if (ns_master.ns_delete_hook)
- (*ns_master.ns_delete_hook)(ns->ns_id, &ns->info);
-
- /*
- * I'm not entirely sure if the vrf->iflist
- * needs to be moved into here or not.
- */
- // if_terminate (&ns->iflist);
-
- RB_REMOVE(ns_head, &ns_tree, ns);
- if (ns->name)
- XFREE(MTYPE_NS_NAME, ns->name);
-
- XFREE(MTYPE_NS, ns);
-}
-
-/* Look up a NS by identifier. */
-static struct ns *ns_lookup(ns_id_t ns_id)
-{
- struct ns ns;
- ns.ns_id = ns_id;
- return (RB_FIND(ns_head, &ns_tree, &ns));
-}
-
-/*
- * Check whether the NS is enabled - that is, whether the NS
- * is ready to allocate resources. Currently there's only one
- * type of resource: socket.
- */
-static int ns_is_enabled(struct ns *ns)
-{
- if (have_netns())
- return ns && ns->fd >= 0;
- else
- return ns && ns->fd == -2 && ns->ns_id == NS_DEFAULT;
-}
-
-/*
- * Enable a NS - that is, let the NS be ready to use.
- * The NS_ENABLE_HOOK callback will be called to inform
- * that they can allocate resources in this NS.
- *
- * RETURN: 1 - enabled successfully; otherwise, 0.
- */
-static int ns_enable(struct ns *ns)
-{
-
- if (!ns_is_enabled(ns)) {
- if (have_netns()) {
- ns->fd = open(ns->name, O_RDONLY);
- } else {
- ns->fd = -2; /* Remember that ns_enable_hook has been
- called */
- errno = -ENOTSUP;
- }
-
- if (!ns_is_enabled(ns)) {
- zlog_err("Can not enable NS %u: %s!", ns->ns_id,
- safe_strerror(errno));
- return 0;
- }
-
- if (have_netns())
- zlog_info("NS %u is associated with NETNS %s.",
- ns->ns_id, ns->name);
-
- zlog_info("NS %u is enabled.", ns->ns_id);
- if (ns_master.ns_enable_hook)
- (*ns_master.ns_enable_hook)(ns->ns_id, &ns->info);
- }
-
- return 1;
-}
-
-/*
- * Disable a NS - that is, let the NS be unusable.
- * The NS_DELETE_HOOK callback will be called to inform
- * that they must release the resources in the NS.
- */
-static void ns_disable(struct ns *ns)
-{
- if (ns_is_enabled(ns)) {
- zlog_info("NS %u is to be disabled.", ns->ns_id);
-
- if (ns_master.ns_disable_hook)
- (*ns_master.ns_disable_hook)(ns->ns_id, &ns->info);
-
- if (have_netns())
- close(ns->fd);
-
- ns->fd = -1;
- }
-}
-
-
-/* Add a NS hook. Please add hooks before calling ns_init(). */
-void ns_add_hook(int type, int (*func)(ns_id_t, void **))
-{
- switch (type) {
- case NS_NEW_HOOK:
- ns_master.ns_new_hook = func;
- break;
- case NS_DELETE_HOOK:
- ns_master.ns_delete_hook = func;
- break;
- case NS_ENABLE_HOOK:
- ns_master.ns_enable_hook = func;
- break;
- case NS_DISABLE_HOOK:
- ns_master.ns_disable_hook = func;
- break;
- default:
- break;
- }
-}
-
-/*
- * NS realization with NETNS
- */
-
-static char *ns_netns_pathname(struct vty *vty, const char *name)
-{
- static char pathname[PATH_MAX];
- char *result;
-
- if (name[0] == '/') /* absolute pathname */
- result = realpath(name, pathname);
- else /* relevant pathname */
- {
- char tmp_name[PATH_MAX];
- snprintf(tmp_name, PATH_MAX, "%s/%s", NS_RUN_DIR, name);
- result = realpath(tmp_name, pathname);
- }
-
- if (!result) {
- vty_out(vty, "Invalid pathname: %s\n", safe_strerror(errno));
- return NULL;
- }
- return pathname;
-}
-
-DEFUN_NOSH (ns_netns,
- ns_netns_cmd,
- "logical-router (1-65535) ns NAME",
- "Enable a logical-router\n"
- "Specify the logical-router indentifier\n"
- "The Name Space\n"
- "The file name in " NS_RUN_DIR ", or a full pathname\n")
-{
- int idx_number = 1;
- int idx_name = 3;
- ns_id_t ns_id = NS_DEFAULT;
- struct ns *ns = NULL;
- char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg);
-
- if (!pathname)
- return CMD_WARNING_CONFIG_FAILED;
-
- ns_id = strtoul(argv[idx_number]->arg, NULL, 10);
- ns = ns_get(ns_id);
-
- if (ns->name && strcmp(ns->name, pathname) != 0) {
- vty_out(vty, "NS %u is already configured with NETNS %s\n",
- ns->ns_id, ns->name);
- return CMD_WARNING;
- }
-
- if (!ns->name)
- ns->name = XSTRDUP(MTYPE_NS_NAME, pathname);
-
- if (!ns_enable(ns)) {
- vty_out(vty, "Can not associate NS %u with NETNS %s\n",
- ns->ns_id, ns->name);
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- return CMD_SUCCESS;
-}
-
-DEFUN (no_ns_netns,
- no_ns_netns_cmd,
- "no logical-router (1-65535) ns NAME",
- NO_STR
- "Enable a Logical-Router\n"
- "Specify the Logical-Router identifier\n"
- "The Name Space\n"
- "The file name in " NS_RUN_DIR ", or a full pathname\n")
-{
- int idx_number = 2;
- int idx_name = 4;
- ns_id_t ns_id = NS_DEFAULT;
- struct ns *ns = NULL;
- char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg);
-
- if (!pathname)
- return CMD_WARNING_CONFIG_FAILED;
-
- ns_id = strtoul(argv[idx_number]->arg, NULL, 10);
- ns = ns_lookup(ns_id);
-
- if (!ns) {
- vty_out(vty, "NS %u is not found\n", ns_id);
- return CMD_SUCCESS;
- }
-
- if (ns->name && strcmp(ns->name, pathname) != 0) {
- vty_out(vty, "Incorrect NETNS file name\n");
- return CMD_WARNING_CONFIG_FAILED;
- }
-
- ns_disable(ns);
-
- if (ns->name) {
- XFREE(MTYPE_NS_NAME, ns->name);
- ns->name = NULL;
- }
-
- return CMD_SUCCESS;
-}
-
-/* NS node. */
-static struct cmd_node ns_node = {NS_NODE, "", /* NS node has no interface. */
- 1};
-
-/* NS configuration write function. */
-static int ns_config_write(struct vty *vty)
-{
- struct ns *ns;
- int write = 0;
-
- RB_FOREACH (ns, ns_head, &ns_tree) {
- if (ns->ns_id == NS_DEFAULT || ns->name == NULL)
- continue;
-
- vty_out(vty, "logical-router %u netns %s\n", ns->ns_id,
- ns->name);
- write = 1;
- }
-
- return write;
-}
-
-/* Initialize NS module. */
-void ns_init(void)
-{
- struct ns *default_ns;
-
- /* The default NS always exists. */
- default_ns = ns_get(NS_DEFAULT);
- if (!default_ns) {
- zlog_err("ns_init: failed to create the default NS!");
- exit(1);
- }
-
- /* Set the default NS name. */
- default_ns->name = XSTRDUP(MTYPE_NS_NAME, NS_DEFAULT_NAME);
-
- /* Enable the default NS. */
- if (!ns_enable(default_ns)) {
- zlog_err("ns_init: failed to enable the default NS!");
- exit(1);
- }
-
- if (have_netns()) {
- /* Install NS commands. */
- install_node(&ns_node, ns_config_write);
- install_element(CONFIG_NODE, &ns_netns_cmd);
- install_element(CONFIG_NODE, &no_ns_netns_cmd);
- }
-}
-
-/* Terminate NS module. */
-void ns_terminate(void)
-{
- struct ns *ns;
-
- while ((ns = RB_ROOT(ns_head, &ns_tree)) != NULL)
- ns_delete(ns);
-}
-
-/* Create a socket for the NS. */
-int ns_socket(int domain, int type, int protocol, ns_id_t ns_id)
-{
- struct ns *ns = ns_lookup(ns_id);
- int ret = -1;
-
- if (!ns_is_enabled(ns)) {
- errno = ENOSYS;
- return -1;
- }
-
- if (have_netns()) {
- ret = (ns_id != NS_DEFAULT) ? setns(ns->fd, CLONE_NEWNET) : 0;
- if (ret >= 0) {
- ret = socket(domain, type, protocol);
- if (ns_id != NS_DEFAULT)
- setns(ns_lookup(NS_DEFAULT)->fd, CLONE_NEWNET);
- }
- } else
- ret = socket(domain, type, protocol);
-
- return ret;
-}
diff --git a/lib/ns.h b/lib/ns.h
index 79b4cab04d..83e5e1b907 100644
--- a/lib/ns.h
+++ b/lib/ns.h
@@ -24,16 +24,22 @@
#include "openbsd-tree.h"
#include "linklist.h"
+#include "vty.h"
typedef u_int32_t ns_id_t;
/* the default NS ID */
-#define NS_DEFAULT 0
#define NS_UNKNOWN UINT32_MAX
/* Default netns directory (Linux) */
#define NS_RUN_DIR "/var/run/netns"
+#ifdef HAVE_NETNS
+#define NS_DEFAULT_NAME "/proc/self/ns/net"
+#else /* !HAVE_NETNS */
+#define NS_DEFAULT_NAME "Default-logical-router"
+#endif /* HAVE_NETNS */
+
struct ns {
RB_ENTRY(ns) entry;
@@ -49,6 +55,9 @@ struct ns {
/* Master list of interfaces belonging to this NS */
struct list *iflist;
+ /* Back Pointer to VRF */
+ void *vrf_ctxt;
+
/* User data */
void *info;
};
@@ -58,6 +67,11 @@ RB_PROTOTYPE(ns_head, ns, entry, ns_compare)
extern struct ns_head ns_tree;
/*
+ * API for managing NETNS. eg from zebra daemon
+ * one want to manage the list of NETNS, etc...
+ */
+
+/*
* NS hooks
*/
@@ -74,20 +88,82 @@ extern struct ns_head ns_tree;
* - param 2: the address of the user data pointer (the user data
* can be stored in or freed from there)
*/
-extern void ns_add_hook(int, int (*)(ns_id_t, void **));
+extern void ns_add_hook(int type, int (*)(struct ns *));
+
/*
* NS initializer/destructor
*/
-/* Please add hooks before calling ns_init(). */
-extern void ns_init(void);
+
extern void ns_terminate(void);
+/* API to initialize NETNS managerment
+ * parameter is the default ns_id
+ */
+extern void ns_init_management(ns_id_t ns_id);
+
+
/*
* NS utilities
*/
-/* Create a socket serving for the given NS */
-extern int ns_socket(int, int, int, ns_id_t);
+/* Create a socket serving for the given NS
+ */
+int ns_socket(int domain, int type, int protocol, ns_id_t ns_id);
+
+/* return the path of the NETNS */
+extern char *ns_netns_pathname(struct vty *vty, const char *name);
+
+/* Parse and execute a function on all the NETNS */
+extern void ns_walk_func(int (*func)(struct ns *));
+
+/* API to get the NETNS name, from the ns pointer */
+extern const char *ns_get_name(struct ns *ns);
+
+/* only called from vrf ( when removing netns from vrf)
+ * or at VRF or logical router termination
+ */
+extern void ns_delete(struct ns *ns);
+
+/* return > 0 if netns is available
+ * called by VRF to check netns backend is available for VRF
+ */
+extern int ns_have_netns(void);
+
+/* API to get context information of a NS */
+extern void *ns_info_lookup(ns_id_t ns_id);
+
+/*
+ * NS init routine
+ * should be called from backendx
+ */
+extern void ns_init(void);
+
+/* API to retrieve default NS */
+extern ns_id_t ns_get_default_id(void);
+
+#define NS_DEFAULT ns_get_default_id()
+
+/* API that can be used to change from NS */
+extern int ns_switchback_to_initial(void);
+extern int ns_switch_to_netns(const char *netns_name);
+
+/*
+ * NS handling routines.
+ * called by modules that use NS backend
+ */
+
+/* API to search for already present NETNS */
+extern struct ns *ns_lookup(ns_id_t ns_id);
+extern struct ns *ns_lookup_name(const char *name);
+
+/* API to handle NS : creation, enable, disable
+ * for enable, a callback function is passed as parameter
+ * the callback belongs to the module that uses NS as backend
+ * upon enabling the NETNS, the upper layer is informed
+ */
+extern int ns_enable(struct ns *ns, void (*func)(ns_id_t, void *));
+extern struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id);
+extern void ns_disable(struct ns *ns);
#endif /*_ZEBRA_NS_H*/
diff --git a/lib/subdir.am b/lib/subdir.am
index 44870917bd..e292d7a342 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -42,7 +42,8 @@ lib_libfrr_la_SOURCES = \
lib/module.c \
lib/network.c \
lib/nexthop.c \
- lib/ns.c \
+ lib/netns_linux.c \
+ lib/netns_other.c \
lib/openbsd-tree.c \
lib/pid_output.c \
lib/plist.c \
@@ -74,6 +75,7 @@ lib_libfrr_la_SOURCES = \
lib/wheel.c \
lib/workqueue.c \
lib/zclient.c \
+ lib/logicalrouter.c \
# end
lib/plist_clippy.c: $(CLIPPY_DEPS)
@@ -158,6 +160,7 @@ pkginclude_HEADERS += \
lib/zassert.h \
lib/zclient.h \
lib/zebra.h \
+ lib/logicalrouter.h \
# end
nodist_pkginclude_HEADERS += \
diff --git a/lib/vrf.c b/lib/vrf.c
index 2e15fa2f5c..ea106b90a2 100644
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -21,6 +21,9 @@
#include <zebra.h>
+/* for basename */
+#include <libgen.h>
+
#include "if.h"
#include "vrf.h"
#include "vrf_int.h"
@@ -29,6 +32,10 @@
#include "log.h"
#include "memory.h"
#include "command.h"
+#include "ns.h"
+
+/* default VRF ID value used when VRF backend is not NETNS */
+#define VRF_DEFAULT_INTERNAL 0
DEFINE_MTYPE_STATIC(LIB, VRF, "VRF")
DEFINE_MTYPE_STATIC(LIB, VRF_BITMAP, "VRF bit-map")
@@ -44,6 +51,8 @@ RB_GENERATE(vrf_name_head, vrf, name_entry, vrf_name_compare);
struct vrf_id_head vrfs_by_id = RB_INITIALIZER(&vrfs_by_id);
struct vrf_name_head vrfs_by_name = RB_INITIALIZER(&vrfs_by_name);
+static int vrf_backend;
+
/*
* Turn on/off debug code
* for vrf.
@@ -61,7 +70,6 @@ struct vrf_master {
};
static int vrf_is_enabled(struct vrf *vrf);
-static void vrf_disable(struct vrf *vrf);
/* VRF list existance check by name. */
struct vrf *vrf_lookup_by_name(const char *name)
@@ -81,6 +89,54 @@ static int vrf_name_compare(const struct vrf *a, const struct vrf *b)
return strcmp(a->name, b->name);
}
+/* 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((struct vrf *)vrf);
+}
+
+int vrf_switch_to_netns(vrf_id_t vrf_id)
+{
+ char *name;
+ struct vrf *vrf = vrf_lookup_by_id(vrf_id);
+
+ /* VRF is default VRF. silently ignore */
+ if (!vrf || vrf->vrf_id == VRF_DEFAULT)
+ return 0;
+ /* VRF has no NETNS backend. silently ignore */
+ if (vrf->data.l.netns_name[0] == '\0')
+ return 0;
+ name = ns_netns_pathname(NULL, vrf->data.l.netns_name);
+ if (debug_vrf)
+ zlog_debug("VRF_SWITCH: %s(%u)", name, vrf->vrf_id);
+ return ns_switch_to_netns(name);
+}
+
+int vrf_switchback_to_initial(void)
+{
+ int ret = ns_switchback_to_initial();
+
+ if (ret == 0 && debug_vrf)
+ zlog_debug("VRF_SWITCHBACK");
+ return ret;
+}
+
/* Get a VRF. If not found, create one.
* Arg:
* name - The name of the vrf. May be NULL if unknown.
@@ -94,7 +150,8 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name)
int new = 0;
if (debug_vrf)
- zlog_debug("VRF_GET: %s(%u)", name, vrf_id);
+ zlog_debug("VRF_GET: %s(%u)",
+ name == NULL ? "(NULL)" : name, vrf_id);
/* Nothing to see, move along here */
if (!name && vrf_id == VRF_UNKNOWN)
@@ -109,8 +166,6 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name)
if (vrf == NULL) {
vrf = XCALLOC(MTYPE_VRF, sizeof(struct vrf));
vrf->vrf_id = VRF_UNKNOWN;
- RB_INIT(if_name_head, &vrf->ifaces_by_name);
- RB_INIT(if_index_head, &vrf->ifaces_by_index);
QOBJ_REG(vrf, vrf);
new = 1;
@@ -134,7 +189,6 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name)
strlcpy(vrf->name, name, sizeof(vrf->name));
RB_INSERT(vrf_name_head, &vrfs_by_name, vrf);
}
-
if (new &&vrf_master.vrf_new_hook)
(*vrf_master.vrf_new_hook)(vrf);
@@ -219,7 +273,7 @@ int vrf_enable(struct vrf *vrf)
* The VRF_DELETE_HOOK callback will be called to inform
* that they must release the resources in the VRF.
*/
-static void vrf_disable(struct vrf *vrf)
+void vrf_disable(struct vrf *vrf)
{
if (!vrf_is_enabled(vrf))
return;
@@ -385,6 +439,8 @@ void vrf_init(int (*create)(struct vrf *), int (*enable)(struct vrf *),
{
struct vrf *default_vrf;
+ /* initialise NS, in case VRF backend if NETNS */
+ ns_init();
if (debug_vrf)
zlog_debug("%s: Initializing VRF subsystem",
__PRETTY_FUNCTION__);
@@ -419,12 +475,17 @@ void vrf_terminate(void)
zlog_debug("%s: Shutting down vrf subsystem",
__PRETTY_FUNCTION__);
- while ((vrf = RB_ROOT(vrf_id_head, &vrfs_by_id)) != NULL) {
+ while (!RB_EMPTY(vrf_id_head, &vrfs_by_id)) {
+ vrf = RB_ROOT(vrf_id_head, &vrfs_by_id);
+
/* Clear configured flag and invoke delete. */
UNSET_FLAG(vrf->status, VRF_CONFIGURED);
vrf_delete(vrf);
}
- while ((vrf = RB_ROOT(vrf_name_head, &vrfs_by_name)) != NULL) {
+
+ while (!RB_EMPTY(vrf_name_head, &vrfs_by_name)) {
+ vrf = RB_ROOT(vrf_name_head, &vrfs_by_name);
+
/* Clear configured flag and invoke delete. */
UNSET_FLAG(vrf->status, VRF_CONFIGURED);
vrf_delete(vrf);
@@ -432,41 +493,163 @@ void vrf_terminate(void)
}
/* Create a socket for the VRF. */
-int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id)
+int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id,
+ char *interfacename)
{
- int ret = -1;
+ int ret, save_errno, ret2;
+ ret = vrf_switch_to_netns(vrf_id);
+ if (ret < 0)
+ zlog_err("%s: Can't switch to VRF %u (%s)",
+ __func__, vrf_id, safe_strerror(errno));
ret = socket(domain, type, protocol);
-
+ save_errno = errno;
+ ret2 = vrf_switchback_to_initial();
+ if (ret2 < 0)
+ zlog_err("%s: Can't switchback from VRF %u (%s)",
+ __func__, vrf_id, safe_strerror(errno));
+ errno = save_errno;
+ if (ret <= 0)
+ return ret;
+ ret2 = vrf_bind(vrf_id, ret, interfacename);
+ if (ret2 < 0) {
+ close(ret);
+ ret = ret2;
+ }
return ret;
}
-/* vrf CLI commands */
-DEFUN_NOSH (vrf,
- vrf_cmd,
- "vrf NAME",
- "Select a VRF to configure\n"
- "VRF's name\n")
+int vrf_is_backend_netns(void)
+{
+ return (vrf_backend == VRF_BACKEND_NETNS);
+}
+
+int vrf_get_backend(void)
+{
+ return vrf_backend;
+}
+
+void vrf_configure_backend(int vrf_backend_netns)
+{
+ vrf_backend = vrf_backend_netns;
+}
+
+int vrf_handler_create(struct vty *vty, const char *vrfname, struct vrf **vrf)
{
- int idx_name = 1;
- const char *vrfname = argv[idx_name]->arg;
struct vrf *vrfp;
if (strlen(vrfname) > VRF_NAMSIZ) {
- vty_out(vty,
- "%% VRF name %s is invalid: length exceeds "
- "%d characters\n",
- vrfname, VRF_NAMSIZ);
+ if (vty)
+ vty_out(vty,
+ "%% VRF name %s invalid: length exceeds %d bytes\n",
+ vrfname, VRF_NAMSIZ);
+ else
+ zlog_warn(
+ "%% VRF name %s invalid: length exceeds %d bytes\n",
+ vrfname, VRF_NAMSIZ);
return CMD_WARNING_CONFIG_FAILED;
}
vrfp = vrf_get(VRF_UNKNOWN, vrfname);
- VTY_PUSH_CONTEXT(VRF_NODE, vrfp);
+ if (vty)
+ VTY_PUSH_CONTEXT(VRF_NODE, vrfp);
+
+ if (vrf)
+ *vrf = vrfp;
+ return CMD_SUCCESS;
+}
+
+int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf,
+ char *pathname, ns_id_t 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_warn("VRF %u is already configured with VRF %s\n",
+ vrf->vrf_id, vrf->name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (vrf->ns_ctxt != NULL) {
+ ns = (struct ns *) vrf->ns_ctxt;
+ if (ns && 0 != strcmp(ns->name, pathname)) {
+ if (vty)
+ vty_out(vty,
+ "VRF %u already configured with NETNS %s\n",
+ vrf->vrf_id, ns->name);
+ else
+ zlog_warn(
+ "VRF %u already configured with NETNS %s",
+ vrf->vrf_id, ns->name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+ 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_warn("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->vrf_ctxt = (void *)vrf;
+ vrf->ns_ctxt = (void *)ns;
+ /* update VRF netns NAME */
+ if (vrf)
+ 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_warn("Can not associate NS %u with NETNS %s",
+ ns->ns_id, ns->name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
return CMD_SUCCESS;
}
+int vrf_is_mapped_on_netns(vrf_id_t vrf_id)
+{
+ struct vrf *vrf = vrf_lookup_by_id(vrf_id);
+
+ if (!vrf || vrf->data.l.netns_name[0] == '\0')
+ return 0;
+ if (vrf->vrf_id == VRF_DEFAULT)
+ return 0;
+ return 1;
+}
+
+/* vrf CLI commands */
+DEFUN_NOSH (vrf,
+ vrf_cmd,
+ "vrf NAME",
+ "Select a VRF to configure\n"
+ "VRF's name\n")
+{
+ int idx_name = 1;
+ const char *vrfname = argv[idx_name]->arg;
+
+ return vrf_handler_create(vty, vrfname, NULL);
+}
+
DEFUN_NOSH (no_vrf,
no_vrf_cmd,
"no vrf NAME",
@@ -500,6 +683,55 @@ DEFUN_NOSH (no_vrf,
struct cmd_node vrf_node = {VRF_NODE, "%s(config-vrf)# ", 1};
+DEFUN_NOSH (vrf_netns,
+ vrf_netns_cmd,
+ "netns NAME",
+ "Attach VRF to a Namespace\n"
+ "The file name in " NS_RUN_DIR ", or a full pathname\n")
+{
+ int idx_name = 1;
+ char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg);
+
+ VTY_DECLVAR_CONTEXT(vrf, vrf);
+
+ if (!pathname)
+ return CMD_WARNING_CONFIG_FAILED;
+ return vrf_netns_handler_create(vty, vrf, pathname, NS_UNKNOWN);
+}
+
+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;
+}
+
/*
* Debug CLI for vrf's
*/
@@ -552,4 +784,105 @@ void vrf_cmd_init(int (*writefunc)(struct vty *vty))
install_element(CONFIG_NODE, &no_vrf_cmd);
install_node(&vrf_node, writefunc);
install_default(VRF_NODE);
+ 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);
+ }
+}
+
+vrf_id_t vrf_get_default_id(void)
+{
+ struct vrf *vrf = vrf_lookup_by_name(VRF_DEFAULT_NAME);
+
+ if (vrf)
+ return vrf->vrf_id;
+ if (vrf_is_backend_netns())
+ return ns_get_default_id();
+ else
+ return VRF_DEFAULT_INTERNAL;
+}
+
+int vrf_bind(vrf_id_t vrf_id, int fd, char *name)
+{
+ int ret = 0;
+
+ if (fd < 0 || name == NULL)
+ return fd;
+ if (vrf_is_mapped_on_netns(vrf_id))
+ return fd;
+#ifdef SO_BINDTODEVICE
+ ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name,
+ strlen(name));
+ if (ret < 0)
+ zlog_debug("bind to interface %s failed, errno=%d",
+ name, errno);
+#endif /* SO_BINDTODEVICE */
+ return ret;
+}
+int vrf_getaddrinfo(const char *node, const char *service,
+ const struct addrinfo *hints,
+ struct addrinfo **res, vrf_id_t vrf_id)
+{
+ int ret, ret2, save_errno;
+
+ ret = vrf_switch_to_netns(vrf_id);
+ if (ret < 0)
+ zlog_err("%s: Can't switch to VRF %u (%s)",
+ __func__, vrf_id, safe_strerror(errno));
+ ret = getaddrinfo(node, service, hints, res);
+ save_errno = errno;
+ ret2 = vrf_switchback_to_initial();
+ if (ret2 < 0)
+ zlog_err("%s: Can't switchback from VRF %u (%s)",
+ __func__, vrf_id, safe_strerror(errno));
+ errno = save_errno;
+ return ret;
+}
+
+int vrf_ioctl(vrf_id_t vrf_id, int d, unsigned long request, char *params)
+{
+ int ret, saved_errno, rc;
+
+ ret = vrf_switch_to_netns(vrf_id);
+ if (ret < 0) {
+ zlog_err("%s: Can't switch to VRF %u (%s)",
+ __func__, vrf_id, safe_strerror(errno));
+ return 0;
+ }
+ rc = ioctl(d, request, params);
+ saved_errno = errno;
+ ret = vrf_switchback_to_initial();
+ if (ret < 0)
+ zlog_err("%s: Can't switchback from VRF %u (%s)",
+ __func__, vrf_id, safe_strerror(errno));
+ errno = saved_errno;
+ return rc;
+}
+
+int vrf_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id,
+ char *interfacename)
+{
+ int ret, save_errno, ret2;
+
+ ret = vrf_switch_to_netns(vrf_id);
+ if (ret < 0)
+ zlog_err("%s: Can't switch to VRF %u (%s)",
+ __func__, vrf_id, safe_strerror(errno));
+ ret = sockunion_socket(su);
+ save_errno = errno;
+ ret2 = vrf_switchback_to_initial();
+ if (ret2 < 0)
+ zlog_err("%s: Can't switchback from VRF %u (%s)",
+ __func__, vrf_id, safe_strerror(errno));
+ errno = save_errno;
+
+ if (ret <= 0)
+ return ret;
+ ret2 = vrf_bind(vrf_id, ret, interfacename);
+ if (ret2 < 0) {
+ close(ret);
+ ret = ret2;
+ }
+ return ret;
}
diff --git a/lib/vrf.h b/lib/vrf.h
index 99c048c702..062e6f3d8d 100644
--- a/lib/vrf.h
+++ b/lib/vrf.h
@@ -26,12 +26,9 @@
#include "linklist.h"
#include "qobj.h"
#include "vty.h"
-
-/* The default NS ID */
-#define NS_DEFAULT 0
+#include "ns.h"
/* The default VRF ID */
-#define VRF_DEFAULT 0
#define VRF_UNKNOWN UINT32_MAX
/* Pending: May need to refine this. */
@@ -42,6 +39,7 @@ enum { IFLA_VRF_UNSPEC, IFLA_VRF_TABLE, __IFLA_VRF_MAX };
#endif
#define VRF_NAMSIZ 36
+#define NS_NAMSIZ 16
#define VRF_DEFAULT_NAME "Default-IP-Routing-Table"
@@ -60,6 +58,7 @@ struct vrf_data {
union {
struct {
uint32_t table_id;
+ char netns_name[NS_NAMSIZ];
} l;
};
};
@@ -88,6 +87,9 @@ struct vrf {
/* The table_id from the kernel */
struct vrf_data data;
+ /* Back pointer to namespace context */
+ void *ns_ctxt;
+
QOBJ_FIELDS
};
RB_HEAD(vrf_id_head, vrf);
@@ -96,6 +98,9 @@ RB_HEAD(vrf_name_head, vrf);
RB_PROTOTYPE(vrf_name_head, vrf, name_entry, vrf_name_compare)
DECLARE_QOBJ_TYPE(vrf)
+/* Allow VRF with netns as backend */
+#define VRF_BACKEND_VRF_LITE 0
+#define VRF_BACKEND_NETNS 1
extern struct vrf_id_head vrfs_by_id;
extern struct vrf_name_head vrfs_by_name;
@@ -195,17 +200,85 @@ extern void vrf_init(int (*create)(struct vrf *), int (*enable)(struct vrf *),
*/
extern void vrf_terminate(void);
-extern void vrf_cmd_init(int (*writefunc)(struct vty *vty));
-
/*
- * VRF utilities
+ * Utilities to create networks objects,
+ * or call network operations
*/
/* Create a socket serving for the given VRF */
-extern int vrf_socket(int, int, int, vrf_id_t);
+extern int vrf_socket(int domain, int type,
+ int protocol, vrf_id_t vrf_id,
+ char *name);
+
+extern int vrf_sockunion_socket(const union sockunion *su,
+ vrf_id_t vrf_id, char *name);
+
+extern int vrf_bind(vrf_id_t vrf_id, int fd, char *name);
+
+/* VRF ioctl operations */
+extern int vrf_getaddrinfo(const char *node, const char *service,
+ const struct addrinfo *hints,
+ struct addrinfo **res, vrf_id_t vrf_id);
+
+extern int vrf_ioctl(vrf_id_t vrf_id, int d, unsigned long request, char *args);
+
+/* function called by macro VRF_DEFAULT
+ * to get the default VRF_ID
+ */
+extern vrf_id_t vrf_get_default_id(void);
+/* The default VRF ID */
+#define VRF_DEFAULT vrf_get_default_id()
+
+/* VRF is mapped on netns or not ? */
+int vrf_is_mapped_on_netns(vrf_id_t vrf_id);
+
+/* VRF switch from NETNS */
+extern int vrf_switch_to_netns(vrf_id_t vrf_id);
+extern int vrf_switchback_to_initial(void);
/*
- * VRF Debugging
+ * VRF backend routines
+ * should be called from zebra only
+ */
+
+/* VRF vty command initialisation
+ */
+extern void vrf_cmd_init(int (*writefunc)(struct vty *vty));
+
+/* VRF vty debugging
*/
extern void vrf_install_commands(void);
+
+/*
+ * VRF utilities
+ */
+
+/* API for configuring VRF backend
+ * should be called from zebra only
+ */
+extern void vrf_configure_backend(int vrf_backend_netns);
+extern int vrf_get_backend(void);
+extern int vrf_is_backend_netns(void);
+
+
+/* API to create a VRF. either from vty
+ * or through discovery
+ */
+extern int vrf_handler_create(struct vty *vty,
+ const char *name,
+ struct vrf **vrf);
+
+/* API to associate a VRF with a NETNS.
+ * called either from vty or through discovery
+ * should be called from zebra only
+ */
+extern int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf,
+ char *pathname, ns_id_t ns_id);
+
+/* used internally to enable or disable VRF.
+ * Notify a change in the VRF ID of the VRF
+ */
+extern void vrf_disable(struct vrf *vrf);
+extern int vrf_enable(struct vrf *vrf);
+
#endif /*_ZEBRA_VRF_H*/
diff --git a/lib/zclient.c b/lib/zclient.c
index d11457a859..ad91eb504b 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -1408,8 +1408,8 @@ static void zclient_vrf_add(struct zclient *zclient, vrf_id_t vrf_id)
/* Lookup/create vrf by vrf_id. */
vrf = vrf_get(vrf_id, vrfname_tmp);
- vrf->data = data;
-
+ vrf->data.l.table_id = data.l.table_id;
+ memcpy(vrf->data.l.netns_name, data.l.netns_name, NS_NAMSIZ);
vrf_enable(vrf);
}