Acked-by: Donald Sharp <sha...@cumulusnetworks.com>

On Thu, Jun 4, 2015 at 8:10 AM, Nicolas Dichtel <nicolas.dich...@6wind.com>
wrote:

> From: Feng Lu <lu.f...@6wind.com>
>
> 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        |   8 ++
>  lib/command.h       |   1 +
>  lib/vrf.c           | 245
> ++++++++++++++++++++++++++++++++++++++++++++++++----
>  vtysh/Makefile.am   |   1 +
>  vtysh/extract.pl.in |   3 +
>  5 files changed, 241 insertions(+), 17 deletions(-)
>
> diff --git a/configure.ac b/configure.ac
> index f68d86fc4f07..6da65f34d66d 100755
> --- a/configure.ac
> +++ b/configure.ac
> @@ -801,6 +801,14 @@ AC_CHECK_FUNCS(setproctitle, ,
>    ]
>  )
>
> +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 ------------------------------------
> 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..89653a8bb615 100644
> --- a/lib/vrf.c
> +++ b/lib/vrf.c
> @@ -22,21 +22,57 @@
>
>  #include <zebra.h>
>
> +#ifdef HAVE_NETNS
> +#undef  _GNU_SOURCE
> +#define _GNU_SOURCE
> +
> +#include <sched.h>
> +#endif
> +
>  #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 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 VRF_RUN_DIR         "/var/run/netns"
> +#define VRF_DEFAULT_NAME    "/proc/self/ns/net"
> +
> +#else /* !HAVE_NETNS */
>
>  #define VRF_DEFAULT_NAME    "Default-IP-Routing-Table"
>
> +#endif /* HAVE_NETNS */
> +
>  struct vrf
>  {
>    /* Identifier, same as the vector index */
>    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. */
> @@ -109,8 +146,7 @@ vrf_delete (struct vrf *vrf)
>  {
>    zlog_info ("VRF %u is to be deleted.", vrf->vrf_id);
>
> -  if (vrf_is_enabled (vrf))
> -    vrf_disable (vrf);
> +  vrf_disable (vrf);
>
>    if (vrf_master.vrf_delete_hook)
>      (*vrf_master.vrf_delete_hook) (vrf->vrf_id, &vrf->info);
> @@ -149,7 +185,11 @@ vrf_lookup (vrf_id_t vrf_id)
>  static int
>  vrf_is_enabled (struct vrf *vrf)
>  {
> -  return vrf && vrf->vrf_id == VRF_DEFAULT;
> +#ifdef HAVE_NETNS
> +  return vrf && vrf->fd >= 0;
> +#else
> +  return vrf && vrf->fd == -2 && vrf->vrf_id == VRF_DEFAULT;
> +#endif
>  }
>
>  /*
> @@ -162,18 +202,34 @@ vrf_is_enabled (struct vrf *vrf)
>  static int
>  vrf_enable (struct vrf *vrf)
>  {
> -  /* Till now, only the default VRF can be enabled. */
> -  if (vrf->vrf_id == VRF_DEFAULT)
> +
> +  if (!vrf_is_enabled (vrf))
>      {
> -      zlog_info ("VRF %u is enabled.", vrf->vrf_id);
> +#ifdef HAVE_NETNS
> +      vrf->fd = open (vrf->name, O_RDONLY);
> +#else
> +      vrf->fd = -2; /* Remember that vrf_enable_hook has been called */
> +      errno = -ENOTSUP;
> +#endif
> +
> +      if (!vrf_is_enabled (vrf))
> +        {
> +          zlog_err ("Can not enable VRF %u: %s!",
> +                    vrf->vrf_id, safe_strerror (errno));
> +          return 0;
> +        }
> +
> +#ifdef HAVE_NETNS
> +      zlog_info ("VRF %u is associated with NETNS %s.",
> +                 vrf->vrf_id, vrf->name);
> +#endif
>
> +      zlog_info ("VRF %u is enabled.", vrf->vrf_id);
>        if (vrf_master.vrf_enable_hook)
>          (*vrf_master.vrf_enable_hook) (vrf->vrf_id, &vrf->info);
> -
> -      return 1;
>      }
>
> -  return 0;
> +  return 1;
>  }
>
>  /*
> @@ -188,10 +244,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 HAVE_NETNS
> +      close (vrf->fd);
> +#endif
> +      vrf->fd = -1;
>      }
>  }
>
> @@ -429,6 +488,144 @@ vrf_bitmap_check (vrf_bitmap_t bmap, vrf_id_t vrf_id)
>                       VRF_BITMAP_FLAG (offset)) ? 1 : 0;
>  }
>
> +#ifdef HAVE_NETNS
> +/*
> + * VRF realization with 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;
> +    }
> +
> +  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 /* HAVE_NETNS */
> +
>  /* Initialize VRF module. */
>  void
>  vrf_init (void)
> @@ -455,6 +652,13 @@ vrf_init (void)
>        zlog_err ("vrf_init: failed to enable the default VRF!");
>        exit (1);
>      }
> +
> +#ifdef HAVE_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 +680,26 @@ 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_is_enabled (vrf))
>      {
>        errno = ENOSYS;
>        return -1;
>      }
>
> -  if (vrf_id == VRF_DEFAULT)
> -    ret = socket (domain, type, protocol);
> -  else
> -    errno = ENOSYS;
> +#ifdef HAVE_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
> +  ret = socket (domain, type, protocol);
> +#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