}
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);
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. */
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,
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;
/* 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);
&& !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);
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 */
/* 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);
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);
}
/* 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))
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,
#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;
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)
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)
case ECOMMUNITY_ENCODE_AS:
eas.as = (*pnt++ << 8);
eas.as |= (*pnt++);
- pnt = ptr_get_be32(pnt, &eas.val);
+ ptr_get_be32(pnt, &eas.val);
snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val);
case ECOMMUNITY_ENCODE_AS:
eas.as = (*pnt++ << 8);
eas.as |= (*pnt++);
- pnt = ptr_get_be32(pnt, &eas.val);
+ ptr_get_be32(pnt, &eas.val);
snprintf(rt_buf, RT_ADDRSTRLEN, "%u:%u", eas.as, eas.val);
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))
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,
/* 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,
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)
}
/* if calculated next update for this peer < current delay, use it */
- if (next_update->tv_sec <= 0 || timercmp(&diff, next_update, <))
+ if (next_update->tv_sec < 0 || timercmp(&diff, next_update, <))
*next_update = diff;
}
#include "vrf.h"
#include "bfd.h"
#include "libfrr.h"
+#include "ns.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_attr.h"
#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"
/* 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);
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);
return 0;
}
+/*
+ * 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
*
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;
#include "queue.h"
#include "hash.h"
#include "filter.h"
+#include "ns.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_open.h"
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;
};
/*
return -1;
}
listener->thread = NULL;
+
thread_add_read(bm->master, bgp_accept, listener, accept_sock,
&listener->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",
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() */
}
/* 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.
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,
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;
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;
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;
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,
}
/* 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;
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;
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;
* 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
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;
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);
#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 *);
return CMD_WARNING_CONFIG_FAILED;
}
- ret = peer_group_unbind(bgp, peer, group);
+ ret = peer_delete(peer);
return bgp_vty_return(vty, ret);
}
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;
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);
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)
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;
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)) {
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);
* 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))
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);
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);
#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;
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. */
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);
doc/eigrpd.8
doc/ripngd.8
doc/pimd.8
+ doc/mtracebis.8
doc/nhrpd.8
doc/vtysh.1
doc/watchfrr.8
etc/frr/
usr/bin/vtysh
+usr/bin/mtracebis
usr/include/frr/
usr/lib/
tools/frr etc/init.d/
usr/share/man/man8/zebra.8
usr/share/man/man8/isisd.8
usr/share/man/man8/watchfrr.8
+usr/share/man/man8/mtracebis.8
usr/share/snmp/mibs/
tools/etc/* etc/
tools/*.service lib/systemd/system
etc/frr/
usr/bin/vtysh
+usr/bin/mtracebis
usr/include/frr/
usr/lib/
tools/frr usr/lib/frr
usr/share/man/man8/isisd.8
usr/share/man/man8/watchfrr.8
usr/share/man/man8/frr-args.8
+usr/share/man/man8/mtracebis.8
usr/share/snmp/mibs/
tools/etc/* etc/
tools/*.service lib/systemd/system
sudo yum install git autoconf automake libtool make gawk \
readline-devel texinfo net-snmp-devel groff pkgconfig \
json-c-devel pam-devel bison flex pytest c-ares-devel \
- perl-XML-LibXML python-devel
+ perl-XML-LibXML python-devel systemd-devel
Get FRR, compile it and install it (from Git)
---------------------------------------------
--enable-group=frr \
--enable-vty-group=frrvt \
--enable-rtadv \
+ --enable-systemd=yes \
--disable-exampledir \
--enable-watchfrr \
--disable-ldpd \
if PIMD
man_MANS += pimd.8
+man_MANS += mtracebis.8
endif
if BGPD
ripd.8.in \
ripngd.8.in \
pimd.8.in \
+ mtracebis.8.in \
nhrpd.8.in \
vtysh.1.in \
watchfrr.8.in \
--- /dev/null
+.\" This file was originally generated by help2man 1.44.1.
+.TH MTRACEBIS "8" "February 2018" "mtracebis 0.1" "System Administration Utilities"
+.SH NAME
+mtracebis \- a program to initiate multicast traceroute "mtrace" queries
+.SH SYNOPSIS
+mtracebis
+<multicast source>
+.SH DESCRIPTION
+.B mtracebis
+is a program used to test multicast connectivity in a multicast and multicast
+traceroute enabled IP network.
+.PP
+Initial version of the program requires multicast source IP address and
+initiates a weak traceroute across the network. This tests whether the
+interfaces towards the source are multicast enabled. First query sent is a
+full query, capable of crossing the network all the way to the source. If this
+fails, hop-by-hop queries are initiated.
+.PP
+Hop-by-hop queries start by requesting only a response from the nearest router.
+Following that, next query is extended to the next two routers, and so on...
+until a set of routers is tested for connectivity.
+.SH SEE ALSO
+See the project homepage at <@PACKAGE_URL@>.
+.SH AUTHORS
+Copyright 2018 Mladen Sablic
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
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, ¬e))
+ if (!zapi_route_notify_decode(zclient->ibuf, &p, &table, ¬e))
return -1;
return 0;
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;
}
+++ /dev/null
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import sys, os
-import subprocess, argparse, tempfile
-import indent
-
-def run(cmd):
- proc = subprocess.Popen(cmd, stdout = subprocess.PIPE)
- rv = proc.communicate('')[0].decode('UTF-8')
- proc.wait()
- return rv
-
-clangfmt = run(['git', 'show', 'master:.clang-format'])
-
-argp = argparse.ArgumentParser(description = 'git whitespace-fixing tool')
-argp.add_argument('branch', metavar='BRANCH', type = str, nargs = '?', default = 'HEAD')
-args = argp.parse_args()
-
-branch = args.branch
-commit = run(['git', 'rev-list', '-n', '1', branch, '--']).strip()
-
-# frr-3.1-dev = first commit that is on master but not on stable/3.0
-masterid = run(['git', 'rev-list', '-n', '1', 'frr-3.1-dev', '--']).strip()
-masterbase = run(['git', 'merge-base', commit, masterid]).strip()
-
-if masterbase == masterid:
- refbranch = 'master'
-else:
- refbranch = '3.0'
-
-sys.stderr.write('autodetected base: %s (can be 3.0 or master)\n' % refbranch)
-
-beforeid = run(['git', 'rev-list', '-n', '1', 'reindent-%s-before' % refbranch, '--']).strip()
-afterid = run(['git', 'rev-list', '-n', '1', 'reindent-%s-after' % refbranch, '--']).strip()
-
-beforebase = run(['git', 'merge-base', commit, beforeid]).strip()
-afterbase = run(['git', 'merge-base', commit, afterid]).strip()
-
-if afterbase == afterid:
- sys.stderr.write('this branch was already rebased\n')
- sys.exit(1)
-
-if beforebase != beforeid:
- sys.stderr.write('you need to rebase your branch onto the tag "reindent-%s-before"\n' % refbranch)
- sys.exit(1)
-
-revs = run(['git', 'rev-list', 'reindent-%s-before..%s' % (refbranch, commit)]).strip().split('\n')
-revs.reverse()
-
-srcdir = os.getcwd()
-tmpdir = tempfile.mkdtemp('frrindent')
-os.chdir(tmpdir)
-
-sys.stderr.write('using temporary directory %s; %d revisions\n' % (tmpdir, len(revs)))
-run(['git', 'clone', '-s', '-b', 'reindent-%s-after' % refbranch, srcdir, 'repo'])
-os.chdir('repo')
-
-with open('.clang-format', 'w') as fd:
- fd.write(clangfmt)
-
-prev = beforeid
-for rev in revs:
- filestat = run(['git', 'diff', '-z', '--name-status', prev, rev]).rstrip('\0').split('\0')
- changes = zip(filestat[0::2], filestat[1::2])
- sys.stderr.write('%s: %d files\n' % (rev, len(changes)))
-
- for typ, name in changes:
- if typ == 'D':
- run(['git', 'rm', name])
- elif typ in ['A', 'M']:
- run(['git', 'checkout', rev, '--', name])
- if name.endswith('.c') or name.endswith('.h'):
- for d in ['babeld/', 'ldpd/', 'nhrpd/']:
- if name.startswith(d):
- break
- else:
- sys.stderr.write('\t%s\n' % name)
- indent.wrap_file(name)
- run(['git', 'add', name])
-
- run(['git', 'commit', '-C', rev])
- prev = rev
-
-run(['git', 'push', 'origin', 'HEAD:refs/heads/reindented-branch'])
-sys.stderr.write('\n\n"reindented-branch" should now be OK.\n')
-sys.stderr.write('you could use "git reset --hard reindented-branch" to set your current branch to the reindented output\n')
-sys.stderr.write('\033[31;1mplease always double-check the output\033[m\n')
-
--- /dev/null
+/* 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_ */
include/linux/neighbour.h \
include/linux/rtnetlink.h \
include/linux/socket.h \
+ include/linux/net_namespace.h \
# end
+++ /dev/null
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# 2017 by David Lamparter, placed in public domain
-
-import sys, re, subprocess, os
-
-# find all DEFUNs
-defun_re = re.compile(
- r'^((DEF(UN(_NOSH|_HIDDEN)?|PY)|ALIAS)\s*\(.*?)^(?=\s*\{)',
- re.M | re.S)
-define_re = re.compile(
- r'((^#\s*define[^\n]+[^\\]\n)+)',
- re.M | re.S)
-# find clang-format control that we just inserted
-clean_re = re.compile(
- r'^/\* \$FRR indent\$ \*/\s*\n\s*/\* clang-format (on|off) \*/\s*\n',
- re.M)
-
-def wrap_file(fn):
- with open(fn, 'r') as fd:
- text = fd.read()
-
- repl = r'/* $FRR indent$ */\n/* clang-format off */\n' + \
- r'\1' + \
- r'/* $FRR indent$ */\n/* clang-format on */\n'
-
- # around each DEFUN, insert an indent-on/off comment
- text = defun_re.sub(repl, text)
- text = define_re.sub(repl, text)
-
- ci = subprocess.Popen(['clang-format'], stdin = subprocess.PIPE, stdout = subprocess.PIPE)
- stdout, ign = ci.communicate(text)
- ci.wait()
- if ci.returncode != 0:
- raise IOError('clang-format returned %d' % (ci.returncode))
-
- # remove the bits we inserted above
- final = clean_re.sub('', stdout)
-
- tmpname = fn + '.indent'
- with open(tmpname, 'w') as ofd:
- ofd.write(final)
- os.rename(tmpname, fn)
-
-if __name__ == '__main__':
- for fn in sys.argv[1:]:
- wrap_file(fn)
struct isis_area *area = circuit->area;
bool change = circuit->ip_router != ip_router
|| circuit->ipv6_router != ipv6_router;
- bool was_enabled = !!circuit->area;
area->ip_circuits += ip_router - circuit->ip_router;
area->ipv6_circuits += ipv6_router - circuit->ipv6_router;
if (!ip_router && !ipv6_router)
isis_csm_state_change(ISIS_DISABLE, circuit, area);
- else if (!was_enabled)
- isis_csm_state_change(ISIS_ENABLE, circuit, area);
else
lsp_regenerate_schedule(circuit->area, circuit->is_type, 0);
}
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) {
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);
}
{
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
{
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);
}
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);
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);
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);
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);
}
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)
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);
}
"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,
break;
case INTERFACE_NODE:
case PW_NODE:
- case NS_NODE:
+ case LOGICALROUTER_NODE:
case VRF_NODE:
case ZEBRA_NODE:
case BGP_NODE:
case CONFIG_NODE:
case INTERFACE_NODE:
case PW_NODE:
- case NS_NODE:
+ case LOGICALROUTER_NODE:
case VRF_NODE:
case ZEBRA_NODE:
case RIP_NODE:
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. */
{
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)
rn = route_node_lookup (ifaddr_ipv4_table, (struct prefix *) &p);
if (! rn)
return NULL;
-
+
ifp = rn->info;
route_unlock_node (rn);
return ifp;
{
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);
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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*/
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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 */
+++ /dev/null
-/*
- * 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;
-}
#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;
/* Master list of interfaces belonging to this NS */
struct list *iflist;
+ /* Back Pointer to VRF */
+ void *vrf_ctxt;
+
/* User data */
void *info;
};
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
*/
* - 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*/
#define IPV4_NET127(a) ((((u_int32_t) (a)) & 0xff000000) == 0x7f000000)
#define IPV4_LINKLOCAL(a) ((((u_int32_t) (a)) & 0xffff0000) == 0xa9fe0000)
#define IPV4_CLASS_DE(a) ((((u_int32_t) (a)) & 0xe0000000) == 0xe0000000)
+#define IPV4_MC_LINKLOCAL(a) ((((u_int32_t) (a)) & 0xffffff00) == 0xe0000000)
/* Max bit/byte length of IPv6 address. */
#define IPV6_MAX_BYTELEN 16
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 \
lib/wheel.c \
lib/workqueue.c \
lib/zclient.c \
+ lib/logicalrouter.c \
# end
lib/plist_clippy.c: $(CLIPPY_DEPS)
lib/zassert.h \
lib/zclient.h \
lib/zebra.h \
+ lib/logicalrouter.h \
# end
nodist_pkginclude_HEADERS += \
#include <zebra.h>
+/* for basename */
+#include <libgen.h>
+
#include "if.h"
#include "vrf.h"
#include "vrf_int.h"
#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")
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.
};
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)
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.
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)
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;
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);
* 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;
{
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__);
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);
}
/* 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",
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
*/
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;
}
#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. */
#endif
#define VRF_NAMSIZ 36
+#define NS_NAMSIZ 16
#define VRF_DEFAULT_NAME "Default-IP-Routing-Table"
union {
struct {
uint32_t table_id;
+ char netns_name[NS_NAMSIZ];
} l;
};
};
/* 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);
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;
*/
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*/
}
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;
/* 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);
}
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;
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. */
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,
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;
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,
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 */
static void ospf_opaque_funclist_term(void);
static void free_opaque_info_per_type(void *val);
static void free_opaque_info_per_id(void *val);
+static void free_opaque_info_owner(void *val);
static int ospf_opaque_lsa_install_hook(struct ospf_lsa *lsa);
static int ospf_opaque_lsa_delete_hook(struct ospf_lsa *lsa);
if (functab->opaque_type == opaque_type) {
/* Cleanup internal control information, if it
* still remains. */
- if (functab->oipt != NULL)
+ if (functab->oipt != NULL) {
free_opaque_info_per_type(
functab->oipt);
+ free_opaque_info_owner(functab->oipt);
+ }
/* Dequeue listnode entry from the list. */
listnode_delete(funclist, functab);
top = ospf_lookup_by_vrf_id(new->vrf_id);
if (new->area != NULL && (top = new->area->ospf) == NULL) {
free_opaque_info_per_type((void *)oipt);
+ free_opaque_info_owner(oipt);
oipt = NULL;
goto out; /* This case may not exist. */
}
"register_opaque_info_per_type: Unexpected LSA-type(%u)",
new->data->type);
free_opaque_info_per_type((void *)oipt);
+ free_opaque_info_owner(oipt);
oipt = NULL;
goto out; /* This case may not exist. */
}
return oipt;
}
+/* Remove "oipt" from its owner's self-originated LSA list. */
+static void free_opaque_info_owner(void *val)
+{
+ struct opaque_info_per_type *oipt = (struct opaque_info_per_type *)val;
+
+ switch (oipt->lsa_type) {
+ case OSPF_OPAQUE_LINK_LSA: {
+ struct ospf_interface *oi =
+ (struct ospf_interface *)(oipt->owner);
+ listnode_delete(oi->opaque_lsa_self, oipt);
+ break;
+ }
+ case OSPF_OPAQUE_AREA_LSA: {
+ struct ospf_area *area = (struct ospf_area *)(oipt->owner);
+ listnode_delete(area->opaque_lsa_self, oipt);
+ break;
+ }
+ case OSPF_OPAQUE_AS_LSA: {
+ struct ospf *top = (struct ospf *)(oipt->owner);
+ listnode_delete(top->opaque_lsa_self, oipt);
+ break;
+ }
+ default:
+ zlog_warn("free_opaque_info_owner: Unexpected LSA-type(%u)",
+ oipt->lsa_type);
+ break; /* This case may not exist. */
+ }
+}
+
static void free_opaque_info_per_type(void *val)
{
struct opaque_info_per_type *oipt = (struct opaque_info_per_type *)val;
zlog_debug("listcount = %d, [%s]dst %s", listcount(update),
IF_NAME(oi), inet_ntoa(addr));
+ /* Check that we have really something to process */
+ if (listcount(update) == 0)
+ return;
+
op = ospf_ls_upd_packet_new(update, oi);
/* Prepare OSPF common header. */
};
struct ospf_metric {
+ enum { metric_increment, metric_decrement, metric_absolute } type;
bool used;
u_int32_t metric;
};
ei = object;
/* Set metric out value. */
- if (metric->used)
+ if (!metric->used)
+ return RMAP_OKAY;
+ if (metric->type == metric_increment)
+ ei->route_map_set.metric += metric->metric;
+ if (metric->type == metric_decrement)
+ ei->route_map_set.metric -= metric->metric;
+ if (metric->type == metric_absolute)
ei->route_map_set.metric = metric->metric;
+
+ if ((signed int)ei->route_map_set.metric < 1)
+ ei->route_map_set.metric = -1;
+ if (ei->route_map_set.metric > OSPF_LS_INFINITY)
+ ei->route_map_set.metric = OSPF_LS_INFINITY;
}
return RMAP_OKAY;
}
metric = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(u_int32_t));
metric->used = false;
- /* OSPF doesn't support the +/- in
- set metric <+/-metric> check
- Ignore the +/- component */
- if (!all_digit(arg)) {
- if ((arg[0] == '+' || arg[0] == '-') && all_digit(arg + 1)) {
- zlog_warn("OSPF does not support 'set metric +/-'");
- arg++;
- } else {
- if (strmatch(arg, "+rtt") || strmatch(arg, "-rtt"))
- zlog_warn(
- "OSPF does not support 'set metric +rtt / -rtt'");
-
- return metric;
- }
+ if (all_digit(arg))
+ metric->type = metric_absolute;
+
+ if (strmatch(arg, "+rtt") || strmatch(arg, "-rtt")) {
+ zlog_warn("OSPF does not support 'set metric +rtt / -rtt'");
+ return metric;
+ }
+
+ if ((arg[0] == '+') && all_digit(arg + 1)) {
+ metric->type = metric_increment;
+ arg++;
}
+
+ if ((arg[0] == '-') && all_digit(arg + 1)) {
+ metric->type = metric_decrement;
+ arg++;
+ }
+
metric->metric = strtoul(arg, NULL, 10);
- metric->used = true;
+
+ if (metric->metric)
+ metric->used = true;
return metric;
}
{
struct interface *ifp;
struct vrf *vrf = vrf_lookup_by_id(ospf->vrf_id);
- json_object *json_vrf = NULL, *json_intf_array = NULL;
+ json_object *json_vrf = NULL;
json_object *json_interface_sub = NULL, *json_interface = NULL;
if (use_json) {
json_vrf = json_object_new_object();
else
json_vrf = json;
- json_intf_array = json_object_new_array();
+ json_interface = json_object_new_object();
}
if (ospf->instance) {
ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf);
if (intf_name == NULL) {
- if (use_json)
- json_object_object_add(json_vrf, "interfaces",
- json_intf_array);
/* Show All Interfaces.*/
FOR_ALL_INTERFACES (vrf, ifp) {
if (ospf_oi_count(ifp)) {
if (use_json) {
- json_interface =
- json_object_new_object();
json_interface_sub =
json_object_new_object();
}
use_json);
if (use_json) {
- json_object_array_add(json_intf_array,
- json_interface);
json_object_object_add(
json_interface, ifp->name,
json_interface_sub);
}
}
}
+ if (use_json)
+ json_object_object_add(json_vrf, "interfaces",
+ json_interface);
} else {
/* Interface name is specified. */
ifp = if_lookup_by_name(intf_name, ospf->vrf_id);
if (use_json) {
json_interface_sub = json_object_new_object();
json_interface = json_object_new_object();
- json_object_object_add(json_vrf, "interfaces",
- json_intf_array);
}
show_ip_ospf_interface_sub(
vty, ospf, ifp, json_interface_sub, use_json);
if (use_json) {
- json_object_array_add(json_intf_array,
- json_interface);
json_object_object_add(json_interface,
ifp->name,
json_interface_sub);
+ json_object_object_add(json_vrf, "interfaces",
+ json_interface);
}
}
}
{
struct ospf_interface *oi;
struct listnode *node;
- json_object *json_vrf = NULL, *json_nbr_array = NULL;
+ json_object *json_vrf = NULL;
json_object *json_nbr_sub = NULL;
if (use_json) {
json_vrf = json_object_new_object();
else
json_vrf = json;
- json_nbr_array = json_object_new_array();
+ json_nbr_sub = json_object_new_object();
}
if (ospf->instance) {
ospf_show_vrf_name(ospf, vty, json_vrf, use_vrf);
if (!use_json)
show_ip_ospf_neighbour_header(vty);
- else
- json_object_object_add(json_vrf, "neighbors",
- json_nbr_array);
for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) {
if (ospf_interface_neighbor_count(oi) == 0)
continue;
- if (use_json) {
- json_nbr_sub = json_object_new_object();
- json_object_array_add(json_nbr_array, json_nbr_sub);
- }
show_ip_ospf_neighbor_sub(vty, oi, json_nbr_sub, use_json);
}
if (use_json) {
+ json_object_object_add(json_vrf, "neighbors",
+ json_nbr_sub);
if (use_vrf) {
if (ospf->vrf_id == VRF_DEFAULT)
json_object_object_add(json, "default",
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",
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;
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;
}
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",
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);
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. */
Makefile.in
libpim.a
pimd
+mtracebis
test_igmpv3_join
tags
TAGS
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-
clear ip pim interfaces Reset PIM interfaces
clear ip pim oil Rescan PIM OIL (output interface list)
debug igmp IGMP protocol activity
+ debug mtrace Mtrace protocol activity
debug mroute PIM interaction with kernel MFC cache
debug pim PIM protocol activity
debug pim zebra ZEBRA protocol activity
pimd:
show memory pim PIM memory statistics
+vtysh:
+ mtrace Multicast traceroute
-x-
--- /dev/null
+/*
+ * Multicast Traceroute for FRRouting
+ * Copyright (C) 2018 Mladen Sablic
+ *
+ * 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
+ */
+
+#ifdef __linux__
+
+#include "pim_igmp_mtrace.h"
+
+#include "checksum.h"
+#include "mtracebis_routeget.h"
+
+#include <sys/select.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <net/if.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#define MTRACEBIS_VERSION "0.1"
+#define MTRACE_TIMEOUT (5)
+
+#define IP_HDR_LEN (sizeof(struct ip))
+#define IP_RA_LEN (4)
+#define MTRACE_BUF_LEN (MTRACE_HDR_SIZE + (MTRACE_MAX_HOPS * MTRACE_RSP_SIZE))
+#define IP_AND_MTRACE_BUF_LEN (IP_HDR_LEN + IP_RA_LEN + MTRACE_BUF_LEN)
+
+static const char *progname;
+static void usage(void)
+{
+ fprintf(stderr, "Usage : %s <multicast source>\n", progname);
+}
+static void version(void)
+{
+ fprintf(stderr, "%s %s\n", progname, MTRACEBIS_VERSION);
+}
+
+static int send_query(int fd, struct in_addr to_addr,
+ struct igmp_mtrace *mtrace)
+{
+ struct sockaddr_in to;
+ socklen_t tolen;
+ int sent;
+
+ memset(&to, 0, sizeof(to));
+ to.sin_family = AF_INET;
+ to.sin_addr = to_addr;
+ tolen = sizeof(to);
+
+ sent = sendto(fd, (char *)mtrace, sizeof(*mtrace), MSG_DONTWAIT,
+ (struct sockaddr *)&to, tolen);
+
+ if (sent < 1)
+ return -1;
+ return 0;
+}
+
+static void print_query(struct igmp_mtrace *mtrace)
+{
+ char src_str[INET_ADDRSTRLEN];
+ char dst_str[INET_ADDRSTRLEN];
+ char grp_str[INET_ADDRSTRLEN];
+
+ printf("* Mtrace from %s to %s via group %s\n",
+ inet_ntop(AF_INET, &mtrace->src_addr, src_str, sizeof(src_str)),
+ inet_ntop(AF_INET, &mtrace->dst_addr, dst_str, sizeof(dst_str)),
+ inet_ntop(AF_INET, &mtrace->grp_addr, grp_str, sizeof(grp_str)));
+}
+
+static int recv_response(int fd, long msec, int *hops)
+{
+ int recvd;
+ char mtrace_buf[IP_AND_MTRACE_BUF_LEN];
+ struct ip *ip;
+ struct igmp_mtrace *mtrace;
+ int mtrace_len;
+ int responses;
+ int i;
+ u_short sum;
+
+ recvd = recvfrom(fd, mtrace_buf, IP_AND_MTRACE_BUF_LEN, 0, NULL, 0);
+
+ if (recvd < 1) {
+ fprintf(stderr, "recvfrom error: %s\n", strerror(errno));
+ return -1;
+ }
+
+ if (recvd < (int)sizeof(struct ip)) {
+ fprintf(stderr, "no ip header\n");
+ return -1;
+ }
+
+ ip = (struct ip *)mtrace_buf;
+
+ if (ip->ip_v != 4) {
+ fprintf(stderr, "IP not version 4\n");
+ return -1;
+ }
+
+ sum = ip->ip_sum;
+ ip->ip_sum = 0;
+
+ if (sum != in_cksum(ip, ip->ip_hl * 4))
+ return -1;
+
+ mtrace = (struct igmp_mtrace *)(mtrace_buf + (4 * ip->ip_hl));
+
+ mtrace_len = ntohs(ip->ip_len) - ip->ip_hl * 4;
+
+ if (mtrace_len < (int)MTRACE_HDR_SIZE)
+ return -1;
+
+ sum = mtrace->checksum;
+ mtrace->checksum = 0;
+ if (sum != in_cksum(mtrace, mtrace_len)) {
+ fprintf(stderr, "mtrace checksum wrong\n");
+ return -1;
+ }
+
+ if (mtrace->type != PIM_IGMP_MTRACE_RESPONSE)
+ return -1;
+
+
+ responses = mtrace_len - sizeof(struct igmp_mtrace);
+ responses /= sizeof(struct igmp_mtrace_rsp);
+
+ printf("%ld ms received responses from %d hops.\n", msec, responses);
+
+ if (hops)
+ *hops = responses;
+
+ for (i = 0; i < responses; i++) {
+ struct igmp_mtrace_rsp *rsp = &mtrace->rsp[i];
+
+ if (rsp->fwd_code != 0)
+ printf("-%d fwd. code 0x%2x.\n", i, rsp->fwd_code);
+ }
+
+ return 0;
+}
+
+static int wait_for_response(int fd, int *hops)
+{
+ fd_set readfds;
+ struct timeval timeout;
+ int ret = -1;
+ long msec, rmsec, tmsec;
+
+ FD_ZERO(&readfds);
+ FD_SET(fd, &readfds);
+
+ memset(&timeout, 0, sizeof(timeout));
+
+ timeout.tv_sec = MTRACE_TIMEOUT;
+
+ tmsec = timeout.tv_sec * 1000 + timeout.tv_usec / 1000;
+ do {
+ ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
+ if (ret <= 0)
+ return ret;
+ rmsec = timeout.tv_sec * 1000 + timeout.tv_usec / 1000;
+ msec = tmsec - rmsec;
+ } while (recv_response(fd, msec, hops) != 0);
+
+ return ret;
+}
+
+int main(int argc, char *const argv[])
+{
+ struct in_addr mc_source;
+ struct in_addr iface_addr;
+ struct in_addr gw_addr;
+ struct in_addr mtrace_addr;
+ struct igmp_mtrace mtrace;
+ int hops = 255;
+ int rhops;
+ int maxhops = 255;
+ int perhop = 3;
+ int ifindex;
+ int unicast = 1;
+ int ttl = 64;
+ int fd = -1;
+ int ret = -1;
+ int c;
+ int i, j;
+ char ifname[IF_NAMESIZE];
+ char ip_str[INET_ADDRSTRLEN];
+
+ mtrace_addr.s_addr = inet_addr("224.0.1.32");
+
+ uid_t uid = getuid();
+
+ if (uid != 0) {
+ printf("must run as root\n");
+ exit(EXIT_FAILURE);
+ }
+
+ if (argc <= 0)
+ progname = "mtracebis";
+ else
+ progname = argv[0];
+
+ if (argc != 2) {
+ usage();
+ exit(EXIT_FAILURE);
+ }
+
+ while (1) {
+ static struct option long_options[] = {
+ {"help", no_argument, 0, 'h'},
+ {"version", no_argument, 0, 'v'},
+ {0, 0, 0, 0} };
+ int option_index = 0;
+
+ c = getopt_long(argc, argv, "vh", long_options, &option_index);
+
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ usage();
+ exit(0);
+ case 'v':
+ version();
+ exit(0);
+ default:
+ usage();
+ exit(EXIT_FAILURE);
+ }
+ }
+ if (inet_pton(AF_INET, argv[1], &mc_source) != 1) {
+ usage();
+ fprintf(stderr, "%s: %s not a valid IPv4 address\n", argv[0],
+ argv[1]);
+ exit(EXIT_FAILURE);
+ }
+
+ ifindex = routeget(mc_source, &iface_addr, &gw_addr);
+ if (ifindex < 0) {
+ fprintf(stderr, "%s: failed to get route to source %s\n",
+ argv[0], argv[1]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (if_indextoname(ifindex, ifname) == NULL) {
+ fprintf(stderr, "%s: if_indextoname error: %s\n", argv[0],
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ /* zero mtrace struct */
+ memset((char *)&mtrace, 0, sizeof(mtrace));
+
+ /* set up query */
+ mtrace.type = PIM_IGMP_MTRACE_QUERY_REQUEST;
+ mtrace.hops = hops;
+ mtrace.checksum = 0;
+ mtrace.grp_addr.s_addr = 0;
+ mtrace.src_addr = mc_source;
+ mtrace.dst_addr = iface_addr;
+ mtrace.rsp_addr = unicast ? iface_addr : mtrace_addr;
+ mtrace.rsp_ttl = ttl;
+ mtrace.qry_id = 0xffffff & time(NULL);
+
+ mtrace.checksum = in_cksum(&mtrace, sizeof(mtrace));
+
+ fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
+
+ if (fd < 1) {
+ fprintf(stderr, "%s: socket error: %s\n", argv[0],
+ strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname,
+ strlen(ifname));
+
+ if (ret < 0) {
+ fprintf(stderr, "%s: setsockopt error: %s\n", argv[0],
+ strerror(errno));
+ ret = EXIT_FAILURE;
+ goto close_fd;
+ }
+
+ print_query(&mtrace);
+ if (send_query(fd, gw_addr, &mtrace) < 0) {
+ fprintf(stderr, "%s: sendto error: %s\n", argv[0],
+ strerror(errno));
+ ret = EXIT_FAILURE;
+ goto close_fd;
+ }
+ printf("Querying full reverse path...\n");
+ ret = wait_for_response(fd, NULL);
+ if (ret > 0) {
+ ret = 0;
+ goto close_fd;
+ }
+ if (ret < 0) {
+ fprintf(stderr, "%s: select error: %s\n", argv[0],
+ strerror(errno));
+ ret = EXIT_FAILURE;
+ goto close_fd;
+ }
+ printf(" * ");
+ printf("switching to hop-by-hop:\n");
+ printf("%3d ? (%s)\n", 0,
+ inet_ntop(AF_INET, &mtrace.dst_addr, ip_str, sizeof(ip_str)));
+ for (i = 1; i < maxhops; i++) {
+ printf("%3d ", -i);
+ mtrace.hops = i;
+ for (j = 0; j < perhop; j++) {
+ mtrace.qry_id++;
+ mtrace.checksum = 0;
+ mtrace.checksum = in_cksum(&mtrace, sizeof(mtrace));
+ if (send_query(fd, gw_addr, &mtrace) < 0) {
+ fprintf(stderr, "%s: sendto error: %s\n",
+ argv[0], strerror(errno));
+ ret = EXIT_FAILURE;
+ goto close_fd;
+ }
+ ret = wait_for_response(fd, &rhops);
+ if (ret > 0) {
+ if (i > rhops) {
+ ret = 0;
+ goto close_fd;
+ }
+ break;
+ }
+ printf(" *");
+ }
+ if (ret <= 0)
+ printf("\n");
+ }
+ ret = 0;
+close_fd:
+ close(fd);
+ exit(ret);
+}
+
+#else /* __linux__ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int main(int argc, char *argv[])
+{
+ printf("%s implemented only for GNU/Linux\n", argv[0]);
+ exit(0);
+}
+
+#endif /* __linux__ */
--- /dev/null
+/*
+ * libnetlink.c RTnetlink service routines.
+ *
+ * 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.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#ifdef __linux__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <net/if_arp.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/uio.h>
+#include <assert.h>
+
+#include "mtracebis_netlink.h"
+
+int rcvbuf = 1024 * 1024;
+
+void rtnl_close(struct rtnl_handle *rth)
+{
+ if (rth->fd >= 0) {
+ close(rth->fd);
+ rth->fd = -1;
+ }
+}
+
+int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions,
+ int protocol)
+{
+ socklen_t addr_len;
+ int sndbuf = 32768;
+
+ memset(rth, 0, sizeof(*rth));
+
+ rth->fd = socket(AF_NETLINK, SOCK_RAW, protocol);
+ if (rth->fd < 0) {
+ perror("Cannot open netlink socket");
+ return -1;
+ }
+
+ if (setsockopt(rth->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) {
+ perror("SO_SNDBUF");
+ return -1;
+ }
+
+ if (setsockopt(rth->fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf)) < 0) {
+ perror("SO_RCVBUF");
+ return -1;
+ }
+
+ memset(&rth->local, 0, sizeof(rth->local));
+ rth->local.nl_family = AF_NETLINK;
+ rth->local.nl_groups = subscriptions;
+
+ if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
+ perror("Cannot bind netlink socket");
+ return -1;
+ }
+ addr_len = sizeof(rth->local);
+ if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
+ perror("Cannot getsockname");
+ return -1;
+ }
+ if (addr_len != sizeof(rth->local)) {
+ fprintf(stderr, "Wrong address length %d\n", addr_len);
+ return -1;
+ }
+ if (rth->local.nl_family != AF_NETLINK) {
+ fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family);
+ return -1;
+ }
+ rth->seq = time(NULL);
+ return 0;
+}
+
+int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
+{
+ return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);
+}
+
+int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct rtgenmsg g;
+ } req;
+
+ memset(&req, 0, sizeof(req));
+ req.nlh.nlmsg_len = sizeof(req);
+ req.nlh.nlmsg_type = type;
+ req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+ req.nlh.nlmsg_pid = 0;
+ req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
+ req.g.rtgen_family = family;
+
+ return send(rth->fd, (void*)&req, sizeof(req), 0);
+}
+
+int rtnl_send(struct rtnl_handle *rth, const char *buf, int len)
+{
+ return send(rth->fd, buf, len, 0);
+}
+
+int rtnl_send_check(struct rtnl_handle *rth, const char *buf, int len)
+{
+ struct nlmsghdr *h;
+ int status;
+ char resp[1024];
+
+ status = send(rth->fd, buf, len, 0);
+ if (status < 0)
+ return status;
+
+ /* Check for immediate errors */
+ status = recv(rth->fd, resp, sizeof(resp), MSG_DONTWAIT|MSG_PEEK);
+ if (status < 0) {
+ if (errno == EAGAIN)
+ return 0;
+ return -1;
+ }
+
+ for (h = (struct nlmsghdr *)resp; NLMSG_OK(h, (uint32_t)status);
+ h = NLMSG_NEXT(h, status)) {
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+ if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr)))
+ fprintf(stderr, "ERROR truncated\n");
+ else
+ errno = -err->error;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
+{
+ struct nlmsghdr nlh;
+ struct sockaddr_nl nladdr;
+ struct iovec iov[2] = {
+ { .iov_base = &nlh, .iov_len = sizeof(nlh) },
+ { .iov_base = req, .iov_len = len }
+ };
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = iov,
+ .msg_iovlen = 2,
+ };
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ nlh.nlmsg_len = NLMSG_LENGTH(len);
+ nlh.nlmsg_type = type;
+ nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+ nlh.nlmsg_pid = 0;
+ nlh.nlmsg_seq = rth->dump = ++rth->seq;
+
+ return sendmsg(rth->fd, &msg, 0);
+}
+
+int rtnl_dump_filter_l(struct rtnl_handle *rth,
+ const struct rtnl_dump_filter_arg *arg)
+{
+ struct sockaddr_nl nladdr;
+ struct iovec iov;
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[16384];
+
+ iov.iov_base = buf;
+ while (1) {
+ int status;
+ const struct rtnl_dump_filter_arg *a;
+ int found_done = 0;
+ int msglen = 0;
+
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rth->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ fprintf(stderr, "netlink receive error %s (%d)\n",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ if (status == 0) {
+ fprintf(stderr, "EOF on netlink\n");
+ return -1;
+ }
+
+ for (a = arg; a->filter; a++) {
+ struct nlmsghdr *h = (struct nlmsghdr*)buf;
+ msglen = status;
+
+ while (NLMSG_OK(h, (uint32_t)msglen)) {
+ int err;
+
+ if (nladdr.nl_pid != 0 ||
+ h->nlmsg_pid != rth->local.nl_pid ||
+ h->nlmsg_seq != rth->dump) {
+ if (a->junk) {
+ err = a->junk(&nladdr, h,
+ a->arg2);
+ if (err < 0)
+ return err;
+ }
+ goto skip_it;
+ }
+
+ if (h->nlmsg_type == NLMSG_DONE) {
+ found_done = 1;
+ break; /* process next filter */
+ }
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+ if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+ fprintf(stderr,
+ "ERROR truncated\n");
+ } else {
+ errno = -err->error;
+ perror("RTNETLINK answers");
+ }
+ return -1;
+ }
+ err = a->filter(&nladdr, h, a->arg1);
+ if (err < 0)
+ return err;
+
+skip_it:
+ h = NLMSG_NEXT(h, msglen);
+ }
+ }
+
+ if (found_done)
+ return 0;
+
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Message truncated\n");
+ continue;
+ }
+ if (msglen) {
+ fprintf(stderr, "!!!Remnant of size %d\n", msglen);
+ exit(1);
+ }
+ }
+}
+
+int rtnl_dump_filter(struct rtnl_handle *rth,
+ rtnl_filter_t filter,
+ void *arg1,
+ rtnl_filter_t junk,
+ void *arg2)
+{
+ const struct rtnl_dump_filter_arg a[2] = {
+ { .filter = filter, .arg1 = arg1, .junk = junk, .arg2 = arg2 },
+ { .filter = NULL, .arg1 = NULL, .junk = NULL, .arg2 = NULL }
+ };
+
+ return rtnl_dump_filter_l(rth, a);
+}
+
+int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+ unsigned groups, struct nlmsghdr *answer,
+ rtnl_filter_t junk,
+ void *jarg)
+{
+ int status;
+ unsigned seq;
+ struct nlmsghdr *h;
+ struct sockaddr_nl nladdr;
+ struct iovec iov = {
+ .iov_base = (void*) n,
+ .iov_len = n->nlmsg_len
+ };
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[16384];
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = peer;
+ nladdr.nl_groups = groups;
+
+ n->nlmsg_seq = seq = ++rtnl->seq;
+
+ if (answer == NULL)
+ n->nlmsg_flags |= NLM_F_ACK;
+
+ status = sendmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ perror("Cannot talk to rtnetlink");
+ return -1;
+ }
+
+ memset(buf,0,sizeof(buf));
+
+ iov.iov_base = buf;
+
+ while (1) {
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ fprintf(stderr, "netlink receive error %s (%d)\n",
+ strerror(errno), errno);
+ return -1;
+ }
+ if (status == 0) {
+ fprintf(stderr, "EOF on netlink\n");
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ fprintf(stderr, "sender address length == %d\n", msg.msg_namelen);
+ exit(1);
+ }
+ for (h = (struct nlmsghdr*)buf; status >= (int)sizeof(*h); ) {
+ int err;
+ int len = h->nlmsg_len;
+ int l = len - sizeof(*h);
+
+ if (l<0 || len>status) {
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Truncated message\n");
+ return -1;
+ }
+ fprintf(stderr, "!!!malformed message: len=%d\n", len);
+ exit(1);
+ }
+
+ if ((int)nladdr.nl_pid != peer ||
+ h->nlmsg_pid != rtnl->local.nl_pid ||
+ h->nlmsg_seq != seq) {
+ if (junk) {
+ err = junk(&nladdr, h, jarg);
+ if (err < 0)
+ return err;
+ }
+ /* Don't forget to skip that message. */
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ continue;
+ }
+
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = (struct nlmsgerr*)NLMSG_DATA(h);
+ if (l < (int)sizeof(struct nlmsgerr)) {
+ fprintf(stderr, "ERROR truncated\n");
+ } else {
+ errno = -err->error;
+ if (errno == 0) {
+ if (answer)
+ memcpy(answer, h, h->nlmsg_len);
+ return 0;
+ }
+ perror("RTNETLINK answers");
+ }
+ return -1;
+ }
+ if (answer) {
+ memcpy(answer, h, h->nlmsg_len);
+ return 0;
+ }
+
+ fprintf(stderr, "Unexpected reply!!!\n");
+
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Message truncated\n");
+ continue;
+ }
+ if (status) {
+ fprintf(stderr, "!!!Remnant of size %d\n", status);
+ exit(1);
+ }
+ }
+}
+
+int rtnl_listen(struct rtnl_handle *rtnl,
+ rtnl_filter_t handler,
+ void *jarg)
+{
+ int status;
+ struct nlmsghdr *h;
+ struct sockaddr_nl nladdr;
+ struct iovec iov;
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[8192];
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = 0;
+ nladdr.nl_groups = 0;
+
+ iov.iov_base = buf;
+ while (1) {
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ fprintf(stderr, "netlink receive error %s (%d)\n",
+ strerror(errno), errno);
+ if (errno == ENOBUFS)
+ continue;
+ return -1;
+ }
+ if (status == 0) {
+ fprintf(stderr, "EOF on netlink\n");
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ fprintf(stderr, "Sender address length == %d\n", msg.msg_namelen);
+ exit(1);
+ }
+ for (h =(struct nlmsghdr*)buf; status >= (int)sizeof(*h); ) {
+ int err;
+ int len = h->nlmsg_len;
+ int l = len - sizeof(*h);
+
+ if (l<0 || len>status) {
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Truncated message\n");
+ return -1;
+ }
+ fprintf(stderr, "!!!malformed message: len=%d\n", len);
+ exit(1);
+ }
+
+ err = handler(&nladdr, h, jarg);
+ if (err < 0)
+ return err;
+
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Message truncated\n");
+ continue;
+ }
+ if (status) {
+ fprintf(stderr, "!!!Remnant of size %d\n", status);
+ exit(1);
+ }
+ }
+}
+
+int rtnl_from_file(FILE *rtnl, rtnl_filter_t handler,
+ void *jarg)
+{
+ int status;
+ struct sockaddr_nl nladdr;
+ char buf[8192];
+ struct nlmsghdr *h = (void*)buf;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = 0;
+ nladdr.nl_groups = 0;
+
+ while (1) {
+ int err, len;
+ int l;
+
+ status = fread(&buf, 1, sizeof(*h), rtnl);
+
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("rtnl_from_file: fread");
+ return -1;
+ }
+ if (status == 0)
+ return 0;
+
+ len = h->nlmsg_len;
+ l = len - sizeof(*h);
+
+ if (l<0 || len>(int)sizeof(buf)) {
+ fprintf(stderr, "!!!malformed message: len=%d @%lu\n",
+ len, ftell(rtnl));
+ return -1;
+ }
+
+ status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl);
+
+ if (status < 0) {
+ perror("rtnl_from_file: fread");
+ return -1;
+ }
+ if (status < l) {
+ fprintf(stderr, "rtnl-from_file: truncated message\n");
+ return -1;
+ }
+
+ err = handler(&nladdr, h, jarg);
+ if (err < 0)
+ return err;
+ }
+}
+
+int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
+{
+ int len = RTA_LENGTH(4);
+ struct rtattr *rta;
+ if ((int)(NLMSG_ALIGN(n->nlmsg_len) + len) > maxlen) {
+ fprintf(stderr,"addattr32: Error! max allowed bound %d exceeded\n",maxlen);
+ return -1;
+ }
+ rta = NLMSG_TAIL(n);
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), &data, 4);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+ return 0;
+}
+
+int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data,
+ int alen)
+{
+ int len = RTA_LENGTH(alen);
+ struct rtattr *rta;
+
+ if ((int)(NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len)) > maxlen) {
+ fprintf(stderr, "addattr_l ERROR: message exceeded bound of %d\n",maxlen);
+ return -1;
+ }
+ rta = NLMSG_TAIL(n);
+ rta->rta_type = type;
+ rta->rta_len = len;
+
+ if (data)
+ memcpy(RTA_DATA(rta), data, alen);
+ else
+ assert(alen == 0);
+
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
+ return 0;
+}
+
+int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len)
+{
+ if ((int)(NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len)) > maxlen) {
+ fprintf(stderr, "addraw_l ERROR: message exceeded bound of %d\n",maxlen);
+ return -1;
+ }
+
+ memcpy(NLMSG_TAIL(n), data, len);
+ memset((uint8_t *) NLMSG_TAIL(n) + len, 0, NLMSG_ALIGN(len) - len);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len);
+ return 0;
+}
+
+struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type)
+{
+ struct rtattr *nest = NLMSG_TAIL(n);
+
+ addattr_l(n, maxlen, type, NULL, 0);
+ return nest;
+}
+
+int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest)
+{
+ nest->rta_len = (uint8_t *)NLMSG_TAIL(n) - (uint8_t *)nest;
+ return n->nlmsg_len;
+}
+
+struct rtattr *addattr_nest_compat(struct nlmsghdr *n, int maxlen, int type,
+ const void *data, int len)
+{
+ struct rtattr *start = NLMSG_TAIL(n);
+
+ addattr_l(n, maxlen, type, data, len);
+ addattr_nest(n, maxlen, type);
+ return start;
+}
+
+int addattr_nest_compat_end(struct nlmsghdr *n, struct rtattr *start)
+{
+ struct rtattr *nest = start + NLMSG_ALIGN(start->rta_len);
+
+ start->rta_len = (uint8_t *)NLMSG_TAIL(n) - (uint8_t *)start;
+ addattr_nest_end(n, nest);
+ return n->nlmsg_len;
+}
+
+int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data)
+{
+ int len = RTA_LENGTH(4);
+ struct rtattr *subrta;
+
+ if ((int)(RTA_ALIGN(rta->rta_len) + len) > maxlen) {
+ fprintf(stderr,"rta_addattr32: Error! max allowed bound %d exceeded\n",maxlen);
+ return -1;
+ }
+ subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ memcpy(RTA_DATA(subrta), &data, 4);
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+ return 0;
+}
+
+int rta_addattr_l(struct rtattr *rta, int maxlen, int type,
+ const void *data, int alen)
+{
+ struct rtattr *subrta;
+ int len = RTA_LENGTH(alen);
+
+ if ((int)(RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len)) > maxlen) {
+ fprintf(stderr,"rta_addattr_l: Error! max allowed bound %d exceeded\n",maxlen);
+ return -1;
+ }
+ subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ memcpy(RTA_DATA(subrta), data, alen);
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len) + RTA_ALIGN(len);
+ return 0;
+}
+
+int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+ memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+ while (RTA_OK(rta, len)) {
+ if ((rta->rta_type <= max) && (!tb[rta->rta_type]))
+ tb[rta->rta_type] = rta;
+ rta = RTA_NEXT(rta,len);
+ }
+ if (len)
+ fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
+ return 0;
+}
+
+int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+ int i = 0;
+
+ memset(tb, 0, sizeof(struct rtattr *) * max);
+ while (RTA_OK(rta, len)) {
+ if (rta->rta_type <= max && i < max)
+ tb[i++] = rta;
+ rta = RTA_NEXT(rta,len);
+ }
+ if (len)
+ fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
+ return i;
+}
+
+int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta,
+ int len)
+{
+ if ((int)RTA_PAYLOAD(rta) < len)
+ return -1;
+ if (RTA_PAYLOAD(rta) >= RTA_ALIGN(len) + sizeof(struct rtattr)) {
+ rta = (struct rtattr *)(uint8_t *)RTA_DATA(rta)+RTA_ALIGN(len);
+ return parse_rtattr_nested(tb, max, rta);
+ }
+ memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+ return 0;
+}
+
+#endif /* __linux__ */
--- /dev/null
+/*
+ * libnetlink.c RTnetlink service routines.
+ *
+ * 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.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ */
+
+#ifdef __linux__
+
+#ifndef __LIBNETLINK_H__
+#define __LIBNETLINK_H__ 1
+
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/if_link.h>
+#include <linux/if_addr.h>
+#include <linux/neighbour.h>
+
+struct rtnl_handle
+{
+ int fd;
+ struct sockaddr_nl local;
+ struct sockaddr_nl peer;
+ __u32 seq;
+ __u32 dump;
+};
+
+extern int rcvbuf;
+
+extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions);
+extern int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, int protocol);
+extern void rtnl_close(struct rtnl_handle *rth);
+extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type);
+extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len);
+
+typedef int (*rtnl_filter_t)(const struct sockaddr_nl *,
+ struct nlmsghdr *n, void *);
+
+struct rtnl_dump_filter_arg
+{
+ rtnl_filter_t filter;
+ void *arg1;
+ rtnl_filter_t junk;
+ void *arg2;
+};
+
+extern int rtnl_dump_filter_l(struct rtnl_handle *rth,
+ const struct rtnl_dump_filter_arg *arg);
+extern int rtnl_dump_filter(struct rtnl_handle *rth, rtnl_filter_t filter,
+ void *arg1,
+ rtnl_filter_t junk,
+ void *arg2);
+
+extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+ unsigned groups, struct nlmsghdr *answer,
+ rtnl_filter_t junk,
+ void *jarg);
+extern int rtnl_send(struct rtnl_handle *rth, const char *buf, int);
+extern int rtnl_send_check(struct rtnl_handle *rth, const char *buf, int);
+
+extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data);
+extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen);
+extern int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len);
+extern struct rtattr *addattr_nest(struct nlmsghdr *n, int maxlen, int type);
+extern int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest);
+extern struct rtattr *addattr_nest_compat(struct nlmsghdr *n, int maxlen, int type, const void *data, int len);
+extern int addattr_nest_compat_end(struct nlmsghdr *n, struct rtattr *nest);
+extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data);
+extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, const void *data, int alen);
+
+extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+extern int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+extern int __parse_rtattr_nested_compat(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+
+#define parse_rtattr_nested(tb, max, rta) \
+ (parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta)))
+
+#define parse_rtattr_nested_compat(tb, max, rta, data, len) \
+({ data = RTA_PAYLOAD(rta) >= len ? RTA_DATA(rta) : NULL; \
+ __parse_rtattr_nested_compat(tb, max, rta, len); })
+
+extern int rtnl_listen(struct rtnl_handle *, rtnl_filter_t handler,
+ void *jarg);
+extern int rtnl_from_file(FILE *, rtnl_filter_t handler,
+ void *jarg);
+
+#define NLMSG_TAIL(nmsg) \
+ ((struct rtattr *) (((uint8_t *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
+
+#ifndef IFA_RTA
+#define IFA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
+#endif
+#ifndef IFA_PAYLOAD
+#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg))
+#endif
+
+#ifndef IFLA_RTA
+#define IFLA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
+#endif
+#ifndef IFLA_PAYLOAD
+#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg))
+#endif
+
+#ifndef NDA_RTA
+#define NDA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
+#endif
+#ifndef NDA_PAYLOAD
+#define NDA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndmsg))
+#endif
+
+#ifndef NDTA_RTA
+#define NDTA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndtmsg))))
+#endif
+#ifndef NDTA_PAYLOAD
+#define NDTA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndtmsg))
+#endif
+
+#endif /* __LIBNETLINK_H__ */
+
+#endif /* __linux__ */
--- /dev/null
+/*
+ * Multicast Traceroute for FRRouting
+ * Copyright (C) 2018 Mladen Sablic
+ *
+ * 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
+ */
+
+#ifdef __linux__
+
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "mtracebis_netlink.h"
+#include "mtracebis_routeget.h"
+
+static int find_dst(struct nlmsghdr *n, struct in_addr *src, struct in_addr *gw)
+{
+ struct rtmsg *r = NLMSG_DATA(n);
+ int len = n->nlmsg_len;
+ struct rtattr *tb[RTA_MAX + 1];
+
+ len -= NLMSG_LENGTH(sizeof(*r));
+ if (len < 0) {
+ fprintf(stderr, "BUG: wrong nlmsg len %d\n", len);
+ return -1;
+ }
+
+ parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len);
+ if (tb[RTA_PREFSRC])
+ src->s_addr = *(uint32_t *)RTA_DATA(tb[RTA_PREFSRC]);
+ if (tb[RTA_GATEWAY])
+ gw->s_addr = *(uint32_t *)RTA_DATA(tb[RTA_GATEWAY]);
+ if (tb[RTA_OIF])
+ return *(int *)RTA_DATA(tb[RTA_OIF]);
+ return 0;
+}
+
+int routeget(struct in_addr dst, struct in_addr *src, struct in_addr *gw)
+{
+ struct {
+ struct nlmsghdr n;
+ struct rtmsg r;
+ char buf[1024];
+ } req;
+ int ret;
+ struct rtnl_handle rth = {.fd = -1};
+
+ memset(&req, 0, sizeof(req));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ req.n.nlmsg_type = RTM_GETROUTE;
+ req.r.rtm_family = AF_INET;
+ req.r.rtm_table = 0;
+ req.r.rtm_protocol = 0;
+ req.r.rtm_scope = 0;
+ req.r.rtm_type = 0;
+ req.r.rtm_src_len = 0;
+ req.r.rtm_dst_len = 0;
+ req.r.rtm_tos = 0;
+
+ addattr_l(&req.n, sizeof(req), RTA_DST, &dst.s_addr, 4);
+ req.r.rtm_dst_len = 32;
+
+ ret = rtnl_open(&rth, 0);
+
+ if (ret < 0)
+ return ret;
+
+ if (rtnl_talk(&rth, &req.n, 0, 0, &req.n, NULL, NULL) < 0) {
+ ret = -1;
+ goto close_rth;
+ }
+
+ ret = find_dst(&req.n, src, gw);
+close_rth:
+ rtnl_close(&rth);
+ return ret;
+}
+
+#endif /* __linux__ */
--- /dev/null
+/*
+ * Multicast Traceroute for FRRouting
+ * Copyright (C) 2018 Mladen Sablic
+ *
+ * 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
+ */
+
+#ifdef __linux__
+
+#ifndef ROUTEGET_H
+#define ROUTEGET_H
+
+#include <netinet/in.h>
+
+int routeget(struct in_addr dst, struct in_addr *src, struct in_addr *gw);
+
+#endif /* ROUTEGET */
+
+#endif /* __linux__ */
ALIAS(no_debug_msdp_packets, undebug_msdp_packets_cmd, "undebug msdp packets",
UNDEBUG_STR DEBUG_MSDP_STR DEBUG_MSDP_PACKETS_STR)
+DEFUN (debug_mtrace,
+ debug_mtrace_cmd,
+ "debug mtrace",
+ DEBUG_STR
+ DEBUG_MTRACE_STR)
+{
+ PIM_DO_DEBUG_MTRACE;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_mtrace,
+ no_debug_mtrace_cmd,
+ "no debug mtrace",
+ NO_STR
+ DEBUG_STR
+ DEBUG_MTRACE_STR)
+{
+ PIM_DONT_DEBUG_MTRACE;
+ return CMD_SUCCESS;
+}
+
DEFUN_NOSH (show_debugging_pim,
show_debugging_pim_cmd,
"show debugging [pim]",
install_element(ENABLE_NODE, &debug_msdp_packets_cmd);
install_element(ENABLE_NODE, &no_debug_msdp_packets_cmd);
install_element(ENABLE_NODE, &undebug_msdp_packets_cmd);
+ install_element(ENABLE_NODE, &debug_mtrace_cmd);
+ install_element(ENABLE_NODE, &no_debug_mtrace_cmd);
install_element(CONFIG_NODE, &debug_igmp_cmd);
install_element(CONFIG_NODE, &no_debug_igmp_cmd);
install_element(CONFIG_NODE, &debug_msdp_packets_cmd);
install_element(CONFIG_NODE, &no_debug_msdp_packets_cmd);
install_element(CONFIG_NODE, &undebug_msdp_packets_cmd);
+ install_element(CONFIG_NODE, &debug_mtrace_cmd);
+ install_element(CONFIG_NODE, &no_debug_mtrace_cmd);
install_element(CONFIG_NODE, &ip_msdp_mesh_group_member_cmd);
install_element(VRF_NODE, &ip_msdp_mesh_group_member_cmd);
#define DEBUG_MSDP_EVENTS_STR "MSDP protocol events\n"
#define DEBUG_MSDP_INTERNAL_STR "MSDP protocol internal\n"
#define DEBUG_MSDP_PACKETS_STR "MSDP protocol packets\n"
+#define DEBUG_MTRACE_STR "Mtrace protocol activity\n"
void pim_cmd_init(void);
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);
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);
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);
}
}
#include "pim_igmp.h"
#include "pim_igmpv2.h"
#include "pim_igmpv3.h"
+#include "pim_igmp_mtrace.h"
#include "pim_iface.h"
#include "pim_sock.h"
#include "pim_mroute.h"
case PIM_IGMP_V2_LEAVE_GROUP:
return igmp_v2_recv_leave(igmp, ip_hdr->ip_src, from_str,
igmp_msg, igmp_msg_len);
+
+ case PIM_IGMP_MTRACE_RESPONSE:
+ return igmp_mtrace_recv_response(igmp, ip_hdr, ip_hdr->ip_src,
+ from_str, igmp_msg,
+ igmp_msg_len);
+ break;
+ case PIM_IGMP_MTRACE_QUERY_REQUEST:
+ return igmp_mtrace_recv_qry_req(igmp, ip_hdr, ip_hdr->ip_src,
+ from_str, igmp_msg,
+ igmp_msg_len);
}
zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
#define PIM_IGMP_V1_MEMBERSHIP_REPORT (0x12)
#define PIM_IGMP_V2_MEMBERSHIP_REPORT (0x16)
#define PIM_IGMP_V2_LEAVE_GROUP (0x17)
+#define PIM_IGMP_MTRACE_RESPONSE (0x1E)
+#define PIM_IGMP_MTRACE_QUERY_REQUEST (0x1F)
#define PIM_IGMP_V3_MEMBERSHIP_REPORT (0x22)
#define IGMP_V3_REPORT_HEADER_SIZE (8)
--- /dev/null
+/*
+ * Multicast traceroute for FRRouting
+ * Copyright (C) 2017 Mladen Sablic
+ *
+ * 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 "pimd.h"
+#include "pim_util.h"
+#include "pim_sock.h"
+#include "pim_rp.h"
+#include "pim_oil.h"
+#include "pim_ifchannel.h"
+#include "pim_macro.h"
+#include "pim_igmp_mtrace.h"
+
+static void mtrace_rsp_init(struct igmp_mtrace_rsp *mtrace_rspp)
+{
+ mtrace_rspp->arrival = 0;
+ mtrace_rspp->incoming.s_addr = 0;
+ mtrace_rspp->outgoing.s_addr = 0;
+ mtrace_rspp->prev_hop.s_addr = 0;
+ mtrace_rspp->in_count = MTRACE_UNKNOWN_COUNT;
+ mtrace_rspp->out_count = MTRACE_UNKNOWN_COUNT;
+ mtrace_rspp->total = MTRACE_UNKNOWN_COUNT;
+ mtrace_rspp->rtg_proto = 0;
+ mtrace_rspp->fwd_ttl = 0;
+ mtrace_rspp->mbz = 0;
+ mtrace_rspp->s = 0;
+ mtrace_rspp->src_mask = 0;
+ mtrace_rspp->fwd_code = MTRACE_FWD_CODE_NO_ERROR;
+}
+
+static void mtrace_rsp_debug(uint32_t qry_id, int rsp,
+ struct igmp_mtrace_rsp *mrspp)
+{
+ char inc_str[INET_ADDRSTRLEN];
+ char out_str[INET_ADDRSTRLEN];
+ char prv_str[INET_ADDRSTRLEN];
+
+ zlog_debug(
+ "Rx mt(%d) qid=%ud arr=%x in=%s out=%s prev=%s proto=%d fwd=%d",
+ rsp, ntohl(qry_id), mrspp->arrival,
+ inet_ntop(AF_INET, &(mrspp->incoming), inc_str,
+ sizeof(inc_str)),
+ inet_ntop(AF_INET, &(mrspp->outgoing), out_str,
+ sizeof(out_str)),
+ inet_ntop(AF_INET, &(mrspp->prev_hop), prv_str,
+ sizeof(prv_str)),
+ mrspp->rtg_proto, mrspp->fwd_code);
+}
+
+static void mtrace_debug(struct pim_interface *pim_ifp,
+ struct igmp_mtrace *mtracep, int mtrace_len)
+{
+ char inc_str[INET_ADDRSTRLEN];
+ char grp_str[INET_ADDRSTRLEN];
+ char src_str[INET_ADDRSTRLEN];
+ char dst_str[INET_ADDRSTRLEN];
+ char rsp_str[INET_ADDRSTRLEN];
+
+ zlog_debug(
+ "Rx mtrace packet incoming on %s: "
+ "hops=%d type=%d size=%d, grp=%s, src=%s,"
+ " dst=%s rsp=%s ttl=%d qid=%ud",
+ inet_ntop(AF_INET, &(pim_ifp->primary_address), inc_str,
+ sizeof(inc_str)),
+ mtracep->hops, mtracep->type, mtrace_len,
+ inet_ntop(AF_INET, &(mtracep->grp_addr), grp_str,
+ sizeof(grp_str)),
+ inet_ntop(AF_INET, &(mtracep->src_addr), src_str,
+ sizeof(src_str)),
+ inet_ntop(AF_INET, &(mtracep->dst_addr), dst_str,
+ sizeof(dst_str)),
+ inet_ntop(AF_INET, &(mtracep->rsp_addr), rsp_str,
+ sizeof(rsp_str)),
+ mtracep->rsp_ttl, ntohl(mtracep->qry_id));
+ if (mtrace_len > (int)sizeof(struct igmp_mtrace)) {
+
+ int i;
+
+ int responses = mtrace_len - sizeof(struct igmp_mtrace);
+
+ if ((responses % sizeof(struct igmp_mtrace_rsp)) != 0)
+ if (PIM_DEBUG_MTRACE)
+ zlog_debug(
+ "Mtrace response block of wrong"
+ " length");
+
+ responses = responses / sizeof(struct igmp_mtrace_rsp);
+
+ for (i = 0; i < responses; i++)
+ mtrace_rsp_debug(mtracep->qry_id, i, &mtracep->rsp[i]);
+ }
+}
+
+/* 5.1 Query Arrival Time */
+static uint32_t query_arrival_time(void)
+{
+ struct timeval tv;
+ uint32_t qat;
+
+ char m_qat[] = "Query arrival time lookup failed: errno=%d: %s";
+
+ if (gettimeofday(&tv, NULL) < 0) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn(m_qat, errno, safe_strerror(errno));
+ return 0;
+ }
+ /* not sure second offset correct, as I get different value */
+ qat = ((tv.tv_sec + 32384) << 16) + ((tv.tv_usec << 10) / 15625);
+
+ return qat;
+}
+
+static int mtrace_send_packet(struct interface *ifp,
+ struct igmp_mtrace *mtracep,
+ size_t mtrace_buf_len, struct in_addr dst_addr,
+ struct in_addr group_addr)
+{
+ struct sockaddr_in to;
+ struct pim_interface *pim_ifp;
+ socklen_t tolen;
+ ssize_t sent;
+ int ret;
+ int fd;
+ char pim_str[INET_ADDRSTRLEN];
+ char rsp_str[INET_ADDRSTRLEN];
+ u_char ttl;
+
+ pim_ifp = ifp->info;
+
+ memset(&to, 0, sizeof(to));
+ to.sin_family = AF_INET;
+ to.sin_addr = dst_addr;
+ tolen = sizeof(to);
+
+ if (PIM_DEBUG_MTRACE)
+ zlog_debug("Sending mtrace packet to %s on %s",
+ inet_ntop(AF_INET, &mtracep->rsp_addr, rsp_str,
+ sizeof(rsp_str)),
+ inet_ntop(AF_INET, &pim_ifp->primary_address,
+ pim_str, sizeof(pim_str)));
+
+ fd = pim_socket_raw(IPPROTO_IGMP);
+
+ if (fd < 0)
+ return -1;
+
+ ret = pim_socket_bind(fd, ifp);
+
+ if (ret < 0) {
+ ret = -1;
+ goto close_fd;
+ }
+
+ if (IPV4_CLASS_DE(ntohl(dst_addr.s_addr))) {
+ if (IPV4_MC_LINKLOCAL(ntohl(dst_addr.s_addr))) {
+ ttl = 1;
+ } else {
+ if (mtracep->type == PIM_IGMP_MTRACE_RESPONSE)
+ ttl = mtracep->rsp_ttl;
+ else
+ ttl = 64;
+ }
+ ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl,
+ sizeof(ttl));
+
+ if (ret < 0) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn("Failed to set socket multicast TTL");
+ ret = -1;
+ goto close_fd;
+ }
+ }
+
+ sent = sendto(fd, (char *)mtracep, mtrace_buf_len, MSG_DONTWAIT,
+ (struct sockaddr *)&to, tolen);
+
+ if (sent != (ssize_t)mtrace_buf_len) {
+ char dst_str[INET_ADDRSTRLEN];
+ char group_str[INET_ADDRSTRLEN];
+
+ pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
+ pim_inet4_dump("<group?>", group_addr, group_str,
+ sizeof(group_str));
+ if (sent < 0) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn(
+ "Send mtrace request failed for %s on"
+ "%s: group=%s msg_size=%zd: errno=%d: "
+ " %s",
+ dst_str, ifp->name, group_str,
+ mtrace_buf_len, errno,
+ safe_strerror(errno));
+ } else {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn(
+ "Send mtrace request failed for %s on"
+ " %s: group=%s msg_size=%zd: sent=%zd",
+ dst_str, ifp->name, group_str,
+ mtrace_buf_len, sent);
+ }
+ ret = -1;
+ goto close_fd;
+ }
+ ret = 0;
+close_fd:
+ close(fd);
+ return ret;
+}
+
+static int mtrace_un_forward_packet(struct pim_instance *pim, struct ip *ip_hdr,
+ struct interface *interface)
+{
+ struct pim_nexthop nexthop;
+ struct sockaddr_in to;
+ struct interface *if_out;
+ socklen_t tolen;
+ int ret;
+ int fd;
+ int sent;
+ uint16_t checksum;
+
+ checksum = ip_hdr->ip_sum;
+
+ ip_hdr->ip_sum = 0;
+
+ if (checksum != in_cksum(ip_hdr, ip_hdr->ip_hl * 4))
+ return -1;
+
+ if (ip_hdr->ip_ttl-- <= 1)
+ return -1;
+
+ ip_hdr->ip_sum = in_cksum(ip_hdr, ip_hdr->ip_hl * 4);
+
+ fd = pim_socket_raw(IPPROTO_RAW);
+
+ if (fd < 0)
+ return -1;
+
+ pim_socket_ip_hdr(fd);
+
+ if (interface == NULL) {
+ ret = pim_nexthop_lookup(pim, &nexthop, ip_hdr->ip_dst, 0);
+
+ if (ret != 0) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn(
+ "Dropping mtrace packet, "
+ "no route to destination");
+ return -1;
+ }
+
+ if_out = nexthop.interface;
+ } else {
+ if_out = interface;
+ }
+
+ ret = pim_socket_bind(fd, if_out);
+
+ if (ret < 0) {
+ close(fd);
+ return -1;
+ }
+
+ memset(&to, 0, sizeof(to));
+ to.sin_family = AF_INET;
+ to.sin_addr = ip_hdr->ip_dst;
+ tolen = sizeof(to);
+
+ sent = sendto(fd, ip_hdr, ntohs(ip_hdr->ip_len), 0,
+ (struct sockaddr *)&to, tolen);
+
+ close(fd);
+
+ if (sent < 0) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn(
+ "Failed to forward mtrace packet:"
+ " sendto errno=%d, %s",
+ errno, safe_strerror(errno));
+ return -1;
+ }
+
+ if (PIM_DEBUG_MTRACE) {
+ zlog_debug("Fwd mtrace packet len=%u to %s ttl=%u",
+ ntohs(ip_hdr->ip_len), inet_ntoa(ip_hdr->ip_dst),
+ ip_hdr->ip_ttl);
+ }
+
+ return 0;
+}
+
+static int mtrace_mc_forward_packet(struct pim_instance *pim, struct ip *ip_hdr)
+{
+ struct prefix_sg sg;
+ struct channel_oil *c_oil;
+ struct listnode *chnode;
+ struct listnode *chnextnode;
+ struct pim_ifchannel *ch = NULL;
+ int ret = -1;
+
+ memset(&sg, 0, sizeof(struct prefix_sg));
+ sg.grp = ip_hdr->ip_dst;
+
+ c_oil = pim_find_channel_oil(pim, &sg);
+
+ if (c_oil == NULL) {
+ if (PIM_DEBUG_MTRACE) {
+ zlog_debug(
+ "Dropping mtrace multicast packet "
+ "len=%u to %s ttl=%u",
+ ntohs(ip_hdr->ip_len),
+ inet_ntoa(ip_hdr->ip_dst), ip_hdr->ip_ttl);
+ }
+ return -1;
+ }
+ if (c_oil->up == NULL)
+ return -1;
+ if (c_oil->up->ifchannels == NULL)
+ return -1;
+ for (ALL_LIST_ELEMENTS(c_oil->up->ifchannels, chnode, chnextnode, ch)) {
+ if (pim_macro_chisin_oiflist(ch)) {
+ int r;
+
+ r = mtrace_un_forward_packet(pim, ip_hdr,
+ ch->interface);
+ if (r == 0)
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+
+static int mtrace_forward_packet(struct pim_instance *pim, struct ip *ip_hdr)
+{
+ if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr)))
+ return mtrace_mc_forward_packet(pim, ip_hdr);
+ else
+ return mtrace_un_forward_packet(pim, ip_hdr, NULL);
+}
+
+/* 6.5 Sending Traceroute Responses */
+static int mtrace_send_mc_response(struct pim_instance *pim,
+ struct igmp_mtrace *mtracep,
+ size_t mtrace_len)
+{
+ struct prefix_sg sg;
+ struct channel_oil *c_oil;
+ struct listnode *chnode;
+ struct listnode *chnextnode;
+ struct pim_ifchannel *ch = NULL;
+ int ret = -1;
+
+ memset(&sg, 0, sizeof(struct prefix_sg));
+ sg.grp = mtracep->rsp_addr;
+
+ c_oil = pim_find_channel_oil(pim, &sg);
+
+ if (c_oil == NULL) {
+ if (PIM_DEBUG_MTRACE) {
+ zlog_debug(
+ "Dropping mtrace multicast response packet "
+ "len=%u to %s",
+ (unsigned int)mtrace_len,
+ inet_ntoa(mtracep->rsp_addr));
+ }
+ return -1;
+ }
+ if (c_oil->up == NULL)
+ return -1;
+ if (c_oil->up->ifchannels == NULL)
+ return -1;
+ for (ALL_LIST_ELEMENTS(c_oil->up->ifchannels, chnode, chnextnode, ch)) {
+ if (pim_macro_chisin_oiflist(ch)) {
+ int r;
+
+ r = mtrace_send_packet(ch->interface, mtracep,
+ mtrace_len, mtracep->rsp_addr,
+ mtracep->grp_addr);
+ if (r == 0)
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+static int mtrace_send_response(struct pim_instance *pim,
+ struct igmp_mtrace *mtracep, size_t mtrace_len)
+{
+ struct pim_nexthop nexthop;
+ int ret;
+
+ mtracep->type = PIM_IGMP_MTRACE_RESPONSE;
+
+ mtracep->checksum = 0;
+ mtracep->checksum = in_cksum((char *)mtracep, mtrace_len);
+
+ if (IPV4_CLASS_DE(ntohl(mtracep->rsp_addr.s_addr))) {
+ struct pim_rpf *p_rpf;
+ char grp_str[INET_ADDRSTRLEN];
+
+ if (pim_rp_i_am_rp(pim, mtracep->rsp_addr))
+ return mtrace_send_mc_response(pim, mtracep,
+ mtrace_len);
+
+ p_rpf = pim_rp_g(pim, mtracep->rsp_addr);
+
+ if (p_rpf == NULL) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn("mtrace no RP for %s",
+ inet_ntop(AF_INET,
+ &(mtracep->rsp_addr),
+ grp_str, sizeof(grp_str)));
+ return -1;
+ }
+ nexthop = p_rpf->source_nexthop;
+ if (PIM_DEBUG_MTRACE)
+ zlog_debug("mtrace response to RP");
+ } else {
+ /* TODO: should use unicast rib lookup */
+ ret = pim_nexthop_lookup(pim, &nexthop, mtracep->rsp_addr, 1);
+
+ if (ret != 0) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn(
+ "Dropped response qid=%ud, no route to "
+ "response address",
+ mtracep->qry_id);
+ return -1;
+ }
+ }
+
+ return mtrace_send_packet(nexthop.interface, mtracep, mtrace_len,
+ mtracep->rsp_addr, mtracep->grp_addr);
+}
+
+int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len)
+{
+ static uint32_t qry_id, qry_src;
+ char mtrace_buf[MTRACE_HDR_SIZE + MTRACE_MAX_HOPS * MTRACE_RSP_SIZE];
+ struct pim_nexthop nexthop;
+ struct interface *ifp;
+ struct interface *out_ifp;
+ struct pim_interface *pim_ifp;
+ struct pim_interface *pim_out_ifp;
+ struct pim_instance *pim;
+ struct igmp_mtrace *mtracep;
+ struct igmp_mtrace_rsp *rspp;
+ struct in_addr nh_addr;
+ enum mtrace_fwd_code fwd_code = MTRACE_FWD_CODE_NO_ERROR;
+ int ret;
+ size_t r_len;
+ int last_rsp_ind = 0;
+ size_t mtrace_len;
+ uint16_t recv_checksum;
+ uint16_t checksum;
+
+ ifp = igmp->interface;
+ pim_ifp = ifp->info;
+ pim = pim_ifp->pim;
+
+ /*
+ * 6. Router Behaviour
+ * Check if mtrace packet is addressed elsewhere and forward,
+ * if applicable
+ */
+ if (!IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr)))
+ if (!if_lookup_exact_address(&ip_hdr->ip_dst, AF_INET,
+ pim->vrf_id))
+ return mtrace_forward_packet(pim, ip_hdr);
+
+ if (igmp_msg_len < (int)sizeof(struct igmp_mtrace)) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn(
+ "Recv mtrace packet from %s on %s: too short,"
+ " len=%d, min=%zu",
+ from_str, ifp->name, igmp_msg_len,
+ sizeof(struct igmp_mtrace));
+ return -1;
+ }
+
+ mtracep = (struct igmp_mtrace *)igmp_msg;
+
+ recv_checksum = mtracep->checksum;
+
+ mtracep->checksum = 0;
+
+ checksum = in_cksum(igmp_msg, igmp_msg_len);
+
+ if (recv_checksum != checksum) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn(
+ "Recv mtrace packet from %s on %s: checksum"
+ " mismatch: received=%x computed=%x",
+ from_str, ifp->name, recv_checksum, checksum);
+ return -1;
+ }
+
+ if (PIM_DEBUG_MTRACE)
+ mtrace_debug(pim_ifp, mtracep, igmp_msg_len);
+
+ /* subtract header from message length */
+ r_len = igmp_msg_len - sizeof(struct igmp_mtrace);
+
+ /* Classify mtrace packet, check if it is a query */
+ if (!r_len) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_debug("Received IGMP multicast traceroute query");
+
+ /* 6.1.1 Packet verification */
+ if (!pim_if_connected_to_source(ifp, mtracep->dst_addr)) {
+ if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr))) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_debug(
+ "Dropping multicast query "
+ "on wrong interface");
+ return -1;
+ }
+ /* Unicast query on wrong interface */
+ fwd_code = MTRACE_FWD_CODE_WRONG_IF;
+ }
+ if (qry_id == mtracep->qry_id && qry_src == from.s_addr) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_debug(
+ "Dropping multicast query with "
+ "duplicate source and id");
+ return -1;
+ }
+ qry_id = mtracep->qry_id;
+ qry_src = from.s_addr;
+ }
+ /* if response fields length is equal to a whole number of responses */
+ else if ((r_len % sizeof(struct igmp_mtrace_rsp)) == 0) {
+ r_len = igmp_msg_len - sizeof(struct igmp_mtrace);
+
+ if (r_len != 0)
+ last_rsp_ind = r_len / sizeof(struct igmp_mtrace_rsp);
+ if (last_rsp_ind > MTRACE_MAX_HOPS) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn("Mtrace request of excessive size");
+ return -1;
+ }
+ } else {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn(
+ "Recv mtrace packet from %s on %s: "
+ "invalid length %d",
+ from_str, ifp->name, igmp_msg_len);
+ return -1;
+ }
+
+ /* 6.2.1 Packet Verification - drop not link-local multicast */
+ if (IPV4_CLASS_DE(ntohl(ip_hdr->ip_dst.s_addr))
+ && !IPV4_MC_LINKLOCAL(ntohl(ip_hdr->ip_dst.s_addr))) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn(
+ "Recv mtrace packet from %s on %s:"
+ " not link-local multicast %s",
+ from_str, ifp->name, inet_ntoa(ip_hdr->ip_dst));
+ return -1;
+ }
+
+ /* 6.2.2. Normal Processing */
+
+ /* 6.2.2. 1. */
+
+ if (last_rsp_ind == MTRACE_MAX_HOPS) {
+ mtracep->rsp[MTRACE_MAX_HOPS - 1].fwd_code =
+ MTRACE_FWD_CODE_NO_SPACE;
+ return mtrace_send_response(pim_ifp->pim, mtracep,
+ igmp_msg_len);
+ }
+
+ /* calculate new mtrace mtrace lenght with extra response */
+ mtrace_len = igmp_msg_len + sizeof(struct igmp_mtrace_rsp);
+
+ /* copy received query/request */
+ memcpy(mtrace_buf, igmp_msg, igmp_msg_len);
+
+ /* repoint mtracep pointer to copy */
+ mtracep = (struct igmp_mtrace *)mtrace_buf;
+
+ /* pointer for extra response field to be filled in */
+ rspp = &mtracep->rsp[last_rsp_ind];
+
+ /* initialize extra response field */
+ mtrace_rsp_init(rspp);
+
+ rspp->arrival = htonl(query_arrival_time());
+ rspp->outgoing = pim_ifp->primary_address;
+ rspp->out_count = htonl(MTRACE_UNKNOWN_COUNT);
+
+ /* 6.2.2. 2. Attempt to determine forwarding information */
+
+ nh_addr.s_addr = 0;
+
+ ret = pim_nexthop_lookup(pim, &nexthop, mtracep->src_addr, 1);
+
+ if (ret == 0) {
+ char nexthop_str[INET_ADDRSTRLEN];
+
+ if (PIM_DEBUG_MTRACE)
+ zlog_debug("mtrace pim_nexthop_lookup OK");
+
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn("mtrace next_hop=%s",
+ inet_ntop(nexthop.mrib_nexthop_addr.family,
+ &nexthop.mrib_nexthop_addr.u.prefix,
+ nexthop_str, sizeof(nexthop_str)));
+
+ if (nexthop.mrib_nexthop_addr.family == AF_INET)
+ nh_addr = nexthop.mrib_nexthop_addr.u.prefix4;
+ }
+ /* 6.4 Forwarding Traceroute Requests: ... Otherwise, ... */
+ else {
+ if (PIM_DEBUG_MTRACE)
+ zlog_debug("mtrace not found neighbor");
+ if (!fwd_code)
+ rspp->fwd_code = MTRACE_FWD_CODE_NO_ROUTE;
+ else
+ rspp->fwd_code = fwd_code;
+ /* 6.5 Sending Traceroute Responses */
+ return mtrace_send_response(pim, mtracep, mtrace_len);
+ }
+
+ out_ifp = nexthop.interface;
+ pim_out_ifp = out_ifp->info;
+
+ rspp->incoming = pim_out_ifp->primary_address;
+ rspp->prev_hop = nh_addr;
+ rspp->in_count = htonl(MTRACE_UNKNOWN_COUNT);
+ rspp->total = htonl(MTRACE_UNKNOWN_COUNT);
+ rspp->rtg_proto = MTRACE_RTG_PROTO_PIM;
+ rspp->s = 1;
+ rspp->src_mask = 32;
+
+ if (nh_addr.s_addr == 0) {
+ /* reached source? */
+ if (pim_if_connected_to_source(out_ifp, mtracep->src_addr))
+ return mtrace_send_response(pim, mtracep, mtrace_len);
+ /*
+ * 6.4 Forwarding Traceroute Requests:
+ * Previous-hop router not known
+ */
+ inet_aton(MCAST_ALL_ROUTERS, &nh_addr);
+ }
+
+ if (mtracep->hops <= (last_rsp_ind + 1))
+ return mtrace_send_response(pim, mtracep, mtrace_len);
+
+ mtracep->checksum = 0;
+
+ mtracep->checksum = in_cksum(mtrace_buf, mtrace_len);
+
+ return mtrace_send_packet(out_ifp, mtracep, mtrace_len, nh_addr,
+ mtracep->grp_addr);
+}
+
+int igmp_mtrace_recv_response(struct igmp_sock *igmp, struct ip *ip_hdr,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len)
+{
+ static uint32_t qry_id, rsp_dst;
+ struct interface *ifp;
+ struct pim_interface *pim_ifp;
+ struct pim_instance *pim;
+ struct igmp_mtrace *mtracep;
+ uint16_t recv_checksum;
+ uint16_t checksum;
+
+ ifp = igmp->interface;
+ pim_ifp = ifp->info;
+ pim = pim_ifp->pim;
+
+ mtracep = (struct igmp_mtrace *)igmp_msg;
+
+ recv_checksum = mtracep->checksum;
+
+ mtracep->checksum = 0;
+
+ checksum = in_cksum(igmp_msg, igmp_msg_len);
+
+ if (recv_checksum != checksum) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_warn(
+ "Recv mtrace response from %s on %s: checksum"
+ " mismatch: received=%x computed=%x",
+ from_str, ifp->name, recv_checksum, checksum);
+ return -1;
+ }
+
+ mtracep->checksum = checksum;
+
+ if (PIM_DEBUG_MTRACE)
+ mtrace_debug(pim_ifp, mtracep, igmp_msg_len);
+
+ /* Drop duplicate packets */
+ if (qry_id == mtracep->qry_id && rsp_dst == ip_hdr->ip_dst.s_addr) {
+ if (PIM_DEBUG_MTRACE)
+ zlog_debug("duplicate mtrace response packet dropped");
+ return -1;
+ }
+
+ qry_id = mtracep->qry_id;
+ rsp_dst = ip_hdr->ip_dst.s_addr;
+
+ return mtrace_forward_packet(pim, ip_hdr);
+}
--- /dev/null
+/*
+ * Multicast traceroute for FRRouting
+ * Copyright (C) 2017 Mladen Sablic
+ *
+ * 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 PIM_IGMP_MTRACE_H
+#define PIM_IGMP_MTRACE_H
+
+#include <zebra.h>
+
+#include "pim_igmp.h"
+
+#define MTRACE_MAX_HOPS (255)
+#define MTRACE_UNKNOWN_COUNT (0xffffffff)
+
+enum mtrace_fwd_code {
+ MTRACE_FWD_CODE_NO_ERROR = 0x00,
+ MTRACE_FWD_CODE_WRONG_IF = 0x01,
+ MTRACE_FWD_CODE_PRUNE_SENT = 0x02,
+ MTRACE_FWD_CODE_PRUNE_RCVD = 0x03,
+ MTRACE_FWD_CODE_SCOPED = 0x04,
+ MTRACE_FWD_CODE_NO_ROUTE = 0x05,
+ MTRACE_FWD_CODE_WRONG_LAST_HOP = 0x06,
+ MTRACE_FWD_CODE_NOT_FORWARDING = 0x07,
+ MTRACE_FWD_CODE_REACHED_RP = 0x08,
+ MTRACE_FWD_CODE_RPF_IF = 0x09,
+ MTRACE_FWD_CODE_NO_MULTICAST = 0x0A,
+ MTRACE_FWD_CODE_INFO_HIDDEN = 0x0B,
+ MTRACE_FWD_CODE_NO_SPACE = 0x81,
+ MTRACE_FWD_CODE_OLD_ROUTER = 0x82,
+ MTRACE_FWD_CODE_ADMIN_PROHIB = 0x83
+};
+
+enum mtrace_rtg_proto {
+ MTRACE_RTG_PROTO_DVMRP = 1,
+ MTRACE_RTG_PROTO_MOSPF = 2,
+ MTRACE_RTG_PROTO_PIM = 3,
+ MTRACE_RTG_PROTO_CBT = 4,
+ MTRACE_RTG_PROTO_PIM_SPECIAL = 5,
+ MTRACE_RTG_PROTO_PIM_STATIC = 6,
+ MTRACE_RTG_PROTO_DVMRP_STATIC = 7,
+ MTRACE_RTG_PROTO_PIM_MBGP = 8,
+ MTRACE_RTG_PROTO_CBT_SPECIAL = 9,
+ MTRACE_RTG_PROTO_CBT_STATIC = 10,
+ MTRACE_RTG_PROTO_PIM_ASSERT = 11,
+};
+
+struct igmp_mtrace_rsp {
+ uint32_t arrival;
+ struct in_addr incoming;
+ struct in_addr outgoing;
+ struct in_addr prev_hop;
+ uint32_t in_count;
+ uint32_t out_count;
+ uint32_t total;
+ uint32_t rtg_proto : 8;
+ uint32_t fwd_ttl : 8;
+ /* little endian order for next three fields */
+ uint32_t src_mask : 6;
+ uint32_t s : 1;
+ uint32_t mbz : 1;
+ uint32_t fwd_code : 8;
+} __attribute__((packed));
+
+struct igmp_mtrace {
+ uint8_t type;
+ uint8_t hops;
+ uint16_t checksum;
+ struct in_addr grp_addr;
+ struct in_addr src_addr;
+ struct in_addr dst_addr;
+ struct in_addr rsp_addr;
+ uint32_t rsp_ttl : 8;
+ uint32_t qry_id : 24;
+ struct igmp_mtrace_rsp rsp[0];
+} __attribute__((packed));
+
+#define MTRACE_HDR_SIZE (sizeof(struct igmp_mtrace))
+#define MTRACE_RSP_SIZE (sizeof(struct igmp_mtrace_rsp))
+
+int igmp_mtrace_recv_qry_req(struct igmp_sock *igmp, struct ip *ip_hdr,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len);
+
+int igmp_mtrace_recv_response(struct igmp_sock *igmp, struct ip *ip_hdr,
+ struct in_addr from, const char *from_str,
+ char *igmp_msg, int igmp_msg_len);
+
+#endif /* PIM_IGMP_MTRACE_H */
XFREE(MTYPE_PIM_CHANNEL_OIL, c_oil);
}
-static struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
- struct prefix_sg *sg)
+struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
+ struct prefix_sg *sg)
{
struct channel_oil *c_oil = NULL;
struct channel_oil lookup;
void pim_oil_terminate(struct pim_instance *pim);
void pim_channel_oil_free(struct channel_oil *c_oil);
+struct channel_oil *pim_find_channel_oil(struct pim_instance *pim,
+ struct prefix_sg *sg);
struct channel_oil *pim_channel_oil_add(struct pim_instance *pim,
struct prefix_sg *sg,
int input_vif_index);
++writes;
}
+ if (PIM_DEBUG_MTRACE) {
+ vty_out(vty, "debug mtrace\n");
+ ++writes;
+ }
+
if (PIM_DEBUG_MROUTE_DETAIL) {
vty_out(vty, "debug mroute detail\n");
++writes;
#define PIM_MASK_PIM_NHT (1 << 22)
#define PIM_MASK_PIM_NHT_DETAIL (1 << 23)
#define PIM_MASK_PIM_NHT_RP (1 << 24)
+#define PIM_MASK_MTRACE (1 << 25)
/* Remember 32 bits!!! */
/* PIM error codes */
#define PIM_DEBUG_PIM_NHT (qpim_debugs & PIM_MASK_PIM_NHT)
#define PIM_DEBUG_PIM_NHT_DETAIL (qpim_debugs & PIM_MASK_PIM_NHT_DETAIL)
#define PIM_DEBUG_PIM_NHT_RP (qpim_debugs & PIM_MASK_PIM_NHT_RP)
+#define PIM_DEBUG_MTRACE (qpim_debugs & PIM_MASK_MTRACE)
#define PIM_DEBUG_EVENTS (qpim_debugs & (PIM_MASK_PIM_EVENTS | PIM_MASK_IGMP_EVENTS | PIM_MASK_MSDP_EVENTS))
#define PIM_DEBUG_PACKETS (qpim_debugs & (PIM_MASK_PIM_PACKETS | PIM_MASK_IGMP_PACKETS | PIM_MASK_MSDP_PACKETS))
#define PIM_DO_DEBUG_MSDP_INTERNAL (qpim_debugs |= PIM_MASK_MSDP_INTERNAL)
#define PIM_DO_DEBUG_PIM_NHT (qpim_debugs |= PIM_MASK_PIM_NHT)
#define PIM_DO_DEBUG_PIM_NHT_RP (qpim_debugs |= PIM_MASK_PIM_NHT_RP)
+#define PIM_DO_DEBUG_MTRACE (qpim_debugs |= PIM_MASK_MTRACE)
#define PIM_DONT_DEBUG_PIM_EVENTS (qpim_debugs &= ~PIM_MASK_PIM_EVENTS)
#define PIM_DONT_DEBUG_PIM_PACKETS (qpim_debugs &= ~PIM_MASK_PIM_PACKETS)
#define PIM_DONT_DEBUG_MSDP_INTERNAL (qpim_debugs &= ~PIM_MASK_MSDP_INTERNAL)
#define PIM_DONT_DEBUG_PIM_NHT (qpim_debugs &= ~PIM_MASK_PIM_NHT)
#define PIM_DONT_DEBUG_PIM_NHT_RP (qpim_debugs &= ~PIM_MASK_PIM_NHT_RP)
+#define PIM_DONT_DEBUG_MTRACE (qpim_debugs &= ~PIM_MASK_MTRACE)
void pim_init(void);
void pim_terminate(void);
if PIMD
noinst_LIBRARIES += pimd/libpim.a
sbin_PROGRAMS += pimd/pimd
+bin_PROGRAMS += pimd/mtracebis
noinst_PROGRAMS += pimd/test_igmpv3_join
dist_examples_DATA += pimd/pimd.conf.sample
endif
pimd/pim_iface.c \
pimd/pim_ifchannel.c \
pimd/pim_igmp.c \
+ pimd/pim_igmp_mtrace.c \
pimd/pim_igmpv2.c \
pimd/pim_igmpv3.c \
pimd/pim_instance.c \
pimd/pim_ifchannel.h \
pimd/pim_igmp.h \
pimd/pim_igmp_join.h \
+ pimd/pim_igmp_mtrace.h \
pimd/pim_igmpv2.h \
pimd/pim_igmpv3.h \
pimd/pim_instance.h \
pimd/pim_zebra.h \
pimd/pim_zlookup.h \
pimd/pimd.h \
+ pimd/mtracebis_netlink.h \
+ pimd/mtracebis_routeget.h \
# end
pimd_pimd_LDADD = pimd/libpim.a lib/libfrr.la @LIBCAP@
pimd_test_igmpv3_join_LDADD = lib/libfrr.la
pimd_test_igmpv3_join_SOURCES = pimd/test_igmpv3_join.c
+
+pimd_mtracebis_LDADD = lib/libfrr.la
+pimd_mtracebis_SOURCES = pimd/mtracebis.c \
+ pimd/mtracebis_netlink.c \
+ pimd/mtracebis_routeget.c \
+ # end
+++ /dev/null
-#!/usr/bin/env python
-# written 2016 by David Lamparter, placed in Public Domain.
-import sys, markdown
-
-template = '''<html><head><meta charset="UTF-8"><style type="text/css">
-body { max-width: 45em; margin: auto; margin-top: 2em; margin-bottom: 2em;
- font-family:Fira Sans,sans-serif; text-align: justify;
- counter-reset: ch2; }
-pre, code { font-family:Fira Mono,monospace; }
-pre > code { display: block; padding:0.5em; border:1px solid black;
- background-color:#eee; color:#000; }
-h2:before { content: counter(ch2) ". "; counter-increment: ch2; }
-h2 { clear: both; margin-top: 3em; text-decoration: underline; counter-reset: ch3; }
-h3:before { content: counter(ch2) "." counter(ch3) ". "; counter-increment: ch3; }
-h3 { clear: both; margin-top: 2em; font-weight: normal; font-style: italic; }
-h4 { font-weight: normal; font-style: italic; }
-img[alt~="float-right"] { float:right; margin-left:2em; margin-bottom:2em; }
-</style></head><body>
-%s
-</body></html>
-'''
-
-md = markdown.Markdown(extensions=['extra', 'toc'])
-
-for fn in sys.argv[1:]:
- with open(fn, 'r') as ifd:
- with open('%s.html' % (fn), 'w') as ofd:
- ofd.write((template % (md.convert(ifd.read().decode('UTF-8')))).encode('UTF-8'))
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, ¬e))
+ if (!zapi_route_notify_decode(zclient->ibuf, &p, &table, ¬e))
return -1;
installed_routes++;
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;
}
my $spelling_file = "$D/spelling.txt";
my $codespell = 0;
my $codespellfile = "/usr/share/codespell/dictionary.txt";
-my $conststructsfile = "$D/const_structs.checkpatch";
my $typedefsfile = "";
my $color = "auto";
my $allow_c99_comments = 1;
return 0;
}
-my $const_structs = "";
-read_words(\$const_structs, $conststructsfile)
- or warn "No structs that should be const will be found - file '$conststructsfile': $!\n";
-
my $typeOtherTypedefs = "";
if (length($typedefsfile)) {
read_words(\$typeOtherTypedefs, $typedefsfile)
$rpt_cleaners = 1;
}
-# Check for FSF mailing addresses.
- if ($rawline =~ /\bwrite to the Free/i ||
- $rawline =~ /\b675\s+Mass\s+Ave/i ||
- $rawline =~ /\b59\s+Temple\s+Pl/i ||
- $rawline =~ /\b51\s+Franklin\s+St/i) {
- my $herevet = "$here\n" . cat_vet($rawline) . "\n";
- my $msg_level = \&ERROR;
- $msg_level = \&CHK if ($file);
- &{$msg_level}("FSF_MAILING_ADDRESS",
- "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.\n" . $herevet)
- }
-
# check for Kconfig help text having a real description
# Only applies when adding the entry originally, after that we do not have
# sufficient context to determine whether it is indeed long enough.
# likely a typedef for a function.
} elsif ($ctx =~ /$Type$/) {
+ # All-uppercase function names are usually macros,
+ # ignore those
+ } elsif ($name eq uc $name) {
+
} else {
if (WARN("SPACING",
"space prohibited between function name and open parenthesis '('\n" . $herecurr) &&
"please use device_initcall() or more appropriate function instead of __initcall() (see include/linux/init.h)\n" . $herecurr);
}
-# check for various structs that are normally const (ops, kgdb, device_tree)
-# and avoid what seem like struct definitions 'struct foo {'
- if ($line !~ /\bconst\b/ &&
- $line =~ /\bstruct\s+($const_structs)\b(?!\s*\{)/) {
- WARN("CONST_STRUCT",
- "struct $1 should normally be const\n" . $herecurr);
- }
-
# use of NR_CPUS is usually wrong
# ignore definitions of NR_CPUS and usage to define arrays as likely right
if ($line =~ /\bNR_CPUS\b/ &&
if [ -z "$mod" ]; then
echo "There doesn't seem to be any changes."
else
- cp $tree/$mod /tmp/f1/
+ echo "Copying source to temp directory..."
+ for file in $mod; do
+ echo "$tree/$file --> /tmp/f1/$file"
+ cp $tree/$file /tmp/f1/
+ done
git -C $tree reset --hard
git -C $tree clean -fd
- cp $tree/$mod /tmp/f2/
+ for file in $mod; do
+ if [ -f $tree/$file ]; then
+ echo "$tree/$file --> /tmp/f2/$file"
+ cp $tree/$file /tmp/f2/
+ fi
+ done
echo "Running style checks..."
for file in /tmp/f1/*; do
echo "$checkpatch $file > $file _cp"
done
echo "Done."
for file in /tmp/f1/*_cp; do
- echo "Report for $(basename $file _cp)"
- echo "==============================================="
if [ -a /tmp/f2/$(basename $file) ]; then
- diff $file /tmp/f2/$(basename $file) | grep -v "normally be const" | grep -A3 "ERROR\|WARNING"
+ result=$(diff $file /tmp/f2/$(basename $file) | grep -A3 "ERROR\|WARNING" | grep -A2 -B2 '/tmp/f1')
else
- cat $file | grep -v "normally be const" | grep -A3 "ERROR\|WARNING"
+ result=$(cat $file | grep -A3 "ERROR\|WARNING" | grep -A2 -B2 '/tmp/f1')
fi
if [ "$?" -eq "0" ]; then
- stat=1
+ echo "Report for $(basename $file _cp)" 1>&2
+ echo "===============================================" 1>&2
+ echo "$result" 1>&2
+ if echo $result | grep -q "ERROR"; then
+ stat=2
+ elif [ "$stat" -eq "0" ]; then
+ stat=1
+ fi
fi
done
fi
--- /dev/null
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sys, os
+import subprocess, argparse, tempfile
+import indent
+
+def run(cmd):
+ proc = subprocess.Popen(cmd, stdout = subprocess.PIPE)
+ rv = proc.communicate('')[0].decode('UTF-8')
+ proc.wait()
+ return rv
+
+clangfmt = run(['git', 'show', 'master:.clang-format'])
+
+argp = argparse.ArgumentParser(description = 'git whitespace-fixing tool')
+argp.add_argument('branch', metavar='BRANCH', type = str, nargs = '?', default = 'HEAD')
+args = argp.parse_args()
+
+branch = args.branch
+commit = run(['git', 'rev-list', '-n', '1', branch, '--']).strip()
+
+# frr-3.1-dev = first commit that is on master but not on stable/3.0
+masterid = run(['git', 'rev-list', '-n', '1', 'frr-3.1-dev', '--']).strip()
+masterbase = run(['git', 'merge-base', commit, masterid]).strip()
+
+if masterbase == masterid:
+ refbranch = 'master'
+else:
+ refbranch = '3.0'
+
+sys.stderr.write('autodetected base: %s (can be 3.0 or master)\n' % refbranch)
+
+beforeid = run(['git', 'rev-list', '-n', '1', 'reindent-%s-before' % refbranch, '--']).strip()
+afterid = run(['git', 'rev-list', '-n', '1', 'reindent-%s-after' % refbranch, '--']).strip()
+
+beforebase = run(['git', 'merge-base', commit, beforeid]).strip()
+afterbase = run(['git', 'merge-base', commit, afterid]).strip()
+
+if afterbase == afterid:
+ sys.stderr.write('this branch was already rebased\n')
+ sys.exit(1)
+
+if beforebase != beforeid:
+ sys.stderr.write('you need to rebase your branch onto the tag "reindent-%s-before"\n' % refbranch)
+ sys.exit(1)
+
+revs = run(['git', 'rev-list', 'reindent-%s-before..%s' % (refbranch, commit)]).strip().split('\n')
+revs.reverse()
+
+srcdir = os.getcwd()
+tmpdir = tempfile.mkdtemp('frrindent')
+os.chdir(tmpdir)
+
+sys.stderr.write('using temporary directory %s; %d revisions\n' % (tmpdir, len(revs)))
+run(['git', 'clone', '-s', '-b', 'reindent-%s-after' % refbranch, srcdir, 'repo'])
+os.chdir('repo')
+
+with open('.clang-format', 'w') as fd:
+ fd.write(clangfmt)
+
+prev = beforeid
+for rev in revs:
+ filestat = run(['git', 'diff', '-z', '--name-status', prev, rev]).rstrip('\0').split('\0')
+ changes = zip(filestat[0::2], filestat[1::2])
+ sys.stderr.write('%s: %d files\n' % (rev, len(changes)))
+
+ for typ, name in changes:
+ if typ == 'D':
+ run(['git', 'rm', name])
+ elif typ in ['A', 'M']:
+ run(['git', 'checkout', rev, '--', name])
+ if name.endswith('.c') or name.endswith('.h'):
+ for d in ['babeld/', 'ldpd/', 'nhrpd/']:
+ if name.startswith(d):
+ break
+ else:
+ sys.stderr.write('\t%s\n' % name)
+ indent.wrap_file(name)
+ run(['git', 'add', name])
+
+ run(['git', 'commit', '-C', rev])
+ prev = rev
+
+run(['git', 'push', 'origin', 'HEAD:refs/heads/reindented-branch'])
+sys.stderr.write('\n\n"reindented-branch" should now be OK.\n')
+sys.stderr.write('you could use "git reset --hard reindented-branch" to set your current branch to the reindented output\n')
+sys.stderr.write('\033[31;1mplease always double-check the output\033[m\n')
+
--- /dev/null
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# 2017 by David Lamparter, placed in public domain
+
+import sys, re, subprocess, os
+
+# find all DEFUNs
+defun_re = re.compile(
+ r'^((DEF(UN(_NOSH|_HIDDEN)?|PY)|ALIAS)\s*\(.*?)^(?=\s*\{)',
+ re.M | re.S)
+define_re = re.compile(
+ r'((^#\s*define[^\n]+[^\\]\n)+)',
+ re.M | re.S)
+# find clang-format control that we just inserted
+clean_re = re.compile(
+ r'^/\* \$FRR indent\$ \*/\s*\n\s*/\* clang-format (on|off) \*/\s*\n',
+ re.M)
+
+def wrap_file(fn):
+ with open(fn, 'r') as fd:
+ text = fd.read()
+
+ repl = r'/* $FRR indent$ */\n/* clang-format off */\n' + \
+ r'\1' + \
+ r'/* $FRR indent$ */\n/* clang-format on */\n'
+
+ # around each DEFUN, insert an indent-on/off comment
+ text = defun_re.sub(repl, text)
+ text = define_re.sub(repl, text)
+
+ ci = subprocess.Popen(['clang-format'], stdin = subprocess.PIPE, stdout = subprocess.PIPE)
+ stdout, ign = ci.communicate(text)
+ ci.wait()
+ if ci.returncode != 0:
+ raise IOError('clang-format returned %d' % (ci.returncode))
+
+ # remove the bits we inserted above
+ final = clean_re.sub('', stdout)
+
+ tmpname = fn + '.indent'
+ with open(tmpname, 'w') as ofd:
+ ofd.write(final)
+ os.rename(tmpname, fn)
+
+if __name__ == '__main__':
+ for fn in sys.argv[1:]:
+ wrap_file(fn)
--- /dev/null
+#!/usr/bin/env python
+# written 2016 by David Lamparter, placed in Public Domain.
+import sys, markdown
+
+template = '''<html><head><meta charset="UTF-8"><style type="text/css">
+body { max-width: 45em; margin: auto; margin-top: 2em; margin-bottom: 2em;
+ font-family:Fira Sans,sans-serif; text-align: justify;
+ counter-reset: ch2; }
+pre, code { font-family:Fira Mono,monospace; }
+pre > code { display: block; padding:0.5em; border:1px solid black;
+ background-color:#eee; color:#000; }
+h2:before { content: counter(ch2) ". "; counter-increment: ch2; }
+h2 { clear: both; margin-top: 3em; text-decoration: underline; counter-reset: ch3; }
+h3:before { content: counter(ch2) "." counter(ch3) ". "; counter-increment: ch3; }
+h3 { clear: both; margin-top: 2em; font-weight: normal; font-style: italic; }
+h4 { font-weight: normal; font-style: italic; }
+img[alt~="float-right"] { float:right; margin-left:2em; margin-bottom:2em; }
+</style></head><body>
+%s
+</body></html>
+'''
+
+md = markdown.Markdown(extensions=['extra', 'toc'])
+
+for fn in sys.argv[1:]:
+ with open(fn, 'r') as ifd:
+ with open('%s.html' % (fn), 'w') as ofd:
+ ofd.write((template % (md.convert(ifd.read().decode('UTF-8')))).encode('UTF-8'))
$(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 \
elsif ($file =~ /lib\/vrf\.c$/) {
$protocol = "VTYSH_ALL";
}
+ elsif ($file =~ /lib\/logicalrouter\.c$/) {
+ $protocol = "VTYSH_ALL";
+ }
elsif ($file =~ /lib\/filter\.c$/) {
$protocol = "VTYSH_ALL";
}
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 = {
break;
case INTERFACE_NODE:
case PW_NODE:
- case NS_NODE:
+ case LOGICALROUTER_NODE:
case VRF_NODE:
case ZEBRA_NODE:
case BGP_NODE:
"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")
"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",
"IP trace\n"
"Trace route to destination address or hostname\n")
+DEFUN (vtysh_mtrace,
+ vtysh_mtrace_cmd,
+ "mtrace WORD",
+ "Multicast trace route to multicast source\n"
+ "Multicast trace route to multicast source address\n")
+{
+ int idx = 1;
+
+ argv_find(argv, argc, "WORD", &idx);
+ execute_command("mtracebis", 1, argv[idx]->arg, NULL);
+ return CMD_SUCCESS;
+}
+
DEFUN (vtysh_ping6,
vtysh_ping6_cmd,
"ping ipv6 WORD",
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);
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);
install_element(VIEW_NODE, &vtysh_ping_ip_cmd);
install_element(VIEW_NODE, &vtysh_traceroute_cmd);
install_element(VIEW_NODE, &vtysh_traceroute_ip_cmd);
+ install_element(VIEW_NODE, &vtysh_mtrace_cmd);
install_element(VIEW_NODE, &vtysh_ping6_cmd);
install_element(VIEW_NODE, &vtysh_traceroute6_cmd);
#if defined(HAVE_SHELL_ACCESS)
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);
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)
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;
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 {
#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. */
}
}
-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));
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(
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,
}
/* 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)) {
} 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])
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)
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);
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",
}
/* 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;
}
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]);
} 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 */
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);
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
*
* 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++;
}
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();
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",
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)
{
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",
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)
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;
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;
}
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");
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");
/* 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);
#include "log.h"
#include "privs.h"
#include "vty.h"
+#include "vrf.h"
#include "zebra/rib.h"
#include "zebra/rt.h"
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)
{
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));
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))
#include "privs.h"
#include "sigevent.h"
#include "vrf.h"
+#include "logicalrouter.h"
#include "libfrr.h"
#include "zebra/rib.h"
#include "zebra/redistribute.h"
#include "zebra/zebra_mpls.h"
#include "zebra/label_manager.h"
+#include "zebra/zebra_netns_notify.h"
#define ZEBRA_PTM_SUPPORT
{"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}};
{
struct vrf *vrf;
struct zebra_vrf *zvrf;
- struct zebra_ns *zns;
zlog_notice("Terminating on signal");
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();
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();
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:"
" -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)
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':
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);
/*
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;
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};
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))
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)
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]);
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. */
{
struct mpls_label_stack *nh_label;
mpls_lse_t out_lse[MPLS_MAX_LABELS];
+ int num_labels = 0;
char label_buf[256];
/*
* you fix this assumption
*/
label_buf[0] = '\0';
- /* outgoing label - either as NEWDST (in the case of LSR) or as ENCAP
- * (in the case of LER)
- */
- nh_label = nexthop->nh_label;
- if (rtmsg->rtm_family == AF_MPLS) {
- assert(nh_label);
- assert(nh_label->num_labels == 1);
- }
- if (nh_label && nh_label->num_labels) {
- int i, num_labels = 0;
- u_int32_t bos;
+ assert(nexthop);
+ for (struct nexthop *nh = nexthop; nh; nh = nh->rparent) {
char label_buf1[20];
- for (i = 0; i < nh_label->num_labels; i++) {
- if (nh_label->label[i] != MPLS_LABEL_IMPLICIT_NULL) {
- bos = ((i == (nh_label->num_labels - 1)) ? 1
- : 0);
- out_lse[i] = mpls_lse_encode(nh_label->label[i],
- 0, 0, bos);
- if (IS_ZEBRA_DEBUG_KERNEL) {
- if (!num_labels)
- sprintf(label_buf, "label %u",
- nh_label->label[i]);
- else {
- sprintf(label_buf1, "/%u",
- nh_label->label[i]);
- strlcat(label_buf, label_buf1,
- sizeof(label_buf));
- }
+ nh_label = nh->nh_label;
+ if (!nh_label || !nh_label->num_labels)
+ continue;
+
+ for (int i = 0; i < nh_label->num_labels; i++) {
+ if (nh_label->label[i] == MPLS_LABEL_IMPLICIT_NULL)
+ continue;
+
+ if (IS_ZEBRA_DEBUG_KERNEL) {
+ if (!num_labels)
+ sprintf(label_buf, "label %u",
+ nh_label->label[i]);
+ else {
+ sprintf(label_buf1, "/%u",
+ nh_label->label[i]);
+ strlcat(label_buf, label_buf1,
+ sizeof(label_buf));
}
- num_labels++;
}
+
+ out_lse[num_labels] =
+ mpls_lse_encode(nh_label->label[i], 0, 0, 0);
+ num_labels++;
}
- if (num_labels) {
- if (rtmsg->rtm_family == AF_MPLS)
- addattr_l(nlmsg, req_size, RTA_NEWDST, &out_lse,
- num_labels * sizeof(mpls_lse_t));
- else {
- struct rtattr *nest;
- u_int16_t encap = LWTUNNEL_ENCAP_MPLS;
-
- addattr_l(nlmsg, req_size, RTA_ENCAP_TYPE,
- &encap, sizeof(u_int16_t));
- nest = addattr_nest(nlmsg, req_size, RTA_ENCAP);
- addattr_l(nlmsg, req_size, MPLS_IPTUNNEL_DST,
- &out_lse,
- num_labels * sizeof(mpls_lse_t));
- addattr_nest_end(nlmsg, nest);
- }
+ }
+
+ if (num_labels) {
+ /* Set the BoS bit */
+ out_lse[num_labels - 1] |= htonl(1 << MPLS_LS_S_SHIFT);
+
+ if (rtmsg->rtm_family == AF_MPLS)
+ addattr_l(nlmsg, req_size, RTA_NEWDST, &out_lse,
+ num_labels * sizeof(mpls_lse_t));
+ else {
+ struct rtattr *nest;
+ u_int16_t encap = LWTUNNEL_ENCAP_MPLS;
+
+ addattr_l(nlmsg, req_size, RTA_ENCAP_TYPE, &encap,
+ sizeof(u_int16_t));
+ nest = addattr_nest(nlmsg, req_size, RTA_ENCAP);
+ addattr_l(nlmsg, req_size, MPLS_IPTUNNEL_DST, &out_lse,
+ num_labels * sizeof(mpls_lse_t));
+ addattr_nest_end(nlmsg, nest);
}
}
{
struct mpls_label_stack *nh_label;
mpls_lse_t out_lse[MPLS_MAX_LABELS];
+ int num_labels = 0;
char label_buf[256];
rtnh->rtnh_len = sizeof(*rtnh);
* you fix this assumption
*/
label_buf[0] = '\0';
- /* outgoing label - either as NEWDST (in the case of LSR) or as ENCAP
- * (in the case of LER)
- */
- nh_label = nexthop->nh_label;
- if (rtmsg->rtm_family == AF_MPLS) {
- assert(nh_label);
- assert(nh_label->num_labels == 1);
- }
- if (nh_label && nh_label->num_labels) {
- int i, num_labels = 0;
- u_int32_t bos;
+ assert(nexthop);
+ for (struct nexthop *nh = nexthop; nh; nh = nh->rparent) {
char label_buf1[20];
- for (i = 0; i < nh_label->num_labels; i++) {
- if (nh_label->label[i] != MPLS_LABEL_IMPLICIT_NULL) {
- bos = ((i == (nh_label->num_labels - 1)) ? 1
- : 0);
- out_lse[i] = mpls_lse_encode(nh_label->label[i],
- 0, 0, bos);
- if (IS_ZEBRA_DEBUG_KERNEL) {
- if (!num_labels)
- sprintf(label_buf, "label %u",
- nh_label->label[i]);
- else {
- sprintf(label_buf1, "/%u",
- nh_label->label[i]);
- strlcat(label_buf, label_buf1,
- sizeof(label_buf));
- }
+ nh_label = nh->nh_label;
+ if (!nh_label || !nh_label->num_labels)
+ continue;
+
+ for (int i = 0; i < nh_label->num_labels; i++) {
+ if (nh_label->label[i] == MPLS_LABEL_IMPLICIT_NULL)
+ continue;
+
+ if (IS_ZEBRA_DEBUG_KERNEL) {
+ if (!num_labels)
+ sprintf(label_buf, "label %u",
+ nh_label->label[i]);
+ else {
+ sprintf(label_buf1, "/%u",
+ nh_label->label[i]);
+ strlcat(label_buf, label_buf1,
+ sizeof(label_buf));
}
- num_labels++;
}
+
+ out_lse[num_labels] =
+ mpls_lse_encode(nh_label->label[i], 0, 0, 0);
+ num_labels++;
}
- if (num_labels) {
- if (rtmsg->rtm_family == AF_MPLS) {
- rta_addattr_l(rta, NL_PKT_BUF_SIZE, RTA_NEWDST,
- &out_lse,
- num_labels * sizeof(mpls_lse_t));
- rtnh->rtnh_len += RTA_LENGTH(
- num_labels * sizeof(mpls_lse_t));
- } else {
- struct rtattr *nest;
- u_int16_t encap = LWTUNNEL_ENCAP_MPLS;
- int len = rta->rta_len;
-
- rta_addattr_l(rta, NL_PKT_BUF_SIZE,
- RTA_ENCAP_TYPE, &encap,
- sizeof(u_int16_t));
- nest = rta_nest(rta, NL_PKT_BUF_SIZE,
- RTA_ENCAP);
- rta_addattr_l(rta, NL_PKT_BUF_SIZE,
- MPLS_IPTUNNEL_DST, &out_lse,
- num_labels * sizeof(mpls_lse_t));
- rta_nest_end(rta, nest);
- rtnh->rtnh_len += rta->rta_len - len;
- }
+ }
+
+ if (num_labels) {
+ /* Set the BoS bit */
+ out_lse[num_labels - 1] |= htonl(1 << MPLS_LS_S_SHIFT);
+
+ if (rtmsg->rtm_family == AF_MPLS) {
+ rta_addattr_l(rta, NL_PKT_BUF_SIZE, RTA_NEWDST,
+ &out_lse,
+ num_labels * sizeof(mpls_lse_t));
+ rtnh->rtnh_len +=
+ RTA_LENGTH(num_labels * sizeof(mpls_lse_t));
+ } else {
+ struct rtattr *nest;
+ u_int16_t encap = LWTUNNEL_ENCAP_MPLS;
+ int len = rta->rta_len;
+
+ rta_addattr_l(rta, NL_PKT_BUF_SIZE, RTA_ENCAP_TYPE,
+ &encap, sizeof(u_int16_t));
+ nest = rta_nest(rta, NL_PKT_BUF_SIZE, RTA_ENCAP);
+ rta_addattr_l(rta, NL_PKT_BUF_SIZE, MPLS_IPTUNNEL_DST,
+ &out_lse,
+ num_labels * sizeof(mpls_lse_t));
+ rta_nest_end(rta, nest);
+ rtnh->rtnh_len += rta->rta_len - len;
}
}
}
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;
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));
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);
} 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));
}
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);
}
/*
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));
#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;
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;
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 */
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;
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",
| 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;
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;
* 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;
/* 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;
}
} 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));
}
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;
#include "command.h"
#include "privs.h"
#include "vrf.h"
+#include "ns.h"
#include "zebra/interface.h"
#include "zebra/rtadv.h"
return 0;
}
-static int rtadv_make_socket(void)
+static int rtadv_make_socket(ns_id_t ns_id)
{
int sock;
int ret = 0;
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",
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)
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)
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 = \
--- /dev/null
+/* 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 */
+}
+
--- /dev/null
+/* 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__ */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
/* zebra NS Routines
* Copyright (C) 2016 Cumulus Networks, Inc.
* Donald Sharp
+ * Copyright (C) 2017/2018 6WIND
*
* This file is part of Quagga.
*
#include "lib/ns.h"
#include "lib/vrf.h"
+#include "lib/logicalrouter.h"
#include "lib/prefix.h"
#include "lib/memory.h"
#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. */
{
struct zebra_ns *zns = (struct zebra_ns *)(*info);
+ zns->ns_id = ns_id;
+
#if defined(HAVE_RTADV)
rtadv_init(zns);
#endif
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)
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();
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;
}
};
#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];
#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
{
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,
if (set) {
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE);
- zebra_deregister_rnh_static_nexthops(nexthop->vrf_id,
- nexthop->resolved, top);
nexthops_free(nexthop->resolved);
nexthop->resolved = NULL;
re->nexthop_mtu = 0;
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:
/*
*/
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;
* 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
*/
hook_call(rib_update, rn, "uninstalling from kernel");
kernel_route_rib(rn, p, src_p, re, NULL);
- zvrf->removals++;
+ if (zvrf)
+ zvrf->removals++;
return;
}
dest->selected_fib = NULL;
/* free RE and nexthops */
- zebra_deregister_rnh_static_nexthops(re->vrf_id, re->nexthop, rn);
+ if (re->type == ZEBRA_ROUTE_STATIC)
+ zebra_deregister_rnh_static_nexthops(re->vrf_id,
+ re->nexthop, rn);
nexthops_free(re->nexthop);
XFREE(MTYPE_RE, re);
}
#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"
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));
}
}
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;
}
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.
*/
struct static_route *si;
struct route_table *table;
struct interface *ifp;
- u_int32_t table_id;
afi_t afi;
safi_t safi;
unsigned i;
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. */
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);
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]);
{
struct zebra_vrf *zvrf = vrf->info;
struct route_table *table;
- u_int32_t table_id;
afi_t afi;
safi_t safi;
unsigned i;
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]);
}
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;
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];
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");
}
/* 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);
}
* 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>
/* 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
*/
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);
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 */
}
}
-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();
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;
}
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);
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;
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;
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);
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;
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;
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);
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: ");
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));
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++;
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);
}
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);
}
/* 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,
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;
/* 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
*/
#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);
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 *);
/* 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;
#include <zebra.h>
#include <sys/un.h>
+/* for basename */
+#include <libgen.h>
#include "prefix.h"
#include "command.h"
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));
}
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, ¬e, sizeof(note));
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);
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 *,