]> git.puffer.fish Git - matthieu/frr.git/commitdiff
lib: split logicalrouter and vrf netns feature
authorPhilippe Guibert <philippe.guibert@6wind.com>
Mon, 5 Feb 2018 15:23:42 +0000 (16:23 +0100)
committerPhilippe Guibert <philippe.guibert@6wind.com>
Tue, 27 Feb 2018 10:11:24 +0000 (11:11 +0100)
This split is introducing logicalrouter.[ch] as the file that contains
the vty commands to configure logical router feature. The split has as
consequence that the backend of logical router is linux_netns.c formerly
called ns.c. The same relationship exists between VRF and its backend
which may be linux_netns.c file.
The split is adapting ns and vrf fiels so as to :
- clarify header
- ensure that the daemon persepctive, the feature VRF or logical router
  is called instead of calling directly ns.
- this implies that VRF will call NS apis, as logical router does.

Also, like it is done for default NS and default VRF, the associated VRF
is enabled first, before NETNS is enabled, so that zvrf->zns pointer is
valid when NETNS discovery applies.

Also, other_netns.c file is a stub handler that will be used for non
linux systems. As NETNS feature is only used by Linux, some BSD systems
may want to use the same backend API to benefit from NETNS. This is what
that file has been done.

Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
lib/command.c
lib/command.h
lib/logicalrouter.c [new file with mode: 0644]
lib/logicalrouter.h [new file with mode: 0644]
lib/netns_linux.c [new file with mode: 0644]
lib/netns_other.c [new file with mode: 0644]
lib/ns.c [deleted file]
lib/ns.h
lib/subdir.am
lib/vrf.c
lib/vrf.h

index d17f2c3d48cb99bce0a48420cad0ecb4b9c4db43..10996d5dd4b80dfbd800662e5defc80ae255bf1c 100644 (file)
@@ -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:
index e1edc1ef321901001acf80a271600692b5e19017..269318989f98b3f70a7ce8f3915994bdd85023d8 100644 (file)
@@ -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/logicalrouter.c b/lib/logicalrouter.c
new file mode 100644 (file)
index 0000000..4dc99d3
--- /dev/null
@@ -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 (file)
index 0000000..5a0780c
--- /dev/null
@@ -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 (file)
index 0000000..0258624
--- /dev/null
@@ -0,0 +1,528 @@
+/*
+ * 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;
+
+#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
+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->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 (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)) {
+               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)
+{
+       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;
+
+       /* 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);
+       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 = ENOSYS;
+               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 = 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);
+                               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 (file)
index 0000000..2402dd1
--- /dev/null
@@ -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 (file)
index 17d70a1..0000000
--- a/lib/ns.c
+++ /dev/null
@@ -1,750 +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
-
-/* 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 *, const struct ns *);
-static struct ns *ns_lookup(ns_id_t);
-static struct ns *ns_lookup_name(const char *);
-
-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;
-
-#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
-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 int ns_enable(struct ns *ns);
-static void ns_disable(struct ns *ns);
-static void ns_get_created(struct ns *ns);
-
-static __inline int ns_compare(const struct ns *a, const struct ns *b)
-{
-       return (a->ns_id - b->ns_id);
-}
-
-static void ns_get_created(struct ns *ns)
-{
-       /*
-        * Initialize interfaces.
-        *
-        * I'm not sure if this belongs here or in
-        * the vrf code.
-        */
-       // if_init (&ns->iflist);
-
-       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;
-}
-
-/* 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);
-       ns_get_created(ns);
-       return ns;
-}
-
-/* Get a NS. If not found, create one. */
-static struct ns *ns_get_by_name(char *ns_name)
-{
-       struct ns *ns;
-
-       ns = ns_lookup_name(ns_name);
-       if (ns)
-               return (ns);
-
-       ns = XCALLOC(MTYPE_NS, sizeof(struct ns));
-       ns->ns_id = NS_UNKNOWN;
-       ns->name = XSTRDUP(MTYPE_NS_NAME, ns_name);
-       ns->fd = -1;
-       RB_INSERT(ns_head, &ns_tree, ns);
-
-       /* ns_id not initialised */
-       ns_get_created(ns);
-       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);
-
-       /*
-        * 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));
-}
-
-/* Look up the data pointer of the specified VRF. */
-void *
-ns_info_lookup(ns_id_t ns_id)
-{
-       struct ns *ns = ns_lookup(ns_id);
-
-       return ns ? ns->info : NULL;
-}
-
-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;
-}
-
-/* Look up a NS by name */
-static struct ns *ns_lookup_name(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;
-}
-
-/*
- * 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)
-{
-       int vrf_on = 0;
-
-       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;
-               }
-
-               /* 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;
-               }
-               vrf_on = vrf_update_vrf_id((vrf_id_t)ns->ns_id,
-                                          (struct vrf *)ns->vrf_ctxt);
-               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);
-               if (vrf_on == 1)
-                       vrf_enable((struct vrf *)ns->vrf_ctxt);
-       }
-
-       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);
-
-               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)(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 needed)\n",
-                               check_base, NS_NAMSIZ-1);
-               else
-                       zlog_warn("NS name (%s) invalid:"
-                                 " too long ( %d needed)",
-                                 check_base, NS_NAMSIZ-1);
-               return NULL;
-       }
-       return pathname;
-}
-
-DEFUN_NOSH (ns_logicalrouter,
-       ns_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 = 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;
-}
-
-static struct cmd_node logicalrouter_node = {NS_NODE, "", /* NS node has no interface. */
-                                            1};
-
-DEFUN (no_ns_logicalrouter,
-       no_ns_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_NS_NAME, ns->name);
-               ns->name = NULL;
-       }
-
-       return CMD_SUCCESS;
-}
-
-int ns_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 is already configured"
-                                       " with NETNS %s\n",
-                                       vrf->vrf_id, ns->name);
-                       else
-                               zlog_warn("VRF %u is 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;
-       } else if (!ns)
-               ns = ns_get_by_name(pathname);
-
-       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);
-       }
-       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)) {
-               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;
-}
-
-
-static int ns_logicalrouter_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;
-}
-
-DEFUN_NOSH (ns_netns,
-           ns_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 ns_handler_create(vty, vrf, pathname, NS_UNKNOWN);
-}
-
-DEFUN (no_ns_netns,
-       no_ns_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
-        */
-       RB_REMOVE(ns_head, &ns_tree, ns);
-       ns->ns_id = NS_UNKNOWN;
-       RB_INSERT(ns_head, &ns_tree, ns);
-       ns_delete(ns);
-       vrf->ns_ctxt = NULL;
-       return CMD_SUCCESS;
-}
-
-void ns_init(void)
-{
-#ifdef HAVE_NETNS
-       if (have_netns_enabled < 0) {
-               ns_default_ns_fd = open(NS_DEFAULT_NAME, O_RDONLY);
-               return;
-       }
-#endif /* HAVE_NETNS */
-       ns_default_ns_fd = -1;
-       default_ns = NULL;
-}
-
-/* Initialize NS module. */
-void ns_init_zebra(ns_id_t default_ns_id)
-{
-       int fd;
-
-       ns_init();
-       default_ns = ns_get(default_ns_id);
-       if (!default_ns) {
-               zlog_err("ns_init: failed to create the default NS!");
-               exit(1);
-       }
-       if (have_netns()) {
-               fd = open(NS_DEFAULT_NAME, O_RDONLY);
-               default_ns->fd = fd;
-       }
-       ns_current_ns_fd = -1;
-       /* Set the default NS name. */
-       default_ns->name = XSTRDUP(MTYPE_NS_NAME, NS_DEFAULT_NAME);
-       zlog_info("ns_init: default NSID is %u", default_ns->ns_id);
-
-       /* Enable the default NS. */
-       if (!ns_enable(default_ns)) {
-               zlog_err("ns_init: failed to enable the default NS!");
-               exit(1);
-       }
-
-       if (have_netns() && !vrf_is_backend_netns()) {
-               /* Install NS commands. */
-               install_node(&logicalrouter_node,
-                            ns_logicalrouter_config_write);
-               install_element(CONFIG_NODE, &ns_logicalrouter_cmd);
-               install_element(CONFIG_NODE, &no_ns_logicalrouter_cmd);
-       }
-}
-
-void ns_cmd_init(void)
-{
-       if (have_netns() && vrf_is_backend_netns()) {
-               /* Install NS commands. */
-               install_element(VRF_NODE, &ns_netns_cmd);
-               install_element(VRF_NODE, &no_ns_netns_cmd);
-       }
-}
-
-/* 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;
-       fd = open(name, O_RDONLY);
-       if (fd == -1) {
-               errno = ENOSYS;
-               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) {
-               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 = 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);
-                               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;
-}
-
index 44257ab0c0108456f18620fdaecbf14b9e79a28d..83e5e1b907895941be84abaab517e673a5bce4b3 100644 (file)
--- a/lib/ns.h
+++ b/lib/ns.h
@@ -25,7 +25,6 @@
 #include "openbsd-tree.h"
 #include "linklist.h"
 #include "vty.h"
-#include "vrf.h"
 
 typedef u_int32_t ns_id_t;
 
@@ -67,6 +66,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
  */
@@ -86,35 +90,80 @@ extern struct ns_head ns_tree;
  */
 extern void ns_add_hook(int type, int (*)(struct ns *));
 
+
 /*
  * NS initializer/destructor
  */
-extern void ns_init(void);
-extern void ns_init_zebra(ns_id_t ns_id);
+
 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);
-extern void ns_cmd_init(void);
-extern int ns_handler_create(struct vty *vty, struct vrf *vrf,
-                            char *pathname, ns_id_t ns_id);
+/* 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);
-extern void *ns_info_lookup(ns_id_t ns_id);
+
+/* 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);
 
-/* API that can be used by all daemons */
+#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);
-extern void ns_init(void);
 
+/*
+ * 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);
 
-/* The default NS ID */
-#define NS_DEFAULT ns_get_default_id()
+/* 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*/
index 44870917bd3c9060fc4e373be7ee7a3271234eef..e292d7a342d0c889f18bb8bd81bcbef225c040ae 100644 (file)
@@ -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 += \
index ade1895bd4809513085039d63b323c20ecf749d6..0ca517d051fa5231b582548ff41136a12880849d 100644 (file)
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -89,16 +89,38 @@ 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 has no NETNS backend. silently ignore */
-       if (!vrf || vrf->data.l.netns_name[0] == '\0')
-               return 0;
        /* VRF is default VRF. silently ignore */
-       if (vrf->vrf_id == VRF_DEFAULT)
+       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)
@@ -115,25 +137,6 @@ int vrf_switchback_to_initial(void)
        return ret;
 }
 
-/* return 1 if vrf can be enabled */
-int vrf_update_vrf_id(vrf_id_t vrf_id, struct vrf *vrf)
-{
-       vrf_id_t old_vrf_id;
-
-       if (!vrf)
-               return 0;
-       old_vrf_id = vrf->vrf_id;
-       if (vrf_id == vrf->vrf_id)
-               return 0;
-       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)
-               return 1;
-       return 0;
-}
-
 /* Get a VRF. If not found, create one.
  * Arg:
  *   name   - The name of the vrf.  May be NULL if unknown.
@@ -436,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__);
@@ -547,6 +552,72 @@ int vrf_handler_create(struct vty *vty, const char *vrfname, struct vrf **vrf)
        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);
@@ -604,6 +675,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
  */
@@ -656,7 +776,11 @@ 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);
-       ns_cmd_init();
+       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)
index 08c53484eeef2f30d8083b6fbb80b1c5665d9b06..326418791ddf3b9cd4f8159a3f8117e1233c1e88 100644 (file)
--- a/lib/vrf.h
+++ b/lib/vrf.h
@@ -26,6 +26,7 @@
 #include "linklist.h"
 #include "qobj.h"
 #include "vty.h"
+#include "ns.h"
 
 /* The default VRF ID */
 #define VRF_UNKNOWN UINT32_MAX
@@ -199,52 +200,79 @@ 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
  */
-extern vrf_id_t vrf_get_default_id(void);
 
 /* Create a socket serving for the given VRF */
-extern int vrf_socket(int, int, int, vrf_id_t);
-extern void vrf_configure_backend(int vrf_backend_netns);
-extern int vrf_get_backend(void);
-extern int vrf_is_backend_netns(void);
-extern int vrf_handler_create(struct vty *vty,
-                             const char *name,
-                             struct vrf **vrf);
-
-/* 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);
+extern int vrf_socket(int domain, int type,
+                     int protocol, vrf_id_t vrf_id);
+extern int vrf_sockunion_socket(const union sockunion *su,
+                               vrf_id_t vrf_id);
 
 /* 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_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id);
+
+/* 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);
 
-/* used by NS when vrf backend is NS.
- * Notify a change in the VRF ID of the VRF
+/*
+ * VRF backend routines
+ * should be called from zebra only
  */
-extern int vrf_update_vrf_id(vrf_id_t vrf_id, struct vrf *vrf);
-extern void vrf_disable(struct vrf *vrf);
-extern int vrf_enable(struct vrf *vrf);
 
-/*
- * VRF Debugging
+/* 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
+ */
 
-/* The default VRF ID */
-#define VRF_DEFAULT vrf_get_default_id()
+/* 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*/