From e27dec3cf15d627cc26c07f40ded138b0bcc6a0a Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 13 Dec 2017 11:04:31 +0100 Subject: [PATCH] zebra: collect and get netnamespaces information upon zebra initialisation, and upon further netnamespace creation, the the netnamespaces are created and a vrf associated to the netnamespace is created. By convention, the name of the netns will be the same as the VRF. Add a stub routine that returns a fake ns identifier, in case netlink ( linux machines) is not available. Also, upon each newly discovered NETNS, a NSID id being generated, either by relying on kernel NSID feature, or by generating locally the NSID ( see previous commit for more information). Signed-off-by: Philippe Guibert --- zebra/main.c | 2 + zebra/subdir.am | 2 + zebra/zebra_netns_notify.c | 264 +++++++++++++++++++++++++++++++++++++ zebra/zebra_netns_notify.h | 29 ++++ zebra/zebra_ns.c | 3 + 5 files changed, 300 insertions(+) create mode 100644 zebra/zebra_netns_notify.c create mode 100644 zebra/zebra_netns_notify.h diff --git a/zebra/main.c b/zebra/main.c index 73e5f1290d..3353290816 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -47,6 +47,7 @@ #include "zebra/redistribute.h" #include "zebra/zebra_mpls.h" #include "zebra/label_manager.h" +#include "zebra/zebra_netns_notify.h" #define ZEBRA_PTM_SUPPORT @@ -140,6 +141,7 @@ static void sigint(void) vrf_terminate(); ns_walk_func(zebra_ns_disabled); + zebra_ns_notify_close(); access_list_reset(); prefix_list_reset(); diff --git a/zebra/subdir.am b/zebra/subdir.am index fcfb0cc203..bb7439c0f6 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -66,6 +66,7 @@ zebra_zebra_SOURCES = \ zebra/zebra_vxlan.c \ zebra/zserv.c \ zebra/zebra_netns_id.c \ + zebra/zebra_netns_notify.c \ # end zebra/zebra_vty_clippy.c: $(CLIPPY_DEPS) @@ -106,6 +107,7 @@ noinst_HEADERS += \ zebra/zebra_vxlan_private.h \ zebra/zserv.h \ zebra/zebra_netns_id.h \ + zebra/zebra_netns_notify.h \ # end zebra_zebra_irdp_la_SOURCES = \ diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c new file mode 100644 index 0000000000..8940546f68 --- /dev/null +++ b/zebra/zebra_netns_notify.c @@ -0,0 +1,264 @@ +/* + * Zebra NS collector and notifier for Network NameSpaces + * Copyright (C) 2017 6WIND + * + * This program 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 of the License, or (at your option) + * any later version. + * + * This program 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 this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#ifdef HAVE_NETLINK +#ifdef HAVE_NETNS +#undef _GNU_SOURCE +#define _GNU_SOURCE + +#include +#endif +#include +#include +#include + +#include "thread.h" +#include "ns.h" +#include "command.h" +#include "memory.h" + +#include "zserv.h" +#include "zebra_memory.h" +#endif /* defined(HAVE_NETLINK) */ + +#include "zebra_netns_notify.h" +#include "zebra_netns_id.h" + +#ifdef HAVE_NETLINK + +/* upon creation of folder under /var/run/netns, + * wait that netns context is bound to + * that folder 10 seconds + */ +#define ZEBRA_NS_POLLING_INTERVAL_MSEC 1000 +#define ZEBRA_NS_POLLING_MAX_RETRIES 200 + +DEFINE_MTYPE_STATIC(ZEBRA, NETNS_MISC, "ZebraNetNSInfo") +static struct thread *zebra_netns_notify_current; + +struct zebra_netns_info { + const char *netnspath; + unsigned int retries; +}; + +static int zebra_ns_ready_read(struct thread *t); +static void zebra_ns_notify_create_context_from_entry_name(const char *name); +static int zebra_ns_continue_read(struct zebra_netns_info *zns_info, + int stop_retry); +static int zebra_ns_notify_read(struct thread *t); + +static void zebra_ns_notify_create_context_from_entry_name(const char *name) +{ + char *netnspath = ns_netns_pathname(NULL, name); + struct vrf *vrf; + int ret; + ns_id_t ns_id; + + if (netnspath == NULL) + return; + + if (vrf_handler_create(NULL, name, &vrf) != CMD_SUCCESS) { + zlog_warn("NS notify : failed to create VRF %s", name); + return; + } + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); + ns_id = zebra_ns_id_get(netnspath); + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); + ret = ns_handler_create(NULL, vrf, netnspath, ns_id); + if (ret != CMD_SUCCESS) { + zlog_warn("NS notify : failed to create NS %s", netnspath); + return; + } + zlog_info("NS notify : created VRF %s NS %s", + name, netnspath); +} + +static int zebra_ns_continue_read(struct zebra_netns_info *zns_info, + int stop_retry) +{ + void *ns_path_ptr = (void *)zns_info->netnspath; + + if (stop_retry) { + XFREE(MTYPE_NETNS_MISC, ns_path_ptr); + XFREE(MTYPE_NETNS_MISC, zns_info); + return 0; + } + thread_add_timer_msec(zebrad.master, zebra_ns_ready_read, + (void *)zns_info, + ZEBRA_NS_POLLING_INTERVAL_MSEC, NULL); + return 0; +} + +static int zebra_ns_ready_read(struct thread *t) +{ + struct zebra_netns_info *zns_info = THREAD_ARG(t); + const char *netnspath; + int err, stop_retry = 0; + + if (!zns_info) + return 0; + if (!zns_info->netnspath) { + XFREE(MTYPE_NETNS_MISC, zns_info); + return 0; + } + netnspath = zns_info->netnspath; + if (--zns_info->retries == 0) + stop_retry = 1; + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); + err = ns_switch_to_netns(netnspath); + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); + if (err < 0) + return zebra_ns_continue_read(zns_info, stop_retry); + + /* go back to default ns */ + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); + err = ns_switchback_to_initial(); + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); + if (err < 0) + return zebra_ns_continue_read(zns_info, stop_retry); + + /* success : close fd and create zns context */ + zebra_ns_notify_create_context_from_entry_name(basename(netnspath)); + return zebra_ns_continue_read(zns_info, 1); +} + +static int zebra_ns_notify_read(struct thread *t) +{ + int fd_monitor = THREAD_FD(t); + struct inotify_event *event; + char buf[BUFSIZ]; + ssize_t len; + + zebra_netns_notify_current = thread_add_read(zebrad.master, + zebra_ns_notify_read, + NULL, fd_monitor, NULL); + len = read(fd_monitor, buf, sizeof(buf)); + if (len < 0) { + zlog_warn("NS notify read: failed to read (%s)", + safe_strerror(errno)); + return 0; + } + for (event = (struct inotify_event *)buf; + (char *)event < &buf[len]; + event = (struct inotify_event *)((char *)event + + sizeof(*event) + event->len)) { + char *netnspath; + struct zebra_netns_info *netnsinfo; + + if (!(event->mask & IN_CREATE)) + continue; + netnspath = ns_netns_pathname(NULL, event->name); + if (!netnspath) + continue; + netnspath = XSTRDUP(MTYPE_NETNS_MISC, netnspath); + netnsinfo = XCALLOC(MTYPE_NETNS_MISC, + sizeof(struct zebra_netns_info)); + netnsinfo->retries = ZEBRA_NS_POLLING_MAX_RETRIES; + netnsinfo->netnspath = netnspath; + thread_add_timer_msec(zebrad.master, zebra_ns_ready_read, + (void *)netnsinfo, 0, NULL); + } + return 0; +} + +void zebra_ns_notify_parse(void) +{ + struct dirent *dent; + DIR *srcdir = opendir(NS_RUN_DIR); + + if (srcdir == NULL) { + zlog_warn("NS parsing init: failed to parse %s", NS_RUN_DIR); + return; + } + while ((dent = readdir(srcdir)) != NULL) { + struct stat st; + + if (strcmp(dent->d_name, ".") == 0 + || strcmp(dent->d_name, "..") == 0) + continue; + if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0) { + zlog_warn("NS parsing init: failed to parse entry %s", + dent->d_name); + continue; + } + if (S_ISDIR(st.st_mode)) { + zlog_warn("NS parsing init: %s is not a NS", + dent->d_name); + continue; + } + zebra_ns_notify_create_context_from_entry_name(dent->d_name); + } + closedir(srcdir); +} + +void zebra_ns_notify_init(void) +{ + int fd_monitor; + + zebra_netns_notify_current = NULL; + fd_monitor = inotify_init(); + if (fd_monitor < 0) { + zlog_warn("NS notify init: failed to initialize inotify (%s)", + safe_strerror(errno)); + } + if (inotify_add_watch(fd_monitor, NS_RUN_DIR, IN_CREATE) < 0) { + zlog_warn("NS notify watch: failed to add watch (%s)", + safe_strerror(errno)); + } + zebra_netns_notify_current = thread_add_read(zebrad.master, + zebra_ns_notify_read, + NULL, fd_monitor, NULL); +} + +void zebra_ns_notify_close(void) +{ + if (zebra_netns_notify_current == NULL) + return; + + int fd = 0; + + if (zebra_netns_notify_current->u.fd > 0) + fd = zebra_netns_notify_current->u.fd; + thread_cancel(zebra_netns_notify_current); + /* auto-removal of inotify items */ + if (fd > 0) + close(fd); +} + +#else +void zebra_ns_notify_parse(void) +{ +} + +void zebra_ns_notify_init(void) +{ +} + +void zebra_ns_notify_close(void) +{ +} +#endif /* !HAVE_NETLINK */ diff --git a/zebra/zebra_netns_notify.h b/zebra/zebra_netns_notify.h new file mode 100644 index 0000000000..0ced749ae8 --- /dev/null +++ b/zebra/zebra_netns_notify.h @@ -0,0 +1,29 @@ +/* + * Zebra NS collector and notifier for Network NameSpaces + * Copyright (C) 2017 6WIND + * + * This program 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 of the License, or (at your option) + * any later version. + * + * This program 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 this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _NETNS_NOTIFY_H +#define _NETNS_NOTIFY_H + +extern void zebra_ns_notify_init(void); +extern void zebra_ns_notify_parse(void); +extern void zebra_ns_notify_close(void); + +extern struct zebra_privs_t zserv_privs; + +#endif /* NETNS_NOTIFY_H */ diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 02fc2b1844..50551c9b35 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -33,6 +33,7 @@ #include "rt.h" #include "zebra_vxlan.h" #include "debug.h" +#include "zebra_netns_notify.h" DEFINE_MTYPE(ZEBRA, ZEBRA_NS, "Zebra Name Space") @@ -249,6 +250,8 @@ int zebra_ns_init(void) ns_add_hook(NS_ENABLE_HOOK, zebra_ns_enabled); ns_add_hook(NS_DISABLE_HOOK, zebra_ns_disabled); ns_add_hook(NS_DELETE_HOOK, zebra_ns_delete); + zebra_ns_notify_parse(); + zebra_ns_notify_init(); } return 0; } -- 2.39.5