From 55cd0f612a046137f0be936e7856921ada4546ca Mon Sep 17 00:00:00 2001 From: Donald Sharp Date: Sat, 17 Feb 2018 19:02:55 -0500 Subject: *: Make assignment from RB_ROOT in while loop work better Fix up the assignment of the variable = RB_ROOT inside of while loop patter we were using. Signed-off-by: Donald Sharp --- lib/ns.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib/ns.c') diff --git a/lib/ns.c b/lib/ns.c index fdf93d0742..0b2a3bec78 100644 --- a/lib/ns.c +++ b/lib/ns.c @@ -424,8 +424,11 @@ void ns_terminate(void) { struct ns *ns; - while ((ns = RB_ROOT(ns_head, &ns_tree)) != NULL) + while (!RB_EMPTY(ns_head, &ns_tree)) { + ns = RB_ROOT(ns_head, &ns_tree); + ns_delete(ns); + } } /* Create a socket for the NS. */ -- cgit v1.2.3 From 78dd30b263948ffb1d5c22592ef2d70b331bf071 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 22 Jan 2018 09:42:53 +0100 Subject: 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(-) (limited to 'lib/ns.c') 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" -- cgit v1.2.3 From b95c18833a36bcf03b7a72c110be24873a65420d Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 6 Dec 2017 12:03:59 +0100 Subject: 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(-) (limited to 'lib/ns.c') 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); + + 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); -/* NS configuration write function. */ -static int ns_config_write(struct vty *vty) + 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"); } -- cgit v1.2.3 From c17d483845c4b772270cade18f2ce5a5e14bc14e Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 10 Jan 2018 10:04:59 +0100 Subject: 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(-) (limited to 'lib/ns.c') 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); -- cgit v1.2.3 From fe533c564e1901ee6b767708345abb52a56056af Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 8 Dec 2017 19:06:34 +0100 Subject: 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(-) (limited to 'lib/ns.c') 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) -- cgit v1.2.3 From 697d3ec73157fde8a008738907fef94fdcd569bb Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 13 Dec 2017 11:04:31 +0100 Subject: 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 | 61 +++++++++++++++++++++++++++--------- lib/vrf.h | 10 ++++++ 4 files changed, 140 insertions(+), 41 deletions(-) (limited to 'lib/ns.c') 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,32 +483,45 @@ void vrf_configure_backend(int vrf_backend_netns) vrf_backend = vrf_backend_netns; } -/* vrf CLI commands */ -DEFUN_NOSH (vrf, - vrf_cmd, - "vrf NAME", - "Select a VRF to configure\n" - "VRF's name\n") +int vrf_handler_create(struct vty *vty, const char *vrfname, struct vrf **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); + 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); - VTY_PUSH_CONTEXT(VRF_NODE, vrfp); + if (vty) + VTY_PUSH_CONTEXT(VRF_NODE, vrfp); + if (vrf) + *vrf = vrfp; return CMD_SUCCESS; } +/* vrf CLI commands */ +DEFUN_NOSH (vrf, + vrf_cmd, + "vrf NAME", + "Select a VRF to configure\n" + "VRF's name\n") +{ + int idx_name = 1; + const char *vrfname = argv[idx_name]->arg; + + return vrf_handler_create(vty, vrfname, NULL); +} + DEFUN_NOSH (no_vrf, no_vrf_cmd, "no vrf NAME", 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 -- cgit v1.2.3 From 3347430b12ecccc4f03fb29111e9621a6e123b3c Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 7 Dec 2017 18:27:31 +0100 Subject: 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(-) (limited to 'lib/ns.c') 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); -- cgit v1.2.3 From ff705b15dd5e191e727662412a8433d718887a08 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 8 Dec 2017 14:32:38 +0100 Subject: 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(-) (limited to 'lib/ns.c') 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, -- cgit v1.2.3 From 81c9005ff6edd2294ec945b93d49f03470b3b827 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 7 Dec 2017 15:58:48 +0100 Subject: 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(-) (limited to 'lib/ns.c') 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); -- cgit v1.2.3 From 4691b65ae4c3e50c295dce4fc007738080826b49 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Fri, 22 Dec 2017 16:02:51 +0100 Subject: 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(-) (limited to 'lib/ns.c') 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)); } -- cgit v1.2.3 From ce1be3692f809cfa4d533d484a75653f91c24c4e Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Wed, 20 Dec 2017 12:29:21 +0100 Subject: 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(-) (limited to 'lib/ns.c') 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 */ -- cgit v1.2.3 From ec31f30d28b65a4820a44ac658e677840ed6f88b Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Tue, 16 Jan 2018 13:59:58 +0100 Subject: 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(-) (limited to 'lib/ns.c') 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(); -- cgit v1.2.3 From e26aedbe0b569b3e88718c457210051ba2eed437 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Mon, 5 Feb 2018 16:23:42 +0100 Subject: 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/netns_linux.c | 528 ++++++++++++++++++++++++++++++++++++ lib/netns_other.c | 165 ++++++++++++ lib/ns.c | 750 ---------------------------------------------------- lib/ns.h | 75 +++++- lib/subdir.am | 5 +- lib/vrf.c | 172 ++++++++++-- lib/vrf.h | 84 ++++-- 11 files changed, 1167 insertions(+), 820 deletions(-) create mode 100644 lib/logicalrouter.c create mode 100644 lib/logicalrouter.h create mode 100644 lib/netns_linux.c create mode 100644 lib/netns_other.c delete mode 100644 lib/ns.c (limited to 'lib/ns.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/netns_linux.c b/lib/netns_linux.c new file mode 100644 index 0000000000..025862440a --- /dev/null +++ b/lib/netns_linux.c @@ -0,0 +1,528 @@ +/* + * NS functions. + * Copyright (C) 2014 6WIND S.A. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with 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_NETNS +#undef _GNU_SOURCE +#define _GNU_SOURCE + +#include +#endif + +/* for basename */ +#include + +#include "if.h" +#include "ns.h" +#include "log.h" +#include "memory.h" + +#include "command.h" +#include "vty.h" +#include "vrf.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); +static struct ns *ns_lookup_name_internal(const char *name); + +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; + +#ifndef CLONE_NEWNET +#define CLONE_NEWNET 0x40000000 +/* New network namespace (lo, device, names sockets, etc) */ +#endif + +#ifndef HAVE_SETNS +static inline int setns(int fd, int nstype) +{ +#ifdef __NR_setns + return syscall(__NR_setns, fd, nstype); +#else + errno = ENOSYS; + return -1; +#endif +} +#endif /* !HAVE_SETNS */ + +#ifdef HAVE_NETNS +static int have_netns_enabled = -1; +#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 + if (have_netns_enabled < 0) { + int fd = open(NS_DEFAULT_NAME, O_RDONLY); + + if (fd < 0) + have_netns_enabled = 0; + else { + have_netns_enabled = 1; + close(fd); + } + } + return have_netns_enabled; +#else + return 0; +#endif +} + +/* Holding NS hooks */ +struct ns_master { + 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, +}; + +static int ns_is_enabled(struct ns *ns); + +static inline int ns_compare(const struct ns *a, const struct ns *b) +{ + return (a->ns_id - b->ns_id); +} + +/* 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. + */ + 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 ns; +} + +/* + * 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 *)) +{ + 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; + } + + 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; + } + 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); + /* 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(). */ +void ns_delete(struct ns *ns) +{ + zlog_info("NS %u is to be deleted.", ns->ns_id); + + ns_disable(ns); + + if (ns_master.ns_delete_hook) + (*ns_master.ns_delete_hook)(ns); + + /* + * I'm not entirely sure if the vrf->iflist + * needs to be moved into here or not. + */ + // if_terminate (&ns->iflist); + + RB_REMOVE(ns_head, &ns_tree, ns); + if (ns->name) + XFREE(MTYPE_NS_NAME, ns->name); + + XFREE(MTYPE_NS, ns); +} + +/* Look up the data pointer of the specified VRF. */ +void * +ns_info_lookup(ns_id_t 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; + + RB_FOREACH(ns, ns_head, &ns_tree) + func(ns); +} + +const char *ns_get_name(struct ns *ns) +{ + if (!ns) + return NULL; + return ns->name; +} + +/* Add a NS hook. Please add hooks before calling ns_init(). */ +void ns_add_hook(int type, int (*func)(struct ns *)) +{ + switch (type) { + case NS_NEW_HOOK: + ns_master.ns_new_hook = func; + break; + case NS_DELETE_HOOK: + ns_master.ns_delete_hook = func; + break; + case NS_ENABLE_HOOK: + ns_master.ns_enable_hook = func; + break; + case NS_DISABLE_HOOK: + ns_master.ns_disable_hook = func; + break; + default: + break; + } +} + +/* + * NS realization with NETNS + */ + +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); + else { + /* relevant pathname */ + char tmp_name[PATH_MAX]; + + snprintf(tmp_name, PATH_MAX, "%s/%s", NS_RUN_DIR, name); + result = realpath(tmp_name, pathname); + } + + if (!result) { + 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)\n", + check_base, NS_NAMSIZ-1); + else + zlog_warn("NS name (%s) invalid: too long (>%d)", + check_base, NS_NAMSIZ-1); + return NULL; + } + return pathname; +} + +void ns_init(void) +{ + static int ns_initialised; + + /* silently return as initialisation done */ + if (ns_initialised == 1) + return; + 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_management(ns_id_t default_ns_id) +{ + int fd; + + ns_init(); + default_ns = ns_get_created_internal(NULL, NULL, default_ns_id); + if (!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; + } + /* 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); + + /* Enable the default NS. */ + if (!ns_enable(default_ns, NULL)) { + zlog_err("%s: failed to enable the default NS!", + __func__); + exit(1); + } +} + +/* Terminate NS module. */ +void ns_terminate(void) +{ + struct ns *ns; + + while (!RB_EMPTY(ns_head, &ns_tree)) { + ns = RB_ROOT(ns_head, &ns_tree); + + ns_delete(ns); + } +} + +int ns_switch_to_netns(const char *name) +{ + int ret; + int fd; + + if (name == NULL) + return -1; + if (ns_default_ns_fd == -1) + 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 && ns_default_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) +{ + struct ns *ns = ns_lookup(ns_id); + int ret; + + if (!ns || !ns_is_enabled(ns)) { + errno = ENOSYS; + return -1; + } + if (have_netns()) { + ret = (ns_id != NS_DEFAULT) ? setns(ns->fd, CLONE_NEWNET) : 0; + if (ret >= 0) { + ret = socket(domain, type, protocol); + if (ns_id != NS_DEFAULT) { + setns(ns_lookup(NS_DEFAULT)->fd, CLONE_NEWNET); + ns_current_ns_fd = ns_id; + } + } + } else + ret = socket(domain, type, protocol); + + 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/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.c b/lib/ns.c deleted file mode 100644 index 17d70a12fe..0000000000 --- a/lib/ns.c +++ /dev/null @@ -1,750 +0,0 @@ -/* - * NS functions. - * Copyright (C) 2014 6WIND S.A. - * - * This file is part of GNU Zebra. - * - * GNU Zebra is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * GNU Zebra is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with 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_NETNS -#undef _GNU_SOURCE -#define _GNU_SOURCE - -#include -#endif - -/* for basename */ -#include - -#include "if.h" -#include "ns.h" -#include "log.h" -#include "memory.h" - -#include "command.h" -#include "vty.h" -#include "vrf.h" - -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) - -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; - -#ifndef CLONE_NEWNET -#define CLONE_NEWNET 0x40000000 /* New network namespace (lo, device, names sockets, etc) */ -#endif - -#ifndef HAVE_SETNS -static inline int setns(int fd, int nstype) -{ -#ifdef __NR_setns - return syscall(__NR_setns, fd, nstype); -#else - errno = ENOSYS; - return -1; -#endif -} -#endif /* HAVE_SETNS */ - -#ifdef HAVE_NETNS -static int have_netns_enabled = -1; -#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 - if (have_netns_enabled < 0) { - int fd = open(NS_DEFAULT_NAME, O_RDONLY); - - if (fd < 0) - have_netns_enabled = 0; - else { - have_netns_enabled = 1; - close(fd); - } - } - return have_netns_enabled; -#else - return 0; -#endif -} - -/* Holding NS hooks */ -struct ns_master { - 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, -}; - -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); - 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) -{ - struct ns *ns; - - ns = ns_lookup_name(ns_name); - if (ns) - return (ns); - - 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; -} - -/* Delete a NS. This is called in ns_terminate(). */ -static void ns_delete(struct ns *ns) -{ - zlog_info("NS %u is to be deleted.", ns->ns_id); - - ns_disable(ns); - - if (ns_master.ns_delete_hook) - (*ns_master.ns_delete_hook)(ns); - - /* - * I'm not entirely sure if the vrf->iflist - * needs to be moved into here or not. - */ - // if_terminate (&ns->iflist); - - RB_REMOVE(ns_head, &ns_tree, ns); - if (ns->name) - XFREE(MTYPE_NS_NAME, ns->name); - - XFREE(MTYPE_NS, ns); -} - -/* Look up a NS by identifier. */ -static struct ns *ns_lookup(ns_id_t ns_id) -{ - struct 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); - - 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); -} - -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) -{ - 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 *)) -{ - switch (type) { - case NS_NEW_HOOK: - ns_master.ns_new_hook = func; - break; - case NS_DELETE_HOOK: - ns_master.ns_delete_hook = func; - break; - case NS_ENABLE_HOOK: - ns_master.ns_enable_hook = func; - break; - case NS_DISABLE_HOOK: - ns_master.ns_disable_hook = func; - break; - default: - break; - } -} - -/* - * NS realization with NETNS - */ - -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); - else /* relevant pathname */ - { - char tmp_name[PATH_MAX]; - snprintf(tmp_name, PATH_MAX, "%s/%s", NS_RUN_DIR, name); - result = realpath(tmp_name, pathname); - } - - if (!result) { - 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; -} - -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); - return; - } -#endif /* HAVE_NETNS */ - ns_default_ns_fd = -1; - default_ns = NULL; -} - -/* Initialize NS module. */ -void ns_init_zebra(ns_id_t default_ns_id) -{ - int fd; - - ns_init(); - 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)) { - zlog_err("ns_init: failed to enable the default NS!"); - 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. */ -void ns_terminate(void) -{ - struct ns *ns; - - while (!RB_EMPTY(ns_head, &ns_tree)) { - ns = RB_ROOT(ns_head, &ns_tree); - - ns_delete(ns); - } -} - -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) -{ - struct ns *ns = ns_lookup(ns_id); - int ret; - - if (!ns || !ns_is_enabled(ns)) { - errno = ENOSYS; - return -1; - } - - if (have_netns()) { - ret = (ns_id != NS_DEFAULT) ? setns(ns->fd, CLONE_NEWNET) : 0; - if (ret >= 0) { - ret = socket(domain, type, protocol); - if (ns_id != NS_DEFAULT) { - setns(ns_lookup(NS_DEFAULT)->fd, CLONE_NEWNET); - ns_current_ns_fd = ns_id; - } - } - } else - ret = socket(domain, type, protocol); - - 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 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); +/* Create a socket serving for the given NS + */ +int ns_socket(int domain, int type, int protocol, ns_id_t ns_id); + +/* return the path of the NETNS */ extern char *ns_netns_pathname(struct vty *vty, const char *name); -extern void *ns_info_lookup(ns_id_t ns_id); + +/* 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); -/* API that can be used by all daemons */ +#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); -extern void ns_init(void); +/* + * 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); -/* The default NS ID */ -#define NS_DEFAULT ns_get_default_id() +/* 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,52 +200,79 @@ 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); - -/* 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); +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); -extern int vrf_sockunion_socket(const union sockunion *su, 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); + /* 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 +/* + * VRF backend routines + * should be called from zebra only */ -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 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*/ -- cgit v1.2.3