]> git.puffer.fish Git - matthieu/frr.git/commitdiff
lib, vtysh: support multiple VRFs by using linux netns
authorFeng Lu <lu.feng@6wind.com>
Thu, 3 Jul 2014 10:24:34 +0000 (18:24 +0800)
committerDonald Sharp <sharpd@cumulusnetworks.com>
Fri, 9 Sep 2016 16:15:14 +0000 (12:15 -0400)
We realize VRFs with linux netns by default. The main job is
to associate a VRF with a netns. Currently this is done by
the configuration:

  [no] vrf N netns <netns-name>

This command is also available in vtysh and goes to only
zebra, because presently only zebra supports multiple VRF.

A file descriptor is added to "struct vrf". This is for the
associated netns file. Once the command "vrf N netns NAME"
is executed, the specified file is opened and the file
descriptor is stored in the VRF N. In this way the
association is formed.

In vrf_socket(), we first switch to the specified VRF by
using the stored file descriptor, and then can allocate
a socket which is working in the associated netns.

Signed-off-by: Feng Lu <lu.feng@6wind.com>
Reviewed-by: Alain Ritoux <alain.ritoux@6wind.com>
Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
(cherry picked from commit 55cfa2f190620f7c711944637659bc208970324d)

12 files changed:
configure.ac
lib/command.c
lib/command.h
lib/ns.c
lib/ns.h
vtysh/Makefile.am
vtysh/extract.pl.in
vtysh/vtysh.c
vtysh/vtysh.h
vtysh/vtysh_config.c
zebra/zebra_ns.c
zebra/zebra_ns.h

index b1ea4821e223705900e25de85a91605656472469..9cfb5d37d3d73ed3317c3b5c1499509c34a6be33 100755 (executable)
@@ -852,6 +852,14 @@ AC_CHECK_FUNCS([dup2 ftruncate getcwd gethostbyname getpagesize gettimeofday \
        if_nametoindex if_indextoname getifaddrs \
        uname fcntl getgrouplist])
 
+AC_CHECK_HEADER([asm-generic/unistd.h],
+                [AC_CHECK_DECL(__NR_setns,
+                               AC_DEFINE(HAVE_NETNS,, Have netns),,
+                               QUAGGA_INCLUDES [#include <asm-generic/unistd.h>
+                               ])
+                 AC_CHECK_FUNCS(setns, AC_DEFINE(HAVE_SETNS,, Have setns))]
+               )
+
 dnl ------------------------------------
 dnl Determine routing get and set method
 dnl ------------------------------------
index 0e921bd6360760d92f1755227a2862c1736ea581..f97e37e8c645636be3275a6f6a626f99f62e8a6f 100644 (file)
@@ -2902,6 +2902,7 @@ DEFUN (config_exit,
       vty_config_unlock (vty);
       break;
     case INTERFACE_NODE:
+    case NS_NODE:
     case VRF_NODE:
     case ZEBRA_NODE:
     case BGP_NODE:
@@ -2960,6 +2961,7 @@ DEFUN (config_end,
       break;
     case CONFIG_NODE:
     case INTERFACE_NODE:
+    case NS_NODE:
     case VRF_NODE:
     case ZEBRA_NODE:
     case RIP_NODE:
index 8f20a928077dadebf3811ea142860224e756be63..0415834b09db124274df66b4a4f7ad28983ee3a9 100644 (file)
@@ -74,6 +74,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. */
   VRF_NODE,                    /* VRF mode node. */
   INTERFACE_NODE,              /* Interface mode node. */
   ZEBRA_NODE,                  /* zebra connection node. */
index daaffc6b09e3c345a1b48f62f02ed143a0a71d09..6fb124181b023c0e9bd4466721a34e28cdd03e9b 100644 (file)
--- a/lib/ns.c
+++ b/lib/ns.c
 
 #include <zebra.h>
 
+#ifdef HAVE_NETNS
+#undef  _GNU_SOURCE
+#define _GNU_SOURCE
+
+#include <sched.h>
+#endif
+
 #include "if.h"
 #include "ns.h"
 #include "prefix.h"
 #include "log.h"
 #include "memory.h"
 
+#include "command.h"
+#include "vty.h"
+
+#ifdef HAVE_NETNS
+
+#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 */
+
+#define NS_DEFAULT_NAME    "/proc/self/ns/net"
+
+#else /* !HAVE_NETNS */
+
 #define NS_DEFAULT_NAME    "Default-logical-router"
 
+#endif /* HAVE_NETNS */
+
 struct ns
 {
   /* Identifier, same as the vector index */
   ns_id_t ns_id;
   /* Name */
   char *name;
+  /* File descriptor */
+  int fd;
 
   /* Master list of interfaces belonging to this NS */
   struct list *iflist;
@@ -90,6 +126,7 @@ ns_get (ns_id_t ns_id)
 
   ns = XCALLOC (MTYPE_NS, sizeof (struct ns));
   ns->ns_id = ns_id;
+  ns->fd = -1;
   rn->info = ns;
 
   /*
@@ -114,8 +151,7 @@ ns_delete (struct ns *ns)
 {
   zlog_info ("NS %u is to be deleted.", ns->ns_id);
 
-  if (ns_is_enabled (ns))
-    ns_disable (ns);
+  ns_disable (ns);
 
   if (ns_master.ns_delete_hook)
     (*ns_master.ns_delete_hook) (ns->ns_id, &ns->info);
@@ -158,7 +194,11 @@ ns_lookup (ns_id_t ns_id)
 static int
 ns_is_enabled (struct ns *ns)
 {
-  return ns && ns->ns_id == NS_DEFAULT;
+#ifdef HAVE_NETNS
+  return ns && ns->fd >= 0;
+#else
+  return ns && ns->fd == -2 && ns->ns_id == NS_DEFAULT;
+#endif
 }
 
 /*
@@ -171,18 +211,34 @@ ns_is_enabled (struct ns *ns)
 static int
 ns_enable (struct ns *ns)
 {
-  /* Till now, only the default NS can be enabled. */
-  if (ns->ns_id == NS_DEFAULT)
+
+  if (!ns_is_enabled (ns))
     {
-      zlog_info ("NS %u is enabled.", ns->ns_id);
+#ifdef HAVE_NETNS
+      ns->fd = open (ns->name, O_RDONLY);
+#else
+      ns->fd = -2; /* Remember that ns_enable_hook has been called */
+      errno = -ENOTSUP;
+#endif
+
+      if (!ns_is_enabled (ns))
+        {
+          zlog_err ("Can not enable NS %u: %s!",
+                    ns->ns_id, safe_strerror (errno));
+          return 0;
+        }
+
+#ifdef HAVE_NETNS
+      zlog_info ("NS %u is associated with NETNS %s.",
+                 ns->ns_id, ns->name);
+#endif
 
+      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;
     }
 
-  return 0;
+  return 1;
 }
 
 /*
@@ -197,10 +253,13 @@ ns_disable (struct ns *ns)
     {
       zlog_info ("NS %u is to be disabled.", ns->ns_id);
 
-      /* Till now, nothing to be done for the default NS. */
-
       if (ns_master.ns_disable_hook)
         (*ns_master.ns_disable_hook) (ns->ns_id, &ns->info);
+
+#ifdef HAVE_NETNS
+      close (ns->fd);
+#endif
+      ns->fd = -1;
     }
 }
 
@@ -438,6 +497,144 @@ ns_bitmap_check (ns_bitmap_t bmap, ns_id_t ns_id)
                      NS_BITMAP_FLAG (offset)) ? 1 : 0;
 }
 
+#ifdef HAVE_NETNS
+/*
+ * 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%s", safe_strerror (errno),
+               VTY_NEWLINE);
+      return NULL;
+    }
+  return pathname;
+}
+
+DEFUN (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")
+{
+  ns_id_t ns_id = NS_DEFAULT;
+  struct ns *ns = NULL;
+  char *pathname = ns_netns_pathname (vty, argv[1]);
+
+  if (!pathname)
+    return CMD_WARNING;
+
+  VTY_GET_INTEGER ("NS ID", ns_id, argv[0]);
+  ns = ns_get (ns_id);
+
+  if (ns->name && strcmp (ns->name, pathname) != 0)
+    {
+      vty_out (vty, "NS %u is already configured with NETNS %s%s",
+               ns->ns_id, ns->name, VTY_NEWLINE);
+      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%s",
+               ns->ns_id, ns->name, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  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")
+{
+  ns_id_t ns_id = NS_DEFAULT;
+  struct ns *ns = NULL;
+  char *pathname = ns_netns_pathname (vty, argv[1]);
+
+  if (!pathname)
+    return CMD_WARNING;
+
+  VTY_GET_INTEGER ("NS ID", ns_id, argv[0]);
+  ns = ns_lookup (ns_id);
+
+  if (!ns)
+    {
+      vty_out (vty, "NS %u is not found%s", ns_id, VTY_NEWLINE);
+      return CMD_SUCCESS;
+    }
+
+  if (ns->name && strcmp (ns->name, pathname) != 0)
+    {
+      vty_out (vty, "Incorrect NETNS file name%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  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 route_node *rn;
+  struct ns *ns;
+  int write = 0;
+
+  for (rn = route_top (ns_table); rn; rn = route_next (rn))
+    if ((ns = rn->info) != NULL &&
+        ns->ns_id != NS_DEFAULT && ns->name)
+      {
+        vty_out (vty, "logical-router %u netns %s%s", ns->ns_id, ns->name, VTY_NEWLINE);
+        write++;
+      }
+
+  return write;
+}
+
+#endif /* HAVE_NETNS */
+
 /* Initialize NS module. */
 void
 ns_init (void)
@@ -464,6 +661,13 @@ ns_init (void)
       zlog_err ("ns_init: failed to enable the default NS!");
       exit (1);
     }
+
+#ifdef 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);
+#endif
 }
 
 /* Terminate NS module. */
@@ -485,19 +689,26 @@ ns_terminate (void)
 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_lookup (ns_id)))
+  if (!ns_is_enabled (ns))
     {
       errno = ENOSYS;
       return -1;
     }
 
-  if (ns_id == NS_DEFAULT)
-    ret = socket (domain, type, protocol);
-  else
-    errno = ENOSYS;
+#ifdef 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);
+#endif
 
   return ret;
 }
-
index 81e24562c3c35e0a8fa6218cff8676c9340ed3d6..3fac739861093e1f39c1944d0252d3b79df13e21 100644 (file)
--- a/lib/ns.h
+++ b/lib/ns.h
@@ -33,7 +33,7 @@ typedef u_int16_t ns_id_t;
 /*
  * The command strings
  */
-
+#define NS_RUN_DIR         "/var/run/netns"
 #define NS_CMD_STR         "logical-router <0-65535>"
 #define NS_CMD_HELP_STR    "Specify the Logical-Router\nThe Logical-Router ID\n"
 
index d4a74487ec757640142c100720ff0ffd8c25dc57..ed49acca47cf03f79333bd8e37f3529eda51c8d4 100644 (file)
@@ -54,8 +54,9 @@ vtysh_cmd_FILES = $(vtysh_scan) \
                  $(top_srcdir)/lib/keychain.c $(top_srcdir)/lib/routemap.c \
                  $(top_srcdir)/lib/filter.c $(top_srcdir)/lib/plist.c \
                  $(top_srcdir)/lib/distribute.c $(top_srcdir)/lib/if_rmap.c \
-                 $(top_srcdir)/lib/vty.c $(top_srcdir)/zebra/debug.c \
                  $(top_srcdir)/lib/vrf.c \
+                 $(top_srcdir)/lib/vty.c $(top_srcdir)/zebra/debug.c \
+                 $(top_srcdir)/lib/ns.c \
                  $(top_srcdir)/zebra/interface.c \
                  $(top_srcdir)/zebra/irdp_interface.c \
                  $(top_srcdir)/zebra/rtadv.c $(top_srcdir)/zebra/zebra_vty.c \
index 4ead72221341e378c619cbe71d871183eb55115e..31ab3b3ad8796e9f4b28a8b859bad4188f952c9f 100755 (executable)
@@ -129,6 +129,9 @@ foreach (@ARGV) {
         elsif ($file =~ /lib\/filter\.c$/) {
             $protocol = "VTYSH_ALL";
         }
+        elsif ($file =~ /lib\/ns\.c$/) {
+            $protocol = "VTYSH_ZEBRA";
+        }
         elsif ($file =~ /lib\/plist\.c$/) {
             if ($defun_array[1] =~ m/ipv6/) {
                 $protocol = "VTYSH_RIPNGD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA";
index 5cf27facb87405f588e26b1aef158cecb84c674b..2436c6182dc1c629ade6fa614e75479a08e961e4 100644 (file)
@@ -41,6 +41,7 @@
 #include "vtysh/vtysh.h"
 #include "log.h"
 #include "bgpd/bgp_vty.h"
+#include "ns.h"
 #include "vrf.h"
 
 /* Struct VTY. */
@@ -936,6 +937,12 @@ static struct cmd_node interface_node =
   "%s(config-if)# ",
 };
 
+static struct cmd_node ns_node =
+{
+  NS_NODE,
+  "%s(config-logical-router)# ",
+};
+
 static struct cmd_node vrf_node =
 {
   VRF_NODE,
@@ -1391,6 +1398,7 @@ vtysh_exit (struct vty *vty)
       vty->node = ENABLE_NODE;
       break;
     case INTERFACE_NODE:
+    case NS_NODE:
     case VRF_NODE:
     case ZEBRA_NODE:
     case BGP_NODE:
@@ -1622,6 +1630,19 @@ DEFSH (VTYSH_ZEBRA,
        "Interface's name\n"
        VRF_CMD_HELP_STR)
 
+DEFUNSH (VTYSH_NS,
+         vtysh_ns,
+         vtysh_ns_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")
+{
+  vty->node = NS_NODE;
+  return CMD_SUCCESS;
+}
+
 DEFUNSH (VTYSH_VRF,
         vtysh_vrf,
         vtysh_vrf_cmd,
@@ -1640,6 +1661,20 @@ DEFSH (VTYSH_ZEBRA,
        "Delete a pseudo vrf's configuration\n"
        "VRF's name\n")
 
+DEFUNSH (VTYSH_NS,
+         vtysh_exit_ns,
+         vtysh_exit_ns_cmd,
+         "exit",
+         "Exit current mode and down to previous mode\n")
+{
+  return vtysh_exit (vty);
+}
+
+ALIAS (vtysh_exit_ns,
+       vtysh_quit_ns_cmd,
+       "quit",
+       "Exit current mode and down to previous mode\n")
+
 DEFUNSH (VTYSH_VRF,
         vtysh_exit_vrf,
         vtysh_exit_vrf_cmd,
@@ -2864,6 +2899,7 @@ vtysh_init_vty (void)
   install_node (&rip_node, NULL);
   install_node (&interface_node, NULL);
   install_node (&link_params_node, NULL);
+  install_node (&ns_node, NULL);
   install_node (&vrf_node, NULL);
   install_node (&rmap_node, NULL);
   install_node (&zebra_node, NULL);
@@ -2894,6 +2930,7 @@ vtysh_init_vty (void)
   vtysh_install_default (RIP_NODE);
   vtysh_install_default (INTERFACE_NODE);
   vtysh_install_default (LINK_PARAMS_NODE);
+  vtysh_install_default (NS_NODE);
   vtysh_install_default (VRF_NODE);
   vtysh_install_default (RMAP_NODE);
   vtysh_install_default (ZEBRA_NODE);
@@ -2991,6 +3028,10 @@ vtysh_init_vty (void)
   install_element (LINK_PARAMS_NODE, &vtysh_exit_interface_cmd);
   install_element (INTERFACE_NODE, &vtysh_quit_interface_cmd);
 
+  install_element (NS_NODE, &vtysh_end_all_cmd);
+  install_element (NS_NODE, &vtysh_exit_ns_cmd);
+  install_element (NS_NODE, &vtysh_quit_ns_cmd);
+
   install_element (VRF_NODE, &vtysh_end_all_cmd);
   install_element (VRF_NODE, &vtysh_exit_vrf_cmd);
   install_element (VRF_NODE, &vtysh_quit_vrf_cmd);
index e974cf331c2db5f6c9f8a3c2df218b9c3c12a6de..75822b1363666c6186cff9a293e302c14a5815f9 100644 (file)
@@ -34,6 +34,7 @@
 #define VTYSH_ALL        VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_PIMD
 #define VTYSH_RMAP       VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_PIMD
 #define VTYSH_INTERFACE          VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_ISISD|VTYSH_PIMD
+#define VTYSH_NS          VTYSH_ZEBRA
 #define VTYSH_VRF        VTYSH_ZEBRA
 
 /* vtysh local configuration file. */
index e678fc1a8b7ae1b4cf68c678f0369b1cde828645..4a1b32368607755906e9e0add465e23d1de8c0e9 100644 (file)
@@ -171,6 +171,7 @@ vtysh_config_parse_line (const char *line)
        {
          if (config->index == RMAP_NODE ||
                   config->index == INTERFACE_NODE ||
+                  config->index == NS_NODE ||
                   config->index == VRF_NODE ||
                   config->index == VTY_NODE)
            config_add_line_uniq (config->line, line);
@@ -183,6 +184,8 @@ vtysh_config_parse_line (const char *line)
     default:
       if (strncmp (line, "interface", strlen ("interface")) == 0)
        config = config_get (INTERFACE_NODE, line);
+      else if (strncmp (line, "ns", strlen ("ns")) == 0)
+       config = config_get (NS_NODE, line);
       else if (strncmp (line, "vrf", strlen ("vrf")) == 0)
        config = config_get (VRF_NODE, line);
       else if (strncmp (line, "router-id", strlen ("router-id")) == 0)
index 6b04aa741a8f4725a865a857b7bf59ab7cd7025c..084a5d181f4f034b1744faf63089459ee980ee49 100644 (file)
@@ -21,6 +21,7 @@
  */
 #include "zebra.h"
 
+#include "lib/ns.h"
 #include "lib/vrf.h"
 #include "lib/prefix.h"
 #include "lib/memory.h"
@@ -86,6 +87,8 @@ zebra_ns_init (void)
 {
   dzns = XCALLOC (MTYPE_ZEBRA_NS, sizeof (struct zebra_ns));
 
+  ns_init ();
+
   zebra_vrf_init ();
 
   zebra_ns_enable (0, (void **)&dzns);
index 07fcfcdac1a3ed8184ddc6a4dac8758997ad14c2..8a821c465a126dec015ccb8b08d84ddadeb1a3f3 100644 (file)
@@ -23,6 +23,8 @@
 #if !defined(__ZEBRA_NS_H__)
 #define __ZEBRA_NS_H__
 
+#include <lib/ns.h>
+
 #ifdef HAVE_NETLINK
 /* Socket interface to kernel */
 struct nlsock
@@ -34,9 +36,6 @@ struct nlsock
 };
 #endif
 
-/* NetNS ID type. */
-typedef u_int16_t ns_id_t;
-
 struct zebra_ns
 {
   /* net-ns name.  */