Merge pull request #1711 from pguibert6WIND/issue_385_step5

Netns Support / VRF/NS/ogical router rework, along with BGP & OSPF support for multiple VRF with NETNS backend
This commit is contained in:
Renato Westphal 2018-02-27 10:20:53 -03:00 committed by GitHub
commit 7bcf94689d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 2764 additions and 731 deletions

View File

@ -289,7 +289,8 @@ void bgp_timer_set(struct peer *peer)
/* First entry point of peer's finite state machine. In Idle /* First entry point of peer's finite state machine. In Idle
status start timer is on unless peer is shutdown or peer is status start timer is on unless peer is shutdown or peer is
inactive. All other timer must be turned off */ 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); BGP_TIMER_OFF(peer->t_start);
} else { } else {
BGP_TIMER_ON(peer->t_start, bgp_start_timer, BGP_TIMER_ON(peer->t_start, bgp_start_timer,
@ -1376,6 +1377,15 @@ int bgp_start(struct peer *peer)
return 0; 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 */ /* Register to be notified on peer up */
if (peer->sort == BGP_PEER_EBGP && peer->ttl == 1 if (peer->sort == BGP_PEER_EBGP && peer->ttl == 1
&& !CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK) && !CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)

View File

@ -41,6 +41,7 @@
#include "vrf.h" #include "vrf.h"
#include "bfd.h" #include "bfd.h"
#include "libfrr.h" #include "libfrr.h"
#include "ns.h"
#include "bgpd/bgpd.h" #include "bgpd/bgpd.h"
#include "bgpd/bgp_attr.h" #include "bgpd/bgp_attr.h"
@ -57,6 +58,7 @@
#include "bgpd/bgp_zebra.h" #include "bgpd/bgp_zebra.h"
#include "bgpd/bgp_packet.h" #include "bgpd/bgp_packet.h"
#include "bgpd/bgp_keepalives.h" #include "bgpd/bgp_keepalives.h"
#include "bgpd/bgp_network.h"
#ifdef ENABLE_BGP_VNC #ifdef ENABLE_BGP_VNC
#include "bgpd/rfapi/rfapi_backend.h" #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". */ /* We have instance configured, link to VRF and make it "up". */
bgp_vrf_link(bgp, vrf); bgp_vrf_link(bgp, vrf);
bgp_handle_socket(bgp, vrf, old_vrf_id, true);
/* Update any redistribute vrf bitmaps if the vrf_id changed */ /* Update any redistribute vrf bitmaps if the vrf_id changed */
if (old_vrf_id != bgp->vrf_id) if (old_vrf_id != bgp->vrf_id)
bgp_update_redist_vrf_bitmaps(bgp, old_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); bgp = bgp_lookup_by_name(vrf->name);
if (bgp) { if (bgp) {
old_vrf_id = bgp->vrf_id; old_vrf_id = bgp->vrf_id;
bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, false);
/* We have instance configured, unlink from VRF and make it /* We have instance configured, unlink from VRF and make it
* "down". */ * "down". */
bgp_vrf_unlink(bgp, vrf); bgp_vrf_unlink(bgp, vrf);

View File

@ -34,6 +34,7 @@
#include "queue.h" #include "queue.h"
#include "hash.h" #include "hash.h"
#include "filter.h" #include "filter.h"
#include "ns.h"
#include "bgpd/bgpd.h" #include "bgpd/bgpd.h"
#include "bgpd/bgp_open.h" #include "bgpd/bgp_open.h"
@ -44,13 +45,14 @@
extern struct zebra_privs_t bgpd_privs; 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. */ /* BGP listening socket. */
struct bgp_listener { struct bgp_listener {
int fd; int fd;
union sockunion su; union sockunion su;
struct thread *thread; struct thread *thread;
struct bgp *bgp;
}; };
/* /*
@ -284,6 +286,7 @@ static int bgp_accept(struct thread *thread)
return -1; return -1;
} }
listener->thread = NULL; listener->thread = NULL;
thread_add_read(bm->master, bgp_accept, listener, accept_sock, thread_add_read(bm->master, bgp_accept, listener, accept_sock,
&listener->thread); &listener->thread);
@ -296,8 +299,13 @@ static int bgp_accept(struct thread *thread)
} }
set_nonblocking(bgp_sock); set_nonblocking(bgp_sock);
/* Obtain BGP instance this connection is meant for. */ /* Obtain BGP instance this connection is meant for.
if (bgp_get_instance_for_inc_conn(bgp_sock, &bgp)) { * - 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)) if (bgp_debug_neighbor_events(NULL))
zlog_debug( zlog_debug(
"[Event] Could not get instance for incoming conn from %s", "[Event] Could not get instance for incoming conn from %s",
@ -407,7 +415,7 @@ static int bgp_accept(struct thread *thread)
peer->doppelganger = peer1; peer->doppelganger = peer1;
peer1->doppelganger = peer; peer1->doppelganger = peer;
peer->fd = bgp_sock; 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_fsm_change_status(peer, Active);
BGP_TIMER_OFF(peer->t_start); /* created in peer_create() */ BGP_TIMER_OFF(peer->t_start); /* created in peer_create() */
@ -435,21 +443,20 @@ static int bgp_accept(struct thread *thread)
} }
/* BGP socket bind. */ /* 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; char *name = NULL;
/* If not bound to an interface or part of a VRF, we don't care. */ if ((peer->bgp->vrf_id == VRF_DEFAULT) &&
if (!peer->bgp->vrf_id && !peer->ifname && !peer->conf_if) !peer->ifname && !peer->conf_if)
return 0; return NULL;
if (peer->su.sa.sa_family != AF_INET if (peer->su.sa.sa_family != AF_INET
&& peer->su.sa.sa_family != AF_INET6) && 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) /* For IPv6 peering, interface (unnumbered or link-local with interface)
* takes precedence over VRF. For IPv4 peering, explicit interface or * takes precedence over VRF. For IPv4 peering, explicit interface or
* VRF are the situations to bind. * VRF are the situations to bind.
@ -461,30 +468,7 @@ static int bgp_bind(struct peer *peer)
else else
name = peer->ifname ? peer->ifname : peer->bgp->name; name = peer->ifname ? peer->ifname : peer->bgp->name;
if (!name) return 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;
} }
static int bgp_update_address(struct interface *ifp, const union sockunion *dst, 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"); zlog_debug("Peer address not learnt: Returning from connect");
return 0; return 0;
} }
if (bgpd_privs.change(ZPRIVS_RAISE))
zlog_err("Can't raise privileges");
/* Make socket for the peer. */ /* 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) if (peer->fd < 0)
return -1; return -1;
@ -591,9 +580,6 @@ int bgp_connect(struct peer *peer)
if (peer->password) if (peer->password)
bgp_md5_set_connect(peer->fd, &peer->su, peer->password); bgp_md5_set_connect(peer->fd, &peer->su, peer->password);
/* Bind socket. */
bgp_bind(peer);
/* Update source bind. */ /* Update source bind. */
if (bgp_update_source(peer) < 0) { if (bgp_update_source(peer) < 0) {
return connect_error; return connect_error;
@ -642,12 +628,12 @@ int bgp_getsockname(struct peer *peer)
return -1; return -1;
#endif #endif
} }
return 0; 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; struct bgp_listener *listener;
int ret, en; int ret, en;
@ -683,8 +669,14 @@ static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen)
return ret; return ret;
} }
listener = XMALLOC(MTYPE_BGP_LISTENER, sizeof(*listener)); listener = XCALLOC(MTYPE_BGP_LISTENER, sizeof(*listener));
listener->fd = sock; 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); memcpy(&listener->su, sa, salen);
listener->thread = NULL; listener->thread = NULL;
thread_add_read(bm->master, bgp_accept, listener, sock, 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. */ /* 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;
struct addrinfo *ainfo_save; 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); snprintf(port_str, sizeof(port_str), "%d", port);
port_str[sizeof(port_str) - 1] = '\0'; 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) { if (ret != 0) {
zlog_err("getaddrinfo: %s", gai_strerror(ret)); zlog_err("getaddrinfo: %s", gai_strerror(ret));
return -1; 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) if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6)
continue; continue;
sock = socket(ainfo->ai_family, ainfo->ai_socktype, if (bgpd_privs.change(ZPRIVS_RAISE))
ainfo->ai_protocol); 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) { if (sock < 0) {
zlog_err("socket: %s", safe_strerror(errno)); zlog_err("socket: %s", safe_strerror(errno));
continue; continue;
@ -734,7 +736,8 @@ int bgp_socket(unsigned short port, const char *address)
* ttl=255 */ * ttl=255 */
sockopt_ttl(ainfo->ai_family, sock, MAXTTL); 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) if (ret == 0)
++count; ++count;
else else
@ -751,6 +754,32 @@ int bgp_socket(unsigned short port, const char *address)
return 0; 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) void bgp_close(void)
{ {
struct listnode *node, *next; struct listnode *node, *next;
@ -760,6 +789,8 @@ void bgp_close(void)
return; return;
for (ALL_LIST_ELEMENTS(bm->listen_sockets, node, next, listener)) { for (ALL_LIST_ELEMENTS(bm->listen_sockets, node, next, listener)) {
if (listener->bgp)
continue;
thread_cancel(listener->thread); thread_cancel(listener->thread);
close(listener->fd); close(listener->fd);
listnode_delete(bm->listen_sockets, listener); listnode_delete(bm->listen_sockets, listener);

View File

@ -23,7 +23,9 @@
#define BGP_SOCKET_SNDBUF_SIZE 65536 #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 void bgp_close(void);
extern int bgp_connect(struct peer *); extern int bgp_connect(struct peer *);
extern int bgp_getsockname(struct peer *); extern int bgp_getsockname(struct peer *);

View File

@ -101,6 +101,42 @@ static void bgp_if_finish(struct bgp *bgp);
extern struct zclient *zclient; 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) void bgp_session_reset(struct peer *peer)
{ {
if (peer->doppelganger && (peer->doppelganger->status != Deleted) 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; 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. */ /* Called from VTY commands. */
int bgp_get(struct bgp **bgp_val, as_t *as, const char *name, int bgp_get(struct bgp **bgp_val, as_t *as, const char *name,
enum bgp_instance_type inst_type) enum bgp_instance_type inst_type)
{ {
struct bgp *bgp; struct bgp *bgp;
struct vrf *vrf = NULL;
/* Multiple instance check. */ /* Multiple instance check. */
if (bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) { 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; 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 Default instance or VRF, link to the VRF structure, if present. */
if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT
|| bgp->inst_type == BGP_INSTANCE_TYPE_VRF) { || bgp->inst_type == BGP_INSTANCE_TYPE_VRF) {
struct vrf *vrf;
vrf = bgp_vrf_lookup_by_instance_type(bgp); vrf = bgp_vrf_lookup_by_instance_type(bgp);
if (vrf) if (vrf)
bgp_vrf_link(bgp, 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)) if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp))
bgp_zebra_instance_register(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. * routes to be processed still referencing the struct bgp.
*/ */
listnode_delete(bm->bgp, bgp); listnode_delete(bm->bgp, bgp);
if (list_isempty(bm->bgp))
bgp_close();
/* Deregister from Zebra, if needed */ /* Deregister from Zebra, if needed */
if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp))
@ -3199,6 +3283,7 @@ int bgp_delete(struct bgp *bgp)
bgp_if_finish(bgp); bgp_if_finish(bgp);
vrf = bgp_vrf_lookup_by_instance_type(bgp); vrf = bgp_vrf_lookup_by_instance_type(bgp);
bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, false);
if (vrf) if (vrf)
bgp_vrf_unlink(bgp, vrf); bgp_vrf_unlink(bgp, vrf);
@ -3337,11 +3422,12 @@ struct peer *peer_lookup(struct bgp *bgp, union sockunion *su)
struct listnode *bgpnode, *nbgpnode; struct listnode *bgpnode, *nbgpnode;
for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp)) { for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp)) {
/* Skip VRFs, this function will not be invoked without /* Skip VRFs Lite only, this function will not be
* an instance * invoked without an instance
* when examining VRFs. * 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; continue;
peer = hash_lookup(bgp->peerhash, &tmp_peer); peer = hash_lookup(bgp->peerhash, &tmp_peer);

View File

@ -1355,6 +1355,9 @@ extern void bgp_instance_up(struct bgp *);
extern void bgp_instance_down(struct bgp *); extern void bgp_instance_down(struct bgp *);
extern int bgp_delete(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_set(struct bgp *, int);
extern int bgp_flag_unset(struct bgp *, int); extern int bgp_flag_unset(struct bgp *, int);
extern int bgp_flag_check(struct bgp *, int); extern int bgp_flag_check(struct bgp *, int);

View File

@ -92,6 +92,11 @@ maximum before starting zebra.
Note that this affects Linux only. Note that this affects Linux only.
.TP .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 \fB\-M\fR, \fB\-\-module \fR\fImodule:options\fR
Load a module at startup. May be specified more than once. Load a module at startup. May be specified more than once.
The \fBsnmp\fR and \fBfpm\fR modules may be The \fBsnmp\fR and \fBfpm\fR modules may be

View File

@ -0,0 +1,23 @@
/* Copyright (c) 2015 6WIND S.A.
* Author: Nicolas Dichtel <nicolas.dichtel@6wind.com>
*
* 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_ */

View File

@ -6,4 +6,5 @@ noinst_HEADERS += \
include/linux/neighbour.h \ include/linux/neighbour.h \
include/linux/rtnetlink.h \ include/linux/rtnetlink.h \
include/linux/socket.h \ include/linux/socket.h \
include/linux/net_namespace.h \
# end # end

View File

@ -62,7 +62,7 @@ const char *node_names[] = {
"aaa", // AAA_NODE, "aaa", // AAA_NODE,
"keychain", // KEYCHAIN_NODE, "keychain", // KEYCHAIN_NODE,
"keychain key", // KEYCHAIN_KEY_NODE, "keychain key", // KEYCHAIN_KEY_NODE,
"logical-router", // NS_NODE, "logical-router", // LOGICALROUTER_NODE,
"vrf", // VRF_NODE, "vrf", // VRF_NODE,
"interface", // INTERFACE_NODE, "interface", // INTERFACE_NODE,
"zebra", // ZEBRA_NODE, "zebra", // ZEBRA_NODE,
@ -1291,7 +1291,7 @@ void cmd_exit(struct vty *vty)
break; break;
case INTERFACE_NODE: case INTERFACE_NODE:
case PW_NODE: case PW_NODE:
case NS_NODE: case LOGICALROUTER_NODE:
case VRF_NODE: case VRF_NODE:
case ZEBRA_NODE: case ZEBRA_NODE:
case BGP_NODE: case BGP_NODE:
@ -1376,7 +1376,7 @@ DEFUN (config_end,
case CONFIG_NODE: case CONFIG_NODE:
case INTERFACE_NODE: case INTERFACE_NODE:
case PW_NODE: case PW_NODE:
case NS_NODE: case LOGICALROUTER_NODE:
case VRF_NODE: case VRF_NODE:
case ZEBRA_NODE: case ZEBRA_NODE:
case RIP_NODE: case RIP_NODE:

View File

@ -85,7 +85,7 @@ enum node_type {
AAA_NODE, /* AAA node. */ AAA_NODE, /* AAA node. */
KEYCHAIN_NODE, /* Key-chain node. */ KEYCHAIN_NODE, /* Key-chain node. */
KEYCHAIN_KEY_NODE, /* Key-chain key node. */ KEYCHAIN_KEY_NODE, /* Key-chain key node. */
NS_NODE, /* Logical-Router node. */ LOGICALROUTER_NODE, /* Logical-Router node. */
VRF_NODE, /* VRF mode node. */ VRF_NODE, /* VRF mode node. */
INTERFACE_NODE, /* Interface mode node. */ INTERFACE_NODE, /* Interface mode node. */
ZEBRA_NODE, /* zebra connection node. */ ZEBRA_NODE, /* zebra connection node. */

View File

@ -384,30 +384,36 @@ struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id, int vty)
{ {
struct interface *ifp; struct interface *ifp;
ifp = if_lookup_by_name_all_vrf(name); ifp = if_lookup_by_name(name, vrf_id);
if (ifp) { if (ifp)
if (ifp->vrf_id == vrf_id)
return ifp; return ifp;
/* Not Found on same VRF. If the interface command
/* Found a match on a different VRF. If the interface command
* was entered in vty without a VRF (passed as VRF_DEFAULT), * was entered in vty without a VRF (passed as VRF_DEFAULT),
* accept the ifp we found. If a vrf was entered and there is * 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 * a mismatch, reject it if from vty.
* or by way of zclient, believe it and update the ifp
* accordingly.
*/ */
ifp = if_lookup_by_name_all_vrf(name);
if (!ifp)
return if_create(name, vrf_id);
if (vty) { if (vty) {
if (vrf_id == VRF_DEFAULT) if (vrf_id == VRF_DEFAULT)
return ifp; return ifp;
return NULL; return NULL;
} else { }
/* 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); if_update_to_new_vrf(ifp, vrf_id);
return ifp; return ifp;
} }
}
return if_create(name, vrf_id);
}
void if_set_index(struct interface *ifp, ifindex_t ifindex) void if_set_index(struct interface *ifp, ifindex_t ifindex)
{ {

159
lib/logicalrouter.c Normal file
View File

@ -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 <zebra.h>
#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;
}

41
lib/logicalrouter.h Normal file
View File

@ -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*/

539
lib/netns_linux.c Normal file
View File

@ -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 <zebra.h>
#ifdef HAVE_NETNS
#undef _GNU_SOURCE
#define _GNU_SOURCE
#include <sched.h>
#endif
/* for basename */
#include <libgen.h>
#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;
}

165
lib/netns_other.c Normal file
View File

@ -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 <zebra.h>
#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 */

456
lib/ns.c
View File

@ -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 <zebra.h>
#ifdef HAVE_NETNS
#undef _GNU_SOURCE
#define _GNU_SOURCE
#include <sched.h>
#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;
}

View File

@ -24,16 +24,22 @@
#include "openbsd-tree.h" #include "openbsd-tree.h"
#include "linklist.h" #include "linklist.h"
#include "vty.h"
typedef u_int32_t ns_id_t; typedef u_int32_t ns_id_t;
/* the default NS ID */ /* the default NS ID */
#define NS_DEFAULT 0
#define NS_UNKNOWN UINT32_MAX #define NS_UNKNOWN UINT32_MAX
/* Default netns directory (Linux) */ /* Default netns directory (Linux) */
#define NS_RUN_DIR "/var/run/netns" #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 { struct ns {
RB_ENTRY(ns) entry; RB_ENTRY(ns) entry;
@ -49,6 +55,9 @@ struct ns {
/* Master list of interfaces belonging to this NS */ /* Master list of interfaces belonging to this NS */
struct list *iflist; struct list *iflist;
/* Back Pointer to VRF */
void *vrf_ctxt;
/* User data */ /* User data */
void *info; void *info;
}; };
@ -57,6 +66,11 @@ RB_PROTOTYPE(ns_head, ns, entry, ns_compare)
extern struct ns_head ns_tree; 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 * NS hooks
*/ */
@ -74,20 +88,82 @@ extern struct ns_head ns_tree;
* - param 2: the address of the user data pointer (the user data * - param 2: the address of the user data pointer (the user data
* can be stored in or freed from there) * 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 * NS initializer/destructor
*/ */
/* Please add hooks before calling ns_init(). */
extern void ns_init(void);
extern void ns_terminate(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 * NS utilities
*/ */
/* Create a socket serving for the given NS */ /* Create a socket serving for the given NS
extern int ns_socket(int, int, int, ns_id_t); */
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*/ #endif /*_ZEBRA_NS_H*/

View File

@ -42,7 +42,8 @@ lib_libfrr_la_SOURCES = \
lib/module.c \ lib/module.c \
lib/network.c \ lib/network.c \
lib/nexthop.c \ lib/nexthop.c \
lib/ns.c \ lib/netns_linux.c \
lib/netns_other.c \
lib/openbsd-tree.c \ lib/openbsd-tree.c \
lib/pid_output.c \ lib/pid_output.c \
lib/plist.c \ lib/plist.c \
@ -74,6 +75,7 @@ lib_libfrr_la_SOURCES = \
lib/wheel.c \ lib/wheel.c \
lib/workqueue.c \ lib/workqueue.c \
lib/zclient.c \ lib/zclient.c \
lib/logicalrouter.c \
# end # end
lib/plist_clippy.c: $(CLIPPY_DEPS) lib/plist_clippy.c: $(CLIPPY_DEPS)
@ -158,6 +160,7 @@ pkginclude_HEADERS += \
lib/zassert.h \ lib/zassert.h \
lib/zclient.h \ lib/zclient.h \
lib/zebra.h \ lib/zebra.h \
lib/logicalrouter.h \
# end # end
nodist_pkginclude_HEADERS += \ nodist_pkginclude_HEADERS += \

374
lib/vrf.c
View File

@ -21,6 +21,9 @@
#include <zebra.h> #include <zebra.h>
/* for basename */
#include <libgen.h>
#include "if.h" #include "if.h"
#include "vrf.h" #include "vrf.h"
#include "vrf_int.h" #include "vrf_int.h"
@ -29,6 +32,10 @@
#include "log.h" #include "log.h"
#include "memory.h" #include "memory.h"
#include "command.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, "VRF")
DEFINE_MTYPE_STATIC(LIB, VRF_BITMAP, "VRF bit-map") 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_id_head vrfs_by_id = RB_INITIALIZER(&vrfs_by_id);
struct vrf_name_head vrfs_by_name = RB_INITIALIZER(&vrfs_by_name); struct vrf_name_head vrfs_by_name = RB_INITIALIZER(&vrfs_by_name);
static int vrf_backend;
/* /*
* Turn on/off debug code * Turn on/off debug code
* for vrf. * for vrf.
@ -61,7 +70,6 @@ struct vrf_master {
}; };
static int vrf_is_enabled(struct vrf *vrf); static int vrf_is_enabled(struct vrf *vrf);
static void vrf_disable(struct vrf *vrf);
/* VRF list existance check by name. */ /* VRF list existance check by name. */
struct vrf *vrf_lookup_by_name(const char *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); 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. /* Get a VRF. If not found, create one.
* Arg: * Arg:
* name - The name of the vrf. May be NULL if unknown. * 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; int new = 0;
if (debug_vrf) 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 */ /* Nothing to see, move along here */
if (!name && vrf_id == VRF_UNKNOWN) 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) { if (vrf == NULL) {
vrf = XCALLOC(MTYPE_VRF, sizeof(struct vrf)); vrf = XCALLOC(MTYPE_VRF, sizeof(struct vrf));
vrf->vrf_id = VRF_UNKNOWN; 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); QOBJ_REG(vrf, vrf);
new = 1; 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)); strlcpy(vrf->name, name, sizeof(vrf->name));
RB_INSERT(vrf_name_head, &vrfs_by_name, vrf); RB_INSERT(vrf_name_head, &vrfs_by_name, vrf);
} }
if (new &&vrf_master.vrf_new_hook) if (new &&vrf_master.vrf_new_hook)
(*vrf_master.vrf_new_hook)(vrf); (*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 * The VRF_DELETE_HOOK callback will be called to inform
* that they must release the resources in the VRF. * 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)) if (!vrf_is_enabled(vrf))
return; return;
@ -385,6 +439,8 @@ void vrf_init(int (*create)(struct vrf *), int (*enable)(struct vrf *),
{ {
struct vrf *default_vrf; struct vrf *default_vrf;
/* initialise NS, in case VRF backend if NETNS */
ns_init();
if (debug_vrf) if (debug_vrf)
zlog_debug("%s: Initializing VRF subsystem", zlog_debug("%s: Initializing VRF subsystem",
__PRETTY_FUNCTION__); __PRETTY_FUNCTION__);
@ -437,13 +493,148 @@ void vrf_terminate(void)
} }
/* Create a socket for the VRF. */ /* 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); 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; 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 */ /* vrf CLI commands */
@ -455,21 +646,8 @@ DEFUN_NOSH (vrf,
{ {
int idx_name = 1; int idx_name = 1;
const char *vrfname = argv[idx_name]->arg; const char *vrfname = argv[idx_name]->arg;
struct vrf *vrfp;
if (strlen(vrfname) > VRF_NAMSIZ) { return vrf_handler_create(vty, vrfname, NULL);
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;
} }
DEFUN_NOSH (no_vrf, DEFUN_NOSH (no_vrf,
@ -505,6 +683,55 @@ DEFUN_NOSH (no_vrf,
struct cmd_node vrf_node = {VRF_NODE, "%s(config-vrf)# ", 1}; 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 * 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_element(CONFIG_NODE, &no_vrf_cmd);
install_node(&vrf_node, writefunc); install_node(&vrf_node, writefunc);
install_default(VRF_NODE); 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;
} }

View File

@ -26,12 +26,9 @@
#include "linklist.h" #include "linklist.h"
#include "qobj.h" #include "qobj.h"
#include "vty.h" #include "vty.h"
#include "ns.h"
/* The default NS ID */
#define NS_DEFAULT 0
/* The default VRF ID */ /* The default VRF ID */
#define VRF_DEFAULT 0
#define VRF_UNKNOWN UINT32_MAX #define VRF_UNKNOWN UINT32_MAX
/* Pending: May need to refine this. */ /* Pending: May need to refine this. */
@ -42,6 +39,7 @@ enum { IFLA_VRF_UNSPEC, IFLA_VRF_TABLE, __IFLA_VRF_MAX };
#endif #endif
#define VRF_NAMSIZ 36 #define VRF_NAMSIZ 36
#define NS_NAMSIZ 16
#define VRF_DEFAULT_NAME "Default-IP-Routing-Table" #define VRF_DEFAULT_NAME "Default-IP-Routing-Table"
@ -60,6 +58,7 @@ struct vrf_data {
union { union {
struct { struct {
uint32_t table_id; uint32_t table_id;
char netns_name[NS_NAMSIZ];
} l; } l;
}; };
}; };
@ -88,6 +87,9 @@ struct vrf {
/* The table_id from the kernel */ /* The table_id from the kernel */
struct vrf_data data; struct vrf_data data;
/* Back pointer to namespace context */
void *ns_ctxt;
QOBJ_FIELDS QOBJ_FIELDS
}; };
RB_HEAD(vrf_id_head, vrf); 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) RB_PROTOTYPE(vrf_name_head, vrf, name_entry, vrf_name_compare)
DECLARE_QOBJ_TYPE(vrf) 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_id_head vrfs_by_id;
extern struct vrf_name_head vrfs_by_name; 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); 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)); extern void vrf_cmd_init(int (*writefunc)(struct vty *vty));
/* VRF vty debugging
*/
extern void vrf_install_commands(void);
/* /*
* VRF utilities * VRF utilities
*/ */
/* Create a socket serving for the given VRF */ /* API for configuring VRF backend
extern int vrf_socket(int, int, int, vrf_id_t); * should be called from zebra only
/*
* VRF Debugging
*/ */
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*/ #endif /*_ZEBRA_VRF_H*/

View File

@ -1402,8 +1402,8 @@ static void zclient_vrf_add(struct zclient *zclient, vrf_id_t vrf_id)
/* Lookup/create vrf by vrf_id. */ /* Lookup/create vrf by vrf_id. */
vrf = vrf_get(vrf_id, vrfname_tmp); 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); vrf_enable(vrf);
} }

View File

@ -169,42 +169,27 @@ int ospf_if_ipmulticast(struct ospf *top, struct prefix *p, ifindex_t ifindex)
return ret; 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_init(struct ospf *ospf)
{ {
int ospf_sock; int ospf_sock;
int ret, hincl = 1; int ret, hincl = 1;
int bufsize = (8 * 1024 * 1024); 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)) { if (ospfd_privs.change(ZPRIVS_RAISE)) {
zlog_err("ospf_sock_init: could not raise privs, %s", zlog_err("ospf_sock_init: could not raise privs, %s",
safe_strerror(errno)); 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) { if (ospf_sock < 0) {
int save_errno = errno; int save_errno = errno;
@ -216,12 +201,6 @@ int ospf_sock_init(struct ospf *ospf)
exit(1); exit(1);
} }
ret = ospf_bind_vrfdevice(ospf, ospf_sock);
if (ret < 0) {
close(ospf_sock);
goto out;
}
#ifdef IP_HDRINCL #ifdef IP_HDRINCL
/* we will include IP header with packet */ /* we will include IP header with packet */
ret = setsockopt(ospf_sock, IPPROTO_IP, IP_HDRINCL, &hincl, ret = setsockopt(ospf_sock, IPPROTO_IP, IP_HDRINCL, &hincl,

View File

@ -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_drop_alldrouters(struct ospf *, struct prefix *, ifindex_t);
extern int ospf_if_ipmulticast(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_sock_init(struct ospf *ospf);
extern int ospf_bind_vrfdevice(struct ospf *, int);
#endif /* _ZEBRA_OSPF_NETWORK_H */ #endif /* _ZEBRA_OSPF_NETWORK_H */

View File

@ -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_refresh_interval, &new->t_lsa_refresher);
new->lsa_refresher_started = monotime(NULL); 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) { if ((new->ibuf = stream_new(OSPF_MAX_PACKET_SIZE + 1)) == NULL) {
zlog_err( zlog_err(
"ospf_new: fatal error: stream_new(%u) failed allocating ibuf", "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); exit(1);
} }
new->t_read = NULL; new->t_read = NULL;
thread_add_read(master, ospf_read, new, new->fd, &new->t_read);
new->oi_write_q = list_new(); new->oi_write_q = list_new();
new->write_oi_count = OSPF_WRITE_INTERFACE_COUNT_DEFAULT; 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); 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; return new;
} }
@ -2049,7 +2052,8 @@ static int ospf_vrf_delete(struct vrf *vrf)
static int ospf_vrf_enable(struct vrf *vrf) static int ospf_vrf_enable(struct vrf *vrf)
{ {
struct ospf *ospf = NULL; 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) if (IS_DEBUG_OSPF_EVENT)
zlog_debug("%s: VRF %s id %u enabled", 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", zlog_err("ospf_sock_init: could not raise privs, %s",
safe_strerror(errno)); safe_strerror(errno));
} }
if (ospf_bind_vrfdevice(ospf, ospf->fd) < 0) ret = ospf_sock_init(ospf);
return 0;
if (ospfd_privs.change(ZPRIVS_LOWER)) { if (ospfd_privs.change(ZPRIVS_LOWER)) {
zlog_err("ospf_sock_init: could not lower privs, %s", zlog_err("ospf_sock_init: could not lower privs, %s",
safe_strerror(errno)); 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->oi_running = 1;
ospf_zebra_vrf_register(ospf); ospf_zebra_vrf_register(ospf);
ospf_router_id_update(ospf); ospf_router_id_update(ospf);
@ -2111,6 +2117,9 @@ static int ospf_vrf_disable(struct vrf *vrf)
if (IS_DEBUG_OSPF_EVENT) if (IS_DEBUG_OSPF_EVENT)
zlog_debug("%s: ospf old_vrf_id %d unlinked", zlog_debug("%s: ospf old_vrf_id %d unlinked",
__PRETTY_FUNCTION__, old_vrf_id); __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. */ /* Note: This is a callback, the VRF will be deleted by the caller. */

View File

@ -147,7 +147,7 @@ vtysh_cmd_FILES = $(vtysh_scan) \
$(top_srcdir)/lib/distribute.c $(top_srcdir)/lib/if_rmap.c \ $(top_srcdir)/lib/distribute.c $(top_srcdir)/lib/if_rmap.c \
$(top_srcdir)/lib/vrf.c \ $(top_srcdir)/lib/vrf.c \
$(top_srcdir)/lib/vty.c $(top_srcdir)/zebra/debug.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/interface.c \
$(top_srcdir)/zebra/irdp_interface.c \ $(top_srcdir)/zebra/irdp_interface.c \
$(top_srcdir)/zebra/rtadv.c $(top_srcdir)/zebra/zebra_vty.c \ $(top_srcdir)/zebra/rtadv.c $(top_srcdir)/zebra/zebra_vty.c \

View File

@ -87,6 +87,9 @@ foreach (@ARGV) {
elsif ($file =~ /lib\/vrf\.c$/) { elsif ($file =~ /lib\/vrf\.c$/) {
$protocol = "VTYSH_ALL"; $protocol = "VTYSH_ALL";
} }
elsif ($file =~ /lib\/logicalrouter\.c$/) {
$protocol = "VTYSH_ALL";
}
elsif ($file =~ /lib\/filter\.c$/) { elsif ($file =~ /lib\/filter\.c$/) {
$protocol = "VTYSH_ALL"; $protocol = "VTYSH_ALL";
} }

View File

@ -975,8 +975,8 @@ static struct cmd_node pw_node = {
PW_NODE, "%s(config-pw)# ", PW_NODE, "%s(config-pw)# ",
}; };
static struct cmd_node ns_node = { static struct cmd_node logicalrouter_node = {
NS_NODE, "%s(config-logical-router)# ", LOGICALROUTER_NODE, "%s(config-logical-router)# ",
}; };
static struct cmd_node vrf_node = { static struct cmd_node vrf_node = {
@ -1508,7 +1508,7 @@ static int vtysh_exit(struct vty *vty)
break; break;
case INTERFACE_NODE: case INTERFACE_NODE:
case PW_NODE: case PW_NODE:
case NS_NODE: case LOGICALROUTER_NODE:
case VRF_NODE: case VRF_NODE:
case ZEBRA_NODE: case ZEBRA_NODE:
case BGP_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" "Delete a pseudo interface's configuration\n"
"Interface's name\n" VRF_CMD_HELP_STR) "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" "Enable a logical-router\n"
"Specify the logical-router indentifier\n" "Specify the logical-router indentifier\n"
"The Name Space\n" "The Name Space\n"
"The file name in " NS_RUN_DIR ", or a full pathname\n") "The file name in " NS_RUN_DIR ", or a full pathname\n")
{ {
vty->node = NS_NODE; vty->node = LOGICALROUTER_NODE;
return CMD_SUCCESS; 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", DEFUNSH(VTYSH_VRF, vtysh_vrf, vtysh_vrf_cmd, "vrf NAME",
"Select a VRF to configure\n" "Select a VRF to configure\n"
"VRF's name\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" "Delete a pseudo vrf's configuration\n"
"VRF's name\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") "Exit current mode and down to previous mode\n")
{ {
return vtysh_exit(vty); 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") "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", 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(&interface_node, NULL);
install_node(&pw_node, NULL); install_node(&pw_node, NULL);
install_node(&link_params_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(&vrf_node, NULL);
install_node(&rmap_node, NULL); install_node(&rmap_node, NULL);
install_node(&zebra_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_exit_interface_cmd);
install_element(PW_NODE, &vtysh_quit_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(CONFIG_NODE, &vtysh_logicalrouter_cmd);
install_element(NS_NODE, &vtysh_exit_ns_cmd); install_element(CONFIG_NODE, &vtysh_no_logicalrouter_cmd);
install_element(NS_NODE, &vtysh_quit_ns_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_end_all_cmd);
install_element(VRF_NODE, &vtysh_exit_vrf_cmd); install_element(VRF_NODE, &vtysh_exit_vrf_cmd);

View File

@ -187,7 +187,7 @@ void vtysh_config_parse_line(void *arg, const char *line)
config->index = INTERFACE_NODE; config->index = INTERFACE_NODE;
} else if (config->index == RMAP_NODE } else if (config->index == RMAP_NODE
|| config->index == INTERFACE_NODE || config->index == INTERFACE_NODE
|| config->index == NS_NODE || config->index == LOGICALROUTER_NODE
|| config->index == VTY_NODE || config->index == VTY_NODE
|| config->index == VRF_NODE) || config->index == VRF_NODE)
config_add_line_uniq(config->line, line); 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) else if (strncmp(line, "pseudowire", strlen("pseudowire")) == 0)
config = config_get(PW_NODE, line); config = config_get(PW_NODE, line);
else if (strncmp(line, "logical-router", strlen("ns")) == 0) 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) else if (strncmp(line, "vrf", strlen("vrf")) == 0)
config = config_get(VRF_NODE, line); config = config_get(VRF_NODE, line);
else if (strncmp(line, "router-id", strlen("router-id")) == 0) else if (strncmp(line, "router-id", strlen("router-id")) == 0)

View File

@ -146,7 +146,7 @@ static int if_get_hwaddr(struct interface *ifp)
ifreq.ifr_addr.sa_family = AF_INET; ifreq.ifr_addr.sa_family = AF_INET;
/* Fetch Hardware address if available. */ /* 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) if (ret < 0)
ifp->hw_addr_len = 0; ifp->hw_addr_len = 0;
else { else {

View File

@ -66,6 +66,7 @@
#include "zebra/kernel_netlink.h" #include "zebra/kernel_netlink.h"
#include "zebra/if_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 /* Note: on netlink systems, there should be a 1-to-1 mapping between interface
names and ifindex values. */ 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 ifreq ifdata;
struct ethtool_cmd ecmd; struct ethtool_cmd ecmd;
int sd; int sd;
int rc; int rc;
const char *ifname = interface->name;
/* initialize struct */ /* initialize struct */
memset(&ifdata, 0, sizeof(ifdata)); memset(&ifdata, 0, sizeof(ifdata));
@ -363,16 +365,20 @@ static int get_iflink_speed(const char *ifname)
ifdata.ifr_data = (__caddr_t)&ecmd; ifdata.ifr_data = (__caddr_t)&ecmd;
/* use ioctl to get IP address of an interface */ /* 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 (sd < 0) {
if (IS_ZEBRA_DEBUG_KERNEL) if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug("Failure to read interface %s speed: %d %s", zlog_debug("Failure to read interface %s speed: %d %s",
ifname, errno, safe_strerror(errno)); ifname, errno, safe_strerror(errno));
return 0; return 0;
} }
/* Get the current link state for the interface */ /* 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 (rc < 0) {
if (IS_ZEBRA_DEBUG_KERNEL) if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug( zlog_debug(
@ -389,7 +395,7 @@ static int get_iflink_speed(const char *ifname)
uint32_t kernel_get_speed(struct interface *ifp) 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, 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 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); netlink_vrf_change(h, tb[IFLA_LINKINFO], name);
vrf_id = (vrf_id_t)ifi->ifi_index; vrf_id = (vrf_id_t)ifi->ifi_index;
} }
if (tb[IFLA_MASTER]) { 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; zif_slave_type = ZEBRA_IF_SLAVE_VRF;
vrf_id = *(u_int32_t *)RTA_DATA(tb[IFLA_MASTER]); vrf_id = *(u_int32_t *)RTA_DATA(tb[IFLA_MASTER]);
} else if (slave_kind && (strcmp(slave_kind, "bridge") == 0)) { } 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 } else
zif_slave_type = ZEBRA_IF_SLAVE_OTHER; 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 linking to another interface, note it. */
if (tb[IFLA_LINK]) 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); SET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK);
ifp->mtu6 = ifp->mtu = *(uint32_t *)RTA_DATA(tb[IFLA_MTU]); ifp->mtu6 = ifp->mtu = *(uint32_t *)RTA_DATA(tb[IFLA_MTU]);
ifp->metric = 0; ifp->metric = 0;
ifp->speed = get_iflink_speed(name); ifp->speed = get_iflink_speed(ifp);
ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN; ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN;
if (desc) if (desc)
@ -789,8 +798,12 @@ static int netlink_address(int cmd, int family, struct interface *ifp,
char buf[NL_PKT_BUF_SIZE]; char buf[NL_PKT_BUF_SIZE];
} req; } 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; p = ifc->address;
memset(&req, 0, sizeof req - NL_PKT_BUF_SIZE); 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); zns = zebra_ns_lookup(ns_id);
ifi = NLMSG_DATA(h); ifi = NLMSG_DATA(h);
/* assume if not default zns, then new VRF */
if (!(h->nlmsg_type == RTM_NEWLINK || h->nlmsg_type == RTM_DELLINK)) { if (!(h->nlmsg_type == RTM_NEWLINK || h->nlmsg_type == RTM_DELLINK)) {
/* If this is not link add/delete message so print warning. */ /* If this is not link add/delete message so print warning. */
zlog_warn("netlink_link_change: wrong kernel message %d", 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 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); netlink_vrf_change(h, tb[IFLA_LINKINFO], name);
vrf_id = (vrf_id_t)ifi->ifi_index; 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 (h->nlmsg_type == RTM_NEWLINK) {
if (tb[IFLA_MASTER]) { 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; zif_slave_type = ZEBRA_IF_SLAVE_VRF;
vrf_id = vrf_id =
*(u_int32_t *)RTA_DATA(tb[IFLA_MASTER]); *(u_int32_t *)RTA_DATA(tb[IFLA_MASTER]);
@ -1103,7 +1118,8 @@ int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
} else } else
zif_slave_type = ZEBRA_IF_SLAVE_OTHER; zif_slave_type = ZEBRA_IF_SLAVE_OTHER;
} }
if (vrf_is_backend_netns())
vrf_id = (vrf_id_t)ns_id;
if (ifp == NULL if (ifp == NULL
|| !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) { || !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) {
/* Add interface notification from kernel */ /* Add interface notification from kernel */

View File

@ -512,9 +512,15 @@ static void if_addr_wakeup(struct interface *ifp)
void if_add_update(struct interface *ifp) void if_add_update(struct interface *ifp)
{ {
struct zebra_if *if_data; 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; if_data = ifp->info;
assert(if_data); 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"; char buf[16] = "169.254.0.1";
struct in_addr ipv4_ll; struct in_addr ipv4_ll;
char mac[6]; char mac[6];
ns_id_t ns_id;
inet_pton(AF_INET, buf, &ipv4_ll); inet_pton(AF_INET, buf, &ipv4_ll);
ipv6_ll_address_to_mac(address, (u_char *)mac); ipv6_ll_address_to_mac(address, (u_char *)mac);
ns_id = zvrf->zns->ns_id;
/* /*
* Remove existed arp record for the interface as netlink * 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 * 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 */ /* 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++; zvrf->neigh_updates++;
} }
@ -1404,7 +1412,7 @@ DEFUN (show_interface_name_vrf,
int idx_ifname = 2; int idx_ifname = 2;
int idx_name = 4; int idx_name = 4;
struct interface *ifp; struct interface *ifp;
vrf_id_t vrf_id = VRF_DEFAULT; vrf_id_t vrf_id;
interface_update_stats(); interface_update_stats();

View File

@ -59,6 +59,7 @@ int if_ioctl(u_long request, caddr_t buffer)
sock = socket(AF_INET, SOCK_DGRAM, 0); sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) { if (sock < 0) {
int save_errno = errno; int save_errno = errno;
if (zserv_privs.change(ZPRIVS_LOWER)) if (zserv_privs.change(ZPRIVS_LOWER))
zlog_err("Can't lower privileges"); zlog_err("Can't lower privileges");
zlog_err("Cannot create UDP socket: %s", zlog_err("Cannot create UDP socket: %s",
@ -78,6 +79,39 @@ int if_ioctl(u_long request, caddr_t buffer)
return 0; 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 #ifndef HAVE_NETLINK
static int if_ioctl_ipv6(u_long request, caddr_t buffer) 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); sock = socket(AF_INET6, SOCK_DGRAM, 0);
if (sock < 0) { if (sock < 0) {
int save_errno = errno; int save_errno = errno;
if (zserv_privs.change(ZPRIVS_LOWER)) if (zserv_privs.change(ZPRIVS_LOWER))
zlog_err("Can't lower privileges"); zlog_err("Can't lower privileges");
zlog_err("Cannot create IPv6 datagram socket: %s", zlog_err("Cannot create IPv6 datagram socket: %s",
@ -122,7 +157,7 @@ void if_get_metric(struct interface *ifp)
ifreq_set_name(&ifreq, 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; return;
ifp->metric = ifreq.ifr_metric; ifp->metric = ifreq.ifr_metric;
if (ifp->metric == 0) if (ifp->metric == 0)
@ -140,7 +175,7 @@ void if_get_mtu(struct interface *ifp)
ifreq_set_name(&ifreq, ifp); ifreq_set_name(&ifreq, ifp);
#if defined(SIOCGIFMTU) #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)"); zlog_info("Can't lookup mtu by ioctl(SIOCGIFMTU)");
ifp->mtu6 = ifp->mtu = -1; ifp->mtu6 = ifp->mtu = -1;
return; return;
@ -376,9 +411,9 @@ void if_get_flags(struct interface *ifp)
ifreq_set_name(&ifreq, 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) { if (ret < 0) {
zlog_err("if_ioctl(SIOCGIFFLAGS) failed: %s", zlog_err("vrf_if_ioctl(SIOCGIFFLAGS) failed: %s",
safe_strerror(errno)); safe_strerror(errno));
return; return;
} }
@ -423,7 +458,7 @@ int if_set_flags(struct interface *ifp, uint64_t flags)
ifreq.ifr_flags = ifp->flags; ifreq.ifr_flags = ifp->flags;
ifreq.ifr_flags |= 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) { if (ret < 0) {
zlog_info("can't set interface flags"); 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 = ifp->flags;
ifreq.ifr_flags &= ~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) { if (ret < 0) {
zlog_info("can't unset interface flags"); zlog_info("can't unset interface flags");

View File

@ -25,6 +25,7 @@
/* Prototypes. */ /* Prototypes. */
extern void ifreq_set_name(struct ifreq *, struct interface *); extern void ifreq_set_name(struct ifreq *, struct interface *);
extern int if_ioctl(u_long, caddr_t); 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_set_flags(struct interface *, uint64_t);
extern int if_unset_flags(struct interface *, uint64_t); extern int if_unset_flags(struct interface *, uint64_t);

View File

@ -30,6 +30,7 @@
#include "log.h" #include "log.h"
#include "privs.h" #include "privs.h"
#include "vty.h" #include "vty.h"
#include "vrf.h"
#include "zebra/rib.h" #include "zebra/rib.h"
#include "zebra/rt.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); 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 */ /* call ioctl system call */
int if_ioctl(u_long request, caddr_t buffer) int if_ioctl(u_long request, caddr_t buffer)
{ {

View File

@ -188,7 +188,7 @@ static int netlink_socket(struct nlsock *nl, unsigned long groups,
return -1; return -1;
} }
sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); sock = ns_socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE, ns_id);
if (sock < 0) { if (sock < 0) {
zlog_err("Can't open %s socket: %s", nl->name, zlog_err("Can't open %s socket: %s", nl->name,
safe_strerror(errno)); safe_strerror(errno));

View File

@ -1384,7 +1384,8 @@ static void routing_socket(struct zebra_ns *zns)
if (zserv_privs.change(ZPRIVS_RAISE)) if (zserv_privs.change(ZPRIVS_RAISE))
zlog_err("routing_socket: Can't raise privileges"); 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 (routing_sock < 0) {
if (zserv_privs.change(ZPRIVS_LOWER)) if (zserv_privs.change(ZPRIVS_LOWER))

View File

@ -34,6 +34,7 @@
#include "privs.h" #include "privs.h"
#include "sigevent.h" #include "sigevent.h"
#include "vrf.h" #include "vrf.h"
#include "logicalrouter.h"
#include "libfrr.h" #include "libfrr.h"
#include "zebra/rib.h" #include "zebra/rib.h"
@ -47,6 +48,7 @@
#include "zebra/redistribute.h" #include "zebra/redistribute.h"
#include "zebra/zebra_mpls.h" #include "zebra/zebra_mpls.h"
#include "zebra/label_manager.h" #include "zebra/label_manager.h"
#include "zebra/zebra_netns_notify.h"
#define ZEBRA_PTM_SUPPORT #define ZEBRA_PTM_SUPPORT
@ -85,6 +87,7 @@ struct option longopts[] = {{"batch", no_argument, NULL, 'b'},
{"label_socket", no_argument, NULL, 'l'}, {"label_socket", no_argument, NULL, 'l'},
{"retain", no_argument, NULL, 'r'}, {"retain", no_argument, NULL, 'r'},
#ifdef HAVE_NETLINK #ifdef HAVE_NETLINK
{"vrfwnetns", no_argument, NULL, 'n'},
{"nl-bufsize", required_argument, NULL, 's'}, {"nl-bufsize", required_argument, NULL, 's'},
#endif /* HAVE_NETLINK */ #endif /* HAVE_NETLINK */
{0}}; {0}};
@ -122,7 +125,6 @@ static void sigint(void)
{ {
struct vrf *vrf; struct vrf *vrf;
struct zebra_vrf *zvrf; struct zebra_vrf *zvrf;
struct zebra_ns *zns;
zlog_notice("Terminating on signal"); zlog_notice("Terminating on signal");
@ -137,10 +139,12 @@ static void sigint(void)
if (zvrf) if (zvrf)
SET_FLAG(zvrf->flags, ZEBRA_VRF_RETAIN); SET_FLAG(zvrf->flags, ZEBRA_VRF_RETAIN);
} }
if (zebrad.lsp_process_q)
work_queue_free(zebrad.lsp_process_q);
vrf_terminate(); vrf_terminate();
zns = zebra_ns_lookup(NS_DEFAULT); ns_walk_func(zebra_ns_disabled);
zebra_ns_disable(0, (void **)&zns); zebra_ns_notify_close();
access_list_reset(); access_list_reset();
prefix_list_reset(); prefix_list_reset();
@ -148,8 +152,6 @@ static void sigint(void)
list_delete_and_null(&zebrad.client_list); list_delete_and_null(&zebrad.client_list);
work_queue_free(zebrad.ribq); work_queue_free(zebrad.ribq);
if (zebrad.lsp_process_q)
work_queue_free(zebrad.lsp_process_q);
meta_queue_free(zebrad.mq); meta_queue_free(zebrad.mq);
frr_fini(); frr_fini();
@ -205,12 +207,16 @@ int main(int argc, char **argv)
char *fuzzing = NULL; char *fuzzing = NULL;
#endif #endif
vrf_configure_backend(VRF_BACKEND_VRF_LITE);
logicalrouter_configure_backend(
LOGICALROUTER_BACKEND_NETNS);
frr_preinit(&zebra_di, argc, argv); frr_preinit(&zebra_di, argc, argv);
frr_opt_add( frr_opt_add(
"bakz:e:l:r" "bakz:e:l:r"
#ifdef HAVE_NETLINK #ifdef HAVE_NETLINK
"s:" "s:n"
#endif #endif
#if defined(HANDLE_ZAPI_FUZZING) #if defined(HANDLE_ZAPI_FUZZING)
"c:" "c:"
@ -225,6 +231,7 @@ int main(int argc, char **argv)
" -k, --keep_kernel Don't delete old routes which installed by zebra.\n" " -k, --keep_kernel Don't delete old routes which installed by zebra.\n"
" -r, --retain When program terminates, retain added route by zebra.\n" " -r, --retain When program terminates, retain added route by zebra.\n"
#ifdef HAVE_NETLINK #ifdef HAVE_NETLINK
" -n, --vrfwnetns Set VRF with NetNS\n"
" -s, --nl-bufsize Set netlink receive buffer size\n" " -s, --nl-bufsize Set netlink receive buffer size\n"
#endif /* HAVE_NETLINK */ #endif /* HAVE_NETLINK */
#if defined(HANDLE_ZAPI_FUZZING) #if defined(HANDLE_ZAPI_FUZZING)
@ -279,6 +286,11 @@ int main(int argc, char **argv)
case 's': case 's':
nl_rcvbufsize = atoi(optarg); nl_rcvbufsize = atoi(optarg);
break; break;
case 'n':
vrf_configure_backend(VRF_BACKEND_NETNS);
logicalrouter_configure_backend(
LOGICALROUTER_BACKEND_OFF);
break;
#endif /* HAVE_NETLINK */ #endif /* HAVE_NETLINK */
#if defined(HANDLE_ZAPI_FUZZING) #if defined(HANDLE_ZAPI_FUZZING)
case 'c': case 'c':

View File

@ -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_delete_ipv4(struct interface *, struct connected *);
extern int kernel_address_add_ipv6 (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_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, extern int kernel_interface_set_master(struct interface *master,
struct interface *slave); struct interface *slave);

View File

@ -194,16 +194,25 @@ static inline int proto2zebra(int proto, int family)
/* /*
Pending: create an efficient table_id (in a tree/hash) based lookup) 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 vrf *vrf;
struct zebra_vrf *zvrf; struct zebra_vrf *zvrf;
RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { 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; continue;
/* case vrf with netns : match the netnsid */
if (vrf_is_backend_netns()) {
if (ns_id == zvrf_id(zvrf))
return 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; return VRF_DEFAULT;
@ -220,7 +229,7 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl,
u_char flags = 0; u_char flags = 0;
struct prefix p; struct prefix p;
struct prefix_ipv6 src_p = {}; struct prefix_ipv6 src_p = {};
vrf_id_t vrf_id = VRF_DEFAULT; vrf_id_t vrf_id;
char anyaddr[16] = {0}; char anyaddr[16] = {0};
@ -288,7 +297,7 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl,
table = rtm->rtm_table; table = rtm->rtm_table;
/* Map to VRF */ /* 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 (vrf_id == VRF_DEFAULT) {
if (!is_zebra_valid_kernel_table(table) if (!is_zebra_valid_kernel_table(table)
&& !is_zebra_main_routing_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 sbuf[40];
char gbuf[40]; char gbuf[40];
char oif_list[256] = "\0"; char oif_list[256] = "\0";
vrf_id_t vrf = ns_id; vrf_id_t vrf;
int table; int table;
if (mroute) if (mroute)
@ -631,7 +640,7 @@ static int netlink_route_change_read_multicast(struct sockaddr_nl *snl,
else else
table = rtm->rtm_table; table = rtm->rtm_table;
vrf = vrf_lookup_by_table(table); vrf = vrf_lookup_by_table(table, ns_id);
if (tb[RTA_IIF]) if (tb[RTA_IIF])
iif = *(int *)RTA_DATA(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) ns_id_t ns_id, int startup)
{ {
int len; int len;
vrf_id_t vrf_id = ns_id;
struct rtmsg *rtm; struct rtmsg *rtm;
rtm = NLMSG_DATA(h); rtm = NLMSG_DATA(h);
if (!(h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE)) { if (!(h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE)) {
/* If this is not route add/delete message print warning. */ /* 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; return 0;
} }
/* Connected route. */ /* Connected route. */
if (IS_ZEBRA_DEBUG_KERNEL) 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_msg_type_to_str(h->nlmsg_type),
nl_family_to_str(rtm->rtm_family), nl_family_to_str(rtm->rtm_family),
nl_rttype_to_str(rtm->rtm_type), 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. */ /* We don't care about change notifications for the MPLS table. */
/* TODO: Revisit this. */ /* 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, 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 {
struct nlmsghdr n; struct nlmsghdr n;
@ -1282,7 +1290,7 @@ static int netlink_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla,
char buf[256]; char buf[256];
} req; } 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.n, 0, sizeof(req.n));
memset(&req.ndm, 0, sizeof(req.ndm)); 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]; char buf[NL_PKT_BUF_SIZE];
} req; } req;
struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); struct zebra_ns *zns;
struct zebra_vrf *zvrf = vrf_info_lookup(re->vrf_id); struct zebra_vrf *zvrf = vrf_info_lookup(re->vrf_id);
zns = zvrf->zns;
memset(&req, 0, sizeof req - NL_PKT_BUF_SIZE); memset(&req, 0, sizeof req - NL_PKT_BUF_SIZE);
bytelen = (family == AF_INET ? 4 : 16); bytelen = (family == AF_INET ? 4 : 16);
@ -1626,8 +1635,9 @@ int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in)
} req; } req;
mroute = mr; 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.n, 0, sizeof(req.n));
memset(&req.ndm, 0, sizeof(req.ndm)); 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 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, 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, static int netlink_vxlan_flood_list_update(struct interface *ifp,
struct in_addr *vtep_ip, int cmd) struct in_addr *vtep_ip, int cmd)
{ {
struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); struct zebra_ns *zns;
struct { struct {
struct nlmsghdr n; struct nlmsghdr n;
struct ndmsg ndm; struct ndmsg ndm;
char buf[256]; char buf[256];
} req; } req;
u_char dst_mac[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; 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.n, 0, sizeof(req.n));
memset(&req.ndm, 0, sizeof(req.ndm)); 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 #endif
static int netlink_macfdb_change(struct sockaddr_nl *snl, struct nlmsghdr *h, 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 ndmsg *ndm;
struct interface *ifp; struct interface *ifp;
@ -1799,7 +1811,7 @@ static int netlink_macfdb_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
return 0; return 0;
/* The interface should exist. */ /* 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); ndm->ndm_ifindex);
if (!ifp || !ifp->info) if (!ifp || !ifp->info)
return 0; return 0;
@ -1930,7 +1942,7 @@ static int netlink_macfdb_table(struct sockaddr_nl *snl, struct nlmsghdr *h,
if (ndm->ndm_family != AF_BRIDGE) if (ndm->ndm_family != AF_BRIDGE)
return 0; 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 */ /* 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, struct ethaddr *mac, struct in_addr vtep_ip,
int local, int cmd, u_char sticky) int local, int cmd, u_char sticky)
{ {
struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); struct zebra_ns *zns;
struct { struct {
struct nlmsghdr n; struct nlmsghdr n;
struct ndmsg ndm; 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; int vid_present = 0, dst_present = 0;
char vid_buf[20]; char vid_buf[20];
char dst_buf[30]; char dst_buf[30];
struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id);
zns = zvrf->zns;
zif = ifp->info; zif = ifp->info;
if ((br_if = zif->brslave_info.br_if) == NULL) { if ((br_if = zif->brslave_info.br_if) == NULL) {
zlog_warn("MAC %s on IF %s(%u) - no mapping to bridge", 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) | NUD_DELAY)
static int netlink_ipneigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h, 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 ndmsg *ndm;
struct interface *ifp; struct interface *ifp;
@ -2107,7 +2121,7 @@ static int netlink_ipneigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
return 0; return 0;
/* The interface should exist. */ /* 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); ndm->ndm_ifindex);
if (!ifp || !ifp->info) if (!ifp || !ifp->info)
return 0; return 0;
@ -2129,7 +2143,7 @@ static int netlink_ipneigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
* itself * itself
*/ */
if (IS_ZEBRA_IF_VLAN(ifp)) { 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); zif->link_ifindex);
if (!link_if) if (!link_if)
return 0; 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? */ /* Is this a notification for the MAC FDB or IP neighbor table? */
ndm = NLMSG_DATA(h); ndm = NLMSG_DATA(h);
if (ndm->ndm_family == AF_BRIDGE) 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) if (ndm->ndm_type != RTN_UNICAST)
return 0; return 0;
if (ndm->ndm_family == AF_INET || ndm->ndm_family == AF_INET6) 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; return 0;
} }
@ -2328,10 +2342,12 @@ static int netlink_neigh_update2(struct interface *ifp, struct ipaddr *ip,
} req; } req;
int ipa_len; int ipa_len;
struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); struct zebra_ns *zns;
char buf[INET6_ADDRSTRLEN]; char buf[INET6_ADDRSTRLEN];
char buf2[ETHER_ADDR_STRLEN]; 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.n, 0, sizeof(req.n));
memset(&req.ndm, 0, sizeof(req.ndm)); memset(&req.ndm, 0, sizeof(req.ndm));

View File

@ -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 kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla,
int llalen) int llalen, ns_id_t ns_id)
{ {
/* TODO */ /* TODO */
return 0; return 0;

View File

@ -34,6 +34,7 @@
#include "command.h" #include "command.h"
#include "privs.h" #include "privs.h"
#include "vrf.h" #include "vrf.h"
#include "ns.h"
#include "zebra/interface.h" #include "zebra/interface.h"
#include "zebra/rtadv.h" #include "zebra/rtadv.h"
@ -621,7 +622,7 @@ static int rtadv_read(struct thread *thread)
return 0; return 0;
} }
static int rtadv_make_socket(void) static int rtadv_make_socket(ns_id_t ns_id)
{ {
int sock; int sock;
int ret = 0; int ret = 0;
@ -631,7 +632,7 @@ static int rtadv_make_socket(void)
zlog_err("rtadv_make_socket: could not raise privs, %s", zlog_err("rtadv_make_socket: could not raise privs, %s",
safe_strerror(errno)); 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)) if (zserv_privs.change(ZPRIVS_LOWER))
zlog_err("rtadv_make_socket: could not lower privs, %s", 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) 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) void rtadv_terminate(struct zebra_ns *zns)

View File

@ -65,6 +65,8 @@ zebra_zebra_SOURCES = \
zebra/zebra_vty.c \ zebra/zebra_vty.c \
zebra/zebra_vxlan.c \ zebra/zebra_vxlan.c \
zebra/zserv.c \ zebra/zserv.c \
zebra/zebra_netns_id.c \
zebra/zebra_netns_notify.c \
# end # end
zebra/zebra_vty_clippy.c: $(CLIPPY_DEPS) zebra/zebra_vty_clippy.c: $(CLIPPY_DEPS)
@ -104,6 +106,8 @@ noinst_HEADERS += \
zebra/zebra_vxlan.h \ zebra/zebra_vxlan.h \
zebra/zebra_vxlan_private.h \ zebra/zebra_vxlan_private.h \
zebra/zserv.h \ zebra/zserv.h \
zebra/zebra_netns_id.h \
zebra/zebra_netns_notify.h \
# end # end
zebra_zebra_irdp_la_SOURCES = \ zebra_zebra_irdp_la_SOURCES = \

355
zebra/zebra_netns_id.c Normal file
View File

@ -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 <zebra.h>
#include "ns.h"
#include "vrf.h"
#include "log.h"
#if defined(HAVE_NETLINK)
#include <linux/net_namespace.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#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 */
}

26
zebra/zebra_netns_id.h Normal file
View File

@ -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__ */

271
zebra/zebra_netns_notify.c Normal file
View File

@ -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 <zebra.h>
#ifdef HAVE_NETLINK
#ifdef HAVE_NETNS
#undef _GNU_SOURCE
#define _GNU_SOURCE
#include <sched.h>
#endif
#include <dirent.h>
#include <sys/inotify.h>
#include <sys/stat.h>
#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 */

View File

@ -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 */

View File

@ -1,6 +1,7 @@
/* zebra NS Routines /* zebra NS Routines
* Copyright (C) 2016 Cumulus Networks, Inc. * Copyright (C) 2016 Cumulus Networks, Inc.
* Donald Sharp * Donald Sharp
* Copyright (C) 2017/2018 6WIND
* *
* This file is part of Quagga. * This file is part of Quagga.
* *
@ -22,6 +23,7 @@
#include "lib/ns.h" #include "lib/ns.h"
#include "lib/vrf.h" #include "lib/vrf.h"
#include "lib/logicalrouter.h"
#include "lib/prefix.h" #include "lib/prefix.h"
#include "lib/memory.h" #include "lib/memory.h"
@ -31,6 +33,11 @@
#include "zebra_memory.h" #include "zebra_memory.h"
#include "rt.h" #include "rt.h"
#include "zebra_vxlan.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") 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; return e1->tableid - e2->tableid;
} }
static int logicalrouter_config_write(struct vty *vty);
struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id) struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id)
{ {
if (ns_id == NS_DEFAULT)
return dzns; 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. */ /* 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); struct zebra_ns *zns = (struct zebra_ns *)(*info);
zns->ns_id = ns_id;
#if defined(HAVE_RTADV) #if defined(HAVE_RTADV)
rtadv_init(zns); rtadv_init(zns);
#endif #endif
@ -155,15 +227,27 @@ int zebra_ns_disable(ns_id_t ns_id, void **info)
kernel_terminate(zns); kernel_terminate(zns);
zns->ns_id = NS_DEFAULT;
return 0; return 0;
} }
int zebra_ns_init(void) 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. */ /* Do any needed per-NS data structure allocation. */
dzns->if_table = route_table_init(); dzns->if_table = route_table_init();
@ -173,7 +257,37 @@ int zebra_ns_init(void)
zebra_vrf_init(); zebra_vrf_init();
/* Default NS is activated */ /* 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; return 0;
} }

View File

@ -69,12 +69,16 @@ struct zebra_ns {
#endif /* HAVE_RTADV */ #endif /* HAVE_RTADV */
struct zebra_ns_table_head ns_tables; struct zebra_ns_table_head ns_tables;
/* Back pointer */
struct ns *ns;
}; };
struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id); struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id);
int zebra_ns_init(void); int zebra_ns_init(void);
int zebra_ns_enable(ns_id_t ns_id, void **info); 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); int zebra_ns_disable(ns_id_t ns_id, void **info);
extern struct route_table *zebra_ns_find_table(struct zebra_ns *zns, 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, extern struct route_table *zebra_ns_get_table(struct zebra_ns *zns,
struct zebra_vrf *zvrf, struct zebra_vrf *zvrf,
uint32_t tableid, afi_t afi); uint32_t tableid, afi_t afi);
int zebra_ns_config_write(struct vty *vty, struct ns *ns);
#endif #endif

View File

@ -25,8 +25,9 @@
#include "command.h" #include "command.h"
#include "memory.h" #include "memory.h"
#include "srcdest_table.h" #include "srcdest_table.h"
#include "vrf.h"
#include "vty.h" #include "vty.h"
#include "zebra/debug.h" #include "zebra/debug.h"
#include "zebra/zserv.h" #include "zebra/zserv.h"
#include "zebra/rib.h" #include "zebra/rib.h"
@ -76,7 +77,7 @@ void zebra_vrf_update_all(struct zserv *client)
struct vrf *vrf; struct vrf *vrf;
RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) { 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)); 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); zlog_info("VRF %s created, id %u", vrf->name, vrf->vrf_id);
zvrf = zebra_vrf_alloc(); zvrf = zebra_vrf_alloc();
zvrf->zns = zebra_ns_lookup(
NS_DEFAULT); /* Point to the global (single) NS */
router_id_init(zvrf);
vrf->info = zvrf; vrf->info = zvrf;
zvrf->vrf = vrf; zvrf->vrf = vrf;
router_id_init(zvrf);
return 0; return 0;
} }
@ -116,6 +114,10 @@ static int zebra_vrf_enable(struct vrf *vrf)
zlog_debug("VRF %s id %u is now active", zlog_debug("VRF %s id %u is now active",
zvrf_name(zvrf), zvrf_id(zvrf)); 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 /* Inform clients that the VRF is now active. This is an
* add for the clients. * add for the clients.
*/ */
@ -562,6 +564,7 @@ static int vrf_config_write(struct vty *vty)
zvrf->l3vni, zvrf->l3vni,
is_l3vni_for_prefix_routes_only(zvrf->l3vni) ? is_l3vni_for_prefix_routes_only(zvrf->l3vni) ?
" prefix-routes-only" :""); " prefix-routes-only" :"");
zebra_ns_config_write(vty, (struct ns *)vrf->ns_ctxt);
vty_out(vty, "!\n"); vty_out(vty, "!\n");
} }
@ -578,8 +581,8 @@ static int vrf_config_write(struct vty *vty)
/* Zebra VRF initialization. */ /* Zebra VRF initialization. */
void zebra_vrf_init(void) void zebra_vrf_init(void)
{ {
vrf_init(zebra_vrf_new, zebra_vrf_enable, zebra_vrf_disable, vrf_init(zebra_vrf_new, zebra_vrf_enable,
zebra_vrf_delete); zebra_vrf_disable, zebra_vrf_delete);
vrf_cmd_init(vrf_config_write); vrf_cmd_init(vrf_config_write);
} }

View File

@ -19,8 +19,8 @@
* with this program; see the file COPYING; if not, write to the Free Software * 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 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#if !defined(__ZEBRA_RIB_H__) #if !defined(__ZEBRA_VRF_H__)
#define __ZEBRA_RIB_H__ #define __ZEBRA_VRF_H__
#include <zebra/zebra_ns.h> #include <zebra/zebra_ns.h>
#include <zebra/zebra_pw.h> #include <zebra/zebra_pw.h>
@ -128,14 +128,28 @@ struct zebra_vrf {
static inline vrf_id_t zvrf_id(struct zebra_vrf *zvrf) static inline vrf_id_t zvrf_id(struct zebra_vrf *zvrf)
{ {
if (!zvrf || !zvrf->vrf)
return VRF_UNKNOWN;
return zvrf->vrf->vrf_id; 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) static inline const char *zvrf_name(struct zebra_vrf *zvrf)
{ {
return zvrf->vrf->name; 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, struct route_table *zebra_vrf_table_with_table_id(afi_t afi, safi_t safi,
vrf_id_t vrf_id, vrf_id_t vrf_id,
u_int32_t table_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, extern void zebra_rtable_node_cleanup(struct route_table *table,
struct route_node *node); struct route_node *node);
#endif #endif /* ZEBRA_VRF_H */

View File

@ -2374,8 +2374,12 @@ DEFUN (show_vrf,
continue; continue;
vty_out(vty, "vrf %s ", zvrf_name(zvrf)); 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"); 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 else
vty_out(vty, "id %u table %u", zvrf_id(zvrf), vty_out(vty, "id %u table %u", zvrf_id(zvrf),
zvrf->table_id); zvrf->table_id);

View File

@ -20,6 +20,8 @@
#include <zebra.h> #include <zebra.h>
#include <sys/un.h> #include <sys/un.h>
/* for basename */
#include <libgen.h>
#include "prefix.h" #include "prefix.h"
#include "command.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) static void zserv_encode_vrf(struct stream *s, struct zebra_vrf *zvrf)
{ {
struct vrf_data data; struct vrf_data data;
const char *netns_name = zvrf_ns_name(zvrf);
data.l.table_id = zvrf->table_id; 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)); stream_put(s, &data, sizeof(struct vrf_data));
/* Interface information. */ /* Interface information. */
stream_put(s, zvrf_name(zvrf), VRF_NAMSIZ); stream_put(s, zvrf_name(zvrf), VRF_NAMSIZ);
/* Write packet size. */ /* Write packet size. */
stream_putw_at(s, 0, stream_get_endp(s)); stream_putw_at(s, 0, stream_get_endp(s));
} }