mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-07-27 05:28:51 +00:00
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:
commit
7bcf94689d
@ -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)
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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 *);
|
||||||
|
118
bgpd/bgpd.c
118
bgpd/bgpd.c
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
23
include/linux/net_namespace.h
Normal file
23
include/linux/net_namespace.h
Normal 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_ */
|
@ -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
|
||||||
|
@ -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:
|
||||||
|
@ -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. */
|
||||||
|
32
lib/if.c
32
lib/if.c
@ -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
159
lib/logicalrouter.c
Normal 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
41
lib/logicalrouter.h
Normal 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
539
lib/netns_linux.c
Normal 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
165
lib/netns_other.c
Normal 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
456
lib/ns.c
@ -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;
|
|
||||||
}
|
|
88
lib/ns.h
88
lib/ns.h
@ -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*/
|
||||||
|
@ -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
374
lib/vrf.c
@ -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;
|
||||||
}
|
}
|
||||||
|
93
lib/vrf.h
93
lib/vrf.h
@ -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*/
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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 */
|
||||||
|
@ -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. */
|
||||||
|
@ -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 \
|
||||||
|
@ -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";
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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)
|
||||||
|
@ -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 {
|
||||||
|
@ -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 */
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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");
|
||||||
|
@ -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);
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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));
|
||||||
|
@ -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))
|
||||||
|
24
zebra/main.c
24
zebra/main.c
@ -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':
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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));
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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)
|
||||||
|
@ -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
355
zebra/zebra_netns_id.c
Normal 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
26
zebra/zebra_netns_id.h
Normal 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
271
zebra/zebra_netns_notify.c
Normal 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 */
|
29
zebra/zebra_netns_notify.h
Normal file
29
zebra/zebra_netns_notify.h
Normal 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 */
|
120
zebra/zebra_ns.c
120
zebra/zebra_ns.c
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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 */
|
||||||
|
@ -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);
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user