summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/ns.c54
-rw-r--r--lib/ns.h6
-rw-r--r--lib/vrf.c37
-rw-r--r--lib/vrf.h7
4 files changed, 103 insertions, 1 deletions
diff --git a/lib/ns.c b/lib/ns.c
index e2c042d16..9aa350992 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 fca5becd7..73482d4d5 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 5b85effab..787105235 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 c1da4e8bb..4bdc183b5 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
*/