diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index de453de0c8..de11a98a20 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -289,7 +289,8 @@ void bgp_timer_set(struct peer *peer) /* First entry point of peer's finite state machine. In Idle status start timer is on unless peer is shutdown or peer is inactive. All other timer must be turned off */ - if (BGP_PEER_START_SUPPRESSED(peer) || !peer_active(peer)) { + if (BGP_PEER_START_SUPPRESSED(peer) || !peer_active(peer) + || peer->bgp->vrf_id == VRF_UNKNOWN) { BGP_TIMER_OFF(peer->t_start); } else { BGP_TIMER_ON(peer->t_start, bgp_start_timer, @@ -1376,6 +1377,15 @@ int bgp_start(struct peer *peer) return 0; } + if (peer->bgp && + peer->bgp->vrf_id == VRF_UNKNOWN) { + if (bgp_debug_neighbor_events(peer)) + zlog_err( + "%s [FSM] In a VRF that is not initialised yet", + peer->host); + return -1; + } + /* Register to be notified on peer up */ if (peer->sort == BGP_PEER_EBGP && peer->ttl == 1 && !CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK) diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 0508f4846d..82c74e4afa 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -41,6 +41,7 @@ #include "vrf.h" #include "bfd.h" #include "libfrr.h" +#include "ns.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_attr.h" @@ -57,6 +58,7 @@ #include "bgpd/bgp_zebra.h" #include "bgpd/bgp_packet.h" #include "bgpd/bgp_keepalives.h" +#include "bgpd/bgp_network.h" #ifdef ENABLE_BGP_VNC #include "bgpd/rfapi/rfapi_backend.h" @@ -259,6 +261,7 @@ static int bgp_vrf_enable(struct vrf *vrf) /* We have instance configured, link to VRF and make it "up". */ bgp_vrf_link(bgp, vrf); + bgp_handle_socket(bgp, vrf, old_vrf_id, true); /* Update any redistribute vrf bitmaps if the vrf_id changed */ if (old_vrf_id != bgp->vrf_id) bgp_update_redist_vrf_bitmaps(bgp, old_vrf_id); @@ -282,6 +285,7 @@ static int bgp_vrf_disable(struct vrf *vrf) bgp = bgp_lookup_by_name(vrf->name); if (bgp) { old_vrf_id = bgp->vrf_id; + bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, false); /* We have instance configured, unlink from VRF and make it * "down". */ bgp_vrf_unlink(bgp, vrf); diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index bf39cbe1fc..0ab583f444 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -34,6 +34,7 @@ #include "queue.h" #include "hash.h" #include "filter.h" +#include "ns.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_open.h" @@ -44,13 +45,14 @@ extern struct zebra_privs_t bgpd_privs; -static int bgp_bind(struct peer *); +static char *bgp_get_bound_name(struct peer *peer); /* BGP listening socket. */ struct bgp_listener { int fd; union sockunion su; struct thread *thread; + struct bgp *bgp; }; /* @@ -284,6 +286,7 @@ static int bgp_accept(struct thread *thread) return -1; } listener->thread = NULL; + thread_add_read(bm->master, bgp_accept, listener, accept_sock, &listener->thread); @@ -296,8 +299,13 @@ static int bgp_accept(struct thread *thread) } set_nonblocking(bgp_sock); - /* Obtain BGP instance this connection is meant for. */ - if (bgp_get_instance_for_inc_conn(bgp_sock, &bgp)) { + /* Obtain BGP instance this connection is meant for. + * - if it is a VRF netns sock, then BGP is in listener structure + * - otherwise, the bgp instance need to be demultiplexed + */ + if (listener->bgp) + bgp = listener->bgp; + else if (bgp_get_instance_for_inc_conn(bgp_sock, &bgp)) { if (bgp_debug_neighbor_events(NULL)) zlog_debug( "[Event] Could not get instance for incoming conn from %s", @@ -407,7 +415,7 @@ static int bgp_accept(struct thread *thread) peer->doppelganger = peer1; peer1->doppelganger = peer; peer->fd = bgp_sock; - bgp_bind(peer); + vrf_bind(peer->bgp->vrf_id, bgp_sock, bgp_get_bound_name(peer)); bgp_fsm_change_status(peer, Active); BGP_TIMER_OFF(peer->t_start); /* created in peer_create() */ @@ -435,21 +443,20 @@ static int bgp_accept(struct thread *thread) } /* BGP socket bind. */ -static int bgp_bind(struct peer *peer) +static char *bgp_get_bound_name(struct peer *peer) { -#ifdef SO_BINDTODEVICE - int ret; - int myerrno; char *name = NULL; - /* If not bound to an interface or part of a VRF, we don't care. */ - if (!peer->bgp->vrf_id && !peer->ifname && !peer->conf_if) - return 0; + if ((peer->bgp->vrf_id == VRF_DEFAULT) && + !peer->ifname && !peer->conf_if) + return NULL; if (peer->su.sa.sa_family != AF_INET && peer->su.sa.sa_family != AF_INET6) - return 0; // unexpected + return NULL; // unexpected + if (!peer) + return name; /* For IPv6 peering, interface (unnumbered or link-local with interface) * takes precedence over VRF. For IPv4 peering, explicit interface or * VRF are the situations to bind. @@ -461,30 +468,7 @@ static int bgp_bind(struct peer *peer) else name = peer->ifname ? peer->ifname : peer->bgp->name; - if (!name) - return 0; - - if (bgp_debug_neighbor_events(peer)) - zlog_debug("%s Binding to interface %s", peer->host, name); - - if (bgpd_privs.change(ZPRIVS_RAISE)) - zlog_err("bgp_bind: could not raise privs"); - - ret = setsockopt(peer->fd, SOL_SOCKET, SO_BINDTODEVICE, name, - strlen(name)); - myerrno = errno; - - if (bgpd_privs.change(ZPRIVS_LOWER)) - zlog_err("bgp_bind: could not lower privs"); - - if (ret < 0) { - if (bgp_debug_neighbor_events(peer)) - zlog_debug("bind to interface %s failed, errno=%d", - name, myerrno); - return ret; - } -#endif /* SO_BINDTODEVICE */ - return 0; + return name; } static int bgp_update_address(struct interface *ifp, const union sockunion *dst, @@ -558,8 +542,13 @@ int bgp_connect(struct peer *peer) zlog_debug("Peer address not learnt: Returning from connect"); return 0; } + if (bgpd_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); /* Make socket for the peer. */ - peer->fd = sockunion_socket(&peer->su); + peer->fd = vrf_sockunion_socket(&peer->su, peer->bgp->vrf_id, + bgp_get_bound_name(peer)); + if (bgpd_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); if (peer->fd < 0) return -1; @@ -591,9 +580,6 @@ int bgp_connect(struct peer *peer) if (peer->password) bgp_md5_set_connect(peer->fd, &peer->su, peer->password); - /* Bind socket. */ - bgp_bind(peer); - /* Update source bind. */ if (bgp_update_source(peer) < 0) { return connect_error; @@ -642,12 +628,12 @@ int bgp_getsockname(struct peer *peer) return -1; #endif } - return 0; } -static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen) +static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen, + struct bgp *bgp) { struct bgp_listener *listener; int ret, en; @@ -683,8 +669,14 @@ static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen) return ret; } - listener = XMALLOC(MTYPE_BGP_LISTENER, sizeof(*listener)); + listener = XCALLOC(MTYPE_BGP_LISTENER, sizeof(*listener)); listener->fd = sock; + + /* this socket needs a change of ns. record bgp back pointer */ + if (bgp->vrf_id != VRF_DEFAULT && + vrf_is_mapped_on_netns(bgp->vrf_id)) + listener->bgp = bgp; + memcpy(&listener->su, sa, salen); listener->thread = NULL; thread_add_read(bm->master, bgp_accept, listener, sock, @@ -695,7 +687,7 @@ static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen) } /* IPv6 supported version of BGP server socket setup. */ -int bgp_socket(unsigned short port, const char *address) +int bgp_socket(struct bgp *bgp, unsigned short port, const char *address) { struct addrinfo *ainfo; struct addrinfo *ainfo_save; @@ -710,7 +702,12 @@ int bgp_socket(unsigned short port, const char *address) snprintf(port_str, sizeof(port_str), "%d", port); port_str[sizeof(port_str) - 1] = '\0'; - ret = getaddrinfo(address, port_str, &req, &ainfo_save); + if (bgpd_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); + ret = vrf_getaddrinfo(address, port_str, &req, + &ainfo_save, bgp->vrf_id); + if (bgpd_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); if (ret != 0) { zlog_err("getaddrinfo: %s", gai_strerror(ret)); return -1; @@ -723,8 +720,13 @@ int bgp_socket(unsigned short port, const char *address) if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6) continue; - sock = socket(ainfo->ai_family, ainfo->ai_socktype, - ainfo->ai_protocol); + if (bgpd_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); + sock = vrf_socket(ainfo->ai_family, ainfo->ai_socktype, + ainfo->ai_protocol, bgp->vrf_id, + NULL); + if (bgpd_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); if (sock < 0) { zlog_err("socket: %s", safe_strerror(errno)); continue; @@ -734,7 +736,8 @@ int bgp_socket(unsigned short port, const char *address) * ttl=255 */ sockopt_ttl(ainfo->ai_family, sock, MAXTTL); - ret = bgp_listener(sock, ainfo->ai_addr, ainfo->ai_addrlen); + ret = bgp_listener(sock, ainfo->ai_addr, + ainfo->ai_addrlen, bgp); if (ret == 0) ++count; else @@ -751,6 +754,32 @@ int bgp_socket(unsigned short port, const char *address) return 0; } +/* this function closes vrf socket + * this should be called only for vrf socket with netns backend + */ +void bgp_close_vrf_socket(struct bgp *bgp) +{ + struct listnode *node, *next; + struct bgp_listener *listener; + + if (!bgp) + return; + + if (bm->listen_sockets == NULL) + return; + + for (ALL_LIST_ELEMENTS(bm->listen_sockets, node, next, listener)) { + if (listener->bgp == bgp) { + thread_cancel(listener->thread); + close(listener->fd); + listnode_delete(bm->listen_sockets, listener); + XFREE(MTYPE_BGP_LISTENER, listener); + } + } +} + +/* this function closes main socket + */ void bgp_close(void) { struct listnode *node, *next; @@ -760,6 +789,8 @@ void bgp_close(void) return; for (ALL_LIST_ELEMENTS(bm->listen_sockets, node, next, listener)) { + if (listener->bgp) + continue; thread_cancel(listener->thread); close(listener->fd); listnode_delete(bm->listen_sockets, listener); diff --git a/bgpd/bgp_network.h b/bgpd/bgp_network.h index 75ff1305c2..f18484e000 100644 --- a/bgpd/bgp_network.h +++ b/bgpd/bgp_network.h @@ -23,7 +23,9 @@ #define BGP_SOCKET_SNDBUF_SIZE 65536 -extern int bgp_socket(unsigned short, const char *); +extern int bgp_socket(struct bgp *bgp, unsigned short port, + const char *address); +extern void bgp_close_vrf_socket(struct bgp *bgp); extern void bgp_close(void); extern int bgp_connect(struct peer *); extern int bgp_getsockname(struct peer *); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 78e748fb6c..07b9df31f2 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -101,6 +101,42 @@ static void bgp_if_finish(struct bgp *bgp); extern struct zclient *zclient; +/* handle main socket creation or deletion */ +static int bgp_check_main_socket(bool create, struct bgp *bgp) +{ + static int bgp_server_main_created; + struct listnode *bgpnode, *nbgpnode; + struct bgp *bgp_temp; + + if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) + return 0; + if (create == true) { + if (bgp_server_main_created) + return 0; + if (bgp_socket(bgp, bm->port, bm->address) < 0) + return BGP_ERR_INVALID_VALUE; + bgp_server_main_created = 1; + return 0; + } + if (!bgp_server_main_created) + return 0; + /* only delete socket on some cases */ + for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp_temp)) { + /* do not count with current bgp */ + if (bgp_temp == bgp) + continue; + /* if other instance non VRF, do not delete socket */ + if (bgp_temp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) + return 0; + /* vrf lite, do not delete socket */ + if (!vrf_is_mapped_on_netns(bgp_temp->vrf_id)) + return 0; + } + bgp_close(); + bgp_server_main_created = 0; + return 0; +} + void bgp_session_reset(struct peer *peer) { if (peer->doppelganger && (peer->doppelganger->status != Deleted) @@ -2981,11 +3017,67 @@ struct bgp *bgp_lookup_by_vrf_id(vrf_id_t vrf_id) return (vrf->info) ? (struct bgp *)vrf->info : NULL; } +/* handle socket creation or deletion, if necessary + * this is called for all new BGP instances + */ +int bgp_handle_socket(struct bgp *bgp, struct vrf *vrf, + vrf_id_t old_vrf_id, bool create) +{ + int ret = 0; + + /* Create BGP server socket, if listen mode not disabled */ + if (!bgp || bgp_option_check(BGP_OPT_NO_LISTEN)) + return 0; + if (bgp->name + && bgp->inst_type == BGP_INSTANCE_TYPE_VRF + && vrf) { + /* + * suppress vrf socket + */ + if (create == FALSE) { + if (vrf_is_mapped_on_netns(vrf->vrf_id)) + bgp_close_vrf_socket(bgp); + else + ret = bgp_check_main_socket(create, bgp); + return ret; + } + /* do nothing + * if vrf_id did not change + */ + if (vrf->vrf_id == old_vrf_id) + return 0; + if (old_vrf_id != VRF_UNKNOWN) { + /* look for old socket. close it. */ + bgp_close_vrf_socket(bgp); + } + /* if backend is not yet identified ( VRF_UNKNOWN) then + * creation will be done later + */ + if (vrf->vrf_id == VRF_UNKNOWN) + return 0; + /* if BGP VRF instance requested + * if backend is NETNS, create BGP server socket in the NETNS + */ + if (vrf_is_mapped_on_netns(bgp->vrf_id)) { + ret = bgp_socket(bgp, bm->port, bm->address); + if (ret < 0) + return BGP_ERR_INVALID_VALUE; + return 0; + } + } + /* if BGP VRF instance requested or VRF lite backend + * if BGP non VRF instance, create it + * if not already done + */ + return bgp_check_main_socket(create, bgp); +} + /* Called from VTY commands. */ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, enum bgp_instance_type inst_type) { struct bgp *bgp; + struct vrf *vrf = NULL; /* Multiple instance check. */ if (bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { @@ -3033,25 +3125,19 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, bgp->t_rmap_def_originate_eval = NULL; - /* Create BGP server socket, if first instance. */ - if (list_isempty(bm->bgp) && !bgp_option_check(BGP_OPT_NO_LISTEN)) { - if (bgp_socket(bm->port, bm->address) < 0) - return BGP_ERR_INVALID_VALUE; - } - - listnode_add(bm->bgp, bgp); - /* If Default instance or VRF, link to the VRF structure, if present. */ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT || bgp->inst_type == BGP_INSTANCE_TYPE_VRF) { - struct vrf *vrf; - vrf = bgp_vrf_lookup_by_instance_type(bgp); if (vrf) bgp_vrf_link(bgp, vrf); } + /* BGP server socket already processed if BGP instance + * already part of the list + */ + bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, true); + listnode_add(bm->bgp, bgp); - /* Register with Zebra, if needed */ if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) bgp_zebra_instance_register(bgp); @@ -3188,8 +3274,6 @@ int bgp_delete(struct bgp *bgp) * routes to be processed still referencing the struct bgp. */ listnode_delete(bm->bgp, bgp); - if (list_isempty(bm->bgp)) - bgp_close(); /* Deregister from Zebra, if needed */ if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) @@ -3199,6 +3283,7 @@ int bgp_delete(struct bgp *bgp) bgp_if_finish(bgp); vrf = bgp_vrf_lookup_by_instance_type(bgp); + bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, false); if (vrf) bgp_vrf_unlink(bgp, vrf); @@ -3337,11 +3422,12 @@ struct peer *peer_lookup(struct bgp *bgp, union sockunion *su) struct listnode *bgpnode, *nbgpnode; for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp)) { - /* Skip VRFs, this function will not be invoked without - * an instance + /* Skip VRFs Lite only, this function will not be + * invoked without an instance * when examining VRFs. */ - if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) + if ((bgp->inst_type == BGP_INSTANCE_TYPE_VRF) && + !vrf_is_mapped_on_netns(bgp->vrf_id)) continue; peer = hash_lookup(bgp->peerhash, &tmp_peer); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 91a9f11620..7f55b753ab 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -1355,6 +1355,9 @@ extern void bgp_instance_up(struct bgp *); extern void bgp_instance_down(struct bgp *); extern int bgp_delete(struct bgp *); +extern int bgp_handle_socket(struct bgp *bgp, struct vrf *vrf, + vrf_id_t old_vrf_id, bool create); + extern int bgp_flag_set(struct bgp *, int); extern int bgp_flag_unset(struct bgp *, int); extern int bgp_flag_check(struct bgp *, int); diff --git a/doc/zebra.8.in b/doc/zebra.8.in index 7f4a81b1a0..3e1d10e068 100644 --- a/doc/zebra.8.in +++ b/doc/zebra.8.in @@ -92,6 +92,11 @@ maximum before starting zebra. Note that this affects Linux only. .TP +\fB\-n\fR, \fB\-\-vrfwnetns \fR\fIEnable namespace VRF\fR +Enable namespace VRF backend. By default, the VRF backend relies on VRF-lite +support from Linux devices. This option permits discovering using Linux named +Netns and map it to FRR VRF contexts. +.TP \fB\-M\fR, \fB\-\-module \fR\fImodule:options\fR Load a module at startup. May be specified more than once. The \fBsnmp\fR and \fBfpm\fR modules may be diff --git a/include/linux/net_namespace.h b/include/linux/net_namespace.h new file mode 100644 index 0000000000..9a92b7e14a --- /dev/null +++ b/include/linux/net_namespace.h @@ -0,0 +1,23 @@ +/* Copyright (c) 2015 6WIND S.A. + * Author: Nicolas Dichtel + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ +#ifndef _LINUX_NET_NAMESPACE_H_ +#define _LINUX_NET_NAMESPACE_H_ + +/* Attributes of RTM_NEWNSID/RTM_GETNSID messages */ +enum { + NETNSA_NONE, +#define NETNSA_NSID_NOT_ASSIGNED -1 + NETNSA_NSID, + NETNSA_PID, + NETNSA_FD, + __NETNSA_MAX, +}; + +#define NETNSA_MAX (__NETNSA_MAX - 1) + +#endif /* _LINUX_NET_NAMESPACE_H_ */ diff --git a/include/subdir.am b/include/subdir.am index 98bd148002..7a12b2ffae 100644 --- a/include/subdir.am +++ b/include/subdir.am @@ -6,4 +6,5 @@ noinst_HEADERS += \ include/linux/neighbour.h \ include/linux/rtnetlink.h \ include/linux/socket.h \ + include/linux/net_namespace.h \ # end diff --git a/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/if.c b/lib/if.c index 12d123a8fa..3a83de46ae 100644 --- a/lib/if.c +++ b/lib/if.c @@ -384,29 +384,35 @@ struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id, int vty) { struct interface *ifp; + ifp = if_lookup_by_name(name, vrf_id); + if (ifp) + return ifp; + /* Not Found on same VRF. If the interface command + * was entered in vty without a VRF (passed as VRF_DEFAULT), + * accept the ifp we found. If a vrf was entered and there is + * a mismatch, reject it if from vty. + */ ifp = if_lookup_by_name_all_vrf(name); - if (ifp) { - if (ifp->vrf_id == vrf_id) + if (!ifp) + return if_create(name, vrf_id); + if (vty) { + if (vrf_id == VRF_DEFAULT) return ifp; - - /* Found a match on a different VRF. If the interface command - * was entered in vty without a VRF (passed as VRF_DEFAULT), - * accept the ifp we found. If a vrf was entered and there is - * a mismatch, reject it if from vty. If it came from the kernel - * or by way of zclient, believe it and update the ifp - * accordingly. - */ - if (vty) { - if (vrf_id == VRF_DEFAULT) - return ifp; - return NULL; - } else { - if_update_to_new_vrf(ifp, vrf_id); - return ifp; - } + return NULL; } - - return if_create(name, vrf_id); + /* if vrf backend uses NETNS, then + * this should not be considered as an update + * then create the new interface + */ + if (ifp->vrf_id != vrf_id && + vrf_is_mapped_on_netns(vrf_id)) + return if_create(name, vrf_id); + /* If it came from the kernel + * or by way of zclient, believe it and update + * the ifp accordingly. + */ + if_update_to_new_vrf(ifp, vrf_id); + return ifp; } void if_set_index(struct interface *ifp, ifindex_t ifindex) 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..0e955bade9 --- /dev/null +++ b/lib/netns_linux.c @@ -0,0 +1,539 @@ +/* + * 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; + +static int ns_debug; + +#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 = EINVAL; + 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_debug) { + if (ns->ns_id != NS_UNKNOWN) + zlog_info("NS %u is created.", ns->ns_id); + else + zlog_info("NS %s is created.", ns->name); + } + if (ns_master.ns_new_hook) + (*ns_master.ns_new_hook) (ns); + return ns; +} + +/* + * 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 (ns_debug) { + if (have_netns()) + zlog_info("NS %u is associated with NETNS %s.", + ns->ns_id, ns->name); + zlog_info("NS %u is enabled.", ns->ns_id); + } + /* zebra first receives NS enable event, + * then VRF enable event + */ + 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)) { + if (ns_debug) + zlog_info("NS %u is to be disabled.", + ns->ns_id); + + if (ns_master.ns_disable_hook) + (*ns_master.ns_disable_hook)(ns); + + 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) +{ + if (ns_debug) + 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; + + ns_debug = 0; + /* 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); + if (ns_debug) + zlog_info("%s: default NSID is %u", + __func__, default_ns->ns_id); + + /* Enable the default NS. */ + if (!ns_enable(default_ns, NULL)) { + 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 = EINVAL; + 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 = EINVAL; + 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 0b2a3bec78..0000000000 --- a/lib/ns.c +++ /dev/null @@ -1,456 +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 - -#include "if.h" -#include "ns.h" -#include "log.h" -#include "memory.h" - -#include "command.h" -#include "vty.h" - -DEFINE_MTYPE_STATIC(LIB, NS, "Logical-Router") -DEFINE_MTYPE_STATIC(LIB, NS_NAME, "Logical-Router Name") - -static __inline int ns_compare(const struct ns *, const struct ns *); -static struct ns *ns_lookup(ns_id_t); - -RB_GENERATE(ns_head, ns, entry, ns_compare) - -struct ns_head ns_tree = RB_INITIALIZER(&ns_tree); - -#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 - -#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 */ - -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)(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 **); -} 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 __inline int ns_compare(const struct ns *a, const struct ns *b) -{ - return (a->ns_id - b->ns_id); -} - -/* 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); - - /* - * Initialize interfaces. - * - * I'm not sure if this belongs here or in - * the vrf code. - */ - // if_init (&ns->iflist); - - zlog_info("NS %u is created.", ns_id); - - if (ns_master.ns_new_hook) - (*ns_master.ns_new_hook)(ns_id, &ns->info); - - 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->ns_id, &ns->info); - - /* - * 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)); -} - -/* - * 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) -{ - - 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; - } - - if (have_netns()) - zlog_info("NS %u is associated with NETNS %s.", - ns->ns_id, ns->name); - - zlog_info("NS %u is enabled.", ns->ns_id); - if (ns_master.ns_enable_hook) - (*ns_master.ns_enable_hook)(ns->ns_id, &ns->info); - } - - 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->ns_id, &ns->info); - - 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)(ns_id_t, void **)) -{ - 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 - */ - -static char *ns_netns_pathname(struct vty *vty, const char *name) -{ - static char pathname[PATH_MAX]; - char *result; - - 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) { - vty_out(vty, "Invalid pathname: %s\n", safe_strerror(errno)); - return NULL; - } - return pathname; -} - -DEFUN_NOSH (ns_netns, - ns_netns_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 = NS_DEFAULT; - 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; -} - -DEFUN (no_ns_netns, - no_ns_netns_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 = NS_DEFAULT; - 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; -} - -/* NS node. */ -static struct cmd_node ns_node = {NS_NODE, "", /* NS node has no interface. */ - 1}; - -/* NS configuration write function. */ -static int ns_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; -} - -/* Initialize NS module. */ -void ns_init(void) -{ - struct ns *default_ns; - - /* The default NS always exists. */ - default_ns = ns_get(NS_DEFAULT); - if (!default_ns) { - zlog_err("ns_init: failed to create the default NS!"); - exit(1); - } - - /* Set the default NS name. */ - default_ns->name = XSTRDUP(MTYPE_NS_NAME, NS_DEFAULT_NAME); - - /* Enable the default NS. */ - if (!ns_enable(default_ns)) { - zlog_err("ns_init: failed to enable the default NS!"); - exit(1); - } - - if (have_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); - } -} - -/* 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); - } -} - -/* 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 = -1; - - if (!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); - } - } else - ret = socket(domain, type, protocol); - - return ret; -} diff --git a/lib/ns.h b/lib/ns.h index 79b4cab04d..83e5e1b907 100644 --- a/lib/ns.h +++ b/lib/ns.h @@ -24,16 +24,22 @@ #include "openbsd-tree.h" #include "linklist.h" +#include "vty.h" 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; @@ -49,6 +55,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; }; @@ -57,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 */ @@ -74,20 +88,82 @@ 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_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); +/* 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); + +/* 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); + +#define NS_DEFAULT ns_get_default_id() + +/* API that can be used to change from NS */ +extern int ns_switchback_to_initial(void); +extern int ns_switch_to_netns(const char *netns_name); + +/* + * NS handling routines. + * called by modules that use NS backend + */ + +/* API to search for already present NETNS */ +extern struct ns *ns_lookup(ns_id_t ns_id); +extern struct ns *ns_lookup_name(const char *name); + +/* API to handle NS : creation, enable, disable + * for enable, a callback function is passed as parameter + * the callback belongs to the module that uses NS as backend + * upon enabling the NETNS, the upper layer is informed + */ +extern int ns_enable(struct ns *ns, void (*func)(ns_id_t, void *)); +extern struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id); +extern void ns_disable(struct ns *ns); #endif /*_ZEBRA_NS_H*/ diff --git a/lib/subdir.am b/lib/subdir.am index 44870917bd..e292d7a342 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -42,7 +42,8 @@ lib_libfrr_la_SOURCES = \ lib/module.c \ lib/network.c \ lib/nexthop.c \ - lib/ns.c \ + lib/netns_linux.c \ + lib/netns_other.c \ lib/openbsd-tree.c \ lib/pid_output.c \ lib/plist.c \ @@ -74,6 +75,7 @@ lib_libfrr_la_SOURCES = \ lib/wheel.c \ lib/workqueue.c \ lib/zclient.c \ + lib/logicalrouter.c \ # end lib/plist_clippy.c: $(CLIPPY_DEPS) @@ -158,6 +160,7 @@ pkginclude_HEADERS += \ lib/zassert.h \ lib/zclient.h \ lib/zebra.h \ + lib/logicalrouter.h \ # end nodist_pkginclude_HEADERS += \ diff --git a/lib/vrf.c b/lib/vrf.c index 02946df2bc..ea106b90a2 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" @@ -29,6 +32,10 @@ #include "log.h" #include "memory.h" #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") @@ -44,6 +51,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. @@ -61,7 +70,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) @@ -81,6 +89,54 @@ 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 is default VRF. silently ignore */ + 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) + 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; +} + /* Get a VRF. If not found, create one. * Arg: * name - The name of the vrf. May be NULL if unknown. @@ -94,7 +150,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) @@ -109,8 +166,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; @@ -134,7 +189,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); @@ -219,7 +273,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; @@ -385,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__); @@ -437,15 +493,150 @@ void vrf_terminate(void) } /* Create a socket for the VRF. */ -int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id) +int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id, + char *interfacename) { - int ret = -1; + int ret, save_errno, ret2; + ret = vrf_switch_to_netns(vrf_id); + if (ret < 0) + zlog_err("%s: Can't switch to VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); ret = socket(domain, type, protocol); - + save_errno = errno; + ret2 = vrf_switchback_to_initial(); + if (ret2 < 0) + zlog_err("%s: Can't switchback from VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); + errno = save_errno; + if (ret <= 0) + return ret; + ret2 = vrf_bind(vrf_id, ret, interfacename); + if (ret2 < 0) { + close(ret); + ret = ret2; + } 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; +} + +int vrf_handler_create(struct vty *vty, const char *vrfname, struct vrf **vrf) +{ + struct vrf *vrfp; + + if (strlen(vrfname) > VRF_NAMSIZ) { + if (vty) + vty_out(vty, + "%% VRF name %s invalid: length exceeds %d bytes\n", + vrfname, VRF_NAMSIZ); + else + zlog_warn( + "%% VRF name %s invalid: length exceeds %d bytes\n", + vrfname, VRF_NAMSIZ); + return CMD_WARNING_CONFIG_FAILED; + } + + vrfp = vrf_get(VRF_UNKNOWN, vrfname); + + if (vty) + VTY_PUSH_CONTEXT(VRF_NODE, vrfp); + + if (vrf) + *vrf = vrfp; + return CMD_SUCCESS; +} + +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); + + 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, @@ -455,21 +646,8 @@ DEFUN_NOSH (vrf, { int idx_name = 1; const char *vrfname = argv[idx_name]->arg; - struct vrf *vrfp; - if (strlen(vrfname) > VRF_NAMSIZ) { - vty_out(vty, - "%% VRF name %s is invalid: length exceeds " - "%d characters\n", - vrfname, VRF_NAMSIZ); - return CMD_WARNING_CONFIG_FAILED; - } - - vrfp = vrf_get(VRF_UNKNOWN, vrfname); - - VTY_PUSH_CONTEXT(VRF_NODE, vrfp); - - return CMD_SUCCESS; + return vrf_handler_create(vty, vrfname, NULL); } DEFUN_NOSH (no_vrf, @@ -505,6 +683,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 */ @@ -557,4 +784,105 @@ 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); + 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) +{ + 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; +} + +int vrf_bind(vrf_id_t vrf_id, int fd, char *name) +{ + int ret = 0; + + if (fd < 0 || name == NULL) + return fd; + if (vrf_is_mapped_on_netns(vrf_id)) + return fd; +#ifdef SO_BINDTODEVICE + ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name, + strlen(name)); + if (ret < 0) + zlog_debug("bind to interface %s failed, errno=%d", + name, errno); +#endif /* SO_BINDTODEVICE */ + return ret; +} +int vrf_getaddrinfo(const char *node, const char *service, + const struct addrinfo *hints, + struct addrinfo **res, vrf_id_t vrf_id) +{ + int ret, ret2, save_errno; + + ret = vrf_switch_to_netns(vrf_id); + if (ret < 0) + zlog_err("%s: Can't switch to VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); + ret = getaddrinfo(node, service, hints, res); + save_errno = errno; + ret2 = vrf_switchback_to_initial(); + if (ret2 < 0) + zlog_err("%s: Can't switchback from VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); + errno = save_errno; + return ret; +} + +int vrf_ioctl(vrf_id_t vrf_id, int d, unsigned long request, char *params) +{ + int ret, saved_errno, rc; + + ret = vrf_switch_to_netns(vrf_id); + if (ret < 0) { + zlog_err("%s: Can't switch to VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); + return 0; + } + rc = ioctl(d, request, params); + saved_errno = errno; + ret = vrf_switchback_to_initial(); + if (ret < 0) + zlog_err("%s: Can't switchback from VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); + errno = saved_errno; + return rc; +} + +int vrf_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id, + char *interfacename) +{ + int ret, save_errno, ret2; + + ret = vrf_switch_to_netns(vrf_id); + if (ret < 0) + zlog_err("%s: Can't switch to VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); + ret = sockunion_socket(su); + save_errno = errno; + ret2 = vrf_switchback_to_initial(); + if (ret2 < 0) + zlog_err("%s: Can't switchback from VRF %u (%s)", + __func__, vrf_id, safe_strerror(errno)); + errno = save_errno; + + if (ret <= 0) + return ret; + ret2 = vrf_bind(vrf_id, ret, interfacename); + if (ret2 < 0) { + close(ret); + ret = ret2; + } + return ret; } diff --git a/lib/vrf.h b/lib/vrf.h index 99c048c702..062e6f3d8d 100644 --- a/lib/vrf.h +++ b/lib/vrf.h @@ -26,12 +26,9 @@ #include "linklist.h" #include "qobj.h" #include "vty.h" - -/* The default NS ID */ -#define NS_DEFAULT 0 +#include "ns.h" /* The default VRF ID */ -#define VRF_DEFAULT 0 #define VRF_UNKNOWN UINT32_MAX /* Pending: May need to refine this. */ @@ -42,6 +39,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 +58,7 @@ struct vrf_data { union { struct { uint32_t table_id; + char netns_name[NS_NAMSIZ]; } l; }; }; @@ -88,6 +87,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); @@ -96,6 +98,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; @@ -195,17 +200,85 @@ extern void vrf_init(int (*create)(struct vrf *), int (*enable)(struct vrf *), */ extern void vrf_terminate(void); +/* + * Utilities to create networks objects, + * or call network operations + */ + +/* Create a socket serving for the given VRF */ +extern int vrf_socket(int domain, int type, + int protocol, vrf_id_t vrf_id, + char *name); + +extern int vrf_sockunion_socket(const union sockunion *su, + vrf_id_t vrf_id, char *name); + +extern int vrf_bind(vrf_id_t vrf_id, int fd, char *name); + +/* VRF ioctl operations */ +extern int vrf_getaddrinfo(const char *node, const char *service, + const struct addrinfo *hints, + struct addrinfo **res, vrf_id_t vrf_id); + +extern int vrf_ioctl(vrf_id_t vrf_id, int d, unsigned long request, char *args); + +/* function called by macro VRF_DEFAULT + * to get the default VRF_ID + */ +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); + +/* + * VRF backend routines + * should be called from zebra only + */ + +/* VRF vty command initialisation + */ extern void vrf_cmd_init(int (*writefunc)(struct vty *vty)); +/* VRF vty debugging + */ +extern void vrf_install_commands(void); + /* * VRF utilities */ -/* Create a socket serving for the given VRF */ -extern int vrf_socket(int, int, int, vrf_id_t); - -/* - * VRF Debugging +/* API for configuring VRF backend + * should be called from zebra only */ -extern void vrf_install_commands(void); +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*/ 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/ospfd/ospf_network.c b/ospfd/ospf_network.c index 022a5a138a..045634d8ab 100644 --- a/ospfd/ospf_network.c +++ b/ospfd/ospf_network.c @@ -169,42 +169,27 @@ int ospf_if_ipmulticast(struct ospf *top, struct prefix *p, ifindex_t ifindex) return ret; } -int ospf_bind_vrfdevice(struct ospf *ospf, int ospf_sock) -{ - int ret = 0; - -#ifdef SO_BINDTODEVICE - - if (ospf && ospf->vrf_id != VRF_DEFAULT && - ospf->vrf_id != VRF_UNKNOWN) { - ret = setsockopt(ospf_sock, SOL_SOCKET, SO_BINDTODEVICE, - ospf->name, - strlen(ospf->name)); - if (ret < 0) { - int save_errno = errno; - - zlog_warn("%s: Could not setsockopt SO_BINDTODEVICE %s", - __PRETTY_FUNCTION__, - safe_strerror(save_errno)); - } - - } -#endif - return ret; -} - int ospf_sock_init(struct ospf *ospf) { int ospf_sock; int ret, hincl = 1; int bufsize = (8 * 1024 * 1024); + /* silently ignore. already done */ + if (ospf->fd > 0) + return -1; + + if (ospf->vrf_id == VRF_UNKNOWN) { + /* silently return since VRF is not ready */ + return -1; + } if (ospfd_privs.change(ZPRIVS_RAISE)) { zlog_err("ospf_sock_init: could not raise privs, %s", safe_strerror(errno)); } - ospf_sock = socket(AF_INET, SOCK_RAW, IPPROTO_OSPFIGP); + ospf_sock = vrf_socket(AF_INET, SOCK_RAW, IPPROTO_OSPFIGP, + ospf->vrf_id, ospf->name); if (ospf_sock < 0) { int save_errno = errno; @@ -216,12 +201,6 @@ int ospf_sock_init(struct ospf *ospf) exit(1); } - ret = ospf_bind_vrfdevice(ospf, ospf_sock); - if (ret < 0) { - close(ospf_sock); - goto out; - } - #ifdef IP_HDRINCL /* we will include IP header with packet */ ret = setsockopt(ospf_sock, IPPROTO_IP, IP_HDRINCL, &hincl, diff --git a/ospfd/ospf_network.h b/ospfd/ospf_network.h index 41a7abda70..cbaf132327 100644 --- a/ospfd/ospf_network.h +++ b/ospfd/ospf_network.h @@ -30,6 +30,5 @@ extern int ospf_if_add_alldrouters(struct ospf *, struct prefix *, ifindex_t); extern int ospf_if_drop_alldrouters(struct ospf *, struct prefix *, ifindex_t); extern int ospf_if_ipmulticast(struct ospf *, struct prefix *, ifindex_t); extern int ospf_sock_init(struct ospf *ospf); -extern int ospf_bind_vrfdevice(struct ospf *, int); #endif /* _ZEBRA_OSPF_NETWORK_H */ diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index 86a3293d71..79af4a55fb 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -308,12 +308,6 @@ static struct ospf *ospf_new(u_short instance, const char *name) new->lsa_refresh_interval, &new->t_lsa_refresher); new->lsa_refresher_started = monotime(NULL); - if ((ospf_sock_init(new)) < 0) { - zlog_err( - "ospf_new: fatal error: ospf_sock_init was unable to open " - "a socket"); - exit(1); - } if ((new->ibuf = stream_new(OSPF_MAX_PACKET_SIZE + 1)) == NULL) { zlog_err( "ospf_new: fatal error: stream_new(%u) failed allocating ibuf", @@ -321,7 +315,6 @@ static struct ospf *ospf_new(u_short instance, const char *name) exit(1); } new->t_read = NULL; - thread_add_read(master, ospf_read, new, new->fd, &new->t_read); new->oi_write_q = list_new(); new->write_oi_count = OSPF_WRITE_INTERFACE_COUNT_DEFAULT; @@ -332,6 +325,16 @@ static struct ospf *ospf_new(u_short instance, const char *name) QOBJ_REG(new, ospf); + new->fd = -1; + if ((ospf_sock_init(new)) < 0) { + if (new->vrf_id != VRF_UNKNOWN) + zlog_warn( + "%s: ospf_sock_init is unable to open a socket", + __func__); + return new; + } + thread_add_read(master, ospf_read, new, new->fd, &new->t_read); + return new; } @@ -2049,7 +2052,8 @@ static int ospf_vrf_delete(struct vrf *vrf) static int ospf_vrf_enable(struct vrf *vrf) { struct ospf *ospf = NULL; - vrf_id_t old_vrf_id = VRF_DEFAULT; + vrf_id_t old_vrf_id; + int ret = 0; if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: VRF %s id %u enabled", @@ -2070,13 +2074,15 @@ static int ospf_vrf_enable(struct vrf *vrf) zlog_err("ospf_sock_init: could not raise privs, %s", safe_strerror(errno)); } - if (ospf_bind_vrfdevice(ospf, ospf->fd) < 0) - return 0; + ret = ospf_sock_init(ospf); if (ospfd_privs.change(ZPRIVS_LOWER)) { zlog_err("ospf_sock_init: could not lower privs, %s", safe_strerror(errno)); } - + if (ret < 0 || ospf->fd <= 0) + return 0; + thread_add_read(master, ospf_read, ospf, + ospf->fd, &ospf->t_read); ospf->oi_running = 1; ospf_zebra_vrf_register(ospf); ospf_router_id_update(ospf); @@ -2111,6 +2117,9 @@ static int ospf_vrf_disable(struct vrf *vrf) if (IS_DEBUG_OSPF_EVENT) zlog_debug("%s: ospf old_vrf_id %d unlinked", __PRETTY_FUNCTION__, old_vrf_id); + thread_cancel(ospf->t_read); + close(ospf->fd); + ospf->fd = -1; } /* Note: This is a callback, the VRF will be deleted by the caller. */ diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am index c9b6f50160..33d34fc0dd 100644 --- a/vtysh/Makefile.am +++ b/vtysh/Makefile.am @@ -147,7 +147,7 @@ vtysh_cmd_FILES = $(vtysh_scan) \ $(top_srcdir)/lib/distribute.c $(top_srcdir)/lib/if_rmap.c \ $(top_srcdir)/lib/vrf.c \ $(top_srcdir)/lib/vty.c $(top_srcdir)/zebra/debug.c \ - $(top_srcdir)/lib/ns.c \ + $(top_srcdir)/lib/logicalrouter.c \ $(top_srcdir)/zebra/interface.c \ $(top_srcdir)/zebra/irdp_interface.c \ $(top_srcdir)/zebra/rtadv.c $(top_srcdir)/zebra/zebra_vty.c \ diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index dedf3d1647..6cfb51b00f 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -87,6 +87,9 @@ foreach (@ARGV) { elsif ($file =~ /lib\/vrf\.c$/) { $protocol = "VTYSH_ALL"; } + elsif ($file =~ /lib\/logicalrouter\.c$/) { + $protocol = "VTYSH_ALL"; + } elsif ($file =~ /lib\/filter\.c$/) { $protocol = "VTYSH_ALL"; } diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index 65e9c9f8c5..e0a0dd585d 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -975,8 +975,8 @@ static struct cmd_node pw_node = { PW_NODE, "%s(config-pw)# ", }; -static struct cmd_node ns_node = { - NS_NODE, "%s(config-logical-router)# ", +static struct cmd_node logicalrouter_node = { + LOGICALROUTER_NODE, "%s(config-logical-router)# ", }; static struct cmd_node vrf_node = { @@ -1508,7 +1508,7 @@ static int vtysh_exit(struct vty *vty) break; case INTERFACE_NODE: case PW_NODE: - case NS_NODE: + case LOGICALROUTER_NODE: case VRF_NODE: case ZEBRA_NODE: case BGP_NODE: @@ -1782,16 +1782,25 @@ DEFSH(VTYSH_ZEBRA, vtysh_no_interface_vrf_cmd, "no interface IFNAME vrf NAME", "Delete a pseudo interface's configuration\n" "Interface's name\n" VRF_CMD_HELP_STR) -DEFUNSH(VTYSH_NS, vtysh_ns, vtysh_ns_cmd, "logical-router (1-65535) ns NAME", +DEFUNSH(VTYSH_ZEBRA, vtysh_logicalrouter, vtysh_logicalrouter_cmd, + "logical-router (1-65535) ns NAME", "Enable a logical-router\n" "Specify the logical-router indentifier\n" "The Name Space\n" "The file name in " NS_RUN_DIR ", or a full pathname\n") { - vty->node = NS_NODE; + vty->node = LOGICALROUTER_NODE; return CMD_SUCCESS; } +DEFSH(VTYSH_ZEBRA, vtysh_no_logicalrouter_cmd, + "no logical-router (1-65535) ns NAME", + NO_STR + "Enable a Logical-Router\n" + "Specify the Logical-Router identifier\n" + "The Name Space\n" + "The file name in " NS_RUN_DIR ", or a full pathname\n") + DEFUNSH(VTYSH_VRF, vtysh_vrf, vtysh_vrf_cmd, "vrf NAME", "Select a VRF to configure\n" "VRF's name\n") @@ -1804,16 +1813,18 @@ DEFSH(VTYSH_ZEBRA, vtysh_no_vrf_cmd, "no vrf NAME", NO_STR "Delete a pseudo vrf's configuration\n" "VRF's name\n") -DEFUNSH(VTYSH_NS, vtysh_exit_ns, vtysh_exit_ns_cmd, "exit", +DEFUNSH(VTYSH_NS, vtysh_exit_logicalrouter, + vtysh_exit_logicalrouter_cmd, "exit", "Exit current mode and down to previous mode\n") { return vtysh_exit(vty); } -DEFUNSH(VTYSH_NS, vtysh_quit_ns, vtysh_quit_ns_cmd, "quit", +DEFUNSH(VTYSH_NS, vtysh_quit_logicalrouter, + vtysh_quit_logicalrouter_cmd, "quit", "Exit current mode and down to previous mode\n") { - return vtysh_exit_ns(self, vty, argc, argv); + return vtysh_exit_logicalrouter(self, vty, argc, argv); } DEFUNSH(VTYSH_VRF, vtysh_exit_vrf, vtysh_exit_vrf_cmd, "exit", @@ -3055,7 +3066,7 @@ void vtysh_init_vty(void) install_node(&interface_node, NULL); install_node(&pw_node, NULL); install_node(&link_params_node, NULL); - install_node(&ns_node, NULL); + install_node(&logicalrouter_node, NULL); install_node(&vrf_node, NULL); install_node(&rmap_node, NULL); install_node(&zebra_node, NULL); @@ -3233,11 +3244,14 @@ void vtysh_init_vty(void) install_element(PW_NODE, &vtysh_exit_interface_cmd); install_element(PW_NODE, &vtysh_quit_interface_cmd); - install_element(NS_NODE, &vtysh_end_all_cmd); + install_element(LOGICALROUTER_NODE, &vtysh_end_all_cmd); - install_element(CONFIG_NODE, &vtysh_ns_cmd); - install_element(NS_NODE, &vtysh_exit_ns_cmd); - install_element(NS_NODE, &vtysh_quit_ns_cmd); + install_element(CONFIG_NODE, &vtysh_logicalrouter_cmd); + install_element(CONFIG_NODE, &vtysh_no_logicalrouter_cmd); + install_element(LOGICALROUTER_NODE, + &vtysh_exit_logicalrouter_cmd); + install_element(LOGICALROUTER_NODE, + &vtysh_quit_logicalrouter_cmd); install_element(VRF_NODE, &vtysh_end_all_cmd); install_element(VRF_NODE, &vtysh_exit_vrf_cmd); diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index 967f855fbc..aa1dd407eb 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -187,7 +187,7 @@ void vtysh_config_parse_line(void *arg, const char *line) config->index = INTERFACE_NODE; } else if (config->index == RMAP_NODE || config->index == INTERFACE_NODE - || config->index == NS_NODE + || config->index == LOGICALROUTER_NODE || config->index == VTY_NODE || config->index == VRF_NODE) config_add_line_uniq(config->line, line); @@ -202,7 +202,7 @@ void vtysh_config_parse_line(void *arg, const char *line) else if (strncmp(line, "pseudowire", strlen("pseudowire")) == 0) config = config_get(PW_NODE, line); else if (strncmp(line, "logical-router", strlen("ns")) == 0) - config = config_get(NS_NODE, line); + config = config_get(LOGICALROUTER_NODE, line); else if (strncmp(line, "vrf", strlen("vrf")) == 0) config = config_get(VRF_NODE, line); else if (strncmp(line, "router-id", strlen("router-id")) == 0) diff --git a/zebra/if_ioctl.c b/zebra/if_ioctl.c index 1d108886de..09fc085018 100644 --- a/zebra/if_ioctl.c +++ b/zebra/if_ioctl.c @@ -146,7 +146,7 @@ static int if_get_hwaddr(struct interface *ifp) ifreq.ifr_addr.sa_family = AF_INET; /* Fetch Hardware address if available. */ - ret = if_ioctl(SIOCGIFHWADDR, (caddr_t)&ifreq); + ret = vrf_if_ioctl(SIOCGIFHWADDR, (caddr_t)&ifreq, ifp->vrf_id); if (ret < 0) ifp->hw_addr_len = 0; else { diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 14905b738b..639f70a6b4 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -66,6 +66,7 @@ #include "zebra/kernel_netlink.h" #include "zebra/if_netlink.h" +extern struct zebra_privs_t zserv_privs; /* Note: on netlink systems, there should be a 1-to-1 mapping between interface names and ifindex values. */ @@ -344,12 +345,13 @@ static void netlink_vrf_change(struct nlmsghdr *h, struct rtattr *tb, } } -static int get_iflink_speed(const char *ifname) +static int get_iflink_speed(struct interface *interface) { struct ifreq ifdata; struct ethtool_cmd ecmd; int sd; int rc; + const char *ifname = interface->name; /* initialize struct */ memset(&ifdata, 0, sizeof(ifdata)); @@ -363,16 +365,20 @@ static int get_iflink_speed(const char *ifname) ifdata.ifr_data = (__caddr_t)&ecmd; /* use ioctl to get IP address of an interface */ - sd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); + sd = vrf_socket(PF_INET, SOCK_DGRAM, IPPROTO_IP, + interface->vrf_id, NULL); if (sd < 0) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug("Failure to read interface %s speed: %d %s", ifname, errno, safe_strerror(errno)); return 0; } - /* Get the current link state for the interface */ - rc = ioctl(sd, SIOCETHTOOL, (char *)&ifdata); + rc = vrf_ioctl(interface->vrf_id, sd, SIOCETHTOOL, (char *)&ifdata); + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); if (rc < 0) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( @@ -389,7 +395,7 @@ static int get_iflink_speed(const char *ifname) uint32_t kernel_get_speed(struct interface *ifp) { - return get_iflink_speed(ifp->name); + return get_iflink_speed(ifp); } static int netlink_extract_bridge_info(struct rtattr *link_data, @@ -615,13 +621,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 +638,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]) @@ -644,7 +653,7 @@ static int netlink_interface(struct sockaddr_nl *snl, struct nlmsghdr *h, SET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK); ifp->mtu6 = ifp->mtu = *(uint32_t *)RTA_DATA(tb[IFLA_MTU]); ifp->metric = 0; - ifp->speed = get_iflink_speed(name); + ifp->speed = get_iflink_speed(ifp); ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN; if (desc) @@ -789,8 +798,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); @@ -1017,6 +1030,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", @@ -1074,7 +1088,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 +1105,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]); @@ -1103,7 +1118,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..7229b8818d 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -512,9 +512,15 @@ static void if_addr_wakeup(struct interface *ifp) void if_add_update(struct interface *ifp) { struct zebra_if *if_data; + struct zebra_ns *zns; + struct zebra_vrf *zvrf = vrf_info_lookup(ifp->vrf_id); - if_link_per_ns(zebra_ns_lookup(NS_DEFAULT), ifp); - + /* case interface populate before vrf enabled */ + if (zvrf->zns) + zns = zvrf->zns; + else + zns = zebra_ns_lookup(NS_DEFAULT); + if_link_per_ns(zns, ifp); if_data = ifp->info; assert(if_data); @@ -800,10 +806,12 @@ void if_nbr_ipv6ll_to_ipv4ll_neigh_update(struct interface *ifp, char buf[16] = "169.254.0.1"; struct in_addr ipv4_ll; char mac[6]; + ns_id_t ns_id; inet_pton(AF_INET, buf, &ipv4_ll); ipv6_ll_address_to_mac(address, (u_char *)mac); + ns_id = zvrf->zns->ns_id; /* * Remove existed arp record for the interface as netlink @@ -811,10 +819,10 @@ void if_nbr_ipv6ll_to_ipv4ll_neigh_update(struct interface *ifp, * * supported message types are RTM_NEWNEIGH and RTM_DELNEIGH */ - kernel_neigh_update (0, ifp->ifindex, ipv4_ll.s_addr, mac, 6); + kernel_neigh_update(0, ifp->ifindex, ipv4_ll.s_addr, mac, 6, ns_id); /* Add arp record */ - kernel_neigh_update (add, ifp->ifindex, ipv4_ll.s_addr, mac, 6); + kernel_neigh_update(add, ifp->ifindex, ipv4_ll.s_addr, mac, 6, ns_id); zvrf->neigh_updates++; } @@ -1404,7 +1412,7 @@ DEFUN (show_interface_name_vrf, int idx_ifname = 2; int idx_name = 4; struct interface *ifp; - vrf_id_t vrf_id = VRF_DEFAULT; + vrf_id_t vrf_id; interface_update_stats(); diff --git a/zebra/ioctl.c b/zebra/ioctl.c index 8e3a1d1a03..d07d37056e 100644 --- a/zebra/ioctl.c +++ b/zebra/ioctl.c @@ -59,6 +59,7 @@ int if_ioctl(u_long request, caddr_t buffer) sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) { int save_errno = errno; + if (zserv_privs.change(ZPRIVS_LOWER)) zlog_err("Can't lower privileges"); zlog_err("Cannot create UDP socket: %s", @@ -78,6 +79,39 @@ int if_ioctl(u_long request, caddr_t buffer) return 0; } +/* call ioctl system call */ +int vrf_if_ioctl(u_long request, caddr_t buffer, vrf_id_t vrf_id) +{ + int sock; + int ret; + int err = 0; + + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); + sock = vrf_socket(AF_INET, SOCK_DGRAM, 0, vrf_id, NULL); + if (sock < 0) { + int save_errno = errno; + + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); + zlog_err("Cannot create UDP socket: %s", + safe_strerror(save_errno)); + exit(1); + } + ret = vrf_ioctl(vrf_id, sock, request, buffer); + if (ret < 0) + err = errno; + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); + close(sock); + + if (ret < 0) { + errno = err; + return ret; + } + return 0; +} + #ifndef HAVE_NETLINK static int if_ioctl_ipv6(u_long request, caddr_t buffer) { @@ -90,6 +124,7 @@ static int if_ioctl_ipv6(u_long request, caddr_t buffer) sock = socket(AF_INET6, SOCK_DGRAM, 0); if (sock < 0) { int save_errno = errno; + if (zserv_privs.change(ZPRIVS_LOWER)) zlog_err("Can't lower privileges"); zlog_err("Cannot create IPv6 datagram socket: %s", @@ -122,7 +157,7 @@ void if_get_metric(struct interface *ifp) ifreq_set_name(&ifreq, ifp); - if (if_ioctl(SIOCGIFMETRIC, (caddr_t)&ifreq) < 0) + if (vrf_if_ioctl(SIOCGIFMETRIC, (caddr_t)&ifreq, ifp->vrf_id) < 0) return; ifp->metric = ifreq.ifr_metric; if (ifp->metric == 0) @@ -140,7 +175,7 @@ void if_get_mtu(struct interface *ifp) ifreq_set_name(&ifreq, ifp); #if defined(SIOCGIFMTU) - if (if_ioctl(SIOCGIFMTU, (caddr_t)&ifreq) < 0) { + if (vrf_if_ioctl(SIOCGIFMTU, (caddr_t)&ifreq, ifp->vrf_id) < 0) { zlog_info("Can't lookup mtu by ioctl(SIOCGIFMTU)"); ifp->mtu6 = ifp->mtu = -1; return; @@ -376,9 +411,9 @@ void if_get_flags(struct interface *ifp) ifreq_set_name(&ifreq, ifp); - ret = if_ioctl(SIOCGIFFLAGS, (caddr_t)&ifreq); + ret = vrf_if_ioctl(SIOCGIFFLAGS, (caddr_t)&ifreq, ifp->vrf_id); if (ret < 0) { - zlog_err("if_ioctl(SIOCGIFFLAGS) failed: %s", + zlog_err("vrf_if_ioctl(SIOCGIFFLAGS) failed: %s", safe_strerror(errno)); return; } @@ -423,7 +458,7 @@ int if_set_flags(struct interface *ifp, uint64_t flags) ifreq.ifr_flags = ifp->flags; ifreq.ifr_flags |= flags; - ret = if_ioctl(SIOCSIFFLAGS, (caddr_t)&ifreq); + ret = vrf_if_ioctl(SIOCSIFFLAGS, (caddr_t)&ifreq, ifp->vrf_id); if (ret < 0) { zlog_info("can't set interface flags"); @@ -444,7 +479,7 @@ int if_unset_flags(struct interface *ifp, uint64_t flags) ifreq.ifr_flags = ifp->flags; ifreq.ifr_flags &= ~flags; - ret = if_ioctl(SIOCSIFFLAGS, (caddr_t)&ifreq); + ret = vrf_if_ioctl(SIOCSIFFLAGS, (caddr_t)&ifreq, ifp->vrf_id); if (ret < 0) { zlog_info("can't unset interface flags"); diff --git a/zebra/ioctl.h b/zebra/ioctl.h index 02f8e6b880..1a6e14ed4d 100644 --- a/zebra/ioctl.h +++ b/zebra/ioctl.h @@ -25,6 +25,7 @@ /* Prototypes. */ extern void ifreq_set_name(struct ifreq *, struct interface *); extern int if_ioctl(u_long, caddr_t); +extern int vrf_if_ioctl(u_long request, caddr_t buffer, vrf_id_t vrf_id); extern int if_set_flags(struct interface *, uint64_t); extern int if_unset_flags(struct interface *, uint64_t); diff --git a/zebra/ioctl_solaris.c b/zebra/ioctl_solaris.c index e8b65925f8..f429c42440 100644 --- a/zebra/ioctl_solaris.c +++ b/zebra/ioctl_solaris.c @@ -30,6 +30,7 @@ #include "log.h" #include "privs.h" #include "vty.h" +#include "vrf.h" #include "zebra/rib.h" #include "zebra/rt.h" @@ -44,6 +45,11 @@ void lifreq_set_name(struct lifreq *lifreq, const char *ifname) strncpy(lifreq->lifr_name, ifname, IFNAMSIZ); } +int vrf_if_ioctl(u_long request, caddr_t buffer, vrf_id_t vrf_id) +{ + return if_ioctl(request, buffer); +} + /* call ioctl system call */ int if_ioctl(u_long request, caddr_t buffer) { 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/main.c b/zebra/main.c index 19b16936d9..749d509a86 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -34,6 +34,7 @@ #include "privs.h" #include "sigevent.h" #include "vrf.h" +#include "logicalrouter.h" #include "libfrr.h" #include "zebra/rib.h" @@ -47,6 +48,7 @@ #include "zebra/redistribute.h" #include "zebra/zebra_mpls.h" #include "zebra/label_manager.h" +#include "zebra/zebra_netns_notify.h" #define ZEBRA_PTM_SUPPORT @@ -85,6 +87,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}}; @@ -122,7 +125,6 @@ static void sigint(void) { struct vrf *vrf; struct zebra_vrf *zvrf; - struct zebra_ns *zns; zlog_notice("Terminating on signal"); @@ -137,10 +139,12 @@ static void sigint(void) if (zvrf) SET_FLAG(zvrf->flags, ZEBRA_VRF_RETAIN); } + if (zebrad.lsp_process_q) + work_queue_free(zebrad.lsp_process_q); vrf_terminate(); - zns = zebra_ns_lookup(NS_DEFAULT); - zebra_ns_disable(0, (void **)&zns); + ns_walk_func(zebra_ns_disabled); + zebra_ns_notify_close(); access_list_reset(); prefix_list_reset(); @@ -148,8 +152,6 @@ static void sigint(void) list_delete_and_null(&zebrad.client_list); work_queue_free(zebrad.ribq); - if (zebrad.lsp_process_q) - work_queue_free(zebrad.lsp_process_q); meta_queue_free(zebrad.mq); frr_fini(); @@ -205,12 +207,16 @@ int main(int argc, char **argv) char *fuzzing = NULL; #endif + vrf_configure_backend(VRF_BACKEND_VRF_LITE); + logicalrouter_configure_backend( + LOGICALROUTER_BACKEND_NETNS); + 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 +231,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 +286,11 @@ int main(int argc, char **argv) case 's': nl_rcvbufsize = atoi(optarg); break; + case 'n': + vrf_configure_backend(VRF_BACKEND_NETNS); + logicalrouter_configure_backend( + LOGICALROUTER_BACKEND_OFF); + break; #endif /* HAVE_NETLINK */ #if defined(HANDLE_ZAPI_FUZZING) case 'c': diff --git a/zebra/rt.h b/zebra/rt.h index 54d45b889a..472f2d7a97 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -78,7 +78,8 @@ extern int kernel_address_add_ipv4(struct interface *, struct connected *); extern int kernel_address_delete_ipv4(struct interface *, struct connected *); extern int kernel_address_add_ipv6 (struct interface *, struct connected *); extern int kernel_address_delete_ipv6 (struct interface *, struct connected *); -extern int kernel_neigh_update(int, int, uint32_t, char *, int); +extern int kernel_neigh_update(int cmd, int ifindex, uint32_t addr, + char *lla, int llalen, ns_id_t ns_id); extern int kernel_interface_set_master(struct interface *master, struct interface *slave); diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index a80ab9d834..e26109badf 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. */ @@ -1274,7 +1282,7 @@ static void _netlink_mpls_debug(int cmd, u_int32_t label, const char *routedesc) } static int netlink_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla, - int llalen) + int llalen, ns_id_t ns_id) { struct { struct nlmsghdr n; @@ -1282,7 +1290,7 @@ static int netlink_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla, char buf[256]; } req; - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_ns *zns = zebra_ns_lookup(ns_id); memset(&req.n, 0, sizeof(req.n)); memset(&req.ndm, 0, sizeof(req.ndm)); @@ -1326,9 +1334,10 @@ static int netlink_route_multipath(int cmd, struct prefix *p, char buf[NL_PKT_BUF_SIZE]; } req; - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_ns *zns; struct zebra_vrf *zvrf = vrf_info_lookup(re->vrf_id); + zns = zvrf->zns; memset(&req, 0, sizeof req - NL_PKT_BUF_SIZE); bytelen = (family == AF_INET ? 4 : 16); @@ -1626,8 +1635,9 @@ int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in) } req; mroute = mr; - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_ns *zns; + zns = zvrf->zns; memset(&req.n, 0, sizeof(req.n)); memset(&req.ndm, 0, sizeof(req.ndm)); @@ -1700,10 +1710,10 @@ void kernel_route_rib(struct route_node *rn, struct prefix *p, } int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, - int llalen) + int llalen, ns_id_t ns_id) { return netlink_neigh_update(add ? RTM_NEWNEIGH : RTM_DELNEIGH, ifindex, - addr, lla, llalen); + addr, lla, llalen, ns_id); } /* @@ -1713,14 +1723,16 @@ int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, static int netlink_vxlan_flood_list_update(struct interface *ifp, struct in_addr *vtep_ip, int cmd) { - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_ns *zns; struct { struct nlmsghdr n; struct ndmsg ndm; char buf[256]; } req; u_char dst_mac[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + zns = zvrf->zns; memset(&req.n, 0, sizeof(req.n)); memset(&req.ndm, 0, sizeof(req.ndm)); @@ -1776,7 +1788,7 @@ int kernel_del_vtep(vni_t vni, struct interface *ifp, struct in_addr *vtep_ip) #endif static int netlink_macfdb_change(struct sockaddr_nl *snl, struct nlmsghdr *h, - int len) + int len, ns_id_t ns_id) { struct ndmsg *ndm; struct interface *ifp; @@ -1799,7 +1811,7 @@ static int netlink_macfdb_change(struct sockaddr_nl *snl, struct nlmsghdr *h, return 0; /* The interface should exist. */ - ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), ndm->ndm_ifindex); if (!ifp || !ifp->info) return 0; @@ -1930,7 +1942,7 @@ static int netlink_macfdb_table(struct sockaddr_nl *snl, struct nlmsghdr *h, if (ndm->ndm_family != AF_BRIDGE) return 0; - return netlink_macfdb_change(snl, h, len); + return netlink_macfdb_change(snl, h, len, ns_id); } /* Request for MAC FDB information from the kernel */ @@ -2012,7 +2024,7 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid, struct ethaddr *mac, struct in_addr vtep_ip, int local, int cmd, u_char sticky) { - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_ns *zns; struct { struct nlmsghdr n; struct ndmsg ndm; @@ -2026,7 +2038,9 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid, int vid_present = 0, dst_present = 0; char vid_buf[20]; char dst_buf[30]; + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + zns = zvrf->zns; zif = ifp->info; if ((br_if = zif->brslave_info.br_if) == NULL) { zlog_warn("MAC %s on IF %s(%u) - no mapping to bridge", @@ -2086,7 +2100,7 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid, | NUD_DELAY) static int netlink_ipneigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h, - int len) + int len, ns_id_t ns_id) { struct ndmsg *ndm; struct interface *ifp; @@ -2107,7 +2121,7 @@ static int netlink_ipneigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h, return 0; /* The interface should exist. */ - ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), ndm->ndm_ifindex); if (!ifp || !ifp->info) return 0; @@ -2129,7 +2143,7 @@ static int netlink_ipneigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h, * itself */ if (IS_ZEBRA_IF_VLAN(ifp)) { - link_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), + link_if = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), zif->link_ifindex); if (!link_if) return 0; @@ -2307,13 +2321,13 @@ int netlink_neigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h, /* Is this a notification for the MAC FDB or IP neighbor table? */ ndm = NLMSG_DATA(h); if (ndm->ndm_family == AF_BRIDGE) - return netlink_macfdb_change(snl, h, len); + return netlink_macfdb_change(snl, h, len, ns_id); if (ndm->ndm_type != RTN_UNICAST) return 0; if (ndm->ndm_family == AF_INET || ndm->ndm_family == AF_INET6) - return netlink_ipneigh_change(snl, h, len); + return netlink_ipneigh_change(snl, h, len, ns_id); return 0; } @@ -2328,10 +2342,12 @@ static int netlink_neigh_update2(struct interface *ifp, struct ipaddr *ip, } req; int ipa_len; - struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); + struct zebra_ns *zns; char buf[INET6_ADDRSTRLEN]; char buf2[ETHER_ADDR_STRLEN]; + struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); + zns = zvrf->zns; memset(&req.n, 0, sizeof(req.n)); memset(&req.ndm, 0, sizeof(req.ndm)); diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 6d4af1203c..b2baee5728 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -424,7 +424,7 @@ void kernel_route_rib(struct route_node *rn, struct prefix *p, } int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, - int llalen) + int llalen, ns_id_t ns_id) { /* TODO */ return 0; 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) diff --git a/zebra/subdir.am b/zebra/subdir.am index 3474823623..bb7439c0f6 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -65,6 +65,8 @@ zebra_zebra_SOURCES = \ zebra/zebra_vty.c \ zebra/zebra_vxlan.c \ zebra/zserv.c \ + zebra/zebra_netns_id.c \ + zebra/zebra_netns_notify.c \ # end zebra/zebra_vty_clippy.c: $(CLIPPY_DEPS) @@ -104,6 +106,8 @@ noinst_HEADERS += \ zebra/zebra_vxlan.h \ zebra/zebra_vxlan_private.h \ zebra/zserv.h \ + zebra/zebra_netns_id.h \ + zebra/zebra_netns_notify.h \ # end zebra_zebra_irdp_la_SOURCES = \ diff --git a/zebra/zebra_netns_id.c b/zebra/zebra_netns_id.c new file mode 100644 index 0000000000..966d6ed0d2 --- /dev/null +++ b/zebra/zebra_netns_id.c @@ -0,0 +1,355 @@ +/* zebra NETNS ID handling routines + * those routines are implemented locally to avoid having external dependencies. + * Copyright (C) 2018 6WIND + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "ns.h" +#include "vrf.h" +#include "log.h" + +#if defined(HAVE_NETLINK) + +#include +#include +#include + +#include "rib.h" +#include "zebra_ns.h" +#include "kernel_netlink.h" +#endif /* defined(HAVE_NETLINK) */ + +#include "zebra_netns_id.h" + +/* 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 + +#if defined(HAVE_NETLINK) + +#define NETLINK_SOCKET_BUFFER_SIZE 512 +#define NETLINK_ALIGNTO 4 +#define NETLINK_ALIGN(len) (((len)+NETLINK_ALIGNTO-1) \ + & ~(NETLINK_ALIGNTO-1)) +#define NETLINK_NLATTR_LEN(_a, _b) (unsigned int)((char *)_a - (char *)_b) + +#endif /* defined(HAVE_NETLINK) */ + +static ns_id_t zebra_ns_id_get_fallback(const char *netnspath) +{ + static int zebra_ns_id_local; + + return zebra_ns_id_local++; +} + +#if defined(HAVE_NETLINK) + +static struct nlmsghdr *initiate_nlh(char *buf, unsigned int *seq, int type) +{ + struct nlmsghdr *nlh; + + nlh = (struct nlmsghdr *)buf; + nlh->nlmsg_len = NETLINK_ALIGN(sizeof(struct nlmsghdr)); + + nlh->nlmsg_type = type; + nlh->nlmsg_flags = NLM_F_REQUEST; + if (type == RTM_NEWNSID) + nlh->nlmsg_flags |= NLM_F_ACK; + nlh->nlmsg_seq = *seq = time(NULL); + return nlh; +} + +static int send_receive(int sock, struct nlmsghdr *nlh, + unsigned int seq, char *buf) +{ + int ret; + static const struct sockaddr_nl snl = { + .nl_family = AF_NETLINK + }; + + ret = sendto(sock, (const void *)nlh, (size_t)nlh->nlmsg_len, 0, + (struct sockaddr *) &snl, (socklen_t)sizeof(snl)); + if (ret < 0) { + zlog_err("netlink( %u) sendmsg() error: %s", + sock, safe_strerror(errno)); + return -1; + } + + /* reception */ + struct sockaddr_nl addr; + struct iovec iov = { + .iov_base = buf, + .iov_len = NETLINK_SOCKET_BUFFER_SIZE, + }; + struct msghdr msg = { + .msg_name = &addr, + .msg_namelen = sizeof(struct sockaddr_nl), + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = NULL, + .msg_controllen = 0, + .msg_flags = 0, + }; + ret = recvmsg(sock, &msg, 0); + if (ret < 0) { + zlog_err("netlink recvmsg: error %d (errno %u)", ret, errno); + return -1; + } + if (msg.msg_flags & MSG_TRUNC) { + zlog_err("netlink recvmsg : error message truncated"); + return -1; + } + /* nlh already points to buf */ + if (nlh->nlmsg_seq != seq) { + zlog_err("netlink recvmsg: bad sequence number %x (expected %x)", + seq, nlh->nlmsg_seq); + return -1; + } + return ret; +} + +/* extract on a valid nlmsg the nsid + * valid nlmsghdr - not a nlmsgerr + */ +static ns_id_t extract_nsid(struct nlmsghdr *nlh, char *buf) +{ + ns_id_t ns_id = NS_UNKNOWN; + int offset = NETLINK_ALIGN(sizeof(struct nlmsghdr)) + + NETLINK_ALIGN(sizeof(struct rtgenmsg)); + int curr_length = offset; + void *tail = (void *)((char *)nlh + NETLINK_ALIGN(nlh->nlmsg_len)); + struct nlattr *attr; + + for (attr = (struct nlattr *)((char *)buf + offset); + NETLINK_NLATTR_LEN(tail, attr) >= sizeof(struct nlattr) && + attr->nla_len >= sizeof(struct nlattr) && + attr->nla_len <= NETLINK_NLATTR_LEN(tail, attr); + attr += NETLINK_ALIGN(attr->nla_len)) { + curr_length += attr->nla_len; + if ((attr->nla_type & NLA_TYPE_MASK) == NETNSA_NSID) { + u_int32_t *ptr = (u_int32_t *)(attr); + + ns_id = ptr[1]; + break; + } + } + return ns_id; +} + +ns_id_t zebra_ns_id_get(const char *netnspath) +{ + int ns_id = -1; + struct sockaddr_nl snl; + int fd, sock, ret; + unsigned int seq; + ns_id_t return_nsid = NS_UNKNOWN; + + /* netns path check */ + if (!netnspath) + return NS_UNKNOWN; + fd = open(netnspath, O_RDONLY); + if (fd == -1) + return NS_UNKNOWN; + + /* netlink socket */ + sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (sock < 0) { + zlog_err("netlink( %u) socket() error: %s", + sock, safe_strerror(errno)); + return NS_UNKNOWN; + } + memset(&snl, 0, sizeof(snl)); + snl.nl_family = AF_NETLINK; + snl.nl_groups = RTNLGRP_NSID; + snl.nl_pid = 0; /* AUTO PID */ + ret = bind(sock, (struct sockaddr *)&snl, sizeof(snl)); + if (ret < 0) { + zlog_err("netlink( %u) socket() bind error: %s", + sock, safe_strerror(errno)); + close(sock); + close(fd); + return NS_UNKNOWN; + } + + /* message to send to netlink,and response : NEWNSID */ + char buf[NETLINK_SOCKET_BUFFER_SIZE]; + struct nlmsghdr *nlh; + struct rtgenmsg *rt; + int len; + + memset(buf, 0, NETLINK_SOCKET_BUFFER_SIZE); + nlh = initiate_nlh(buf, &seq, RTM_NEWNSID); + rt = (struct rtgenmsg *)(buf + nlh->nlmsg_len); + nlh->nlmsg_len += NETLINK_ALIGN(sizeof(struct rtgenmsg)); + rt->rtgen_family = AF_UNSPEC; + + addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_FD, fd); + addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID, ns_id); + + ret = send_receive(sock, nlh, seq, buf); + if (ret < 0) { + close(sock); + close(fd); + return NS_UNKNOWN; + } + nlh = (struct nlmsghdr *)buf; + + /* message to analyse : NEWNSID response */ + len = ret; + ret = 0; + do { + if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) { + return_nsid = extract_nsid(nlh, buf); + if (return_nsid != NS_UNKNOWN) + break; + } else { + if (nlh->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *) + ((char *)nlh + + NETLINK_ALIGN(sizeof( + struct nlmsghdr))); + + ret = -1; + if (err->error < 0) + errno = -err->error; + else + errno = err->error; + if (errno == 0) { + /* request NEWNSID was successfull + * return EEXIST error to get GETNSID + */ + errno = EEXIST; + } + } else { + /* other errors ignored + * attempt to get nsid + */ + ret = -1; + errno = EEXIST; + break; + } + } + len = len - NETLINK_ALIGN(nlh->nlmsg_len); + nlh = (struct nlmsghdr *)((char *)nlh + + NETLINK_ALIGN(nlh->nlmsg_len)); + } while (len != 0 && return_nsid != NS_UNKNOWN && ret == 0); + + if (ret <= 0) { + if (errno != EEXIST && ret != 0) { + zlog_err("netlink( %u) recvfrom() error 2 when reading: %s", + fd, safe_strerror(errno)); + close(sock); + close(fd); + if (errno == ENOTSUP) { + zlog_warn("NEWNSID locally generated"); + return zebra_ns_id_get_fallback(netnspath); + } + return NS_UNKNOWN; + } + /* message to send to netlink : GETNSID */ + memset(buf, 0, NETLINK_SOCKET_BUFFER_SIZE); + nlh = initiate_nlh(buf, &seq, RTM_GETNSID); + rt = (struct rtgenmsg *)(buf + nlh->nlmsg_len); + nlh->nlmsg_len += NETLINK_ALIGN(sizeof(struct rtgenmsg)); + rt->rtgen_family = AF_UNSPEC; + + addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_FD, fd); + addattr32(nlh, NETLINK_SOCKET_BUFFER_SIZE, NETNSA_NSID, ns_id); + + ret = send_receive(sock, nlh, seq, buf); + if (ret < 0) { + close(sock); + close(fd); + return NS_UNKNOWN; + } + nlh = (struct nlmsghdr *)buf; + len = ret; + ret = 0; + do { + if (nlh->nlmsg_type >= NLMSG_MIN_TYPE) { + return_nsid = extract_nsid(nlh, buf); + if (return_nsid != NS_UNKNOWN) + break; + } else if (nlh->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *err = (struct nlmsgerr *) + ((char *)nlh + + NETLINK_ALIGN(sizeof( + struct nlmsghdr))); + if (err->error < 0) + errno = -err->error; + else + errno = err->error; + break; + } + len = len - NETLINK_ALIGN(nlh->nlmsg_len); + nlh = (struct nlmsghdr *)((char *)nlh + + NETLINK_ALIGN(nlh->nlmsg_len)); + } while (len != 0 && return_nsid != NS_UNKNOWN && ret == 0); + } + + close(fd); + close(sock); + return return_nsid; +} + +#else +ns_id_t zebra_ns_id_get(const char *netnspath) +{ + return zebra_ns_id_get_fallback(netnspath); +} +#endif /* ! defined(HAVE_NETLINK) */ + +#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 new file mode 100644 index 0000000000..d6530e6694 --- /dev/null +++ b/zebra/zebra_netns_id.h @@ -0,0 +1,26 @@ +/* zebra NETNS ID handling routines + * Copyright (C) 2018 6WIND + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ +#if !defined(__ZEBRA_NS_ID_H__) +#define __ZEBRA_NS_ID_H__ +#include "zebra.h" +#include "ns.h" + +extern ns_id_t zebra_ns_id_get(const char *netnspath); +extern ns_id_t zebra_ns_id_get_default(void); + +#endif /* __ZEBRA_NS_ID_H__ */ diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c new file mode 100644 index 0000000000..b98d6ed703 --- /dev/null +++ b/zebra/zebra_netns_notify.c @@ -0,0 +1,271 @@ +/* + * Zebra NS collector and notifier for Network NameSpaces + * Copyright (C) 2017 6WIND + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#ifdef HAVE_NETLINK +#ifdef HAVE_NETNS +#undef _GNU_SOURCE +#define _GNU_SOURCE + +#include +#endif +#include +#include +#include + +#include "thread.h" +#include "ns.h" +#include "command.h" +#include "memory.h" + +#include "zserv.h" +#include "zebra_memory.h" +#endif /* defined(HAVE_NETLINK) */ + +#include "zebra_netns_notify.h" +#include "zebra_netns_id.h" + +#ifdef HAVE_NETLINK + +/* upon creation of folder under /var/run/netns, + * wait that netns context is bound to + * that folder 10 seconds + */ +#define ZEBRA_NS_POLLING_INTERVAL_MSEC 1000 +#define ZEBRA_NS_POLLING_MAX_RETRIES 200 + +DEFINE_MTYPE_STATIC(ZEBRA, NETNS_MISC, "ZebraNetNSInfo") +static struct thread *zebra_netns_notify_current; + +struct zebra_netns_info { + const char *netnspath; + unsigned int retries; +}; + +static int zebra_ns_ready_read(struct thread *t); +static void zebra_ns_notify_create_context_from_entry_name(const char *name); +static int zebra_ns_continue_read(struct zebra_netns_info *zns_info, + int stop_retry); +static int zebra_ns_notify_read(struct thread *t); + +static void zebra_ns_notify_create_context_from_entry_name(const char *name) +{ + char *netnspath = ns_netns_pathname(NULL, name); + struct vrf *vrf; + int ret; + ns_id_t ns_id; + + if (netnspath == NULL) + return; + + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); + ns_id = zebra_ns_id_get(netnspath); + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); + /* if VRF with NS ID already present */ + vrf = vrf_lookup_by_id((vrf_id_t)ns_id); + if (vrf) { + zlog_warn("NS notify : same NSID used by VRF %s. Ignore NS %s creation", + vrf->name, netnspath); + return; + } + if (vrf_handler_create(NULL, name, &vrf) != CMD_SUCCESS) { + zlog_warn("NS notify : failed to create VRF %s", name); + return; + } + ret = vrf_netns_handler_create(NULL, vrf, netnspath, ns_id); + if (ret != CMD_SUCCESS) { + zlog_warn("NS notify : failed to create NS %s", netnspath); + return; + } + zlog_info("NS notify : created VRF %s NS %s", + name, netnspath); +} + +static int zebra_ns_continue_read(struct zebra_netns_info *zns_info, + int stop_retry) +{ + void *ns_path_ptr = (void *)zns_info->netnspath; + + if (stop_retry) { + XFREE(MTYPE_NETNS_MISC, ns_path_ptr); + XFREE(MTYPE_NETNS_MISC, zns_info); + return 0; + } + thread_add_timer_msec(zebrad.master, zebra_ns_ready_read, + (void *)zns_info, + ZEBRA_NS_POLLING_INTERVAL_MSEC, NULL); + return 0; +} + +static int zebra_ns_ready_read(struct thread *t) +{ + struct zebra_netns_info *zns_info = THREAD_ARG(t); + const char *netnspath; + int err, stop_retry = 0; + + if (!zns_info) + return 0; + if (!zns_info->netnspath) { + XFREE(MTYPE_NETNS_MISC, zns_info); + return 0; + } + netnspath = zns_info->netnspath; + if (--zns_info->retries == 0) + stop_retry = 1; + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); + err = ns_switch_to_netns(netnspath); + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); + if (err < 0) + return zebra_ns_continue_read(zns_info, stop_retry); + + /* go back to default ns */ + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog_err("Can't raise privileges"); + err = ns_switchback_to_initial(); + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog_err("Can't lower privileges"); + if (err < 0) + return zebra_ns_continue_read(zns_info, stop_retry); + + /* success : close fd and create zns context */ + zebra_ns_notify_create_context_from_entry_name(basename(netnspath)); + return zebra_ns_continue_read(zns_info, 1); +} + +static int zebra_ns_notify_read(struct thread *t) +{ + int fd_monitor = THREAD_FD(t); + struct inotify_event *event; + char buf[BUFSIZ]; + ssize_t len; + + zebra_netns_notify_current = thread_add_read(zebrad.master, + zebra_ns_notify_read, + NULL, fd_monitor, NULL); + len = read(fd_monitor, buf, sizeof(buf)); + if (len < 0) { + zlog_warn("NS notify read: failed to read (%s)", + safe_strerror(errno)); + return 0; + } + for (event = (struct inotify_event *)buf; + (char *)event < &buf[len]; + event = (struct inotify_event *)((char *)event + + sizeof(*event) + event->len)) { + char *netnspath; + struct zebra_netns_info *netnsinfo; + + if (!(event->mask & IN_CREATE)) + continue; + netnspath = ns_netns_pathname(NULL, event->name); + if (!netnspath) + continue; + netnspath = XSTRDUP(MTYPE_NETNS_MISC, netnspath); + netnsinfo = XCALLOC(MTYPE_NETNS_MISC, + sizeof(struct zebra_netns_info)); + netnsinfo->retries = ZEBRA_NS_POLLING_MAX_RETRIES; + netnsinfo->netnspath = netnspath; + thread_add_timer_msec(zebrad.master, zebra_ns_ready_read, + (void *)netnsinfo, 0, NULL); + } + return 0; +} + +void zebra_ns_notify_parse(void) +{ + struct dirent *dent; + DIR *srcdir = opendir(NS_RUN_DIR); + + if (srcdir == NULL) { + zlog_warn("NS parsing init: failed to parse %s", NS_RUN_DIR); + return; + } + while ((dent = readdir(srcdir)) != NULL) { + struct stat st; + + if (strcmp(dent->d_name, ".") == 0 + || strcmp(dent->d_name, "..") == 0) + continue; + if (fstatat(dirfd(srcdir), dent->d_name, &st, 0) < 0) { + zlog_warn("NS parsing init: failed to parse entry %s", + dent->d_name); + continue; + } + if (S_ISDIR(st.st_mode)) { + zlog_warn("NS parsing init: %s is not a NS", + dent->d_name); + continue; + } + zebra_ns_notify_create_context_from_entry_name(dent->d_name); + } + closedir(srcdir); +} + +void zebra_ns_notify_init(void) +{ + int fd_monitor; + + zebra_netns_notify_current = NULL; + fd_monitor = inotify_init(); + if (fd_monitor < 0) { + zlog_warn("NS notify init: failed to initialize inotify (%s)", + safe_strerror(errno)); + } + if (inotify_add_watch(fd_monitor, NS_RUN_DIR, IN_CREATE) < 0) { + zlog_warn("NS notify watch: failed to add watch (%s)", + safe_strerror(errno)); + } + zebra_netns_notify_current = thread_add_read(zebrad.master, + zebra_ns_notify_read, + NULL, fd_monitor, NULL); +} + +void zebra_ns_notify_close(void) +{ + if (zebra_netns_notify_current == NULL) + return; + + int fd = 0; + + if (zebra_netns_notify_current->u.fd > 0) + fd = zebra_netns_notify_current->u.fd; + thread_cancel(zebra_netns_notify_current); + /* auto-removal of inotify items */ + if (fd > 0) + close(fd); +} + +#else +void zebra_ns_notify_parse(void) +{ +} + +void zebra_ns_notify_init(void) +{ +} + +void zebra_ns_notify_close(void) +{ +} +#endif /* !HAVE_NETLINK */ diff --git a/zebra/zebra_netns_notify.h b/zebra/zebra_netns_notify.h new file mode 100644 index 0000000000..0ced749ae8 --- /dev/null +++ b/zebra/zebra_netns_notify.h @@ -0,0 +1,29 @@ +/* + * Zebra NS collector and notifier for Network NameSpaces + * Copyright (C) 2017 6WIND + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _NETNS_NOTIFY_H +#define _NETNS_NOTIFY_H + +extern void zebra_ns_notify_init(void); +extern void zebra_ns_notify_parse(void); +extern void zebra_ns_notify_close(void); + +extern struct zebra_privs_t zserv_privs; + +#endif /* NETNS_NOTIFY_H */ diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 1715881f7e..cb302985c8 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. * @@ -22,6 +23,7 @@ #include "lib/ns.h" #include "lib/vrf.h" +#include "lib/logicalrouter.h" #include "lib/prefix.h" #include "lib/memory.h" @@ -31,6 +33,11 @@ #include "zebra_memory.h" #include "rt.h" #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") @@ -53,9 +60,72 @@ zebra_ns_table_entry_compare(const struct zebra_ns_table *e1, return e1->tableid - e2->tableid; } +static int logicalrouter_config_write(struct vty *vty); + struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id) { - 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) +{ + 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; + + /* Do any needed per-NS data structure allocation. */ + zns->if_table = route_table_init(); + zebra_vxlan_ns_init(zns); + + 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); +} + +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. */ @@ -63,6 +133,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 @@ -155,15 +227,27 @@ int zebra_ns_disable(ns_id_t ns_id, void **info) kernel_terminate(zns); + zns->ns_id = NS_DEFAULT; + return 0; } int zebra_ns_init(void) { - dzns = XCALLOC(MTYPE_ZEBRA_NS, sizeof(struct zebra_ns)); + ns_id_t ns_id; - ns_init(); + dzns = zebra_ns_alloc(); + + 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_management(ns_id); + + logicalrouter_init(logicalrouter_config_write); /* Do any needed per-NS data structure allocation. */ dzns->if_table = route_table_init(); @@ -173,7 +257,37 @@ int zebra_ns_init(void) zebra_vrf_init(); /* Default NS is activated */ - zebra_ns_enable(NS_DEFAULT, (void **)&dzns); + zebra_ns_enable(ns_id, (void **)&dzns); + if (vrf_is_backend_netns()) { + ns_add_hook(NS_NEW_HOOK, zebra_ns_new); + ns_add_hook(NS_ENABLE_HOOK, zebra_ns_enabled); + ns_add_hook(NS_DISABLE_HOOK, zebra_ns_disabled); + ns_add_hook(NS_DELETE_HOOK, zebra_ns_delete); + zebra_ns_notify_parse(); + zebra_ns_notify_init(); + } + return 0; +} + +static int logicalrouter_config_write(struct vty *vty) +{ + struct ns *ns; + int write = 0; + + RB_FOREACH(ns, ns_head, &ns_tree) { + if (ns->ns_id == NS_DEFAULT || ns->name == NULL) + continue; + vty_out(vty, "logical-router %u netns %s\n", ns->ns_id, + ns->name); + write = 1; + } + return write; +} + +int zebra_ns_config_write(struct vty *vty, struct ns *ns) +{ + if (ns && ns->name != NULL) + vty_out(vty, " netns %s\n", ns->name); return 0; } diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index 765f2c6893..3a998a49ff 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -69,12 +69,16 @@ 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); 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, @@ -82,4 +86,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 cd47f21278..bb15fd04f3 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" @@ -76,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)); } } @@ -90,12 +91,9 @@ static int zebra_vrf_new(struct vrf *vrf) zlog_info("VRF %s created, id %u", vrf->name, vrf->vrf_id); zvrf = zebra_vrf_alloc(); - zvrf->zns = zebra_ns_lookup( - NS_DEFAULT); /* Point to the global (single) NS */ - router_id_init(zvrf); vrf->info = zvrf; zvrf->vrf = vrf; - + router_id_init(zvrf); return 0; } @@ -116,6 +114,10 @@ static int zebra_vrf_enable(struct vrf *vrf) zlog_debug("VRF %s id %u is now active", zvrf_name(zvrf), zvrf_id(zvrf)); + if (vrf_is_backend_netns()) + zvrf->zns = zebra_ns_lookup((ns_id_t)vrf->vrf_id); + else + zvrf->zns = zebra_ns_lookup(NS_DEFAULT); /* Inform clients that the VRF is now active. This is an * add for the clients. */ @@ -562,6 +564,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"); } @@ -578,8 +581,8 @@ static int vrf_config_write(struct vty *vty) /* Zebra VRF initialization. */ void zebra_vrf_init(void) { - vrf_init(zebra_vrf_new, zebra_vrf_enable, zebra_vrf_disable, - zebra_vrf_delete); + vrf_init(zebra_vrf_new, zebra_vrf_enable, + zebra_vrf_disable, zebra_vrf_delete); vrf_cmd_init(vrf_config_write); } diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index 4d53eee093..4c12d7dee9 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -19,8 +19,8 @@ * with this program; see the file COPYING; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#if !defined(__ZEBRA_RIB_H__) -#define __ZEBRA_RIB_H__ +#if !defined(__ZEBRA_VRF_H__) +#define __ZEBRA_VRF_H__ #include #include @@ -128,14 +128,28 @@ struct zebra_vrf { static inline vrf_id_t zvrf_id(struct zebra_vrf *zvrf) { + if (!zvrf || !zvrf->vrf) + return VRF_UNKNOWN; return zvrf->vrf->vrf_id; } +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); @@ -154,4 +168,4 @@ extern void zebra_vrf_init(void); extern void zebra_rtable_node_cleanup(struct route_table *table, struct route_node *node); -#endif +#endif /* ZEBRA_VRF_H */ 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); 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)); }