From ce1be3692f809cfa4d533d484a75653f91c24c4e Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 20 Dec 2017 12:29:21 +0100 Subject: [PATCH] lib: provide an API to switch from one netns to an other Two apis are provided so that the switch from one netns to an other one is taken care. Also an other API to know if the VRF has a NETNS backend or a VRF Lite backend. Signed-off-by: Philippe Guibert --- lib/ns.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++- lib/ns.h | 6 ++++++ lib/vrf.c | 37 +++++++++++++++++++++++++++++++++++++ lib/vrf.h | 7 +++++++ 4 files changed, 103 insertions(+), 1 deletion(-) diff --git a/lib/ns.c b/lib/ns.c index e2c042d16c..9aa3509923 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -51,6 +51,9 @@ RB_GENERATE(ns_head, ns, entry, ns_compare) struct ns_head ns_tree = RB_INITIALIZER(&ns_tree); +static int ns_current_ns_fd; +static int ns_default_ns_fd; + #ifndef CLONE_NEWNET #define CLONE_NEWNET 0x40000000 /* New network namespace (lo, device, names sockets, etc) */ #endif @@ -613,13 +616,26 @@ DEFUN (no_ns_netns, return CMD_SUCCESS; } +void ns_init(void) +{ +#ifdef HAVE_NETNS + if (have_netns_enabled < 0) { + ns_default_ns_fd = open(NS_DEFAULT_NAME, O_RDONLY); + return; + } +#endif /* HAVE_NETNS */ + ns_default_ns_fd = -1; +} + /* Initialize NS module. */ void ns_init_zebra(void) { struct ns *default_ns; + ns_init(); /* The default NS always exists. */ default_ns = ns_get(NS_DEFAULT); + ns_current_ns_fd = -1; if (!default_ns) { zlog_err("ns_init: failed to create the default NS!"); exit(1); @@ -664,6 +680,40 @@ void ns_terminate(void) } } +int ns_switch_to_netns(const char *name) +{ + int ret; + int fd; + + if (name == NULL) + return -1; + fd = open(name, O_RDONLY); + if (fd == -1) { + errno = ENOSYS; + return -1; + } + ret = setns(fd, CLONE_NEWNET); + ns_current_ns_fd = fd; + close(fd); + return ret; +} + +/* returns 1 if switch() was not called before + * return status of setns() otherwise + */ +int ns_switchback_to_initial(void) +{ + if (ns_current_ns_fd != -1) { + int ret; + + ret = setns(ns_default_ns_fd, CLONE_NEWNET); + ns_current_ns_fd = -1; + return ret; + } + /* silently ignore if setns() is not called */ + return 1; +} + /* Create a socket for the NS. */ int ns_socket(int domain, int type, int protocol, ns_id_t ns_id) { @@ -679,8 +729,10 @@ int ns_socket(int domain, int type, int protocol, ns_id_t ns_id) ret = (ns_id != NS_DEFAULT) ? setns(ns->fd, CLONE_NEWNET) : 0; if (ret >= 0) { ret = socket(domain, type, protocol); - if (ns_id != NS_DEFAULT) + if (ns_id != NS_DEFAULT) { setns(ns_lookup(NS_DEFAULT)->fd, CLONE_NEWNET); + ns_current_ns_fd = ns_id; + } } } else ret = socket(domain, type, protocol); diff --git a/lib/ns.h b/lib/ns.h index fca5becd7a..73482d4d56 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -84,6 +84,7 @@ extern void ns_add_hook(int type, int (*)(struct ns *)); /* * NS initializer/destructor */ +extern void ns_init(void); extern void ns_init_zebra(void); extern void ns_terminate(void); @@ -101,4 +102,9 @@ extern void *ns_info_lookup(ns_id_t ns_id); extern void ns_walk_func(int (*func)(struct ns *)); extern const char *ns_get_name(struct ns *ns); +/* API that can be used by all daemons */ +extern int ns_switchback_to_initial(void); +extern int ns_switch_to_netns(const char *netns_name); +extern void ns_init(void); + #endif /*_ZEBRA_NS_H*/ diff --git a/lib/vrf.c b/lib/vrf.c index 5b85effabd..7871052352 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -86,6 +86,32 @@ static int vrf_name_compare(const struct vrf *a, const struct vrf *b) return strcmp(a->name, b->name); } +int vrf_switch_to_netns(vrf_id_t vrf_id) +{ + char *name; + struct vrf *vrf = vrf_lookup_by_id(vrf_id); + + /* VRF has no NETNS backend. silently ignore */ + if (!vrf || vrf->data.l.netns_name[0] == '\0') + return 0; + /* VRF is default VRF. silently ignore */ + if (vrf->vrf_id == VRF_DEFAULT) + return 0; + name = ns_netns_pathname(NULL, vrf->data.l.netns_name); + if (debug_vrf) + zlog_debug("VRF_SWITCH: %s(%u)", name, vrf->vrf_id); + return ns_switch_to_netns(name); +} + +int vrf_switchback_to_initial(void) +{ + int ret = ns_switchback_to_initial(); + + if (ret == 0 && debug_vrf) + zlog_debug("VRF_SWITCHBACK"); + return ret; +} + /* return 1 if vrf can be enabled */ int vrf_update_vrf_id(vrf_id_t vrf_id, struct vrf *vrf) { @@ -509,6 +535,17 @@ int vrf_handler_create(struct vty *vty, const char *vrfname, struct vrf **vrf) return CMD_SUCCESS; } +int vrf_is_mapped_on_netns(vrf_id_t vrf_id) +{ + struct vrf *vrf = vrf_lookup_by_id(vrf_id); + + if (!vrf || vrf->data.l.netns_name[0] == '\0') + return 0; + if (vrf->vrf_id == VRF_DEFAULT) + return 0; + return 1; +} + /* vrf CLI commands */ DEFUN_NOSH (vrf, vrf_cmd, diff --git a/lib/vrf.h b/lib/vrf.h index c1da4e8bbc..4bdc183b5c 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -218,6 +218,13 @@ extern int vrf_handler_create(struct vty *vty, const char *name, struct vrf **vrf); +/* VRF is mapped on netns or not ? */ +int vrf_is_mapped_on_netns(vrf_id_t vrf_id); + +/* VRF switch from NETNS */ +extern int vrf_switch_to_netns(vrf_id_t vrf_id); +extern int vrf_switchback_to_initial(void); + /* used by NS when vrf backend is NS. * Notify a change in the VRF ID of the VRF */ -- 2.39.5