From 32bcb8b0a54e1468e8fe9e303ba78a71ce05cf3d Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Sat, 3 Sep 2016 20:45:35 -0400 Subject: lib: Create ns.c Create the ns.c files from the original vrf.c code to allow us to create the 'logical-router' command to work within namespaces. Signed-off-by: Donald Sharp --- lib/ns.c | 503 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 503 insertions(+) create mode 100644 lib/ns.c (limited to 'lib/ns.c') diff --git a/lib/ns.c b/lib/ns.c new file mode 100644 index 0000000000..daaffc6b09 --- /dev/null +++ b/lib/ns.c @@ -0,0 +1,503 @@ +/* + * NS functions. + * Copyright (C) 2014 6WIND S.A. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#include "if.h" +#include "ns.h" +#include "prefix.h" +#include "table.h" +#include "log.h" +#include "memory.h" + +#define NS_DEFAULT_NAME "Default-logical-router" + +struct ns +{ + /* Identifier, same as the vector index */ + ns_id_t ns_id; + /* Name */ + char *name; + + /* Master list of interfaces belonging to this NS */ + struct list *iflist; + + /* User data */ + void *info; +}; + +/* Holding NS hooks */ +struct ns_master +{ + int (*ns_new_hook) (ns_id_t, void **); + int (*ns_delete_hook) (ns_id_t, void **); + int (*ns_enable_hook) (ns_id_t, void **); + int (*ns_disable_hook) (ns_id_t, void **); +} ns_master = {0,}; + +/* NS table */ +struct route_table *ns_table = NULL; + +static int ns_is_enabled (struct ns *ns); +static int ns_enable (struct ns *ns); +static void ns_disable (struct ns *ns); + + +/* Build the table key */ +static void +ns_build_key (ns_id_t ns_id, struct prefix *p) +{ + p->family = AF_INET; + p->prefixlen = IPV4_MAX_BITLEN; + p->u.prefix4.s_addr = ns_id; +} + +/* Get a NS. If not found, create one. */ +static struct ns * +ns_get (ns_id_t ns_id) +{ + struct prefix p; + struct route_node *rn; + struct ns *ns; + + ns_build_key (ns_id, &p); + rn = route_node_get (ns_table, &p); + if (rn->info) + { + ns = (struct ns *)rn->info; + route_unlock_node (rn); /* get */ + return ns; + } + + ns = XCALLOC (MTYPE_NS, sizeof (struct ns)); + ns->ns_id = ns_id; + rn->info = ns; + + /* + * Initialize interfaces. + * + * I'm not sure if this belongs here or in + * the vrf code. + */ + // if_init (&ns->iflist); + + zlog_info ("NS %u is created.", ns_id); + + if (ns_master.ns_new_hook) + (*ns_master.ns_new_hook) (ns_id, &ns->info); + + return ns; +} + +/* Delete a NS. This is called in ns_terminate(). */ +static void +ns_delete (struct ns *ns) +{ + zlog_info ("NS %u is to be deleted.", ns->ns_id); + + if (ns_is_enabled (ns)) + ns_disable (ns); + + if (ns_master.ns_delete_hook) + (*ns_master.ns_delete_hook) (ns->ns_id, &ns->info); + + /* + * I'm not entirely sure if the vrf->iflist + * needs to be moved into here or not. + */ + //if_terminate (&ns->iflist); + + if (ns->name) + XFREE (MTYPE_NS_NAME, ns->name); + + XFREE (MTYPE_NS, ns); +} + +/* Look up a NS by identifier. */ +static struct ns * +ns_lookup (ns_id_t ns_id) +{ + struct prefix p; + struct route_node *rn; + struct ns *ns = NULL; + + ns_build_key (ns_id, &p); + rn = route_node_lookup (ns_table, &p); + if (rn) + { + ns = (struct ns *)rn->info; + route_unlock_node (rn); /* lookup */ + } + return ns; +} + +/* + * Check whether the NS is enabled - that is, whether the NS + * is ready to allocate resources. Currently there's only one + * type of resource: socket. + */ +static int +ns_is_enabled (struct ns *ns) +{ + return ns && ns->ns_id == NS_DEFAULT; +} + +/* + * Enable a NS - that is, let the NS be ready to use. + * The NS_ENABLE_HOOK callback will be called to inform + * that they can allocate resources in this NS. + * + * RETURN: 1 - enabled successfully; otherwise, 0. + */ +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_master.ns_enable_hook) + (*ns_master.ns_enable_hook) (ns->ns_id, &ns->info); + + return 1; + } + + return 0; +} + +/* + * Disable a NS - that is, let the NS be unusable. + * The NS_DELETE_HOOK callback will be called to inform + * that they must release the resources in the NS. + */ +static void +ns_disable (struct ns *ns) +{ + if (ns_is_enabled (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); + } +} + + +/* Add a NS hook. Please add hooks before calling ns_init(). */ +void +ns_add_hook (int type, int (*func)(ns_id_t, void **)) +{ + switch (type) { + case NS_NEW_HOOK: + ns_master.ns_new_hook = func; + break; + case NS_DELETE_HOOK: + ns_master.ns_delete_hook = func; + break; + case NS_ENABLE_HOOK: + ns_master.ns_enable_hook = func; + break; + case NS_DISABLE_HOOK: + ns_master.ns_disable_hook = func; + break; + default: + break; + } +} + +/* Return the iterator of the first NS. */ +ns_iter_t +ns_first (void) +{ + struct route_node *rn; + + for (rn = route_top (ns_table); rn; rn = route_next (rn)) + if (rn->info) + { + route_unlock_node (rn); /* top/next */ + return (ns_iter_t)rn; + } + return NS_ITER_INVALID; +} + +/* Return the next NS iterator to the given iterator. */ +ns_iter_t +ns_next (ns_iter_t iter) +{ + struct route_node *rn = NULL; + + /* Lock it first because route_next() will unlock it. */ + if (iter != NS_ITER_INVALID) + rn = route_next (route_lock_node ((struct route_node *)iter)); + + for (; rn; rn = route_next (rn)) + if (rn->info) + { + route_unlock_node (rn); /* next */ + return (ns_iter_t)rn; + } + return NS_ITER_INVALID; +} + +/* Return the NS iterator of the given NS ID. If it does not exist, + * the iterator of the next existing NS is returned. */ +ns_iter_t +ns_iterator (ns_id_t ns_id) +{ + struct prefix p; + struct route_node *rn; + + ns_build_key (ns_id, &p); + rn = route_node_get (ns_table, &p); + if (rn->info) + { + /* OK, the NS exists. */ + route_unlock_node (rn); /* get */ + return (ns_iter_t)rn; + } + + /* Find the next NS. */ + for (rn = route_next (rn); rn; rn = route_next (rn)) + if (rn->info) + { + route_unlock_node (rn); /* next */ + return (ns_iter_t)rn; + } + + return NS_ITER_INVALID; +} + +/* Obtain the NS ID from the given NS iterator. */ +ns_id_t +ns_iter2id (ns_iter_t iter) +{ + struct route_node *rn = (struct route_node *) iter; + return (rn && rn->info) ? ((struct ns *)rn->info)->ns_id : NS_DEFAULT; +} + +/* Obtain the data pointer from the given NS iterator. */ +void * +ns_iter2info (ns_iter_t iter) +{ + struct route_node *rn = (struct route_node *) iter; + return (rn && rn->info) ? ((struct ns *)rn->info)->info : NULL; +} + +/* Obtain the interface list from the given NS iterator. */ +struct list * +ns_iter2iflist (ns_iter_t iter) +{ + struct route_node *rn = (struct route_node *) iter; + return (rn && rn->info) ? ((struct ns *)rn->info)->iflist : NULL; +} + +/* Get the data pointer of the specified NS. If not found, create one. */ +void * +ns_info_get (ns_id_t ns_id) +{ + struct ns *ns = ns_get (ns_id); + return ns->info; +} + +/* Look up the data pointer of the specified NS. */ +void * +ns_info_lookup (ns_id_t ns_id) +{ + struct ns *ns = ns_lookup (ns_id); + return ns ? ns->info : NULL; +} + +/* Look up the interface list in a NS. */ +struct list * +ns_iflist (ns_id_t ns_id) +{ + struct ns * ns = ns_lookup (ns_id); + return ns ? ns->iflist : NULL; +} + +/* Get the interface list of the specified NS. Create one if not find. */ +struct list * +ns_iflist_get (ns_id_t ns_id) +{ + struct ns * ns = ns_get (ns_id); + return ns->iflist; +} + +/* + * NS bit-map + */ + +#define NS_BITMAP_NUM_OF_GROUPS 8 +#define NS_BITMAP_NUM_OF_BITS_IN_GROUP \ + (UINT16_MAX / NS_BITMAP_NUM_OF_GROUPS) +#define NS_BITMAP_NUM_OF_BYTES_IN_GROUP \ + (NS_BITMAP_NUM_OF_BITS_IN_GROUP / CHAR_BIT + 1) /* +1 for ensure */ + +#define NS_BITMAP_GROUP(_id) \ + ((_id) / NS_BITMAP_NUM_OF_BITS_IN_GROUP) +#define NS_BITMAP_BIT_OFFSET(_id) \ + ((_id) % NS_BITMAP_NUM_OF_BITS_IN_GROUP) + +#define NS_BITMAP_INDEX_IN_GROUP(_bit_offset) \ + ((_bit_offset) / CHAR_BIT) +#define NS_BITMAP_FLAG(_bit_offset) \ + (((u_char)1) << ((_bit_offset) % CHAR_BIT)) + +struct ns_bitmap +{ + u_char *groups[NS_BITMAP_NUM_OF_GROUPS]; +}; + +ns_bitmap_t +ns_bitmap_init (void) +{ + return (ns_bitmap_t) XCALLOC (MTYPE_NS_BITMAP, sizeof (struct ns_bitmap)); +} + +void +ns_bitmap_free (ns_bitmap_t bmap) +{ + struct ns_bitmap *bm = (struct ns_bitmap *) bmap; + int i; + + if (bmap == NS_BITMAP_NULL) + return; + + for (i = 0; i < NS_BITMAP_NUM_OF_GROUPS; i++) + if (bm->groups[i]) + XFREE (MTYPE_NS_BITMAP, bm->groups[i]); + + XFREE (MTYPE_NS_BITMAP, bm); +} + +void +ns_bitmap_set (ns_bitmap_t bmap, ns_id_t ns_id) +{ + struct ns_bitmap *bm = (struct ns_bitmap *) bmap; + u_char group = NS_BITMAP_GROUP (ns_id); + u_char offset = NS_BITMAP_BIT_OFFSET (ns_id); + + if (bmap == NS_BITMAP_NULL) + return; + + if (bm->groups[group] == NULL) + bm->groups[group] = XCALLOC (MTYPE_NS_BITMAP, + NS_BITMAP_NUM_OF_BYTES_IN_GROUP); + + SET_FLAG (bm->groups[group][NS_BITMAP_INDEX_IN_GROUP (offset)], + NS_BITMAP_FLAG (offset)); +} + +void +ns_bitmap_unset (ns_bitmap_t bmap, ns_id_t ns_id) +{ + struct ns_bitmap *bm = (struct ns_bitmap *) bmap; + u_char group = NS_BITMAP_GROUP (ns_id); + u_char offset = NS_BITMAP_BIT_OFFSET (ns_id); + + if (bmap == NS_BITMAP_NULL || bm->groups[group] == NULL) + return; + + UNSET_FLAG (bm->groups[group][NS_BITMAP_INDEX_IN_GROUP (offset)], + NS_BITMAP_FLAG (offset)); +} + +int +ns_bitmap_check (ns_bitmap_t bmap, ns_id_t ns_id) +{ + struct ns_bitmap *bm = (struct ns_bitmap *) bmap; + u_char group = NS_BITMAP_GROUP (ns_id); + u_char offset = NS_BITMAP_BIT_OFFSET (ns_id); + + if (bmap == NS_BITMAP_NULL || bm->groups[group] == NULL) + return 0; + + return CHECK_FLAG (bm->groups[group][NS_BITMAP_INDEX_IN_GROUP (offset)], + NS_BITMAP_FLAG (offset)) ? 1 : 0; +} + +/* Initialize NS module. */ +void +ns_init (void) +{ + struct ns *default_ns; + + /* Allocate NS table. */ + ns_table = route_table_init (); + + /* The default NS always exists. */ + default_ns = ns_get (NS_DEFAULT); + if (!default_ns) + { + zlog_err ("ns_init: failed to create the default NS!"); + exit (1); + } + + /* Set the default NS name. */ + default_ns->name = XSTRDUP (MTYPE_NS_NAME, NS_DEFAULT_NAME); + + /* Enable the default NS. */ + if (!ns_enable (default_ns)) + { + zlog_err ("ns_init: failed to enable the default NS!"); + exit (1); + } +} + +/* Terminate NS module. */ +void +ns_terminate (void) +{ + struct route_node *rn; + struct ns *ns; + + for (rn = route_top (ns_table); rn; rn = route_next (rn)) + if ((ns = rn->info) != NULL) + ns_delete (ns); + + route_table_finish (ns_table); + ns_table = NULL; +} + +/* Create a socket for the NS. */ +int +ns_socket (int domain, int type, int protocol, ns_id_t ns_id) +{ + int ret = -1; + + if (!ns_is_enabled (ns_lookup (ns_id))) + { + errno = ENOSYS; + return -1; + } + + if (ns_id == NS_DEFAULT) + ret = socket (domain, type, protocol); + else + errno = ENOSYS; + + return ret; +} + -- cgit v1.2.3 From 13460c44a22415dd55846aca6fc31cf8607c90e9 Mon Sep 17 00:00:00 2001 From: Feng Lu Date: Thu, 3 Jul 2014 18:24:34 +0800 Subject: 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 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 Reviewed-by: Alain Ritoux Signed-off-by: Nicolas Dichtel (cherry picked from commit 55cfa2f190620f7c711944637659bc208970324d) --- configure.ac | 8 ++ lib/command.c | 2 + lib/command.h | 1 + lib/ns.c | 245 +++++++++++++++++++++++++++++++++++++++++++++++---- lib/ns.h | 2 +- vtysh/Makefile.am | 3 +- vtysh/extract.pl.in | 3 + vtysh/vtysh.c | 41 +++++++++ vtysh/vtysh.h | 1 + vtysh/vtysh_config.c | 3 + zebra/zebra_ns.c | 3 + zebra/zebra_ns.h | 5 +- 12 files changed, 295 insertions(+), 22 deletions(-) (limited to 'lib/ns.c') diff --git a/configure.ac b/configure.ac index b1ea4821e2..9cfb5d37d3 100755 --- a/configure.ac +++ b/configure.ac @@ -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 + ]) + AC_CHECK_FUNCS(setns, AC_DEFINE(HAVE_SETNS,, Have setns))] + ) + dnl ------------------------------------ dnl Determine routing get and set method dnl ------------------------------------ diff --git a/lib/command.c b/lib/command.c index 0e921bd636..f97e37e8c6 100644 --- a/lib/command.c +++ b/lib/command.c @@ -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: diff --git a/lib/command.h b/lib/command.h index 8f20a92807..0415834b09 100644 --- a/lib/command.h +++ b/lib/command.h @@ -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. */ diff --git a/lib/ns.c b/lib/ns.c index daaffc6b09..6fb124181b 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -22,6 +22,13 @@ #include +#ifdef HAVE_NETNS +#undef _GNU_SOURCE +#define _GNU_SOURCE + +#include +#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) + + 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; } - diff --git a/lib/ns.h b/lib/ns.h index 81e24562c3..3fac739861 100644 --- 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" diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am index d4a74487ec..ed49acca47 100644 --- a/vtysh/Makefile.am +++ b/vtysh/Makefile.am @@ -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 \ diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index 4ead722213..31ab3b3ad8 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -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"; diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 5cf27facb8..2436c6182d 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -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); diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index e974cf331c..75822b1363 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -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. */ diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index e678fc1a8b..4a1b323686 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -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) diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 6b04aa741a..084a5d181f 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -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); diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index 07fcfcdac1..8a821c465a 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -23,6 +23,8 @@ #if !defined(__ZEBRA_NS_H__) #define __ZEBRA_NS_H__ +#include + #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. */ -- cgit v1.2.3 From c253dcb5d80aa60112bb31ab95ae9839081064d3 Mon Sep 17 00:00:00 2001 From: Nicolas Dichtel Date: Thu, 3 Sep 2015 10:47:43 +0200 Subject: vrf: add a runtime check before playing with netns This patch adds a runtime check to determine if netns are available. Some systems like OpenWRT have the system call setns() but don't have the kernel option CONFIG_NET_NS enabled. Reported-by: Christian Franke Signed-off-by: Nicolas Dichtel Tested-by: Christian Franke (cherry picked from commit 04a3aabf58d95d01c4c8168eeff43cf9d9892eee) --- lib/ns.c | 94 +++++++++++++++++++++++++++++++--------------------- vtysh/vtysh_config.c | 2 +- 2 files changed, 58 insertions(+), 38 deletions(-) (limited to 'lib/ns.c') diff --git a/lib/ns.c b/lib/ns.c index 6fb124181b..4765a18ef2 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -39,7 +39,6 @@ #include "command.h" #include "vty.h" -#ifdef HAVE_NETNS #ifndef CLONE_NEWNET #define CLONE_NEWNET 0x40000000 /* New network namespace (lo, device, names sockets, etc) */ @@ -57,7 +56,10 @@ static inline int setns(int fd, int nstype) } #endif /* HAVE_SETNS */ +#ifdef HAVE_NETNS + #define NS_DEFAULT_NAME "/proc/self/ns/net" +static int have_netns_enabled = -1; #else /* !HAVE_NETNS */ @@ -65,6 +67,27 @@ static inline int setns(int fd, int nstype) #endif /* HAVE_NETNS */ +static int have_netns(void) +{ +#ifdef HAVE_NETNS + if (have_netns_enabled < 0) + { + int fd = open (NS_DEFAULT_NAME, O_RDONLY); + + if (fd < 0) + have_netns_enabled = 0; + else + { + have_netns_enabled = 1; + close(fd); + } + } + return have_netns_enabled; +#else + return 0; +#endif +} + struct ns { /* Identifier, same as the vector index */ @@ -194,11 +217,10 @@ ns_lookup (ns_id_t ns_id) static int ns_is_enabled (struct ns *ns) { -#ifdef HAVE_NETNS - return ns && ns->fd >= 0; -#else - return ns && ns->fd == -2 && ns->ns_id == NS_DEFAULT; -#endif + if (have_netns()) + return ns && ns->fd >= 0; + else + return ns && ns->fd == -2 && ns->ns_id == NS_DEFAULT; } /* @@ -214,12 +236,12 @@ ns_enable (struct ns *ns) 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 (have_netns()) { + ns->fd = open (ns->name, O_RDONLY); + } else { + ns->fd = -2; /* Remember that ns_enable_hook has been called */ + errno = -ENOTSUP; + } if (!ns_is_enabled (ns)) { @@ -228,10 +250,9 @@ ns_enable (struct ns *ns) return 0; } -#ifdef HAVE_NETNS - zlog_info ("NS %u is associated with NETNS %s.", - ns->ns_id, ns->name); -#endif + if (have_netns()) + zlog_info ("NS %u is associated with NETNS %s.", + ns->ns_id, ns->name); zlog_info ("NS %u is enabled.", ns->ns_id); if (ns_master.ns_enable_hook) @@ -256,9 +277,9 @@ ns_disable (struct ns *ns) if (ns_master.ns_disable_hook) (*ns_master.ns_disable_hook) (ns->ns_id, &ns->info); -#ifdef HAVE_NETNS - close (ns->fd); -#endif + if (have_netns()) + close (ns->fd); + ns->fd = -1; } } @@ -497,7 +518,6 @@ 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 */ @@ -633,8 +653,6 @@ ns_config_write (struct vty *vty) return write; } -#endif /* HAVE_NETNS */ - /* Initialize NS module. */ void ns_init (void) @@ -662,12 +680,13 @@ ns_init (void) 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 + if (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); + } } /* Terminate NS module. */ @@ -698,17 +717,18 @@ ns_socket (int domain, int type, int protocol, ns_id_t ns_id) return -1; } -#ifdef HAVE_NETNS - ret = (ns_id != NS_DEFAULT) ? setns (ns->fd, CLONE_NEWNET) : 0; - if (ret >= 0) + if (have_netns()) { - ret = socket (domain, type, protocol); - if (ns_id != NS_DEFAULT) - setns (ns_lookup (NS_DEFAULT)->fd, CLONE_NEWNET); + 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 + else + ret = socket (domain, type, protocol); return ret; } diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index 4a1b323686..118b7ba035 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -184,7 +184,7 @@ 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) + else if (strncmp (line, "logical-router", strlen ("ns")) == 0) config = config_get (NS_NODE, line); else if (strncmp (line, "vrf", strlen ("vrf")) == 0) config = config_get (VRF_NODE, line); -- cgit v1.2.3