From: Feng Lu <lu.f...@6wind.com>

Linux supports netns from version 3.x. 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.f...@6wind.com>
Reviewed-by: Alain Ritoux <alain.rit...@6wind.com>
Signed-off-by: Nicolas Dichtel <nicolas.dich...@6wind.com>
---
 configure.ac        |   2 +
 lib/command.h       |   1 +
 lib/vrf.c           | 238 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 vtysh/Makefile.am   |   1 +
 vtysh/extract.pl.in |   3 +
 5 files changed, 241 insertions(+), 4 deletions(-)

diff --git a/configure.ac b/configure.ac
index f68d86fc4f07..5f273ae48a9d 100755
--- a/configure.ac
+++ b/configure.ac
@@ -801,6 +801,8 @@ AC_CHECK_FUNCS(setproctitle, ,
   ]
 )
 
+AC_CHECK_FUNCS(setns, AC_DEFINE(HAVE_SETNS,, Have setns))
+
 dnl ------------------------------------
 dnl Determine routing get and set method
 dnl ------------------------------------
diff --git a/lib/command.h b/lib/command.h
index a36a524a048f..bb0122fa4d5f 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -68,6 +68,7 @@ enum node_type
   AUTH_ENABLE_NODE,            /* Authentication mode for change enable. */
   ENABLE_NODE,                 /* Enable node. */
   CONFIG_NODE,                 /* Config node. Default mode of config file. */
+  VRF_NODE,                    /* VRF node. */
   SERVICE_NODE,                /* Service node. */
   DEBUG_NODE,                  /* Debug node. */
   AAA_NODE,                    /* AAA node. */
diff --git a/lib/vrf.c b/lib/vrf.c
index 683026e5c069..c3e870ef8960 100644
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -22,14 +22,48 @@
 
 #include <zebra.h>
 
+#undef  _GNU_SOURCE
+#define _GNU_SOURCE
+
+#include <sched.h>
+
 #include "if.h"
 #include "vrf.h"
 #include "prefix.h"
 #include "table.h"
 #include "log.h"
 #include "memory.h"
+#include "command.h"
+#include "vty.h"
+
+#ifdef GNU_LINUX
+
+#define VRF_USE_NETNS 1
+
+#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 */
+
+#endif /* CLONE_NEWNET */
 
+#ifdef VRF_USE_NETNS
+#define VRF_RUN_DIR         "/var/run/netns"
+#define VRF_DEFAULT_NAME    "/proc/self/ns/net"
+#else
 #define VRF_DEFAULT_NAME    "Default-IP-Routing-Table"
+#endif
 
 struct vrf
 {
@@ -37,6 +71,8 @@ struct vrf
   vrf_id_t vrf_id;
   /* Name */
   char *name;
+  /* File descriptor */
+  int fd;
 
   /* Master list of interfaces belonging to this VRF */
   struct list *iflist;
@@ -90,6 +126,7 @@ vrf_get (vrf_id_t vrf_id)
 
   vrf = XCALLOC (MTYPE_VRF, sizeof (struct vrf));
   vrf->vrf_id = vrf_id;
+  vrf->fd = -1;
   rn->info = vrf;
 
   /* Initialize interfaces. */
@@ -149,7 +186,11 @@ vrf_lookup (vrf_id_t vrf_id)
 static int
 vrf_is_enabled (struct vrf *vrf)
 {
+#ifdef VRF_USE_NETNS
+  return vrf && vrf->fd >= 0;
+#else
   return vrf && vrf->vrf_id == VRF_DEFAULT;
+#endif
 }
 
 /*
@@ -162,7 +203,30 @@ vrf_is_enabled (struct vrf *vrf)
 static int
 vrf_enable (struct vrf *vrf)
 {
-  /* Till now, only the default VRF can be enabled. */
+#ifdef VRF_USE_NETNS
+
+  if (!vrf_is_enabled (vrf))
+    {
+      vrf->fd = open (vrf->name, O_RDONLY);
+
+      if (!vrf_is_enabled (vrf))
+        {
+          zlog_err ("Can not enable VRF %u: %s!",
+                    vrf->vrf_id, safe_strerror (errno));
+          return 0;
+        }
+
+      zlog_info ("VRF %u is associated with NETNS %s.",
+                 vrf->vrf_id, vrf->name);
+
+      if (vrf_master.vrf_enable_hook)
+        (*vrf_master.vrf_enable_hook) (vrf->vrf_id, &vrf->info);
+    }
+
+  return 1;
+
+#else
+
   if (vrf->vrf_id == VRF_DEFAULT)
     {
       zlog_info ("VRF %u is enabled.", vrf->vrf_id);
@@ -174,6 +238,8 @@ vrf_enable (struct vrf *vrf)
     }
 
   return 0;
+
+#endif
 }
 
 /*
@@ -188,10 +254,13 @@ vrf_disable (struct vrf *vrf)
     {
       zlog_info ("VRF %u is to be disabled.", vrf->vrf_id);
 
-      /* Till now, nothing to be done for the default VRF. */
-
       if (vrf_master.vrf_disable_hook)
         (*vrf_master.vrf_disable_hook) (vrf->vrf_id, &vrf->info);
+
+#ifdef VRF_USE_NETNS
+      close (vrf->fd);
+      vrf->fd = -1;
+#endif
     }
 }
 
@@ -429,6 +498,145 @@ vrf_bitmap_check (vrf_bitmap_t bmap, vrf_id_t vrf_id)
                      VRF_BITMAP_FLAG (offset)) ? 1 : 0;
 }
 
+/*
+ * VRF realization with NETNS
+ */
+#ifdef VRF_USE_NETNS
+
+static char *
+vrf_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", VRF_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 (vrf_netns,
+       vrf_netns_cmd,
+       "vrf <1-65535> netns NAME",
+       "Enable a VRF\n"
+       "Specify the VRF identifier\n"
+       "Associate with a NETNS\n"
+       "The file name in " VRF_RUN_DIR ", or a full pathname\n")
+{
+  vrf_id_t vrf_id = VRF_DEFAULT;
+  struct vrf *vrf = NULL;
+  char *pathname = vrf_netns_pathname (vty, argv[1]);
+
+  if (!pathname)
+    return CMD_WARNING;
+
+  VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]);
+  vrf = vrf_get (vrf_id);
+
+  if (vrf->name && strcmp (vrf->name, pathname) != 0)
+    {
+      vty_out (vty, "VRF %u is already configured with NETNS %s%s",
+               vrf->vrf_id, vrf->name, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (!vrf->name)
+    vrf->name = XSTRDUP (MTYPE_VRF_NAME, pathname);
+
+  if (!vrf_enable (vrf))
+    {
+      vty_out (vty, "Can not associate VRF %u with NETNS %s%s",
+               vrf->vrf_id, vrf->name, VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  return CMD_SUCCESS;
+}
+
+DEFUN (no_vrf_netns,
+       no_vrf_netns_cmd,
+       "no vrf <1-65535> netns NAME",
+       NO_STR
+       "Enable a VRF\n"
+       "Specify the VRF identifier\n"
+       "Associate with a NETNS\n"
+       "The file name in " VRF_RUN_DIR ", or a full pathname\n")
+{
+  vrf_id_t vrf_id = VRF_DEFAULT;
+  struct vrf *vrf = NULL;
+  char *pathname = vrf_netns_pathname (vty, argv[1]);
+
+  if (!pathname)
+    return CMD_WARNING;
+
+  VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]);
+  vrf = vrf_lookup (vrf_id);
+
+  if (!vrf)
+    {
+      vty_out (vty, "VRF %u is not found%s", vrf_id, VTY_NEWLINE);
+      return CMD_SUCCESS;
+    }
+
+  if (vrf->name && strcmp (vrf->name, pathname) != 0)
+    {
+      vty_out (vty, "Incorrect NETNS file name%s", VTY_NEWLINE);
+      return CMD_WARNING;
+    }
+
+  if (vrf_is_enabled (vrf))
+    vrf_disable (vrf);
+
+  if (vrf->name)
+    {
+      XFREE (MTYPE_VRF_NAME, vrf->name);
+      vrf->name = NULL;
+    }
+
+  return CMD_SUCCESS;
+}
+
+/* VRF node. */
+static struct cmd_node vrf_node =
+{
+  VRF_NODE,
+  "",       /* VRF node has no interface. */
+  1
+};
+
+/* VRF configuration write function. */
+static int
+vrf_config_write (struct vty *vty)
+{
+  struct route_node *rn;
+  struct vrf *vrf;
+  int write = 0;
+
+  for (rn = route_top (vrf_table); rn; rn = route_next (rn))
+    if ((vrf = rn->info) != NULL &&
+        vrf->vrf_id != VRF_DEFAULT && vrf->name)
+      {
+        vty_out (vty, "vrf %u netns %s%s", vrf->vrf_id, vrf->name, 
VTY_NEWLINE);
+        write++;
+      }
+
+  return write;
+}
+
+#endif
+
 /* Initialize VRF module. */
 void
 vrf_init (void)
@@ -455,6 +663,13 @@ vrf_init (void)
       zlog_err ("vrf_init: failed to enable the default VRF!");
       exit (1);
     }
+
+#ifdef VRF_USE_NETNS
+  /* Install VRF commands. */
+  install_node (&vrf_node, vrf_config_write);
+  install_element (CONFIG_NODE, &vrf_netns_cmd);
+  install_element (CONFIG_NODE, &no_vrf_netns_cmd);
+#endif
 }
 
 /* Terminate VRF module. */
@@ -476,19 +691,34 @@ vrf_terminate (void)
 int
 vrf_socket (int domain, int type, int protocol, vrf_id_t vrf_id)
 {
+  struct vrf *vrf = vrf_lookup (vrf_id);
   int ret = -1;
 
-  if (!vrf_is_enabled (vrf_lookup (vrf_id)))
+  if (!vrf || !vrf_is_enabled (vrf))
     {
       errno = ENOSYS;
       return -1;
     }
 
+#ifdef VRF_USE_NETNS
+
+  ret = (vrf_id != VRF_DEFAULT) ? setns (vrf->fd, CLONE_NEWNET) : 0;
+  if (ret >= 0)
+    {
+      ret = socket (domain, type, protocol);
+      if (vrf_id != VRF_DEFAULT)
+        setns (vrf_lookup (VRF_DEFAULT)->fd, CLONE_NEWNET);
+    }
+
+#else
+
   if (vrf_id == VRF_DEFAULT)
     ret = socket (domain, type, protocol);
   else
     errno = ENOSYS;
 
+#endif
+
   return ret;
 }
 
diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am
index d1ff69b9c96a..850b505e5d62 100644
--- a/vtysh/Makefile.am
+++ b/vtysh/Makefile.am
@@ -28,6 +28,7 @@ vtysh_cmd_FILES = $(top_srcdir)/bgpd/*.c 
$(top_srcdir)/isisd/*.c \
                  $(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/vrf.c \
                  $(top_srcdir)/lib/vty.c $(top_srcdir)/zebra/debug.c \
                  $(top_srcdir)/zebra/interface.c \
                  $(top_srcdir)/zebra/irdp_interface.c \
diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in
index f057e24922e1..aa90be4a19b1 100755
--- a/vtysh/extract.pl.in
+++ b/vtysh/extract.pl.in
@@ -99,6 +99,9 @@ foreach (@ARGV) {
         elsif ($file =~ /lib\/filter\.c$/) {
             $protocol = "VTYSH_ALL";
         }
+        elsif ($file =~ /lib\/vrf\.c$/) {
+            $protocol = "VTYSH_ZEBRA";
+        }
         elsif ($file =~ /lib\/plist\.c$/) {
             if ($defun_array[1] =~ m/ipv6/) {
                 $protocol = "VTYSH_RIPNGD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ZEBRA";
-- 
2.4.2


_______________________________________________
Quagga-dev mailing list
Quagga-dev@lists.quagga.net
https://lists.quagga.net/mailman/listinfo/quagga-dev

Reply via email to