summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_dump.c4
-rw-r--r--bgpd/bgp_evpn.c66
-rw-r--r--bgpd/bgp_evpn.h3
-rw-r--r--bgpd/bgp_evpn_private.h21
-rw-r--r--bgpd/bgp_evpn_vty.c8
-rw-r--r--bgpd/bgp_fsm.c12
-rw-r--r--bgpd/bgp_main.c4
-rw-r--r--bgpd/bgp_mpath.c23
-rw-r--r--bgpd/bgp_network.c127
-rw-r--r--bgpd/bgp_network.h4
-rw-r--r--bgpd/bgp_zebra.c13
-rw-r--r--bgpd/bgpd.c119
-rw-r--r--bgpd/bgpd.h5
-rw-r--r--doc/zebra.8.in5
-rw-r--r--eigrpd/eigrp_zebra.c9
-rw-r--r--include/linux/net_namespace.h23
-rw-r--r--include/subdir.am1
-rw-r--r--ldpd/interface.c5
-rw-r--r--ldpd/l2vpn.c13
-rw-r--r--ldpd/lde.c5
-rw-r--r--ldpd/lde_lib.c4
-rw-r--r--ldpd/ldp_vty_conf.c13
-rw-r--r--ldpd/ldpd.c41
-rw-r--r--ldpd/ldpe.c5
-rw-r--r--ldpd/neighbor.c4
-rw-r--r--lib/command.c6
-rw-r--r--lib/command.h2
-rw-r--r--lib/if.c52
-rw-r--r--lib/logicalrouter.c159
-rw-r--r--lib/logicalrouter.h41
-rw-r--r--lib/netns_linux.c539
-rw-r--r--lib/netns_other.c165
-rw-r--r--lib/ns.c453
-rw-r--r--lib/ns.h88
-rw-r--r--lib/subdir.am5
-rw-r--r--lib/vrf.c381
-rw-r--r--lib/vrf.h91
-rw-r--r--lib/zclient.c18
-rw-r--r--lib/zclient.h5
-rw-r--r--ospfd/ospf_network.c41
-rw-r--r--ospfd/ospf_network.h1
-rw-r--r--ospfd/ospfd.c31
-rw-r--r--pimd/CAVEATS6
-rw-r--r--pimd/pim_iface.c12
-rw-r--r--pimd/pim_ifchannel.c5
-rw-r--r--sharpd/sharp_zebra.c9
-rw-r--r--vtysh/Makefile.am2
-rwxr-xr-xvtysh/extract.pl.in3
-rw-r--r--vtysh/vtysh.c40
-rw-r--r--vtysh/vtysh_config.c4
-rw-r--r--vtysh/vtysh_main.c2
-rw-r--r--zebra/if_ioctl.c2
-rw-r--r--zebra/if_netlink.c40
-rw-r--r--zebra/interface.c18
-rw-r--r--zebra/ioctl.c47
-rw-r--r--zebra/ioctl.h1
-rw-r--r--zebra/ioctl_solaris.c6
-rw-r--r--zebra/kernel_netlink.c2
-rw-r--r--zebra/kernel_socket.c3
-rw-r--r--zebra/main.c24
-rw-r--r--zebra/rt.h3
-rw-r--r--zebra/rt_netlink.c74
-rw-r--r--zebra/rt_socket.c2
-rw-r--r--zebra/rtadv.c7
-rw-r--r--zebra/subdir.am4
-rw-r--r--zebra/zebra_netns_id.c355
-rw-r--r--zebra/zebra_netns_id.h26
-rw-r--r--zebra/zebra_netns_notify.c271
-rw-r--r--zebra/zebra_netns_notify.h29
-rw-r--r--zebra/zebra_ns.c209
-rw-r--r--zebra/zebra_ns.h25
-rw-r--r--zebra/zebra_pw.c5
-rw-r--r--zebra/zebra_rib.c15
-rw-r--r--zebra/zebra_vrf.c80
-rw-r--r--zebra/zebra_vrf.h26
-rw-r--r--zebra/zebra_vty.c139
-rw-r--r--zebra/zebra_vxlan.c40
-rw-r--r--zebra/zebra_vxlan.h3
-rw-r--r--zebra/zebra_vxlan_private.h3
-rw-r--r--zebra/zserv.c35
-rw-r--r--zebra/zserv.h3
81 files changed, 3269 insertions, 926 deletions
diff --git a/bgpd/bgp_dump.c b/bgpd/bgp_dump.c
index 0831369062..4e998b1fd9 100644
--- a/bgpd/bgp_dump.c
+++ b/bgpd/bgp_dump.c
@@ -465,7 +465,7 @@ static void bgp_dump_common(struct stream *obuf, struct peer *peer,
}
if (peer->su.sa.sa_family == AF_INET) {
- stream_putw(obuf, peer->ifindex);
+ stream_putw(obuf, peer->ifp ? peer->ifp->ifindex : 0);
stream_putw(obuf, AFI_IP);
stream_put(obuf, &peer->su.sin.sin_addr, IPV4_MAX_BYTELEN);
@@ -477,7 +477,7 @@ static void bgp_dump_common(struct stream *obuf, struct peer *peer,
stream_put(obuf, empty, IPV4_MAX_BYTELEN);
} else if (peer->su.sa.sa_family == AF_INET6) {
/* Interface Index and Address family. */
- stream_putw(obuf, peer->ifindex);
+ stream_putw(obuf, peer->ifp ? peer->ifp->ifindex : 0);
stream_putw(obuf, AFI_IP6);
/* Source IP Address and Destination IP Address. */
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c
index ec8e2907a6..e21a08c268 100644
--- a/bgpd/bgp_evpn.c
+++ b/bgpd/bgp_evpn.c
@@ -666,10 +666,11 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,
for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom))
attr->ecommunity = ecommunity_merge(attr->ecommunity, ecom);
- /* Add the export RTs for L3VNI - currently only supported for IPV4 host
- * routes
+ /*
+ * only attach l3-vni export rts for ipv4 address family and if we are
+ * advertising both the labels in type-2 routes
*/
- if (afi == AFI_IP) {
+ if (afi == AFI_IP && CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) {
vrf_export_rtl = bgpevpn_get_vrf_export_rtl(vpn);
if (vrf_export_rtl && !list_isempty(vrf_export_rtl)) {
for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode,
@@ -690,7 +691,12 @@ static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,
ecommunity_merge(attr->ecommunity, &ecom_sticky);
}
- if (afi == AFI_IP && !is_zero_mac(&attr->rmac)) {
+ /*
+ * only attach l3-vni rmac for ipv4 address family and if we are
+ * advertising both the labels in type-2 routes
+ */
+ if (afi == AFI_IP && !is_zero_mac(&attr->rmac) &&
+ CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) {
memset(&ecom_rmac, 0, sizeof(ecom_rmac));
encode_rmac_extcomm(&eval_rmac, &attr->rmac);
ecom_rmac.size = 1;
@@ -1189,8 +1195,13 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
/* The VNI goes into the 'label' field of the route */
vni2label(vpn->vni, &label[0]);
- /* Type-2 routes may carry a second VNI - the L3-VNI */
- if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) {
+
+ /* Type-2 routes may carry a second VNI - the L3-VNI.
+ * Only attach second label if we are advertising two labels for
+ * type-2 routes.
+ */
+ if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE &&
+ CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) {
vni_t l3vni;
l3vni = bgpevpn_get_l3vni(vpn);
@@ -1209,6 +1220,24 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
&& !CHECK_FLAG(tmp_ri->flags, BGP_INFO_REMOVED))
route_change = 0;
else {
+ /*
+ * The attributes have changed, type-2 routes needs to
+ * be advertised with right labels.
+ */
+ vni2label(vpn->vni, &label[0]);
+ if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE &&
+ CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) {
+ vni_t l3vni;
+
+ l3vni = bgpevpn_get_l3vni(vpn);
+ if (l3vni) {
+ vni2label(l3vni, &label[1]);
+ num_labels++;
+ }
+ }
+ memcpy(&tmp_ri->extra->label, label, sizeof(label));
+ tmp_ri->extra->num_labels = num_labels;
+
/* The attribute has changed. */
/* Add (or update) attribute to hash. */
attr_new = bgp_attr_intern(attr);
@@ -4227,7 +4256,8 @@ static void link_l2vni_hash_to_l3vni(struct hash_backet *backet,
int bgp_evpn_local_l3vni_add(vni_t l3vni,
vrf_id_t vrf_id,
struct ethaddr *rmac,
- struct in_addr originator_ip)
+ struct in_addr originator_ip,
+ int filter)
{
struct bgp *bgp_vrf = NULL; /* bgp VRF instance */
struct bgp *bgp_def = NULL; /* default bgp instance */
@@ -4279,6 +4309,10 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni,
/* set the originator ip */
bgp_vrf->originator_ip = originator_ip;
+ /* set the right filter - are we using l3vni only for prefix routes? */
+ if (filter)
+ SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY);
+
/* auto derive RD/RT */
if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD))
evpn_auto_rt_import_add_for_vrf(bgp_vrf);
@@ -4292,9 +4326,12 @@ int bgp_evpn_local_l3vni_add(vni_t l3vni,
link_l2vni_hash_to_l3vni,
bgp_vrf);
- /* updates all corresponding local mac-ip routes */
- for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn))
- update_routes_for_vni(bgp_def, vpn);
+ /* Only update all corresponding type-2 routes if we are advertising two
+ * labels along with type-2 routes
+ */
+ if (!filter)
+ for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn))
+ update_routes_for_vni(bgp_def, vpn);
/* advertise type-5 routes if needed */
update_advertise_vrf_routes(bgp_vrf);
@@ -4353,9 +4390,12 @@ int bgp_evpn_local_l3vni_del(vni_t l3vni,
}
/* update all corresponding local mac-ip routes */
- for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn))
- update_routes_for_vni(bgp_def, vpn);
-
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY)) {
+ for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn)) {
+ UNSET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS);
+ update_routes_for_vni(bgp_def, vpn);
+ }
+ }
/* Delete the instance if it was autocreated */
if (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO))
diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h
index a8dcbc112b..d8d92618f6 100644
--- a/bgpd/bgp_evpn.h
+++ b/bgpd/bgp_evpn.h
@@ -91,7 +91,8 @@ extern int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni,
u_char flags);
extern int bgp_evpn_local_l3vni_add(vni_t vni, vrf_id_t vrf_id,
struct ethaddr *rmac,
- struct in_addr originator_ip);
+ struct in_addr originator_ip,
+ int filter);
extern int bgp_evpn_local_l3vni_del(vni_t vni, vrf_id_t vrf_id);
extern int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni);
extern int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h
index cc0ec82344..1dbc1f25f0 100644
--- a/bgpd/bgp_evpn_private.h
+++ b/bgpd/bgp_evpn_private.h
@@ -61,6 +61,8 @@ struct bgpevpn {
#define VNI_FLAG_RD_CFGD 0x4 /* RD is user configured. */
#define VNI_FLAG_IMPRT_CFGD 0x8 /* Import RT is user configured */
#define VNI_FLAG_EXPRT_CFGD 0x10 /* Export RT is user configured */
+#define VNI_FLAG_USE_TWO_LABELS 0x20 /* Attach both L2-VNI and L3-VNI if
+ needed for this VPN */
/* Flag to indicate if we are advertising the g/w mac ip for this VNI*/
u_int8_t advertise_gw_macip;
@@ -179,9 +181,13 @@ static inline void bgpevpn_unlink_from_l3vni(struct bgpevpn *vpn)
struct bgp *bgp_vrf = NULL;
bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
- if (!bgp_vrf || !bgp_vrf->l2vnis)
+ if (!bgp_vrf)
return;
- listnode_delete(bgp_vrf->l2vnis, vpn);
+
+ UNSET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS);
+
+ if (bgp_vrf->l2vnis)
+ listnode_delete(bgp_vrf->l2vnis, vpn);
}
static inline void bgpevpn_link_to_l3vni(struct bgpevpn *vpn)
@@ -189,9 +195,16 @@ static inline void bgpevpn_link_to_l3vni(struct bgpevpn *vpn)
struct bgp *bgp_vrf = NULL;
bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
- if (!bgp_vrf || !bgp_vrf->l2vnis)
+ if (!bgp_vrf)
return;
- listnode_add_sort(bgp_vrf->l2vnis, vpn);
+
+ /* check if we are advertising two labels for this vpn */
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY))
+ SET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS);
+
+ if (bgp_vrf->l2vnis)
+ listnode_add_sort(bgp_vrf->l2vnis, vpn);
}
static inline int is_vni_configured(struct bgpevpn *vpn)
diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c
index 1373afec4e..58c72bf36f 100644
--- a/bgpd/bgp_evpn_vty.c
+++ b/bgpd/bgp_evpn_vty.c
@@ -3941,6 +3941,10 @@ DEFUN (show_bgp_vrf_l3vni_info,
vty_out(vty, " L3-VNI: %u\n", bgp->l3vni);
vty_out(vty, " Rmac: %s\n",
prefix_mac2str(&bgp->rmac, buf, sizeof(buf)));
+ vty_out(vty, " VNI Filter: %s\n",
+ CHECK_FLAG(bgp->vrf_flags,
+ BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY) ?
+ "prefix-routes-only" : "none");
vty_out(vty, " L2-VNI List:\n");
vty_out(vty, " ");
for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn))
@@ -3966,6 +3970,10 @@ DEFUN (show_bgp_vrf_l3vni_info,
json_object_string_add(json, "rmac",
prefix_mac2str(&bgp->rmac, buf,
sizeof(buf)));
+ json_object_string_add(json, "vniFilter",
+ CHECK_FLAG(bgp->vrf_flags,
+ BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY)
+ ? "prefix-routes-only" : "none");
/* list of l2vnis */
for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn))
json_object_array_add(json_vnis,
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index de453de0c8..de11a98a20 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -289,7 +289,8 @@ void bgp_timer_set(struct peer *peer)
/* First entry point of peer's finite state machine. In Idle
status start timer is on unless peer is shutdown or peer is
inactive. All other timer must be turned off */
- if (BGP_PEER_START_SUPPRESSED(peer) || !peer_active(peer)) {
+ if (BGP_PEER_START_SUPPRESSED(peer) || !peer_active(peer)
+ || peer->bgp->vrf_id == VRF_UNKNOWN) {
BGP_TIMER_OFF(peer->t_start);
} else {
BGP_TIMER_ON(peer->t_start, bgp_start_timer,
@@ -1376,6 +1377,15 @@ int bgp_start(struct peer *peer)
return 0;
}
+ if (peer->bgp &&
+ peer->bgp->vrf_id == VRF_UNKNOWN) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_err(
+ "%s [FSM] In a VRF that is not initialised yet",
+ peer->host);
+ return -1;
+ }
+
/* Register to be notified on peer up */
if (peer->sort == BGP_PEER_EBGP && peer->ttl == 1
&& !CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)
diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c
index 0508f4846d..82c74e4afa 100644
--- a/bgpd/bgp_main.c
+++ b/bgpd/bgp_main.c
@@ -41,6 +41,7 @@
#include "vrf.h"
#include "bfd.h"
#include "libfrr.h"
+#include "ns.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_attr.h"
@@ -57,6 +58,7 @@
#include "bgpd/bgp_zebra.h"
#include "bgpd/bgp_packet.h"
#include "bgpd/bgp_keepalives.h"
+#include "bgpd/bgp_network.h"
#ifdef ENABLE_BGP_VNC
#include "bgpd/rfapi/rfapi_backend.h"
@@ -259,6 +261,7 @@ static int bgp_vrf_enable(struct vrf *vrf)
/* We have instance configured, link to VRF and make it "up". */
bgp_vrf_link(bgp, vrf);
+ bgp_handle_socket(bgp, vrf, old_vrf_id, true);
/* Update any redistribute vrf bitmaps if the vrf_id changed */
if (old_vrf_id != bgp->vrf_id)
bgp_update_redist_vrf_bitmaps(bgp, old_vrf_id);
@@ -282,6 +285,7 @@ static int bgp_vrf_disable(struct vrf *vrf)
bgp = bgp_lookup_by_name(vrf->name);
if (bgp) {
old_vrf_id = bgp->vrf_id;
+ bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, false);
/* We have instance configured, unlink from VRF and make it
* "down". */
bgp_vrf_unlink(bgp, vrf);
diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c
index 9d32c4bee1..5d83192a30 100644
--- a/bgpd/bgp_mpath.c
+++ b/bgpd/bgp_mpath.c
@@ -93,6 +93,26 @@ int bgp_maximum_paths_unset(struct bgp *bgp, afi_t afi, safi_t safi,
}
/*
+ * bgp_interface_same
+ *
+ * Return true if ifindex for ifp1 and ifp2 are the same, else return false.
+ */
+static int bgp_interface_same(struct interface *ifp1, struct interface *ifp2)
+{
+ if (!ifp1 && !ifp2)
+ return 1;
+
+ if (!ifp1 && ifp2)
+ return 0;
+
+ if (ifp1 && !ifp2)
+ return 0;
+
+ return (ifp1->ifindex == ifp2->ifindex);
+}
+
+
+/*
* bgp_info_nexthop_cmp
*
* Compare the nexthops of two paths. Return value is less than, equal to,
@@ -130,7 +150,8 @@ int bgp_info_nexthop_cmp(struct bgp_info *bi1, struct bgp_info *bi2)
if (!bi1->attr->mp_nexthop_prefer_global &&
!bi2->attr->mp_nexthop_prefer_global)
- compare = !(bi1->peer->ifindex == bi2->peer->ifindex);
+ compare = !bgp_interface_same(bi1->peer->ifp, bi2->peer->ifp);
+
if (!compare)
compare = IPV6_ADDR_CMP(&addr1, &addr2);
break;
diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c
index bf39cbe1fc..0ab583f444 100644
--- a/bgpd/bgp_network.c
+++ b/bgpd/bgp_network.c
@@ -34,6 +34,7 @@
#include "queue.h"
#include "hash.h"
#include "filter.h"
+#include "ns.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_open.h"
@@ -44,13 +45,14 @@
extern struct zebra_privs_t bgpd_privs;
-static int bgp_bind(struct peer *);
+static char *bgp_get_bound_name(struct peer *peer);
/* BGP listening socket. */
struct bgp_listener {
int fd;
union sockunion su;
struct thread *thread;
+ struct bgp *bgp;
};
/*
@@ -284,6 +286,7 @@ static int bgp_accept(struct thread *thread)
return -1;
}
listener->thread = NULL;
+
thread_add_read(bm->master, bgp_accept, listener, accept_sock,
&listener->thread);
@@ -296,8 +299,13 @@ static int bgp_accept(struct thread *thread)
}
set_nonblocking(bgp_sock);
- /* Obtain BGP instance this connection is meant for. */
- if (bgp_get_instance_for_inc_conn(bgp_sock, &bgp)) {
+ /* Obtain BGP instance this connection is meant for.
+ * - if it is a VRF netns sock, then BGP is in listener structure
+ * - otherwise, the bgp instance need to be demultiplexed
+ */
+ if (listener->bgp)
+ bgp = listener->bgp;
+ else if (bgp_get_instance_for_inc_conn(bgp_sock, &bgp)) {
if (bgp_debug_neighbor_events(NULL))
zlog_debug(
"[Event] Could not get instance for incoming conn from %s",
@@ -407,7 +415,7 @@ static int bgp_accept(struct thread *thread)
peer->doppelganger = peer1;
peer1->doppelganger = peer;
peer->fd = bgp_sock;
- bgp_bind(peer);
+ vrf_bind(peer->bgp->vrf_id, bgp_sock, bgp_get_bound_name(peer));
bgp_fsm_change_status(peer, Active);
BGP_TIMER_OFF(peer->t_start); /* created in peer_create() */
@@ -435,21 +443,20 @@ static int bgp_accept(struct thread *thread)
}
/* BGP socket bind. */
-static int bgp_bind(struct peer *peer)
+static char *bgp_get_bound_name(struct peer *peer)
{
-#ifdef SO_BINDTODEVICE
- int ret;
- int myerrno;
char *name = NULL;
- /* If not bound to an interface or part of a VRF, we don't care. */
- if (!peer->bgp->vrf_id && !peer->ifname && !peer->conf_if)
- return 0;
+ if ((peer->bgp->vrf_id == VRF_DEFAULT) &&
+ !peer->ifname && !peer->conf_if)
+ return NULL;
if (peer->su.sa.sa_family != AF_INET
&& peer->su.sa.sa_family != AF_INET6)
- return 0; // unexpected
+ return NULL; // unexpected
+ if (!peer)
+ return name;
/* For IPv6 peering, interface (unnumbered or link-local with interface)
* takes precedence over VRF. For IPv4 peering, explicit interface or
* VRF are the situations to bind.
@@ -461,30 +468,7 @@ static int bgp_bind(struct peer *peer)
else
name = peer->ifname ? peer->ifname : peer->bgp->name;
- if (!name)
- return 0;
-
- if (bgp_debug_neighbor_events(peer))
- zlog_debug("%s Binding to interface %s", peer->host, name);
-
- if (bgpd_privs.change(ZPRIVS_RAISE))
- zlog_err("bgp_bind: could not raise privs");
-
- ret = setsockopt(peer->fd, SOL_SOCKET, SO_BINDTODEVICE, name,
- strlen(name));
- myerrno = errno;
-
- if (bgpd_privs.change(ZPRIVS_LOWER))
- zlog_err("bgp_bind: could not lower privs");
-
- if (ret < 0) {
- if (bgp_debug_neighbor_events(peer))
- zlog_debug("bind to interface %s failed, errno=%d",
- name, myerrno);
- return ret;
- }
-#endif /* SO_BINDTODEVICE */
- return 0;
+ return name;
}
static int bgp_update_address(struct interface *ifp, const union sockunion *dst,
@@ -558,8 +542,13 @@ int bgp_connect(struct peer *peer)
zlog_debug("Peer address not learnt: Returning from connect");
return 0;
}
+ if (bgpd_privs.change(ZPRIVS_RAISE))
+ zlog_err("Can't raise privileges");
/* Make socket for the peer. */
- peer->fd = sockunion_socket(&peer->su);
+ peer->fd = vrf_sockunion_socket(&peer->su, peer->bgp->vrf_id,
+ bgp_get_bound_name(peer));
+ if (bgpd_privs.change(ZPRIVS_LOWER))
+ zlog_err("Can't lower privileges");
if (peer->fd < 0)
return -1;
@@ -591,9 +580,6 @@ int bgp_connect(struct peer *peer)
if (peer->password)
bgp_md5_set_connect(peer->fd, &peer->su, peer->password);
- /* Bind socket. */
- bgp_bind(peer);
-
/* Update source bind. */
if (bgp_update_source(peer) < 0) {
return connect_error;
@@ -642,12 +628,12 @@ int bgp_getsockname(struct peer *peer)
return -1;
#endif
}
-
return 0;
}
-static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen)
+static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen,
+ struct bgp *bgp)
{
struct bgp_listener *listener;
int ret, en;
@@ -683,8 +669,14 @@ static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen)
return ret;
}
- listener = XMALLOC(MTYPE_BGP_LISTENER, sizeof(*listener));
+ listener = XCALLOC(MTYPE_BGP_LISTENER, sizeof(*listener));
listener->fd = sock;
+
+ /* this socket needs a change of ns. record bgp back pointer */
+ if (bgp->vrf_id != VRF_DEFAULT &&
+ vrf_is_mapped_on_netns(bgp->vrf_id))
+ listener->bgp = bgp;
+
memcpy(&listener->su, sa, salen);
listener->thread = NULL;
thread_add_read(bm->master, bgp_accept, listener, sock,
@@ -695,7 +687,7 @@ static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen)
}
/* IPv6 supported version of BGP server socket setup. */
-int bgp_socket(unsigned short port, const char *address)
+int bgp_socket(struct bgp *bgp, unsigned short port, const char *address)
{
struct addrinfo *ainfo;
struct addrinfo *ainfo_save;
@@ -710,7 +702,12 @@ int bgp_socket(unsigned short port, const char *address)
snprintf(port_str, sizeof(port_str), "%d", port);
port_str[sizeof(port_str) - 1] = '\0';
- ret = getaddrinfo(address, port_str, &req, &ainfo_save);
+ if (bgpd_privs.change(ZPRIVS_RAISE))
+ zlog_err("Can't raise privileges");
+ ret = vrf_getaddrinfo(address, port_str, &req,
+ &ainfo_save, bgp->vrf_id);
+ if (bgpd_privs.change(ZPRIVS_LOWER))
+ zlog_err("Can't lower privileges");
if (ret != 0) {
zlog_err("getaddrinfo: %s", gai_strerror(ret));
return -1;
@@ -723,8 +720,13 @@ int bgp_socket(unsigned short port, const char *address)
if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6)
continue;
- sock = socket(ainfo->ai_family, ainfo->ai_socktype,
- ainfo->ai_protocol);
+ if (bgpd_privs.change(ZPRIVS_RAISE))
+ zlog_err("Can't raise privileges");
+ sock = vrf_socket(ainfo->ai_family, ainfo->ai_socktype,
+ ainfo->ai_protocol, bgp->vrf_id,
+ NULL);
+ if (bgpd_privs.change(ZPRIVS_LOWER))
+ zlog_err("Can't lower privileges");
if (sock < 0) {
zlog_err("socket: %s", safe_strerror(errno));
continue;
@@ -734,7 +736,8 @@ int bgp_socket(unsigned short port, const char *address)
* ttl=255 */
sockopt_ttl(ainfo->ai_family, sock, MAXTTL);
- ret = bgp_listener(sock, ainfo->ai_addr, ainfo->ai_addrlen);
+ ret = bgp_listener(sock, ainfo->ai_addr,
+ ainfo->ai_addrlen, bgp);
if (ret == 0)
++count;
else
@@ -751,6 +754,32 @@ int bgp_socket(unsigned short port, const char *address)
return 0;
}
+/* this function closes vrf socket
+ * this should be called only for vrf socket with netns backend
+ */
+void bgp_close_vrf_socket(struct bgp *bgp)
+{
+ struct listnode *node, *next;
+ struct bgp_listener *listener;
+
+ if (!bgp)
+ return;
+
+ if (bm->listen_sockets == NULL)
+ return;
+
+ for (ALL_LIST_ELEMENTS(bm->listen_sockets, node, next, listener)) {
+ if (listener->bgp == bgp) {
+ thread_cancel(listener->thread);
+ close(listener->fd);
+ listnode_delete(bm->listen_sockets, listener);
+ XFREE(MTYPE_BGP_LISTENER, listener);
+ }
+ }
+}
+
+/* this function closes main socket
+ */
void bgp_close(void)
{
struct listnode *node, *next;
@@ -760,6 +789,8 @@ void bgp_close(void)
return;
for (ALL_LIST_ELEMENTS(bm->listen_sockets, node, next, listener)) {
+ if (listener->bgp)
+ continue;
thread_cancel(listener->thread);
close(listener->fd);
listnode_delete(bm->listen_sockets, listener);
diff --git a/bgpd/bgp_network.h b/bgpd/bgp_network.h
index 75ff1305c2..f18484e000 100644
--- a/bgpd/bgp_network.h
+++ b/bgpd/bgp_network.h
@@ -23,7 +23,9 @@
#define BGP_SOCKET_SNDBUF_SIZE 65536
-extern int bgp_socket(unsigned short, const char *);
+extern int bgp_socket(struct bgp *bgp, unsigned short port,
+ const char *address);
+extern void bgp_close_vrf_socket(struct bgp *bgp);
extern void bgp_close(void);
extern int bgp_connect(struct peer *);
extern int bgp_getsockname(struct peer *);
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index 1dc08e2b00..1da6136428 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -1726,6 +1726,7 @@ static void bgp_zebra_connected(struct zclient *zclient)
static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient,
zebra_size_t length, vrf_id_t vrf_id)
{
+ int filter = 0;
char buf[ETHER_ADDR_STRLEN];
vni_t l3vni = 0;
struct ethaddr rmac;
@@ -1739,16 +1740,20 @@ static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient,
if (cmd == ZEBRA_L3VNI_ADD) {
stream_get(&rmac, s, sizeof(struct ethaddr));
originator_ip.s_addr = stream_get_ipv4(s);
+ stream_get(&filter, s, sizeof(int));
}
if (BGP_DEBUG(zebra, ZEBRA))
- zlog_debug("Rx L3-VNI %s VRF %s VNI %u RMAC %s",
+ zlog_debug("Rx L3-VNI %s VRF %s VNI %u RMAC %s filter %s",
(cmd == ZEBRA_L3VNI_ADD) ? "add" : "del",
- vrf_id_to_name(vrf_id), l3vni,
- prefix_mac2str(&rmac, buf, sizeof(buf)));
+ vrf_id_to_name(vrf_id),
+ l3vni,
+ prefix_mac2str(&rmac, buf, sizeof(buf)),
+ filter ? "prefix-routes-only" : "none");
if (cmd == ZEBRA_L3VNI_ADD)
- bgp_evpn_local_l3vni_add(l3vni, vrf_id, &rmac, originator_ip);
+ bgp_evpn_local_l3vni_add(l3vni, vrf_id, &rmac, originator_ip,
+ filter);
else
bgp_evpn_local_l3vni_del(l3vni, vrf_id);
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 78e748fb6c..63eff81145 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -101,6 +101,42 @@ static void bgp_if_finish(struct bgp *bgp);
extern struct zclient *zclient;
+/* handle main socket creation or deletion */
+static int bgp_check_main_socket(bool create, struct bgp *bgp)
+{
+ static int bgp_server_main_created;
+ struct listnode *bgpnode, *nbgpnode;
+ struct bgp *bgp_temp;
+
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+ return 0;
+ if (create == true) {
+ if (bgp_server_main_created)
+ return 0;
+ if (bgp_socket(bgp, bm->port, bm->address) < 0)
+ return BGP_ERR_INVALID_VALUE;
+ bgp_server_main_created = 1;
+ return 0;
+ }
+ if (!bgp_server_main_created)
+ return 0;
+ /* only delete socket on some cases */
+ for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp_temp)) {
+ /* do not count with current bgp */
+ if (bgp_temp == bgp)
+ continue;
+ /* if other instance non VRF, do not delete socket */
+ if (bgp_temp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
+ return 0;
+ /* vrf lite, do not delete socket */
+ if (!vrf_is_mapped_on_netns(bgp_temp->vrf_id))
+ return 0;
+ }
+ bgp_close();
+ bgp_server_main_created = 0;
+ return 0;
+}
+
void bgp_session_reset(struct peer *peer)
{
if (peer->doppelganger && (peer->doppelganger->status != Deleted)
@@ -1200,7 +1236,6 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src)
peer_dst->config = peer_src->config;
peer_dst->local_as = peer_src->local_as;
- peer_dst->ifindex = peer_src->ifindex;
peer_dst->port = peer_src->port;
(void)peer_sort(peer_dst);
peer_dst->rmap_type = peer_src->rmap_type;
@@ -2981,11 +3016,67 @@ struct bgp *bgp_lookup_by_vrf_id(vrf_id_t vrf_id)
return (vrf->info) ? (struct bgp *)vrf->info : NULL;
}
+/* handle socket creation or deletion, if necessary
+ * this is called for all new BGP instances
+ */
+int bgp_handle_socket(struct bgp *bgp, struct vrf *vrf,
+ vrf_id_t old_vrf_id, bool create)
+{
+ int ret = 0;
+
+ /* Create BGP server socket, if listen mode not disabled */
+ if (!bgp || bgp_option_check(BGP_OPT_NO_LISTEN))
+ return 0;
+ if (bgp->name
+ && bgp->inst_type == BGP_INSTANCE_TYPE_VRF
+ && vrf) {
+ /*
+ * suppress vrf socket
+ */
+ if (create == FALSE) {
+ if (vrf_is_mapped_on_netns(vrf->vrf_id))
+ bgp_close_vrf_socket(bgp);
+ else
+ ret = bgp_check_main_socket(create, bgp);
+ return ret;
+ }
+ /* do nothing
+ * if vrf_id did not change
+ */
+ if (vrf->vrf_id == old_vrf_id)
+ return 0;
+ if (old_vrf_id != VRF_UNKNOWN) {
+ /* look for old socket. close it. */
+ bgp_close_vrf_socket(bgp);
+ }
+ /* if backend is not yet identified ( VRF_UNKNOWN) then
+ * creation will be done later
+ */
+ if (vrf->vrf_id == VRF_UNKNOWN)
+ return 0;
+ /* if BGP VRF instance requested
+ * if backend is NETNS, create BGP server socket in the NETNS
+ */
+ if (vrf_is_mapped_on_netns(bgp->vrf_id)) {
+ ret = bgp_socket(bgp, bm->port, bm->address);
+ if (ret < 0)
+ return BGP_ERR_INVALID_VALUE;
+ return 0;
+ }
+ }
+ /* if BGP VRF instance requested or VRF lite backend
+ * if BGP non VRF instance, create it
+ * if not already done
+ */
+ return bgp_check_main_socket(create, bgp);
+}
+
/* Called from VTY commands. */
int bgp_get(struct bgp **bgp_val, as_t *as, const char *name,
enum bgp_instance_type inst_type)
{
struct bgp *bgp;
+ struct vrf *vrf = NULL;
/* Multiple instance check. */
if (bgp_option_check(BGP_OPT_MULTIPLE_INSTANCE)) {
@@ -3033,25 +3124,19 @@ int bgp_get(struct bgp **bgp_val, as_t *as, const char *name,
bgp->t_rmap_def_originate_eval = NULL;
- /* Create BGP server socket, if first instance. */
- if (list_isempty(bm->bgp) && !bgp_option_check(BGP_OPT_NO_LISTEN)) {
- if (bgp_socket(bm->port, bm->address) < 0)
- return BGP_ERR_INVALID_VALUE;
- }
-
- listnode_add(bm->bgp, bgp);
-
/* If Default instance or VRF, link to the VRF structure, if present. */
if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT
|| bgp->inst_type == BGP_INSTANCE_TYPE_VRF) {
- struct vrf *vrf;
-
vrf = bgp_vrf_lookup_by_instance_type(bgp);
if (vrf)
bgp_vrf_link(bgp, vrf);
}
+ /* BGP server socket already processed if BGP instance
+ * already part of the list
+ */
+ bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, true);
+ listnode_add(bm->bgp, bgp);
- /* Register with Zebra, if needed */
if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp))
bgp_zebra_instance_register(bgp);
@@ -3188,8 +3273,6 @@ int bgp_delete(struct bgp *bgp)
* routes to be processed still referencing the struct bgp.
*/
listnode_delete(bm->bgp, bgp);
- if (list_isempty(bm->bgp))
- bgp_close();
/* Deregister from Zebra, if needed */
if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp))
@@ -3199,6 +3282,7 @@ int bgp_delete(struct bgp *bgp)
bgp_if_finish(bgp);
vrf = bgp_vrf_lookup_by_instance_type(bgp);
+ bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, false);
if (vrf)
bgp_vrf_unlink(bgp, vrf);
@@ -3337,11 +3421,12 @@ struct peer *peer_lookup(struct bgp *bgp, union sockunion *su)
struct listnode *bgpnode, *nbgpnode;
for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp)) {
- /* Skip VRFs, this function will not be invoked without
- * an instance
+ /* Skip VRFs Lite only, this function will not be
+ * invoked without an instance
* when examining VRFs.
*/
- if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+ if ((bgp->inst_type == BGP_INSTANCE_TYPE_VRF) &&
+ !vrf_is_mapped_on_netns(bgp->vrf_id))
continue;
peer = hash_lookup(bgp->peerhash, &tmp_peer);
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 220b6d989e..b3c7418602 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -436,6 +436,7 @@ struct bgp {
#define BGP_VRF_IMPORT_RT_CFGD (1 << 3)
#define BGP_VRF_EXPORT_RT_CFGD (1 << 4)
#define BGP_VRF_RD_CFGD (1 << 5)
+#define BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY (1 << 6)
/* unique ID for auto derivation of RD for this vrf */
uint16_t vrf_rd_id;
@@ -686,7 +687,6 @@ struct peer {
time_t readtime; /* Last read time */
time_t resettime; /* Last reset time */
- ifindex_t ifindex; /* ifindex of the BGP connection. */
char *conf_if; /* neighbor interface config name. */
struct interface *ifp; /* corresponding interface */
char *ifname; /* bind interface name. */
@@ -1354,6 +1354,9 @@ extern void bgp_instance_up(struct bgp *);
extern void bgp_instance_down(struct bgp *);
extern int bgp_delete(struct bgp *);
+extern int bgp_handle_socket(struct bgp *bgp, struct vrf *vrf,
+ vrf_id_t old_vrf_id, bool create);
+
extern int bgp_flag_set(struct bgp *, int);
extern int bgp_flag_unset(struct bgp *, int);
extern int bgp_flag_check(struct bgp *, int);
diff --git a/doc/zebra.8.in b/doc/zebra.8.in
index 7f4a81b1a0..3e1d10e068 100644
--- a/doc/zebra.8.in
+++ b/doc/zebra.8.in
@@ -92,6 +92,11 @@ maximum before starting zebra.
Note that this affects Linux only.
.TP
+\fB\-n\fR, \fB\-\-vrfwnetns \fR\fIEnable namespace VRF\fR
+Enable namespace VRF backend. By default, the VRF backend relies on VRF-lite
+support from Linux devices. This option permits discovering using Linux named
+Netns and map it to FRR VRF contexts.
+.TP
\fB\-M\fR, \fB\-\-module \fR\fImodule:options\fR
Load a module at startup. May be specified more than once.
The \fBsnmp\fR and \fBfpm\fR modules may be
diff --git a/eigrpd/eigrp_zebra.c b/eigrpd/eigrp_zebra.c
index 3759c64148..e8392f50b4 100644
--- a/eigrpd/eigrp_zebra.c
+++ b/eigrpd/eigrp_zebra.c
@@ -94,13 +94,14 @@ static int eigrp_router_id_update_zebra(int command, struct zclient *zclient,
return 0;
}
-static int eigrp_zebra_notify_owner(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int eigrp_zebra_route_notify_owner(int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
{
struct prefix p;
enum zapi_route_notify_owner note;
+ uint32_t table;
- if (!zapi_route_notify_decode(zclient->ibuf, &p, &note))
+ if (!zapi_route_notify_decode(zclient->ibuf, &p, &table, &note))
return -1;
return 0;
@@ -128,7 +129,7 @@ void eigrp_zebra_init(void)
zclient->interface_address_delete = eigrp_interface_address_delete;
zclient->redistribute_route_add = eigrp_zebra_read_route;
zclient->redistribute_route_del = eigrp_zebra_read_route;
- zclient->notify_owner = eigrp_zebra_notify_owner;
+ zclient->route_notify_owner = eigrp_zebra_route_notify_owner;
}
diff --git a/include/linux/net_namespace.h b/include/linux/net_namespace.h
new file mode 100644
index 0000000000..9a92b7e14a
--- /dev/null
+++ b/include/linux/net_namespace.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2015 6WIND S.A.
+ * Author: Nicolas Dichtel <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_ */
diff --git a/include/subdir.am b/include/subdir.am
index 98bd148002..7a12b2ffae 100644
--- a/include/subdir.am
+++ b/include/subdir.am
@@ -6,4 +6,5 @@ noinst_HEADERS += \
include/linux/neighbour.h \
include/linux/rtnetlink.h \
include/linux/socket.h \
+ include/linux/net_namespace.h \
# end
diff --git a/ldpd/interface.c b/ldpd/interface.c
index bbcea9f553..b25be43a5c 100644
--- a/ldpd/interface.c
+++ b/ldpd/interface.c
@@ -306,8 +306,11 @@ if_reset(struct iface *iface, int af)
ia = iface_af_get(iface, af);
if_stop_hello_timer(ia);
- while ((adj = RB_ROOT(ia_adj_head, &ia->adj_tree)) != NULL)
+ while (!RB_EMPTY(ia_adj_head, &ia->adj_tree)) {
+ adj = RB_ROOT(ia_adj_head, &ia->adj_tree);
+
adj_del(adj, S_SHUTDOWN);
+ }
/* try to cleanup */
switch (af) {
diff --git a/ldpd/l2vpn.c b/ldpd/l2vpn.c
index f638d6a65b..1cfeae3092 100644
--- a/ldpd/l2vpn.c
+++ b/ldpd/l2vpn.c
@@ -76,16 +76,21 @@ l2vpn_del(struct l2vpn *l2vpn)
struct l2vpn_if *lif;
struct l2vpn_pw *pw;
- while ((lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree)) != NULL) {
+ while (!RB_EMPTY(l2vpn_if_head, &l2vpn->if_tree)) {
+ lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree);
+
RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
free(lif);
}
- while ((pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree)) != NULL) {
+ while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_tree)) {
+ pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree);
+
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
free(pw);
}
- while ((pw = RB_ROOT(l2vpn_pw_head,
- &l2vpn->pw_inactive_tree)) != NULL) {
+ while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_inactive_tree)) {
+ pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_inactive_tree);
+
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
free(pw);
}
diff --git a/ldpd/lde.c b/ldpd/lde.c
index a70b97d06b..5aa53fd39e 100644
--- a/ldpd/lde.c
+++ b/ldpd/lde.c
@@ -1324,8 +1324,11 @@ lde_nbr_clear(void)
{
struct lde_nbr *ln;
- while ((ln = RB_ROOT(nbr_tree, &lde_nbrs)) != NULL)
+ while (!RB_EMPTY(nbr_tree, &lde_nbrs)) {
+ ln = RB_ROOT(nbr_tree, &lde_nbrs);
+
lde_nbr_del(ln);
+ }
}
static void
diff --git a/ldpd/lde_lib.c b/ldpd/lde_lib.c
index 18c8c0a122..28e455c7a5 100644
--- a/ldpd/lde_lib.c
+++ b/ldpd/lde_lib.c
@@ -129,7 +129,9 @@ fec_clear(struct fec_tree *fh, void (*free_cb)(void *))
{
struct fec *f;
- while ((f = RB_ROOT(fec_tree, fh)) != NULL) {
+ while (!RB_EMPTY(fec_tree, fh)) {
+ f = RB_ROOT(fec_tree, fh);
+
fec_remove(fh, f);
free_cb(f);
}
diff --git a/ldpd/ldp_vty_conf.c b/ldpd/ldp_vty_conf.c
index 76c602afbb..382b006884 100644
--- a/ldpd/ldp_vty_conf.c
+++ b/ldpd/ldp_vty_conf.c
@@ -1475,18 +1475,23 @@ l2vpn_del_api(struct ldpd_conf *conf, struct l2vpn *l2vpn)
struct l2vpn_if *lif;
struct l2vpn_pw *pw;
- while ((lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree)) != NULL) {
+ while (!RB_EMPTY(l2vpn_if_head, &l2vpn->if_tree)) {
+ lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree);
+
QOBJ_UNREG(lif);
RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
free(lif);
}
- while ((pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree)) != NULL) {
+ while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_tree)) {
+ pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree);
+
QOBJ_UNREG(pw);
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
free(pw);
}
- while ((pw = RB_ROOT(l2vpn_pw_head,
- &l2vpn->pw_inactive_tree)) != NULL) {
+ while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_inactive_tree)) {
+ pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_inactive_tree);
+
QOBJ_UNREG(pw);
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
free(pw);
diff --git a/ldpd/ldpd.c b/ldpd/ldpd.c
index 12aeb1fff3..255febeb60 100644
--- a/ldpd/ldpd.c
+++ b/ldpd/ldpd.c
@@ -1066,13 +1066,17 @@ ldp_config_reset_main(struct ldpd_conf *conf)
struct iface *iface;
struct nbr_params *nbrp;
- while ((iface = RB_ROOT(iface_head, &conf->iface_tree)) != NULL) {
+ while (!RB_EMPTY(iface_head, &conf->iface_tree)) {
+ iface = RB_ROOT(iface_head, &conf->iface_tree);
+
QOBJ_UNREG(iface);
RB_REMOVE(iface_head, &conf->iface_tree, iface);
free(iface);
}
- while ((nbrp = RB_ROOT(nbrp_head, &conf->nbrp_tree)) != NULL) {
+ while (!RB_EMPTY(nbrp_head, &conf->nbrp_tree)) {
+ nbrp = RB_ROOT(nbrp_head, &conf->nbrp_tree);
+
QOBJ_UNREG(nbrp);
RB_REMOVE(nbrp_head, &conf->nbrp_tree, nbrp);
free(nbrp);
@@ -1128,20 +1132,25 @@ ldp_config_reset_l2vpns(struct ldpd_conf *conf)
struct l2vpn_if *lif;
struct l2vpn_pw *pw;
- while ((l2vpn = RB_ROOT(l2vpn_head, &conf->l2vpn_tree)) != NULL) {
- while ((lif = RB_ROOT(l2vpn_if_head,
- &l2vpn->if_tree)) != NULL) {
+ while (!RB_EMPTY(l2vpn_head, &conf->l2vpn_tree)) {
+ l2vpn = RB_ROOT(l2vpn_head, &conf->l2vpn_tree);
+ while (!RB_EMPTY(l2vpn_if_head, &l2vpn->if_tree)) {
+ lif = RB_ROOT(l2vpn_if_head, &l2vpn->if_tree);
+
QOBJ_UNREG(lif);
RB_REMOVE(l2vpn_if_head, &l2vpn->if_tree, lif);
free(lif);
}
- while ((pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree)) != NULL) {
+ while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_tree)) {
+ pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_tree);
+
QOBJ_UNREG(pw);
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_tree, pw);
free(pw);
}
- while ((pw = RB_ROOT(l2vpn_pw_head,
- &l2vpn->pw_inactive_tree)) != NULL) {
+ while (!RB_EMPTY(l2vpn_pw_head, &l2vpn->pw_inactive_tree)) {
+ pw = RB_ROOT(l2vpn_pw_head, &l2vpn->pw_inactive_tree);
+
QOBJ_UNREG(pw);
RB_REMOVE(l2vpn_pw_head, &l2vpn->pw_inactive_tree, pw);
free(pw);
@@ -1160,19 +1169,27 @@ ldp_clear_config(struct ldpd_conf *xconf)
struct nbr_params *nbrp;
struct l2vpn *l2vpn;
- while ((iface = RB_ROOT(iface_head, &xconf->iface_tree)) != NULL) {
+ while (!RB_EMPTY(iface_head, &xconf->iface_tree)) {
+ iface = RB_ROOT(iface_head, &xconf->iface_tree);
+
RB_REMOVE(iface_head, &xconf->iface_tree, iface);
free(iface);
}
- while ((tnbr = RB_ROOT(tnbr_head, &xconf->tnbr_tree)) != NULL) {
+ while (!RB_EMPTY(tnbr_head, &xconf->tnbr_tree)) {
+ tnbr = RB_ROOT(tnbr_head, &xconf->tnbr_tree);
+
RB_REMOVE(tnbr_head, &xconf->tnbr_tree, tnbr);
free(tnbr);
}
- while ((nbrp = RB_ROOT(nbrp_head, &xconf->nbrp_tree)) != NULL) {
+ while (!RB_EMPTY(nbrp_head, &xconf->nbrp_tree)) {
+ nbrp = RB_ROOT(nbrp_head, &xconf->nbrp_tree);
+
RB_REMOVE(nbrp_head, &xconf->nbrp_tree, nbrp);
free(nbrp);
}
- while ((l2vpn = RB_ROOT(l2vpn_head, &xconf->l2vpn_tree)) != NULL) {
+ while (!RB_EMPTY(l2vpn_head, &xconf->l2vpn_tree)) {
+ l2vpn = RB_ROOT(l2vpn_head, &xconf->l2vpn_tree);
+
RB_REMOVE(l2vpn_head, &xconf->l2vpn_tree, l2vpn);
l2vpn_del(l2vpn);
}
diff --git a/ldpd/ldpe.c b/ldpd/ldpe.c
index 9d00bcd2b6..56af76d94e 100644
--- a/ldpd/ldpe.c
+++ b/ldpd/ldpe.c
@@ -219,8 +219,11 @@ ldpe_shutdown(void)
assert(if_addr != LIST_FIRST(&global.addr_list));
free(if_addr);
}
- while ((adj = RB_ROOT(global_adj_head, &global.adj_tree)) != NULL)
+ while (!RB_EMPTY(global_adj_head, &global.adj_tree)) {
+ adj = RB_ROOT(global_adj_head, &global.adj_tree);
+
adj_del(adj, S_SHUTDOWN);
+ }
/* clean up */
if (iev_lde)
diff --git a/ldpd/neighbor.c b/ldpd/neighbor.c
index 39860a1859..1c3f650dff 100644
--- a/ldpd/neighbor.c
+++ b/ldpd/neighbor.c
@@ -316,7 +316,9 @@ nbr_del(struct nbr *nbr)
mapping_list_clr(&nbr->release_list);
mapping_list_clr(&nbr->abortreq_list);
- while ((adj = RB_ROOT(nbr_adj_head, &nbr->adj_tree)) != NULL) {
+ while (!RB_EMPTY(nbr_adj_head, &nbr->adj_tree)) {
+ adj = RB_ROOT(nbr_adj_head, &nbr->adj_tree);
+
adj->nbr = NULL;
RB_REMOVE(nbr_adj_head, &nbr->adj_tree, adj);
}
diff --git a/lib/command.c b/lib/command.c
index d17f2c3d48..10996d5dd4 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -62,7 +62,7 @@ const char *node_names[] = {
"aaa", // AAA_NODE,
"keychain", // KEYCHAIN_NODE,
"keychain key", // KEYCHAIN_KEY_NODE,
- "logical-router", // NS_NODE,
+ "logical-router", // LOGICALROUTER_NODE,
"vrf", // VRF_NODE,
"interface", // INTERFACE_NODE,
"zebra", // ZEBRA_NODE,
@@ -1291,7 +1291,7 @@ void cmd_exit(struct vty *vty)
break;
case INTERFACE_NODE:
case PW_NODE:
- case NS_NODE:
+ case LOGICALROUTER_NODE:
case VRF_NODE:
case ZEBRA_NODE:
case BGP_NODE:
@@ -1376,7 +1376,7 @@ DEFUN (config_end,
case CONFIG_NODE:
case INTERFACE_NODE:
case PW_NODE:
- case NS_NODE:
+ case LOGICALROUTER_NODE:
case VRF_NODE:
case ZEBRA_NODE:
case RIP_NODE:
diff --git a/lib/command.h b/lib/command.h
index e1edc1ef32..269318989f 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -85,7 +85,7 @@ enum node_type {
AAA_NODE, /* AAA node. */
KEYCHAIN_NODE, /* Key-chain node. */
KEYCHAIN_KEY_NODE, /* Key-chain key node. */
- NS_NODE, /* Logical-Router node. */
+ LOGICALROUTER_NODE, /* Logical-Router node. */
VRF_NODE, /* VRF mode node. */
INTERFACE_NODE, /* Interface mode node. */
ZEBRA_NODE, /* zebra connection node. */
diff --git a/lib/if.c b/lib/if.c
index 7866ddb8c4..3a83de46ae 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -384,29 +384,35 @@ struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id, int vty)
{
struct interface *ifp;
+ ifp = if_lookup_by_name(name, vrf_id);
+ if (ifp)
+ return ifp;
+ /* Not Found on same VRF. If the interface command
+ * was entered in vty without a VRF (passed as VRF_DEFAULT),
+ * accept the ifp we found. If a vrf was entered and there is
+ * a mismatch, reject it if from vty.
+ */
ifp = if_lookup_by_name_all_vrf(name);
- if (ifp) {
- if (ifp->vrf_id == vrf_id)
+ if (!ifp)
+ return if_create(name, vrf_id);
+ if (vty) {
+ if (vrf_id == VRF_DEFAULT)
return ifp;
-
- /* Found a match on a different VRF. If the interface command
- * was entered in vty without a VRF (passed as VRF_DEFAULT),
- * accept the ifp we found. If a vrf was entered and there is
- * a mismatch, reject it if from vty. If it came from the kernel
- * or by way of zclient, believe it and update the ifp
- * accordingly.
- */
- if (vty) {
- if (vrf_id == VRF_DEFAULT)
- return ifp;
- return NULL;
- } else {
- if_update_to_new_vrf(ifp, vrf_id);
- return ifp;
- }
+ return NULL;
}
-
- return if_create(name, vrf_id);
+ /* if vrf backend uses NETNS, then
+ * this should not be considered as an update
+ * then create the new interface
+ */
+ if (ifp->vrf_id != vrf_id &&
+ vrf_is_mapped_on_netns(vrf_id))
+ return if_create(name, vrf_id);
+ /* If it came from the kernel
+ * or by way of zclient, believe it and update
+ * the ifp accordingly.
+ */
+ if_update_to_new_vrf(ifp, vrf_id);
+ return ifp;
}
void if_set_index(struct interface *ifp, ifindex_t ifindex)
@@ -1064,7 +1070,7 @@ ifaddr_ipv4_lookup (struct in_addr *addr, ifindex_t ifindex)
rn = route_node_lookup (ifaddr_ipv4_table, (struct prefix *) &p);
if (! rn)
return NULL;
-
+
ifp = rn->info;
route_unlock_node (rn);
return ifp;
@@ -1078,7 +1084,9 @@ void if_terminate(struct vrf *vrf)
{
struct interface *ifp;
- while ((ifp = RB_ROOT(if_name_head, &vrf->ifaces_by_name)) != NULL) {
+ while (!RB_EMPTY(if_name_head, &vrf->ifaces_by_name)) {
+ ifp = RB_ROOT(if_name_head, &vrf->ifaces_by_name);
+
if (ifp->node) {
ifp->node->info = NULL;
route_unlock_node(ifp->node);
diff --git a/lib/logicalrouter.c b/lib/logicalrouter.c
new file mode 100644
index 0000000000..4dc99d304f
--- /dev/null
+++ b/lib/logicalrouter.c
@@ -0,0 +1,159 @@
+/*
+ * Logical Router functions.
+ * Copyright (C) 2018 6WIND S.A.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <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;
+}
diff --git a/lib/logicalrouter.h b/lib/logicalrouter.h
new file mode 100644
index 0000000000..5a0780c009
--- /dev/null
+++ b/lib/logicalrouter.h
@@ -0,0 +1,41 @@
+/*
+ * Logical Router related header.
+ * Copyright (C) 2018 6WIND S.A.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _ZEBRA_LOGICAL_ROUTER_H
+#define _ZEBRA_LOGICAL_ROUTER_H
+
+/* Logical Router Backend defines */
+#define LOGICALROUTER_BACKEND_OFF 0
+#define LOGICALROUTER_BACKEND_NETNS 1
+
+/*
+ * Logical Router initializer/destructor
+ */
+extern void logicalrouter_init(int (*writefunc)(struct vty *vty));
+extern void logicalrouter_terminate(void);
+
+/* used to configure backend for logical router
+ * Currently, the whole NETNS feature is exclusively shared
+ * between logical router and VRF backend NETNS
+ * However, when logical router feature will be available,
+ * one can think of having exclusivity only per NETNS
+ */
+extern void logicalrouter_configure_backend(int backend_netns);
+
+#endif /*_ZEBRA_LOGICAL_ROUTER_H*/
diff --git a/lib/netns_linux.c b/lib/netns_linux.c
new file mode 100644
index 0000000000..0e955bade9
--- /dev/null
+++ b/lib/netns_linux.c
@@ -0,0 +1,539 @@
+/*
+ * NS functions.
+ * Copyright (C) 2014 6WIND S.A.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <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;
+}
+
diff --git a/lib/netns_other.c b/lib/netns_other.c
new file mode 100644
index 0000000000..2402dd17d6
--- /dev/null
+++ b/lib/netns_other.c
@@ -0,0 +1,165 @@
+/*
+ * NetNS backend for non Linux systems
+ * Copyright (C) 2018 6WIND S.A.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#if !defined(GNU_LINUX) && (defined(SUNOS_5) || defined(OPEN_BSD))
+/* SUNOS_5 or OPEN_BSD */
+
+#include <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 */
diff --git a/lib/ns.c b/lib/ns.c
deleted file mode 100644
index fdf93d0742..0000000000
--- a/lib/ns.c
+++ /dev/null
@@ -1,453 +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 ((ns = RB_ROOT(ns_head, &ns_tree)) != NULL)
- ns_delete(ns);
-}
-
-/* Create a socket for the NS. */
-int ns_socket(int domain, int type, int protocol, ns_id_t ns_id)
-{
- struct ns *ns = ns_lookup(ns_id);
- int ret = -1;
-
- if (!ns_is_enabled(ns)) {
- errno = ENOSYS;
- return -1;
- }
-
- if (have_netns()) {
- ret = (ns_id != NS_DEFAULT) ? setns(ns->fd, CLONE_NEWNET) : 0;
- if (ret >= 0) {
- ret = socket(domain, type, protocol);
- if (ns_id != NS_DEFAULT)
- setns(ns_lookup(NS_DEFAULT)->fd, CLONE_NEWNET);
- }
- } else
- ret = socket(domain, type, protocol);
-
- return ret;
-}
diff --git a/lib/ns.h b/lib/ns.h
index 79b4cab04d..83e5e1b907 100644
--- a/lib/ns.h
+++ b/lib/ns.h
@@ -24,16 +24,22 @@
#include "openbsd-tree.h"
#include "linklist.h"
+#include "vty.h"
typedef u_int32_t ns_id_t;
/* the default NS ID */
-#define NS_DEFAULT 0
#define NS_UNKNOWN UINT32_MAX
/* Default netns directory (Linux) */
#define NS_RUN_DIR "/var/run/netns"
+#ifdef HAVE_NETNS
+#define NS_DEFAULT_NAME "/proc/self/ns/net"
+#else /* !HAVE_NETNS */
+#define NS_DEFAULT_NAME "Default-logical-router"
+#endif /* HAVE_NETNS */
+
struct ns {
RB_ENTRY(ns) entry;
@@ -49,6 +55,9 @@ struct ns {
/* Master list of interfaces belonging to this NS */
struct list *iflist;
+ /* Back Pointer to VRF */
+ void *vrf_ctxt;
+
/* User data */
void *info;
};
@@ -58,6 +67,11 @@ RB_PROTOTYPE(ns_head, ns, entry, ns_compare)
extern struct ns_head ns_tree;
/*
+ * API for managing NETNS. eg from zebra daemon
+ * one want to manage the list of NETNS, etc...
+ */
+
+/*
* NS hooks
*/
@@ -74,20 +88,82 @@ extern struct ns_head ns_tree;
* - param 2: the address of the user data pointer (the user data
* can be stored in or freed from there)
*/
-extern void ns_add_hook(int, int (*)(ns_id_t, void **));
+extern void ns_add_hook(int type, int (*)(struct ns *));
+
/*
* NS initializer/destructor
*/
-/* Please add hooks before calling ns_init(). */
-extern void ns_init(void);
+
extern void ns_terminate(void);
+/* API to initialize NETNS managerment
+ * parameter is the default ns_id
+ */
+extern void ns_init_management(ns_id_t ns_id);
+
+
/*
* NS utilities
*/
-/* Create a socket serving for the given NS */
-extern int ns_socket(int, int, int, ns_id_t);
+/* Create a socket serving for the given NS
+ */
+int ns_socket(int domain, int type, int protocol, ns_id_t ns_id);
+
+/* return the path of the NETNS */
+extern char *ns_netns_pathname(struct vty *vty, const char *name);
+
+/* Parse and execute a function on all the NETNS */
+extern void ns_walk_func(int (*func)(struct ns *));
+
+/* API to get the NETNS name, from the ns pointer */
+extern const char *ns_get_name(struct ns *ns);
+
+/* only called from vrf ( when removing netns from vrf)
+ * or at VRF or logical router termination
+ */
+extern void ns_delete(struct ns *ns);
+
+/* return > 0 if netns is available
+ * called by VRF to check netns backend is available for VRF
+ */
+extern int ns_have_netns(void);
+
+/* API to get context information of a NS */
+extern void *ns_info_lookup(ns_id_t ns_id);
+
+/*
+ * NS init routine
+ * should be called from backendx
+ */
+extern void ns_init(void);
+
+/* API to retrieve default NS */
+extern ns_id_t ns_get_default_id(void);
+
+#define NS_DEFAULT ns_get_default_id()
+
+/* API that can be used to change from NS */
+extern int ns_switchback_to_initial(void);
+extern int ns_switch_to_netns(const char *netns_name);
+
+/*
+ * NS handling routines.
+ * called by modules that use NS backend
+ */
+
+/* API to search for already present NETNS */
+extern struct ns *ns_lookup(ns_id_t ns_id);
+extern struct ns *ns_lookup_name(const char *name);
+
+/* API to handle NS : creation, enable, disable
+ * for enable, a callback function is passed as parameter
+ * the callback belongs to the module that uses NS as backend
+ * upon enabling the NETNS, the upper layer is informed
+ */
+extern int ns_enable(struct ns *ns, void (*func)(ns_id_t, void *));
+extern struct ns *ns_get_created(struct ns *ns, char *name, ns_id_t ns_id);
+extern void ns_disable(struct ns *ns);
#endif /*_ZEBRA_NS_H*/
diff --git a/lib/subdir.am b/lib/subdir.am
index 44870917bd..e292d7a342 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -42,7 +42,8 @@ lib_libfrr_la_SOURCES = \
lib/module.c \
lib/network.c \
lib/nexthop.c \
- lib/ns.c \
+ lib/netns_linux.c \
+ lib/netns_other.c \
lib/openbsd-tree.c \
lib/pid_output.c \
lib/plist.c \
@@ -74,6 +75,7 @@ lib_libfrr_la_SOURCES = \
lib/wheel.c \
lib/workqueue.c \
lib/zclient.c \
+ lib/logicalrouter.c \
# end
lib/plist_clippy.c: $(CLIPPY_DEPS)
@@ -158,6 +160,7 @@ pkginclude_HEADERS += \
lib/zassert.h \
lib/zclient.h \
lib/zebra.h \
+ lib/logicalrouter.h \
# end
nodist_pkginclude_HEADERS += \
diff --git a/lib/vrf.c b/lib/vrf.c
index 2e15fa2f5c..ea106b90a2 100644
--- a/lib/vrf.c
+++ b/lib/vrf.c
@@ -21,6 +21,9 @@
#include <zebra.h>
+/* for basename */
+#include <libgen.h>
+
#include "if.h"
#include "vrf.h"
#include "vrf_int.h"
@@ -29,6 +32,10 @@
#include "log.h"
#include "memory.h"
#include "command.h"
+#include "ns.h"
+
+/* default VRF ID value used when VRF backend is not NETNS */
+#define VRF_DEFAULT_INTERNAL 0
DEFINE_MTYPE_STATIC(LIB, VRF, "VRF")
DEFINE_MTYPE_STATIC(LIB, VRF_BITMAP, "VRF bit-map")
@@ -44,6 +51,8 @@ RB_GENERATE(vrf_name_head, vrf, name_entry, vrf_name_compare);
struct vrf_id_head vrfs_by_id = RB_INITIALIZER(&vrfs_by_id);
struct vrf_name_head vrfs_by_name = RB_INITIALIZER(&vrfs_by_name);
+static int vrf_backend;
+
/*
* Turn on/off debug code
* for vrf.
@@ -61,7 +70,6 @@ struct vrf_master {
};
static int vrf_is_enabled(struct vrf *vrf);
-static void vrf_disable(struct vrf *vrf);
/* VRF list existance check by name. */
struct vrf *vrf_lookup_by_name(const char *name)
@@ -81,6 +89,54 @@ static int vrf_name_compare(const struct vrf *a, const struct vrf *b)
return strcmp(a->name, b->name);
}
+/* if ns_id is different and not VRF_UNKNOWN,
+ * then update vrf identifier, and enable VRF
+ */
+static void vrf_update_vrf_id(ns_id_t ns_id, void *opaqueptr)
+{
+ ns_id_t vrf_id = (vrf_id_t)ns_id;
+ vrf_id_t old_vrf_id;
+ struct vrf *vrf = (struct vrf *)opaqueptr;
+
+ if (!vrf)
+ return;
+ old_vrf_id = vrf->vrf_id;
+ if (vrf_id == vrf->vrf_id)
+ return;
+ if (vrf->vrf_id != VRF_UNKNOWN)
+ RB_REMOVE(vrf_id_head, &vrfs_by_id, vrf);
+ vrf->vrf_id = vrf_id;
+ RB_INSERT(vrf_id_head, &vrfs_by_id, vrf);
+ if (old_vrf_id == VRF_UNKNOWN)
+ vrf_enable((struct vrf *)vrf);
+}
+
+int vrf_switch_to_netns(vrf_id_t vrf_id)
+{
+ char *name;
+ struct vrf *vrf = vrf_lookup_by_id(vrf_id);
+
+ /* VRF is default VRF. silently ignore */
+ if (!vrf || vrf->vrf_id == VRF_DEFAULT)
+ return 0;
+ /* VRF has no NETNS backend. silently ignore */
+ if (vrf->data.l.netns_name[0] == '\0')
+ return 0;
+ name = ns_netns_pathname(NULL, vrf->data.l.netns_name);
+ if (debug_vrf)
+ zlog_debug("VRF_SWITCH: %s(%u)", name, vrf->vrf_id);
+ return ns_switch_to_netns(name);
+}
+
+int vrf_switchback_to_initial(void)
+{
+ int ret = ns_switchback_to_initial();
+
+ if (ret == 0 && debug_vrf)
+ zlog_debug("VRF_SWITCHBACK");
+ return ret;
+}
+
/* Get a VRF. If not found, create one.
* Arg:
* name - The name of the vrf. May be NULL if unknown.
@@ -94,7 +150,8 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name)
int new = 0;
if (debug_vrf)
- zlog_debug("VRF_GET: %s(%u)", name, vrf_id);
+ zlog_debug("VRF_GET: %s(%u)",
+ name == NULL ? "(NULL)" : name, vrf_id);
/* Nothing to see, move along here */
if (!name && vrf_id == VRF_UNKNOWN)
@@ -109,8 +166,6 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name)
if (vrf == NULL) {
vrf = XCALLOC(MTYPE_VRF, sizeof(struct vrf));
vrf->vrf_id = VRF_UNKNOWN;
- RB_INIT(if_name_head, &vrf->ifaces_by_name);
- RB_INIT(if_index_head, &vrf->ifaces_by_index);
QOBJ_REG(vrf, vrf);
new = 1;
@@ -134,7 +189,6 @@ struct vrf *vrf_get(vrf_id_t vrf_id, const char *name)
strlcpy(vrf->name, name, sizeof(vrf->name));
RB_INSERT(vrf_name_head, &vrfs_by_name, vrf);
}
-
if (new &&vrf_master.vrf_new_hook)
(*vrf_master.vrf_new_hook)(vrf);
@@ -219,7 +273,7 @@ int vrf_enable(struct vrf *vrf)
* The VRF_DELETE_HOOK callback will be called to inform
* that they must release the resources in the VRF.
*/
-static void vrf_disable(struct vrf *vrf)
+void vrf_disable(struct vrf *vrf)
{
if (!vrf_is_enabled(vrf))
return;
@@ -385,6 +439,8 @@ void vrf_init(int (*create)(struct vrf *), int (*enable)(struct vrf *),
{
struct vrf *default_vrf;
+ /* initialise NS, in case VRF backend if NETNS */
+ ns_init();
if (debug_vrf)
zlog_debug("%s: Initializing VRF subsystem",
__PRETTY_FUNCTION__);
@@ -419,12 +475,17 @@ void vrf_terminate(void)
zlog_debug("%s: Shutting down vrf subsystem",
__PRETTY_FUNCTION__);
- while ((vrf = RB_ROOT(vrf_id_head, &vrfs_by_id)) != NULL) {
+ while (!RB_EMPTY(vrf_id_head, &vrfs_by_id)) {
+ vrf = RB_ROOT(vrf_id_head, &vrfs_by_id);
+
/* Clear configured flag and invoke delete. */
UNSET_FLAG(vrf->status, VRF_CONFIGURED);
vrf_delete(vrf);
}
- while ((vrf = RB_ROOT(vrf_name_head, &vrfs_by_name)) != NULL) {
+
+ while (!RB_EMPTY(vrf_name_head, &vrfs_by_name)) {
+ vrf = RB_ROOT(vrf_name_head, &vrfs_by_name);
+
/* Clear configured flag and invoke delete. */
UNSET_FLAG(vrf->status, VRF_CONFIGURED);
vrf_delete(vrf);
@@ -432,41 +493,163 @@ void vrf_terminate(void)
}
/* Create a socket for the VRF. */
-int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id)
+int vrf_socket(int domain, int type, int protocol, vrf_id_t vrf_id,
+ char *interfacename)
{
- int ret = -1;
+ int ret, save_errno, ret2;
+ ret = vrf_switch_to_netns(vrf_id);
+ if (ret < 0)
+ zlog_err("%s: Can't switch to VRF %u (%s)",
+ __func__, vrf_id, safe_strerror(errno));
ret = socket(domain, type, protocol);
-
+ save_errno = errno;
+ ret2 = vrf_switchback_to_initial();
+ if (ret2 < 0)
+ zlog_err("%s: Can't switchback from VRF %u (%s)",
+ __func__, vrf_id, safe_strerror(errno));
+ errno = save_errno;
+ if (ret <= 0)
+ return ret;
+ ret2 = vrf_bind(vrf_id, ret, interfacename);
+ if (ret2 < 0) {
+ close(ret);
+ ret = ret2;
+ }
return ret;
}
-/* vrf CLI commands */
-DEFUN_NOSH (vrf,
- vrf_cmd,
- "vrf NAME",
- "Select a VRF to configure\n"
- "VRF's name\n")
+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)
{
- int idx_name = 1;
- const char *vrfname = argv[idx_name]->arg;
struct vrf *vrfp;
if (strlen(vrfname) > VRF_NAMSIZ) {
- vty_out(vty,
- "%% VRF name %s is invalid: length exceeds "
- "%d characters\n",
- vrfname, VRF_NAMSIZ);
+ if (vty)
+ vty_out(vty,
+ "%% VRF name %s invalid: length exceeds %d bytes\n",
+ vrfname, VRF_NAMSIZ);
+ else
+ zlog_warn(
+ "%% VRF name %s invalid: length exceeds %d bytes\n",
+ vrfname, VRF_NAMSIZ);
return CMD_WARNING_CONFIG_FAILED;
}
vrfp = vrf_get(VRF_UNKNOWN, vrfname);
- VTY_PUSH_CONTEXT(VRF_NODE, vrfp);
+ if (vty)
+ VTY_PUSH_CONTEXT(VRF_NODE, vrfp);
+
+ if (vrf)
+ *vrf = vrfp;
+ return CMD_SUCCESS;
+}
+
+int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf,
+ char *pathname, ns_id_t ns_id)
+{
+ struct ns *ns = NULL;
+
+ if (!vrf)
+ return CMD_WARNING_CONFIG_FAILED;
+ if (vrf->vrf_id != VRF_UNKNOWN && vrf->ns_ctxt == NULL) {
+ if (vty)
+ vty_out(vty,
+ "VRF %u is already configured with VRF %s\n",
+ vrf->vrf_id, vrf->name);
+ else
+ zlog_warn("VRF %u is already configured with VRF %s\n",
+ vrf->vrf_id, vrf->name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (vrf->ns_ctxt != NULL) {
+ ns = (struct ns *) vrf->ns_ctxt;
+ if (ns && 0 != strcmp(ns->name, pathname)) {
+ if (vty)
+ vty_out(vty,
+ "VRF %u already configured with NETNS %s\n",
+ vrf->vrf_id, ns->name);
+ else
+ zlog_warn(
+ "VRF %u already configured with NETNS %s",
+ vrf->vrf_id, ns->name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+ ns = ns_lookup_name(pathname);
+ if (ns && ns->vrf_ctxt) {
+ struct vrf *vrf2 = (struct vrf *)ns->vrf_ctxt;
+
+ if (vrf2 == vrf)
+ return CMD_SUCCESS;
+ if (vty)
+ vty_out(vty, "NS %s is already configured"
+ " with VRF %u(%s)\n",
+ ns->name, vrf2->vrf_id, vrf2->name);
+ else
+ zlog_warn("NS %s is already configured with VRF %u(%s)",
+ ns->name, vrf2->vrf_id, vrf2->name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ ns = ns_get_created(ns, pathname, ns_id);
+ ns->vrf_ctxt = (void *)vrf;
+ vrf->ns_ctxt = (void *)ns;
+ /* update VRF netns NAME */
+ if (vrf)
+ strlcpy(vrf->data.l.netns_name, basename(pathname), NS_NAMSIZ);
+
+ if (!ns_enable(ns, vrf_update_vrf_id)) {
+ if (vty)
+ vty_out(vty, "Can not associate NS %u with NETNS %s\n",
+ ns->ns_id, ns->name);
+ else
+ zlog_warn("Can not associate NS %u with NETNS %s",
+ ns->ns_id, ns->name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
return CMD_SUCCESS;
}
+int vrf_is_mapped_on_netns(vrf_id_t vrf_id)
+{
+ struct vrf *vrf = vrf_lookup_by_id(vrf_id);
+
+ if (!vrf || vrf->data.l.netns_name[0] == '\0')
+ return 0;
+ if (vrf->vrf_id == VRF_DEFAULT)
+ return 0;
+ return 1;
+}
+
+/* vrf CLI commands */
+DEFUN_NOSH (vrf,
+ vrf_cmd,
+ "vrf NAME",
+ "Select a VRF to configure\n"
+ "VRF's name\n")
+{
+ int idx_name = 1;
+ const char *vrfname = argv[idx_name]->arg;
+
+ return vrf_handler_create(vty, vrfname, NULL);
+}
+
DEFUN_NOSH (no_vrf,
no_vrf_cmd,
"no vrf NAME",
@@ -500,6 +683,55 @@ DEFUN_NOSH (no_vrf,
struct cmd_node vrf_node = {VRF_NODE, "%s(config-vrf)# ", 1};
+DEFUN_NOSH (vrf_netns,
+ vrf_netns_cmd,
+ "netns NAME",
+ "Attach VRF to a Namespace\n"
+ "The file name in " NS_RUN_DIR ", or a full pathname\n")
+{
+ int idx_name = 1;
+ char *pathname = ns_netns_pathname(vty, argv[idx_name]->arg);
+
+ VTY_DECLVAR_CONTEXT(vrf, vrf);
+
+ if (!pathname)
+ return CMD_WARNING_CONFIG_FAILED;
+ return vrf_netns_handler_create(vty, vrf, pathname, NS_UNKNOWN);
+}
+
+DEFUN (no_vrf_netns,
+ no_vrf_netns_cmd,
+ "no netns [NAME]",
+ NO_STR
+ "Detach VRF from a Namespace\n"
+ "The file name in " NS_RUN_DIR ", or a full pathname\n")
+{
+ struct ns *ns = NULL;
+
+ VTY_DECLVAR_CONTEXT(vrf, vrf);
+
+ if (!vrf_is_backend_netns()) {
+ vty_out(vty, "VRF backend is not Netns. Aborting\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (!vrf->ns_ctxt) {
+ vty_out(vty, "VRF %s(%u) is not configured with NetNS\n",
+ vrf->name, vrf->vrf_id);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ ns = (struct ns *)vrf->ns_ctxt;
+
+ ns->vrf_ctxt = NULL;
+ vrf_disable(vrf);
+ /* vrf ID from VRF is necessary for Zebra
+ * so that propagate to other clients is done
+ */
+ ns_delete(ns);
+ vrf->ns_ctxt = NULL;
+ return CMD_SUCCESS;
+}
+
/*
* Debug CLI for vrf's
*/
@@ -552,4 +784,105 @@ void vrf_cmd_init(int (*writefunc)(struct vty *vty))
install_element(CONFIG_NODE, &no_vrf_cmd);
install_node(&vrf_node, writefunc);
install_default(VRF_NODE);
+ if (vrf_is_backend_netns() && ns_have_netns()) {
+ /* Install NS commands. */
+ install_element(VRF_NODE, &vrf_netns_cmd);
+ install_element(VRF_NODE, &no_vrf_netns_cmd);
+ }
+}
+
+vrf_id_t vrf_get_default_id(void)
+{
+ struct vrf *vrf = vrf_lookup_by_name(VRF_DEFAULT_NAME);
+
+ if (vrf)
+ return vrf->vrf_id;
+ if (vrf_is_backend_netns())
+ return ns_get_default_id();
+ else
+ return VRF_DEFAULT_INTERNAL;
+}
+
+int vrf_bind(vrf_id_t vrf_id, int fd, char *name)
+{
+ int ret = 0;
+
+ if (fd < 0 || name == NULL)
+ return fd;
+ if (vrf_is_mapped_on_netns(vrf_id))
+ return fd;
+#ifdef SO_BINDTODEVICE
+ ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name,
+ strlen(name));
+ if (ret < 0)
+ zlog_debug("bind to interface %s failed, errno=%d",
+ name, errno);
+#endif /* SO_BINDTODEVICE */
+ return ret;
+}
+int vrf_getaddrinfo(const char *node, const char *service,
+ const struct addrinfo *hints,
+ struct addrinfo **res, vrf_id_t vrf_id)
+{
+ int ret, ret2, save_errno;
+
+ ret = vrf_switch_to_netns(vrf_id);
+ if (ret < 0)
+ zlog_err("%s: Can't switch to VRF %u (%s)",
+ __func__, vrf_id, safe_strerror(errno));
+ ret = getaddrinfo(node, service, hints, res);
+ save_errno = errno;
+ ret2 = vrf_switchback_to_initial();
+ if (ret2 < 0)
+ zlog_err("%s: Can't switchback from VRF %u (%s)",
+ __func__, vrf_id, safe_strerror(errno));
+ errno = save_errno;
+ return ret;
+}
+
+int vrf_ioctl(vrf_id_t vrf_id, int d, unsigned long request, char *params)
+{
+ int ret, saved_errno, rc;
+
+ ret = vrf_switch_to_netns(vrf_id);
+ if (ret < 0) {
+ zlog_err("%s: Can't switch to VRF %u (%s)",
+ __func__, vrf_id, safe_strerror(errno));
+ return 0;
+ }
+ rc = ioctl(d, request, params);
+ saved_errno = errno;
+ ret = vrf_switchback_to_initial();
+ if (ret < 0)
+ zlog_err("%s: Can't switchback from VRF %u (%s)",
+ __func__, vrf_id, safe_strerror(errno));
+ errno = saved_errno;
+ return rc;
+}
+
+int vrf_sockunion_socket(const union sockunion *su, vrf_id_t vrf_id,
+ char *interfacename)
+{
+ int ret, save_errno, ret2;
+
+ ret = vrf_switch_to_netns(vrf_id);
+ if (ret < 0)
+ zlog_err("%s: Can't switch to VRF %u (%s)",
+ __func__, vrf_id, safe_strerror(errno));
+ ret = sockunion_socket(su);
+ save_errno = errno;
+ ret2 = vrf_switchback_to_initial();
+ if (ret2 < 0)
+ zlog_err("%s: Can't switchback from VRF %u (%s)",
+ __func__, vrf_id, safe_strerror(errno));
+ errno = save_errno;
+
+ if (ret <= 0)
+ return ret;
+ ret2 = vrf_bind(vrf_id, ret, interfacename);
+ if (ret2 < 0) {
+ close(ret);
+ ret = ret2;
+ }
+ return ret;
}
diff --git a/lib/vrf.h b/lib/vrf.h
index 99c048c702..062e6f3d8d 100644
--- a/lib/vrf.h
+++ b/lib/vrf.h
@@ -26,12 +26,9 @@
#include "linklist.h"
#include "qobj.h"
#include "vty.h"
-
-/* The default NS ID */
-#define NS_DEFAULT 0
+#include "ns.h"
/* The default VRF ID */
-#define VRF_DEFAULT 0
#define VRF_UNKNOWN UINT32_MAX
/* Pending: May need to refine this. */
@@ -42,6 +39,7 @@ enum { IFLA_VRF_UNSPEC, IFLA_VRF_TABLE, __IFLA_VRF_MAX };
#endif
#define VRF_NAMSIZ 36
+#define NS_NAMSIZ 16
#define VRF_DEFAULT_NAME "Default-IP-Routing-Table"
@@ -60,6 +58,7 @@ struct vrf_data {
union {
struct {
uint32_t table_id;
+ char netns_name[NS_NAMSIZ];
} l;
};
};
@@ -88,6 +87,9 @@ struct vrf {
/* The table_id from the kernel */
struct vrf_data data;
+ /* Back pointer to namespace context */
+ void *ns_ctxt;
+
QOBJ_FIELDS
};
RB_HEAD(vrf_id_head, vrf);
@@ -96,6 +98,9 @@ RB_HEAD(vrf_name_head, vrf);
RB_PROTOTYPE(vrf_name_head, vrf, name_entry, vrf_name_compare)
DECLARE_QOBJ_TYPE(vrf)
+/* Allow VRF with netns as backend */
+#define VRF_BACKEND_VRF_LITE 0
+#define VRF_BACKEND_NETNS 1
extern struct vrf_id_head vrfs_by_id;
extern struct vrf_name_head vrfs_by_name;
@@ -195,17 +200,85 @@ extern void vrf_init(int (*create)(struct vrf *), int (*enable)(struct vrf *),
*/
extern void vrf_terminate(void);
-extern void vrf_cmd_init(int (*writefunc)(struct vty *vty));
-
/*
- * VRF utilities
+ * Utilities to create networks objects,
+ * or call network operations
*/
/* Create a socket serving for the given VRF */
-extern int vrf_socket(int, int, int, vrf_id_t);
+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 Debugging
+ * VRF backend routines
+ * should be called from zebra only
+ */
+
+/* VRF vty command initialisation
+ */
+extern void vrf_cmd_init(int (*writefunc)(struct vty *vty));
+
+/* VRF vty debugging
*/
extern void vrf_install_commands(void);
+
+/*
+ * VRF utilities
+ */
+
+/* API for configuring VRF backend
+ * should be called from zebra only
+ */
+extern void vrf_configure_backend(int vrf_backend_netns);
+extern int vrf_get_backend(void);
+extern int vrf_is_backend_netns(void);
+
+
+/* API to create a VRF. either from vty
+ * or through discovery
+ */
+extern int vrf_handler_create(struct vty *vty,
+ const char *name,
+ struct vrf **vrf);
+
+/* API to associate a VRF with a NETNS.
+ * called either from vty or through discovery
+ * should be called from zebra only
+ */
+extern int vrf_netns_handler_create(struct vty *vty, struct vrf *vrf,
+ char *pathname, ns_id_t ns_id);
+
+/* used internally to enable or disable VRF.
+ * Notify a change in the VRF ID of the VRF
+ */
+extern void vrf_disable(struct vrf *vrf);
+extern int vrf_enable(struct vrf *vrf);
+
#endif /*_ZEBRA_VRF_H*/
diff --git a/lib/zclient.c b/lib/zclient.c
index 714888a3f3..ad91eb504b 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -1210,14 +1210,20 @@ stream_failure:
}
bool zapi_route_notify_decode(struct stream *s, struct prefix *p,
+ uint32_t *tableid,
enum zapi_route_notify_owner *note)
{
+ uint32_t t;
+
STREAM_GET(note, s, sizeof(*note));
STREAM_GETC(s, p->family);
STREAM_GETC(s, p->prefixlen);
STREAM_GET(&p->u.prefix, s,
- PSIZE(p->prefixlen));
+ prefix_blen(p));
+ STREAM_GETL(s, t);
+
+ *tableid = t;
return true;
@@ -1402,8 +1408,8 @@ static void zclient_vrf_add(struct zclient *zclient, vrf_id_t vrf_id)
/* Lookup/create vrf by vrf_id. */
vrf = vrf_get(vrf_id, vrfname_tmp);
- vrf->data = data;
-
+ vrf->data.l.table_id = data.l.table_id;
+ memcpy(vrf->data.l.netns_name, data.l.netns_name, NS_NAMSIZ);
vrf_enable(vrf);
}
@@ -2363,9 +2369,9 @@ static int zclient_read(struct thread *thread)
vrf_id);
break;
case ZEBRA_ROUTE_NOTIFY_OWNER:
- if (zclient->notify_owner)
- (*zclient->notify_owner)(command, zclient,
- length, vrf_id);
+ if (zclient->route_notify_owner)
+ (*zclient->route_notify_owner)(command, zclient, length,
+ vrf_id);
break;
default:
break;
diff --git a/lib/zclient.h b/lib/zclient.h
index d8a70c6cf3..4c84af1f61 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -213,8 +213,8 @@ struct zclient {
int (*local_macip_add)(int, struct zclient *, uint16_t, vrf_id_t);
int (*local_macip_del)(int, struct zclient *, uint16_t, vrf_id_t);
int (*pw_status_update)(int, struct zclient *, uint16_t, vrf_id_t);
- int (*notify_owner)(int command, struct zclient *zclient,
- uint16_t length, vrf_id_t vrf_id);
+ int (*route_notify_owner)(int command, struct zclient *zclient,
+ uint16_t length, vrf_id_t vrf_id);
};
/* Zebra API message flag. */
@@ -513,6 +513,7 @@ extern int zclient_send_rnh(struct zclient *zclient, int command,
extern int zapi_route_encode(u_char, struct stream *, struct zapi_route *);
extern int zapi_route_decode(struct stream *, struct zapi_route *);
bool zapi_route_notify_decode(struct stream *s, struct prefix *p,
+ uint32_t *tableid,
enum zapi_route_notify_owner *note);
extern struct nexthop *nexthop_from_zapi_nexthop(struct zapi_nexthop *znh);
extern bool zapi_nexthop_update_decode(struct stream *s,
diff --git a/ospfd/ospf_network.c b/ospfd/ospf_network.c
index 022a5a138a..045634d8ab 100644
--- a/ospfd/ospf_network.c
+++ b/ospfd/ospf_network.c
@@ -169,42 +169,27 @@ int ospf_if_ipmulticast(struct ospf *top, struct prefix *p, ifindex_t ifindex)
return ret;
}
-int ospf_bind_vrfdevice(struct ospf *ospf, int ospf_sock)
-{
- int ret = 0;
-
-#ifdef SO_BINDTODEVICE
-
- if (ospf && ospf->vrf_id != VRF_DEFAULT &&
- ospf->vrf_id != VRF_UNKNOWN) {
- ret = setsockopt(ospf_sock, SOL_SOCKET, SO_BINDTODEVICE,
- ospf->name,
- strlen(ospf->name));
- if (ret < 0) {
- int save_errno = errno;
-
- zlog_warn("%s: Could not setsockopt SO_BINDTODEVICE %s",
- __PRETTY_FUNCTION__,
- safe_strerror(save_errno));
- }
-
- }
-#endif
- return ret;
-}
-
int ospf_sock_init(struct ospf *ospf)
{
int ospf_sock;
int ret, hincl = 1;
int bufsize = (8 * 1024 * 1024);
+ /* silently ignore. already done */
+ if (ospf->fd > 0)
+ return -1;
+
+ if (ospf->vrf_id == VRF_UNKNOWN) {
+ /* silently return since VRF is not ready */
+ return -1;
+ }
if (ospfd_privs.change(ZPRIVS_RAISE)) {
zlog_err("ospf_sock_init: could not raise privs, %s",
safe_strerror(errno));
}
- ospf_sock = socket(AF_INET, SOCK_RAW, IPPROTO_OSPFIGP);
+ ospf_sock = vrf_socket(AF_INET, SOCK_RAW, IPPROTO_OSPFIGP,
+ ospf->vrf_id, ospf->name);
if (ospf_sock < 0) {
int save_errno = errno;
@@ -216,12 +201,6 @@ int ospf_sock_init(struct ospf *ospf)
exit(1);
}
- ret = ospf_bind_vrfdevice(ospf, ospf_sock);
- if (ret < 0) {
- close(ospf_sock);
- goto out;
- }
-
#ifdef IP_HDRINCL
/* we will include IP header with packet */
ret = setsockopt(ospf_sock, IPPROTO_IP, IP_HDRINCL, &hincl,
diff --git a/ospfd/ospf_network.h b/ospfd/ospf_network.h
index 41a7abda70..cbaf132327 100644
--- a/ospfd/ospf_network.h
+++ b/ospfd/ospf_network.h
@@ -30,6 +30,5 @@ extern int ospf_if_add_alldrouters(struct ospf *, struct prefix *, ifindex_t);
extern int ospf_if_drop_alldrouters(struct ospf *, struct prefix *, ifindex_t);
extern int ospf_if_ipmulticast(struct ospf *, struct prefix *, ifindex_t);
extern int ospf_sock_init(struct ospf *ospf);
-extern int ospf_bind_vrfdevice(struct ospf *, int);
#endif /* _ZEBRA_OSPF_NETWORK_H */
diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c
index 86a3293d71..79af4a55fb 100644
--- a/ospfd/ospfd.c
+++ b/ospfd/ospfd.c
@@ -308,12 +308,6 @@ static struct ospf *ospf_new(u_short instance, const char *name)
new->lsa_refresh_interval, &new->t_lsa_refresher);
new->lsa_refresher_started = monotime(NULL);
- if ((ospf_sock_init(new)) < 0) {
- zlog_err(
- "ospf_new: fatal error: ospf_sock_init was unable to open "
- "a socket");
- exit(1);
- }
if ((new->ibuf = stream_new(OSPF_MAX_PACKET_SIZE + 1)) == NULL) {
zlog_err(
"ospf_new: fatal error: stream_new(%u) failed allocating ibuf",
@@ -321,7 +315,6 @@ static struct ospf *ospf_new(u_short instance, const char *name)
exit(1);
}
new->t_read = NULL;
- thread_add_read(master, ospf_read, new, new->fd, &new->t_read);
new->oi_write_q = list_new();
new->write_oi_count = OSPF_WRITE_INTERFACE_COUNT_DEFAULT;
@@ -332,6 +325,16 @@ static struct ospf *ospf_new(u_short instance, const char *name)
QOBJ_REG(new, ospf);
+ new->fd = -1;
+ if ((ospf_sock_init(new)) < 0) {
+ if (new->vrf_id != VRF_UNKNOWN)
+ zlog_warn(
+ "%s: ospf_sock_init is unable to open a socket",
+ __func__);
+ return new;
+ }
+ thread_add_read(master, ospf_read, new, new->fd, &new->t_read);
+
return new;
}
@@ -2049,7 +2052,8 @@ static int ospf_vrf_delete(struct vrf *vrf)
static int ospf_vrf_enable(struct vrf *vrf)
{
struct ospf *ospf = NULL;
- vrf_id_t old_vrf_id = VRF_DEFAULT;
+ vrf_id_t old_vrf_id;
+ int ret = 0;
if (IS_DEBUG_OSPF_EVENT)
zlog_debug("%s: VRF %s id %u enabled",
@@ -2070,13 +2074,15 @@ static int ospf_vrf_enable(struct vrf *vrf)
zlog_err("ospf_sock_init: could not raise privs, %s",
safe_strerror(errno));
}
- if (ospf_bind_vrfdevice(ospf, ospf->fd) < 0)
- return 0;
+ ret = ospf_sock_init(ospf);
if (ospfd_privs.change(ZPRIVS_LOWER)) {
zlog_err("ospf_sock_init: could not lower privs, %s",
safe_strerror(errno));
}
-
+ if (ret < 0 || ospf->fd <= 0)
+ return 0;
+ thread_add_read(master, ospf_read, ospf,
+ ospf->fd, &ospf->t_read);
ospf->oi_running = 1;
ospf_zebra_vrf_register(ospf);
ospf_router_id_update(ospf);
@@ -2111,6 +2117,9 @@ static int ospf_vrf_disable(struct vrf *vrf)
if (IS_DEBUG_OSPF_EVENT)
zlog_debug("%s: ospf old_vrf_id %d unlinked",
__PRETTY_FUNCTION__, old_vrf_id);
+ thread_cancel(ospf->t_read);
+ close(ospf->fd);
+ ospf->fd = -1;
}
/* Note: This is a callback, the VRF will be deleted by the caller. */
diff --git a/pimd/CAVEATS b/pimd/CAVEATS
index 43dd823ae5..120708ba14 100644
--- a/pimd/CAVEATS
+++ b/pimd/CAVEATS
@@ -173,4 +173,10 @@ C19 Provision to prevent group mode clash
flow. There could be some provision to prevent such a behavior,
but currently there is none.
+C20 Multicast traceroute module is based on:
+ draft-ietf-idmr-traceroute-ipm-07
+ It only implements, so far, weak traceroutes. The multicast routing
+ state of the router is not quieried but RPF path is followed along
+ PIM and IGMP enabled interfaces.
+
-x-
diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c
index f02cf7ed31..a807c69c60 100644
--- a/pimd/pim_iface.c
+++ b/pimd/pim_iface.c
@@ -85,9 +85,11 @@ static void *if_list_clean(struct pim_interface *pim_ifp)
if (pim_ifp->sec_addr_list)
list_delete_and_null(&pim_ifp->sec_addr_list);
- while ((ch = RB_ROOT(pim_ifchannel_rb,
- &pim_ifp->ifchannel_rb)) != NULL)
+ while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) {
+ ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb);
+
pim_ifchannel_delete(ch);
+ }
XFREE(MTYPE_PIM_INTERFACE, pim_ifp);
@@ -250,9 +252,11 @@ void pim_if_delete(struct interface *ifp)
if (pim_ifp->boundary_oil_plist)
XFREE(MTYPE_PIM_INTERFACE, pim_ifp->boundary_oil_plist);
- while ((ch = RB_ROOT(pim_ifchannel_rb,
- &pim_ifp->ifchannel_rb)) != NULL)
+ while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) {
+ ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb);
+
pim_ifchannel_delete(ch);
+ }
XFREE(MTYPE_PIM_INTERFACE, pim_ifp);
diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c
index 7d3b783adf..4d564e5046 100644
--- a/pimd/pim_ifchannel.c
+++ b/pimd/pim_ifchannel.c
@@ -211,8 +211,9 @@ void pim_ifchannel_delete_all(struct interface *ifp)
if (!pim_ifp)
return;
- while ((ch = RB_ROOT(pim_ifchannel_rb,
- &pim_ifp->ifchannel_rb)) != NULL) {
+ while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) {
+ ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb);
+
pim_ifchannel_delete(ch);
}
}
diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c
index 78e8cf0adc..3b22db20aa 100644
--- a/sharpd/sharp_zebra.c
+++ b/sharpd/sharp_zebra.c
@@ -131,13 +131,14 @@ static int interface_state_down(int command, struct zclient *zclient,
extern uint32_t total_routes;
extern uint32_t installed_routes;
-static int notify_owner(int command, struct zclient *zclient,
- zebra_size_t length, vrf_id_t vrf_id)
+static int route_notify_owner(int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
{
struct prefix p;
enum zapi_route_notify_owner note;
+ uint32_t table;
- if (!zapi_route_notify_decode(zclient->ibuf, &p, &note))
+ if (!zapi_route_notify_decode(zclient->ibuf, &p, &table, &note))
return -1;
installed_routes++;
@@ -210,5 +211,5 @@ void sharp_zebra_init(void)
zclient->interface_down = interface_state_down;
zclient->interface_address_add = interface_address_add;
zclient->interface_address_delete = interface_address_delete;
- zclient->notify_owner = notify_owner;
+ zclient->route_notify_owner = route_notify_owner;
}
diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am
index c9b6f50160..33d34fc0dd 100644
--- a/vtysh/Makefile.am
+++ b/vtysh/Makefile.am
@@ -147,7 +147,7 @@ vtysh_cmd_FILES = $(vtysh_scan) \
$(top_srcdir)/lib/distribute.c $(top_srcdir)/lib/if_rmap.c \
$(top_srcdir)/lib/vrf.c \
$(top_srcdir)/lib/vty.c $(top_srcdir)/zebra/debug.c \
- $(top_srcdir)/lib/ns.c \
+ $(top_srcdir)/lib/logicalrouter.c \
$(top_srcdir)/zebra/interface.c \
$(top_srcdir)/zebra/irdp_interface.c \
$(top_srcdir)/zebra/rtadv.c $(top_srcdir)/zebra/zebra_vty.c \
diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in
index dedf3d1647..6cfb51b00f 100755
--- a/vtysh/extract.pl.in
+++ b/vtysh/extract.pl.in
@@ -87,6 +87,9 @@ foreach (@ARGV) {
elsif ($file =~ /lib\/vrf\.c$/) {
$protocol = "VTYSH_ALL";
}
+ elsif ($file =~ /lib\/logicalrouter\.c$/) {
+ $protocol = "VTYSH_ALL";
+ }
elsif ($file =~ /lib\/filter\.c$/) {
$protocol = "VTYSH_ALL";
}
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index 65e9c9f8c5..e0a0dd585d 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -975,8 +975,8 @@ static struct cmd_node pw_node = {
PW_NODE, "%s(config-pw)# ",
};
-static struct cmd_node ns_node = {
- NS_NODE, "%s(config-logical-router)# ",
+static struct cmd_node logicalrouter_node = {
+ LOGICALROUTER_NODE, "%s(config-logical-router)# ",
};
static struct cmd_node vrf_node = {
@@ -1508,7 +1508,7 @@ static int vtysh_exit(struct vty *vty)
break;
case INTERFACE_NODE:
case PW_NODE:
- case NS_NODE:
+ case LOGICALROUTER_NODE:
case VRF_NODE:
case ZEBRA_NODE:
case BGP_NODE:
@@ -1782,16 +1782,25 @@ DEFSH(VTYSH_ZEBRA, vtysh_no_interface_vrf_cmd, "no interface IFNAME vrf NAME",
"Delete a pseudo interface's configuration\n"
"Interface's name\n" VRF_CMD_HELP_STR)
-DEFUNSH(VTYSH_NS, vtysh_ns, vtysh_ns_cmd, "logical-router (1-65535) ns NAME",
+DEFUNSH(VTYSH_ZEBRA, vtysh_logicalrouter, vtysh_logicalrouter_cmd,
+ "logical-router (1-65535) ns NAME",
"Enable a logical-router\n"
"Specify the logical-router indentifier\n"
"The Name Space\n"
"The file name in " NS_RUN_DIR ", or a full pathname\n")
{
- vty->node = NS_NODE;
+ vty->node = LOGICALROUTER_NODE;
return CMD_SUCCESS;
}
+DEFSH(VTYSH_ZEBRA, vtysh_no_logicalrouter_cmd,
+ "no logical-router (1-65535) ns NAME",
+ NO_STR
+ "Enable a Logical-Router\n"
+ "Specify the Logical-Router identifier\n"
+ "The Name Space\n"
+ "The file name in " NS_RUN_DIR ", or a full pathname\n")
+
DEFUNSH(VTYSH_VRF, vtysh_vrf, vtysh_vrf_cmd, "vrf NAME",
"Select a VRF to configure\n"
"VRF's name\n")
@@ -1804,16 +1813,18 @@ DEFSH(VTYSH_ZEBRA, vtysh_no_vrf_cmd, "no vrf NAME", NO_STR
"Delete a pseudo vrf's configuration\n"
"VRF's name\n")
-DEFUNSH(VTYSH_NS, vtysh_exit_ns, vtysh_exit_ns_cmd, "exit",
+DEFUNSH(VTYSH_NS, vtysh_exit_logicalrouter,
+ vtysh_exit_logicalrouter_cmd, "exit",
"Exit current mode and down to previous mode\n")
{
return vtysh_exit(vty);
}
-DEFUNSH(VTYSH_NS, vtysh_quit_ns, vtysh_quit_ns_cmd, "quit",
+DEFUNSH(VTYSH_NS, vtysh_quit_logicalrouter,
+ vtysh_quit_logicalrouter_cmd, "quit",
"Exit current mode and down to previous mode\n")
{
- return vtysh_exit_ns(self, vty, argc, argv);
+ return vtysh_exit_logicalrouter(self, vty, argc, argv);
}
DEFUNSH(VTYSH_VRF, vtysh_exit_vrf, vtysh_exit_vrf_cmd, "exit",
@@ -3055,7 +3066,7 @@ void vtysh_init_vty(void)
install_node(&interface_node, NULL);
install_node(&pw_node, NULL);
install_node(&link_params_node, NULL);
- install_node(&ns_node, NULL);
+ install_node(&logicalrouter_node, NULL);
install_node(&vrf_node, NULL);
install_node(&rmap_node, NULL);
install_node(&zebra_node, NULL);
@@ -3233,11 +3244,14 @@ void vtysh_init_vty(void)
install_element(PW_NODE, &vtysh_exit_interface_cmd);
install_element(PW_NODE, &vtysh_quit_interface_cmd);
- install_element(NS_NODE, &vtysh_end_all_cmd);
+ install_element(LOGICALROUTER_NODE, &vtysh_end_all_cmd);
- install_element(CONFIG_NODE, &vtysh_ns_cmd);
- install_element(NS_NODE, &vtysh_exit_ns_cmd);
- install_element(NS_NODE, &vtysh_quit_ns_cmd);
+ install_element(CONFIG_NODE, &vtysh_logicalrouter_cmd);
+ install_element(CONFIG_NODE, &vtysh_no_logicalrouter_cmd);
+ install_element(LOGICALROUTER_NODE,
+ &vtysh_exit_logicalrouter_cmd);
+ install_element(LOGICALROUTER_NODE,
+ &vtysh_quit_logicalrouter_cmd);
install_element(VRF_NODE, &vtysh_end_all_cmd);
install_element(VRF_NODE, &vtysh_exit_vrf_cmd);
diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c
index 967f855fbc..aa1dd407eb 100644
--- a/vtysh/vtysh_config.c
+++ b/vtysh/vtysh_config.c
@@ -187,7 +187,7 @@ void vtysh_config_parse_line(void *arg, const char *line)
config->index = INTERFACE_NODE;
} else if (config->index == RMAP_NODE
|| config->index == INTERFACE_NODE
- || config->index == NS_NODE
+ || config->index == LOGICALROUTER_NODE
|| config->index == VTY_NODE
|| config->index == VRF_NODE)
config_add_line_uniq(config->line, line);
@@ -202,7 +202,7 @@ void vtysh_config_parse_line(void *arg, const char *line)
else if (strncmp(line, "pseudowire", strlen("pseudowire")) == 0)
config = config_get(PW_NODE, line);
else if (strncmp(line, "logical-router", strlen("ns")) == 0)
- config = config_get(NS_NODE, line);
+ config = config_get(LOGICALROUTER_NODE, line);
else if (strncmp(line, "vrf", strlen("vrf")) == 0)
config = config_get(VRF_NODE, line);
else if (strncmp(line, "router-id", strlen("router-id")) == 0)
diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c
index a4985c423c..ca6c7798eb 100644
--- a/vtysh/vtysh_main.c
+++ b/vtysh/vtysh_main.c
@@ -527,7 +527,7 @@ int main(int argc, char **argv, char **env)
homedir = vtysh_get_home();
if (homedir) {
snprintf(history_file, sizeof(history_file),
- "%s/.history_quagga", homedir);
+ "%s/.history_frr", homedir);
if (read_history(history_file) != 0) {
int fp;
diff --git a/zebra/if_ioctl.c b/zebra/if_ioctl.c
index 1d108886de..09fc085018 100644
--- a/zebra/if_ioctl.c
+++ b/zebra/if_ioctl.c
@@ -146,7 +146,7 @@ static int if_get_hwaddr(struct interface *ifp)
ifreq.ifr_addr.sa_family = AF_INET;
/* Fetch Hardware address if available. */
- ret = if_ioctl(SIOCGIFHWADDR, (caddr_t)&ifreq);
+ ret = vrf_if_ioctl(SIOCGIFHWADDR, (caddr_t)&ifreq, ifp->vrf_id);
if (ret < 0)
ifp->hw_addr_len = 0;
else {
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c
index 14905b738b..639f70a6b4 100644
--- a/zebra/if_netlink.c
+++ b/zebra/if_netlink.c
@@ -66,6 +66,7 @@
#include "zebra/kernel_netlink.h"
#include "zebra/if_netlink.h"
+extern struct zebra_privs_t zserv_privs;
/* Note: on netlink systems, there should be a 1-to-1 mapping between interface
names and ifindex values. */
@@ -344,12 +345,13 @@ static void netlink_vrf_change(struct nlmsghdr *h, struct rtattr *tb,
}
}
-static int get_iflink_speed(const char *ifname)
+static int get_iflink_speed(struct interface *interface)
{
struct ifreq ifdata;
struct ethtool_cmd ecmd;
int sd;
int rc;
+ const char *ifname = interface->name;
/* initialize struct */
memset(&ifdata, 0, sizeof(ifdata));
@@ -363,16 +365,20 @@ static int get_iflink_speed(const char *ifname)
ifdata.ifr_data = (__caddr_t)&ecmd;
/* use ioctl to get IP address of an interface */
- sd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (zserv_privs.change(ZPRIVS_RAISE))
+ zlog_err("Can't raise privileges");
+ sd = vrf_socket(PF_INET, SOCK_DGRAM, IPPROTO_IP,
+ interface->vrf_id, NULL);
if (sd < 0) {
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug("Failure to read interface %s speed: %d %s",
ifname, errno, safe_strerror(errno));
return 0;
}
-
/* Get the current link state for the interface */
- rc = ioctl(sd, SIOCETHTOOL, (char *)&ifdata);
+ rc = vrf_ioctl(interface->vrf_id, sd, SIOCETHTOOL, (char *)&ifdata);
+ if (zserv_privs.change(ZPRIVS_LOWER))
+ zlog_err("Can't lower privileges");
if (rc < 0) {
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug(
@@ -389,7 +395,7 @@ static int get_iflink_speed(const char *ifname)
uint32_t kernel_get_speed(struct interface *ifp)
{
- return get_iflink_speed(ifp->name);
+ return get_iflink_speed(ifp);
}
static int netlink_extract_bridge_info(struct rtattr *link_data,
@@ -615,13 +621,14 @@ static int netlink_interface(struct sockaddr_nl *snl, struct nlmsghdr *h,
}
/* If VRF, create the VRF structure itself. */
- if (zif_type == ZEBRA_IF_VRF) {
+ if (zif_type == ZEBRA_IF_VRF && !vrf_is_backend_netns()) {
netlink_vrf_change(h, tb[IFLA_LINKINFO], name);
vrf_id = (vrf_id_t)ifi->ifi_index;
}
if (tb[IFLA_MASTER]) {
- if (slave_kind && (strcmp(slave_kind, "vrf") == 0)) {
+ if (slave_kind && (strcmp(slave_kind, "vrf") == 0)
+ && !vrf_is_backend_netns()) {
zif_slave_type = ZEBRA_IF_SLAVE_VRF;
vrf_id = *(u_int32_t *)RTA_DATA(tb[IFLA_MASTER]);
} else if (slave_kind && (strcmp(slave_kind, "bridge") == 0)) {
@@ -631,6 +638,8 @@ static int netlink_interface(struct sockaddr_nl *snl, struct nlmsghdr *h,
} else
zif_slave_type = ZEBRA_IF_SLAVE_OTHER;
}
+ if (vrf_is_backend_netns())
+ vrf_id = (vrf_id_t)ns_id;
/* If linking to another interface, note it. */
if (tb[IFLA_LINK])
@@ -644,7 +653,7 @@ static int netlink_interface(struct sockaddr_nl *snl, struct nlmsghdr *h,
SET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK);
ifp->mtu6 = ifp->mtu = *(uint32_t *)RTA_DATA(tb[IFLA_MTU]);
ifp->metric = 0;
- ifp->speed = get_iflink_speed(name);
+ ifp->speed = get_iflink_speed(ifp);
ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN;
if (desc)
@@ -789,8 +798,12 @@ static int netlink_address(int cmd, int family, struct interface *ifp,
char buf[NL_PKT_BUF_SIZE];
} req;
- struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
+ struct zebra_ns *zns;
+ if (vrf_is_backend_netns())
+ zns = zebra_ns_lookup((ns_id_t)ifp->vrf_id);
+ else
+ zns = zebra_ns_lookup(NS_DEFAULT);
p = ifc->address;
memset(&req, 0, sizeof req - NL_PKT_BUF_SIZE);
@@ -1017,6 +1030,7 @@ int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
zns = zebra_ns_lookup(ns_id);
ifi = NLMSG_DATA(h);
+ /* assume if not default zns, then new VRF */
if (!(h->nlmsg_type == RTM_NEWLINK || h->nlmsg_type == RTM_DELLINK)) {
/* If this is not link add/delete message so print warning. */
zlog_warn("netlink_link_change: wrong kernel message %d",
@@ -1074,7 +1088,7 @@ int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
}
/* If VRF, create or update the VRF structure itself. */
- if (zif_type == ZEBRA_IF_VRF) {
+ if (zif_type == ZEBRA_IF_VRF && !vrf_is_backend_netns()) {
netlink_vrf_change(h, tb[IFLA_LINKINFO], name);
vrf_id = (vrf_id_t)ifi->ifi_index;
}
@@ -1091,7 +1105,8 @@ int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
if (h->nlmsg_type == RTM_NEWLINK) {
if (tb[IFLA_MASTER]) {
- if (slave_kind && (strcmp(slave_kind, "vrf") == 0)) {
+ if (slave_kind && (strcmp(slave_kind, "vrf") == 0)
+ && !vrf_is_backend_netns()) {
zif_slave_type = ZEBRA_IF_SLAVE_VRF;
vrf_id =
*(u_int32_t *)RTA_DATA(tb[IFLA_MASTER]);
@@ -1103,7 +1118,8 @@ int netlink_link_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
} else
zif_slave_type = ZEBRA_IF_SLAVE_OTHER;
}
-
+ if (vrf_is_backend_netns())
+ vrf_id = (vrf_id_t)ns_id;
if (ifp == NULL
|| !CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_ACTIVE)) {
/* Add interface notification from kernel */
diff --git a/zebra/interface.c b/zebra/interface.c
index 07570e64bf..7229b8818d 100644
--- a/zebra/interface.c
+++ b/zebra/interface.c
@@ -512,9 +512,15 @@ static void if_addr_wakeup(struct interface *ifp)
void if_add_update(struct interface *ifp)
{
struct zebra_if *if_data;
+ struct zebra_ns *zns;
+ struct zebra_vrf *zvrf = vrf_info_lookup(ifp->vrf_id);
- if_link_per_ns(zebra_ns_lookup(NS_DEFAULT), ifp);
-
+ /* case interface populate before vrf enabled */
+ if (zvrf->zns)
+ zns = zvrf->zns;
+ else
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ if_link_per_ns(zns, ifp);
if_data = ifp->info;
assert(if_data);
@@ -800,10 +806,12 @@ void if_nbr_ipv6ll_to_ipv4ll_neigh_update(struct interface *ifp,
char buf[16] = "169.254.0.1";
struct in_addr ipv4_ll;
char mac[6];
+ ns_id_t ns_id;
inet_pton(AF_INET, buf, &ipv4_ll);
ipv6_ll_address_to_mac(address, (u_char *)mac);
+ ns_id = zvrf->zns->ns_id;
/*
* Remove existed arp record for the interface as netlink
@@ -811,10 +819,10 @@ void if_nbr_ipv6ll_to_ipv4ll_neigh_update(struct interface *ifp,
*
* supported message types are RTM_NEWNEIGH and RTM_DELNEIGH
*/
- kernel_neigh_update (0, ifp->ifindex, ipv4_ll.s_addr, mac, 6);
+ kernel_neigh_update(0, ifp->ifindex, ipv4_ll.s_addr, mac, 6, ns_id);
/* Add arp record */
- kernel_neigh_update (add, ifp->ifindex, ipv4_ll.s_addr, mac, 6);
+ kernel_neigh_update(add, ifp->ifindex, ipv4_ll.s_addr, mac, 6, ns_id);
zvrf->neigh_updates++;
}
@@ -1404,7 +1412,7 @@ DEFUN (show_interface_name_vrf,
int idx_ifname = 2;
int idx_name = 4;
struct interface *ifp;
- vrf_id_t vrf_id = VRF_DEFAULT;
+ vrf_id_t vrf_id;
interface_update_stats();
diff --git a/zebra/ioctl.c b/zebra/ioctl.c
index 8e3a1d1a03..d07d37056e 100644
--- a/zebra/ioctl.c
+++ b/zebra/ioctl.c
@@ -59,6 +59,7 @@ int if_ioctl(u_long request, caddr_t buffer)
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
int save_errno = errno;
+
if (zserv_privs.change(ZPRIVS_LOWER))
zlog_err("Can't lower privileges");
zlog_err("Cannot create UDP socket: %s",
@@ -78,6 +79,39 @@ int if_ioctl(u_long request, caddr_t buffer)
return 0;
}
+/* call ioctl system call */
+int vrf_if_ioctl(u_long request, caddr_t buffer, vrf_id_t vrf_id)
+{
+ int sock;
+ int ret;
+ int err = 0;
+
+ if (zserv_privs.change(ZPRIVS_RAISE))
+ zlog_err("Can't raise privileges");
+ sock = vrf_socket(AF_INET, SOCK_DGRAM, 0, vrf_id, NULL);
+ if (sock < 0) {
+ int save_errno = errno;
+
+ if (zserv_privs.change(ZPRIVS_LOWER))
+ zlog_err("Can't lower privileges");
+ zlog_err("Cannot create UDP socket: %s",
+ safe_strerror(save_errno));
+ exit(1);
+ }
+ ret = vrf_ioctl(vrf_id, sock, request, buffer);
+ if (ret < 0)
+ err = errno;
+ if (zserv_privs.change(ZPRIVS_LOWER))
+ zlog_err("Can't lower privileges");
+ close(sock);
+
+ if (ret < 0) {
+ errno = err;
+ return ret;
+ }
+ return 0;
+}
+
#ifndef HAVE_NETLINK
static int if_ioctl_ipv6(u_long request, caddr_t buffer)
{
@@ -90,6 +124,7 @@ static int if_ioctl_ipv6(u_long request, caddr_t buffer)
sock = socket(AF_INET6, SOCK_DGRAM, 0);
if (sock < 0) {
int save_errno = errno;
+
if (zserv_privs.change(ZPRIVS_LOWER))
zlog_err("Can't lower privileges");
zlog_err("Cannot create IPv6 datagram socket: %s",
@@ -122,7 +157,7 @@ void if_get_metric(struct interface *ifp)
ifreq_set_name(&ifreq, ifp);
- if (if_ioctl(SIOCGIFMETRIC, (caddr_t)&ifreq) < 0)
+ if (vrf_if_ioctl(SIOCGIFMETRIC, (caddr_t)&ifreq, ifp->vrf_id) < 0)
return;
ifp->metric = ifreq.ifr_metric;
if (ifp->metric == 0)
@@ -140,7 +175,7 @@ void if_get_mtu(struct interface *ifp)
ifreq_set_name(&ifreq, ifp);
#if defined(SIOCGIFMTU)
- if (if_ioctl(SIOCGIFMTU, (caddr_t)&ifreq) < 0) {
+ if (vrf_if_ioctl(SIOCGIFMTU, (caddr_t)&ifreq, ifp->vrf_id) < 0) {
zlog_info("Can't lookup mtu by ioctl(SIOCGIFMTU)");
ifp->mtu6 = ifp->mtu = -1;
return;
@@ -376,9 +411,9 @@ void if_get_flags(struct interface *ifp)
ifreq_set_name(&ifreq, ifp);
- ret = if_ioctl(SIOCGIFFLAGS, (caddr_t)&ifreq);
+ ret = vrf_if_ioctl(SIOCGIFFLAGS, (caddr_t)&ifreq, ifp->vrf_id);
if (ret < 0) {
- zlog_err("if_ioctl(SIOCGIFFLAGS) failed: %s",
+ zlog_err("vrf_if_ioctl(SIOCGIFFLAGS) failed: %s",
safe_strerror(errno));
return;
}
@@ -423,7 +458,7 @@ int if_set_flags(struct interface *ifp, uint64_t flags)
ifreq.ifr_flags = ifp->flags;
ifreq.ifr_flags |= flags;
- ret = if_ioctl(SIOCSIFFLAGS, (caddr_t)&ifreq);
+ ret = vrf_if_ioctl(SIOCSIFFLAGS, (caddr_t)&ifreq, ifp->vrf_id);
if (ret < 0) {
zlog_info("can't set interface flags");
@@ -444,7 +479,7 @@ int if_unset_flags(struct interface *ifp, uint64_t flags)
ifreq.ifr_flags = ifp->flags;
ifreq.ifr_flags &= ~flags;
- ret = if_ioctl(SIOCSIFFLAGS, (caddr_t)&ifreq);
+ ret = vrf_if_ioctl(SIOCSIFFLAGS, (caddr_t)&ifreq, ifp->vrf_id);
if (ret < 0) {
zlog_info("can't unset interface flags");
diff --git a/zebra/ioctl.h b/zebra/ioctl.h
index 02f8e6b880..1a6e14ed4d 100644
--- a/zebra/ioctl.h
+++ b/zebra/ioctl.h
@@ -25,6 +25,7 @@
/* Prototypes. */
extern void ifreq_set_name(struct ifreq *, struct interface *);
extern int if_ioctl(u_long, caddr_t);
+extern int vrf_if_ioctl(u_long request, caddr_t buffer, vrf_id_t vrf_id);
extern int if_set_flags(struct interface *, uint64_t);
extern int if_unset_flags(struct interface *, uint64_t);
diff --git a/zebra/ioctl_solaris.c b/zebra/ioctl_solaris.c
index e8b65925f8..f429c42440 100644
--- a/zebra/ioctl_solaris.c
+++ b/zebra/ioctl_solaris.c
@@ -30,6 +30,7 @@
#include "log.h"
#include "privs.h"
#include "vty.h"
+#include "vrf.h"
#include "zebra/rib.h"
#include "zebra/rt.h"
@@ -44,6 +45,11 @@ void lifreq_set_name(struct lifreq *lifreq, const char *ifname)
strncpy(lifreq->lifr_name, ifname, IFNAMSIZ);
}
+int vrf_if_ioctl(u_long request, caddr_t buffer, vrf_id_t vrf_id)
+{
+ return if_ioctl(request, buffer);
+}
+
/* call ioctl system call */
int if_ioctl(u_long request, caddr_t buffer)
{
diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c
index 1be2cbcaf5..0b3b6eed45 100644
--- a/zebra/kernel_netlink.c
+++ b/zebra/kernel_netlink.c
@@ -188,7 +188,7 @@ static int netlink_socket(struct nlsock *nl, unsigned long groups,
return -1;
}
- sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ sock = ns_socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE, ns_id);
if (sock < 0) {
zlog_err("Can't open %s socket: %s", nl->name,
safe_strerror(errno));
diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c
index 4d888d8069..3b28a9b242 100644
--- a/zebra/kernel_socket.c
+++ b/zebra/kernel_socket.c
@@ -1384,7 +1384,8 @@ static void routing_socket(struct zebra_ns *zns)
if (zserv_privs.change(ZPRIVS_RAISE))
zlog_err("routing_socket: Can't raise privileges");
- routing_sock = socket(AF_ROUTE, SOCK_RAW, 0);
+ routing_sock = ns_socket(AF_ROUTE, SOCK_RAW,
+ 0, (ns_id_t)zns->ns->ns_id);
if (routing_sock < 0) {
if (zserv_privs.change(ZPRIVS_LOWER))
diff --git a/zebra/main.c b/zebra/main.c
index 19b16936d9..749d509a86 100644
--- a/zebra/main.c
+++ b/zebra/main.c
@@ -34,6 +34,7 @@
#include "privs.h"
#include "sigevent.h"
#include "vrf.h"
+#include "logicalrouter.h"
#include "libfrr.h"
#include "zebra/rib.h"
@@ -47,6 +48,7 @@
#include "zebra/redistribute.h"
#include "zebra/zebra_mpls.h"
#include "zebra/label_manager.h"
+#include "zebra/zebra_netns_notify.h"
#define ZEBRA_PTM_SUPPORT
@@ -85,6 +87,7 @@ struct option longopts[] = {{"batch", no_argument, NULL, 'b'},
{"label_socket", no_argument, NULL, 'l'},
{"retain", no_argument, NULL, 'r'},
#ifdef HAVE_NETLINK
+ {"vrfwnetns", no_argument, NULL, 'n'},
{"nl-bufsize", required_argument, NULL, 's'},
#endif /* HAVE_NETLINK */
{0}};
@@ -122,7 +125,6 @@ static void sigint(void)
{
struct vrf *vrf;
struct zebra_vrf *zvrf;
- struct zebra_ns *zns;
zlog_notice("Terminating on signal");
@@ -137,10 +139,12 @@ static void sigint(void)
if (zvrf)
SET_FLAG(zvrf->flags, ZEBRA_VRF_RETAIN);
}
+ if (zebrad.lsp_process_q)
+ work_queue_free(zebrad.lsp_process_q);
vrf_terminate();
- zns = zebra_ns_lookup(NS_DEFAULT);
- zebra_ns_disable(0, (void **)&zns);
+ ns_walk_func(zebra_ns_disabled);
+ zebra_ns_notify_close();
access_list_reset();
prefix_list_reset();
@@ -148,8 +152,6 @@ static void sigint(void)
list_delete_and_null(&zebrad.client_list);
work_queue_free(zebrad.ribq);
- if (zebrad.lsp_process_q)
- work_queue_free(zebrad.lsp_process_q);
meta_queue_free(zebrad.mq);
frr_fini();
@@ -205,12 +207,16 @@ int main(int argc, char **argv)
char *fuzzing = NULL;
#endif
+ vrf_configure_backend(VRF_BACKEND_VRF_LITE);
+ logicalrouter_configure_backend(
+ LOGICALROUTER_BACKEND_NETNS);
+
frr_preinit(&zebra_di, argc, argv);
frr_opt_add(
"bakz:e:l:r"
#ifdef HAVE_NETLINK
- "s:"
+ "s:n"
#endif
#if defined(HANDLE_ZAPI_FUZZING)
"c:"
@@ -225,6 +231,7 @@ int main(int argc, char **argv)
" -k, --keep_kernel Don't delete old routes which installed by zebra.\n"
" -r, --retain When program terminates, retain added route by zebra.\n"
#ifdef HAVE_NETLINK
+ " -n, --vrfwnetns Set VRF with NetNS\n"
" -s, --nl-bufsize Set netlink receive buffer size\n"
#endif /* HAVE_NETLINK */
#if defined(HANDLE_ZAPI_FUZZING)
@@ -279,6 +286,11 @@ int main(int argc, char **argv)
case 's':
nl_rcvbufsize = atoi(optarg);
break;
+ case 'n':
+ vrf_configure_backend(VRF_BACKEND_NETNS);
+ logicalrouter_configure_backend(
+ LOGICALROUTER_BACKEND_OFF);
+ break;
#endif /* HAVE_NETLINK */
#if defined(HANDLE_ZAPI_FUZZING)
case 'c':
diff --git a/zebra/rt.h b/zebra/rt.h
index 54d45b889a..472f2d7a97 100644
--- a/zebra/rt.h
+++ b/zebra/rt.h
@@ -78,7 +78,8 @@ extern int kernel_address_add_ipv4(struct interface *, struct connected *);
extern int kernel_address_delete_ipv4(struct interface *, struct connected *);
extern int kernel_address_add_ipv6 (struct interface *, struct connected *);
extern int kernel_address_delete_ipv6 (struct interface *, struct connected *);
-extern int kernel_neigh_update(int, int, uint32_t, char *, int);
+extern int kernel_neigh_update(int cmd, int ifindex, uint32_t addr,
+ char *lla, int llalen, ns_id_t ns_id);
extern int kernel_interface_set_master(struct interface *master,
struct interface *slave);
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index a80ab9d834..e26109badf 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -194,16 +194,25 @@ static inline int proto2zebra(int proto, int family)
/*
Pending: create an efficient table_id (in a tree/hash) based lookup)
*/
-static vrf_id_t vrf_lookup_by_table(u_int32_t table_id)
+static vrf_id_t vrf_lookup_by_table(u_int32_t table_id, ns_id_t ns_id)
{
struct vrf *vrf;
struct zebra_vrf *zvrf;
RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) {
- if ((zvrf = vrf->info) == NULL || (zvrf->table_id != table_id))
+ zvrf = vrf->info;
+ if (zvrf == NULL)
continue;
-
- return zvrf_id(zvrf);
+ /* case vrf with netns : match the netnsid */
+ if (vrf_is_backend_netns()) {
+ if (ns_id == zvrf_id(zvrf))
+ return zvrf_id(zvrf);
+ } else {
+ /* VRF is VRF_BACKEND_VRF_LITE */
+ if (zvrf->table_id != table_id)
+ continue;
+ return zvrf_id(zvrf);
+ }
}
return VRF_DEFAULT;
@@ -220,7 +229,7 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl,
u_char flags = 0;
struct prefix p;
struct prefix_ipv6 src_p = {};
- vrf_id_t vrf_id = VRF_DEFAULT;
+ vrf_id_t vrf_id;
char anyaddr[16] = {0};
@@ -288,7 +297,7 @@ static int netlink_route_change_read_unicast(struct sockaddr_nl *snl,
table = rtm->rtm_table;
/* Map to VRF */
- vrf_id = vrf_lookup_by_table(table);
+ vrf_id = vrf_lookup_by_table(table, ns_id);
if (vrf_id == VRF_DEFAULT) {
if (!is_zebra_valid_kernel_table(table)
&& !is_zebra_main_routing_table(table))
@@ -609,7 +618,7 @@ static int netlink_route_change_read_multicast(struct sockaddr_nl *snl,
char sbuf[40];
char gbuf[40];
char oif_list[256] = "\0";
- vrf_id_t vrf = ns_id;
+ vrf_id_t vrf;
int table;
if (mroute)
@@ -631,7 +640,7 @@ static int netlink_route_change_read_multicast(struct sockaddr_nl *snl,
else
table = rtm->rtm_table;
- vrf = vrf_lookup_by_table(table);
+ vrf = vrf_lookup_by_table(table, ns_id);
if (tb[RTA_IIF])
iif = *(int *)RTA_DATA(tb[RTA_IIF]);
@@ -687,24 +696,23 @@ int netlink_route_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
ns_id_t ns_id, int startup)
{
int len;
- vrf_id_t vrf_id = ns_id;
struct rtmsg *rtm;
rtm = NLMSG_DATA(h);
if (!(h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE)) {
/* If this is not route add/delete message print warning. */
- zlog_warn("Kernel message: %d vrf %u\n", h->nlmsg_type, vrf_id);
+ zlog_warn("Kernel message: %d NS %u\n", h->nlmsg_type, ns_id);
return 0;
}
/* Connected route. */
if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug("%s %s %s proto %s vrf %u",
+ zlog_debug("%s %s %s proto %s NS %u",
nl_msg_type_to_str(h->nlmsg_type),
nl_family_to_str(rtm->rtm_family),
nl_rttype_to_str(rtm->rtm_type),
- nl_rtproto_to_str(rtm->rtm_protocol), vrf_id);
+ nl_rtproto_to_str(rtm->rtm_protocol), ns_id);
/* We don't care about change notifications for the MPLS table. */
/* TODO: Revisit this. */
@@ -1274,7 +1282,7 @@ static void _netlink_mpls_debug(int cmd, u_int32_t label, const char *routedesc)
}
static int netlink_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla,
- int llalen)
+ int llalen, ns_id_t ns_id)
{
struct {
struct nlmsghdr n;
@@ -1282,7 +1290,7 @@ static int netlink_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla,
char buf[256];
} req;
- struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
+ struct zebra_ns *zns = zebra_ns_lookup(ns_id);
memset(&req.n, 0, sizeof(req.n));
memset(&req.ndm, 0, sizeof(req.ndm));
@@ -1326,9 +1334,10 @@ static int netlink_route_multipath(int cmd, struct prefix *p,
char buf[NL_PKT_BUF_SIZE];
} req;
- struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
+ struct zebra_ns *zns;
struct zebra_vrf *zvrf = vrf_info_lookup(re->vrf_id);
+ zns = zvrf->zns;
memset(&req, 0, sizeof req - NL_PKT_BUF_SIZE);
bytelen = (family == AF_INET ? 4 : 16);
@@ -1626,8 +1635,9 @@ int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *in)
} req;
mroute = mr;
- struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
+ struct zebra_ns *zns;
+ zns = zvrf->zns;
memset(&req.n, 0, sizeof(req.n));
memset(&req.ndm, 0, sizeof(req.ndm));
@@ -1700,10 +1710,10 @@ void kernel_route_rib(struct route_node *rn, struct prefix *p,
}
int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla,
- int llalen)
+ int llalen, ns_id_t ns_id)
{
return netlink_neigh_update(add ? RTM_NEWNEIGH : RTM_DELNEIGH, ifindex,
- addr, lla, llalen);
+ addr, lla, llalen, ns_id);
}
/*
@@ -1713,14 +1723,16 @@ int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla,
static int netlink_vxlan_flood_list_update(struct interface *ifp,
struct in_addr *vtep_ip, int cmd)
{
- struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
+ struct zebra_ns *zns;
struct {
struct nlmsghdr n;
struct ndmsg ndm;
char buf[256];
} req;
u_char dst_mac[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+ struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id);
+ zns = zvrf->zns;
memset(&req.n, 0, sizeof(req.n));
memset(&req.ndm, 0, sizeof(req.ndm));
@@ -1776,7 +1788,7 @@ int kernel_del_vtep(vni_t vni, struct interface *ifp, struct in_addr *vtep_ip)
#endif
static int netlink_macfdb_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
- int len)
+ int len, ns_id_t ns_id)
{
struct ndmsg *ndm;
struct interface *ifp;
@@ -1799,7 +1811,7 @@ static int netlink_macfdb_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
return 0;
/* The interface should exist. */
- ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT),
+ ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id),
ndm->ndm_ifindex);
if (!ifp || !ifp->info)
return 0;
@@ -1930,7 +1942,7 @@ static int netlink_macfdb_table(struct sockaddr_nl *snl, struct nlmsghdr *h,
if (ndm->ndm_family != AF_BRIDGE)
return 0;
- return netlink_macfdb_change(snl, h, len);
+ return netlink_macfdb_change(snl, h, len, ns_id);
}
/* Request for MAC FDB information from the kernel */
@@ -2012,7 +2024,7 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid,
struct ethaddr *mac, struct in_addr vtep_ip,
int local, int cmd, u_char sticky)
{
- struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
+ struct zebra_ns *zns;
struct {
struct nlmsghdr n;
struct ndmsg ndm;
@@ -2026,7 +2038,9 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid,
int vid_present = 0, dst_present = 0;
char vid_buf[20];
char dst_buf[30];
+ struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id);
+ zns = zvrf->zns;
zif = ifp->info;
if ((br_if = zif->brslave_info.br_if) == NULL) {
zlog_warn("MAC %s on IF %s(%u) - no mapping to bridge",
@@ -2086,7 +2100,7 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid,
| NUD_DELAY)
static int netlink_ipneigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
- int len)
+ int len, ns_id_t ns_id)
{
struct ndmsg *ndm;
struct interface *ifp;
@@ -2107,7 +2121,7 @@ static int netlink_ipneigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
return 0;
/* The interface should exist. */
- ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT),
+ ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id),
ndm->ndm_ifindex);
if (!ifp || !ifp->info)
return 0;
@@ -2129,7 +2143,7 @@ static int netlink_ipneigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
* itself
*/
if (IS_ZEBRA_IF_VLAN(ifp)) {
- link_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT),
+ link_if = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id),
zif->link_ifindex);
if (!link_if)
return 0;
@@ -2307,13 +2321,13 @@ int netlink_neigh_change(struct sockaddr_nl *snl, struct nlmsghdr *h,
/* Is this a notification for the MAC FDB or IP neighbor table? */
ndm = NLMSG_DATA(h);
if (ndm->ndm_family == AF_BRIDGE)
- return netlink_macfdb_change(snl, h, len);
+ return netlink_macfdb_change(snl, h, len, ns_id);
if (ndm->ndm_type != RTN_UNICAST)
return 0;
if (ndm->ndm_family == AF_INET || ndm->ndm_family == AF_INET6)
- return netlink_ipneigh_change(snl, h, len);
+ return netlink_ipneigh_change(snl, h, len, ns_id);
return 0;
}
@@ -2328,10 +2342,12 @@ static int netlink_neigh_update2(struct interface *ifp, struct ipaddr *ip,
} req;
int ipa_len;
- struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT);
+ struct zebra_ns *zns;
char buf[INET6_ADDRSTRLEN];
char buf2[ETHER_ADDR_STRLEN];
+ struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id);
+ zns = zvrf->zns;
memset(&req.n, 0, sizeof(req.n));
memset(&req.ndm, 0, sizeof(req.ndm));
diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c
index 6d4af1203c..b2baee5728 100644
--- a/zebra/rt_socket.c
+++ b/zebra/rt_socket.c
@@ -424,7 +424,7 @@ void kernel_route_rib(struct route_node *rn, struct prefix *p,
}
int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla,
- int llalen)
+ int llalen, ns_id_t ns_id)
{
/* TODO */
return 0;
diff --git a/zebra/rtadv.c b/zebra/rtadv.c
index 32418eb82f..860e8710d6 100644
--- a/zebra/rtadv.c
+++ b/zebra/rtadv.c
@@ -34,6 +34,7 @@
#include "command.h"
#include "privs.h"
#include "vrf.h"
+#include "ns.h"
#include "zebra/interface.h"
#include "zebra/rtadv.h"
@@ -621,7 +622,7 @@ static int rtadv_read(struct thread *thread)
return 0;
}
-static int rtadv_make_socket(void)
+static int rtadv_make_socket(ns_id_t ns_id)
{
int sock;
int ret = 0;
@@ -631,7 +632,7 @@ static int rtadv_make_socket(void)
zlog_err("rtadv_make_socket: could not raise privs, %s",
safe_strerror(errno));
- sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
+ sock = ns_socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6, ns_id);
if (zserv_privs.change(ZPRIVS_LOWER))
zlog_err("rtadv_make_socket: could not lower privs, %s",
@@ -1686,7 +1687,7 @@ static void rtadv_event(struct zebra_ns *zns, enum rtadv_event event, int val)
void rtadv_init(struct zebra_ns *zns)
{
- zns->rtadv.sock = rtadv_make_socket();
+ zns->rtadv.sock = rtadv_make_socket(zns->ns_id);
}
void rtadv_terminate(struct zebra_ns *zns)
diff --git a/zebra/subdir.am b/zebra/subdir.am
index 3474823623..bb7439c0f6 100644
--- a/zebra/subdir.am
+++ b/zebra/subdir.am
@@ -65,6 +65,8 @@ zebra_zebra_SOURCES = \
zebra/zebra_vty.c \
zebra/zebra_vxlan.c \
zebra/zserv.c \
+ zebra/zebra_netns_id.c \
+ zebra/zebra_netns_notify.c \
# end
zebra/zebra_vty_clippy.c: $(CLIPPY_DEPS)
@@ -104,6 +106,8 @@ noinst_HEADERS += \
zebra/zebra_vxlan.h \
zebra/zebra_vxlan_private.h \
zebra/zserv.h \
+ zebra/zebra_netns_id.h \
+ zebra/zebra_netns_notify.h \
# end
zebra_zebra_irdp_la_SOURCES = \
diff --git a/zebra/zebra_netns_id.c b/zebra/zebra_netns_id.c
new file mode 100644
index 0000000000..966d6ed0d2
--- /dev/null
+++ b/zebra/zebra_netns_id.c
@@ -0,0 +1,355 @@
+/* zebra NETNS ID handling routines
+ * those routines are implemented locally to avoid having external dependencies.
+ * Copyright (C) 2018 6WIND
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <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 */
+}
+
diff --git a/zebra/zebra_netns_id.h b/zebra/zebra_netns_id.h
new file mode 100644
index 0000000000..d6530e6694
--- /dev/null
+++ b/zebra/zebra_netns_id.h
@@ -0,0 +1,26 @@
+/* zebra NETNS ID handling routines
+ * Copyright (C) 2018 6WIND
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#if !defined(__ZEBRA_NS_ID_H__)
+#define __ZEBRA_NS_ID_H__
+#include "zebra.h"
+#include "ns.h"
+
+extern ns_id_t zebra_ns_id_get(const char *netnspath);
+extern ns_id_t zebra_ns_id_get_default(void);
+
+#endif /* __ZEBRA_NS_ID_H__ */
diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c
new file mode 100644
index 0000000000..b98d6ed703
--- /dev/null
+++ b/zebra/zebra_netns_notify.c
@@ -0,0 +1,271 @@
+/*
+ * Zebra NS collector and notifier for Network NameSpaces
+ * Copyright (C) 2017 6WIND
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <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 */
diff --git a/zebra/zebra_netns_notify.h b/zebra/zebra_netns_notify.h
new file mode 100644
index 0000000000..0ced749ae8
--- /dev/null
+++ b/zebra/zebra_netns_notify.h
@@ -0,0 +1,29 @@
+/*
+ * Zebra NS collector and notifier for Network NameSpaces
+ * Copyright (C) 2017 6WIND
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _NETNS_NOTIFY_H
+#define _NETNS_NOTIFY_H
+
+extern void zebra_ns_notify_init(void);
+extern void zebra_ns_notify_parse(void);
+extern void zebra_ns_notify_close(void);
+
+extern struct zebra_privs_t zserv_privs;
+
+#endif /* NETNS_NOTIFY_H */
diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c
index c48a6f7bd8..cb302985c8 100644
--- a/zebra/zebra_ns.c
+++ b/zebra/zebra_ns.c
@@ -1,6 +1,7 @@
/* zebra NS Routines
* Copyright (C) 2016 Cumulus Networks, Inc.
* Donald Sharp
+ * Copyright (C) 2017/2018 6WIND
*
* This file is part of Quagga.
*
@@ -22,6 +23,7 @@
#include "lib/ns.h"
#include "lib/vrf.h"
+#include "lib/logicalrouter.h"
#include "lib/prefix.h"
#include "lib/memory.h"
@@ -31,14 +33,99 @@
#include "zebra_memory.h"
#include "rt.h"
#include "zebra_vxlan.h"
+#include "debug.h"
+#include "zebra_netns_notify.h"
+#include "zebra_netns_id.h"
+
+extern struct zebra_privs_t zserv_privs;
DEFINE_MTYPE(ZEBRA, ZEBRA_NS, "Zebra Name Space")
+static inline int
+zebra_ns_table_entry_compare(const struct zebra_ns_table *e1,
+ const struct zebra_ns_table *e2);
+
+RB_GENERATE(zebra_ns_table_head, zebra_ns_table, zebra_ns_table_entry,
+ zebra_ns_table_entry_compare);
+
static struct zebra_ns *dzns;
+static inline int
+zebra_ns_table_entry_compare(const struct zebra_ns_table *e1,
+ const struct zebra_ns_table *e2)
+{
+ if (e1->tableid == e2->tableid)
+ return (e1->afi - e2->afi);
+
+ return e1->tableid - e2->tableid;
+}
+
+static int logicalrouter_config_write(struct vty *vty);
+
struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id)
{
- return dzns;
+ if (ns_id == NS_DEFAULT)
+ return dzns;
+ struct zebra_ns *info = (struct zebra_ns *)ns_info_lookup(ns_id);
+
+ return (info == NULL) ? dzns : info;
+}
+
+static struct zebra_ns *zebra_ns_alloc(void)
+{
+ return XCALLOC(MTYPE_ZEBRA_NS, sizeof(struct zebra_ns));
+}
+
+static int zebra_ns_new(struct ns *ns)
+{
+ struct zebra_ns *zns;
+
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_info("ZNS %s with id %u (created)", ns->name, ns->ns_id);
+
+ zns = zebra_ns_alloc();
+ ns->info = zns;
+ zns->ns = ns;
+
+ /* Do any needed per-NS data structure allocation. */
+ zns->if_table = route_table_init();
+ zebra_vxlan_ns_init(zns);
+
+ return 0;
+}
+
+static int zebra_ns_delete(struct ns *ns)
+{
+ struct zebra_ns *zns = (struct zebra_ns *) ns->info;
+
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_info("ZNS %s with id %u (deleted)", ns->name, ns->ns_id);
+ if (!zns)
+ return 0;
+ XFREE(MTYPE_ZEBRA_NS, zns);
+ return 0;
+}
+
+static int zebra_ns_enabled(struct ns *ns)
+{
+ struct zebra_ns *zns = ns->info;
+
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_info("ZNS %s with id %u (enabled)", ns->name, ns->ns_id);
+ if (!zns)
+ return 0;
+ return zebra_ns_enable(ns->ns_id, (void **)&zns);
+}
+
+int zebra_ns_disabled(struct ns *ns)
+{
+ struct zebra_ns *zns = ns->info;
+
+ if (IS_ZEBRA_DEBUG_EVENT)
+ zlog_info("ZNS %s with id %u (disabled)", ns->name, ns->ns_id);
+ if (!zns)
+ return 0;
+ return zebra_ns_disable(ns->ns_id, (void **)&zns);
}
/* Do global enable actions - open sockets, read kernel config etc. */
@@ -46,6 +133,8 @@ int zebra_ns_enable(ns_id_t ns_id, void **info)
{
struct zebra_ns *zns = (struct zebra_ns *)(*info);
+ zns->ns_id = ns_id;
+
#if defined(HAVE_RTADV)
rtadv_init(zns);
#endif
@@ -57,10 +146,79 @@ int zebra_ns_enable(ns_id_t ns_id, void **info)
return 0;
}
+struct route_table *zebra_ns_find_table(struct zebra_ns *zns,
+ uint32_t tableid, afi_t afi)
+{
+ struct zebra_ns_table finder;
+ struct zebra_ns_table *znst;
+
+ memset(&finder, 0, sizeof(finder));
+ finder.afi = afi;
+ finder.tableid = tableid;
+ znst = RB_FIND(zebra_ns_table_head, &zns->ns_tables, &finder);
+
+ if (znst)
+ return znst->table;
+ else
+ return NULL;
+}
+
+struct route_table *zebra_ns_get_table(struct zebra_ns *zns,
+ struct zebra_vrf *zvrf, uint32_t tableid,
+ afi_t afi)
+{
+ struct zebra_ns_table finder;
+ struct zebra_ns_table *znst;
+ rib_table_info_t *info;
+
+ memset(&finder, 0, sizeof(finder));
+ finder.afi = afi;
+ finder.tableid = tableid;
+ znst = RB_FIND(zebra_ns_table_head, &zns->ns_tables, &finder);
+
+ if (znst)
+ return znst->table;
+
+ znst = XCALLOC(MTYPE_ZEBRA_NS, sizeof(*znst));
+ znst->tableid = tableid;
+ znst->afi = afi;
+ znst->table =
+ (afi == AFI_IP6) ? srcdest_table_init() : route_table_init();
+
+ info = XCALLOC(MTYPE_RIB_TABLE_INFO, sizeof(*info));
+ info->zvrf = zvrf;
+ info->afi = afi;
+ info->safi = SAFI_UNICAST;
+ znst->table->info = info;
+ znst->table->cleanup = zebra_rtable_node_cleanup;
+
+ RB_INSERT(zebra_ns_table_head, &zns->ns_tables, znst);
+ return znst->table;
+}
+
+static void zebra_ns_free_table(struct zebra_ns_table *znst)
+{
+ void *table_info;
+
+ rib_close_table(znst->table);
+
+ table_info = znst->table->info;
+ route_table_finish(znst->table);
+ XFREE(MTYPE_RIB_TABLE_INFO, table_info);
+ XFREE(MTYPE_ZEBRA_NS, znst);
+}
+
int zebra_ns_disable(ns_id_t ns_id, void **info)
{
+ struct zebra_ns_table *znst;
struct zebra_ns *zns = (struct zebra_ns *)(*info);
+ while (!RB_EMPTY(zebra_ns_table_head, &zns->ns_tables)) {
+ znst = RB_ROOT(zebra_ns_table_head, &zns->ns_tables);
+
+ RB_REMOVE(zebra_ns_table_head, &zns->ns_tables, znst);
+ zebra_ns_free_table(znst);
+ }
route_table_finish(zns->if_table);
zebra_vxlan_ns_disable(zns);
#if defined(HAVE_RTADV)
@@ -69,14 +227,27 @@ int zebra_ns_disable(ns_id_t ns_id, void **info)
kernel_terminate(zns);
+ zns->ns_id = NS_DEFAULT;
+
return 0;
}
+
int zebra_ns_init(void)
{
- dzns = XCALLOC(MTYPE_ZEBRA_NS, sizeof(struct zebra_ns));
+ ns_id_t ns_id;
+
+ 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);
- ns_init();
+ logicalrouter_init(logicalrouter_config_write);
/* Do any needed per-NS data structure allocation. */
dzns->if_table = route_table_init();
@@ -86,7 +257,37 @@ int zebra_ns_init(void)
zebra_vrf_init();
/* Default NS is activated */
- zebra_ns_enable(NS_DEFAULT, (void **)&dzns);
+ zebra_ns_enable(ns_id, (void **)&dzns);
+ if (vrf_is_backend_netns()) {
+ ns_add_hook(NS_NEW_HOOK, zebra_ns_new);
+ ns_add_hook(NS_ENABLE_HOOK, zebra_ns_enabled);
+ ns_add_hook(NS_DISABLE_HOOK, zebra_ns_disabled);
+ ns_add_hook(NS_DELETE_HOOK, zebra_ns_delete);
+ zebra_ns_notify_parse();
+ zebra_ns_notify_init();
+ }
+ return 0;
+}
+
+static int logicalrouter_config_write(struct vty *vty)
+{
+ struct ns *ns;
+ int write = 0;
+
+ RB_FOREACH(ns, ns_head, &ns_tree) {
+ if (ns->ns_id == NS_DEFAULT || ns->name == NULL)
+ continue;
+ vty_out(vty, "logical-router %u netns %s\n", ns->ns_id,
+ ns->name);
+ write = 1;
+ }
+ return write;
+}
+
+int zebra_ns_config_write(struct vty *vty, struct ns *ns)
+{
+ if (ns && ns->name != NULL)
+ vty_out(vty, " netns %s\n", ns->name);
return 0;
}
diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h
index 5d90b9be67..3a998a49ff 100644
--- a/zebra/zebra_ns.h
+++ b/zebra/zebra_ns.h
@@ -34,6 +34,18 @@ struct nlsock {
};
#endif
+struct zebra_ns_table {
+ RB_ENTRY(zebra_ns_table) zebra_ns_table_entry;
+
+ uint32_t tableid;
+ afi_t afi;
+
+ struct route_table *table;
+};
+RB_HEAD(zebra_ns_table_head, zebra_ns_table);
+RB_PROTOTYPE(zebra_ns_table_head, zebra_ns_table, zebra_ns_table_entry,
+ zebra_ns_table_entry_compare)
+
struct zebra_ns {
/* net-ns name. */
char name[VRF_NAMSIZ];
@@ -55,11 +67,24 @@ struct zebra_ns {
#if defined(HAVE_RTADV)
struct rtadv rtadv;
#endif /* HAVE_RTADV */
+
+ struct zebra_ns_table_head ns_tables;
+
+ /* Back pointer */
+ struct ns *ns;
};
struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id);
int zebra_ns_init(void);
int zebra_ns_enable(ns_id_t ns_id, void **info);
+int zebra_ns_disabled(struct ns *ns);
int zebra_ns_disable(ns_id_t ns_id, void **info);
+
+extern struct route_table *zebra_ns_find_table(struct zebra_ns *zns,
+ uint32_t tableid, afi_t afi);
+extern struct route_table *zebra_ns_get_table(struct zebra_ns *zns,
+ struct zebra_vrf *zvrf,
+ uint32_t tableid, afi_t afi);
+int zebra_ns_config_write(struct vty *vty, struct ns *ns);
#endif
diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c
index bbd01a759e..96bee36be6 100644
--- a/zebra/zebra_pw.c
+++ b/zebra/zebra_pw.c
@@ -294,8 +294,11 @@ void zebra_pw_exit(struct zebra_vrf *zvrf)
{
struct zebra_pw *pw;
- while ((pw = RB_ROOT(zebra_pw_head, &zvrf->pseudowires)) != NULL)
+ while (!RB_EMPTY(zebra_pw_head, &zvrf->pseudowires)) {
+ pw = RB_ROOT(zebra_pw_head, &zvrf->pseudowires);
+
zebra_pw_del(zvrf, pw);
+ }
}
DEFUN_NOSH (pseudowire_if,
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 967bc92850..59d9e0c2ad 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -1033,8 +1033,7 @@ void kernel_route_rib_pass_fail(struct route_node *rn, struct prefix *p,
else
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);
}
- zsend_route_notify_owner(re->type, re->instance, re->vrf_id,
- p, ZAPI_ROUTE_INSTALLED);
+ zsend_route_notify_owner(re, p, ZAPI_ROUTE_INSTALLED);
break;
case SOUTHBOUND_INSTALL_FAILURE:
/*
@@ -1044,8 +1043,7 @@ void kernel_route_rib_pass_fail(struct route_node *rn, struct prefix *p,
*/
dest->selected_fib = re;
- zsend_route_notify_owner(re->type, re->instance, re->vrf_id,
- p, ZAPI_ROUTE_FAIL_INSTALL);
+ zsend_route_notify_owner(re, p, ZAPI_ROUTE_FAIL_INSTALL);
zlog_warn("%u:%s: Route install failed", re->vrf_id,
prefix2str(p, buf, sizeof(buf)));
break;
@@ -1112,10 +1110,8 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re,
* If this is a replace to a new RE let the originator of the RE
* know that they've lost
*/
- if (old && old != re)
- zsend_route_notify_owner(old->type, old->instance,
- old->vrf_id, p,
- ZAPI_ROUTE_BETTER_ADMIN_WON);
+ if (old && (old != re) && (old->type != re->type))
+ zsend_route_notify_owner(old, p, ZAPI_ROUTE_BETTER_ADMIN_WON);
/*
* Make sure we update the FPM any time we send new information to
@@ -1150,7 +1146,8 @@ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re)
*/
hook_call(rib_update, rn, "uninstalling from kernel");
kernel_route_rib(rn, p, src_p, re, NULL);
- zvrf->removals++;
+ if (zvrf)
+ zvrf->removals++;
return;
}
diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c
index b9b3048486..bb15fd04f3 100644
--- a/zebra/zebra_vrf.c
+++ b/zebra/zebra_vrf.c
@@ -25,8 +25,9 @@
#include "command.h"
#include "memory.h"
#include "srcdest_table.h"
-
+#include "vrf.h"
#include "vty.h"
+
#include "zebra/debug.h"
#include "zebra/zserv.h"
#include "zebra/rib.h"
@@ -76,7 +77,7 @@ void zebra_vrf_update_all(struct zserv *client)
struct vrf *vrf;
RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) {
- if (vrf->vrf_id)
+ if (vrf->vrf_id != VRF_UNKNOWN)
zsend_vrf_add(client, vrf_info_lookup(vrf->vrf_id));
}
}
@@ -90,12 +91,9 @@ static int zebra_vrf_new(struct vrf *vrf)
zlog_info("VRF %s created, id %u", vrf->name, vrf->vrf_id);
zvrf = zebra_vrf_alloc();
- zvrf->zns = zebra_ns_lookup(
- NS_DEFAULT); /* Point to the global (single) NS */
- router_id_init(zvrf);
vrf->info = zvrf;
zvrf->vrf = vrf;
-
+ router_id_init(zvrf);
return 0;
}
@@ -116,6 +114,10 @@ static int zebra_vrf_enable(struct vrf *vrf)
zlog_debug("VRF %s id %u is now active",
zvrf_name(zvrf), zvrf_id(zvrf));
+ if (vrf_is_backend_netns())
+ zvrf->zns = zebra_ns_lookup((ns_id_t)vrf->vrf_id);
+ else
+ zvrf->zns = zebra_ns_lookup(NS_DEFAULT);
/* Inform clients that the VRF is now active. This is an
* add for the clients.
*/
@@ -174,7 +176,6 @@ static int zebra_vrf_disable(struct vrf *vrf)
struct static_route *si;
struct route_table *table;
struct interface *ifp;
- u_int32_t table_id;
afi_t afi;
safi_t safi;
unsigned i;
@@ -213,12 +214,6 @@ static int zebra_vrf_disable(struct vrf *vrf)
for (afi = AFI_IP; afi <= AFI_IP6; afi++) {
for (safi = SAFI_UNICAST; safi <= SAFI_MULTICAST; safi++)
rib_close_table(zvrf->table[afi][safi]);
-
- if (vrf->vrf_id == VRF_DEFAULT)
- for (table_id = 0; table_id < ZEBRA_KERNEL_TABLE_MAX;
- table_id++)
- if (zvrf->other_table[afi][table_id])
- rib_close_table(zvrf->other_table[afi][table_id]);
}
/* Cleanup Vxlan, MPLS and PW tables. */
@@ -236,7 +231,8 @@ static int zebra_vrf_disable(struct vrf *vrf)
struct route_node *rnode;
rib_dest_t *dest;
- for (ALL_LIST_ELEMENTS(zebrad.mq->subq[i], lnode, nnode, rnode)) {
+ for (ALL_LIST_ELEMENTS(zebrad.mq->subq[i],
+ lnode, nnode, rnode)) {
dest = rib_dest_from_rnode(rnode);
if (dest && rib_dest_vrf(dest) == zvrf) {
route_unlock_node(rnode);
@@ -258,17 +254,6 @@ static int zebra_vrf_disable(struct vrf *vrf)
zvrf->table[afi][safi] = NULL;
}
- if (vrf->vrf_id == VRF_DEFAULT)
- for (table_id = 0; table_id < ZEBRA_KERNEL_TABLE_MAX;
- table_id++)
- if (zvrf->other_table[afi][table_id]) {
- table = zvrf->other_table[afi][table_id];
- table_info = table->info;
- route_table_finish(table);
- XFREE(MTYPE_RIB_TABLE_INFO, table_info);
- zvrf->other_table[afi][table_id] = NULL;
- }
-
route_table_finish(zvrf->rnh_table[afi]);
zvrf->rnh_table[afi] = NULL;
route_table_finish(zvrf->import_check_table[afi]);
@@ -282,7 +267,6 @@ static int zebra_vrf_delete(struct vrf *vrf)
{
struct zebra_vrf *zvrf = vrf->info;
struct route_table *table;
- u_int32_t table_id;
afi_t afi;
safi_t safi;
unsigned i;
@@ -328,14 +312,6 @@ static int zebra_vrf_delete(struct vrf *vrf)
route_table_finish(table);
}
- for (table_id = 0; table_id < ZEBRA_KERNEL_TABLE_MAX; table_id++)
- if (zvrf->other_table[afi][table_id]) {
- table = zvrf->other_table[afi][table_id];
- table_info = table->info;
- route_table_finish(table);
- XFREE(MTYPE_RIB_TABLE_INFO, table_info);
- }
-
route_table_finish(zvrf->rnh_table[afi]);
route_table_finish(zvrf->import_check_table[afi]);
}
@@ -407,8 +383,8 @@ struct route_table *zebra_vrf_table_with_table_id(afi_t afi, safi_t safi,
return table;
}
-static void zebra_rtable_node_cleanup(struct route_table *table,
- struct route_node *node)
+void zebra_rtable_node_cleanup(struct route_table *table,
+ struct route_node *node)
{
struct route_entry *re, *next;
@@ -545,34 +521,20 @@ struct route_table *zebra_vrf_other_route_table(afi_t afi, u_int32_t table_id,
vrf_id_t vrf_id)
{
struct zebra_vrf *zvrf;
- rib_table_info_t *info;
- struct route_table *table;
+ struct zebra_ns *zns;
zvrf = vrf_info_lookup(vrf_id);
if (!zvrf)
return NULL;
- if (afi >= AFI_MAX)
- return NULL;
+ zns = zvrf->zns;
- if (table_id >= ZEBRA_KERNEL_TABLE_MAX)
+ if (afi >= AFI_MAX)
return NULL;
if ((vrf_id == VRF_DEFAULT) && (table_id != RT_TABLE_MAIN)
&& (table_id != zebrad.rtm_table_default)) {
- if (zvrf->other_table[afi][table_id] == NULL) {
- table = (afi == AFI_IP6) ? srcdest_table_init()
- : route_table_init();
- info = XCALLOC(MTYPE_RIB_TABLE_INFO, sizeof(*info));
- info->zvrf = zvrf;
- info->afi = afi;
- info->safi = SAFI_UNICAST;
- table->info = info;
- table->cleanup = zebra_rtable_node_cleanup;
- zvrf->other_table[afi][table_id] = table;
- }
-
- return (zvrf->other_table[afi][table_id]);
+ return zebra_ns_get_table(zns, zvrf, table_id, afi);
}
return zvrf->table[afi][SAFI_UNICAST];
@@ -598,7 +560,11 @@ static int vrf_config_write(struct vty *vty)
if (vrf_is_user_cfged(vrf)) {
vty_out(vty, "vrf %s\n", zvrf_name(zvrf));
if (zvrf->l3vni)
- vty_out(vty, " vni %u\n", zvrf->l3vni);
+ vty_out(vty, " vni %u%s\n",
+ zvrf->l3vni,
+ is_l3vni_for_prefix_routes_only(zvrf->l3vni) ?
+ " prefix-routes-only" :"");
+ zebra_ns_config_write(vty, (struct ns *)vrf->ns_ctxt);
vty_out(vty, "!\n");
}
@@ -615,8 +581,8 @@ static int vrf_config_write(struct vty *vty)
/* Zebra VRF initialization. */
void zebra_vrf_init(void)
{
- vrf_init(zebra_vrf_new, zebra_vrf_enable, zebra_vrf_disable,
- zebra_vrf_delete);
+ vrf_init(zebra_vrf_new, zebra_vrf_enable,
+ zebra_vrf_disable, zebra_vrf_delete);
vrf_cmd_init(vrf_config_write);
}
diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h
index d3a5316b9d..4c12d7dee9 100644
--- a/zebra/zebra_vrf.h
+++ b/zebra/zebra_vrf.h
@@ -19,8 +19,8 @@
* with this program; see the file COPYING; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#if !defined(__ZEBRA_RIB_H__)
-#define __ZEBRA_RIB_H__
+#if !defined(__ZEBRA_VRF_H__)
+#define __ZEBRA_VRF_H__
#include <zebra/zebra_ns.h>
#include <zebra/zebra_pw.h>
@@ -62,9 +62,6 @@ struct zebra_vrf {
/* Import check table (used mostly by BGP */
struct route_table *import_check_table[AFI_MAX];
- /* Routing tables off of main table for redistribute table */
- struct route_table *other_table[AFI_MAX][ZEBRA_KERNEL_TABLE_MAX];
-
/* 2nd pointer type used primarily to quell a warning on
* ALL_LIST_ELEMENTS_RO
*/
@@ -131,14 +128,28 @@ struct zebra_vrf {
static inline vrf_id_t zvrf_id(struct zebra_vrf *zvrf)
{
+ if (!zvrf || !zvrf->vrf)
+ return VRF_UNKNOWN;
return zvrf->vrf->vrf_id;
}
+static inline const char *zvrf_ns_name(struct zebra_vrf *zvrf)
+{
+ if (!zvrf->vrf || !zvrf->vrf->ns_ctxt)
+ return NULL;
+ return ns_get_name((struct ns *)zvrf->vrf->ns_ctxt);
+}
+
static inline const char *zvrf_name(struct zebra_vrf *zvrf)
{
return zvrf->vrf->name;
}
+static inline bool zvrf_is_active(struct zebra_vrf *zvrf)
+{
+ return zvrf->vrf->status & VRF_ACTIVE;
+}
+
struct route_table *zebra_vrf_table_with_table_id(afi_t afi, safi_t safi,
vrf_id_t vrf_id,
u_int32_t table_id);
@@ -154,4 +165,7 @@ extern struct route_table *
zebra_vrf_other_route_table(afi_t afi, u_int32_t table_id, vrf_id_t vrf_id);
extern int zebra_vrf_has_config(struct zebra_vrf *zvrf);
extern void zebra_vrf_init(void);
-#endif
+
+extern void zebra_rtable_node_cleanup(struct route_table *table,
+ struct route_node *node);
+#endif /* ZEBRA_VRF_H */
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
index 269244f768..4824c09f3d 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -1235,46 +1235,21 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,
}
}
-static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
- safi_t safi, bool use_fib, u_char use_json,
- route_tag_t tag,
- const struct prefix *longer_prefix_p,
- bool supernets_only, int type,
- u_short ospf_instance_id)
+static void do_show_route_helper(struct vty *vty, struct zebra_vrf *zvrf,
+ struct route_table *table, afi_t afi,
+ bool use_fib, route_tag_t tag,
+ const struct prefix *longer_prefix_p,
+ bool supernets_only, int type,
+ u_short ospf_instance_id, u_char use_json)
{
- struct route_table *table;
- rib_dest_t *dest;
struct route_node *rn;
struct route_entry *re;
int first = 1;
- struct zebra_vrf *zvrf = NULL;
- char buf[BUFSIZ];
+ rib_dest_t *dest;
json_object *json = NULL;
json_object *json_prefix = NULL;
- u_int32_t addr;
-
- if (!(zvrf = zebra_vrf_lookup_by_name(vrf_name))) {
- if (use_json)
- vty_out(vty, "{}\n");
- else
- vty_out(vty, "vrf %s not defined\n", vrf_name);
- return CMD_SUCCESS;
- }
-
- if (zvrf_id(zvrf) == VRF_UNKNOWN) {
- if (use_json)
- vty_out(vty, "{}\n");
- else
- vty_out(vty, "vrf %s inactive\n", vrf_name);
- return CMD_SUCCESS;
- }
-
- table = zebra_vrf_table(afi, safi, zvrf_id(zvrf));
- if (!table) {
- if (use_json)
- vty_out(vty, "{}\n");
- return CMD_SUCCESS;
- }
+ uint32_t addr;
+ char buf[BUFSIZ];
if (use_json)
json = json_object_new_object();
@@ -1352,6 +1327,67 @@ static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
json, JSON_C_TO_STRING_PRETTY));
json_object_free(json);
}
+}
+
+static int do_show_ip_route(struct vty *vty, const char *vrf_name, afi_t afi,
+ safi_t safi, bool use_fib, u_char use_json,
+ route_tag_t tag,
+ const struct prefix *longer_prefix_p,
+ bool supernets_only, int type,
+ u_short ospf_instance_id)
+{
+ struct route_table *table;
+ struct zebra_vrf *zvrf = NULL;
+
+ if (!(zvrf = zebra_vrf_lookup_by_name(vrf_name))) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "vrf %s not defined\n", vrf_name);
+ return CMD_SUCCESS;
+ }
+
+ if (zvrf_id(zvrf) == VRF_UNKNOWN) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "vrf %s inactive\n", vrf_name);
+ return CMD_SUCCESS;
+ }
+
+ table = zebra_vrf_table(afi, safi, zvrf_id(zvrf));
+ if (!table) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ return CMD_SUCCESS;
+ }
+
+ do_show_route_helper(vty, zvrf, table, afi, use_fib, tag,
+ longer_prefix_p, supernets_only, type,
+ ospf_instance_id, use_json);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (show_route_table,
+ show_route_table_cmd,
+ "show <ip$ipv4|ipv6$ipv6> route table (1-4294967295)$table [json$json]",
+ SHOW_STR
+ IP_STR
+ IP6_STR
+ "IP routing table\n"
+ "Table to display\n"
+ "The table number to display, if available\n"
+ JSON_STR)
+{
+ afi_t afi = ipv4 ? AFI_IP : AFI_IP6;
+ struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT);
+ struct route_table *t;
+
+ t = zebra_ns_find_table(zvrf->zns, table, afi);
+ if (t)
+ do_show_route_helper(vty, zvrf, t, afi, false, 0, false, false,
+ 0, 0, !!json);
return CMD_SUCCESS;
}
@@ -2338,8 +2374,12 @@ DEFUN (show_vrf,
continue;
vty_out(vty, "vrf %s ", zvrf_name(zvrf));
- if (zvrf_id(zvrf) == VRF_UNKNOWN)
+ if (zvrf_id(zvrf) == VRF_UNKNOWN
+ || !zvrf_is_active(zvrf))
vty_out(vty, "inactive");
+ else if (zvrf_ns_name(zvrf))
+ vty_out(vty, "id %u netns %s",
+ zvrf_id(zvrf), zvrf_ns_name(zvrf));
else
vty_out(vty, "id %u table %u", zvrf_id(zvrf),
zvrf->table_id);
@@ -2353,20 +2393,26 @@ DEFUN (show_vrf,
DEFUN (default_vrf_vni_mapping,
default_vrf_vni_mapping_cmd,
- "vni " CMD_VNI_RANGE,
+ "vni " CMD_VNI_RANGE "[prefix-routes-only]",
"VNI corresponding to the DEFAULT VRF\n"
- "VNI-ID\n")
+ "VNI-ID\n"
+ "Prefix routes only \n")
{
int ret = 0;
char err[ERR_STR_SZ];
struct zebra_vrf *zvrf = NULL;
vni_t vni = strtoul(argv[1]->arg, NULL, 10);
+ int filter = 0;
zvrf = vrf_info_lookup(VRF_DEFAULT);
if (!zvrf)
return CMD_WARNING;
- ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 1);
+ if (argc == 3)
+ filter = 1;
+
+ ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ,
+ filter, 1);
if (ret != 0) {
vty_out(vty, "%s\n", err);
return CMD_WARNING;
@@ -2391,7 +2437,7 @@ DEFUN (no_default_vrf_vni_mapping,
if (!zvrf)
return CMD_WARNING;
- ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0);
+ ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0, 0);
if (ret != 0) {
vty_out(vty, "%s\n", err);
return CMD_WARNING;
@@ -2402,11 +2448,13 @@ DEFUN (no_default_vrf_vni_mapping,
DEFUN (vrf_vni_mapping,
vrf_vni_mapping_cmd,
- "vni " CMD_VNI_RANGE,
+ "vni " CMD_VNI_RANGE "[prefix-routes-only]",
"VNI corresponding to tenant VRF\n"
- "VNI-ID\n")
+ "VNI-ID\n"
+ "prefix-routes-only\n")
{
int ret = 0;
+ int filter = 0;
ZEBRA_DECLVAR_CONTEXT(vrf, zvrf);
vni_t vni = strtoul(argv[1]->arg, NULL, 10);
@@ -2415,9 +2463,13 @@ DEFUN (vrf_vni_mapping,
assert(vrf);
assert(zvrf);
+ if (argc == 3)
+ filter = 1;
+
/* Mark as having FRR configuration */
vrf_set_user_cfged(vrf);
- ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 1);
+ ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ,
+ filter, 1);
if (ret != 0) {
vty_out(vty, "%s\n", err);
return CMD_WARNING;
@@ -2442,7 +2494,7 @@ DEFUN (no_vrf_vni_mapping,
assert(vrf);
assert(zvrf);
- ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0);
+ ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, 0, 0);
if (ret != 0) {
vty_out(vty, "%s\n", err);
return CMD_WARNING;
@@ -3341,6 +3393,7 @@ void zebra_vty_init(void)
install_element(VIEW_NODE, &show_vrf_cmd);
install_element(VIEW_NODE, &show_vrf_vni_cmd);
install_element(VIEW_NODE, &show_route_cmd);
+ install_element(VIEW_NODE, &show_route_table_cmd);
install_element(VIEW_NODE, &show_route_detail_cmd);
install_element(VIEW_NODE, &show_route_summary_cmd);
install_element(VIEW_NODE, &show_ip_nht_cmd);
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index c9cc556a44..20b9e94288 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -928,6 +928,9 @@ static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx)
zl3vni_svi_if_name(zl3vni));
vty_out(vty, " State: %s\n",
zl3vni_state2str(zl3vni));
+ vty_out(vty, " VNI Filter: %s\n",
+ CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) ?
+ "prefix-routes-only" : "none");
vty_out(vty, " Router MAC: %s\n",
zl3vni_rmac2str(zl3vni, buf, sizeof(buf)));
vty_out(vty, " L2 VNIs: ");
@@ -951,6 +954,10 @@ static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx)
json_object_string_add(json, "routerMac",
zl3vni_rmac2str(zl3vni, buf,
sizeof(buf)));
+ json_object_string_add(json, "vniFilter",
+ CHECK_FLAG(zl3vni->filter,
+ PREFIX_ROUTES_ONLY) ?
+ "prefix-routes-only" : "none");
for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni)) {
json_object_array_add(json_vni_list,
json_object_new_int(zvni->vni));
@@ -3613,15 +3620,19 @@ static int zl3vni_send_add_to_client(zebra_l3vni_t *zl3vni)
stream_putl(s, zl3vni->vni);
stream_put(s, &rmac, sizeof(struct ethaddr));
stream_put_in_addr(s, &zl3vni->local_vtep_ip);
+ stream_put(s, &zl3vni->filter, sizeof(int));
/* Write packet size. */
stream_putw_at(s, 0, stream_get_endp(s));
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("Send L3_VNI_ADD %u VRF %s RMAC %s local-ip %s to %s",
+ zlog_debug(
+ "Send L3_VNI_ADD %u VRF %s RMAC %s local-ip %s filter %s to %s",
zl3vni->vni, vrf_id_to_name(zl3vni_vrf_id(zl3vni)),
prefix_mac2str(&rmac, buf, sizeof(buf)),
inet_ntoa(zl3vni->local_vtep_ip),
+ CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) ?
+ "prefix-routes-only" : "none",
zebra_route_string(client->proto));
client->l3vniadd_cnt++;
@@ -3666,10 +3677,6 @@ static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni)
if (!zl3vni)
return;
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("L3-VNI %u is UP - send add to BGP",
- zl3vni->vni);
-
/* send l3vni add to BGP */
zl3vni_send_add_to_client(zl3vni);
}
@@ -3679,10 +3686,6 @@ static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni)
if (!zl3vni)
return;
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("L3-VNI %u is Down - Send del to BGP",
- zl3vni->vni);
-
/* send l3-vni del to BGP*/
zl3vni_send_del_to_client(zl3vni);
}
@@ -3835,6 +3838,17 @@ static int zebra_vxlan_readd_remote_rmac(zebra_l3vni_t *zl3vni,
/* Public functions */
+int is_l3vni_for_prefix_routes_only(vni_t vni)
+{
+ zebra_l3vni_t *zl3vni = NULL;
+
+ zl3vni = zl3vni_lookup(vni);
+ if (!zl3vni)
+ return 0;
+
+ return CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) ? 1 : 0;
+}
+
/* handle evpn route in vrf table */
void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id,
struct ethaddr *rmac,
@@ -6432,7 +6446,7 @@ int zebra_vxlan_if_add(struct interface *ifp)
int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf,
vni_t vni,
char *err, int err_str_sz,
- int add)
+ int filter, int add)
{
zebra_l3vni_t *zl3vni = NULL;
struct zebra_vrf *zvrf_default = NULL;
@@ -6477,6 +6491,12 @@ int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf,
/* associate the vrf with vni */
zvrf->l3vni = vni;
+ /* set the filter in l3vni to denote if we are using l3vni only
+ * for prefix routes
+ */
+ if (filter)
+ SET_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY);
+
/* associate with vxlan-intf;
* we need to associate with the vxlan-intf first
*/
diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h
index d9801a8b60..df9b07db60 100644
--- a/zebra/zebra_vxlan.h
+++ b/zebra/zebra_vxlan.h
@@ -52,6 +52,7 @@ is_evpn_enabled()
#define VNI_STR_LEN 32
+extern int is_l3vni_for_prefix_routes_only(vni_t vni);
extern ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id);
extern int zebra_vxlan_vrf_delete(struct zebra_vrf *zvrf);
extern int zebra_vxlan_vrf_enable(struct zebra_vrf *zvrf);
@@ -154,7 +155,7 @@ extern int zebra_vxlan_advertise_all_vni(struct zserv *client,
struct zebra_vrf *zvrf);
extern int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni,
char *err,
- int err_str_sz, int add);
+ int err_str_sz, int filter, int add);
extern void zebra_vxlan_init_tables(struct zebra_vrf *zvrf);
extern void zebra_vxlan_close_tables(struct zebra_vrf *);
extern void zebra_vxlan_cleanup_tables(struct zebra_vrf *);
diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h
index 8d34b3e2f1..e8de25cefd 100644
--- a/zebra/zebra_vxlan_private.h
+++ b/zebra/zebra_vxlan_private.h
@@ -101,6 +101,9 @@ struct zebra_l3vni_t_ {
/* vrf_id */
vrf_id_t vrf_id;
+ uint32_t filter;
+#define PREFIX_ROUTES_ONLY (1 << 0) /* l3-vni used for prefix routes only */
+
/* Local IP */
struct in_addr local_vtep_ip;
diff --git a/zebra/zserv.c b/zebra/zserv.c
index b3b1fa79e9..98cb54f7b8 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -20,6 +20,8 @@
#include <zebra.h>
#include <sys/un.h>
+/* for basename */
+#include <libgen.h>
#include "prefix.h"
#include "command.h"
@@ -182,13 +184,19 @@ static void zserv_encode_interface(struct stream *s, struct interface *ifp)
static void zserv_encode_vrf(struct stream *s, struct zebra_vrf *zvrf)
{
struct vrf_data data;
+ const char *netns_name = zvrf_ns_name(zvrf);
data.l.table_id = zvrf->table_id;
- /* Pass the tableid */
+
+ if (netns_name)
+ strlcpy(data.l.netns_name,
+ basename((char *)netns_name), NS_NAMSIZ);
+ else
+ memset(data.l.netns_name, 0, NS_NAMSIZ);
+ /* Pass the tableid and the netns NAME */
stream_put(s, &data, sizeof(struct vrf_data));
/* Interface information. */
stream_put(s, zvrf_name(zvrf), VRF_NAMSIZ);
-
/* Write packet size. */
stream_putw_at(s, 0, stream_get_endp(s));
}
@@ -978,29 +986,38 @@ static int zsend_ipv4_nexthop_lookup_mrib(struct zserv *client,
return zebra_server_send_message(client);
}
-int zsend_route_notify_owner(u_char proto, u_short instance,
- vrf_id_t vrf_id, struct prefix *p,
+int zsend_route_notify_owner(struct route_entry *re, struct prefix *p,
enum zapi_route_notify_owner note)
{
struct zserv *client;
struct stream *s;
uint8_t blen;
- client = zebra_find_client(proto, instance);
+ client = zebra_find_client(re->type, re->instance);
if (!client || !client->notify_owner) {
if (IS_ZEBRA_DEBUG_PACKET) {
char buff[PREFIX_STRLEN];
- zlog_debug("Not Notifying Owner: %u about prefix %s",
- proto, prefix2str(p, buff, sizeof(buff)));
+ zlog_debug(
+ "Not Notifying Owner: %u about prefix %s(%u) %d",
+ re->type, prefix2str(p, buff, sizeof(buff)),
+ re->table, note);
}
return 0;
}
+ if (IS_ZEBRA_DEBUG_PACKET) {
+ char buff[PREFIX_STRLEN];
+
+ zlog_debug("Notifying Owner: %u about prefix %s(%u) %d",
+ re->type, prefix2str(p, buff, sizeof(buff)),
+ re->table, note);
+ }
+
s = client->obuf;
stream_reset(s);
- zclient_create_header(s, ZEBRA_ROUTE_NOTIFY_OWNER, vrf_id);
+ zclient_create_header(s, ZEBRA_ROUTE_NOTIFY_OWNER, re->vrf_id);
stream_put(s, &note, sizeof(note));
@@ -1010,6 +1027,8 @@ int zsend_route_notify_owner(u_char proto, u_short instance,
stream_putc(s, p->prefixlen);
stream_put(s, &p->u.prefix, blen);
+ stream_putl(s, re->table);
+
stream_putw_at(s, 0, stream_get_endp(s));
return zebra_server_send_message(client);
diff --git a/zebra/zserv.h b/zebra/zserv.h
index 7d5f6b4543..2044816227 100644
--- a/zebra/zserv.h
+++ b/zebra/zserv.h
@@ -175,8 +175,7 @@ extern int zsend_interface_vrf_update(struct zserv *, struct interface *,
extern int zsend_interface_link_params(struct zserv *, struct interface *);
extern int zsend_pw_update(struct zserv *, struct zebra_pw *);
-extern int zsend_route_notify_owner(u_char proto, u_short instance,
- vrf_id_t vrf_id, struct prefix *p,
+extern int zsend_route_notify_owner(struct route_entry *re, struct prefix *p,
enum zapi_route_notify_owner note);
extern void zserv_nexthop_num_warn(const char *, const struct prefix *,