From 78dd30b263948ffb1d5c22592ef2d70b331bf071 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 22 Jan 2018 09:42:53 +0100 Subject: [PATCH 01/41] zebra: add a runtime flag to enable vrf with netns The netns backend is chosen by VRF if a runtime flag named vrfwnetns is selected when running zebra. In the case the NETNS backend is chosen, in some case the VRFID value is being assigned the value of the NSID. Within the perimeter of that work, this is why the vrf_lookup_by_table function is extended with a new parameter. Signed-off-by: Philippe Guibert --- lib/ns.c | 6 ++++-- lib/vrf.c | 17 +++++++++++++++++ lib/vrf.h | 6 ++++++ zebra/if_netlink.c | 12 ++++++++---- zebra/main.c | 9 ++++++++- zebra/rt_netlink.c | 32 ++++++++++++++++++++------------ zebra/zebra_vrf.c | 3 ++- 7 files changed, 65 insertions(+), 20 deletions(-) diff --git a/lib/ns.c b/lib/ns.c index 0b2a3bec78..e3a1d9d0dc 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -378,7 +378,9 @@ static int ns_config_write(struct vty *vty) struct ns *ns; int write = 0; - RB_FOREACH (ns, ns_head, &ns_tree) { + if (vrf_is_backend_netns()) + return 0; + RB_FOREACH(ns, ns_head, &ns_tree) { if (ns->ns_id == NS_DEFAULT || ns->name == NULL) continue; @@ -411,7 +413,7 @@ void ns_init(void) exit(1); } - if (have_netns()) { + if (have_netns() && !vrf_is_backend_netns()) { /* Install NS commands. */ install_node(&ns_node, ns_config_write); install_element(CONFIG_NODE, &ns_netns_cmd); diff --git a/lib/vrf.c b/lib/vrf.c index 02946df2bc..c300a87a36 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -44,6 +44,8 @@ RB_GENERATE(vrf_name_head, vrf, name_entry, vrf_name_compare); struct vrf_id_head vrfs_by_id = RB_INITIALIZER(&vrfs_by_id); struct vrf_name_head vrfs_by_name = RB_INITIALIZER(&vrfs_by_name); +static int vrf_backend; + /* * Turn on/off debug code * for vrf. @@ -446,6 +448,21 @@ int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id) return ret; } +int vrf_is_backend_netns(void) +{ + return (vrf_backend == VRF_BACKEND_NETNS); +} + +int vrf_get_backend(void) +{ + return vrf_backend; +} + +void vrf_configure_backend(int vrf_backend_netns) +{ + vrf_backend = vrf_backend_netns; +} + /* vrf CLI commands */ DEFUN_NOSH (vrf, vrf_cmd, diff --git a/lib/vrf.h b/lib/vrf.h index 99c048c702..f1dc450194 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -96,6 +96,9 @@ RB_HEAD(vrf_name_head, vrf); RB_PROTOTYPE(vrf_name_head, vrf, name_entry, vrf_name_compare) DECLARE_QOBJ_TYPE(vrf) +/* Allow VRF with netns as backend */ +#define VRF_BACKEND_VRF_LITE 0 +#define VRF_BACKEND_NETNS 1 extern struct vrf_id_head vrfs_by_id; extern struct vrf_name_head vrfs_by_name; @@ -203,6 +206,9 @@ extern void vrf_cmd_init(int (*writefunc)(struct vty *vty)); /* Create a socket serving for the given VRF */ extern int vrf_socket(int, int, int, vrf_id_t); +extern void vrf_configure_backend(int vrf_backend_netns); +extern int vrf_get_backend(void); +extern int vrf_is_backend_netns(void); /* * VRF Debugging diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 14905b738b..ef30c7830f 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -615,13 +615,14 @@ static int netlink_interface(struct sockaddr_nl *snl, struct nlmsghdr *h, } /* If VRF, create the VRF structure itself. */ - if (zif_type == ZEBRA_IF_VRF) { + if (zif_type == ZEBRA_IF_VRF && !vrf_is_backend_netns()) { netlink_vrf_change(h, tb[IFLA_LINKINFO], name); vrf_id = (vrf_id_t)ifi->ifi_index; } if (tb[IFLA_MASTER]) { - if (slave_kind && (strcmp(slave_kind, "vrf") == 0)) { + if (slave_kind && (strcmp(slave_kind, "vrf") == 0) + && !vrf_is_backend_netns()) { zif_slave_type = ZEBRA_IF_SLAVE_VRF; vrf_id = *(u_int32_t *)RTA_DATA(tb[IFLA_MASTER]); } else if (slave_kind && (strcmp(slave_kind, "bridge") == 0)) { @@ -631,6 +632,8 @@ static int netlink_interface(struct sockaddr_nl *snl, struct nlmsghdr *h, } else zif_slave_type = ZEBRA_IF_SLAVE_OTHER; } + if (vrf_is_backend_netns()) + vrf_id = (vrf_id_t)ns_id; /* If linking to another interface, note it. */ if (tb[IFLA_LINK]) @@ -1074,7 +1077,7 @@ int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h, } /* If VRF, create or update the VRF structure itself. */ - if (zif_type == ZEBRA_IF_VRF) { + if (zif_type == ZEBRA_IF_VRF && !vrf_is_backend_netns()) { netlink_vrf_change(h, tb[IFLA_LINKINFO], name); vrf_id = (vrf_id_t)ifi->ifi_index; } @@ -1091,7 +1094,8 @@ int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h, if (h->nlmsg_type == RTM_NEWLINK) { if (tb[IFLA_MASTER]) { - if (slave_kind && (strcmp(slave_kind, "vrf") == 0)) { + if (slave_kind && (strcmp(slave_kind, "vrf") == 0) + && !vrf_is_backend_netns()) { zif_slave_type = ZEBRA_IF_SLAVE_VRF; vrf_id = *(u_int32_t *)RTA_DATA(tb[IFLA_MASTER]); diff --git a/zebra/main.c b/zebra/main.c index 19b16936d9..a881fcb9c6 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -85,6 +85,7 @@ struct option longopts[] = {{"batch", no_argument, NULL, 'b'}, {"label_socket", no_argument, NULL, 'l'}, {"retain", no_argument, NULL, 'r'}, #ifdef HAVE_NETLINK + {"vrfwnetns", no_argument, NULL, 'n'}, {"nl-bufsize", required_argument, NULL, 's'}, #endif /* HAVE_NETLINK */ {0}}; @@ -205,12 +206,14 @@ int main(int argc, char **argv) char *fuzzing = NULL; #endif + vrf_configure_backend(VRF_BACKEND_VRF_LITE); + frr_preinit(&zebra_di, argc, argv); frr_opt_add( "bakz:e:l:r" #ifdef HAVE_NETLINK - "s:" + "s:n" #endif #if defined(HANDLE_ZAPI_FUZZING) "c:" @@ -225,6 +228,7 @@ int main(int argc, char **argv) " -k, --keep_kernel Don't delete old routes which installed by zebra.\n" " -r, --retain When program terminates, retain added route by zebra.\n" #ifdef HAVE_NETLINK + " -n, --vrfwnetns Set VRF with NetNS\n" " -s, --nl-bufsize Set netlink receive buffer size\n" #endif /* HAVE_NETLINK */ #if defined(HANDLE_ZAPI_FUZZING) @@ -279,6 +283,9 @@ int main(int argc, char **argv) case 's': nl_rcvbufsize = atoi(optarg); break; + case 'n': + vrf_configure_backend(VRF_BACKEND_NETNS); + break; #endif /* HAVE_NETLINK */ #if defined(HANDLE_ZAPI_FUZZING) case 'c': diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index a80ab9d834..20abd76973 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -194,16 +194,25 @@ static inline int proto2zebra(int proto, int family) /* Pending: create an efficient table_id (in a tree/hash) based lookup) */ -static vrf_id_t vrf_lookup_by_table(u_int32_t table_id) +static vrf_id_t vrf_lookup_by_table(u_int32_t table_id, ns_id_t ns_id) { struct vrf *vrf; struct zebra_vrf *zvrf; RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { - if ((zvrf = vrf->info) == NULL || (zvrf->table_id != table_id)) + zvrf = vrf->info; + if (zvrf == NULL) continue; - - return zvrf_id(zvrf); + /* case vrf with netns : match the netnsid */ + if (vrf_is_backend_netns()) { + if (ns_id == zvrf_id(zvrf)) + return zvrf_id(zvrf); + } else { + /* VRF is VRF_BACKEND_VRF_LITE */ + if (zvrf->table_id != table_id) + continue; + return zvrf_id(zvrf); + } } return VRF_DEFAULT; @@ -220,7 +229,7 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl, u_char flags = 0; struct prefix p; struct prefix_ipv6 src_p = {}; - vrf_id_t vrf_id = VRF_DEFAULT; + vrf_id_t vrf_id; char anyaddr[16] = {0}; @@ -288,7 +297,7 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl, table = rtm->rtm_table; /* Map to VRF */ - vrf_id = vrf_lookup_by_table(table); + vrf_id = vrf_lookup_by_table(table, ns_id); if (vrf_id == VRF_DEFAULT) { if (!is_zebra_valid_kernel_table(table) && !is_zebra_main_routing_table(table)) @@ -609,7 +618,7 @@ static int netlink_route_change_read_multicast(struct sockaddr_nl *snl, char sbuf[40]; char gbuf[40]; char oif_list[256] = "\0"; - vrf_id_t vrf = ns_id; + vrf_id_t vrf; int table; if (mroute) @@ -631,7 +640,7 @@ static int netlink_route_change_read_multicast(struct sockaddr_nl *snl, else table = rtm->rtm_table; - vrf = vrf_lookup_by_table(table); + vrf = vrf_lookup_by_table(table, ns_id); if (tb[RTA_IIF]) iif = *(int *)RTA_DATA(tb[RTA_IIF]); @@ -687,24 +696,23 @@ int netlink_route_change(struct sockaddr_nl *snl, struct nlmsghdr *h, ns_id_t ns_id, int startup) { int len; - vrf_id_t vrf_id = ns_id; struct rtmsg *rtm; rtm = NLMSG_DATA(h); if (!(h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE)) { /* If this is not route add/delete message print warning. */ - zlog_warn("Kernel message: %d vrf %u\n", h->nlmsg_type, vrf_id); + zlog_warn("Kernel message: %d NS %u\n", h->nlmsg_type, ns_id); return 0; } /* Connected route. */ if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("%s %s %s proto %s vrf %u", + zlog_debug("%s %s %s proto %s NS %u", nl_msg_type_to_str(h->nlmsg_type), nl_family_to_str(rtm->rtm_family), nl_rttype_to_str(rtm->rtm_type), - nl_rtproto_to_str(rtm->rtm_protocol), vrf_id); + nl_rtproto_to_str(rtm->rtm_protocol), ns_id); /* We don't care about change notifications for the MPLS table. */ /* TODO: Revisit this. */ diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index cd47f21278..6eec2c18c4 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -25,8 +25,9 @@ #include "command.h" #include "memory.h" #include "srcdest_table.h" - +#include "vrf.h" #include "vty.h" + #include "zebra/debug.h" #include "zebra/zserv.h" #include "zebra/rib.h" From b95c18833a36bcf03b7a72c110be24873a65420d Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 6 Dec 2017 12:03:59 +0100 Subject: [PATCH 02/41] zebra: copy logical-router-command under vrf subnode a vty command is added: in addition to this command ( kept for future usage): - [no] logical-router-id netns a new command is being placed under vrf subnode - vrf [no] netns exit This command permits to map a VRF with a Netnamespace. The commit only handles the relationship between vrf and ns structures. It adds 2 attributes to vrf structure: - one defines the kind of vrf ( mapped under netns or vrf from kernel) - the other is the opaque pointer to ns The show running-config is handled by zebra daemon. Signed-off-by: Philippe Guibert --- lib/ns.c | 184 ++++++++++++++++++++++++++++++++++++++-------- lib/ns.h | 4 + lib/vrf.c | 2 + lib/vrf.h | 3 + zebra/zebra_ns.c | 8 ++ zebra/zebra_ns.h | 1 + zebra/zebra_vrf.c | 1 + 7 files changed, 173 insertions(+), 30 deletions(-) diff --git a/lib/ns.c b/lib/ns.c index e3a1d9d0dc..25136d0a1e 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -35,12 +35,14 @@ #include "command.h" #include "vty.h" +#include "vrf.h" -DEFINE_MTYPE_STATIC(LIB, NS, "Logical-Router") -DEFINE_MTYPE_STATIC(LIB, NS_NAME, "Logical-Router Name") +DEFINE_MTYPE_STATIC(LIB, NS, "NetNS Context") +DEFINE_MTYPE_STATIC(LIB, NS_NAME, "NetNS Name") static __inline int ns_compare(const struct ns *, const struct ns *); static struct ns *ns_lookup(ns_id_t); +static struct ns *ns_lookup_name(const char *); RB_GENERATE(ns_head, ns, entry, ns_compare) @@ -105,12 +107,31 @@ struct ns_master { static int ns_is_enabled(struct ns *ns); static int ns_enable(struct ns *ns); static void ns_disable(struct ns *ns); +static void ns_get_created(struct ns *ns); static __inline int ns_compare(const struct ns *a, const struct ns *b) { return (a->ns_id - b->ns_id); } +static void ns_get_created(struct ns *ns) +{ + /* + * Initialize interfaces. + * + * I'm not sure if this belongs here or in + * the vrf code. + */ + // if_init (&ns->iflist); + + if (ns->ns_id != NS_UNKNOWN) + zlog_info("NS %u is created.", ns->ns_id); + else + zlog_info("NS %s is created.", ns->name); + if (ns_master.ns_new_hook) + (*ns_master.ns_new_hook)(ns->ns_id, &ns->info); +} + /* Get a NS. If not found, create one. */ static struct ns *ns_get(ns_id_t ns_id) { @@ -124,20 +145,27 @@ static struct ns *ns_get(ns_id_t ns_id) ns->ns_id = ns_id; ns->fd = -1; RB_INSERT(ns_head, &ns_tree, ns); + ns_get_created(ns); + return ns; +} - /* - * Initialize interfaces. - * - * I'm not sure if this belongs here or in - * the vrf code. - */ - // if_init (&ns->iflist); +/* Get a NS. If not found, create one. */ +static struct ns *ns_get_by_name(char *ns_name) +{ + struct ns *ns; - zlog_info("NS %u is created.", ns_id); + ns = ns_lookup_name(ns_name); + if (ns) + return (ns); - if (ns_master.ns_new_hook) - (*ns_master.ns_new_hook)(ns_id, &ns->info); + ns = XCALLOC(MTYPE_NS, sizeof(struct ns)); + ns->ns_id = NS_UNKNOWN; + ns->name = XSTRDUP(MTYPE_NS_NAME, ns_name); + ns->fd = -1; + RB_INSERT(ns_head, &ns_tree, ns); + /* ns_id not initialised */ + ns_get_created(ns); return ns; } @@ -172,6 +200,20 @@ static struct ns *ns_lookup(ns_id_t ns_id) return (RB_FIND(ns_head, &ns_tree, &ns)); } +/* Look up a NS by name */ +static struct ns *ns_lookup_name(const char *name) +{ + struct ns *ns = NULL; + + RB_FOREACH(ns, ns_head, &ns_tree) { + if (ns->name != NULL) { + if (strcmp(name, ns->name) == 0) + return ns; + } + } + return NULL; +} + /* * Check whether the NS is enabled - that is, whether the NS * is ready to allocate resources. Currently there's only one @@ -289,8 +331,8 @@ static char *ns_netns_pathname(struct vty *vty, const char *name) return pathname; } -DEFUN_NOSH (ns_netns, - ns_netns_cmd, +DEFUN_NOSH (ns_logicalrouter, + ns_logicalrouter_cmd, "logical-router (1-65535) ns NAME", "Enable a logical-router\n" "Specify the logical-router indentifier\n" @@ -299,7 +341,7 @@ DEFUN_NOSH (ns_netns, { int idx_number = 1; int idx_name = 3; - ns_id_t ns_id = NS_DEFAULT; + ns_id_t ns_id; struct ns *ns = NULL; char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); @@ -327,8 +369,11 @@ DEFUN_NOSH (ns_netns, return CMD_SUCCESS; } -DEFUN (no_ns_netns, - no_ns_netns_cmd, +static struct cmd_node logicalrouter_node = {NS_NODE, "", /* NS node has no interface. */ + 1}; + +DEFUN (no_ns_logicalrouter, + no_ns_logicalrouter_cmd, "no logical-router (1-65535) ns NAME", NO_STR "Enable a Logical-Router\n" @@ -338,7 +383,7 @@ DEFUN (no_ns_netns, { int idx_number = 2; int idx_name = 4; - ns_id_t ns_id = NS_DEFAULT; + ns_id_t ns_id; struct ns *ns = NULL; char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); @@ -368,30 +413,99 @@ DEFUN (no_ns_netns, return CMD_SUCCESS; } -/* NS node. */ -static struct cmd_node ns_node = {NS_NODE, "", /* NS node has no interface. */ - 1}; +DEFUN_NOSH (ns_netns, + ns_netns_cmd, + "netns NAME", + "Attach VRF to a Namespace\n" + "The file name in " NS_RUN_DIR ", or a full pathname\n") +{ + int idx_name = 1; + struct ns *ns = NULL; + char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); -/* NS configuration write function. */ -static int ns_config_write(struct vty *vty) + VTY_DECLVAR_CONTEXT(vrf, vrf); + + if (!pathname) + return CMD_WARNING_CONFIG_FAILED; + + if (!vrf) + return CMD_WARNING_CONFIG_FAILED; + if (vrf->vrf_id != VRF_UNKNOWN && vrf->ns_ctxt == NULL) { + vty_out(vty, "VRF %u is already configured with VRF %s\n", + vrf->vrf_id, vrf->name); + return CMD_WARNING_CONFIG_FAILED; + } + if (vrf->ns_ctxt != NULL) { + ns = (struct ns *) vrf->ns_ctxt; + if (ns && 0 != strcmp(ns->name, pathname)) { + vty_out(vty, "VRF %u is already configured" + " with NETNS %s\n", + vrf->vrf_id, ns->name); + return CMD_WARNING_CONFIG_FAILED; + } + } + ns = ns_lookup_name(pathname); + if (ns && ns->vrf_ctxt) { + struct vrf *vrf2 = (struct vrf *)ns->vrf_ctxt; + + vty_out(vty, "NS %s is already configured" + " with VRF %u(%s)\n", + ns->name, vrf2->vrf_id, vrf2->name); + return CMD_WARNING_CONFIG_FAILED; + } else if (!ns) + ns = ns_get_by_name(pathname); + + if (!ns_enable(ns)) { + vty_out(vty, "Can not associate NS %u with NETNS %s\n", + ns->ns_id, ns->name); + return CMD_WARNING_CONFIG_FAILED; + } + + vrf->ns_ctxt = (void *)ns; + ns->vrf_ctxt = (void *)vrf; + return CMD_SUCCESS; +} + +static int ns_logicalrouter_config_write(struct vty *vty) { struct ns *ns; int write = 0; - if (vrf_is_backend_netns()) - return 0; RB_FOREACH(ns, ns_head, &ns_tree) { if (ns->ns_id == NS_DEFAULT || ns->name == NULL) continue; - vty_out(vty, "logical-router %u netns %s\n", ns->ns_id, ns->name); write = 1; } - return write; } +DEFUN (no_ns_netns, + no_ns_netns_cmd, + "no netns [NAME]", + NO_STR + "Detach VRF from a Namespace\n" + "The file name in " NS_RUN_DIR ", or a full pathname\n") +{ + struct ns *ns = NULL; + + VTY_DECLVAR_CONTEXT(vrf, vrf); + + if (!vrf->ns_ctxt) { + vty_out(vty, "VRF %s(%u) is not configured with NetNS\n", + vrf->name, vrf->vrf_id); + return CMD_WARNING_CONFIG_FAILED; + } + + ns = (struct ns *)vrf->ns_ctxt; + + ns->vrf_ctxt = NULL; + ns_delete(ns); + vrf->ns_ctxt = NULL; + return CMD_SUCCESS; +} + /* Initialize NS module. */ void ns_init(void) { @@ -415,9 +529,19 @@ void ns_init(void) if (have_netns() && !vrf_is_backend_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); + install_node(&logicalrouter_node, + ns_logicalrouter_config_write); + install_element(CONFIG_NODE, &ns_logicalrouter_cmd); + install_element(CONFIG_NODE, &no_ns_logicalrouter_cmd); + } +} + +void ns_cmd_init(void) +{ + if (have_netns()) { + /* Install NS commands. */ + install_element(VRF_NODE, &ns_netns_cmd); + install_element(VRF_NODE, &no_ns_netns_cmd); } } diff --git a/lib/ns.h b/lib/ns.h index 79b4cab04d..fab3e19368 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -49,6 +49,9 @@ struct ns { /* Master list of interfaces belonging to this NS */ struct list *iflist; + /* Back Pointer to VRF */ + void *vrf_ctxt; + /* User data */ void *info; }; @@ -89,5 +92,6 @@ extern void ns_terminate(void); /* Create a socket serving for the given NS */ extern int ns_socket(int, int, int, ns_id_t); +extern void ns_cmd_init(void); #endif /*_ZEBRA_NS_H*/ diff --git a/lib/vrf.c b/lib/vrf.c index c300a87a36..56c8bdbabe 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -29,6 +29,7 @@ #include "log.h" #include "memory.h" #include "command.h" +#include "ns.h" DEFINE_MTYPE_STATIC(LIB, VRF, "VRF") DEFINE_MTYPE_STATIC(LIB, VRF_BITMAP, "VRF bit-map") @@ -574,4 +575,5 @@ void vrf_cmd_init(int (*writefunc)(struct vty *vty)) install_element(CONFIG_NODE, &no_vrf_cmd); install_node(&vrf_node, writefunc); install_default(VRF_NODE); + ns_cmd_init(); } diff --git a/lib/vrf.h b/lib/vrf.h index f1dc450194..40e6ab6cd6 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -88,6 +88,9 @@ struct vrf { /* The table_id from the kernel */ struct vrf_data data; + /* Back pointer to namespace context */ + void *ns_ctxt; + QOBJ_FIELDS }; RB_HEAD(vrf_id_head, vrf); diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 1715881f7e..80847518a7 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -1,6 +1,7 @@ /* zebra NS Routines * Copyright (C) 2016 Cumulus Networks, Inc. * Donald Sharp + * Copyright (C) 2017/2018 6WIND * * This file is part of Quagga. * @@ -177,3 +178,10 @@ int zebra_ns_init(void) return 0; } + +int zebra_ns_config_write(struct vty *vty, struct ns *ns) +{ + if (ns && ns->name != NULL) + vty_out(vty, " netns %s\n", ns->name); + return 0; +} diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index 765f2c6893..99e4984164 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -82,4 +82,5 @@ extern struct route_table *zebra_ns_find_table(struct zebra_ns *zns, extern struct route_table *zebra_ns_get_table(struct zebra_ns *zns, struct zebra_vrf *zvrf, uint32_t tableid, afi_t afi); +int zebra_ns_config_write(struct vty *vty, struct ns *ns); #endif diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 6eec2c18c4..a3596a4263 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -563,6 +563,7 @@ static int vrf_config_write(struct vty *vty) zvrf->l3vni, is_l3vni_for_prefix_routes_only(zvrf->l3vni) ? " prefix-routes-only" :""); + zebra_ns_config_write(vty, (struct ns *)vrf->ns_ctxt); vty_out(vty, "!\n"); } From c17d483845c4b772270cade18f2ce5a5e14bc14e Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 10 Jan 2018 10:04:59 +0100 Subject: [PATCH 03/41] lib: netns vty command not available when vrf backend is vrf lite Using the vrf backend kind, the vty command that configured netns under vty will not be installed if the vrf backend is vrf lite Signed-off-by: Philippe Guibert --- lib/ns.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/ns.c b/lib/ns.c index 25136d0a1e..fcac28cf7e 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -492,6 +492,10 @@ DEFUN (no_ns_netns, VTY_DECLVAR_CONTEXT(vrf, vrf); + if (!vrf_is_backend_netns()) { + vty_out(vty, "VRF backend is not Netns. Aborting\n"); + return CMD_WARNING_CONFIG_FAILED; + } if (!vrf->ns_ctxt) { vty_out(vty, "VRF %s(%u) is not configured with NetNS\n", vrf->name, vrf->vrf_id); @@ -538,7 +542,7 @@ void ns_init(void) void ns_cmd_init(void) { - if (have_netns()) { + if (have_netns() && vrf_is_backend_netns()) { /* Install NS commands. */ install_element(VRF_NODE, &ns_netns_cmd); install_element(VRF_NODE, &no_ns_netns_cmd); From fe533c564e1901ee6b767708345abb52a56056af Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 8 Dec 2017 19:06:34 +0100 Subject: [PATCH 04/41] zebra: socket operations stick to namespace if necessary Upon following calls: interface poll, address poll, route poll, and ICMPv6 handling, each new Namespace is being parsed. For that, the socket operations need to switch from one NS to one other, to get the necessary information. As of now, there is a crash when dumping interfaces, through show running-config. Signed-off-by: Philippe Guibert --- lib/ns.c | 4 ++-- lib/vrf.c | 3 ++- zebra/if_netlink.c | 10 ++++++++-- zebra/interface.c | 7 ++++++- zebra/kernel_netlink.c | 2 +- zebra/kernel_socket.c | 3 ++- zebra/rtadv.c | 7 ++++--- 7 files changed, 25 insertions(+), 11 deletions(-) diff --git a/lib/ns.c b/lib/ns.c index fcac28cf7e..170290a9e9 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -565,9 +565,9 @@ void 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; + int ret; - if (!ns_is_enabled(ns)) { + if (!ns || !ns_is_enabled(ns)) { errno = ENOSYS; return -1; } diff --git a/lib/vrf.c b/lib/vrf.c index 56c8bdbabe..81ff6d56fd 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -97,7 +97,8 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name) int new = 0; if (debug_vrf) - zlog_debug("VRF_GET: %s(%u)", name, vrf_id); + zlog_debug("VRF_GET: %s(%u)", + name == NULL ? "(NULL)" : name, vrf_id); /* Nothing to see, move along here */ if (!name && vrf_id == VRF_UNKNOWN) diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index ef30c7830f..6897bd4ee2 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -792,8 +792,12 @@ static int netlink_address(int cmd, int family, struct interface *ifp, char buf[NL_PKT_BUF_SIZE]; } req; - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_ns *zns; + if (vrf_is_backend_netns()) + zns = zebra_ns_lookup((ns_id_t)ifp->vrf_id); + else + zns = zebra_ns_lookup(NS_DEFAULT); p = ifc->address; memset(&req, 0, sizeof req - NL_PKT_BUF_SIZE); @@ -1020,6 +1024,7 @@ int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h, zns = zebra_ns_lookup(ns_id); ifi = NLMSG_DATA(h); + /* assume if not default zns, then new VRF */ if (!(h->nlmsg_type == RTM_NEWLINK || h->nlmsg_type == RTM_DELLINK)) { /* If this is not link add/delete message so print warning. */ zlog_warn("netlink_link_change: wrong kernel message %d", @@ -1107,7 +1112,8 @@ int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h, } else zif_slave_type = ZEBRA_IF_SLAVE_OTHER; } - + if (vrf_is_backend_netns()) + vrf_id = (vrf_id_t)ns_id; if (ifp == NULL || !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { /* Add interface notification from kernel */ diff --git a/zebra/interface.c b/zebra/interface.c index 07570e64bf..6ee1db6a4c 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -512,8 +512,13 @@ static void if_addr_wakeup(struct interface *ifp) void if_add_update(struct interface *ifp) { struct zebra_if *if_data; + struct zebra_ns *zns; - if_link_per_ns(zebra_ns_lookup(NS_DEFAULT), ifp); + if (vrf_is_backend_netns()) + zns = zebra_ns_lookup((ns_id_t)ifp->vrf_id); + else + zns = zebra_ns_lookup(NS_DEFAULT); + if_link_per_ns(zns, ifp); if_data = ifp->info; assert(if_data); diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 1be2cbcaf5..0b3b6eed45 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -188,7 +188,7 @@ static int netlink_socket(struct nlsock *nl, unsigned long groups, return -1; } - sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + sock = ns_socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE, ns_id); if (sock < 0) { zlog_err("Can't open %s socket: %s", nl->name, safe_strerror(errno)); diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 4d888d8069..3b28a9b242 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -1384,7 +1384,8 @@ static void routing_socket(struct zebra_ns *zns) if (zserv_privs.change(ZPRIVS_RAISE)) zlog_err("routing_socket: Can't raise privileges"); - routing_sock = socket(AF_ROUTE, SOCK_RAW, 0); + routing_sock = ns_socket(AF_ROUTE, SOCK_RAW, + 0, (ns_id_t)zns->ns->ns_id); if (routing_sock < 0) { if (zserv_privs.change(ZPRIVS_LOWER)) diff --git a/zebra/rtadv.c b/zebra/rtadv.c index 32418eb82f..860e8710d6 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -34,6 +34,7 @@ #include "command.h" #include "privs.h" #include "vrf.h" +#include "ns.h" #include "zebra/interface.h" #include "zebra/rtadv.h" @@ -621,7 +622,7 @@ static int rtadv_read(struct thread *thread) return 0; } -static int rtadv_make_socket(void) +static int rtadv_make_socket(ns_id_t ns_id) { int sock; int ret = 0; @@ -631,7 +632,7 @@ static int rtadv_make_socket(void) zlog_err("rtadv_make_socket: could not raise privs, %s", safe_strerror(errno)); - sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); + sock = ns_socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6, ns_id); if (zserv_privs.change(ZPRIVS_LOWER)) zlog_err("rtadv_make_socket: could not lower privs, %s", @@ -1686,7 +1687,7 @@ static void rtadv_event(struct zebra_ns *zns, enum rtadv_event event, int val) void rtadv_init(struct zebra_ns *zns) { - zns->rtadv.sock = rtadv_make_socket(); + zns->rtadv.sock = rtadv_make_socket(zns->ns_id); } void rtadv_terminate(struct zebra_ns *zns) From 9a76375f39f556188d25920fb53802c091bc4064 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 11 Dec 2017 15:19:15 +0100 Subject: [PATCH 05/41] zebra: route configuration fix for vrf when applied to namespaces For each route to be added or deleted, instead of applying directly to default namespaces, when a vrf is mapped to a namespace, then the correct zns must be found out. Signed-off-by: Philippe Guibert --- zebra/rt_netlink.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 20abd76973..84c7b8e9cf 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1334,9 +1334,13 @@ static int netlink_route_multipath(int cmd, struct prefix *p, char buf[NL_PKT_BUF_SIZE]; } req; - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_ns *zns; struct zebra_vrf *zvrf = vrf_info_lookup(re->vrf_id); + if (!vrf_is_backend_netns()) + zns = zebra_ns_lookup(NS_DEFAULT); + else + zns = (struct zebra_ns *)ns_info_lookup(re->vrf_id); memset(&req, 0, sizeof req - NL_PKT_BUF_SIZE); bytelen = (family == AF_INET ? 4 : 16); From 5895d33f40fdf22f1b422154dc598bbba69e1796 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 19 Dec 2017 12:23:32 +0100 Subject: [PATCH 06/41] zebra: ipv6 operations stick to namespace All ipv6 operations stick to namespace. Signed-off-by: Philippe Guibert --- zebra/interface.c | 9 +++++++-- zebra/rt.h | 3 ++- zebra/rt_netlink.c | 48 ++++++++++++++++++++++++++++++---------------- zebra/rt_socket.c | 2 +- 4 files changed, 42 insertions(+), 20 deletions(-) diff --git a/zebra/interface.c b/zebra/interface.c index 6ee1db6a4c..74ffdee31f 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -805,21 +805,26 @@ void if_nbr_ipv6ll_to_ipv4ll_neigh_update(struct interface *ifp, char buf[16] = "169.254.0.1"; struct in_addr ipv4_ll; char mac[6]; + ns_id_t ns_id; inet_pton(AF_INET, buf, &ipv4_ll); ipv6_ll_address_to_mac(address, (u_char *)mac); + if (!vrf_is_backend_netns()) + ns_id = NS_DEFAULT; + else + ns_id = (ns_id_t)(ifp->vrf_id); /* * Remove existed arp record for the interface as netlink * protocol does not have update message types * * supported message types are RTM_NEWNEIGH and RTM_DELNEIGH */ - kernel_neigh_update (0, ifp->ifindex, ipv4_ll.s_addr, mac, 6); + kernel_neigh_update(0, ifp->ifindex, ipv4_ll.s_addr, mac, 6, ns_id); /* Add arp record */ - kernel_neigh_update (add, ifp->ifindex, ipv4_ll.s_addr, mac, 6); + kernel_neigh_update(add, ifp->ifindex, ipv4_ll.s_addr, mac, 6, ns_id); zvrf->neigh_updates++; } diff --git a/zebra/rt.h b/zebra/rt.h index 54d45b889a..472f2d7a97 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -78,7 +78,8 @@ extern int kernel_address_add_ipv4(struct interface *, struct connected *); extern int kernel_address_delete_ipv4(struct interface *, struct connected *); extern int kernel_address_add_ipv6 (struct interface *, struct connected *); extern int kernel_address_delete_ipv6 (struct interface *, struct connected *); -extern int kernel_neigh_update(int, int, uint32_t, char *, int); +extern int kernel_neigh_update(int cmd, int ifindex, uint32_t addr, + char *lla, int llalen, ns_id_t ns_id); extern int kernel_interface_set_master(struct interface *master, struct interface *slave); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 84c7b8e9cf..0371c6b99b 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1282,7 +1282,7 @@ static void _netlink_mpls_debug(int cmd, u_int32_t label, const char *routedesc) } static int netlink_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla, - int llalen) + int llalen, ns_id_t ns_id) { struct { struct nlmsghdr n; @@ -1290,7 +1290,7 @@ static int netlink_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla, char buf[256]; } req; - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_ns *zns = zebra_ns_lookup(ns_id); memset(&req.n, 0, sizeof(req.n)); memset(&req.ndm, 0, sizeof(req.ndm)); @@ -1638,8 +1638,12 @@ int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in) } req; mroute = mr; - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_ns *zns; + if (!vrf_is_backend_netns()) + zns = zebra_ns_lookup(NS_DEFAULT); + else + zns = (struct zebra_ns *)ns_info_lookup(zvrf->vrf->vrf_id); memset(&req.n, 0, sizeof(req.n)); memset(&req.ndm, 0, sizeof(req.ndm)); @@ -1712,10 +1716,10 @@ void kernel_route_rib(struct route_node *rn, struct prefix *p, } int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, - int llalen) + int llalen, ns_id_t ns_id) { return netlink_neigh_update(add ? RTM_NEWNEIGH : RTM_DELNEIGH, ifindex, - addr, lla, llalen); + addr, lla, llalen, ns_id); } /* @@ -1725,7 +1729,7 @@ int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, static int netlink_vxlan_flood_list_update(struct interface *ifp, struct in_addr *vtep_ip, int cmd) { - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_ns *zns; struct { struct nlmsghdr n; struct ndmsg ndm; @@ -1733,6 +1737,10 @@ static int netlink_vxlan_flood_list_update(struct interface *ifp, } req; u_char dst_mac[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; + if (!vrf_is_backend_netns()) + zns = zebra_ns_lookup(NS_DEFAULT); + else + zns = (struct zebra_ns *)ns_info_lookup(ifp->vrf_id); memset(&req.n, 0, sizeof(req.n)); memset(&req.ndm, 0, sizeof(req.ndm)); @@ -1788,7 +1796,7 @@ int kernel_del_vtep(vni_t vni, struct interface *ifp, struct in_addr *vtep_ip) #endif static int netlink_macfdb_change(struct sockaddr_nl *snl, struct nlmsghdr *h, - int len) + int len, ns_id_t ns_id) { struct ndmsg *ndm; struct interface *ifp; @@ -1811,7 +1819,7 @@ static int netlink_macfdb_change(struct sockaddr_nl *snl, struct nlmsghdr *h, return 0; /* The interface should exist. */ - ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), ndm->ndm_ifindex); if (!ifp || !ifp->info) return 0; @@ -1942,7 +1950,7 @@ static int netlink_macfdb_table(struct sockaddr_nl *snl, struct nlmsghdr *h, if (ndm->ndm_family != AF_BRIDGE) return 0; - return netlink_macfdb_change(snl, h, len); + return netlink_macfdb_change(snl, h, len, ns_id); } /* Request for MAC FDB information from the kernel */ @@ -2024,7 +2032,7 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid, struct ethaddr *mac, struct in_addr vtep_ip, int local, int cmd, u_char sticky) { - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_ns *zns; struct { struct nlmsghdr n; struct ndmsg ndm; @@ -2039,6 +2047,10 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid, char vid_buf[20]; char dst_buf[30]; + if (!vrf_is_backend_netns()) + zns = zebra_ns_lookup(NS_DEFAULT); + else + zns = (struct zebra_ns *)ns_info_lookup(ifp->vrf_id); zif = ifp->info; if ((br_if = zif->brslave_info.br_if) == NULL) { zlog_warn("MAC %s on IF %s(%u) - no mapping to bridge", @@ -2098,7 +2110,7 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid, | NUD_DELAY) static int netlink_ipneigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h, - int len) + int len, ns_id_t ns_id) { struct ndmsg *ndm; struct interface *ifp; @@ -2119,7 +2131,7 @@ static int netlink_ipneigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h, return 0; /* The interface should exist. */ - ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), ndm->ndm_ifindex); if (!ifp || !ifp->info) return 0; @@ -2141,7 +2153,7 @@ static int netlink_ipneigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h, * itself */ if (IS_ZEBRA_IF_VLAN(ifp)) { - link_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + link_if = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), zif->link_ifindex); if (!link_if) return 0; @@ -2319,13 +2331,13 @@ int netlink_neigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h, /* Is this a notification for the MAC FDB or IP neighbor table? */ ndm = NLMSG_DATA(h); if (ndm->ndm_family == AF_BRIDGE) - return netlink_macfdb_change(snl, h, len); + return netlink_macfdb_change(snl, h, len, ns_id); if (ndm->ndm_type != RTN_UNICAST) return 0; if (ndm->ndm_family == AF_INET || ndm->ndm_family == AF_INET6) - return netlink_ipneigh_change(snl, h, len); + return netlink_ipneigh_change(snl, h, len, ns_id); return 0; } @@ -2340,10 +2352,14 @@ static int netlink_neigh_update2(struct interface *ifp, struct ipaddr *ip, } req; int ipa_len; - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_ns *zns; char buf[INET6_ADDRSTRLEN]; char buf2[ETHER_ADDR_STRLEN]; + if (!vrf_is_backend_netns()) + zns = zebra_ns_lookup(NS_DEFAULT); + else + zns = (struct zebra_ns *)ns_info_lookup(ifp->vrf_id); memset(&req.n, 0, sizeof(req.n)); memset(&req.ndm, 0, sizeof(req.ndm)); diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 6d4af1203c..b2baee5728 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -424,7 +424,7 @@ void kernel_route_rib(struct route_node *rn, struct prefix *p, } int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, - int llalen) + int llalen, ns_id_t ns_id) { /* TODO */ return 0; From 2c7d402164cc027a62be8ed2be2ffb25b17da1bd Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 22 Jan 2018 11:30:05 +0100 Subject: [PATCH 07/41] zebra: fix static analysis issue with zvrf_id Using c-lang scan-build tool, fix a dereference of a null pointer. Signed-off-by: Philippe Guibert --- zebra/zebra_vrf.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index 4d53eee093..3b9e930969 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -128,6 +128,8 @@ struct zebra_vrf { static inline vrf_id_t zvrf_id(struct zebra_vrf *zvrf) { + if (!zvrf || !zvrf->vrf) + return VRF_UNKNOWN; return zvrf->vrf->vrf_id; } From fbb65ff50428eefba0653e8f3f6f33afa003c4cd Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 22 Dec 2017 16:21:09 +0100 Subject: [PATCH 08/41] zebra: zns context is filled in when vrf is enabled This commit is also a fix that avoids a VRF to be attached to the wrong namespace context, at creation time. Because the VRF, at creation time does not know yet the namespace where it will get its information. Signed-off-by: Philippe Guibert --- zebra/zebra_vrf.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index a3596a4263..276687ca83 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -91,12 +91,9 @@ static int zebra_vrf_new(struct vrf *vrf) zlog_info("VRF %s created, id %u", vrf->name, vrf->vrf_id); zvrf = zebra_vrf_alloc(); - zvrf->zns = zebra_ns_lookup( - NS_DEFAULT); /* Point to the global (single) NS */ - router_id_init(zvrf); vrf->info = zvrf; zvrf->vrf = vrf; - + router_id_init(zvrf); return 0; } @@ -117,6 +114,10 @@ static int zebra_vrf_enable(struct vrf *vrf) zlog_debug("VRF %s id %u is now active", zvrf_name(zvrf), zvrf_id(zvrf)); + if (vrf_is_backend_netns()) + zvrf->zns = zebra_ns_lookup((ns_id_t)vrf->vrf_id); + else + zvrf->zns = zebra_ns_lookup(NS_DEFAULT); /* Inform clients that the VRF is now active. This is an * add for the clients. */ From 697d3ec73157fde8a008738907fef94fdcd569bb Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 13 Dec 2017 11:04:31 +0100 Subject: [PATCH 09/41] lib: externalise vrf and ns creation In addition to have the possibility to create from vty vrf based on a netns backend, the API will be made accessible from external, especially for zebra that will handle the netns discovery part. This commit is externalising following functions: - netns_pathname - ns_handler_create - vrf_handler_create Also, the VRF initialisation case when under NETNS backend is changed, since the NS identifier may not be known at the configuration time,but may be known later, under discovery process. Signed-off-by: Philippe Guibert --- lib/ns.c | 105 ++++++++++++++++++++++++++++++++++++++++-------------- lib/ns.h | 5 +++ lib/vrf.c | 63 +++++++++++++++++++++++--------- lib/vrf.h | 10 ++++++ 4 files changed, 141 insertions(+), 42 deletions(-) diff --git a/lib/ns.c b/lib/ns.c index 170290a9e9..694a6dab9d 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -236,6 +236,7 @@ static int ns_is_enabled(struct ns *ns) */ static int ns_enable(struct ns *ns) { + int vrf_on = 0; if (!ns_is_enabled(ns)) { if (have_netns()) { @@ -252,13 +253,26 @@ static int ns_enable(struct ns *ns) return 0; } + /* Non default NS. leave */ + if (ns->ns_id == NS_UNKNOWN) { + zlog_err("Can not enable NS %s %u: Invalid NSID", + ns->name, ns->ns_id); + return 0; + } + vrf_on = vrf_update_vrf_id((vrf_id_t)ns->ns_id, + (struct vrf *)ns->vrf_ctxt); 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); + /* zebra first receives NS enable event, + * then VRF enable event + */ if (ns_master.ns_enable_hook) (*ns_master.ns_enable_hook)(ns->ns_id, &ns->info); + if (vrf_on == 1) + vrf_enable((struct vrf *)ns->vrf_ctxt); } return 1; @@ -310,7 +324,7 @@ void ns_add_hook(int type, int (*func)(ns_id_t, void **)) * NS realization with NETNS */ -static char *ns_netns_pathname(struct vty *vty, const char *name) +char *ns_netns_pathname(struct vty *vty, const char *name) { static char pathname[PATH_MAX]; char *result; @@ -325,7 +339,9 @@ static char *ns_netns_pathname(struct vty *vty, const char *name) } if (!result) { - vty_out(vty, "Invalid pathname: %s\n", safe_strerror(errno)); + if (vty) + vty_out(vty, "Invalid pathname: %s\n", + safe_strerror(errno)); return NULL; } return pathname; @@ -413,34 +429,34 @@ DEFUN (no_ns_logicalrouter, return CMD_SUCCESS; } -DEFUN_NOSH (ns_netns, - ns_netns_cmd, - "netns NAME", - "Attach VRF to a Namespace\n" - "The file name in " NS_RUN_DIR ", or a full pathname\n") +int ns_handler_create(struct vty *vty, struct vrf *vrf, + char *pathname, ns_id_t ns_id) { - int idx_name = 1; struct ns *ns = NULL; - char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); - - VTY_DECLVAR_CONTEXT(vrf, vrf); - - if (!pathname) - return CMD_WARNING_CONFIG_FAILED; if (!vrf) return CMD_WARNING_CONFIG_FAILED; if (vrf->vrf_id != VRF_UNKNOWN && vrf->ns_ctxt == NULL) { - vty_out(vty, "VRF %u is already configured with VRF %s\n", - vrf->vrf_id, vrf->name); + if (vty) + vty_out(vty, + "VRF %u is already configured with VRF %s\n", + vrf->vrf_id, vrf->name); + else + zlog_warn("VRF %u is already configured with VRF %s\n", + vrf->vrf_id, vrf->name); return CMD_WARNING_CONFIG_FAILED; } if (vrf->ns_ctxt != NULL) { ns = (struct ns *) vrf->ns_ctxt; if (ns && 0 != strcmp(ns->name, pathname)) { - vty_out(vty, "VRF %u is already configured" - " with NETNS %s\n", - vrf->vrf_id, ns->name); + if (vty) + vty_out(vty, + "VRF %u is already configured" + " with NETNS %s\n", + vrf->vrf_id, ns->name); + else + zlog_warn("VRF %u is already configured with NETNS %s", + vrf->vrf_id, ns->name); return CMD_WARNING_CONFIG_FAILED; } } @@ -448,24 +464,38 @@ DEFUN_NOSH (ns_netns, if (ns && ns->vrf_ctxt) { struct vrf *vrf2 = (struct vrf *)ns->vrf_ctxt; - vty_out(vty, "NS %s is already configured" - " with VRF %u(%s)\n", - ns->name, vrf2->vrf_id, vrf2->name); + if (vty) + vty_out(vty, "NS %s is already configured" + " with VRF %u(%s)\n", + ns->name, vrf2->vrf_id, vrf2->name); + else + zlog_warn("NS %s is already configured with VRF %u(%s)", + ns->name, vrf2->vrf_id, vrf2->name); return CMD_WARNING_CONFIG_FAILED; } else if (!ns) ns = ns_get_by_name(pathname); + if (ns_id != ns->ns_id) { + RB_REMOVE(ns_head, &ns_tree, ns); + ns->ns_id = ns_id; + RB_INSERT(ns_head, &ns_tree, ns); + } + ns->vrf_ctxt = (void *)vrf; + vrf->ns_ctxt = (void *)ns; if (!ns_enable(ns)) { - vty_out(vty, "Can not associate NS %u with NETNS %s\n", - ns->ns_id, ns->name); + if (vty) + vty_out(vty, "Can not associate NS %u with NETNS %s\n", + ns->ns_id, ns->name); + else + zlog_warn("Can not associate NS %u with NETNS %s", + ns->ns_id, ns->name); return CMD_WARNING_CONFIG_FAILED; } - vrf->ns_ctxt = (void *)ns; - ns->vrf_ctxt = (void *)vrf; return CMD_SUCCESS; } + static int ns_logicalrouter_config_write(struct vty *vty) { struct ns *ns; @@ -481,6 +511,22 @@ static int ns_logicalrouter_config_write(struct vty *vty) return write; } +DEFUN_NOSH (ns_netns, + ns_netns_cmd, + "netns NAME", + "Attach VRF to a Namespace\n" + "The file name in " NS_RUN_DIR ", or a full pathname\n") +{ + int idx_name = 1; + char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); + + VTY_DECLVAR_CONTEXT(vrf, vrf); + + if (!pathname) + return CMD_WARNING_CONFIG_FAILED; + return ns_handler_create(vty, vrf, pathname, NS_UNKNOWN); +} + DEFUN (no_ns_netns, no_ns_netns_cmd, "no netns [NAME]", @@ -505,6 +551,13 @@ DEFUN (no_ns_netns, ns = (struct ns *)vrf->ns_ctxt; ns->vrf_ctxt = NULL; + vrf_disable(vrf); + /* vrf ID from VRF is necessary for Zebra + * so that propagate to other clients is done + */ + RB_REMOVE(ns_head, &ns_tree, ns); + ns->ns_id = NS_UNKNOWN; + RB_INSERT(ns_head, &ns_tree, ns); ns_delete(ns); vrf->ns_ctxt = NULL; return CMD_SUCCESS; diff --git a/lib/ns.h b/lib/ns.h index fab3e19368..fda062e65f 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -24,6 +24,8 @@ #include "openbsd-tree.h" #include "linklist.h" +#include "vty.h" +#include "vrf.h" typedef u_int32_t ns_id_t; @@ -93,5 +95,8 @@ extern void ns_terminate(void); /* Create a socket serving for the given NS */ extern int ns_socket(int, int, int, ns_id_t); extern void ns_cmd_init(void); +extern int ns_handler_create(struct vty *vty, struct vrf *vrf, + char *pathname, ns_id_t ns_id); +extern char *ns_netns_pathname(struct vty *vty, const char *name); #endif /*_ZEBRA_NS_H*/ diff --git a/lib/vrf.c b/lib/vrf.c index 81ff6d56fd..e855f3f83b 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -64,7 +64,6 @@ struct vrf_master { }; static int vrf_is_enabled(struct vrf *vrf); -static void vrf_disable(struct vrf *vrf); /* VRF list existance check by name. */ struct vrf *vrf_lookup_by_name(const char *name) @@ -84,6 +83,25 @@ static int vrf_name_compare(const struct vrf *a, const struct vrf *b) return strcmp(a->name, b->name); } +/* return 1 if vrf can be enabled */ +int vrf_update_vrf_id(vrf_id_t vrf_id, struct vrf *vrf) +{ + vrf_id_t old_vrf_id; + + if (!vrf) + return 0; + old_vrf_id = vrf->vrf_id; + if (vrf_id == vrf->vrf_id) + return 0; + if (vrf->vrf_id != VRF_UNKNOWN) + RB_REMOVE(vrf_id_head, &vrfs_by_id, vrf); + vrf->vrf_id = vrf_id; + RB_INSERT(vrf_id_head, &vrfs_by_id, vrf); + if (old_vrf_id == VRF_UNKNOWN) + return 1; + return 0; +} + /* Get a VRF. If not found, create one. * Arg: * name - The name of the vrf. May be NULL if unknown. @@ -223,7 +241,7 @@ int vrf_enable(struct vrf *vrf) * The VRF_DELETE_HOOK callback will be called to inform * that they must release the resources in the VRF. */ -static void vrf_disable(struct vrf *vrf) +void vrf_disable(struct vrf *vrf) { if (!vrf_is_enabled(vrf)) return; @@ -465,6 +483,32 @@ void vrf_configure_backend(int vrf_backend_netns) vrf_backend = vrf_backend_netns; } +int vrf_handler_create(struct vty *vty, const char *vrfname, struct vrf **vrf) +{ + struct vrf *vrfp; + + if (strlen(vrfname) > VRF_NAMSIZ) { + if (vty) + vty_out(vty, + "%% VRF name %s invalid: length exceeds %d bytes\n", + vrfname, VRF_NAMSIZ); + else + zlog_warn( + "%% VRF name %s invalid: length exceeds %d bytes\n", + vrfname, VRF_NAMSIZ); + return CMD_WARNING_CONFIG_FAILED; + } + + vrfp = vrf_get(VRF_UNKNOWN, vrfname); + + if (vty) + VTY_PUSH_CONTEXT(VRF_NODE, vrfp); + + if (vrf) + *vrf = vrfp; + return CMD_SUCCESS; +} + /* vrf CLI commands */ DEFUN_NOSH (vrf, vrf_cmd, @@ -474,21 +518,8 @@ DEFUN_NOSH (vrf, { int idx_name = 1; const char *vrfname = argv[idx_name]->arg; - struct vrf *vrfp; - if (strlen(vrfname) > VRF_NAMSIZ) { - vty_out(vty, - "%% VRF name %s is invalid: length exceeds " - "%d characters\n", - vrfname, VRF_NAMSIZ); - return CMD_WARNING_CONFIG_FAILED; - } - - vrfp = vrf_get(VRF_UNKNOWN, vrfname); - - VTY_PUSH_CONTEXT(VRF_NODE, vrfp); - - return CMD_SUCCESS; + return vrf_handler_create(vty, vrfname, NULL); } DEFUN_NOSH (no_vrf, diff --git a/lib/vrf.h b/lib/vrf.h index 40e6ab6cd6..16d75c3efb 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -212,6 +212,16 @@ extern int vrf_socket(int, int, int, vrf_id_t); extern void vrf_configure_backend(int vrf_backend_netns); extern int vrf_get_backend(void); extern int vrf_is_backend_netns(void); +extern int vrf_handler_create(struct vty *vty, + const char *name, + struct vrf **vrf); + +/* used by NS when vrf backend is NS. + * Notify a change in the VRF ID of the VRF + */ +extern int vrf_update_vrf_id(vrf_id_t vrf_id, struct vrf *vrf); +extern void vrf_disable(struct vrf *vrf); +extern int vrf_enable(struct vrf *vrf); /* * VRF Debugging From 3347430b12ecccc4f03fb29111e9621a6e123b3c Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 7 Dec 2017 18:27:31 +0100 Subject: [PATCH 10/41] zebra: add the registration mechanism for netns If vrf backend is netns, then the zebra will create its own zebra_ns context for each new netns discovered. As consequence, a routing table, and other contexts will be created for each new namespace discovered. When it is enabled, a populate process will be done, consisting in learning new interfaces and routes, and addresses from other NETNS. Signed-off-by: Philippe Guibert --- lib/ns.c | 21 ++++++++-------- lib/ns.h | 5 ++-- zebra/zebra_ns.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++- zebra/zebra_ns.h | 3 +++ 4 files changed, 78 insertions(+), 14 deletions(-) diff --git a/lib/ns.c b/lib/ns.c index 694a6dab9d..1929104eeb 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -96,10 +96,10 @@ static int have_netns(void) /* 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 **); + int (*ns_new_hook)(struct ns *ns); + int (*ns_delete_hook)(struct ns *ns); + int (*ns_enable_hook)(struct ns *ns); + int (*ns_disable_hook)(struct ns *ns); } ns_master = { 0, }; @@ -129,7 +129,8 @@ static void ns_get_created(struct ns *ns) else zlog_info("NS %s is created.", ns->name); if (ns_master.ns_new_hook) - (*ns_master.ns_new_hook)(ns->ns_id, &ns->info); + (*ns_master.ns_new_hook) (ns); + return; } /* Get a NS. If not found, create one. */ @@ -177,7 +178,7 @@ static void ns_delete(struct ns *ns) ns_disable(ns); if (ns_master.ns_delete_hook) - (*ns_master.ns_delete_hook)(ns->ns_id, &ns->info); + (*ns_master.ns_delete_hook)(ns); /* * I'm not entirely sure if the vrf->iflist @@ -270,7 +271,7 @@ static int ns_enable(struct ns *ns) * then VRF enable event */ if (ns_master.ns_enable_hook) - (*ns_master.ns_enable_hook)(ns->ns_id, &ns->info); + (*ns_master.ns_enable_hook)(ns); if (vrf_on == 1) vrf_enable((struct vrf *)ns->vrf_ctxt); } @@ -289,7 +290,7 @@ static void ns_disable(struct ns *ns) zlog_info("NS %u is to be disabled.", ns->ns_id); if (ns_master.ns_disable_hook) - (*ns_master.ns_disable_hook)(ns->ns_id, &ns->info); + (*ns_master.ns_disable_hook)(ns); if (have_netns()) close(ns->fd); @@ -300,7 +301,7 @@ static void ns_disable(struct ns *ns) /* Add a NS hook. Please add hooks before calling ns_init(). */ -void ns_add_hook(int type, int (*func)(ns_id_t, void **)) +void ns_add_hook(int type, int (*func)(struct ns *)) { switch (type) { case NS_NEW_HOOK: @@ -564,7 +565,7 @@ DEFUN (no_ns_netns, } /* Initialize NS module. */ -void ns_init(void) +void ns_init_zebra(void) { struct ns *default_ns; diff --git a/lib/ns.h b/lib/ns.h index fda062e65f..590e1f2c20 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -79,13 +79,12 @@ extern struct ns_head ns_tree; * - param 2: the address of the user data pointer (the user data * can be stored in or freed from there) */ -extern void ns_add_hook(int, int (*)(ns_id_t, void **)); +extern void ns_add_hook(int type, int (*)(struct ns *)); /* * NS initializer/destructor */ -/* Please add hooks before calling ns_init(). */ -extern void ns_init(void); +extern void ns_init_zebra(void); extern void ns_terminate(void); /* diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 80847518a7..6ce64b3a33 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -32,6 +32,7 @@ #include "zebra_memory.h" #include "rt.h" #include "zebra_vxlan.h" +#include "debug.h" DEFINE_MTYPE(ZEBRA, ZEBRA_NS, "Zebra Name Space") @@ -59,6 +60,58 @@ struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id) return dzns; } +static struct zebra_ns *zebra_ns_alloc(void) +{ + return XCALLOC(MTYPE_ZEBRA_NS, sizeof(struct zebra_ns)); +} + +static int zebra_ns_new(struct ns *ns) +{ + struct zebra_ns *zns; + + if (IS_ZEBRA_DEBUG_EVENT) + zlog_info("ZNS %s with id %u (created)", ns->name, ns->ns_id); + + zns = zebra_ns_alloc(); + ns->info = zns; + zns->ns = ns; + return 0; +} + +static int zebra_ns_delete(struct ns *ns) +{ + struct zebra_ns *zns = (struct zebra_ns *) ns->info; + + if (IS_ZEBRA_DEBUG_EVENT) + zlog_info("ZNS %s with id %u (deleted)", ns->name, ns->ns_id); + if (!zns) + return 0; + XFREE(MTYPE_ZEBRA_NS, zns); + return 0; +} + +static int zebra_ns_enabled(struct ns *ns) +{ + struct zebra_ns *zns = ns->info; + + if (IS_ZEBRA_DEBUG_EVENT) + zlog_info("ZNS %s with id %u (enabled)", ns->name, ns->ns_id); + if (!zns) + return 0; + return zebra_ns_enable(ns->ns_id, (void **)&zns); +} + +static int zebra_ns_disabled(struct ns *ns) +{ + struct zebra_ns *zns = ns->info; + + if (IS_ZEBRA_DEBUG_EVENT) + zlog_info("ZNS %s with id %u (disabled)", ns->name, ns->ns_id); + if (!zns) + return 0; + return zebra_ns_disable(ns->ns_id, (void **)&zns); +} + /* Do global enable actions - open sockets, read kernel config etc. */ int zebra_ns_enable(ns_id_t ns_id, void **info) { @@ -162,7 +215,9 @@ int zebra_ns_disable(ns_id_t ns_id, void **info) int zebra_ns_init(void) { - dzns = XCALLOC(MTYPE_ZEBRA_NS, sizeof(struct zebra_ns)); + dzns = zebra_ns_alloc(); + + ns_init_zebra(); ns_init(); @@ -176,6 +231,12 @@ int zebra_ns_init(void) /* Default NS is activated */ zebra_ns_enable(NS_DEFAULT, (void **)&dzns); + if (vrf_is_backend_netns()) { + ns_add_hook(NS_NEW_HOOK, zebra_ns_new); + 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); + } return 0; } diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index 99e4984164..aaf5abaa26 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -69,6 +69,9 @@ struct zebra_ns { #endif /* HAVE_RTADV */ struct zebra_ns_table_head ns_tables; + + /* Back pointer */ + struct ns *ns; }; struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id); From ff705b15dd5e191e727662412a8433d718887a08 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 8 Dec 2017 14:32:38 +0100 Subject: [PATCH 11/41] zebra: handle the zns init/destroy The zebra netnamespace contexts are initialised, based on the callback coming from the NS. Reversely, the list of ns is parsed to disable the ns contexts. Signed-off-by: Philippe Guibert --- lib/ns.c | 17 +++++++++++++++++ lib/ns.h | 2 ++ zebra/main.c | 4 +--- zebra/zebra_ns.c | 17 +++++++++++++++-- zebra/zebra_ns.h | 1 + 5 files changed, 36 insertions(+), 5 deletions(-) diff --git a/lib/ns.c b/lib/ns.c index 1929104eeb..5e6bddf0d8 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -201,6 +201,23 @@ static struct ns *ns_lookup(ns_id_t ns_id) return (RB_FIND(ns_head, &ns_tree, &ns)); } +/* Look up the data pointer of the specified VRF. */ +void * +ns_info_lookup(ns_id_t ns_id) +{ + struct ns *ns = ns_lookup(ns_id); + + return ns ? ns->info : NULL; +} + +void ns_walk_func(int (*func)(struct ns *)) +{ + struct ns *ns = NULL; + + RB_FOREACH(ns, ns_head, &ns_tree) + func(ns); +} + /* Look up a NS by name */ static struct ns *ns_lookup_name(const char *name) { diff --git a/lib/ns.h b/lib/ns.h index 590e1f2c20..6aebc44259 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -97,5 +97,7 @@ extern void ns_cmd_init(void); extern int ns_handler_create(struct vty *vty, struct vrf *vrf, char *pathname, ns_id_t ns_id); extern char *ns_netns_pathname(struct vty *vty, const char *name); +extern void *ns_info_lookup(ns_id_t ns_id); +extern void ns_walk_func(int (*func)(struct ns *)); #endif /*_ZEBRA_NS_H*/ diff --git a/zebra/main.c b/zebra/main.c index a881fcb9c6..73e5f1290d 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -123,7 +123,6 @@ static void sigint(void) { struct vrf *vrf; struct zebra_vrf *zvrf; - struct zebra_ns *zns; zlog_notice("Terminating on signal"); @@ -140,8 +139,7 @@ static void sigint(void) } vrf_terminate(); - zns = zebra_ns_lookup(NS_DEFAULT); - zebra_ns_disable(0, (void **)&zns); + ns_walk_func(zebra_ns_disabled); access_list_reset(); prefix_list_reset(); diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 6ce64b3a33..02fc2b1844 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -57,7 +57,11 @@ zebra_ns_table_entry_compare(const struct zebra_ns_table *e1, struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id) { - return dzns; + if (ns_id == NS_DEFAULT) + return dzns; + struct zebra_ns *info = (struct zebra_ns *)ns_info_lookup(ns_id); + + return (info == NULL) ? dzns : info; } static struct zebra_ns *zebra_ns_alloc(void) @@ -75,6 +79,11 @@ static int zebra_ns_new(struct ns *ns) zns = zebra_ns_alloc(); ns->info = zns; zns->ns = ns; + + /* Do any needed per-NS data structure allocation. */ + zns->if_table = route_table_init(); + zebra_vxlan_ns_init(zns); + return 0; } @@ -101,7 +110,7 @@ static int zebra_ns_enabled(struct ns *ns) return zebra_ns_enable(ns->ns_id, (void **)&zns); } -static int zebra_ns_disabled(struct ns *ns) +int zebra_ns_disabled(struct ns *ns) { struct zebra_ns *zns = ns->info; @@ -117,6 +126,8 @@ int zebra_ns_enable(ns_id_t ns_id, void **info) { struct zebra_ns *zns = (struct zebra_ns *)(*info); + zns->ns_id = ns_id; + #if defined(HAVE_RTADV) rtadv_init(zns); #endif @@ -209,6 +220,8 @@ int zebra_ns_disable(ns_id_t ns_id, void **info) kernel_terminate(zns); + zns->ns_id = NS_DEFAULT; + return 0; } diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index aaf5abaa26..3a998a49ff 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -78,6 +78,7 @@ struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id); int zebra_ns_init(void); int zebra_ns_enable(ns_id_t ns_id, void **info); +int zebra_ns_disabled(struct ns *ns); int zebra_ns_disable(ns_id_t ns_id, void **info); extern struct route_table *zebra_ns_find_table(struct zebra_ns *zns, From 81c9005ff6edd2294ec945b93d49f03470b3b827 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 7 Dec 2017 15:58:48 +0100 Subject: [PATCH 12/41] zebra: enhance show vrf for netns and fixing Show vrf command displays information on the vrf, if it is related to vrf kernel or if it is related to netns. When a vrf from kernel is detected, before creating a new vrf, a check is done against an already present vrf, and if that vrf is not a vrf mapped with a netns. If that is that case, then the creation is rejected. Signed-off-by: Philippe Guibert --- lib/ns.c | 7 +++++++ lib/ns.h | 1 + zebra/zebra_vrf.h | 13 +++++++++++++ zebra/zebra_vty.c | 6 +++++- 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/lib/ns.c b/lib/ns.c index 5e6bddf0d8..5af896632c 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -218,6 +218,13 @@ void ns_walk_func(int (*func)(struct ns *)) func(ns); } +const char *ns_get_name(struct ns *ns) +{ + if (!ns) + return NULL; + return ns->name; +} + /* Look up a NS by name */ static struct ns *ns_lookup_name(const char *name) { diff --git a/lib/ns.h b/lib/ns.h index 6aebc44259..fca5becd7a 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -99,5 +99,6 @@ extern int ns_handler_create(struct vty *vty, struct vrf *vrf, extern char *ns_netns_pathname(struct vty *vty, const char *name); 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); #endif /*_ZEBRA_NS_H*/ diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index 3b9e930969..ae5a174116 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -22,6 +22,7 @@ #if !defined(__ZEBRA_RIB_H__) #define __ZEBRA_RIB_H__ +#include #include #include #include @@ -133,11 +134,23 @@ static inline vrf_id_t zvrf_id(struct zebra_vrf *zvrf) return zvrf->vrf->vrf_id; } +static inline const char *zvrf_ns_name(struct zebra_vrf *zvrf) +{ + if (!zvrf->vrf || !zvrf->vrf->ns_ctxt) + return NULL; + return ns_get_name((struct ns *)zvrf->vrf->ns_ctxt); +} + static inline const char *zvrf_name(struct zebra_vrf *zvrf) { return zvrf->vrf->name; } +static inline bool zvrf_is_active(struct zebra_vrf *zvrf) +{ + return zvrf->vrf->status & VRF_ACTIVE; +} + struct route_table *zebra_vrf_table_with_table_id(afi_t afi, safi_t safi, vrf_id_t vrf_id, u_int32_t table_id); diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index ccc7cb30c3..4824c09f3d 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -2374,8 +2374,12 @@ DEFUN (show_vrf, continue; vty_out(vty, "vrf %s ", zvrf_name(zvrf)); - if (zvrf_id(zvrf) == VRF_UNKNOWN) + if (zvrf_id(zvrf) == VRF_UNKNOWN + || !zvrf_is_active(zvrf)) vty_out(vty, "inactive"); + else if (zvrf_ns_name(zvrf)) + vty_out(vty, "id %u netns %s", + zvrf_id(zvrf), zvrf_ns_name(zvrf)); else vty_out(vty, "id %u table %u", zvrf_id(zvrf), zvrf->table_id); From 4691b65ae4c3e50c295dce4fc007738080826b49 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 22 Dec 2017 16:02:51 +0100 Subject: [PATCH 13/41] lib: add namespace name structure in zebra message The addition of the name of the netns in the vrf message introduces also a limitation when the size of the netns is bigger than 15 bytes. Then the netns are ignored by the library. In addition to this, some sanity checks have been introduced. some functions to create the netns from a call not coming from the vty is being added with traces. Also, the ns vty function is reentrant, if the context is already created. Signed-off-by: Philippe Guibert Signed-off-by: Renato Westphal --- lib/ns.c | 25 +++++++++++++++++++++++++ lib/vrf.c | 6 +++--- lib/vrf.h | 2 ++ lib/zclient.c | 4 ++-- zebra/zebra_vrf.c | 2 +- zebra/zserv.c | 12 ++++++++++-- 6 files changed, 43 insertions(+), 8 deletions(-) diff --git a/lib/ns.c b/lib/ns.c index 5af896632c..e2c042d16c 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -28,6 +28,9 @@ #include #endif +/* for basename */ +#include + #include "if.h" #include "ns.h" #include "log.h" @@ -353,6 +356,7 @@ char *ns_netns_pathname(struct vty *vty, const char *name) { static char pathname[PATH_MAX]; char *result; + char *check_base; if (name[0] == '/') /* absolute pathname */ result = realpath(name, pathname); @@ -367,6 +371,21 @@ char *ns_netns_pathname(struct vty *vty, const char *name) if (vty) vty_out(vty, "Invalid pathname: %s\n", safe_strerror(errno)); + else + zlog_warn("Invalid pathname: %s", + safe_strerror(errno)); + return NULL; + } + check_base = basename(pathname); + if (check_base != NULL && strlen(check_base) + 1 > NS_NAMSIZ) { + if (vty) + vty_out(vty, "NS name (%s) invalid:" + " too long( %d needed)\n", + check_base, NS_NAMSIZ-1); + else + zlog_warn("NS name (%s) invalid:" + " too long ( %d needed)", + check_base, NS_NAMSIZ-1); return NULL; } return pathname; @@ -489,6 +508,8 @@ int ns_handler_create(struct vty *vty, struct vrf *vrf, if (ns && ns->vrf_ctxt) { struct vrf *vrf2 = (struct vrf *)ns->vrf_ctxt; + if (vrf2 == vrf) + return CMD_SUCCESS; if (vty) vty_out(vty, "NS %s is already configured" " with VRF %u(%s)\n", @@ -507,6 +528,10 @@ int ns_handler_create(struct vty *vty, struct vrf *vrf, } ns->vrf_ctxt = (void *)vrf; vrf->ns_ctxt = (void *)ns; + /* update VRF netns NAME */ + if (vrf) + strlcpy(vrf->data.l.netns_name, basename(pathname), NS_NAMSIZ); + if (!ns_enable(ns)) { if (vty) vty_out(vty, "Can not associate NS %u with NETNS %s\n", diff --git a/lib/vrf.c b/lib/vrf.c index e855f3f83b..5b85effabd 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -21,6 +21,9 @@ #include +/* for basename */ +#include + #include "if.h" #include "vrf.h" #include "vrf_int.h" @@ -131,8 +134,6 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name) if (vrf == NULL) { vrf = XCALLOC(MTYPE_VRF, sizeof(struct vrf)); vrf->vrf_id = VRF_UNKNOWN; - RB_INIT(if_name_head, &vrf->ifaces_by_name); - RB_INIT(if_index_head, &vrf->ifaces_by_index); QOBJ_REG(vrf, vrf); new = 1; @@ -156,7 +157,6 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name) strlcpy(vrf->name, name, sizeof(vrf->name)); RB_INSERT(vrf_name_head, &vrfs_by_name, vrf); } - if (new &&vrf_master.vrf_new_hook) (*vrf_master.vrf_new_hook)(vrf); diff --git a/lib/vrf.h b/lib/vrf.h index 16d75c3efb..c1da4e8bbc 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -42,6 +42,7 @@ enum { IFLA_VRF_UNSPEC, IFLA_VRF_TABLE, __IFLA_VRF_MAX }; #endif #define VRF_NAMSIZ 36 +#define NS_NAMSIZ 16 #define VRF_DEFAULT_NAME "Default-IP-Routing-Table" @@ -60,6 +61,7 @@ struct vrf_data { union { struct { uint32_t table_id; + char netns_name[NS_NAMSIZ]; } l; }; }; diff --git a/lib/zclient.c b/lib/zclient.c index 714888a3f3..9260e0b3ba 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -1402,8 +1402,8 @@ static void zclient_vrf_add(struct zclient *zclient, vrf_id_t vrf_id) /* Lookup/create vrf by vrf_id. */ vrf = vrf_get(vrf_id, vrfname_tmp); - vrf->data = data; - + vrf->data.l.table_id = data.l.table_id; + memcpy(vrf->data.l.netns_name, data.l.netns_name, NS_NAMSIZ); vrf_enable(vrf); } diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 276687ca83..874a9c74e7 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -77,7 +77,7 @@ void zebra_vrf_update_all(struct zserv *client) struct vrf *vrf; RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { - if (vrf->vrf_id) + if (vrf->vrf_id != VRF_UNKNOWN) zsend_vrf_add(client, vrf_info_lookup(vrf->vrf_id)); } } diff --git a/zebra/zserv.c b/zebra/zserv.c index b3b1fa79e9..f269422986 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -20,6 +20,8 @@ #include #include +/* for basename */ +#include #include "prefix.h" #include "command.h" @@ -182,13 +184,19 @@ static void zserv_encode_interface(struct stream *s, struct interface *ifp) static void zserv_encode_vrf(struct stream *s, struct zebra_vrf *zvrf) { struct vrf_data data; + const char *netns_name = zvrf_ns_name(zvrf); data.l.table_id = zvrf->table_id; - /* Pass the tableid */ + + if (netns_name) + strlcpy(data.l.netns_name, + basename((char *)netns_name), NS_NAMSIZ); + else + memset(data.l.netns_name, 0, NS_NAMSIZ); + /* Pass the tableid and the netns NAME */ stream_put(s, &data, sizeof(struct vrf_data)); /* Interface information. */ stream_put(s, zvrf_name(zvrf), VRF_NAMSIZ); - /* Write packet size. */ stream_putw_at(s, 0, stream_get_endp(s)); } From ce1be3692f809cfa4d533d484a75653f91c24c4e Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 20 Dec 2017 12:29:21 +0100 Subject: [PATCH 14/41] 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 */ From 05895ad0be6c8fadcd895c3b1aa137b89791fbde Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 7 Dec 2017 18:13:54 +0100 Subject: [PATCH 15/41] zebra: upon NS creation, collect the NSID via netlink A NS identifier is collected by netlink. This identifier is a 32 bit identifier that is either generated by the kernel (if not set) or manually set by a set netlink command. The commit here is getting the NSID from the newly created NS. If the linux option to create or get a new NSID from the kernel does not exist, then the NSID is locally genrated. Signed-off-by: Philippe Guibert --- include/linux/net_namespace.h | 23 +++ include/subdir.am | 1 + zebra/subdir.am | 2 + zebra/zebra_netns_id.c | 314 ++++++++++++++++++++++++++++++++++ zebra/zebra_netns_id.h | 25 +++ 5 files changed, 365 insertions(+) create mode 100644 include/linux/net_namespace.h create mode 100644 zebra/zebra_netns_id.c create mode 100644 zebra/zebra_netns_id.h diff --git a/include/linux/net_namespace.h b/include/linux/net_namespace.h new file mode 100644 index 0000000000..9a92b7e14a --- /dev/null +++ b/include/linux/net_namespace.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2015 6WIND S.A. + * Author: Nicolas Dichtel + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ +#ifndef _LINUX_NET_NAMESPACE_H_ +#define _LINUX_NET_NAMESPACE_H_ + +/* Attributes of RTM_NEWNSID/RTM_GETNSID messages */ +enum { + NETNSA_NONE, +#define NETNSA_NSID_NOT_ASSIGNED -1 + NETNSA_NSID, + NETNSA_PID, + NETNSA_FD, + __NETNSA_MAX, +}; + +#define NETNSA_MAX (__NETNSA_MAX - 1) + +#endif /* _LINUX_NET_NAMESPACE_H_ */ diff --git a/include/subdir.am b/include/subdir.am index 98bd148002..7a12b2ffae 100644 --- a/include/subdir.am +++ b/include/subdir.am @@ -6,4 +6,5 @@ noinst_HEADERS += \ include/linux/neighbour.h \ include/linux/rtnetlink.h \ include/linux/socket.h \ + include/linux/net_namespace.h \ # end diff --git a/zebra/subdir.am b/zebra/subdir.am index 3474823623..fcfb0cc203 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -65,6 +65,7 @@ zebra_zebra_SOURCES = \ zebra/zebra_vty.c \ zebra/zebra_vxlan.c \ zebra/zserv.c \ + zebra/zebra_netns_id.c \ # end zebra/zebra_vty_clippy.c: $(CLIPPY_DEPS) @@ -104,6 +105,7 @@ noinst_HEADERS += \ zebra/zebra_vxlan.h \ zebra/zebra_vxlan_private.h \ zebra/zserv.h \ + zebra/zebra_netns_id.h \ # end zebra_zebra_irdp_la_SOURCES = \ diff --git a/zebra/zebra_netns_id.c b/zebra/zebra_netns_id.c new file mode 100644 index 0000000000..c5e792bd77 --- /dev/null +++ b/zebra/zebra_netns_id.c @@ -0,0 +1,314 @@ +/* zebra NETNS ID handling routines + * those routines are implemented locally to avoid having external dependencies. + * Copyright (C) 2018 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 + +#include "ns.h" +#include "log.h" + +#if defined(HAVE_NETLINK) + +#include +#include +#include + +#include "rib.h" +#include "zebra_ns.h" +#include "kernel_netlink.h" +#endif /* defined(HAVE_NETLINK) */ + +#include "zebra_netns_id.h" + +/* in case NEWNSID not available, the NSID will be locally obtained + */ +#define NS_BASE_NSID 0 + +#if defined(HAVE_NETLINK) + +#define NETLINK_SOCKET_BUFFER_SIZE 512 +#define NETLINK_ALIGNTO 4 +#define NETLINK_ALIGN(len) (((len)+NETLINK_ALIGNTO-1) \ + & ~(NETLINK_ALIGNTO-1)) +#define NETLINK_NLATTR_LEN(_a, _b) (unsigned int)((char *)_a - (char *)_b) + +#endif /* defined(HAVE_NETLINK) */ + +static ns_id_t zebra_ns_id_get_fallback(const char *netnspath) +{ + static int zebra_ns_id_local; + + return zebra_ns_id_local++; +} + +#if defined(HAVE_NETLINK) + +static struct nlmsghdr *initiate_nlh(char *buf, unsigned int *seq, int type) +{ + struct nlmsghdr *nlh; + + nlh = (struct nlmsghdr *)buf; + nlh->nlmsg_len = NETLINK_ALIGN(sizeof(struct nlmsghdr)); + + nlh->nlmsg_type = type; + nlh->nlmsg_flags = NLM_F_REQUEST; + if (type == RTM_NEWNSID) + nlh->nlmsg_flags |= NLM_F_ACK; + nlh->nlmsg_seq = *seq = time(NULL); + return nlh; +} + +static int send_receive(int sock, struct nlmsghdr *nlh, + unsigned int seq, char *buf) +{ + int ret; + static const struct sockaddr_nl snl = { + .nl_family = AF_NETLINK + }; + + ret = sendto(sock, (const void *)nlh, (size_t)nlh->nlmsg_len, 0, + (struct sockaddr *) &snl, (socklen_t)sizeof(snl)); + if (ret < 0) { + zlog_err("netlink( %u) sendmsg() error: %s", + sock, safe_strerror(errno)); + return -1; + } + + /* reception */ + struct sockaddr_nl addr; + struct iovec iov = { + .iov_base = buf, + .iov_len = NETLINK_SOCKET_BUFFER_SIZE, + }; + struct msghdr msg = { + .msg_name = &addr, + .msg_namelen = sizeof(struct sockaddr_nl), + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = NULL, + .msg_controllen = 0, + .msg_flags = 0, + }; + ret = recvmsg(sock, &msg, 0); + if (ret < 0) { + zlog_err("netlink recvmsg: error %d (errno %u)", ret, errno); + return -1; + } + if (msg.msg_flags & MSG_TRUNC) { + zlog_err("netlink recvmsg : error message truncated"); + return -1; + } + /* nlh already points to buf */ + if (nlh->nlmsg_seq != seq) { + zlog_err("netlink recvmsg: bad sequence number %x (expected %x)", + seq, nlh->nlmsg_seq); + return -1; + } + return ret; +} + +/* extract on a valid nlmsg the nsid + * valid nlmsghdr - not a nlmsgerr + */ +static ns_id_t extract_nsid(struct nlmsghdr *nlh, char *buf) +{ + ns_id_t ns_id = NS_UNKNOWN; + int offset = NETLINK_ALIGN(sizeof(struct nlmsghdr)) + + NETLINK_ALIGN(sizeof(struct rtgenmsg)); + int curr_length = offset; + void *tail = (void *)((char *)nlh + NETLINK_ALIGN(nlh->nlmsg_len)); + struct nlattr *attr; + + for (attr = (struct nlattr *)((char *)buf + offset); + NETLINK_NLATTR_LEN(tail, attr) >= sizeof(struct nlattr) && + attr->nla_len >= sizeof(struct nlattr) && + attr->nla_len <= NETLINK_NLATTR_LEN(tail, attr); + attr += NETLINK_ALIGN(attr->nla_len)) { + curr_length += attr->nla_len; + if ((attr->nla_type & NLA_TYPE_MASK) == NETNSA_NSID) { + u_int32_t *ptr = (u_int32_t *)(attr); + + ns_id = ptr[1]; + break; + } + } + return ns_id; +} + +ns_id_t zebra_ns_id_get(const char *netnspath) +{ + int ns_id = -1; + struct sockaddr_nl snl; + int fd, sock, ret; + unsigned int seq; + ns_id_t return_nsid = NS_UNKNOWN; + + /* netns path check */ + if (!netnspath) + return NS_UNKNOWN; + fd = open(netnspath, O_RDONLY); + if (fd == -1) + return NS_UNKNOWN; + + /* netlink socket */ + sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock < 0) { + zlog_err("netlink( %u) socket() error: %s", + sock, safe_strerror(errno)); + return NS_UNKNOWN; + } + memset(&snl, 0, sizeof(snl)); + snl.nl_family = AF_NETLINK; + snl.nl_groups = RTNLGRP_NSID; + snl.nl_pid = 0; /* AUTO PID */ + ret = bind(sock, (struct sockaddr *)&snl, sizeof(snl)); + if (ret < 0) { + zlog_err("netlink( %u) socket() bind error: %s", + sock, safe_strerror(errno)); + close(sock); + close(fd); + return NS_UNKNOWN; + } + + /* message to send to netlink,and response : NEWNSID */ + char buf[NETLINK_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct rtgenmsg *rt; + int len; + + memset(buf, 0, NETLINK_SOCKET_BUFFER_SIZE); + nlh = initiate_nlh(buf, &seq, RTM_NEWNSID); + rt = (struct rtgenmsg *)(buf + nlh->nlmsg_len); + nlh->nlmsg_len += NETLINK_ALIGN(sizeof(struct rtgenmsg)); + rt->rtgen_family = AF_UNSPEC; + + addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_FD, fd); + addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID, ns_id); + + ret = send_receive(sock, nlh, seq, buf); + if (ret < 0) { + close(sock); + close(fd); + return NS_UNKNOWN; + } + nlh = (struct nlmsghdr *)buf; + + /* message to analyse : NEWNSID response */ + len = ret; + ret = 0; + do { + if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) { + return_nsid = extract_nsid(nlh, buf); + if (return_nsid != NS_UNKNOWN) + break; + } else { + if (nlh->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *) + ((char *)nlh + + NETLINK_ALIGN(sizeof( + struct nlmsghdr))); + + ret = -1; + if (err->error < 0) + errno = -err->error; + else + errno = err->error; + if (errno == 0) { + /* request NEWNSID was successfull + * return EEXIST error to get GETNSID + */ + errno = EEXIST; + } + } else { + /* other errors ignored + * attempt to get nsid + */ + ret = -1; + errno = EEXIST; + break; + } + } + len = len - NETLINK_ALIGN(nlh->nlmsg_len); + nlh = (struct nlmsghdr *)((char *)nlh + + NETLINK_ALIGN(nlh->nlmsg_len)); + } while (len != 0 && return_nsid != NS_UNKNOWN && ret == 0); + + if (ret <= 0) { + if (errno != EEXIST && ret != 0) { + zlog_err("netlink( %u) recvfrom() error 2 when reading: %s", + fd, safe_strerror(errno)); + close(sock); + close(fd); + if (errno == ENOTSUP) { + zlog_warn("NEWNSID locally generated"); + return zebra_ns_id_get_fallback(netnspath); + } + return NS_UNKNOWN; + } + /* message to send to netlink : GETNSID */ + memset(buf, 0, NETLINK_SOCKET_BUFFER_SIZE); + nlh = initiate_nlh(buf, &seq, RTM_GETNSID); + rt = (struct rtgenmsg *)(buf + nlh->nlmsg_len); + nlh->nlmsg_len += NETLINK_ALIGN(sizeof(struct rtgenmsg)); + rt->rtgen_family = AF_UNSPEC; + + addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_FD, fd); + addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID, ns_id); + + ret = send_receive(sock, nlh, seq, buf); + if (ret < 0) { + close(sock); + close(fd); + return NS_UNKNOWN; + } + nlh = (struct nlmsghdr *)buf; + len = ret; + ret = 0; + do { + if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) { + return_nsid = extract_nsid(nlh, buf); + if (return_nsid != NS_UNKNOWN) + break; + } else if (nlh->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *) + ((char *)nlh + + NETLINK_ALIGN(sizeof( + struct nlmsghdr))); + if (err->error < 0) + errno = -err->error; + else + errno = err->error; + break; + } + len = len - NETLINK_ALIGN(nlh->nlmsg_len); + nlh = (struct nlmsghdr *)((char *)nlh + + NETLINK_ALIGN(nlh->nlmsg_len)); + } while (len != 0 && return_nsid != NS_UNKNOWN && ret == 0); + } + + close(fd); + close(sock); + return return_nsid; +} + +#else +ns_id_t zebra_ns_id_get(const char *netnspath) +{ + return zebra_ns_id_get_fallback(netnspath); +} +#endif /* ! defined(HAVE_NETLINK) */ diff --git a/zebra/zebra_netns_id.h b/zebra/zebra_netns_id.h new file mode 100644 index 0000000000..18fdf50cf1 --- /dev/null +++ b/zebra/zebra_netns_id.h @@ -0,0 +1,25 @@ +/* zebra NETNS ID handling routines + * Copyright (C) 2018 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 + */ +#if !defined(__ZEBRA_NS_ID_H__) +#define __ZEBRA_NS_ID_H__ +#include "zebra.h" +#include "ns.h" + +extern ns_id_t zebra_ns_id_get(const char *netnspath); + +#endif /* __ZEBRA_NS_ID_H__ */ From e27dec3cf15d627cc26c07f40ded138b0bcc6a0a Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 13 Dec 2017 11:04:31 +0100 Subject: [PATCH 16/41] 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; } From ec31f30d28b65a4820a44ac658e677840ed6f88b Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 16 Jan 2018 13:59:58 +0100 Subject: [PATCH 17/41] zebra: upon startup, a NSID is assigned to default netns when the netns backend is selected for VRF, the default VRF is being assigned a NSID. This avoids the need to handle the case where if the incoming NSID was 0 for a non default VRF, then a specific handling had to be done to keep 0 value for default VRF. In most cases, as the first NETNS to get a NSID will be the default VRF, most probably the default VRF will be assigned to 0, while the other ones will have their value incremented. On some cases, where the NSID is already assigned for NETNS, including default VRF, then the default VRF value will be the one derived from the NSID of default VRF, thus keeping consistency between VRF IDs and NETNS IDs. Default NS is attempted to be created. Actually, some VMs may have the netns feature, but the NS initialisation fails because that folder is not present. Signed-off-by: Philippe Guibert --- lib/ns.c | 35 ++++++++++++++++++++++------------- lib/ns.h | 14 ++++++++++++-- lib/vrf.c | 15 +++++++++++++++ lib/vrf.h | 10 ++++++---- zebra/zebra_netns_id.c | 41 +++++++++++++++++++++++++++++++++++++++++ zebra/zebra_netns_id.h | 1 + zebra/zebra_ns.c | 12 +++++++++++- 7 files changed, 108 insertions(+), 20 deletions(-) diff --git a/lib/ns.c b/lib/ns.c index 9aa3509923..17d70a12fe 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -51,6 +51,7 @@ RB_GENERATE(ns_head, ns, entry, ns_compare) struct ns_head ns_tree = RB_INITIALIZER(&ns_tree); +static struct ns *default_ns; static int ns_current_ns_fd; static int ns_default_ns_fd; @@ -71,16 +72,12 @@ 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 */ - -#define NS_DEFAULT_NAME "Default-logical-router" - #endif /* HAVE_NETNS */ +/* default NS ID value used when VRF backend is not NETNS */ +#define NS_DEFAULT_INTERNAL 0 + static int have_netns(void) { #ifdef HAVE_NETNS @@ -625,24 +622,28 @@ void ns_init(void) } #endif /* HAVE_NETNS */ ns_default_ns_fd = -1; + default_ns = NULL; } /* Initialize NS module. */ -void ns_init_zebra(void) +void ns_init_zebra(ns_id_t default_ns_id) { - struct ns *default_ns; + int fd; ns_init(); - /* The default NS always exists. */ - default_ns = ns_get(NS_DEFAULT); - ns_current_ns_fd = -1; + default_ns = ns_get(default_ns_id); if (!default_ns) { zlog_err("ns_init: failed to create the default NS!"); exit(1); } - + if (have_netns()) { + fd = open(NS_DEFAULT_NAME, O_RDONLY); + default_ns->fd = fd; + } + ns_current_ns_fd = -1; /* Set the default NS name. */ default_ns->name = XSTRDUP(MTYPE_NS_NAME, NS_DEFAULT_NAME); + zlog_info("ns_init: default NSID is %u", default_ns->ns_id); /* Enable the default NS. */ if (!ns_enable(default_ns)) { @@ -739,3 +740,11 @@ int ns_socket(int domain, int type, int protocol, ns_id_t ns_id) return ret; } + +ns_id_t ns_get_default_id(void) +{ + if (default_ns) + return default_ns->ns_id; + return NS_UNKNOWN; +} + diff --git a/lib/ns.h b/lib/ns.h index 73482d4d56..44257ab0c0 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -30,12 +30,17 @@ typedef u_int32_t ns_id_t; /* the default NS ID */ -#define NS_DEFAULT 0 #define NS_UNKNOWN UINT32_MAX /* Default netns directory (Linux) */ #define NS_RUN_DIR "/var/run/netns" +#ifdef HAVE_NETNS +#define NS_DEFAULT_NAME "/proc/self/ns/net" +#else /* !HAVE_NETNS */ +#define NS_DEFAULT_NAME "Default-logical-router" +#endif /* HAVE_NETNS */ + struct ns { RB_ENTRY(ns) entry; @@ -85,7 +90,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_init_zebra(ns_id_t ns_id); extern void ns_terminate(void); /* @@ -101,10 +106,15 @@ extern char *ns_netns_pathname(struct vty *vty, const char *name); 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); +extern ns_id_t ns_get_default_id(void); /* 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); + +/* The default NS ID */ +#define NS_DEFAULT ns_get_default_id() + #endif /*_ZEBRA_NS_H*/ diff --git a/lib/vrf.c b/lib/vrf.c index 7871052352..f4dc237eb3 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -34,6 +34,9 @@ #include "command.h" #include "ns.h" +/* default VRF ID value used when VRF backend is not NETNS */ +#define VRF_DEFAULT_INTERNAL 0 + DEFINE_MTYPE_STATIC(LIB, VRF, "VRF") DEFINE_MTYPE_STATIC(LIB, VRF_BITMAP, "VRF bit-map") @@ -646,3 +649,15 @@ void vrf_cmd_init(int (*writefunc)(struct vty *vty)) install_default(VRF_NODE); ns_cmd_init(); } + +vrf_id_t vrf_get_default_id(void) +{ + struct vrf *vrf = vrf_lookup_by_name(VRF_DEFAULT_NAME); + + if (vrf) + return vrf->vrf_id; + if (vrf_is_backend_netns()) + return ns_get_default_id(); + else + return VRF_DEFAULT_INTERNAL; +} diff --git a/lib/vrf.h b/lib/vrf.h index 4bdc183b5c..9553d43808 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -27,11 +27,7 @@ #include "qobj.h" #include "vty.h" -/* The default NS ID */ -#define NS_DEFAULT 0 - /* The default VRF ID */ -#define VRF_DEFAULT 0 #define VRF_UNKNOWN UINT32_MAX /* Pending: May need to refine this. */ @@ -208,6 +204,7 @@ extern void vrf_cmd_init(int (*writefunc)(struct vty *vty)); /* * VRF utilities */ +extern vrf_id_t vrf_get_default_id(void); /* Create a socket serving for the given VRF */ extern int vrf_socket(int, int, int, vrf_id_t); @@ -236,4 +233,9 @@ extern int vrf_enable(struct vrf *vrf); * VRF Debugging */ extern void vrf_install_commands(void); + + +/* The default VRF ID */ +#define VRF_DEFAULT vrf_get_default_id() + #endif /*_ZEBRA_VRF_H*/ diff --git a/zebra/zebra_netns_id.c b/zebra/zebra_netns_id.c index c5e792bd77..966d6ed0d2 100644 --- a/zebra/zebra_netns_id.c +++ b/zebra/zebra_netns_id.c @@ -20,6 +20,7 @@ #include #include "ns.h" +#include "vrf.h" #include "log.h" #if defined(HAVE_NETLINK) @@ -35,6 +36,9 @@ #include "zebra_netns_id.h" +/* default NS ID value used when VRF backend is not NETNS */ +#define NS_DEFAULT_INTERNAL 0 + /* in case NEWNSID not available, the NSID will be locally obtained */ #define NS_BASE_NSID 0 @@ -312,3 +316,40 @@ ns_id_t zebra_ns_id_get(const char *netnspath) return zebra_ns_id_get_fallback(netnspath); } #endif /* ! defined(HAVE_NETLINK) */ + +#ifdef HAVE_NETNS +static void zebra_ns_create_netns_directory(void) +{ + /* check that /var/run/netns is created */ + /* S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH */ + if (mkdir(NS_RUN_DIR, 0755)) { + if (errno != EEXIST) { + zlog_warn("NS check: failed to access %s", NS_RUN_DIR); + return; + } + } +} +#endif + +ns_id_t zebra_ns_id_get_default(void) +{ +#ifdef HAVE_NETNS + int fd; +#endif /* !HAVE_NETNS */ + +#ifdef HAVE_NETNS + if (vrf_is_backend_netns()) + zebra_ns_create_netns_directory(); + fd = open(NS_DEFAULT_NAME, O_RDONLY); + + if (fd == -1) + return NS_DEFAULT_INTERNAL; + if (!vrf_is_backend_netns()) + return NS_DEFAULT_INTERNAL; + close(fd); + return zebra_ns_id_get((char *)NS_DEFAULT_NAME); +#else /* HAVE_NETNS */ + return NS_DEFAULT_INTERNAL; +#endif /* !HAVE_NETNS */ +} + diff --git a/zebra/zebra_netns_id.h b/zebra/zebra_netns_id.h index 18fdf50cf1..d6530e6694 100644 --- a/zebra/zebra_netns_id.h +++ b/zebra/zebra_netns_id.h @@ -21,5 +21,6 @@ #include "ns.h" extern ns_id_t zebra_ns_id_get(const char *netnspath); +extern ns_id_t zebra_ns_id_get_default(void); #endif /* __ZEBRA_NS_ID_H__ */ diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 50551c9b35..da5b22def2 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -34,6 +34,9 @@ #include "zebra_vxlan.h" #include "debug.h" #include "zebra_netns_notify.h" +#include "zebra_netns_id.h" + +extern struct zebra_privs_t zserv_privs; DEFINE_MTYPE(ZEBRA, ZEBRA_NS, "Zebra Name Space") @@ -229,9 +232,16 @@ int zebra_ns_disable(ns_id_t ns_id, void **info) int zebra_ns_init(void) { + ns_id_t ns_id; + dzns = zebra_ns_alloc(); - ns_init_zebra(); + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); + ns_id = zebra_ns_id_get_default(); + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); + ns_init_zebra(ns_id); ns_init(); From 0439cb9d9e15053181f0bf13e49c39d74ca37e50 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 22 Jan 2018 13:46:20 +0100 Subject: [PATCH 18/41] zebra: fix initialised vrf_id value never read this is a static analysis performed by c-lang scan-build tool that demonstrated this issue. This commit is handling the fix. Signed-off-by: Philippe Guibert --- zebra/interface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zebra/interface.c b/zebra/interface.c index 74ffdee31f..e919d9f08f 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -1414,7 +1414,7 @@ DEFUN (show_interface_name_vrf, int idx_ifname = 2; int idx_name = 4; struct interface *ifp; - vrf_id_t vrf_id = VRF_DEFAULT; + vrf_id_t vrf_id; interface_update_stats(); From e9e9b1150f0cea709ffffba1121ae216fbdbc2ca Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 19 Dec 2017 12:44:44 +0100 Subject: [PATCH 19/41] lib: create interface even if name is the same For supporting vrf based on namespaces, it is possible that an interface with the same index is present. This is the case for loopback interfaces. For that, for each query, if the interface is not found , matching the vrf identifier, then a new interface is created, when the backens for VRF is NETNS. Signed-off-by: Philippe Guibert --- lib/if.c | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/lib/if.c b/lib/if.c index 12d123a8fa..3a83de46ae 100644 --- a/lib/if.c +++ b/lib/if.c @@ -384,29 +384,35 @@ struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id, int vty) { struct interface *ifp; + ifp = if_lookup_by_name(name, vrf_id); + if (ifp) + return ifp; + /* Not Found on same VRF. If the interface command + * was entered in vty without a VRF (passed as VRF_DEFAULT), + * accept the ifp we found. If a vrf was entered and there is + * a mismatch, reject it if from vty. + */ ifp = if_lookup_by_name_all_vrf(name); - if (ifp) { - if (ifp->vrf_id == vrf_id) + if (!ifp) + return if_create(name, vrf_id); + if (vty) { + if (vrf_id == VRF_DEFAULT) return ifp; - - /* Found a match on a different VRF. If the interface command - * was entered in vty without a VRF (passed as VRF_DEFAULT), - * accept the ifp we found. If a vrf was entered and there is - * a mismatch, reject it if from vty. If it came from the kernel - * or by way of zclient, believe it and update the ifp - * accordingly. - */ - if (vty) { - if (vrf_id == VRF_DEFAULT) - return ifp; - return NULL; - } else { - if_update_to_new_vrf(ifp, vrf_id); - return ifp; - } + return NULL; } - - return if_create(name, vrf_id); + /* if vrf backend uses NETNS, then + * this should not be considered as an update + * then create the new interface + */ + if (ifp->vrf_id != vrf_id && + vrf_is_mapped_on_netns(vrf_id)) + return if_create(name, vrf_id); + /* If it came from the kernel + * or by way of zclient, believe it and update + * the ifp accordingly. + */ + if_update_to_new_vrf(ifp, vrf_id); + return ifp; } void if_set_index(struct interface *ifp, ifindex_t ifindex) From 7d206035d9a6dd1e66d5349f76d9c614fbac65f6 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 22 Jan 2018 16:06:58 +0100 Subject: [PATCH 20/41] ospfd: fix static analysis with variable initialised never read the vrf identifier in the ospf_vrf_enable routine is never read, then does not need to be initialised. Signed-off-by: Philippe Guibert --- ospfd/ospfd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 86a3293d71..68c682c6c4 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -2049,7 +2049,7 @@ static int ospf_vrf_delete(struct vrf *vrf) static int ospf_vrf_enable(struct vrf *vrf) { struct ospf *ospf = NULL; - vrf_id_t old_vrf_id = VRF_DEFAULT; + vrf_id_t old_vrf_id; if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: VRF %s id %u enabled", From 40289934f1b07acf934f26aec63ed523e6889a1d Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 24 Jan 2018 19:06:06 +0100 Subject: [PATCH 21/41] zebra: fix assert mpls when terminating zebra The assert appears in zebra_mpls.c when checking default zebra_vrf. It appears that when the mpls entries are flushed, it gets the default vrf which is already flushed by vrf_terminate() function. In order to avoid that assert to trigger a crash, the mpls flush is called before vrf termination. Signed-off-by: Philippe Guibert --- zebra/main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zebra/main.c b/zebra/main.c index 3353290816..9cad913f4b 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -138,6 +138,8 @@ static void sigint(void) if (zvrf) SET_FLAG(zvrf->flags, ZEBRA_VRF_RETAIN); } + if (zebrad.lsp_process_q) + work_queue_free(zebrad.lsp_process_q); vrf_terminate(); ns_walk_func(zebra_ns_disabled); @@ -149,8 +151,6 @@ static void sigint(void) list_delete_and_null(&zebrad.client_list); work_queue_free(zebrad.ribq); - if (zebrad.lsp_process_q) - work_queue_free(zebrad.lsp_process_q); meta_queue_free(zebrad.mq); frr_fini(); From 2e0d2b3d9c4f2681d00ec2607319f3bb8078e31d Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 26 Jan 2018 12:28:27 +0100 Subject: [PATCH 22/41] lib: add two APIs to handle socket operations with VRF NETNS The vrf_sockunion_socket() wraps sockunion_socket() with vrf_id as additional parameter. The creation of socket forces the user to transparently move to new NETNS for doing the operation. The vrf_getaddr_info() wraps getaddr_info() with vrf_id as additional parameter. That API relies on the underlying system. Then there may be need to switch to an other netns in that case too. Also, the vrf_socket() implementation is simplified. Signed-off-by: Philippe Guibert --- lib/vrf.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-- lib/vrf.h | 9 +++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/lib/vrf.c b/lib/vrf.c index f4dc237eb3..ade1895bd4 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -490,10 +490,19 @@ void vrf_terminate(void) /* Create a socket for the VRF. */ int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id) { - int ret = -1; + int ret, save_errno, ret2; + ret = vrf_switch_to_netns(vrf_id); + if (ret < 0) + zlog_err("%s: Can't switch to VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); ret = socket(domain, type, protocol); - + save_errno = errno; + ret2 = vrf_switchback_to_initial(); + if (ret2 < 0) + zlog_err("%s: Can't switchback from VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); + errno = save_errno; return ret; } @@ -661,3 +670,41 @@ vrf_id_t vrf_get_default_id(void) else return VRF_DEFAULT_INTERNAL; } + +int vrf_getaddrinfo(const char *node, const char *service, + const struct addrinfo *hints, + struct addrinfo **res, vrf_id_t vrf_id) +{ + int ret, ret2, save_errno; + + ret = vrf_switch_to_netns(vrf_id); + if (ret < 0) + zlog_err("%s: Can't switch to VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); + ret = getaddrinfo(node, service, hints, res); + save_errno = errno; + ret2 = vrf_switchback_to_initial(); + if (ret2 < 0) + zlog_err("%s: Can't switchback from VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); + errno = save_errno; + return ret; +} + +int vrf_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id) +{ + int ret, save_errno, ret2; + + ret = vrf_switch_to_netns(vrf_id); + if (ret < 0) + zlog_err("%s: Can't switch to VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); + ret = sockunion_socket(su); + save_errno = errno; + ret2 = vrf_switchback_to_initial(); + if (ret2 < 0) + zlog_err("%s: Can't switchback from VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); + errno = save_errno; + return ret; +} diff --git a/lib/vrf.h b/lib/vrf.h index 9553d43808..08c53484ee 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -222,6 +222,15 @@ int vrf_is_mapped_on_netns(vrf_id_t vrf_id); extern int vrf_switch_to_netns(vrf_id_t vrf_id); extern int vrf_switchback_to_initial(void); +/* VRF ioctl operations */ +extern int vrf_getaddrinfo(const char *node, const char *service, + const struct addrinfo *hints, + struct addrinfo **res, vrf_id_t vrf_id); +extern int vrf_sockunion_socket(const union sockunion *su, 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 */ From 61cf4b371572363106c489b6f716b2b111d68ef9 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 20 Dec 2017 12:37:18 +0100 Subject: [PATCH 23/41] bgpd: bgp support for netns The change contained in this commit does the following: - discovery of vrf id from zebra daemon, and adaptation of bgp contexts with BGP. The list of network addresses contain a reference to the bgp context supporting the vrf. The bgp context contains a vrf pointer that gives information about the netns path in case the vrf is a netns path. Only some contexts are impacted, namely socket creation, and retrieval of local IP settings. ( this requires vrf identifier). Signed-off-by: Philippe Guibert --- bgpd/bgp_fsm.c | 9 +++++++ bgpd/bgp_main.c | 3 +++ bgpd/bgp_network.c | 58 +++++++++++++++++++++++++++++++++++----------- bgpd/bgp_network.h | 3 ++- bgpd/bgpd.c | 9 +++---- 5 files changed, 63 insertions(+), 19 deletions(-) diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index de453de0c8..79f4b1c91a 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -1376,6 +1376,15 @@ int bgp_start(struct peer *peer) return 0; } + if (peer->bgp && + peer->bgp->vrf_id == VRF_UNKNOWN) { + if (bgp_debug_neighbor_events(peer)) + zlog_err( + "%s [FSM] In a VRF that is not initialised yet", + peer->host); + return -1; + } + /* Register to be notified on peer up */ if (peer->sort == BGP_PEER_EBGP && peer->ttl == 1 && !CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK) diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 0508f4846d..b5448b694e 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -41,6 +41,7 @@ #include "vrf.h" #include "bfd.h" #include "libfrr.h" +#include "ns.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_attr.h" @@ -57,6 +58,7 @@ #include "bgpd/bgp_zebra.h" #include "bgpd/bgp_packet.h" #include "bgpd/bgp_keepalives.h" +#include "bgpd/bgp_network.h" #ifdef ENABLE_BGP_VNC #include "bgpd/rfapi/rfapi_backend.h" @@ -297,6 +299,7 @@ static int bgp_vrf_disable(struct vrf *vrf) static void bgp_vrf_init(void) { + ns_init(); vrf_init(bgp_vrf_new, bgp_vrf_enable, bgp_vrf_disable, bgp_vrf_delete); } diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index bf39cbe1fc..e3cca63905 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -34,6 +34,7 @@ #include "queue.h" #include "hash.h" #include "filter.h" +#include "ns.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_open.h" @@ -51,6 +52,7 @@ struct bgp_listener { int fd; union sockunion su; struct thread *thread; + struct bgp *bgp; }; /* @@ -284,6 +286,7 @@ static int bgp_accept(struct thread *thread) return -1; } listener->thread = NULL; + thread_add_read(bm->master, bgp_accept, listener, accept_sock, &listener->thread); @@ -296,8 +299,13 @@ static int bgp_accept(struct thread *thread) } set_nonblocking(bgp_sock); - /* Obtain BGP instance this connection is meant for. */ - if (bgp_get_instance_for_inc_conn(bgp_sock, &bgp)) { + /* Obtain BGP instance this connection is meant for. + * - if it is a VRF netns sock, then BGP is in listener structure + * - otherwise, the bgp instance need to be demultiplexed + */ + if (listener->bgp) + bgp = listener->bgp; + else if (bgp_get_instance_for_inc_conn(bgp_sock, &bgp)) { if (bgp_debug_neighbor_events(NULL)) zlog_debug( "[Event] Could not get instance for incoming conn from %s", @@ -442,10 +450,12 @@ static int bgp_bind(struct peer *peer) int myerrno; char *name = NULL; - /* If not bound to an interface or part of a VRF, we don't care. */ - if (!peer->bgp->vrf_id && !peer->ifname && !peer->conf_if) + /* If not bound to an interface or part of a VRF lite, we don't care. */ + if ((peer->bgp->vrf_id == VRF_DEFAULT) && + !peer->ifname && !peer->conf_if) + return 0; + if (vrf_is_mapped_on_netns(peer->bgp->vrf_id)) return 0; - if (peer->su.sa.sa_family != AF_INET && peer->su.sa.sa_family != AF_INET6) return 0; // unexpected @@ -558,8 +568,12 @@ int bgp_connect(struct peer *peer) zlog_debug("Peer address not learnt: Returning from connect"); return 0; } + if (bgpd_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); /* Make socket for the peer. */ - peer->fd = sockunion_socket(&peer->su); + peer->fd = vrf_sockunion_socket(&peer->su, peer->bgp->vrf_id); + if (bgpd_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); if (peer->fd < 0) return -1; @@ -642,12 +656,12 @@ int bgp_getsockname(struct peer *peer) return -1; #endif } - return 0; } -static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen) +static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen, + struct bgp *bgp) { struct bgp_listener *listener; int ret, en; @@ -683,8 +697,14 @@ static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen) return ret; } - listener = XMALLOC(MTYPE_BGP_LISTENER, sizeof(*listener)); + listener = XCALLOC(MTYPE_BGP_LISTENER, sizeof(*listener)); listener->fd = sock; + + /* this socket needs a change of ns. record bgp back pointer */ + if (bgp->vrf_id != VRF_DEFAULT && + vrf_is_mapped_on_netns(bgp->vrf_id)) + listener->bgp = bgp; + memcpy(&listener->su, sa, salen); listener->thread = NULL; thread_add_read(bm->master, bgp_accept, listener, sock, @@ -695,7 +715,7 @@ static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen) } /* IPv6 supported version of BGP server socket setup. */ -int bgp_socket(unsigned short port, const char *address) +int bgp_socket(struct bgp *bgp, unsigned short port, const char *address) { struct addrinfo *ainfo; struct addrinfo *ainfo_save; @@ -710,7 +730,12 @@ int bgp_socket(unsigned short port, const char *address) snprintf(port_str, sizeof(port_str), "%d", port); port_str[sizeof(port_str) - 1] = '\0'; - ret = getaddrinfo(address, port_str, &req, &ainfo_save); + if (bgpd_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); + ret = vrf_getaddrinfo(address, port_str, &req, + &ainfo_save, bgp->vrf_id); + if (bgpd_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); if (ret != 0) { zlog_err("getaddrinfo: %s", gai_strerror(ret)); return -1; @@ -723,8 +748,12 @@ int bgp_socket(unsigned short port, const char *address) if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6) continue; - sock = socket(ainfo->ai_family, ainfo->ai_socktype, - ainfo->ai_protocol); + if (bgpd_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); + sock = vrf_socket(ainfo->ai_family, ainfo->ai_socktype, + ainfo->ai_protocol, bgp->vrf_id); + if (bgpd_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); if (sock < 0) { zlog_err("socket: %s", safe_strerror(errno)); continue; @@ -734,7 +763,8 @@ int bgp_socket(unsigned short port, const char *address) * ttl=255 */ sockopt_ttl(ainfo->ai_family, sock, MAXTTL); - ret = bgp_listener(sock, ainfo->ai_addr, ainfo->ai_addrlen); + ret = bgp_listener(sock, ainfo->ai_addr, + ainfo->ai_addrlen, bgp); if (ret == 0) ++count; else diff --git a/bgpd/bgp_network.h b/bgpd/bgp_network.h index 75ff1305c2..5691b73e22 100644 --- a/bgpd/bgp_network.h +++ b/bgpd/bgp_network.h @@ -23,7 +23,8 @@ #define BGP_SOCKET_SNDBUF_SIZE 65536 -extern int bgp_socket(unsigned short, const char *); +extern int bgp_socket(struct bgp *bgp, unsigned short port, + const char *address); extern void bgp_close(void); extern int bgp_connect(struct peer *); extern int bgp_getsockname(struct peer *); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 78e748fb6c..1d44eb76cd 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -3035,7 +3035,7 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, /* Create BGP server socket, if first instance. */ if (list_isempty(bm->bgp) && !bgp_option_check(BGP_OPT_NO_LISTEN)) { - if (bgp_socket(bm->port, bm->address) < 0) + if (bgp_socket(bgp, bm->port, bm->address) < 0) return BGP_ERR_INVALID_VALUE; } @@ -3337,11 +3337,12 @@ struct peer *peer_lookup(struct bgp *bgp, union sockunion *su) struct listnode *bgpnode, *nbgpnode; for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp)) { - /* Skip VRFs, this function will not be invoked without - * an instance + /* Skip VRFs Lite only, this function will not be + * invoked without an instance * when examining VRFs. */ - if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) + if ((bgp->inst_type == BGP_INSTANCE_TYPE_VRF) && + !vrf_is_mapped_on_netns(bgp->vrf_id)) continue; peer = hash_lookup(bgp->peerhash, &tmp_peer); From e5619c289af97beefefa36067afdef542e86bb0d Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 26 Jan 2018 12:25:34 +0100 Subject: [PATCH 24/41] bgpd: server socket is created for all enabled VRF Upon creation of BGP instances, server socket may or may not be created. In the case of VRF instances, if the VRF backend relies on NETNS, then a new server socket will be created for each BGP VRF instance. If the VRF backend relies on VRF LITE, then only one server socket will be enough. Moreover, At startup, with BGP VRF configuration, a server socket may not be created if VRF is not the default one or VRF is not recognized yet. Signed-off-by: Philippe Guibert --- bgpd/bgp_main.c | 2 + bgpd/bgp_network.c | 28 ++++++++++++ bgpd/bgp_network.h | 1 + bgpd/bgpd.c | 111 +++++++++++++++++++++++++++++++++++++++------ bgpd/bgpd.h | 3 ++ 5 files changed, 132 insertions(+), 13 deletions(-) diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index b5448b694e..717fe09762 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -261,6 +261,7 @@ static int bgp_vrf_enable(struct vrf *vrf) /* We have instance configured, link to VRF and make it "up". */ bgp_vrf_link(bgp, vrf); + bgp_handle_socket(bgp, vrf, old_vrf_id, true); /* Update any redistribute vrf bitmaps if the vrf_id changed */ if (old_vrf_id != bgp->vrf_id) bgp_update_redist_vrf_bitmaps(bgp, old_vrf_id); @@ -284,6 +285,7 @@ static int bgp_vrf_disable(struct vrf *vrf) bgp = bgp_lookup_by_name(vrf->name); if (bgp) { old_vrf_id = bgp->vrf_id; + bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, false); /* We have instance configured, unlink from VRF and make it * "down". */ bgp_vrf_unlink(bgp, vrf); diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index e3cca63905..59c59f924e 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -781,6 +781,32 @@ int bgp_socket(struct bgp *bgp, unsigned short port, const char *address) return 0; } +/* this function closes vrf socket + * this should be called only for vrf socket with netns backend + */ +void bgp_close_vrf_socket(struct bgp *bgp) +{ + struct listnode *node, *next; + struct bgp_listener *listener; + + if (!bgp) + return; + + if (bm->listen_sockets == NULL) + return; + + for (ALL_LIST_ELEMENTS(bm->listen_sockets, node, next, listener)) { + if (listener->bgp == bgp) { + thread_cancel(listener->thread); + close(listener->fd); + listnode_delete(bm->listen_sockets, listener); + XFREE(MTYPE_BGP_LISTENER, listener); + } + } +} + +/* this function closes main socket + */ void bgp_close(void) { struct listnode *node, *next; @@ -790,6 +816,8 @@ void bgp_close(void) return; for (ALL_LIST_ELEMENTS(bm->listen_sockets, node, next, listener)) { + if (listener->bgp) + continue; thread_cancel(listener->thread); close(listener->fd); listnode_delete(bm->listen_sockets, listener); diff --git a/bgpd/bgp_network.h b/bgpd/bgp_network.h index 5691b73e22..f18484e000 100644 --- a/bgpd/bgp_network.h +++ b/bgpd/bgp_network.h @@ -25,6 +25,7 @@ extern int bgp_socket(struct bgp *bgp, unsigned short port, const char *address); +extern void bgp_close_vrf_socket(struct bgp *bgp); extern void bgp_close(void); extern int bgp_connect(struct peer *); extern int bgp_getsockname(struct peer *); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 1d44eb76cd..07b9df31f2 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -101,6 +101,42 @@ static void bgp_if_finish(struct bgp *bgp); extern struct zclient *zclient; +/* handle main socket creation or deletion */ +static int bgp_check_main_socket(bool create, struct bgp *bgp) +{ + static int bgp_server_main_created; + struct listnode *bgpnode, *nbgpnode; + struct bgp *bgp_temp; + + if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) + return 0; + if (create == true) { + if (bgp_server_main_created) + return 0; + if (bgp_socket(bgp, bm->port, bm->address) < 0) + return BGP_ERR_INVALID_VALUE; + bgp_server_main_created = 1; + return 0; + } + if (!bgp_server_main_created) + return 0; + /* only delete socket on some cases */ + for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp_temp)) { + /* do not count with current bgp */ + if (bgp_temp == bgp) + continue; + /* if other instance non VRF, do not delete socket */ + if (bgp_temp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) + return 0; + /* vrf lite, do not delete socket */ + if (!vrf_is_mapped_on_netns(bgp_temp->vrf_id)) + return 0; + } + bgp_close(); + bgp_server_main_created = 0; + return 0; +} + void bgp_session_reset(struct peer *peer) { if (peer->doppelganger && (peer->doppelganger->status != Deleted) @@ -2981,11 +3017,67 @@ struct bgp *bgp_lookup_by_vrf_id(vrf_id_t vrf_id) return (vrf->info) ? (struct bgp *)vrf->info : NULL; } +/* handle socket creation or deletion, if necessary + * this is called for all new BGP instances + */ +int bgp_handle_socket(struct bgp *bgp, struct vrf *vrf, + vrf_id_t old_vrf_id, bool create) +{ + int ret = 0; + + /* Create BGP server socket, if listen mode not disabled */ + if (!bgp || bgp_option_check(BGP_OPT_NO_LISTEN)) + return 0; + if (bgp->name + && bgp->inst_type == BGP_INSTANCE_TYPE_VRF + && vrf) { + /* + * suppress vrf socket + */ + if (create == FALSE) { + if (vrf_is_mapped_on_netns(vrf->vrf_id)) + bgp_close_vrf_socket(bgp); + else + ret = bgp_check_main_socket(create, bgp); + return ret; + } + /* do nothing + * if vrf_id did not change + */ + if (vrf->vrf_id == old_vrf_id) + return 0; + if (old_vrf_id != VRF_UNKNOWN) { + /* look for old socket. close it. */ + bgp_close_vrf_socket(bgp); + } + /* if backend is not yet identified ( VRF_UNKNOWN) then + * creation will be done later + */ + if (vrf->vrf_id == VRF_UNKNOWN) + return 0; + /* if BGP VRF instance requested + * if backend is NETNS, create BGP server socket in the NETNS + */ + if (vrf_is_mapped_on_netns(bgp->vrf_id)) { + ret = bgp_socket(bgp, bm->port, bm->address); + if (ret < 0) + return BGP_ERR_INVALID_VALUE; + return 0; + } + } + /* if BGP VRF instance requested or VRF lite backend + * if BGP non VRF instance, create it + * if not already done + */ + return bgp_check_main_socket(create, bgp); +} + /* Called from VTY commands. */ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, enum bgp_instance_type inst_type) { struct bgp *bgp; + struct vrf *vrf = NULL; /* Multiple instance check. */ if (bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { @@ -3033,25 +3125,19 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, bgp->t_rmap_def_originate_eval = NULL; - /* Create BGP server socket, if first instance. */ - if (list_isempty(bm->bgp) && !bgp_option_check(BGP_OPT_NO_LISTEN)) { - if (bgp_socket(bgp, bm->port, bm->address) < 0) - return BGP_ERR_INVALID_VALUE; - } - - listnode_add(bm->bgp, bgp); - /* If Default instance or VRF, link to the VRF structure, if present. */ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT || bgp->inst_type == BGP_INSTANCE_TYPE_VRF) { - struct vrf *vrf; - vrf = bgp_vrf_lookup_by_instance_type(bgp); if (vrf) bgp_vrf_link(bgp, vrf); } + /* BGP server socket already processed if BGP instance + * already part of the list + */ + bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, true); + listnode_add(bm->bgp, bgp); - /* Register with Zebra, if needed */ if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) bgp_zebra_instance_register(bgp); @@ -3188,8 +3274,6 @@ int bgp_delete(struct bgp *bgp) * routes to be processed still referencing the struct bgp. */ listnode_delete(bm->bgp, bgp); - if (list_isempty(bm->bgp)) - bgp_close(); /* Deregister from Zebra, if needed */ if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) @@ -3199,6 +3283,7 @@ int bgp_delete(struct bgp *bgp) bgp_if_finish(bgp); vrf = bgp_vrf_lookup_by_instance_type(bgp); + bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, false); if (vrf) bgp_vrf_unlink(bgp, vrf); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 91a9f11620..7f55b753ab 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -1355,6 +1355,9 @@ extern void bgp_instance_up(struct bgp *); extern void bgp_instance_down(struct bgp *); extern int bgp_delete(struct bgp *); +extern int bgp_handle_socket(struct bgp *bgp, struct vrf *vrf, + vrf_id_t old_vrf_id, bool create); + extern int bgp_flag_set(struct bgp *, int); extern int bgp_flag_unset(struct bgp *, int); extern int bgp_flag_check(struct bgp *, int); From f62abc7d6577a44a67111b25a70d40170e2790af Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 30 Jan 2018 15:30:10 +0100 Subject: [PATCH 25/41] bgpd: do not start BGP VRF peer connection, if VRF not unknown Upon starting a BGP VRF instance, the server socket is not created, because the VRF ID is not known, and then underlying VRF backend is not ready yet. Because of that, the peer connection attempt will not be started before. Signed-off-by: Philippe Guibert --- bgpd/bgp_fsm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 79f4b1c91a..de11a98a20 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -289,7 +289,8 @@ void bgp_timer_set(struct peer *peer) /* First entry point of peer's finite state machine. In Idle status start timer is on unless peer is shutdown or peer is inactive. All other timer must be turned off */ - if (BGP_PEER_START_SUPPRESSED(peer) || !peer_active(peer)) { + if (BGP_PEER_START_SUPPRESSED(peer) || !peer_active(peer) + || peer->bgp->vrf_id == VRF_UNKNOWN) { BGP_TIMER_OFF(peer->t_start); } else { BGP_TIMER_ON(peer->t_start, bgp_start_timer, From 0268f30e3cab287bb10e84f7663098ed9cbd50c7 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 29 Jan 2018 16:14:46 +0100 Subject: [PATCH 26/41] zebra: speed ioctl read() with interfaces from various NETNS When interfaces are located on different NETNS ( different VRF), then a switch from netns context is necessary when calling setns(). The VRF apis to switch and switch back are called, so that the ioctl will work accordingly. Signed-off-by: Philippe Guibert --- zebra/if_netlink.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 6897bd4ee2..a02533c1fe 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -66,6 +66,7 @@ #include "zebra/kernel_netlink.h" #include "zebra/if_netlink.h" +extern struct zebra_privs_t zserv_privs; /* Note: on netlink systems, there should be a 1-to-1 mapping between interface names and ifindex values. */ @@ -344,12 +345,14 @@ static void netlink_vrf_change(struct nlmsghdr *h, struct rtattr *tb, } } -static int get_iflink_speed(const char *ifname) +static int get_iflink_speed(struct interface *interface) { struct ifreq ifdata; struct ethtool_cmd ecmd; int sd; int rc; + int ret, saved_errno; + const char *ifname = interface->name; /* initialize struct */ memset(&ifdata, 0, sizeof(ifdata)); @@ -363,16 +366,29 @@ static int get_iflink_speed(const char *ifname) ifdata.ifr_data = (__caddr_t)&ecmd; /* use ioctl to get IP address of an interface */ - sd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); + sd = vrf_socket(PF_INET, SOCK_DGRAM, IPPROTO_IP, interface->vrf_id); if (sd < 0) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("Failure to read interface %s speed: %d %s", ifname, errno, safe_strerror(errno)); return 0; } - /* Get the current link state for the interface */ + ret = vrf_switch_to_netns(interface->vrf_id); + if (ret < 0) + zlog_err("%s: Can't switch to VRF %u (%s)", + __func__, interface->vrf_id, safe_strerror(errno)); rc = ioctl(sd, SIOCETHTOOL, (char *)&ifdata); + saved_errno = errno; + ret = vrf_switchback_to_initial(); + if (ret < 0) + zlog_err("%s: Can't switchback from VRF %u (%s)", + __func__, interface->vrf_id, safe_strerror(errno)); + errno = saved_errno; + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); if (rc < 0) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( @@ -389,7 +405,7 @@ static int get_iflink_speed(const char *ifname) uint32_t kernel_get_speed(struct interface *ifp) { - return get_iflink_speed(ifp->name); + return get_iflink_speed(ifp); } static int netlink_extract_bridge_info(struct rtattr *link_data, @@ -647,7 +663,7 @@ static int netlink_interface(struct sockaddr_nl *snl, struct nlmsghdr *h, SET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK); ifp->mtu6 = ifp->mtu = *(uint32_t *)RTA_DATA(tb[IFLA_MTU]); ifp->metric = 0; - ifp->speed = get_iflink_speed(name); + ifp->speed = get_iflink_speed(ifp); ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN; if (desc) From 4db21619552b3f6823bf1ae8103eac82aab628f7 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 22 Feb 2018 19:10:32 +0100 Subject: [PATCH 27/41] zebra: handle some ioctl operations for VRF A new API is available for interface ioctl operations on Linux: vrf_if_ioctl. This is the unified API that permits doing ioctl operations on a per interface basis. Signed-off-by: Philippe Guibert --- zebra/if_ioctl.c | 2 +- zebra/ioctl.c | 47 +++++++++++++++++++++++++++++++++++++------ zebra/ioctl.h | 1 + zebra/ioctl_solaris.c | 6 ++++++ 4 files changed, 49 insertions(+), 7 deletions(-) diff --git a/zebra/if_ioctl.c b/zebra/if_ioctl.c index 1d108886de..09fc085018 100644 --- a/zebra/if_ioctl.c +++ b/zebra/if_ioctl.c @@ -146,7 +146,7 @@ static int if_get_hwaddr(struct interface *ifp) ifreq.ifr_addr.sa_family = AF_INET; /* Fetch Hardware address if available. */ - ret = if_ioctl(SIOCGIFHWADDR, (caddr_t)&ifreq); + ret = vrf_if_ioctl(SIOCGIFHWADDR, (caddr_t)&ifreq, ifp->vrf_id); if (ret < 0) ifp->hw_addr_len = 0; else { diff --git a/zebra/ioctl.c b/zebra/ioctl.c index 8e3a1d1a03..d07d37056e 100644 --- a/zebra/ioctl.c +++ b/zebra/ioctl.c @@ -59,6 +59,7 @@ int if_ioctl(u_long request, caddr_t buffer) sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { int save_errno = errno; + if (zserv_privs.change(ZPRIVS_LOWER)) zlog_err("Can't lower privileges"); zlog_err("Cannot create UDP socket: %s", @@ -78,6 +79,39 @@ int if_ioctl(u_long request, caddr_t buffer) return 0; } +/* call ioctl system call */ +int vrf_if_ioctl(u_long request, caddr_t buffer, vrf_id_t vrf_id) +{ + int sock; + int ret; + int err = 0; + + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); + sock = vrf_socket(AF_INET, SOCK_DGRAM, 0, vrf_id, NULL); + if (sock < 0) { + int save_errno = errno; + + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); + zlog_err("Cannot create UDP socket: %s", + safe_strerror(save_errno)); + exit(1); + } + ret = vrf_ioctl(vrf_id, sock, request, buffer); + if (ret < 0) + err = errno; + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); + close(sock); + + if (ret < 0) { + errno = err; + return ret; + } + return 0; +} + #ifndef HAVE_NETLINK static int if_ioctl_ipv6(u_long request, caddr_t buffer) { @@ -90,6 +124,7 @@ static int if_ioctl_ipv6(u_long request, caddr_t buffer) sock = socket(AF_INET6, SOCK_DGRAM, 0); if (sock < 0) { int save_errno = errno; + if (zserv_privs.change(ZPRIVS_LOWER)) zlog_err("Can't lower privileges"); zlog_err("Cannot create IPv6 datagram socket: %s", @@ -122,7 +157,7 @@ void if_get_metric(struct interface *ifp) ifreq_set_name(&ifreq, ifp); - if (if_ioctl(SIOCGIFMETRIC, (caddr_t)&ifreq) < 0) + if (vrf_if_ioctl(SIOCGIFMETRIC, (caddr_t)&ifreq, ifp->vrf_id) < 0) return; ifp->metric = ifreq.ifr_metric; if (ifp->metric == 0) @@ -140,7 +175,7 @@ void if_get_mtu(struct interface *ifp) ifreq_set_name(&ifreq, ifp); #if defined(SIOCGIFMTU) - if (if_ioctl(SIOCGIFMTU, (caddr_t)&ifreq) < 0) { + if (vrf_if_ioctl(SIOCGIFMTU, (caddr_t)&ifreq, ifp->vrf_id) < 0) { zlog_info("Can't lookup mtu by ioctl(SIOCGIFMTU)"); ifp->mtu6 = ifp->mtu = -1; return; @@ -376,9 +411,9 @@ void if_get_flags(struct interface *ifp) ifreq_set_name(&ifreq, ifp); - ret = if_ioctl(SIOCGIFFLAGS, (caddr_t)&ifreq); + ret = vrf_if_ioctl(SIOCGIFFLAGS, (caddr_t)&ifreq, ifp->vrf_id); if (ret < 0) { - zlog_err("if_ioctl(SIOCGIFFLAGS) failed: %s", + zlog_err("vrf_if_ioctl(SIOCGIFFLAGS) failed: %s", safe_strerror(errno)); return; } @@ -423,7 +458,7 @@ int if_set_flags(struct interface *ifp, uint64_t flags) ifreq.ifr_flags = ifp->flags; ifreq.ifr_flags |= flags; - ret = if_ioctl(SIOCSIFFLAGS, (caddr_t)&ifreq); + ret = vrf_if_ioctl(SIOCSIFFLAGS, (caddr_t)&ifreq, ifp->vrf_id); if (ret < 0) { zlog_info("can't set interface flags"); @@ -444,7 +479,7 @@ int if_unset_flags(struct interface *ifp, uint64_t flags) ifreq.ifr_flags = ifp->flags; ifreq.ifr_flags &= ~flags; - ret = if_ioctl(SIOCSIFFLAGS, (caddr_t)&ifreq); + ret = vrf_if_ioctl(SIOCSIFFLAGS, (caddr_t)&ifreq, ifp->vrf_id); if (ret < 0) { zlog_info("can't unset interface flags"); diff --git a/zebra/ioctl.h b/zebra/ioctl.h index 02f8e6b880..1a6e14ed4d 100644 --- a/zebra/ioctl.h +++ b/zebra/ioctl.h @@ -25,6 +25,7 @@ /* Prototypes. */ extern void ifreq_set_name(struct ifreq *, struct interface *); extern int if_ioctl(u_long, caddr_t); +extern int vrf_if_ioctl(u_long request, caddr_t buffer, vrf_id_t vrf_id); extern int if_set_flags(struct interface *, uint64_t); extern int if_unset_flags(struct interface *, uint64_t); diff --git a/zebra/ioctl_solaris.c b/zebra/ioctl_solaris.c index e8b65925f8..f429c42440 100644 --- a/zebra/ioctl_solaris.c +++ b/zebra/ioctl_solaris.c @@ -30,6 +30,7 @@ #include "log.h" #include "privs.h" #include "vty.h" +#include "vrf.h" #include "zebra/rib.h" #include "zebra/rt.h" @@ -44,6 +45,11 @@ void lifreq_set_name(struct lifreq *lifreq, const char *ifname) strncpy(lifreq->lifr_name, ifname, IFNAMSIZ); } +int vrf_if_ioctl(u_long request, caddr_t buffer, vrf_id_t vrf_id) +{ + return if_ioctl(request, buffer); +} + /* call ioctl system call */ int if_ioctl(u_long request, caddr_t buffer) { From 822160dbe6f293a3c94487b4d0abe0f1d9883cca Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 1 Feb 2018 18:47:21 +0100 Subject: [PATCH 28/41] doc: add vrfwnetns keyword for zebra in doc The option to enable VRF backend is documented. Signed-off-by: Philippe Guibert --- doc/zebra.8.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/zebra.8.in b/doc/zebra.8.in index 7f4a81b1a0..3e1d10e068 100644 --- a/doc/zebra.8.in +++ b/doc/zebra.8.in @@ -92,6 +92,11 @@ maximum before starting zebra. Note that this affects Linux only. .TP +\fB\-n\fR, \fB\-\-vrfwnetns \fR\fIEnable namespace VRF\fR +Enable namespace VRF backend. By default, the VRF backend relies on VRF-lite +support from Linux devices. This option permits discovering using Linux named +Netns and map it to FRR VRF contexts. +.TP \fB\-M\fR, \fB\-\-module \fR\fImodule:options\fR Load a module at startup. May be specified more than once. The \fBsnmp\fR and \fBfpm\fR modules may be From e26aedbe0b569b3e88718c457210051ba2eed437 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 5 Feb 2018 16:23:42 +0100 Subject: [PATCH 29/41] lib: split logicalrouter and vrf netns feature This split is introducing logicalrouter.[ch] as the file that contains the vty commands to configure logical router feature. The split has as consequence that the backend of logical router is linux_netns.c formerly called ns.c. The same relationship exists between VRF and its backend which may be linux_netns.c file. The split is adapting ns and vrf fiels so as to : - clarify header - ensure that the daemon persepctive, the feature VRF or logical router is called instead of calling directly ns. - this implies that VRF will call NS apis, as logical router does. Also, like it is done for default NS and default VRF, the associated VRF is enabled first, before NETNS is enabled, so that zvrf->zns pointer is valid when NETNS discovery applies. Also, other_netns.c file is a stub handler that will be used for non linux systems. As NETNS feature is only used by Linux, some BSD systems may want to use the same backend API to benefit from NETNS. This is what that file has been done. Signed-off-by: Philippe Guibert --- lib/command.c | 6 +- lib/command.h | 2 +- lib/logicalrouter.c | 159 ++++++++++ lib/logicalrouter.h | 41 +++ lib/{ns.c => netns_linux.c} | 606 ++++++++++++------------------------ lib/netns_other.c | 165 ++++++++++ lib/ns.h | 83 ++++- lib/subdir.am | 5 +- lib/vrf.c | 172 ++++++++-- lib/vrf.h | 88 ++++-- 10 files changed, 837 insertions(+), 490 deletions(-) create mode 100644 lib/logicalrouter.c create mode 100644 lib/logicalrouter.h rename lib/{ns.c => netns_linux.c} (51%) create mode 100644 lib/netns_other.c diff --git a/lib/command.c b/lib/command.c index d17f2c3d48..10996d5dd4 100644 --- a/lib/command.c +++ b/lib/command.c @@ -62,7 +62,7 @@ const char *node_names[] = { "aaa", // AAA_NODE, "keychain", // KEYCHAIN_NODE, "keychain key", // KEYCHAIN_KEY_NODE, - "logical-router", // NS_NODE, + "logical-router", // LOGICALROUTER_NODE, "vrf", // VRF_NODE, "interface", // INTERFACE_NODE, "zebra", // ZEBRA_NODE, @@ -1291,7 +1291,7 @@ void cmd_exit(struct vty *vty) break; case INTERFACE_NODE: case PW_NODE: - case NS_NODE: + case LOGICALROUTER_NODE: case VRF_NODE: case ZEBRA_NODE: case BGP_NODE: @@ -1376,7 +1376,7 @@ DEFUN (config_end, case CONFIG_NODE: case INTERFACE_NODE: case PW_NODE: - case NS_NODE: + case LOGICALROUTER_NODE: case VRF_NODE: case ZEBRA_NODE: case RIP_NODE: diff --git a/lib/command.h b/lib/command.h index e1edc1ef32..269318989f 100644 --- a/lib/command.h +++ b/lib/command.h @@ -85,7 +85,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. */ + LOGICALROUTER_NODE, /* Logical-Router node. */ VRF_NODE, /* VRF mode node. */ INTERFACE_NODE, /* Interface mode node. */ ZEBRA_NODE, /* zebra connection node. */ diff --git a/lib/logicalrouter.c b/lib/logicalrouter.c new file mode 100644 index 0000000000..4dc99d304f --- /dev/null +++ b/lib/logicalrouter.c @@ -0,0 +1,159 @@ +/* + * Logical Router functions. + * Copyright (C) 2018 6WIND S.A. + * + * 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 + +#include "ns.h" +#include "log.h" +#include "memory.h" + +#include "command.h" +#include "vty.h" +#include "logicalrouter.h" + +/* Comment that useless define to avoid compilation error + * in order to use it, one could provide the kind of NETNS to NS backend + * so that the allocation will match the logical router + * DEFINE_MTYPE_STATIC(LIB, LOGICALROUTER, "LogicalRouter Context") + */ +DEFINE_MTYPE_STATIC(LIB, LOGICALROUTER_NAME, "Logical Router Name") + +/* Logical Router node has no interface. */ +static struct cmd_node logicalrouter_node = {LOGICALROUTER_NODE, "", + 1}; + +static int logicalrouter_backend; + +/* Get a NS. If not found, create one. */ +static struct ns *logicalrouter_get(ns_id_t ns_id) +{ + struct ns *ns; + + ns = ns_lookup(ns_id); + if (ns) + return (ns); + ns = ns_get_created(ns, NULL, ns_id); + return ns; +} + +static int logicalrouter_is_backend_netns(void) +{ + return (logicalrouter_backend == LOGICALROUTER_BACKEND_NETNS); +} + + +DEFUN_NOSH (logicalrouter, + logicalrouter_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") +{ + int idx_number = 1; + int idx_name = 3; + ns_id_t ns_id; + struct ns *ns = NULL; + char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); + + if (!pathname) + return CMD_WARNING_CONFIG_FAILED; + + ns_id = strtoul(argv[idx_number]->arg, NULL, 10); + ns = logicalrouter_get(ns_id); + + if (ns->name && strcmp(ns->name, pathname) != 0) { + vty_out(vty, "NS %u is already configured with NETNS %s\n", + ns->ns_id, ns->name); + return CMD_WARNING; + } + + if (!ns->name) + ns->name = XSTRDUP(MTYPE_LOGICALROUTER_NAME, pathname); + + if (!ns_enable(ns, NULL)) { + vty_out(vty, "Can not associate NS %u with NETNS %s\n", + ns->ns_id, ns->name); + return CMD_WARNING_CONFIG_FAILED; + } + + return CMD_SUCCESS; +} + +DEFUN (no_logicalrouter, + no_logicalrouter_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") +{ + int idx_number = 2; + int idx_name = 4; + ns_id_t ns_id; + struct ns *ns = NULL; + char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); + + if (!pathname) + return CMD_WARNING_CONFIG_FAILED; + + ns_id = strtoul(argv[idx_number]->arg, NULL, 10); + ns = ns_lookup(ns_id); + + if (!ns) { + vty_out(vty, "NS %u is not found\n", ns_id); + return CMD_SUCCESS; + } + + if (ns->name && strcmp(ns->name, pathname) != 0) { + vty_out(vty, "Incorrect NETNS file name\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + ns_disable(ns); + + if (ns->name) { + XFREE(MTYPE_LOGICALROUTER_NAME, ns->name); + ns->name = NULL; + } + + return CMD_SUCCESS; +} + +/* Initialize NS module. */ +void logicalrouter_init(int (*writefunc)(struct vty *vty)) +{ + if (ns_have_netns() && logicalrouter_is_backend_netns()) { + /* Install LogicalRouter commands. */ + install_node(&logicalrouter_node, writefunc); + install_element(CONFIG_NODE, &logicalrouter_cmd); + install_element(CONFIG_NODE, &no_logicalrouter_cmd); + } +} + +void logicalrouter_terminate(void) +{ + ns_terminate(); +} + +void logicalrouter_configure_backend(int backend_netns) +{ + logicalrouter_backend = backend_netns; +} diff --git a/lib/logicalrouter.h b/lib/logicalrouter.h new file mode 100644 index 0000000000..5a0780c009 --- /dev/null +++ b/lib/logicalrouter.h @@ -0,0 +1,41 @@ +/* + * Logical Router related header. + * Copyright (C) 2018 6WIND S.A. + * + * 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 _ZEBRA_LOGICAL_ROUTER_H +#define _ZEBRA_LOGICAL_ROUTER_H + +/* Logical Router Backend defines */ +#define LOGICALROUTER_BACKEND_OFF 0 +#define LOGICALROUTER_BACKEND_NETNS 1 + +/* + * Logical Router initializer/destructor + */ +extern void logicalrouter_init(int (*writefunc)(struct vty *vty)); +extern void logicalrouter_terminate(void); + +/* used to configure backend for logical router + * Currently, the whole NETNS feature is exclusively shared + * between logical router and VRF backend NETNS + * However, when logical router feature will be available, + * one can think of having exclusivity only per NETNS + */ +extern void logicalrouter_configure_backend(int backend_netns); + +#endif /*_ZEBRA_LOGICAL_ROUTER_H*/ diff --git a/lib/ns.c b/lib/netns_linux.c similarity index 51% rename from lib/ns.c rename to lib/netns_linux.c index 17d70a12fe..025862440a 100644 --- a/lib/ns.c +++ b/lib/netns_linux.c @@ -43,9 +43,8 @@ DEFINE_MTYPE_STATIC(LIB, NS, "NetNS Context") DEFINE_MTYPE_STATIC(LIB, NS_NAME, "NetNS Name") -static __inline int ns_compare(const struct ns *, const struct ns *); -static struct ns *ns_lookup(ns_id_t); -static struct ns *ns_lookup_name(const char *); +static inline int ns_compare(const struct ns *ns, const struct ns *ns2); +static struct ns *ns_lookup_name_internal(const char *name); RB_GENERATE(ns_head, ns, entry, ns_compare) @@ -56,7 +55,8 @@ 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) */ +#define CLONE_NEWNET 0x40000000 +/* New network namespace (lo, device, names sockets, etc) */ #endif #ifndef HAVE_SETNS @@ -69,7 +69,7 @@ static inline int setns(int fd, int nstype) return -1; #endif } -#endif /* HAVE_SETNS */ +#endif /* !HAVE_SETNS */ #ifdef HAVE_NETNS static int have_netns_enabled = -1; @@ -108,73 +108,163 @@ struct ns_master { }; static int ns_is_enabled(struct ns *ns); -static int ns_enable(struct ns *ns); -static void ns_disable(struct ns *ns); -static void ns_get_created(struct ns *ns); -static __inline int ns_compare(const struct ns *a, const struct ns *b) +static inline int ns_compare(const struct ns *a, const struct ns *b) { return (a->ns_id - b->ns_id); } -static void ns_get_created(struct ns *ns) +/* Look up a NS by identifier. */ +static struct ns *ns_lookup_internal(ns_id_t ns_id) { + struct ns ns; + + ns.ns_id = ns_id; + return RB_FIND(ns_head, &ns_tree, &ns); +} + +/* Look up a NS by name */ +static struct ns *ns_lookup_name_internal(const char *name) +{ + struct ns *ns = NULL; + + RB_FOREACH(ns, ns_head, &ns_tree) { + if (ns->name != NULL) { + if (strcmp(name, ns->name) == 0) + return ns; + } + } + return NULL; +} + +static struct ns *ns_get_created_internal(struct ns *ns, char *name, + ns_id_t ns_id) +{ + int created = 0; /* * Initialize interfaces. - * - * I'm not sure if this belongs here or in - * the vrf code. */ - // if_init (&ns->iflist); - + if (!ns && !name && ns_id != NS_UNKNOWN) + ns = ns_lookup_internal(ns_id); + if (!ns && name) + ns = ns_lookup_name_internal(name); + if (!ns) { + ns = XCALLOC(MTYPE_NS, sizeof(struct ns)); + ns->ns_id = ns_id; + if (name) + ns->name = XSTRDUP(MTYPE_NS_NAME, name); + ns->fd = -1; + RB_INSERT(ns_head, &ns_tree, ns); + created = 1; + } + if (ns_id != ns->ns_id) { + RB_REMOVE(ns_head, &ns_tree, ns); + ns->ns_id = ns_id; + RB_INSERT(ns_head, &ns_tree, ns); + } + if (!created) + return ns; if (ns->ns_id != NS_UNKNOWN) zlog_info("NS %u is created.", ns->ns_id); else zlog_info("NS %s is created.", ns->name); if (ns_master.ns_new_hook) (*ns_master.ns_new_hook) (ns); - return; -} - -/* Get a NS. If not found, create one. */ -static struct ns *ns_get(ns_id_t ns_id) -{ - struct ns *ns; - - ns = ns_lookup(ns_id); - if (ns) - return (ns); - - ns = XCALLOC(MTYPE_NS, sizeof(struct ns)); - ns->ns_id = ns_id; - ns->fd = -1; - RB_INSERT(ns_head, &ns_tree, ns); - ns_get_created(ns); return ns; } -/* Get a NS. If not found, create one. */ -static struct ns *ns_get_by_name(char *ns_name) +/* + * 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_internal(struct ns *ns, void (*func)(ns_id_t, void *)) { - struct ns *ns; + if (!ns_is_enabled(ns)) { + if (have_netns()) { + ns->fd = open(ns->name, O_RDONLY); + } else { + ns->fd = -2; + /* Remember ns_enable_hook has been called */ + errno = -ENOTSUP; + } - ns = ns_lookup_name(ns_name); - if (ns) - return (ns); + if (!ns_is_enabled(ns)) { + zlog_err("Can not enable NS %u: %s!", ns->ns_id, + safe_strerror(errno)); + return 0; + } - ns = XCALLOC(MTYPE_NS, sizeof(struct ns)); - ns->ns_id = NS_UNKNOWN; - ns->name = XSTRDUP(MTYPE_NS_NAME, ns_name); - ns->fd = -1; - RB_INSERT(ns_head, &ns_tree, ns); + /* Non default NS. leave */ + if (ns->ns_id == NS_UNKNOWN) { + zlog_err("Can not enable NS %s %u: Invalid NSID", + ns->name, ns->ns_id); + return 0; + } + if (func) + func(ns->ns_id, (void *)ns->vrf_ctxt); + if (have_netns()) + zlog_info("NS %u is associated with NETNS %s.", + ns->ns_id, ns->name); - /* ns_id not initialised */ - ns_get_created(ns); - return ns; + zlog_info("NS %u is enabled.", ns->ns_id); + /* zebra first receives NS enable event, + * then VRF enable event + */ + if (ns_master.ns_enable_hook) + (*ns_master.ns_enable_hook)(ns); + } + + return 1; +} + +/* + * 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) +{ + if (have_netns()) + return ns && ns->fd >= 0; + else + return ns && ns->fd == -2 && ns->ns_id == NS_DEFAULT; +} + +/* + * 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_internal(struct ns *ns) +{ + if (ns_is_enabled(ns)) { + zlog_info("NS %u is to be disabled.", ns->ns_id); + + if (ns_master.ns_disable_hook) + (*ns_master.ns_disable_hook)(ns); + + if (have_netns()) + close(ns->fd); + + ns->fd = -1; + } +} + +struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id) +{ + return ns_get_created_internal(ns, name, ns_id); +} + +int ns_have_netns(void) +{ + return have_netns(); } /* Delete a NS. This is called in ns_terminate(). */ -static void ns_delete(struct ns *ns) +void ns_delete(struct ns *ns) { zlog_info("NS %u is to be deleted.", ns->ns_id); @@ -196,23 +286,36 @@ static void ns_delete(struct ns *ns) XFREE(MTYPE_NS, ns); } -/* Look up a NS by identifier. */ -static struct ns *ns_lookup(ns_id_t ns_id) -{ - struct ns ns; - ns.ns_id = ns_id; - return (RB_FIND(ns_head, &ns_tree, &ns)); -} - /* Look up the data pointer of the specified VRF. */ void * ns_info_lookup(ns_id_t ns_id) { - struct ns *ns = ns_lookup(ns_id); + struct ns *ns = ns_lookup_internal(ns_id); return ns ? ns->info : NULL; } +/* Look up a NS by name */ +struct ns *ns_lookup_name(const char *name) +{ + return ns_lookup_name_internal(name); +} + +int ns_enable(struct ns *ns, void (*func)(ns_id_t, void *)) +{ + return ns_enable_internal(ns, func); +} + +void ns_disable(struct ns *ns) +{ + return ns_disable_internal(ns); +} + +struct ns *ns_lookup(ns_id_t ns_id) +{ + return ns_lookup_internal(ns_id); +} + void ns_walk_func(int (*func)(struct ns *)) { struct ns *ns = NULL; @@ -228,105 +331,6 @@ const char *ns_get_name(struct ns *ns) return ns->name; } -/* Look up a NS by name */ -static struct ns *ns_lookup_name(const char *name) -{ - struct ns *ns = NULL; - - RB_FOREACH(ns, ns_head, &ns_tree) { - if (ns->name != NULL) { - if (strcmp(name, ns->name) == 0) - return ns; - } - } - return NULL; -} - -/* - * 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) -{ - if (have_netns()) - return ns && ns->fd >= 0; - else - return ns && ns->fd == -2 && 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) -{ - int vrf_on = 0; - - if (!ns_is_enabled(ns)) { - 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)) { - zlog_err("Can not enable NS %u: %s!", ns->ns_id, - safe_strerror(errno)); - return 0; - } - - /* Non default NS. leave */ - if (ns->ns_id == NS_UNKNOWN) { - zlog_err("Can not enable NS %s %u: Invalid NSID", - ns->name, ns->ns_id); - return 0; - } - vrf_on = vrf_update_vrf_id((vrf_id_t)ns->ns_id, - (struct vrf *)ns->vrf_ctxt); - 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); - /* zebra first receives NS enable event, - * then VRF enable event - */ - if (ns_master.ns_enable_hook) - (*ns_master.ns_enable_hook)(ns); - if (vrf_on == 1) - vrf_enable((struct vrf *)ns->vrf_ctxt); - } - - return 1; -} - -/* - * 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); - - if (ns_master.ns_disable_hook) - (*ns_master.ns_disable_hook)(ns); - - if (have_netns()) - close(ns->fd); - - ns->fd = -1; - } -} - - /* Add a NS hook. Please add hooks before calling ns_init(). */ void ns_add_hook(int type, int (*func)(struct ns *)) { @@ -360,9 +364,10 @@ char *ns_netns_pathname(struct vty *vty, const char *name) if (name[0] == '/') /* absolute pathname */ result = realpath(name, pathname); - else /* relevant 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); } @@ -379,294 +384,66 @@ char *ns_netns_pathname(struct vty *vty, const char *name) check_base = basename(pathname); if (check_base != NULL && strlen(check_base) + 1 > NS_NAMSIZ) { if (vty) - vty_out(vty, "NS name (%s) invalid:" - " too long( %d needed)\n", + vty_out(vty, "NS name (%s) invalid: too long (>%d)\n", check_base, NS_NAMSIZ-1); else - zlog_warn("NS name (%s) invalid:" - " too long ( %d needed)", + zlog_warn("NS name (%s) invalid: too long (>%d)", check_base, NS_NAMSIZ-1); return NULL; } return pathname; } -DEFUN_NOSH (ns_logicalrouter, - ns_logicalrouter_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") -{ - int idx_number = 1; - int idx_name = 3; - ns_id_t ns_id; - struct ns *ns = NULL; - char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); - - if (!pathname) - return CMD_WARNING_CONFIG_FAILED; - - ns_id = strtoul(argv[idx_number]->arg, NULL, 10); - ns = ns_get(ns_id); - - if (ns->name && strcmp(ns->name, pathname) != 0) { - vty_out(vty, "NS %u is already configured with NETNS %s\n", - ns->ns_id, ns->name); - 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\n", - ns->ns_id, ns->name); - return CMD_WARNING_CONFIG_FAILED; - } - - return CMD_SUCCESS; -} - -static struct cmd_node logicalrouter_node = {NS_NODE, "", /* NS node has no interface. */ - 1}; - -DEFUN (no_ns_logicalrouter, - no_ns_logicalrouter_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") -{ - int idx_number = 2; - int idx_name = 4; - ns_id_t ns_id; - struct ns *ns = NULL; - char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); - - if (!pathname) - return CMD_WARNING_CONFIG_FAILED; - - ns_id = strtoul(argv[idx_number]->arg, NULL, 10); - ns = ns_lookup(ns_id); - - if (!ns) { - vty_out(vty, "NS %u is not found\n", ns_id); - return CMD_SUCCESS; - } - - if (ns->name && strcmp(ns->name, pathname) != 0) { - vty_out(vty, "Incorrect NETNS file name\n"); - return CMD_WARNING_CONFIG_FAILED; - } - - ns_disable(ns); - - if (ns->name) { - XFREE(MTYPE_NS_NAME, ns->name); - ns->name = NULL; - } - - return CMD_SUCCESS; -} - -int ns_handler_create(struct vty *vty, struct vrf *vrf, - char *pathname, ns_id_t ns_id) -{ - struct ns *ns = NULL; - - if (!vrf) - return CMD_WARNING_CONFIG_FAILED; - if (vrf->vrf_id != VRF_UNKNOWN && vrf->ns_ctxt == NULL) { - if (vty) - vty_out(vty, - "VRF %u is already configured with VRF %s\n", - vrf->vrf_id, vrf->name); - else - zlog_warn("VRF %u is already configured with VRF %s\n", - vrf->vrf_id, vrf->name); - return CMD_WARNING_CONFIG_FAILED; - } - if (vrf->ns_ctxt != NULL) { - ns = (struct ns *) vrf->ns_ctxt; - if (ns && 0 != strcmp(ns->name, pathname)) { - if (vty) - vty_out(vty, - "VRF %u is already configured" - " with NETNS %s\n", - vrf->vrf_id, ns->name); - else - zlog_warn("VRF %u is already configured with NETNS %s", - vrf->vrf_id, ns->name); - return CMD_WARNING_CONFIG_FAILED; - } - } - ns = ns_lookup_name(pathname); - if (ns && ns->vrf_ctxt) { - struct vrf *vrf2 = (struct vrf *)ns->vrf_ctxt; - - if (vrf2 == vrf) - return CMD_SUCCESS; - if (vty) - vty_out(vty, "NS %s is already configured" - " with VRF %u(%s)\n", - ns->name, vrf2->vrf_id, vrf2->name); - else - zlog_warn("NS %s is already configured with VRF %u(%s)", - ns->name, vrf2->vrf_id, vrf2->name); - return CMD_WARNING_CONFIG_FAILED; - } else if (!ns) - ns = ns_get_by_name(pathname); - - if (ns_id != ns->ns_id) { - RB_REMOVE(ns_head, &ns_tree, ns); - ns->ns_id = ns_id; - RB_INSERT(ns_head, &ns_tree, ns); - } - ns->vrf_ctxt = (void *)vrf; - vrf->ns_ctxt = (void *)ns; - /* update VRF netns NAME */ - if (vrf) - strlcpy(vrf->data.l.netns_name, basename(pathname), NS_NAMSIZ); - - if (!ns_enable(ns)) { - if (vty) - vty_out(vty, "Can not associate NS %u with NETNS %s\n", - ns->ns_id, ns->name); - else - zlog_warn("Can not associate NS %u with NETNS %s", - ns->ns_id, ns->name); - return CMD_WARNING_CONFIG_FAILED; - } - - return CMD_SUCCESS; -} - - -static int ns_logicalrouter_config_write(struct vty *vty) -{ - struct ns *ns; - int write = 0; - - RB_FOREACH(ns, ns_head, &ns_tree) { - if (ns->ns_id == NS_DEFAULT || ns->name == NULL) - continue; - vty_out(vty, "logical-router %u netns %s\n", ns->ns_id, - ns->name); - write = 1; - } - return write; -} - -DEFUN_NOSH (ns_netns, - ns_netns_cmd, - "netns NAME", - "Attach VRF to a Namespace\n" - "The file name in " NS_RUN_DIR ", or a full pathname\n") -{ - int idx_name = 1; - char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); - - VTY_DECLVAR_CONTEXT(vrf, vrf); - - if (!pathname) - return CMD_WARNING_CONFIG_FAILED; - return ns_handler_create(vty, vrf, pathname, NS_UNKNOWN); -} - -DEFUN (no_ns_netns, - no_ns_netns_cmd, - "no netns [NAME]", - NO_STR - "Detach VRF from a Namespace\n" - "The file name in " NS_RUN_DIR ", or a full pathname\n") -{ - struct ns *ns = NULL; - - VTY_DECLVAR_CONTEXT(vrf, vrf); - - if (!vrf_is_backend_netns()) { - vty_out(vty, "VRF backend is not Netns. Aborting\n"); - return CMD_WARNING_CONFIG_FAILED; - } - if (!vrf->ns_ctxt) { - vty_out(vty, "VRF %s(%u) is not configured with NetNS\n", - vrf->name, vrf->vrf_id); - return CMD_WARNING_CONFIG_FAILED; - } - - ns = (struct ns *)vrf->ns_ctxt; - - ns->vrf_ctxt = NULL; - vrf_disable(vrf); - /* vrf ID from VRF is necessary for Zebra - * so that propagate to other clients is done - */ - RB_REMOVE(ns_head, &ns_tree, ns); - ns->ns_id = NS_UNKNOWN; - RB_INSERT(ns_head, &ns_tree, ns); - ns_delete(ns); - vrf->ns_ctxt = NULL; - 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); + static int ns_initialised; + + /* silently return as initialisation done */ + if (ns_initialised == 1) return; - } -#endif /* HAVE_NETNS */ + errno = 0; +#ifdef HAVE_NETNS + if (have_netns_enabled < 0) + ns_default_ns_fd = open(NS_DEFAULT_NAME, O_RDONLY); + else + ns_default_ns_fd = -1; +#else ns_default_ns_fd = -1; default_ns = NULL; +#endif /* HAVE_NETNS */ + if (ns_default_ns_fd == -1) + zlog_err("NS initialisation failure (%s)", + safe_strerror(errno)); + ns_current_ns_fd = -1; + ns_initialised = 1; } /* Initialize NS module. */ -void ns_init_zebra(ns_id_t default_ns_id) +void ns_init_management(ns_id_t default_ns_id) { int fd; ns_init(); - default_ns = ns_get(default_ns_id); + default_ns = ns_get_created_internal(NULL, NULL, default_ns_id); if (!default_ns) { - zlog_err("ns_init: failed to create the default NS!"); + zlog_err("%s: failed to create the default NS!", + __func__); exit(1); } if (have_netns()) { fd = open(NS_DEFAULT_NAME, O_RDONLY); default_ns->fd = fd; } - ns_current_ns_fd = -1; /* Set the default NS name. */ default_ns->name = XSTRDUP(MTYPE_NS_NAME, NS_DEFAULT_NAME); - zlog_info("ns_init: default NSID is %u", default_ns->ns_id); + zlog_info("%s: default NSID is %u", __func__, default_ns->ns_id); /* Enable the default NS. */ - if (!ns_enable(default_ns)) { - zlog_err("ns_init: failed to enable the default NS!"); + if (!ns_enable(default_ns, NULL)) { + zlog_err("%s: failed to enable the default NS!", + __func__); exit(1); } - - if (have_netns() && !vrf_is_backend_netns()) { - /* Install NS commands. */ - install_node(&logicalrouter_node, - ns_logicalrouter_config_write); - install_element(CONFIG_NODE, &ns_logicalrouter_cmd); - install_element(CONFIG_NODE, &no_ns_logicalrouter_cmd); - } -} - -void ns_cmd_init(void) -{ - if (have_netns() && vrf_is_backend_netns()) { - /* Install NS commands. */ - install_element(VRF_NODE, &ns_netns_cmd); - install_element(VRF_NODE, &no_ns_netns_cmd); - } } /* Terminate NS module. */ @@ -688,6 +465,8 @@ int ns_switch_to_netns(const char *name) if (name == NULL) return -1; + if (ns_default_ns_fd == -1) + return -1; fd = open(name, O_RDONLY); if (fd == -1) { errno = ENOSYS; @@ -704,7 +483,7 @@ int ns_switch_to_netns(const char *name) */ int ns_switchback_to_initial(void) { - if (ns_current_ns_fd != -1) { + if (ns_current_ns_fd != -1 && ns_default_ns_fd != -1) { int ret; ret = setns(ns_default_ns_fd, CLONE_NEWNET); @@ -725,7 +504,6 @@ int ns_socket(int domain, int type, int protocol, ns_id_t ns_id) errno = ENOSYS; return -1; } - if (have_netns()) { ret = (ns_id != NS_DEFAULT) ? setns(ns->fd, CLONE_NEWNET) : 0; if (ret >= 0) { diff --git a/lib/netns_other.c b/lib/netns_other.c new file mode 100644 index 0000000000..2402dd17d6 --- /dev/null +++ b/lib/netns_other.c @@ -0,0 +1,165 @@ +/* + * NetNS backend for non Linux systems + * Copyright (C) 2018 6WIND S.A. + * + * 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 + */ + + +#if !defined(GNU_LINUX) && (defined(SUNOS_5) || defined(OPEN_BSD)) +/* SUNOS_5 or OPEN_BSD */ + +#include +#include "ns.h" +#include "log.h" +#include "memory.h" + +DEFINE_MTYPE_STATIC(LIB, NS, "NetNS Context") +DEFINE_MTYPE_STATIC(LIB, NS_NAME, "NetNS Name") + + +static inline int ns_compare(const struct ns *ns, const struct ns *ns2); + +RB_GENERATE(ns_head, ns, entry, ns_compare) + +struct ns_head ns_tree = RB_INITIALIZER(&ns_tree); + +static inline int ns_compare(const struct ns *a, const struct ns *b) +{ + return (a->ns_id - b->ns_id); +} + +void ns_terminate(void) +{ +} + +/* API to initialize NETNS managerment + * parameter is the default ns_id + */ +void ns_init_management(ns_id_t ns_id) +{ +} + + +/* + * NS utilities + */ + +/* Create a socket serving for the given NS + */ +int ns_socket(int domain, int type, int protocol, ns_id_t ns_id) +{ + return -1; +} + +/* return the path of the NETNS */ +char *ns_netns_pathname(struct vty *vty, const char *name) +{ + return NULL; +} + +/* Parse and execute a function on all the NETNS */ +void ns_walk_func(int (*func)(struct ns *)) +{ +} + +/* API to get the NETNS name, from the ns pointer */ +const char *ns_get_name(struct ns *ns) +{ + return NULL; +} + +/* only called from vrf ( when removing netns from vrf) + * or at VRF or logical router termination + */ +void ns_delete(struct ns *ns) +{ +} + +/* return > 0 if netns is available + * called by VRF to check netns backend is available for VRF + */ +int ns_have_netns(void) +{ + return 0; +} + +/* API to get context information of a NS */ +void *ns_info_lookup(ns_id_t ns_id) +{ + return NULL; +} + +/* + * NS init routine + * should be called from backendx + */ +void ns_init(void) +{ +} + +/* API to retrieve default NS */ +ns_id_t ns_get_default_id(void) +{ + return NS_UNKNOWN; +} + + +/* API that can be used to change from NS */ +int ns_switchback_to_initial(void) +{ + return 0; +} +int ns_switch_to_netns(const char *netns_name) +{ + return 0; +} + +/* + * NS handling routines. + * called by modules that use NS backend + */ + +/* API to search for already present NETNS */ +struct ns *ns_lookup(ns_id_t ns_id) +{ + return NULL; +} + +struct ns *ns_lookup_name(const char *name) +{ + return NULL; +} + +/* API to handle NS : creation, enable, disable + * for enable, a callback function is passed as parameter + * the callback belongs to the module that uses NS as backend + * upon enabling the NETNS, the upper layer is informed + */ +int ns_enable(struct ns *ns, int (*func)(ns_id_t, void *)) +{ + return 0; +} + +struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id) +{ + return NULL; +} + +void ns_disable(struct ns *ns) +{ +} + +#endif /* !GNU_LINUX */ diff --git a/lib/ns.h b/lib/ns.h index 44257ab0c0..83e5e1b907 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -25,7 +25,6 @@ #include "openbsd-tree.h" #include "linklist.h" #include "vty.h" -#include "vrf.h" typedef u_int32_t ns_id_t; @@ -67,6 +66,11 @@ RB_PROTOTYPE(ns_head, ns, entry, ns_compare) extern struct ns_head ns_tree; +/* + * API for managing NETNS. eg from zebra daemon + * one want to manage the list of NETNS, etc... + */ + /* * NS hooks */ @@ -86,35 +90,80 @@ extern struct ns_head ns_tree; */ extern void ns_add_hook(int type, int (*)(struct ns *)); + /* * NS initializer/destructor */ -extern void ns_init(void); -extern void ns_init_zebra(ns_id_t ns_id); + extern void ns_terminate(void); +/* API to initialize NETNS managerment + * parameter is the default ns_id + */ +extern void ns_init_management(ns_id_t ns_id); + + /* * NS utilities */ -/* Create a socket serving for the given NS */ -extern int ns_socket(int, int, int, ns_id_t); -extern void ns_cmd_init(void); -extern int ns_handler_create(struct vty *vty, struct vrf *vrf, - char *pathname, ns_id_t ns_id); -extern char *ns_netns_pathname(struct vty *vty, const char *name); -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); -extern ns_id_t ns_get_default_id(void); +/* Create a socket serving for the given NS + */ +int ns_socket(int domain, int type, int protocol, ns_id_t ns_id); -/* 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); +/* return the path of the NETNS */ +extern char *ns_netns_pathname(struct vty *vty, const char *name); + +/* Parse and execute a function on all the NETNS */ +extern void ns_walk_func(int (*func)(struct ns *)); + +/* API to get the NETNS name, from the ns pointer */ +extern const char *ns_get_name(struct ns *ns); + +/* only called from vrf ( when removing netns from vrf) + * or at VRF or logical router termination + */ +extern void ns_delete(struct ns *ns); + +/* return > 0 if netns is available + * called by VRF to check netns backend is available for VRF + */ +extern int ns_have_netns(void); + +/* API to get context information of a NS */ +extern void *ns_info_lookup(ns_id_t ns_id); + +/* + * NS init routine + * should be called from backendx + */ extern void ns_init(void); +/* API to retrieve default NS */ +extern ns_id_t ns_get_default_id(void); -/* The default NS ID */ #define NS_DEFAULT ns_get_default_id() +/* API that can be used to change from NS */ +extern int ns_switchback_to_initial(void); +extern int ns_switch_to_netns(const char *netns_name); + +/* + * NS handling routines. + * called by modules that use NS backend + */ + +/* API to search for already present NETNS */ +extern struct ns *ns_lookup(ns_id_t ns_id); +extern struct ns *ns_lookup_name(const char *name); + +/* API to handle NS : creation, enable, disable + * for enable, a callback function is passed as parameter + * the callback belongs to the module that uses NS as backend + * upon enabling the NETNS, the upper layer is informed + */ +extern int ns_enable(struct ns *ns, void (*func)(ns_id_t, void *)); +extern struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id); +extern void ns_disable(struct ns *ns); + #endif /*_ZEBRA_NS_H*/ diff --git a/lib/subdir.am b/lib/subdir.am index 44870917bd..e292d7a342 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -42,7 +42,8 @@ lib_libfrr_la_SOURCES = \ lib/module.c \ lib/network.c \ lib/nexthop.c \ - lib/ns.c \ + lib/netns_linux.c \ + lib/netns_other.c \ lib/openbsd-tree.c \ lib/pid_output.c \ lib/plist.c \ @@ -74,6 +75,7 @@ lib_libfrr_la_SOURCES = \ lib/wheel.c \ lib/workqueue.c \ lib/zclient.c \ + lib/logicalrouter.c \ # end lib/plist_clippy.c: $(CLIPPY_DEPS) @@ -158,6 +160,7 @@ pkginclude_HEADERS += \ lib/zassert.h \ lib/zclient.h \ lib/zebra.h \ + lib/logicalrouter.h \ # end nodist_pkginclude_HEADERS += \ diff --git a/lib/vrf.c b/lib/vrf.c index ade1895bd4..0ca517d051 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -89,16 +89,38 @@ static int vrf_name_compare(const struct vrf *a, const struct vrf *b) return strcmp(a->name, b->name); } +/* if ns_id is different and not VRF_UNKNOWN, + * then update vrf identifier, and enable VRF + */ +static void vrf_update_vrf_id(ns_id_t ns_id, void *opaqueptr) +{ + ns_id_t vrf_id = (vrf_id_t)ns_id; + vrf_id_t old_vrf_id; + struct vrf *vrf = (struct vrf *)opaqueptr; + + if (!vrf) + return; + old_vrf_id = vrf->vrf_id; + if (vrf_id == vrf->vrf_id) + return; + if (vrf->vrf_id != VRF_UNKNOWN) + RB_REMOVE(vrf_id_head, &vrfs_by_id, vrf); + vrf->vrf_id = vrf_id; + RB_INSERT(vrf_id_head, &vrfs_by_id, vrf); + if (old_vrf_id == VRF_UNKNOWN) + vrf_enable((struct vrf *)vrf); +} + 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) + if (!vrf || vrf->vrf_id == VRF_DEFAULT) + return 0; + /* VRF has no NETNS backend. silently ignore */ + if (vrf->data.l.netns_name[0] == '\0') return 0; name = ns_netns_pathname(NULL, vrf->data.l.netns_name); if (debug_vrf) @@ -115,25 +137,6 @@ int vrf_switchback_to_initial(void) return ret; } -/* return 1 if vrf can be enabled */ -int vrf_update_vrf_id(vrf_id_t vrf_id, struct vrf *vrf) -{ - vrf_id_t old_vrf_id; - - if (!vrf) - return 0; - old_vrf_id = vrf->vrf_id; - if (vrf_id == vrf->vrf_id) - return 0; - if (vrf->vrf_id != VRF_UNKNOWN) - RB_REMOVE(vrf_id_head, &vrfs_by_id, vrf); - vrf->vrf_id = vrf_id; - RB_INSERT(vrf_id_head, &vrfs_by_id, vrf); - if (old_vrf_id == VRF_UNKNOWN) - return 1; - return 0; -} - /* Get a VRF. If not found, create one. * Arg: * name - The name of the vrf. May be NULL if unknown. @@ -436,6 +439,8 @@ void vrf_init(int (*create)(struct vrf *), int (*enable)(struct vrf *), { struct vrf *default_vrf; + /* initialise NS, in case VRF backend if NETNS */ + ns_init(); if (debug_vrf) zlog_debug("%s: Initializing VRF subsystem", __PRETTY_FUNCTION__); @@ -547,6 +552,72 @@ int vrf_handler_create(struct vty *vty, const char *vrfname, struct vrf **vrf) return CMD_SUCCESS; } +int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, + char *pathname, ns_id_t ns_id) +{ + struct ns *ns = NULL; + + if (!vrf) + return CMD_WARNING_CONFIG_FAILED; + if (vrf->vrf_id != VRF_UNKNOWN && vrf->ns_ctxt == NULL) { + if (vty) + vty_out(vty, + "VRF %u is already configured with VRF %s\n", + vrf->vrf_id, vrf->name); + else + zlog_warn("VRF %u is already configured with VRF %s\n", + vrf->vrf_id, vrf->name); + return CMD_WARNING_CONFIG_FAILED; + } + if (vrf->ns_ctxt != NULL) { + ns = (struct ns *) vrf->ns_ctxt; + if (ns && 0 != strcmp(ns->name, pathname)) { + if (vty) + vty_out(vty, + "VRF %u already configured with NETNS %s\n", + vrf->vrf_id, ns->name); + else + zlog_warn( + "VRF %u already configured with NETNS %s", + vrf->vrf_id, ns->name); + return CMD_WARNING_CONFIG_FAILED; + } + } + ns = ns_lookup_name(pathname); + if (ns && ns->vrf_ctxt) { + struct vrf *vrf2 = (struct vrf *)ns->vrf_ctxt; + + if (vrf2 == vrf) + return CMD_SUCCESS; + if (vty) + vty_out(vty, "NS %s is already configured" + " with VRF %u(%s)\n", + ns->name, vrf2->vrf_id, vrf2->name); + else + zlog_warn("NS %s is already configured with VRF %u(%s)", + ns->name, vrf2->vrf_id, vrf2->name); + return CMD_WARNING_CONFIG_FAILED; + } + ns = ns_get_created(ns, pathname, ns_id); + ns->vrf_ctxt = (void *)vrf; + vrf->ns_ctxt = (void *)ns; + /* update VRF netns NAME */ + if (vrf) + strlcpy(vrf->data.l.netns_name, basename(pathname), NS_NAMSIZ); + + if (!ns_enable(ns, vrf_update_vrf_id)) { + if (vty) + vty_out(vty, "Can not associate NS %u with NETNS %s\n", + ns->ns_id, ns->name); + else + zlog_warn("Can not associate NS %u with NETNS %s", + ns->ns_id, ns->name); + return CMD_WARNING_CONFIG_FAILED; + } + + return CMD_SUCCESS; +} + int vrf_is_mapped_on_netns(vrf_id_t vrf_id) { struct vrf *vrf = vrf_lookup_by_id(vrf_id); @@ -604,6 +675,55 @@ DEFUN_NOSH (no_vrf, struct cmd_node vrf_node = {VRF_NODE, "%s(config-vrf)# ", 1}; +DEFUN_NOSH (vrf_netns, + vrf_netns_cmd, + "netns NAME", + "Attach VRF to a Namespace\n" + "The file name in " NS_RUN_DIR ", or a full pathname\n") +{ + int idx_name = 1; + char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg); + + VTY_DECLVAR_CONTEXT(vrf, vrf); + + if (!pathname) + return CMD_WARNING_CONFIG_FAILED; + return vrf_netns_handler_create(vty, vrf, pathname, NS_UNKNOWN); +} + +DEFUN (no_vrf_netns, + no_vrf_netns_cmd, + "no netns [NAME]", + NO_STR + "Detach VRF from a Namespace\n" + "The file name in " NS_RUN_DIR ", or a full pathname\n") +{ + struct ns *ns = NULL; + + VTY_DECLVAR_CONTEXT(vrf, vrf); + + if (!vrf_is_backend_netns()) { + vty_out(vty, "VRF backend is not Netns. Aborting\n"); + return CMD_WARNING_CONFIG_FAILED; + } + if (!vrf->ns_ctxt) { + vty_out(vty, "VRF %s(%u) is not configured with NetNS\n", + vrf->name, vrf->vrf_id); + return CMD_WARNING_CONFIG_FAILED; + } + + ns = (struct ns *)vrf->ns_ctxt; + + ns->vrf_ctxt = NULL; + vrf_disable(vrf); + /* vrf ID from VRF is necessary for Zebra + * so that propagate to other clients is done + */ + ns_delete(ns); + vrf->ns_ctxt = NULL; + return CMD_SUCCESS; +} + /* * Debug CLI for vrf's */ @@ -656,7 +776,11 @@ void vrf_cmd_init(int (*writefunc)(struct vty *vty)) install_element(CONFIG_NODE, &no_vrf_cmd); install_node(&vrf_node, writefunc); install_default(VRF_NODE); - ns_cmd_init(); + if (vrf_is_backend_netns() && ns_have_netns()) { + /* Install NS commands. */ + install_element(VRF_NODE, &vrf_netns_cmd); + install_element(VRF_NODE, &no_vrf_netns_cmd); + } } vrf_id_t vrf_get_default_id(void) diff --git a/lib/vrf.h b/lib/vrf.h index 08c53484ee..326418791d 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -26,6 +26,7 @@ #include "linklist.h" #include "qobj.h" #include "vty.h" +#include "ns.h" /* The default VRF ID */ #define VRF_UNKNOWN UINT32_MAX @@ -199,21 +200,28 @@ extern void vrf_init(int (*create)(struct vrf *), int (*enable)(struct vrf *), */ extern void vrf_terminate(void); -extern void vrf_cmd_init(int (*writefunc)(struct vty *vty)); - /* - * VRF utilities + * Utilities to create networks objects, + * or call network operations */ -extern vrf_id_t vrf_get_default_id(void); /* Create a socket serving for the given VRF */ -extern int vrf_socket(int, int, int, vrf_id_t); -extern void vrf_configure_backend(int vrf_backend_netns); -extern int vrf_get_backend(void); -extern int vrf_is_backend_netns(void); -extern int vrf_handler_create(struct vty *vty, - const char *name, - struct vrf **vrf); +extern int vrf_socket(int domain, int type, + int protocol, vrf_id_t vrf_id); +extern int vrf_sockunion_socket(const union sockunion *su, + vrf_id_t vrf_id); + +/* VRF ioctl operations */ +extern int vrf_getaddrinfo(const char *node, const char *service, + const struct addrinfo *hints, + struct addrinfo **res, vrf_id_t vrf_id); + +/* function called by macro VRF_DEFAULT + * to get the default VRF_ID + */ +extern vrf_id_t vrf_get_default_id(void); +/* The default VRF ID */ +#define VRF_DEFAULT vrf_get_default_id() /* VRF is mapped on netns or not ? */ int vrf_is_mapped_on_netns(vrf_id_t vrf_id); @@ -222,29 +230,49 @@ int vrf_is_mapped_on_netns(vrf_id_t vrf_id); extern int vrf_switch_to_netns(vrf_id_t vrf_id); extern int vrf_switchback_to_initial(void); -/* VRF ioctl operations */ -extern int vrf_getaddrinfo(const char *node, const char *service, - const struct addrinfo *hints, - struct addrinfo **res, vrf_id_t vrf_id); -extern int vrf_sockunion_socket(const union sockunion *su, 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 - */ -extern int vrf_update_vrf_id(vrf_id_t vrf_id, struct vrf *vrf); -extern void vrf_disable(struct vrf *vrf); -extern int vrf_enable(struct vrf *vrf); - /* - * VRF Debugging + * VRF backend routines + * should be called from zebra only + */ + +/* VRF vty command initialisation + */ +extern void vrf_cmd_init(int (*writefunc)(struct vty *vty)); + +/* VRF vty debugging */ extern void vrf_install_commands(void); +/* + * VRF utilities + */ -/* The default VRF ID */ -#define VRF_DEFAULT vrf_get_default_id() +/* API for configuring VRF backend + * should be called from zebra only + */ +extern void vrf_configure_backend(int vrf_backend_netns); +extern int vrf_get_backend(void); +extern int vrf_is_backend_netns(void); + + +/* API to create a VRF. either from vty + * or through discovery + */ +extern int vrf_handler_create(struct vty *vty, + const char *name, + struct vrf **vrf); + +/* API to associate a VRF with a NETNS. + * called either from vty or through discovery + * should be called from zebra only + */ +extern int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf, + char *pathname, ns_id_t ns_id); + +/* used internally to enable or disable VRF. + * Notify a change in the VRF ID of the VRF + */ +extern void vrf_disable(struct vrf *vrf); +extern int vrf_enable(struct vrf *vrf); #endif /*_ZEBRA_VRF_H*/ From 736d41ad74f1135256ece64873fb261164ed03e0 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 5 Feb 2018 16:30:21 +0100 Subject: [PATCH 30/41] zebra: adapt the vrf and logical router initialisation The zebra daemon introduces the logical router initialisation. Because right now, the usage of logical router and vrf NETNS is exclusive, then the logical router and VRF are initialised accordingly. Signed-off-by: Philippe Guibert --- zebra/main.c | 5 +++++ zebra/zebra_netns_notify.c | 2 +- zebra/zebra_ns.c | 25 ++++++++++++++++++++++--- zebra/zebra_vrf.c | 4 ++-- zebra/zebra_vrf.h | 7 +++---- 5 files changed, 33 insertions(+), 10 deletions(-) diff --git a/zebra/main.c b/zebra/main.c index 9cad913f4b..749d509a86 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -34,6 +34,7 @@ #include "privs.h" #include "sigevent.h" #include "vrf.h" +#include "logicalrouter.h" #include "libfrr.h" #include "zebra/rib.h" @@ -207,6 +208,8 @@ int main(int argc, char **argv) #endif vrf_configure_backend(VRF_BACKEND_VRF_LITE); + logicalrouter_configure_backend( + LOGICALROUTER_BACKEND_NETNS); frr_preinit(&zebra_di, argc, argv); @@ -285,6 +288,8 @@ int main(int argc, char **argv) break; case 'n': vrf_configure_backend(VRF_BACKEND_NETNS); + logicalrouter_configure_backend( + LOGICALROUTER_BACKEND_OFF); break; #endif /* HAVE_NETLINK */ #if defined(HANDLE_ZAPI_FUZZING) diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c index 8940546f68..b28998acf8 100644 --- a/zebra/zebra_netns_notify.c +++ b/zebra/zebra_netns_notify.c @@ -84,7 +84,7 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name) 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); + ret = vrf_netns_handler_create(NULL, vrf, netnspath, ns_id); if (ret != CMD_SUCCESS) { zlog_warn("NS notify : failed to create NS %s", netnspath); return; diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index da5b22def2..cb302985c8 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -23,6 +23,7 @@ #include "lib/ns.h" #include "lib/vrf.h" +#include "lib/logicalrouter.h" #include "lib/prefix.h" #include "lib/memory.h" @@ -59,6 +60,8 @@ zebra_ns_table_entry_compare(const struct zebra_ns_table *e1, return e1->tableid - e2->tableid; } +static int logicalrouter_config_write(struct vty *vty); + struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id) { if (ns_id == NS_DEFAULT) @@ -241,9 +244,10 @@ int zebra_ns_init(void) ns_id = zebra_ns_id_get_default(); if (zserv_privs.change(ZPRIVS_LOWER)) zlog_err("Can't lower privileges"); - ns_init_zebra(ns_id); - ns_init(); + ns_init_management(ns_id); + + logicalrouter_init(logicalrouter_config_write); /* Do any needed per-NS data structure allocation. */ dzns->if_table = route_table_init(); @@ -253,7 +257,7 @@ int zebra_ns_init(void) zebra_vrf_init(); /* Default NS is activated */ - zebra_ns_enable(NS_DEFAULT, (void **)&dzns); + zebra_ns_enable(ns_id, (void **)&dzns); if (vrf_is_backend_netns()) { ns_add_hook(NS_NEW_HOOK, zebra_ns_new); @@ -266,6 +270,21 @@ int zebra_ns_init(void) return 0; } +static int logicalrouter_config_write(struct vty *vty) +{ + struct ns *ns; + int write = 0; + + RB_FOREACH(ns, ns_head, &ns_tree) { + if (ns->ns_id == NS_DEFAULT || ns->name == NULL) + continue; + vty_out(vty, "logical-router %u netns %s\n", ns->ns_id, + ns->name); + write = 1; + } + return write; +} + int zebra_ns_config_write(struct vty *vty, struct ns *ns) { if (ns && ns->name != NULL) diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 874a9c74e7..bb15fd04f3 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -581,8 +581,8 @@ static int vrf_config_write(struct vty *vty) /* Zebra VRF initialization. */ void zebra_vrf_init(void) { - vrf_init(zebra_vrf_new, zebra_vrf_enable, zebra_vrf_disable, - zebra_vrf_delete); + vrf_init(zebra_vrf_new, zebra_vrf_enable, + zebra_vrf_disable, zebra_vrf_delete); vrf_cmd_init(vrf_config_write); } diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index ae5a174116..4c12d7dee9 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -19,10 +19,9 @@ * 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 */ -#if !defined(__ZEBRA_RIB_H__) -#define __ZEBRA_RIB_H__ +#if !defined(__ZEBRA_VRF_H__) +#define __ZEBRA_VRF_H__ -#include #include #include #include @@ -169,4 +168,4 @@ extern void zebra_vrf_init(void); extern void zebra_rtable_node_cleanup(struct route_table *table, struct route_node *node); -#endif +#endif /* ZEBRA_VRF_H */ From f5d20fdb4e1fae6ccdcd3efa9136663481a5e355 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 5 Feb 2018 16:36:13 +0100 Subject: [PATCH 31/41] vtysh: change logical router node name The logical router node goes from NS_NODE to LOGICALROUTER_NODE. Vty commands are renamed accordingly. Signed-off-by: Philippe Guibert --- vtysh/Makefile.am | 2 +- vtysh/extract.pl.in | 3 +++ vtysh/vtysh.c | 40 +++++++++++++++++++++++++++------------- vtysh/vtysh_config.c | 4 ++-- 4 files changed, 33 insertions(+), 16 deletions(-) diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am index c9b6f50160..33d34fc0dd 100644 --- a/vtysh/Makefile.am +++ b/vtysh/Makefile.am @@ -147,7 +147,7 @@ vtysh_cmd_FILES = $(vtysh_scan) \ $(top_srcdir)/lib/distribute.c $(top_srcdir)/lib/if_rmap.c \ $(top_srcdir)/lib/vrf.c \ $(top_srcdir)/lib/vty.c $(top_srcdir)/zebra/debug.c \ - $(top_srcdir)/lib/ns.c \ + $(top_srcdir)/lib/logicalrouter.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 dedf3d1647..6cfb51b00f 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -87,6 +87,9 @@ foreach (@ARGV) { elsif ($file =~ /lib\/vrf\.c$/) { $protocol = "VTYSH_ALL"; } + elsif ($file =~ /lib\/logicalrouter\.c$/) { + $protocol = "VTYSH_ALL"; + } elsif ($file =~ /lib\/filter\.c$/) { $protocol = "VTYSH_ALL"; } diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 65e9c9f8c5..e0a0dd585d 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -975,8 +975,8 @@ static struct cmd_node pw_node = { PW_NODE, "%s(config-pw)# ", }; -static struct cmd_node ns_node = { - NS_NODE, "%s(config-logical-router)# ", +static struct cmd_node logicalrouter_node = { + LOGICALROUTER_NODE, "%s(config-logical-router)# ", }; static struct cmd_node vrf_node = { @@ -1508,7 +1508,7 @@ static int vtysh_exit(struct vty *vty) break; case INTERFACE_NODE: case PW_NODE: - case NS_NODE: + case LOGICALROUTER_NODE: case VRF_NODE: case ZEBRA_NODE: case BGP_NODE: @@ -1782,16 +1782,25 @@ DEFSH(VTYSH_ZEBRA, vtysh_no_interface_vrf_cmd, "no interface IFNAME vrf NAME", "Delete a pseudo interface's configuration\n" "Interface's name\n" VRF_CMD_HELP_STR) -DEFUNSH(VTYSH_NS, vtysh_ns, vtysh_ns_cmd, "logical-router (1-65535) ns NAME", +DEFUNSH(VTYSH_ZEBRA, vtysh_logicalrouter, vtysh_logicalrouter_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; + vty->node = LOGICALROUTER_NODE; return CMD_SUCCESS; } +DEFSH(VTYSH_ZEBRA, vtysh_no_logicalrouter_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") + DEFUNSH(VTYSH_VRF, vtysh_vrf, vtysh_vrf_cmd, "vrf NAME", "Select a VRF to configure\n" "VRF's name\n") @@ -1804,16 +1813,18 @@ DEFSH(VTYSH_ZEBRA, vtysh_no_vrf_cmd, "no vrf NAME", NO_STR "Delete a pseudo vrf's configuration\n" "VRF's name\n") -DEFUNSH(VTYSH_NS, vtysh_exit_ns, vtysh_exit_ns_cmd, "exit", +DEFUNSH(VTYSH_NS, vtysh_exit_logicalrouter, + vtysh_exit_logicalrouter_cmd, "exit", "Exit current mode and down to previous mode\n") { return vtysh_exit(vty); } -DEFUNSH(VTYSH_NS, vtysh_quit_ns, vtysh_quit_ns_cmd, "quit", +DEFUNSH(VTYSH_NS, vtysh_quit_logicalrouter, + vtysh_quit_logicalrouter_cmd, "quit", "Exit current mode and down to previous mode\n") { - return vtysh_exit_ns(self, vty, argc, argv); + return vtysh_exit_logicalrouter(self, vty, argc, argv); } DEFUNSH(VTYSH_VRF, vtysh_exit_vrf, vtysh_exit_vrf_cmd, "exit", @@ -3055,7 +3066,7 @@ void vtysh_init_vty(void) install_node(&interface_node, NULL); install_node(&pw_node, NULL); install_node(&link_params_node, NULL); - install_node(&ns_node, NULL); + install_node(&logicalrouter_node, NULL); install_node(&vrf_node, NULL); install_node(&rmap_node, NULL); install_node(&zebra_node, NULL); @@ -3233,11 +3244,14 @@ void vtysh_init_vty(void) install_element(PW_NODE, &vtysh_exit_interface_cmd); install_element(PW_NODE, &vtysh_quit_interface_cmd); - install_element(NS_NODE, &vtysh_end_all_cmd); + install_element(LOGICALROUTER_NODE, &vtysh_end_all_cmd); - install_element(CONFIG_NODE, &vtysh_ns_cmd); - install_element(NS_NODE, &vtysh_exit_ns_cmd); - install_element(NS_NODE, &vtysh_quit_ns_cmd); + install_element(CONFIG_NODE, &vtysh_logicalrouter_cmd); + install_element(CONFIG_NODE, &vtysh_no_logicalrouter_cmd); + install_element(LOGICALROUTER_NODE, + &vtysh_exit_logicalrouter_cmd); + install_element(LOGICALROUTER_NODE, + &vtysh_quit_logicalrouter_cmd); install_element(VRF_NODE, &vtysh_end_all_cmd); install_element(VRF_NODE, &vtysh_exit_vrf_cmd); diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index 967f855fbc..aa1dd407eb 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -187,7 +187,7 @@ void vtysh_config_parse_line(void *arg, const char *line) config->index = INTERFACE_NODE; } else if (config->index == RMAP_NODE || config->index == INTERFACE_NODE - || config->index == NS_NODE + || config->index == LOGICALROUTER_NODE || config->index == VTY_NODE || config->index == VRF_NODE) config_add_line_uniq(config->line, line); @@ -202,7 +202,7 @@ void vtysh_config_parse_line(void *arg, const char *line) else if (strncmp(line, "pseudowire", strlen("pseudowire")) == 0) config = config_get(PW_NODE, line); else if (strncmp(line, "logical-router", strlen("ns")) == 0) - config = config_get(NS_NODE, line); + config = config_get(LOGICALROUTER_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) From 9dab51ac71bddf2b4e2a5e1c3a185cb50c935301 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 5 Feb 2018 16:37:49 +0100 Subject: [PATCH 32/41] bgpd: no need to initialise netns directly NETNS is initialised from the VRF, instead of being directly called, because this is not up to BGP daemon to initialise the various VRF backend. Signed-off-by: Philippe Guibert --- bgpd/bgp_main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 717fe09762..82c74e4afa 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -301,7 +301,6 @@ static int bgp_vrf_disable(struct vrf *vrf) static void bgp_vrf_init(void) { - ns_init(); vrf_init(bgp_vrf_new, bgp_vrf_enable, bgp_vrf_disable, bgp_vrf_delete); } From 516d7591d68cf4537bb7f0603ff703e808381d03 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 5 Feb 2018 17:00:45 +0100 Subject: [PATCH 33/41] lib: add vrf_ioctl API That API can be used to wrap the ioctl call with various vrf instances. This permits transparently doing the ioctl() call without taking into consideration the vrf backend kind. Signed-off-by: Philippe Guibert --- lib/vrf.c | 20 ++++++++++++++++++++ lib/vrf.h | 2 ++ zebra/if_netlink.c | 13 +------------ 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/lib/vrf.c b/lib/vrf.c index 0ca517d051..890a7adca2 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -815,6 +815,26 @@ int vrf_getaddrinfo(const char *node, const char *service, return ret; } +int vrf_ioctl(vrf_id_t vrf_id, int d, unsigned long request, char *params) +{ + int ret, saved_errno, rc; + + ret = vrf_switch_to_netns(vrf_id); + if (ret < 0) { + zlog_err("%s: Can't switch to VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); + return 0; + } + rc = ioctl(d, request, params); + saved_errno = errno; + ret = vrf_switchback_to_initial(); + if (ret < 0) + zlog_err("%s: Can't switchback from VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); + errno = saved_errno; + return rc; +} + int vrf_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id) { int ret, save_errno, ret2; diff --git a/lib/vrf.h b/lib/vrf.h index 326418791d..cb4159a0a3 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -216,6 +216,8 @@ extern int vrf_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res, vrf_id_t vrf_id); +extern int vrf_ioctl(vrf_id_t vrf_id, int d, unsigned long request, char *args); + /* function called by macro VRF_DEFAULT * to get the default VRF_ID */ diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index a02533c1fe..65f1e56587 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -351,7 +351,6 @@ static int get_iflink_speed(struct interface *interface) struct ethtool_cmd ecmd; int sd; int rc; - int ret, saved_errno; const char *ifname = interface->name; /* initialize struct */ @@ -376,17 +375,7 @@ static int get_iflink_speed(struct interface *interface) return 0; } /* Get the current link state for the interface */ - ret = vrf_switch_to_netns(interface->vrf_id); - if (ret < 0) - zlog_err("%s: Can't switch to VRF %u (%s)", - __func__, interface->vrf_id, safe_strerror(errno)); - rc = ioctl(sd, SIOCETHTOOL, (char *)&ifdata); - saved_errno = errno; - ret = vrf_switchback_to_initial(); - if (ret < 0) - zlog_err("%s: Can't switchback from VRF %u (%s)", - __func__, interface->vrf_id, safe_strerror(errno)); - errno = saved_errno; + rc = vrf_ioctl(interface->vrf_id, sd, SIOCETHTOOL, (char *)&ifdata); if (zserv_privs.change(ZPRIVS_LOWER)) zlog_err("Can't lower privileges"); if (rc < 0) { From 0f4977c6689441e7b3075fc7a82c1ddc9ffdfa1c Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 5 Feb 2018 17:28:51 +0100 Subject: [PATCH 34/41] lib: add vrf-lite bind capability to vrf APIs Because socket creation is tightly linked with socket binding for vrf lite, the proposal is made to extend socket creation APIs and to create a new API called vrf_bind that applies to vrf lite. The passed interface name is the interface that will be bound to the socket passed. Signed-off-by: Philippe Guibert --- bgpd/bgp_network.c | 4 ++-- lib/vrf.c | 38 ++++++++++++++++++++++++++++++++++++-- lib/vrf.h | 8 ++++++-- zebra/if_netlink.c | 3 ++- 4 files changed, 46 insertions(+), 7 deletions(-) diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 59c59f924e..240a23d198 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -571,7 +571,7 @@ int bgp_connect(struct peer *peer) if (bgpd_privs.change(ZPRIVS_RAISE)) zlog_err("Can't raise privileges"); /* Make socket for the peer. */ - peer->fd = vrf_sockunion_socket(&peer->su, peer->bgp->vrf_id); + peer->fd = vrf_sockunion_socket(&peer->su, peer->bgp->vrf_id, NULL); if (bgpd_privs.change(ZPRIVS_LOWER)) zlog_err("Can't lower privileges"); if (peer->fd < 0) @@ -751,7 +751,7 @@ int bgp_socket(struct bgp *bgp, unsigned short port, const char *address) if (bgpd_privs.change(ZPRIVS_RAISE)) zlog_err("Can't raise privileges"); sock = vrf_socket(ainfo->ai_family, ainfo->ai_socktype, - ainfo->ai_protocol, bgp->vrf_id); + ainfo->ai_protocol, bgp->vrf_id, NULL); if (bgpd_privs.change(ZPRIVS_LOWER)) zlog_err("Can't lower privileges"); if (sock < 0) { diff --git a/lib/vrf.c b/lib/vrf.c index 890a7adca2..ea106b90a2 100644 --- a/lib/vrf.c +++ b/lib/vrf.c @@ -493,7 +493,8 @@ void vrf_terminate(void) } /* Create a socket for the VRF. */ -int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id) +int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id, + char *interfacename) { int ret, save_errno, ret2; @@ -508,6 +509,13 @@ int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id) zlog_err("%s: Can't switchback from VRF %u (%s)", __func__, vrf_id, safe_strerror(errno)); errno = save_errno; + if (ret <= 0) + return ret; + ret2 = vrf_bind(vrf_id, ret, interfacename); + if (ret2 < 0) { + close(ret); + ret = ret2; + } return ret; } @@ -795,6 +803,23 @@ vrf_id_t vrf_get_default_id(void) return VRF_DEFAULT_INTERNAL; } +int vrf_bind(vrf_id_t vrf_id, int fd, char *name) +{ + int ret = 0; + + if (fd < 0 || name == NULL) + return fd; + if (vrf_is_mapped_on_netns(vrf_id)) + return fd; +#ifdef SO_BINDTODEVICE + ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name, + strlen(name)); + if (ret < 0) + zlog_debug("bind to interface %s failed, errno=%d", + name, errno); +#endif /* SO_BINDTODEVICE */ + return ret; +} int vrf_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res, vrf_id_t vrf_id) @@ -835,7 +860,8 @@ int vrf_ioctl(vrf_id_t vrf_id, int d, unsigned long request, char *params) return rc; } -int vrf_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id) +int vrf_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id, + char *interfacename) { int ret, save_errno, ret2; @@ -850,5 +876,13 @@ int vrf_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id) zlog_err("%s: Can't switchback from VRF %u (%s)", __func__, vrf_id, safe_strerror(errno)); errno = save_errno; + + if (ret <= 0) + return ret; + ret2 = vrf_bind(vrf_id, ret, interfacename); + if (ret2 < 0) { + close(ret); + ret = ret2; + } return ret; } diff --git a/lib/vrf.h b/lib/vrf.h index cb4159a0a3..062e6f3d8d 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -207,9 +207,13 @@ extern void vrf_terminate(void); /* Create a socket serving for the given VRF */ extern int vrf_socket(int domain, int type, - int protocol, vrf_id_t vrf_id); + int protocol, vrf_id_t vrf_id, + char *name); + extern int vrf_sockunion_socket(const union sockunion *su, - vrf_id_t vrf_id); + vrf_id_t vrf_id, char *name); + +extern int vrf_bind(vrf_id_t vrf_id, int fd, char *name); /* VRF ioctl operations */ extern int vrf_getaddrinfo(const char *node, const char *service, diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 65f1e56587..639f70a6b4 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -367,7 +367,8 @@ static int get_iflink_speed(struct interface *interface) /* use ioctl to get IP address of an interface */ if (zserv_privs.change(ZPRIVS_RAISE)) zlog_err("Can't raise privileges"); - sd = vrf_socket(PF_INET, SOCK_DGRAM, IPPROTO_IP, interface->vrf_id); + sd = vrf_socket(PF_INET, SOCK_DGRAM, IPPROTO_IP, + interface->vrf_id, NULL); if (sd < 0) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("Failure to read interface %s speed: %d %s", From 97896a91c2a7c8d856a1326cd4017f458e4a5493 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 5 Feb 2018 17:39:37 +0100 Subject: [PATCH 35/41] bgpd: make bgpd rely on vrf_bind() API usage Instead of relying on local usage of vrf bind operation, the vrf API for that usage is done. Signed-off-by: Philippe Guibert fixup bgp --- bgpd/bgp_network.c | 53 ++++++++++++---------------------------------- 1 file changed, 13 insertions(+), 40 deletions(-) diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 240a23d198..0ab583f444 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -45,7 +45,7 @@ extern struct zebra_privs_t bgpd_privs; -static int bgp_bind(struct peer *); +static char *bgp_get_bound_name(struct peer *peer); /* BGP listening socket. */ struct bgp_listener { @@ -415,7 +415,7 @@ static int bgp_accept(struct thread *thread) peer->doppelganger = peer1; peer1->doppelganger = peer; peer->fd = bgp_sock; - bgp_bind(peer); + vrf_bind(peer->bgp->vrf_id, bgp_sock, bgp_get_bound_name(peer)); bgp_fsm_change_status(peer, Active); BGP_TIMER_OFF(peer->t_start); /* created in peer_create() */ @@ -443,23 +443,20 @@ static int bgp_accept(struct thread *thread) } /* BGP socket bind. */ -static int bgp_bind(struct peer *peer) +static char *bgp_get_bound_name(struct peer *peer) { -#ifdef SO_BINDTODEVICE - int ret; - int myerrno; char *name = NULL; - /* If not bound to an interface or part of a VRF lite, we don't care. */ if ((peer->bgp->vrf_id == VRF_DEFAULT) && !peer->ifname && !peer->conf_if) - return 0; - if (vrf_is_mapped_on_netns(peer->bgp->vrf_id)) - return 0; + return NULL; + if (peer->su.sa.sa_family != AF_INET && peer->su.sa.sa_family != AF_INET6) - return 0; // unexpected + return NULL; // unexpected + if (!peer) + return name; /* For IPv6 peering, interface (unnumbered or link-local with interface) * takes precedence over VRF. For IPv4 peering, explicit interface or * VRF are the situations to bind. @@ -471,30 +468,7 @@ static int bgp_bind(struct peer *peer) else name = peer->ifname ? peer->ifname : peer->bgp->name; - if (!name) - return 0; - - if (bgp_debug_neighbor_events(peer)) - zlog_debug("%s Binding to interface %s", peer->host, name); - - if (bgpd_privs.change(ZPRIVS_RAISE)) - zlog_err("bgp_bind: could not raise privs"); - - ret = setsockopt(peer->fd, SOL_SOCKET, SO_BINDTODEVICE, name, - strlen(name)); - myerrno = errno; - - if (bgpd_privs.change(ZPRIVS_LOWER)) - zlog_err("bgp_bind: could not lower privs"); - - if (ret < 0) { - if (bgp_debug_neighbor_events(peer)) - zlog_debug("bind to interface %s failed, errno=%d", - name, myerrno); - return ret; - } -#endif /* SO_BINDTODEVICE */ - return 0; + return name; } static int bgp_update_address(struct interface *ifp, const union sockunion *dst, @@ -571,7 +545,8 @@ int bgp_connect(struct peer *peer) if (bgpd_privs.change(ZPRIVS_RAISE)) zlog_err("Can't raise privileges"); /* Make socket for the peer. */ - peer->fd = vrf_sockunion_socket(&peer->su, peer->bgp->vrf_id, NULL); + peer->fd = vrf_sockunion_socket(&peer->su, peer->bgp->vrf_id, + bgp_get_bound_name(peer)); if (bgpd_privs.change(ZPRIVS_LOWER)) zlog_err("Can't lower privileges"); if (peer->fd < 0) @@ -605,9 +580,6 @@ int bgp_connect(struct peer *peer) if (peer->password) bgp_md5_set_connect(peer->fd, &peer->su, peer->password); - /* Bind socket. */ - bgp_bind(peer); - /* Update source bind. */ if (bgp_update_source(peer) < 0) { return connect_error; @@ -751,7 +723,8 @@ int bgp_socket(struct bgp *bgp, unsigned short port, const char *address) if (bgpd_privs.change(ZPRIVS_RAISE)) zlog_err("Can't raise privileges"); sock = vrf_socket(ainfo->ai_family, ainfo->ai_socktype, - ainfo->ai_protocol, bgp->vrf_id, NULL); + ainfo->ai_protocol, bgp->vrf_id, + NULL); if (bgpd_privs.change(ZPRIVS_LOWER)) zlog_err("Can't lower privileges"); if (sock < 0) { From 3c0eb8faa2936ed43c557fe375383d6d03646291 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 29 Jan 2018 16:56:11 +0100 Subject: [PATCH 36/41] ospfd: basic support for VRF NETNS backend The change consists in taking into account of the VRF identifier upon which the ospf socket is created. Moreover, if the VRF is a netns backend, then it is not necessary to perform the bind operations to vrf device. Also, when a VRF instance is enabled, it informs ospf VRF, and automatically OSPF VRF benefits from it. Reversely, when VRF instance is disabled, then OSPF VRF will be disabled too. Signed-off-by: Philippe Guibert --- ospfd/ospf_network.c | 41 ++++++++++------------------------------- ospfd/ospf_network.h | 1 - ospfd/ospfd.c | 29 +++++++++++++++++++---------- 3 files changed, 29 insertions(+), 42 deletions(-) diff --git a/ospfd/ospf_network.c b/ospfd/ospf_network.c index 022a5a138a..045634d8ab 100644 --- a/ospfd/ospf_network.c +++ b/ospfd/ospf_network.c @@ -169,42 +169,27 @@ int ospf_if_ipmulticast(struct ospf *top, struct prefix *p, ifindex_t ifindex) return ret; } -int ospf_bind_vrfdevice(struct ospf *ospf, int ospf_sock) -{ - int ret = 0; - -#ifdef SO_BINDTODEVICE - - if (ospf && ospf->vrf_id != VRF_DEFAULT && - ospf->vrf_id != VRF_UNKNOWN) { - ret = setsockopt(ospf_sock, SOL_SOCKET, SO_BINDTODEVICE, - ospf->name, - strlen(ospf->name)); - if (ret < 0) { - int save_errno = errno; - - zlog_warn("%s: Could not setsockopt SO_BINDTODEVICE %s", - __PRETTY_FUNCTION__, - safe_strerror(save_errno)); - } - - } -#endif - return ret; -} - int ospf_sock_init(struct ospf *ospf) { int ospf_sock; int ret, hincl = 1; int bufsize = (8 * 1024 * 1024); + /* silently ignore. already done */ + if (ospf->fd > 0) + return -1; + + if (ospf->vrf_id == VRF_UNKNOWN) { + /* silently return since VRF is not ready */ + return -1; + } if (ospfd_privs.change(ZPRIVS_RAISE)) { zlog_err("ospf_sock_init: could not raise privs, %s", safe_strerror(errno)); } - ospf_sock = socket(AF_INET, SOCK_RAW, IPPROTO_OSPFIGP); + ospf_sock = vrf_socket(AF_INET, SOCK_RAW, IPPROTO_OSPFIGP, + ospf->vrf_id, ospf->name); if (ospf_sock < 0) { int save_errno = errno; @@ -216,12 +201,6 @@ int ospf_sock_init(struct ospf *ospf) exit(1); } - ret = ospf_bind_vrfdevice(ospf, ospf_sock); - if (ret < 0) { - close(ospf_sock); - goto out; - } - #ifdef IP_HDRINCL /* we will include IP header with packet */ ret = setsockopt(ospf_sock, IPPROTO_IP, IP_HDRINCL, &hincl, diff --git a/ospfd/ospf_network.h b/ospfd/ospf_network.h index 41a7abda70..cbaf132327 100644 --- a/ospfd/ospf_network.h +++ b/ospfd/ospf_network.h @@ -30,6 +30,5 @@ extern int ospf_if_add_alldrouters(struct ospf *, struct prefix *, ifindex_t); extern int ospf_if_drop_alldrouters(struct ospf *, struct prefix *, ifindex_t); extern int ospf_if_ipmulticast(struct ospf *, struct prefix *, ifindex_t); extern int ospf_sock_init(struct ospf *ospf); -extern int ospf_bind_vrfdevice(struct ospf *, int); #endif /* _ZEBRA_OSPF_NETWORK_H */ diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 68c682c6c4..79af4a55fb 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -308,12 +308,6 @@ static struct ospf *ospf_new(u_short instance, const char *name) new->lsa_refresh_interval, &new->t_lsa_refresher); new->lsa_refresher_started = monotime(NULL); - if ((ospf_sock_init(new)) < 0) { - zlog_err( - "ospf_new: fatal error: ospf_sock_init was unable to open " - "a socket"); - exit(1); - } if ((new->ibuf = stream_new(OSPF_MAX_PACKET_SIZE + 1)) == NULL) { zlog_err( "ospf_new: fatal error: stream_new(%u) failed allocating ibuf", @@ -321,7 +315,6 @@ static struct ospf *ospf_new(u_short instance, const char *name) exit(1); } new->t_read = NULL; - thread_add_read(master, ospf_read, new, new->fd, &new->t_read); new->oi_write_q = list_new(); new->write_oi_count = OSPF_WRITE_INTERFACE_COUNT_DEFAULT; @@ -332,6 +325,16 @@ static struct ospf *ospf_new(u_short instance, const char *name) QOBJ_REG(new, ospf); + new->fd = -1; + if ((ospf_sock_init(new)) < 0) { + if (new->vrf_id != VRF_UNKNOWN) + zlog_warn( + "%s: ospf_sock_init is unable to open a socket", + __func__); + return new; + } + thread_add_read(master, ospf_read, new, new->fd, &new->t_read); + return new; } @@ -2050,6 +2053,7 @@ static int ospf_vrf_enable(struct vrf *vrf) { struct ospf *ospf = NULL; vrf_id_t old_vrf_id; + int ret = 0; if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: VRF %s id %u enabled", @@ -2070,13 +2074,15 @@ static int ospf_vrf_enable(struct vrf *vrf) zlog_err("ospf_sock_init: could not raise privs, %s", safe_strerror(errno)); } - if (ospf_bind_vrfdevice(ospf, ospf->fd) < 0) - return 0; + ret = ospf_sock_init(ospf); if (ospfd_privs.change(ZPRIVS_LOWER)) { zlog_err("ospf_sock_init: could not lower privs, %s", safe_strerror(errno)); } - + if (ret < 0 || ospf->fd <= 0) + return 0; + thread_add_read(master, ospf_read, ospf, + ospf->fd, &ospf->t_read); ospf->oi_running = 1; ospf_zebra_vrf_register(ospf); ospf_router_id_update(ospf); @@ -2111,6 +2117,9 @@ static int ospf_vrf_disable(struct vrf *vrf) if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: ospf old_vrf_id %d unlinked", __PRETTY_FUNCTION__, old_vrf_id); + thread_cancel(ospf->t_read); + close(ospf->fd); + ospf->fd = -1; } /* Note: This is a callback, the VRF will be deleted by the caller. */ From a2c999f21f037bcb5d86ff060fb2b08e9709a969 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 12 Feb 2018 23:00:04 +0100 Subject: [PATCH 37/41] lib: add debug guard for ns informational traces Informational traces are being added. Signed-off-by: Philippe Guibert --- lib/netns_linux.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/lib/netns_linux.c b/lib/netns_linux.c index 025862440a..c14d2ea4b4 100644 --- a/lib/netns_linux.c +++ b/lib/netns_linux.c @@ -54,6 +54,8 @@ static struct ns *default_ns; static int ns_current_ns_fd; static int ns_default_ns_fd; +static int ns_debug; + #ifndef CLONE_NEWNET #define CLONE_NEWNET 0x40000000 /* New network namespace (lo, device, names sockets, etc) */ @@ -164,10 +166,12 @@ static struct ns *ns_get_created_internal(struct ns *ns, char *name, } if (!created) return ns; - if (ns->ns_id != NS_UNKNOWN) - zlog_info("NS %u is created.", ns->ns_id); - else - zlog_info("NS %s is created.", ns->name); + if (ns_debug) { + if (ns->ns_id != NS_UNKNOWN) + zlog_info("NS %u is created.", ns->ns_id); + else + zlog_info("NS %s is created.", ns->name); + } if (ns_master.ns_new_hook) (*ns_master.ns_new_hook) (ns); return ns; @@ -205,11 +209,12 @@ static int ns_enable_internal(struct ns *ns, void (*func)(ns_id_t, void *)) } if (func) func(ns->ns_id, (void *)ns->vrf_ctxt); - 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_debug) { + 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); + } /* zebra first receives NS enable event, * then VRF enable event */ @@ -241,7 +246,9 @@ static int ns_is_enabled(struct ns *ns) static void ns_disable_internal(struct ns *ns) { if (ns_is_enabled(ns)) { - zlog_info("NS %u is to be disabled.", ns->ns_id); + if (ns_debug) + zlog_info("NS %u is to be disabled.", + ns->ns_id); if (ns_master.ns_disable_hook) (*ns_master.ns_disable_hook)(ns); @@ -266,7 +273,8 @@ int ns_have_netns(void) /* Delete a NS. This is called in ns_terminate(). */ void ns_delete(struct ns *ns) { - zlog_info("NS %u is to be deleted.", ns->ns_id); + if (ns_debug) + zlog_info("NS %u is to be deleted.", ns->ns_id); ns_disable(ns); @@ -398,6 +406,7 @@ void ns_init(void) { static int ns_initialised; + ns_debug = 0; /* silently return as initialisation done */ if (ns_initialised == 1) return; @@ -436,7 +445,9 @@ void ns_init_management(ns_id_t default_ns_id) } /* Set the default NS name. */ default_ns->name = XSTRDUP(MTYPE_NS_NAME, NS_DEFAULT_NAME); - zlog_info("%s: default NSID is %u", __func__, default_ns->ns_id); + if (ns_debug) + zlog_info("%s: default NSID is %u", + __func__, default_ns->ns_id); /* Enable the default NS. */ if (!ns_enable(default_ns, NULL)) { From c485b14bdc0d8e1fb2d6ad19057bf328e4118cfd Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 13 Feb 2018 10:03:43 +0100 Subject: [PATCH 38/41] lib: netns checkstyle fix A space is appended between RB_FOREACH and ' ', to comply with style practiced in frr. Signed-off-by: Philippe Guibert --- lib/netns_linux.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/netns_linux.c b/lib/netns_linux.c index c14d2ea4b4..c2282f844c 100644 --- a/lib/netns_linux.c +++ b/lib/netns_linux.c @@ -130,7 +130,7 @@ static struct ns *ns_lookup_name_internal(const char *name) { struct ns *ns = NULL; - RB_FOREACH(ns, ns_head, &ns_tree) { + RB_FOREACH (ns, ns_head, &ns_tree) { if (ns->name != NULL) { if (strcmp(name, ns->name) == 0) return ns; @@ -328,7 +328,7 @@ void ns_walk_func(int (*func)(struct ns *)) { struct ns *ns = NULL; - RB_FOREACH(ns, ns_head, &ns_tree) + RB_FOREACH (ns, ns_head, &ns_tree) func(ns); } From 281da0a94dcd14d4f628026ffd628c08df5104f1 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 26 Feb 2018 09:14:50 +0100 Subject: [PATCH 39/41] lib: do not use ENOSYS errno when returning from ns ENOSYS should not be used for other goals. Signed-off-by: Philippe Guibert --- lib/netns_linux.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/netns_linux.c b/lib/netns_linux.c index c2282f844c..0e955bade9 100644 --- a/lib/netns_linux.c +++ b/lib/netns_linux.c @@ -67,7 +67,7 @@ static inline int setns(int fd, int nstype) #ifdef __NR_setns return syscall(__NR_setns, fd, nstype); #else - errno = ENOSYS; + errno = EINVAL; return -1; #endif } @@ -480,7 +480,7 @@ int ns_switch_to_netns(const char *name) return -1; fd = open(name, O_RDONLY); if (fd == -1) { - errno = ENOSYS; + errno = EINVAL; return -1; } ret = setns(fd, CLONE_NEWNET); @@ -512,7 +512,7 @@ int ns_socket(int domain, int type, int protocol, ns_id_t ns_id) int ret; if (!ns || !ns_is_enabled(ns)) { - errno = ENOSYS; + errno = EINVAL; return -1; } if (have_netns()) { From 009f8ad5f3a99ae813d8a6739a1137443c83e345 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 13 Feb 2018 10:48:48 +0100 Subject: [PATCH 40/41] zebra: retrieve zns context from zvrf when netlink discovery So as to get the correct NETNS where some discovery must be done and populated, the zns pointer is directly retrieved from zvrf, instead of checking that the VRF is a backend NETNS or not. In the case where the interfaces are discovered before the VRF is enabled ( VRF-lite populate), then the default NS is retrieved. Signed-off-by: Philippe Guibert --- zebra/interface.c | 12 +++++------- zebra/rt_netlink.c | 28 ++++++++-------------------- 2 files changed, 13 insertions(+), 27 deletions(-) diff --git a/zebra/interface.c b/zebra/interface.c index e919d9f08f..7229b8818d 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -513,13 +513,14 @@ void if_add_update(struct interface *ifp) { struct zebra_if *if_data; struct zebra_ns *zns; + struct zebra_vrf *zvrf = vrf_info_lookup(ifp->vrf_id); - if (vrf_is_backend_netns()) - zns = zebra_ns_lookup((ns_id_t)ifp->vrf_id); + /* case interface populate before vrf enabled */ + if (zvrf->zns) + zns = zvrf->zns; else zns = zebra_ns_lookup(NS_DEFAULT); if_link_per_ns(zns, ifp); - if_data = ifp->info; assert(if_data); @@ -810,11 +811,8 @@ void if_nbr_ipv6ll_to_ipv4ll_neigh_update(struct interface *ifp, inet_pton(AF_INET, buf, &ipv4_ll); ipv6_ll_address_to_mac(address, (u_char *)mac); + ns_id = zvrf->zns->ns_id; - if (!vrf_is_backend_netns()) - ns_id = NS_DEFAULT; - else - ns_id = (ns_id_t)(ifp->vrf_id); /* * Remove existed arp record for the interface as netlink * protocol does not have update message types diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 0371c6b99b..e26109badf 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -1337,10 +1337,7 @@ static int netlink_route_multipath(int cmd, struct prefix *p, struct zebra_ns *zns; struct zebra_vrf *zvrf = vrf_info_lookup(re->vrf_id); - if (!vrf_is_backend_netns()) - zns = zebra_ns_lookup(NS_DEFAULT); - else - zns = (struct zebra_ns *)ns_info_lookup(re->vrf_id); + zns = zvrf->zns; memset(&req, 0, sizeof req - NL_PKT_BUF_SIZE); bytelen = (family == AF_INET ? 4 : 16); @@ -1640,10 +1637,7 @@ int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in) mroute = mr; struct zebra_ns *zns; - if (!vrf_is_backend_netns()) - zns = zebra_ns_lookup(NS_DEFAULT); - else - zns = (struct zebra_ns *)ns_info_lookup(zvrf->vrf->vrf_id); + zns = zvrf->zns; memset(&req.n, 0, sizeof(req.n)); memset(&req.ndm, 0, sizeof(req.ndm)); @@ -1736,11 +1730,9 @@ static int netlink_vxlan_flood_list_update(struct interface *ifp, char buf[256]; } req; u_char dst_mac[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); - if (!vrf_is_backend_netns()) - zns = zebra_ns_lookup(NS_DEFAULT); - else - zns = (struct zebra_ns *)ns_info_lookup(ifp->vrf_id); + zns = zvrf->zns; memset(&req.n, 0, sizeof(req.n)); memset(&req.ndm, 0, sizeof(req.ndm)); @@ -2046,11 +2038,9 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid, int vid_present = 0, dst_present = 0; char vid_buf[20]; char dst_buf[30]; + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); - if (!vrf_is_backend_netns()) - zns = zebra_ns_lookup(NS_DEFAULT); - else - zns = (struct zebra_ns *)ns_info_lookup(ifp->vrf_id); + zns = zvrf->zns; zif = ifp->info; if ((br_if = zif->brslave_info.br_if) == NULL) { zlog_warn("MAC %s on IF %s(%u) - no mapping to bridge", @@ -2355,11 +2345,9 @@ static int netlink_neigh_update2(struct interface *ifp, struct ipaddr *ip, struct zebra_ns *zns; char buf[INET6_ADDRSTRLEN]; char buf2[ETHER_ADDR_STRLEN]; + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); - if (!vrf_is_backend_netns()) - zns = zebra_ns_lookup(NS_DEFAULT); - else - zns = (struct zebra_ns *)ns_info_lookup(ifp->vrf_id); + zns = zvrf->zns; memset(&req.n, 0, sizeof(req.n)); memset(&req.ndm, 0, sizeof(req.ndm)); From b7b816df6bd8b110aedb0f047fa8e3105ce86d1d Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 16 Feb 2018 18:22:34 +0100 Subject: [PATCH 41/41] zebra: prevent from discovering a NS with same NSID as previous one This limitation ignores the creation of a new NS context, when an already present NS is available with the same NSID. This limitation removes confusion, so that only the first NS will be used for configuration. Signed-off-by: Philippe Guibert --- zebra/zebra_netns_notify.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c index b28998acf8..b98d6ed703 100644 --- a/zebra/zebra_netns_notify.c +++ b/zebra/zebra_netns_notify.c @@ -75,15 +75,22 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name) 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"); + /* if VRF with NS ID already present */ + vrf = vrf_lookup_by_id((vrf_id_t)ns_id); + if (vrf) { + zlog_warn("NS notify : same NSID used by VRF %s. Ignore NS %s creation", + vrf->name, netnspath); + return; + } + if (vrf_handler_create(NULL, name, &vrf) != CMD_SUCCESS) { + zlog_warn("NS notify : failed to create VRF %s", name); + return; + } ret = vrf_netns_handler_create(NULL, vrf, netnspath, ns_id); if (ret != CMD_SUCCESS) { zlog_warn("NS notify : failed to create NS %s", netnspath);