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