lib, vtysh: support multiple VRFs by using linux netns

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)
This commit is contained in:
Feng Lu 2014-07-03 18:24:34 +08:00 committed by Donald Sharp
parent 32bcb8b0a5
commit 13460c44a2
12 changed files with 296 additions and 23 deletions

View file

@ -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 ------------------------------------

View 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:

View 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. */

247
lib/ns.c
View file

@ -22,6 +22,13 @@
#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"
@ -29,14 +36,43 @@
#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)
{
zlog_info ("NS %u is enabled.", ns->ns_id);
if (!ns_is_enabled (ns))
{
#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;
}

View file

@ -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"

View 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 \

View file

@ -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";

View 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);

View 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. */

View 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)

View 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);

View 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. */