diff options
191 files changed, 9568 insertions, 2161 deletions
diff --git a/.clang-format b/.clang-format index 4bd962747f..654577d936 100644 --- a/.clang-format +++ b/.clang-format @@ -28,6 +28,8 @@ ForEachMacros:    - frr_each    - frr_each_safe    - frr_each_from +  - frr_with_mutex +  - frr_with_privs    - LIST_FOREACH    - LIST_FOREACH_SAFE    - SLIST_FOREACH diff --git a/bfdd/bfd.c b/bfdd/bfd.c index 5d143d4e5f..1f1568f511 100644 --- a/bfdd/bfd.c +++ b/bfdd/bfd.c @@ -1706,6 +1706,8 @@ static int bfd_vrf_disable(struct vrf *vrf)  	socket_close(&bvrf->bg_mhop);  	socket_close(&bvrf->bg_shop6);  	socket_close(&bvrf->bg_mhop6); +	socket_close(&bvrf->bg_echo); +	socket_close(&bvrf->bg_echov6);  	/* free context */  	XFREE(MTYPE_BFDD_VRF, bvrf); diff --git a/bfdd/bfd_packet.c b/bfdd/bfd_packet.c index d68a1ad5fd..7fbe6db163 100644 --- a/bfdd/bfd_packet.c +++ b/bfdd/bfd_packet.c @@ -894,7 +894,7 @@ int bp_udp_shop(vrf_id_t vrf_id)  {  	int sd; -	frr_elevate_privs(&bglobal.bfdd_privs) { +	frr_with_privs(&bglobal.bfdd_privs) {  		sd = vrf_socket(AF_INET, SOCK_DGRAM, PF_UNSPEC, vrf_id, NULL);  	}  	if (sd == -1) @@ -909,7 +909,7 @@ int bp_udp_mhop(vrf_id_t vrf_id)  {  	int sd; -	frr_elevate_privs(&bglobal.bfdd_privs) { +	frr_with_privs(&bglobal.bfdd_privs) {  		sd = vrf_socket(AF_INET, SOCK_DGRAM, PF_UNSPEC, vrf_id, NULL);  	}  	if (sd == -1) @@ -934,7 +934,7 @@ int bp_peer_socket(const struct bfd_session *bs)  	    && bs->key.vrfname[0])  		device_to_bind = (const char *)bs->key.vrfname; -	frr_elevate_privs(&bglobal.bfdd_privs) { +	frr_with_privs(&bglobal.bfdd_privs) {  		sd = vrf_socket(AF_INET, SOCK_DGRAM, PF_UNSPEC,  				bs->vrf->vrf_id, device_to_bind);  	} @@ -1001,7 +1001,7 @@ int bp_peer_socketv6(const struct bfd_session *bs)  	    && bs->key.vrfname[0])  		device_to_bind = (const char *)bs->key.vrfname; -	frr_elevate_privs(&bglobal.bfdd_privs) { +	frr_with_privs(&bglobal.bfdd_privs) {  		sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC,  				bs->vrf->vrf_id, device_to_bind);  	} @@ -1121,7 +1121,7 @@ int bp_udp6_shop(vrf_id_t vrf_id)  {  	int sd; -	frr_elevate_privs(&bglobal.bfdd_privs) { +	frr_with_privs(&bglobal.bfdd_privs) {  		sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC, vrf_id, NULL);  	}  	if (sd == -1) @@ -1137,7 +1137,7 @@ int bp_udp6_mhop(vrf_id_t vrf_id)  {  	int sd; -	frr_elevate_privs(&bglobal.bfdd_privs) { +	frr_with_privs(&bglobal.bfdd_privs) {  		sd = vrf_socket(AF_INET6, SOCK_DGRAM, PF_UNSPEC, vrf_id, NULL);  	}  	if (sd == -1) @@ -1153,7 +1153,7 @@ int bp_echo_socket(vrf_id_t vrf_id)  {  	int s; -	frr_elevate_privs(&bglobal.bfdd_privs) { +	frr_with_privs(&bglobal.bfdd_privs) {  		s = vrf_socket(AF_INET, SOCK_DGRAM, 0, vrf_id, NULL);  	}  	if (s == -1) @@ -1169,7 +1169,7 @@ int bp_echov6_socket(vrf_id_t vrf_id)  {  	int s; -	frr_elevate_privs(&bglobal.bfdd_privs) { +	frr_with_privs(&bglobal.bfdd_privs) {  		s = vrf_socket(AF_INET6, SOCK_DGRAM, 0, vrf_id, NULL);  	}  	if (s == -1) diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c index 497fb0749e..76a65f7f04 100644 --- a/bgpd/bgp_advertise.c +++ b/bgpd/bgp_advertise.c @@ -194,6 +194,7 @@ void bgp_adj_in_set(struct bgp_node *rn, struct peer *peer, struct attr *attr,  	adj = XCALLOC(MTYPE_BGP_ADJ_IN, sizeof(struct bgp_adj_in));  	adj->peer = peer_lock(peer); /* adj_in peer reference */  	adj->attr = bgp_attr_intern(attr); +	adj->uptime = bgp_clock();  	adj->addpath_rx_id = addpath_id;  	BGP_ADJ_IN_ADD(rn, adj);  	bgp_lock_node(rn); diff --git a/bgpd/bgp_advertise.h b/bgpd/bgp_advertise.h index 1b55b6e64b..c983598756 100644 --- a/bgpd/bgp_advertise.h +++ b/bgpd/bgp_advertise.h @@ -101,6 +101,9 @@ struct bgp_adj_in {  	/* Received attribute.  */  	struct attr *attr; +	/* timestamp (monotime) */ +	time_t uptime; +  	/* Addpath identifier */  	uint32_t addpath_rx_id;  }; diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c new file mode 100644 index 0000000000..8fca202345 --- /dev/null +++ b/bgpd/bgp_bmp.c @@ -0,0 +1,2240 @@ +/* BMP support. + * Copyright (C) 2018 Yasuhiro Ohara + * Copyright (C) 2019 David Lamparter for NetDEF, Inc. + * + * 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 "log.h" +#include "stream.h" +#include "sockunion.h" +#include "command.h" +#include "prefix.h" +#include "thread.h" +#include "linklist.h" +#include "queue.h" +#include "pullwr.h" +#include "memory.h" +#include "network.h" +#include "filter.h" +#include "lib_errors.h" +#include "stream.h" +#include "libfrr.h" +#include "version.h" +#include "jhash.h" +#include "termtable.h" + +#include "bgpd/bgp_table.h" +#include "bgpd/bgpd.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_dump.h" +#include "bgpd/bgp_errors.h" +#include "bgpd/bgp_packet.h" +#include "bgpd/bgp_bmp.h" +#include "bgpd/bgp_fsm.h" +#include "bgpd/bgp_updgrp.h" +#include "bgpd/bgp_vty.h" + +static void bmp_close(struct bmp *bmp); +static struct bmp_bgp *bmp_bgp_find(struct bgp *bgp); +static void bmp_targets_put(struct bmp_targets *bt); +static struct bmp_bgp_peer *bmp_bgp_peer_find(uint64_t peerid); +static struct bmp_bgp_peer *bmp_bgp_peer_get(struct peer *peer); +static void bmp_active_disconnected(struct bmp_active *ba); +static void bmp_active_put(struct bmp_active *ba); + +DEFINE_MGROUP(BMP, "BMP (BGP Monitoring Protocol)") + +DEFINE_MTYPE_STATIC(BMP, BMP_CONN,	"BMP connection state") +DEFINE_MTYPE_STATIC(BMP, BMP_TARGETS,	"BMP targets") +DEFINE_MTYPE_STATIC(BMP, BMP_TARGETSNAME, "BMP targets name") +DEFINE_MTYPE_STATIC(BMP, BMP_LISTENER,	"BMP listener") +DEFINE_MTYPE_STATIC(BMP, BMP_ACTIVE,	"BMP active connection config") +DEFINE_MTYPE_STATIC(BMP, BMP_ACLNAME,	"BMP access-list name") +DEFINE_MTYPE_STATIC(BMP, BMP_QUEUE,	"BMP update queue item") +DEFINE_MTYPE_STATIC(BMP, BMP,		"BMP instance state") +DEFINE_MTYPE_STATIC(BMP, BMP_MIRRORQ,	"BMP route mirroring buffer") +DEFINE_MTYPE_STATIC(BMP, BMP_PEER,	"BMP per BGP peer data") +DEFINE_MTYPE_STATIC(BMP, BMP_OPEN,	"BMP stored BGP OPEN message") + +DEFINE_QOBJ_TYPE(bmp_targets) + +static int bmp_bgp_cmp(const struct bmp_bgp *a, const struct bmp_bgp *b) +{ +	if (a->bgp < b->bgp) +		return -1; +	if (a->bgp > b->bgp) +		return 1; +	return 0; +} + +static uint32_t bmp_bgp_hash(const struct bmp_bgp *e) +{ +	return jhash(&e->bgp, sizeof(e->bgp), 0x55aa5a5a); +} + +DECLARE_HASH(bmp_bgph, struct bmp_bgp, bbi, bmp_bgp_cmp, bmp_bgp_hash) + +struct bmp_bgph_head bmp_bgph; + +static int bmp_bgp_peer_cmp(const struct bmp_bgp_peer *a, +		const struct bmp_bgp_peer *b) +{ +	if (a->peerid < b->peerid) +		return -1; +	if (a->peerid > b->peerid) +		return 1; +	return 0; +} + +static uint32_t bmp_bgp_peer_hash(const struct bmp_bgp_peer *e) +{ +	return e->peerid; +} + +DECLARE_HASH(bmp_peerh, struct bmp_bgp_peer, bpi, +		bmp_bgp_peer_cmp, bmp_bgp_peer_hash) + +struct bmp_peerh_head bmp_peerh; + +DECLARE_LIST(bmp_mirrorq, struct bmp_mirrorq, bmi) + +/* listener management */ + +static int bmp_listener_cmp(const struct bmp_listener *a, +		const struct bmp_listener *b) +{ +	int c; + +	c = sockunion_cmp(&a->addr, &b->addr); +	if (c) +		return c; +	if (a->port < b->port) +		return -1; +	if (a->port > b->port) +		return 1; +	return 0; +} + +DECLARE_SORTLIST_UNIQ(bmp_listeners, struct bmp_listener, bli, bmp_listener_cmp) + +static int bmp_targets_cmp(const struct bmp_targets *a, +			   const struct bmp_targets *b) +{ +	return strcmp(a->name, b->name); +} + +DECLARE_SORTLIST_UNIQ(bmp_targets, struct bmp_targets, bti, bmp_targets_cmp) + +DECLARE_LIST(bmp_session, struct bmp, bsi) + +DECLARE_DLIST(bmp_qlist, struct bmp_queue_entry, bli) + +static int bmp_qhash_cmp(const struct bmp_queue_entry *a, +		const struct bmp_queue_entry *b) +{ +	int ret; +	ret = prefix_cmp(&a->p, &b->p); +	if (ret) +		return ret; +	ret = memcmp(&a->peerid, &b->peerid, +			offsetof(struct bmp_queue_entry, refcount) - +			offsetof(struct bmp_queue_entry, peerid)); +	return ret; +} + +static uint32_t bmp_qhash_hkey(const struct bmp_queue_entry *e) +{ +	uint32_t key; + +	key = prefix_hash_key((void *)&e->p); +	key = jhash(&e->peerid, +			offsetof(struct bmp_queue_entry, refcount) - +			offsetof(struct bmp_queue_entry, peerid), +			key); +	return key; +} + +DECLARE_HASH(bmp_qhash, struct bmp_queue_entry, bhi, +		bmp_qhash_cmp, bmp_qhash_hkey) + +static int bmp_active_cmp(const struct bmp_active *a, +		const struct bmp_active *b) +{ +	int c; + +	c = strcmp(a->hostname, b->hostname); +	if (c) +		return c; +	if (a->port < b->port) +		return -1; +	if (a->port > b->port) +		return 1; +	return 0; +} + +DECLARE_SORTLIST_UNIQ(bmp_actives, struct bmp_active, bai, bmp_active_cmp) + +static struct bmp *bmp_new(struct bmp_targets *bt, int bmp_sock) +{ +	struct bmp *new = XCALLOC(MTYPE_BMP_CONN, sizeof(struct bmp)); +	afi_t afi; +	safi_t safi; + +	monotime(&new->t_up); +	new->targets = bt; +	new->socket = bmp_sock; +	new->syncafi = AFI_MAX; + +	FOREACH_AFI_SAFI (afi, safi) { +		new->afistate[afi][safi] = bt->afimon[afi][safi] +			? BMP_AFI_NEEDSYNC : BMP_AFI_INACTIVE; +	} + +	bmp_session_add_tail(&bt->sessions, new); +	return new; +} + +static void bmp_free(struct bmp *bmp) +{ +	bmp_session_del(&bmp->targets->sessions, bmp); +	XFREE(MTYPE_BMP_CONN, bmp); +} + +static void bmp_common_hdr(struct stream *s, uint8_t ver, uint8_t type) +{ +	stream_putc(s, ver); +	stream_putl(s, 0); //dummy message length. will be set later. +	stream_putc(s, type); +} + +static void bmp_per_peer_hdr(struct stream *s, struct peer *peer, +		uint8_t flags, const struct timeval *tv) +{ +	char peer_distinguisher[8]; + +#define BMP_PEER_TYPE_GLOBAL_INSTANCE 0 +#define BMP_PEER_TYPE_RD_INSTANCE     1 +#define BMP_PEER_TYPE_LOCAL_INSTANCE  2 + +#define BMP_PEER_FLAG_V (1 << 7) +#define BMP_PEER_FLAG_L (1 << 6) +#define BMP_PEER_FLAG_A (1 << 5) + +	/* Peer Type */ +	stream_putc(s, BMP_PEER_TYPE_GLOBAL_INSTANCE); + +	/* Peer Flags */ +	if (peer->su.sa.sa_family == AF_INET6) +		SET_FLAG(flags, BMP_PEER_FLAG_V); +	else +		UNSET_FLAG(flags, BMP_PEER_FLAG_V); +	stream_putc(s, flags); + +	/* Peer Distinguisher */ +	memset (&peer_distinguisher[0], 0, 8); +	stream_put(s, &peer_distinguisher[0], 8); + +	/* Peer Address */ +	if (peer->su.sa.sa_family == AF_INET6) +		stream_put(s, &peer->su.sin6.sin6_addr, 16); +	else if (peer->su.sa.sa_family == AF_INET) { +		stream_putl(s, 0); +		stream_putl(s, 0); +		stream_putl(s, 0); +		stream_put_in_addr(s, &peer->su.sin.sin_addr); +	} else { +		stream_putl(s, 0); +		stream_putl(s, 0); +		stream_putl(s, 0); +		stream_putl(s, 0); +	} + +	/* Peer AS */ +	stream_putl(s, peer->as); + +	/* Peer BGP ID */ +	stream_put_in_addr(s, &peer->remote_id); + +	/* Timestamp */ +	if (tv) { +		stream_putl(s, tv->tv_sec); +		stream_putl(s, tv->tv_usec); +	} else { +		stream_putl(s, 0); +		stream_putl(s, 0); +	} +} + +static void bmp_put_info_tlv(struct stream *s, uint16_t type, +		const char *string) +{ +	int len = strlen (string); +	stream_putw(s, type); +	stream_putw(s, len); +	stream_put(s, string, len); +} + +static int bmp_send_initiation(struct bmp *bmp) +{ +	int len; +	struct stream *s; +	s = stream_new(BGP_MAX_PACKET_SIZE); +	bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_INITIATION); + +#define BMP_INFO_TYPE_SYSDESCR	1 +#define BMP_INFO_TYPE_SYSNAME	2 +	bmp_put_info_tlv(s, BMP_INFO_TYPE_SYSDESCR, +			FRR_FULL_NAME " " FRR_VER_SHORT); +	bmp_put_info_tlv(s, BMP_INFO_TYPE_SYSNAME, cmd_hostname_get()); + +	len = stream_get_endp(s); +	stream_putl_at(s, BMP_LENGTH_POS, len); //message length is set. + +	pullwr_write_stream(bmp->pullwr, s); +	stream_free(s); +	return 0; +} + +static void bmp_notify_put(struct stream *s, struct bgp_notify *nfy) +{ +	size_t len_pos; +	uint8_t marker[16] = { +		0xff, 0xff, 0xff, 0xff, +		0xff, 0xff, 0xff, 0xff, +		0xff, 0xff, 0xff, 0xff, +		0xff, 0xff, 0xff, 0xff, +	}; + +	stream_put(s, marker, sizeof(marker)); +	len_pos = stream_get_endp(s); +	stream_putw(s, 0); +	stream_putc(s, BGP_MSG_NOTIFY); +	stream_putc(s, nfy->code); +	stream_putc(s, nfy->subcode); +	stream_put(s, nfy->data, nfy->length); + +	stream_putw_at(s, len_pos, stream_get_endp(s) - len_pos +			+ sizeof(marker)); +} + +static struct stream *bmp_peerstate(struct peer *peer, bool down) +{ +	struct stream *s; +	size_t len; +	struct timeval uptime, uptime_real; + +	uptime.tv_sec = peer->uptime; +	uptime.tv_usec = 0; +	monotime_to_realtime(&uptime, &uptime_real); + +#define BGP_BMP_MAX_PACKET_SIZE	1024 +	s = stream_new(BGP_MAX_PACKET_SIZE); + +	if (peer->status == Established && !down) { +		struct bmp_bgp_peer *bbpeer; + +		bmp_common_hdr(s, BMP_VERSION_3, +				BMP_TYPE_PEER_UP_NOTIFICATION); +		bmp_per_peer_hdr(s, peer, 0, &uptime_real); + +		/* Local Address (16 bytes) */ +		if (peer->su_local->sa.sa_family == AF_INET6) +			stream_put(s, &peer->su_local->sin6.sin6_addr, 16); +		else if (peer->su_local->sa.sa_family == AF_INET) { +			stream_putl(s, 0); +			stream_putl(s, 0); +			stream_putl(s, 0); +			stream_put_in_addr(s, &peer->su_local->sin.sin_addr); +		} + +		/* Local Port, Remote Port */ +		if (peer->su_local->sa.sa_family == AF_INET6) +			stream_putw(s, peer->su_local->sin6.sin6_port); +		else if (peer->su_local->sa.sa_family == AF_INET) +			stream_putw(s, peer->su_local->sin.sin_port); +		if (peer->su_remote->sa.sa_family == AF_INET6) +			stream_putw(s, peer->su_remote->sin6.sin6_port); +		else if (peer->su_remote->sa.sa_family == AF_INET) +			stream_putw(s, peer->su_remote->sin.sin_port); + +		static const uint8_t dummy_open[] = { +			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +			0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +			0x00, 0x13, 0x01, +		}; + +		bbpeer = bmp_bgp_peer_find(peer->qobj_node.nid); + +		if (bbpeer && bbpeer->open_tx) +			stream_put(s, bbpeer->open_tx, bbpeer->open_tx_len); +		else { +			stream_put(s, dummy_open, sizeof(dummy_open)); +			zlog_warn("bmp: missing TX OPEN message for peer %s\n", +					peer->host); +		} +		if (bbpeer && bbpeer->open_rx) +			stream_put(s, bbpeer->open_rx, bbpeer->open_rx_len); +		else { +			stream_put(s, dummy_open, sizeof(dummy_open)); +			zlog_warn("bmp: missing RX OPEN message for peer %s\n", +					peer->host); +		} + +		if (peer->desc) +			bmp_put_info_tlv(s, 0, peer->desc); +	} else { +		uint8_t type; +		size_t type_pos; + +		bmp_common_hdr(s, BMP_VERSION_3, +				BMP_TYPE_PEER_DOWN_NOTIFICATION); +		bmp_per_peer_hdr(s, peer, 0, &uptime_real); + +		type_pos = stream_get_endp(s); +		stream_putc(s, 0);	/* placeholder for down reason */ + +		switch (peer->last_reset) { +		case PEER_DOWN_NOTIFY_RECEIVED: +			type = BMP_PEERDOWN_REMOTE_NOTIFY; +			bmp_notify_put(s, &peer->notify); +			break; +		case PEER_DOWN_CLOSE_SESSION: +			type = BMP_PEERDOWN_REMOTE_CLOSE; +			break; +		default: +			type = BMP_PEERDOWN_LOCAL_NOTIFY; +			stream_put(s, peer->last_reset_cause, +					peer->last_reset_cause_size); +			break; +		} +		stream_putc_at(s, type_pos, type); +	} + +	len = stream_get_endp(s); +	stream_putl_at(s, BMP_LENGTH_POS, len); //message length is set. +	return s; +} + + +static int bmp_send_peerup(struct bmp *bmp) +{ +	struct peer *peer; +	struct listnode *node; +	struct stream *s; + +	/* Walk down all peers */ +	for (ALL_LIST_ELEMENTS_RO(bmp->targets->bgp->peer, node, peer)) { +		s = bmp_peerstate(peer, false); +		pullwr_write_stream(bmp->pullwr, s); +		stream_free(s); +	} + +	return 0; +} + +/* XXX: kludge - filling the pullwr's buffer */ +static void bmp_send_all(struct bmp_bgp *bmpbgp, struct stream *s) +{ +	struct bmp_targets *bt; +	struct bmp *bmp; + +	frr_each(bmp_targets, &bmpbgp->targets, bt) +		frr_each(bmp_session, &bt->sessions, bmp) +			pullwr_write_stream(bmp->pullwr, s); +	stream_free(s); +} + +/* + * Route Mirroring + */ + +#define BMP_MIRROR_TLV_TYPE_BGP_MESSAGE 0 +#define BMP_MIRROR_TLV_TYPE_INFO        1 + +#define BMP_MIRROR_INFO_CODE_ERRORPDU   0 +#define BMP_MIRROR_INFO_CODE_LOSTMSGS   1 + +static struct bmp_mirrorq *bmp_pull_mirror(struct bmp *bmp) +{ +	struct bmp_mirrorq *bmq; + +	bmq = bmp->mirrorpos; +	if (!bmq) +		return NULL; + +	bmp->mirrorpos = bmp_mirrorq_next(&bmp->targets->bmpbgp->mirrorq, bmq); + +	bmq->refcount--; +	if (!bmq->refcount) { +		bmp->targets->bmpbgp->mirror_qsize -= sizeof(*bmq) + bmq->len; +		bmp_mirrorq_del(&bmp->targets->bmpbgp->mirrorq, bmq); +	} +	return bmq; +} + +static void bmp_mirror_cull(struct bmp_bgp *bmpbgp) +{ +	while (bmpbgp->mirror_qsize > bmpbgp->mirror_qsizelimit) { +		struct bmp_mirrorq *bmq, *inner; +		struct bmp_targets *bt; +		struct bmp *bmp; + +		bmq = bmp_mirrorq_first(&bmpbgp->mirrorq); + +		frr_each(bmp_targets, &bmpbgp->targets, bt) { +			if (!bt->mirror) +				continue; +			frr_each(bmp_session, &bt->sessions, bmp) { +				if (bmp->mirrorpos != bmq) +					continue; + +				while ((inner = bmp_pull_mirror(bmp))) { +					if (!inner->refcount) +						XFREE(MTYPE_BMP_MIRRORQ, +								inner); +				} + +				zlog_warn("bmp[%s] lost mirror messages due to buffer size limit", +						bmp->remote); +				bmp->mirror_lost = true; +				pullwr_bump(bmp->pullwr); +			} +		} +	} +} + +static int bmp_mirror_packet(struct peer *peer, uint8_t type, bgp_size_t size, +		struct stream *packet) +{ +	struct bmp_bgp *bmpbgp = bmp_bgp_find(peer->bgp); +	struct timeval tv; +	struct bmp_mirrorq *qitem; +	struct bmp_targets *bt; +	struct bmp *bmp; + +	gettimeofday(&tv, NULL); + +	if (type == BGP_MSG_OPEN) { +		struct bmp_bgp_peer *bbpeer = bmp_bgp_peer_get(peer); + +		XFREE(MTYPE_BMP_OPEN, bbpeer->open_rx); + +		bbpeer->open_rx_len = size; +		bbpeer->open_rx = XMALLOC(MTYPE_BMP_OPEN, size); +		memcpy(bbpeer->open_rx, packet->data, size); +	} + +	if (!bmpbgp) +		return 0; + +	qitem = XCALLOC(MTYPE_BMP_MIRRORQ, sizeof(*qitem) + size); +	qitem->peerid = peer->qobj_node.nid; +	qitem->tv = tv; +	qitem->len = size; +	memcpy(qitem->data, packet->data, size); + +	frr_each(bmp_targets, &bmpbgp->targets, bt) { +		if (!bt->mirror) +			continue; +		frr_each(bmp_session, &bt->sessions, bmp) { +			qitem->refcount++; +			if (!bmp->mirrorpos) +				bmp->mirrorpos = qitem; +			pullwr_bump(bmp->pullwr); +		} +	} +	if (qitem->refcount == 0) +		XFREE(MTYPE_BMP_MIRRORQ, qitem); +	else { +		bmpbgp->mirror_qsize += sizeof(*qitem) + size; +		bmp_mirrorq_add_tail(&bmpbgp->mirrorq, qitem); + +		bmp_mirror_cull(bmpbgp); + +		bmpbgp->mirror_qsizemax = MAX(bmpbgp->mirror_qsizemax, +				bmpbgp->mirror_qsize); +	} +	return 0; +} + +static void bmp_wrmirror_lost(struct bmp *bmp, struct pullwr *pullwr) +{ +	struct stream *s; +	struct timeval tv; + +	gettimeofday(&tv, NULL); + +	s = stream_new(BGP_MAX_PACKET_SIZE); + +	bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_ROUTE_MIRRORING); +	bmp_per_peer_hdr(s, bmp->targets->bgp->peer_self, 0, &tv); + +	stream_putw(s, BMP_MIRROR_TLV_TYPE_INFO); +	stream_putw(s, 2); +	stream_putw(s, BMP_MIRROR_INFO_CODE_LOSTMSGS); +	stream_putl_at(s, BMP_LENGTH_POS, stream_get_endp(s)); + +	bmp->cnt_mirror_overruns++; +	pullwr_write_stream(bmp->pullwr, s); +	stream_free(s); +} + +static bool bmp_wrmirror(struct bmp *bmp, struct pullwr *pullwr) +{ +	struct bmp_mirrorq *bmq; +	struct peer *peer; +	bool written = false; + +	if (bmp->mirror_lost) { +		bmp_wrmirror_lost(bmp, pullwr); +		bmp->mirror_lost = false; +		return true; +	} + +	bmq = bmp_pull_mirror(bmp); +	if (!bmq) +		return false; + +	peer = QOBJ_GET_TYPESAFE(bmq->peerid, peer); +	if (!peer) { +		zlog_info("bmp: skipping mirror message for deleted peer"); +		goto out; +	} + +	struct stream *s; +	s = stream_new(BGP_MAX_PACKET_SIZE); + +	bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_ROUTE_MIRRORING); +	bmp_per_peer_hdr(s, peer, 0, &bmq->tv); + +	/* BMP Mirror TLV. */ +	stream_putw(s, BMP_MIRROR_TLV_TYPE_BGP_MESSAGE); +	stream_putw(s, bmq->len); +	stream_putl_at(s, BMP_LENGTH_POS, stream_get_endp(s) + bmq->len); + +	bmp->cnt_mirror++; +	pullwr_write_stream(bmp->pullwr, s); +	pullwr_write(bmp->pullwr, bmq->data, bmq->len); + +	stream_free(s); +	written = true; + +out: +	if (!bmq->refcount) +		XFREE(MTYPE_BMP_MIRRORQ, bmq); +	return written; +} + +static int bmp_outgoing_packet(struct peer *peer, uint8_t type, bgp_size_t size, +		struct stream *packet) +{ +	if (type == BGP_MSG_OPEN) { +		struct bmp_bgp_peer *bbpeer = bmp_bgp_peer_get(peer); + +		XFREE(MTYPE_BMP_OPEN, bbpeer->open_tx); + +		bbpeer->open_tx_len = size; +		bbpeer->open_tx = XMALLOC(MTYPE_BMP_OPEN, size); +		memcpy(bbpeer->open_tx, packet->data, size); +	} +	return 0; +} + +static int bmp_peer_established(struct peer *peer) +{ +	struct bmp_bgp *bmpbgp = bmp_bgp_find(peer->bgp); + +	if (!bmpbgp) +		return 0; + +	if (peer->doppelganger && (peer->doppelganger->status != Deleted)) { +		struct bmp_bgp_peer *bbpeer, *bbdopp; + +		bbpeer = bmp_bgp_peer_get(peer); +		bbdopp = bmp_bgp_peer_find(peer->doppelganger->qobj_node.nid); +		if (bbdopp) { +			XFREE(MTYPE_BMP_OPEN, bbpeer->open_tx); +			XFREE(MTYPE_BMP_OPEN, bbpeer->open_rx); + +			bbpeer->open_tx = bbdopp->open_tx; +			bbpeer->open_tx_len = bbdopp->open_tx_len; +			bbpeer->open_rx = bbdopp->open_rx; +			bbpeer->open_rx_len = bbdopp->open_rx_len; + +			bmp_peerh_del(&bmp_peerh, bbdopp); +			XFREE(MTYPE_BMP_PEER, bbdopp); +		} +	} + +	bmp_send_all(bmpbgp, bmp_peerstate(peer, false)); +	return 0; +} + +static int bmp_peer_backward(struct peer *peer) +{ +	struct bmp_bgp *bmpbgp = bmp_bgp_find(peer->bgp); +	struct bmp_bgp_peer *bbpeer; + +	if (!bmpbgp) +		return 0; + +	bbpeer = bmp_bgp_peer_find(peer->qobj_node.nid); +	if (bbpeer) { +		XFREE(MTYPE_BMP_OPEN, bbpeer->open_tx); +		bbpeer->open_tx_len = 0; +		XFREE(MTYPE_BMP_OPEN, bbpeer->open_rx); +		bbpeer->open_rx_len = 0; +	} + +	bmp_send_all(bmpbgp, bmp_peerstate(peer, true)); +	return 0; +} + +static void bmp_eor(struct bmp *bmp, afi_t afi, safi_t safi, uint8_t flags) +{ +	struct peer *peer; +	struct listnode *node; +	struct stream *s, *s2; +	iana_afi_t pkt_afi; +	iana_safi_t pkt_safi; + +	s = stream_new(BGP_MAX_PACKET_SIZE); + +	/* Make BGP update packet. */ +	bgp_packet_set_marker(s, BGP_MSG_UPDATE); + +	/* Unfeasible Routes Length */ +	stream_putw(s, 0); + +	if (afi == AFI_IP && safi == SAFI_UNICAST) { +		/* Total Path Attribute Length */ +		stream_putw(s, 0); +	} else { +		/* Convert AFI, SAFI to values for packet. */ +		bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi); + +		/* Total Path Attribute Length */ +		stream_putw(s, 6); +		stream_putc(s, BGP_ATTR_FLAG_OPTIONAL); +		stream_putc(s, BGP_ATTR_MP_UNREACH_NLRI); +		stream_putc(s, 3); +		stream_putw(s, pkt_afi); +		stream_putc(s, pkt_safi); +	} + +	bgp_packet_set_size(s); + +	for (ALL_LIST_ELEMENTS_RO(bmp->targets->bgp->peer, node, peer)) { +		if (!peer->afc_nego[afi][safi]) +			continue; + +		s2 = stream_new(BGP_MAX_PACKET_SIZE); + +		bmp_common_hdr(s2, BMP_VERSION_3, +				BMP_TYPE_ROUTE_MONITORING); +		bmp_per_peer_hdr(s2, peer, flags, NULL); + +		stream_putl_at(s2, BMP_LENGTH_POS, +				stream_get_endp(s) + stream_get_endp(s2)); + +		bmp->cnt_update++; +		pullwr_write_stream(bmp->pullwr, s2); +		pullwr_write_stream(bmp->pullwr, s); +		stream_free(s2); +	} +	stream_free(s); +} + +static struct stream *bmp_update(struct prefix *p, struct peer *peer, +		struct attr *attr, afi_t afi, safi_t safi) +{ +	struct bpacket_attr_vec_arr vecarr; +	struct stream *s; +	size_t attrlen_pos = 0, mpattrlen_pos = 0; +	bgp_size_t total_attr_len = 0; + +	bpacket_attr_vec_arr_reset(&vecarr); + +	s = stream_new(BGP_MAX_PACKET_SIZE); +	bgp_packet_set_marker(s, BGP_MSG_UPDATE); + +	/* 2: withdrawn routes length */ +	stream_putw(s, 0); + +	/* 3: total attributes length - attrlen_pos stores the position */ +	attrlen_pos = stream_get_endp(s); +	stream_putw(s, 0); + +	/* 5: Encode all the attributes, except MP_REACH_NLRI attr. */ +	total_attr_len = bgp_packet_attribute(NULL, peer, s, attr, +			&vecarr, NULL, afi, safi, peer, NULL, NULL, 0, 0, 0); + +	/* space check? */ + +	/* peer_cap_enhe & add-path removed */ +	if (afi == AFI_IP && safi == SAFI_UNICAST) +		stream_put_prefix(s, p); +	else { +		size_t p1 = stream_get_endp(s); + +		/* MPLS removed for now */ + +		mpattrlen_pos = bgp_packet_mpattr_start(s, peer, afi, safi, +				&vecarr, attr); +		bgp_packet_mpattr_prefix(s, afi, safi, p, NULL, NULL, 0, +				0, 0, attr); +		bgp_packet_mpattr_end(s, mpattrlen_pos); +		total_attr_len += stream_get_endp(s) - p1; +	} + +	/* set the total attribute length correctly */ +	stream_putw_at(s, attrlen_pos, total_attr_len); +	bgp_packet_set_size(s); +	return s; +} + +static struct stream *bmp_withdraw(struct prefix *p, afi_t afi, safi_t safi) +{ +	struct stream *s; +	size_t attrlen_pos = 0, mp_start, mplen_pos; +	bgp_size_t total_attr_len = 0; +	bgp_size_t unfeasible_len; + +	s = stream_new(BGP_MAX_PACKET_SIZE); + +	bgp_packet_set_marker(s, BGP_MSG_UPDATE); +	stream_putw(s, 0); + +	if (afi == AFI_IP && safi == SAFI_UNICAST) { +		stream_put_prefix(s, p); +		unfeasible_len = stream_get_endp(s) - BGP_HEADER_SIZE +				 - BGP_UNFEASIBLE_LEN; +		stream_putw_at(s, BGP_HEADER_SIZE, unfeasible_len); +		stream_putw(s, 0); +	} else { +		attrlen_pos = stream_get_endp(s); +		/* total attr length = 0 for now. reevaluate later */ +		stream_putw(s, 0); +		mp_start = stream_get_endp(s); +		mplen_pos = bgp_packet_mpunreach_start(s, afi, safi); + +		bgp_packet_mpunreach_prefix(s, p, afi, safi, NULL, NULL, 0, +				0, 0, NULL); +		/* Set the mp_unreach attr's length */ +		bgp_packet_mpunreach_end(s, mplen_pos); + +		/* Set total path attribute length. */ +		total_attr_len = stream_get_endp(s) - mp_start; +		stream_putw_at(s, attrlen_pos, total_attr_len); +	} + +	bgp_packet_set_size(s); +	return s; +} + +static void bmp_monitor(struct bmp *bmp, struct peer *peer, uint8_t flags, +			struct prefix *p, struct attr *attr, afi_t afi, +			safi_t safi, time_t uptime) +{ +	struct stream *hdr, *msg; +	struct timeval tv = { .tv_sec = uptime, .tv_usec = 0 }; + +	if (attr) +		msg = bmp_update(p, peer, attr, afi, safi); +	else +		msg = bmp_withdraw(p, afi, safi); + +	hdr = stream_new(BGP_MAX_PACKET_SIZE); +	bmp_common_hdr(hdr, BMP_VERSION_3, BMP_TYPE_ROUTE_MONITORING); +	bmp_per_peer_hdr(hdr, peer, flags, &tv); + +	stream_putl_at(hdr, BMP_LENGTH_POS, +			stream_get_endp(hdr) + stream_get_endp(msg)); + +	bmp->cnt_update++; +	pullwr_write_stream(bmp->pullwr, hdr); +	pullwr_write_stream(bmp->pullwr, msg); +	stream_free(hdr); +	stream_free(msg); +} + +static bool bmp_wrsync(struct bmp *bmp, struct pullwr *pullwr) +{ +	afi_t afi; +	safi_t safi; + +	if (bmp->syncafi == AFI_MAX) { +		FOREACH_AFI_SAFI (afi, safi) { +			if (bmp->afistate[afi][safi] != BMP_AFI_NEEDSYNC) +				continue; + +			bmp->afistate[afi][safi] = BMP_AFI_SYNC; + +			bmp->syncafi = afi; +			bmp->syncsafi = safi; +			bmp->syncpeerid = 0; +			memset(&bmp->syncpos, 0, sizeof(bmp->syncpos)); +			bmp->syncpos.family = afi2family(afi); +			zlog_info("bmp[%s] %s %s sending table", +					bmp->remote, +					afi2str(bmp->syncafi), +					safi2str(bmp->syncsafi)); +			/* break does not work here, 2 loops... */ +			goto afibreak; +		} +		if (bmp->syncafi == AFI_MAX) +			return false; +	} + +afibreak: +	afi = bmp->syncafi; +	safi = bmp->syncsafi; + +	if (!bmp->targets->afimon[afi][safi]) { +		/* shouldn't happen */ +		bmp->afistate[afi][safi] = BMP_AFI_INACTIVE; +		bmp->syncafi = AFI_MAX; +		bmp->syncsafi = SAFI_MAX; +		return true; +	} + +	struct bgp_table *table = bmp->targets->bgp->rib[afi][safi]; +	struct bgp_node *bn; +	struct bgp_path_info *bpi = NULL, *bpiter; +	struct bgp_adj_in *adjin = NULL, *adjiter; + +	bn = bgp_node_lookup(table, &bmp->syncpos); +	do { +		if (!bn) { +			bn = bgp_table_get_next(table, &bmp->syncpos); +			if (!bn) { +				zlog_info("bmp[%s] %s %s table completed (EoR)", +						bmp->remote, afi2str(afi), +						safi2str(safi)); +				bmp_eor(bmp, afi, safi, BMP_PEER_FLAG_L); +				bmp_eor(bmp, afi, safi, 0); + +				bmp->afistate[afi][safi] = BMP_AFI_LIVE; +				bmp->syncafi = AFI_MAX; +				bmp->syncsafi = SAFI_MAX; +				return true; +			} +			bmp->syncpeerid = 0; +			prefix_copy(&bmp->syncpos, &bn->p); +		} + +		if (bmp->targets->afimon[afi][safi] & BMP_MON_POSTPOLICY) { +			for (bpiter = bn->info; bpiter; bpiter = bpiter->next) { +				if (!CHECK_FLAG(bpiter->flags, BGP_PATH_VALID)) +					continue; +				if (bpiter->peer->qobj_node.nid +				    <= bmp->syncpeerid) +					continue; +				if (bpi && bpiter->peer->qobj_node.nid +						> bpi->peer->qobj_node.nid) +					continue; +				bpi = bpiter; +			} +		} +		if (bmp->targets->afimon[afi][safi] & BMP_MON_PREPOLICY) { +			for (adjiter = bn->adj_in; adjiter; +			     adjiter = adjiter->next) { +				if (adjiter->peer->qobj_node.nid +				    <= bmp->syncpeerid) +					continue; +				if (adjin && adjiter->peer->qobj_node.nid +						> adjin->peer->qobj_node.nid) +					continue; +				adjin = adjiter; +			} +		} +		if (bpi || adjin) +			break; + +		bn = NULL; +	} while (1); + +	if (adjin && bpi +	    && adjin->peer->qobj_node.nid < bpi->peer->qobj_node.nid) { +		bpi = NULL; +		bmp->syncpeerid = adjin->peer->qobj_node.nid; +	} else if (adjin && bpi +		   && adjin->peer->qobj_node.nid > bpi->peer->qobj_node.nid) { +		adjin = NULL; +		bmp->syncpeerid = bpi->peer->qobj_node.nid; +	} else if (bpi) { +		bmp->syncpeerid = bpi->peer->qobj_node.nid; +	} else if (adjin) { +		bmp->syncpeerid = adjin->peer->qobj_node.nid; +	} + +	if (bpi) +		bmp_monitor(bmp, bpi->peer, BMP_PEER_FLAG_L, &bn->p, bpi->attr, +			    afi, safi, bpi->uptime); +	if (adjin) +		bmp_monitor(bmp, adjin->peer, 0, &bn->p, adjin->attr, +			    afi, safi, adjin->uptime); + +	return true; +} + +static struct bmp_queue_entry *bmp_pull(struct bmp *bmp) +{ +	struct bmp_queue_entry *bqe; + +	bqe = bmp->queuepos; +	if (!bqe) +		return NULL; + +	bmp->queuepos = bmp_qlist_next(&bmp->targets->updlist, bqe); + +	bqe->refcount--; +	if (!bqe->refcount) { +		bmp_qhash_del(&bmp->targets->updhash, bqe); +		bmp_qlist_del(&bmp->targets->updlist, bqe); +	} +	return bqe; +} + +static bool bmp_wrqueue(struct bmp *bmp, struct pullwr *pullwr) +{ +	struct bmp_queue_entry *bqe; +	struct peer *peer; +	struct bgp_node *bn; +	bool written = false; + +	bqe = bmp_pull(bmp); +	if (!bqe) +		return false; + +	afi_t afi = bqe->afi; +	safi_t safi = bqe->safi; + +	switch (bmp->afistate[afi][safi]) { +	case BMP_AFI_INACTIVE: +	case BMP_AFI_NEEDSYNC: +		goto out; +	case BMP_AFI_SYNC: +		if (prefix_cmp(&bqe->p, &bmp->syncpos) <= 0) +			/* currently syncing but have already passed this +			 * prefix => send it. */ +			break; + +		/* currently syncing & haven't reached this prefix yet +		 * => it'll be sent as part of the table sync, no need here */ +		goto out; +	case BMP_AFI_LIVE: +		break; +	} + +	peer = QOBJ_GET_TYPESAFE(bqe->peerid, peer); +	if (!peer) { +		zlog_info("bmp: skipping queued item for deleted peer"); +		goto out; +	} +	if (peer->status != Established) +		goto out; + +	bn = bgp_node_lookup(bmp->targets->bgp->rib[afi][safi], &bqe->p); + +	if (bmp->targets->afimon[afi][safi] & BMP_MON_POSTPOLICY) { +		struct bgp_path_info *bpi; + +		for (bpi = bn ? bn->info : NULL; bpi; bpi = bpi->next) { +			if (!CHECK_FLAG(bpi->flags, BGP_PATH_VALID)) +				continue; +			if (bpi->peer == peer) +				break; +		} + +		bmp_monitor(bmp, peer, BMP_PEER_FLAG_L, &bqe->p, +			    bpi ? bpi->attr : NULL, afi, safi, +			    bpi ? bpi->uptime : monotime(NULL)); +		written = true; +	} + +	if (bmp->targets->afimon[afi][safi] & BMP_MON_PREPOLICY) { +		struct bgp_adj_in *adjin; + +		for (adjin = bn ? bn->adj_in : NULL; adjin; +		     adjin = adjin->next) { +			if (adjin->peer == peer) +				break; +		} +		bmp_monitor(bmp, peer, BMP_PEER_FLAG_L, &bqe->p, +			    adjin ? adjin->attr : NULL, afi, safi, +			    adjin ? adjin->uptime : monotime(NULL)); +		written = true; +	} + +out: +	if (!bqe->refcount) +		XFREE(MTYPE_BMP_QUEUE, bqe); +	return written; +} + +static void bmp_wrfill(struct bmp *bmp, struct pullwr *pullwr) +{ +	switch(bmp->state) { +	case BMP_PeerUp: +		bmp_send_peerup(bmp); +		bmp->state = BMP_Run; +		break; + +	case BMP_Run: +		if (bmp_wrmirror(bmp, pullwr)) +			break; +		if (bmp_wrqueue(bmp, pullwr)) +			break; +		if (bmp_wrsync(bmp, pullwr)) +			break; +		break; +	} +} + +static void bmp_wrerr(struct bmp *bmp, struct pullwr *pullwr, bool eof) +{ +	if (eof) +		zlog_info("bmp[%s] disconnected", bmp->remote); +	else +		flog_warn(EC_LIB_SYSTEM_CALL, "bmp[%s] connection error: %s", +				bmp->remote, strerror(errno)); + +	bmp_close(bmp); +	bmp_free(bmp); +} + +static void bmp_process_one(struct bmp_targets *bt, struct bgp *bgp, +		afi_t afi, safi_t safi, struct bgp_node *bn, struct peer *peer) +{ +	struct bmp *bmp; +	struct bmp_queue_entry *bqe, bqeref; +	size_t refcount; +	char buf[256]; + +	prefix2str(&bn->p, buf, sizeof(buf)); + +	refcount = bmp_session_count(&bt->sessions); +	if (refcount == 0) +		return; + +	memset(&bqeref, 0, sizeof(bqeref)); +	prefix_copy(&bqeref.p, &bn->p); +	bqeref.peerid = peer->qobj_node.nid; +	bqeref.afi = afi; +	bqeref.safi = safi; + +	bqe = bmp_qhash_find(&bt->updhash, &bqeref); +	if (bqe) { +		if (bqe->refcount >= refcount) +			/* nothing to do here */ +			return; + +		bmp_qlist_del(&bt->updlist, bqe); +	} else { +		bqe = XMALLOC(MTYPE_BMP_QUEUE, sizeof(*bqe)); +		memcpy(bqe, &bqeref, sizeof(*bqe)); + +		bmp_qhash_add(&bt->updhash, bqe); +	} + +	bqe->refcount = refcount; +	bmp_qlist_add_tail(&bt->updlist, bqe); + +	frr_each (bmp_session, &bt->sessions, bmp) +		if (!bmp->queuepos) +			bmp->queuepos = bqe; +} + +static int bmp_process(struct bgp *bgp, afi_t afi, safi_t safi, +			struct bgp_node *bn, struct peer *peer, bool withdraw) +{ +	struct bmp_bgp *bmpbgp = bmp_bgp_find(peer->bgp); +	struct bmp_targets *bt; +	struct bmp *bmp; + +	if (!bmpbgp) +		return 0; + +	frr_each(bmp_targets, &bmpbgp->targets, bt) { +		if (!bt->afimon[afi][safi]) +			continue; + +		bmp_process_one(bt, bgp, afi, safi, bn, peer); + +		frr_each(bmp_session, &bt->sessions, bmp) { +			pullwr_bump(bmp->pullwr); +		} +	} +	return 0; +} + +static void bmp_stat_put_u32(struct stream *s, size_t *cnt, uint16_t type, +		uint32_t value) +{ +	stream_putw(s, type); +	stream_putw(s, 4); +	stream_putl(s, value); +	(*cnt)++; +} + +static int bmp_stats(struct thread *thread) +{ +	struct bmp_targets *bt = THREAD_ARG(thread); +	struct stream *s; +	struct peer *peer; +	struct listnode *node; +	struct timeval tv; + +	if (bt->stat_msec) +		thread_add_timer_msec(bm->master, bmp_stats, bt, bt->stat_msec, +				&bt->t_stats); + +	gettimeofday(&tv, NULL); + +	/* Walk down all peers */ +	for (ALL_LIST_ELEMENTS_RO(bt->bgp->peer, node, peer)) { +		size_t count = 0, count_pos, len; + +		if (peer->status != Established) +			continue; + +		s = stream_new(BGP_MAX_PACKET_SIZE); +		bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_STATISTICS_REPORT); +		bmp_per_peer_hdr(s, peer, 0, &tv); + +		count_pos = stream_get_endp(s); +		stream_putl(s, 0); + +		bmp_stat_put_u32(s, &count, BMP_STATS_PFX_REJECTED, +				peer->stat_pfx_filter); +		bmp_stat_put_u32(s, &count, BMP_STATS_UPD_LOOP_ASPATH, +				peer->stat_pfx_aspath_loop); +		bmp_stat_put_u32(s, &count, BMP_STATS_UPD_LOOP_ORIGINATOR, +				peer->stat_pfx_originator_loop); +		bmp_stat_put_u32(s, &count, BMP_STATS_UPD_LOOP_CLUSTER, +				peer->stat_pfx_cluster_loop); +		bmp_stat_put_u32(s, &count, BMP_STATS_PFX_DUP_WITHDRAW, +				peer->stat_pfx_dup_withdraw); +		bmp_stat_put_u32(s, &count, BMP_STATS_UPD_7606_WITHDRAW, +				peer->stat_upd_7606); +		bmp_stat_put_u32(s, &count, BMP_STATS_FRR_NH_INVALID, +				peer->stat_pfx_nh_invalid); + +		stream_putl_at(s, count_pos, count); + +		len = stream_get_endp(s); +		stream_putl_at(s, BMP_LENGTH_POS, len); + +		bmp_send_all(bt->bmpbgp, s); +	} +	return 0; +} + +static struct bmp *bmp_open(struct bmp_targets *bt, int bmp_sock) +{ +	union sockunion su, *sumem; +	struct prefix p; +	int on = 1; +	struct access_list *acl = NULL; +	enum filter_type ret; +	char buf[SU_ADDRSTRLEN]; +	struct bmp *bmp; + +	sumem = sockunion_getpeername(bmp_sock); +	if (!sumem) { +		close(bmp_sock); +		return NULL; +	} +	memcpy(&su, sumem, sizeof(su)); +	sockunion_free(sumem); + +	set_nonblocking(bmp_sock); +	set_cloexec(bmp_sock); +	shutdown(bmp_sock, SHUT_RD); + +	sockunion2hostprefix(&su, &p); + +	acl = NULL; +	switch (p.family) { +	case AF_INET: +		acl = access_list_lookup(AFI_IP, bt->acl_name); +		break; +	case AF_INET6: +		acl = access_list_lookup(AFI_IP6, bt->acl6_name); +		break; +	default: +		break; +	} + +	ret = FILTER_PERMIT; +	if (acl) { +		ret = access_list_apply(acl, &p); +	} + +	sockunion2str(&su, buf, SU_ADDRSTRLEN); +	snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ":%u", +			su.sa.sa_family == AF_INET +				? ntohs(su.sin.sin_port) +				: ntohs(su.sin6.sin6_port)); + +	if (ret == FILTER_DENY) { +		bt->cnt_aclrefused++; +		zlog_info("bmp[%s] connection refused by access-list", buf); +		close(bmp_sock); +		return NULL; +	} +	bt->cnt_accept++; + +	setsockopt(bmp_sock, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)); +	setsockopt(bmp_sock, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)); + +	zlog_info("bmp[%s] connection established", buf); + +	/* Allocate new BMP structure and set up default values. */ +	bmp = bmp_new(bt, bmp_sock); +	strlcpy(bmp->remote, buf, sizeof(bmp->remote)); + +	bmp->state = BMP_PeerUp; +	bmp->pullwr = pullwr_new(bm->master, bmp_sock, bmp, bmp_wrfill, +			bmp_wrerr); +	bmp_send_initiation(bmp); + +	return bmp; +} + +/* Accept BMP connection. */ +static int bmp_accept(struct thread *thread) +{ +	union sockunion su; +	struct bmp_listener *bl = THREAD_ARG(thread); +	int bmp_sock; + +	/* We continue hearing BMP socket. */ +	thread_add_read(bm->master, bmp_accept, bl, bl->sock, &bl->t_accept); + +	memset(&su, 0, sizeof(union sockunion)); + +	/* We can handle IPv4 or IPv6 socket. */ +	bmp_sock = sockunion_accept(bl->sock, &su); +	if (bmp_sock < 0) { +		zlog_info("bmp: accept_sock failed: %s\n", +                          safe_strerror (errno)); +		return -1; +	} +	bmp_open(bl->targets, bmp_sock); +	return 0; +} + +static void bmp_close(struct bmp *bmp) +{ +	struct bmp_queue_entry *bqe; +	struct bmp_mirrorq *bmq; + +	if (bmp->active) +		bmp_active_disconnected(bmp->active); + +	while ((bmq = bmp_pull_mirror(bmp))) +		if (!bmq->refcount) +			XFREE(MTYPE_BMP_MIRRORQ, bmq); +	while ((bqe = bmp_pull(bmp))) +		if (!bqe->refcount) +			XFREE(MTYPE_BMP_QUEUE, bqe); + +	THREAD_OFF(bmp->t_read); +	pullwr_del(bmp->pullwr); +	close(bmp->socket); +} + +static struct bmp_bgp *bmp_bgp_find(struct bgp *bgp) +{ +	struct bmp_bgp dummy = { .bgp = bgp }; +	return bmp_bgph_find(&bmp_bgph, &dummy); +} + +static struct bmp_bgp *bmp_bgp_get(struct bgp *bgp) +{ +	struct bmp_bgp *bmpbgp; + +	bmpbgp = bmp_bgp_find(bgp); +	if (bmpbgp) +		return bmpbgp; + +	bmpbgp = XCALLOC(MTYPE_BMP, sizeof(*bmpbgp)); +	bmpbgp->bgp = bgp; +	bmpbgp->mirror_qsizelimit = ~0UL; +	bmp_mirrorq_init(&bmpbgp->mirrorq); +	bmp_bgph_add(&bmp_bgph, bmpbgp); + +	return bmpbgp; +} + +static void bmp_bgp_put(struct bmp_bgp *bmpbgp) +{ +	struct bmp_targets *bt; + +	bmp_bgph_del(&bmp_bgph, bmpbgp); + +	frr_each_safe(bmp_targets, &bmpbgp->targets, bt) +		bmp_targets_put(bt); + +	bmp_mirrorq_fini(&bmpbgp->mirrorq); +	XFREE(MTYPE_BMP, bmpbgp); +} + +static int bmp_bgp_del(struct bgp *bgp) +{ +	struct bmp_bgp *bmpbgp = bmp_bgp_find(bgp); + +	if (bmpbgp) +		bmp_bgp_put(bmpbgp); +	return 0; +} + +static struct bmp_bgp_peer *bmp_bgp_peer_find(uint64_t peerid) +{ +	struct bmp_bgp_peer dummy = { .peerid = peerid }; +	return bmp_peerh_find(&bmp_peerh, &dummy); +} + +static struct bmp_bgp_peer *bmp_bgp_peer_get(struct peer *peer) +{ +	struct bmp_bgp_peer *bbpeer; + +	bbpeer = bmp_bgp_peer_find(peer->qobj_node.nid); +	if (bbpeer) +		return bbpeer; + +	bbpeer = XCALLOC(MTYPE_BMP_PEER, sizeof(*bbpeer)); +	bbpeer->peerid = peer->qobj_node.nid; +	bmp_peerh_add(&bmp_peerh, bbpeer); + +	return bbpeer; +} + +static struct bmp_targets *bmp_targets_find1(struct bgp *bgp, const char *name) +{ +	struct bmp_bgp *bmpbgp = bmp_bgp_find(bgp); +	struct bmp_targets dummy; + +	if (!bmpbgp) +		return NULL; +	dummy.name = (char *)name; +	return bmp_targets_find(&bmpbgp->targets, &dummy); +} + +static struct bmp_targets *bmp_targets_get(struct bgp *bgp, const char *name) +{ +	struct bmp_targets *bt; + +	bt = bmp_targets_find1(bgp, name); +	if (bt) +		return bt; + +	bt = XCALLOC(MTYPE_BMP_TARGETS, sizeof(*bt)); +	bt->name = XSTRDUP(MTYPE_BMP_TARGETSNAME, name); +	bt->bgp = bgp; +	bt->bmpbgp = bmp_bgp_get(bgp); +	bmp_session_init(&bt->sessions); +	bmp_qhash_init(&bt->updhash); +	bmp_qlist_init(&bt->updlist); +	bmp_actives_init(&bt->actives); +	bmp_listeners_init(&bt->listeners); + +	QOBJ_REG(bt, bmp_targets); +	bmp_targets_add(&bt->bmpbgp->targets, bt); +	return bt; +} + +static void bmp_targets_put(struct bmp_targets *bt) +{ +	struct bmp *bmp; +	struct bmp_active *ba; + +	frr_each_safe (bmp_actives, &bt->actives, ba) +		bmp_active_put(ba); + +	frr_each_safe(bmp_session, &bt->sessions, bmp) { +		bmp_close(bmp); +		bmp_free(bmp); +	} + +	bmp_targets_del(&bt->bmpbgp->targets, bt); +	QOBJ_UNREG(bt); + +	bmp_listeners_fini(&bt->listeners); +	bmp_actives_fini(&bt->actives); +	bmp_qhash_fini(&bt->updhash); +	bmp_qlist_fini(&bt->updlist); + +	XFREE(MTYPE_BMP_ACLNAME, bt->acl_name); +	XFREE(MTYPE_BMP_ACLNAME, bt->acl6_name); +	bmp_session_fini(&bt->sessions); + +	XFREE(MTYPE_BMP_TARGETSNAME, bt->name); +	XFREE(MTYPE_BMP_TARGETS, bt); +} + +static struct bmp_listener *bmp_listener_find(struct bmp_targets *bt, +					      const union sockunion *su, +					      int port) +{ +	struct bmp_listener dummy; +	dummy.addr = *su; +	dummy.port = port; +	return bmp_listeners_find(&bt->listeners, &dummy); +} + +static struct bmp_listener *bmp_listener_get(struct bmp_targets *bt, +					     const union sockunion *su, +					     int port) +{ +	struct bmp_listener *bl = bmp_listener_find(bt, su, port); + +	if (bl) +		return bl; + +	bl = XCALLOC(MTYPE_BMP_LISTENER, sizeof(*bl)); +	bl->targets = bt; +	bl->addr = *su; +	bl->port = port; +	bl->sock = -1; + +	bmp_listeners_add(&bt->listeners, bl); +	return bl; +} + +static void bmp_listener_put(struct bmp_listener *bl) +{ +	bmp_listeners_del(&bl->targets->listeners, bl); +	XFREE(MTYPE_BMP_LISTENER, bl); +} + +static void bmp_listener_start(struct bmp_listener *bl) +{ +	int sock, ret; + +	sock = socket(bl->addr.sa.sa_family, SOCK_STREAM, 0); +	if (sock < 0) +		return; + +	sockopt_reuseaddr(sock); +	sockopt_reuseport(sock); +	sockopt_v6only(bl->addr.sa.sa_family, sock); +	set_cloexec(sock); + +	ret = sockunion_bind(sock, &bl->addr, bl->port, &bl->addr); +	if (ret < 0) +		goto out_sock; + +	ret = listen(sock, 3); +	if (ret < 0) +		goto out_sock; + +	bl->sock = sock; +	thread_add_read(bm->master, bmp_accept, bl, sock, &bl->t_accept); +	return; +out_sock: +	close(sock); +} + +static void bmp_listener_stop(struct bmp_listener *bl) +{ +	THREAD_OFF(bl->t_accept); + +	if (bl->sock != -1) +		close(bl->sock); +	bl->sock = -1; +} + +static struct bmp_active *bmp_active_find(struct bmp_targets *bt, +					  const char *hostname, int port) +{ +	struct bmp_active dummy; +	dummy.hostname = (char *)hostname; +	dummy.port = port; +	return bmp_actives_find(&bt->actives, &dummy); +} + +static struct bmp_active *bmp_active_get(struct bmp_targets *bt, +					 const char *hostname, int port) +{ +	struct bmp_active *ba; + +	ba = bmp_active_find(bt, hostname, port); +	if (ba) +		return ba; + +	ba = XCALLOC(MTYPE_BMP_ACTIVE, sizeof(*ba)); +	ba->targets = bt; +	ba->hostname = XSTRDUP(MTYPE_TMP, hostname); +	ba->port = port; +	ba->minretry = BMP_DFLT_MINRETRY; +	ba->maxretry = BMP_DFLT_MAXRETRY; +	ba->socket = -1; + +	bmp_actives_add(&bt->actives, ba); +	return ba; +} + +static void bmp_active_put(struct bmp_active *ba) +{ +	THREAD_OFF(ba->t_timer); +	THREAD_OFF(ba->t_read); +	THREAD_OFF(ba->t_write); + +	bmp_actives_del(&ba->targets->actives, ba); + +	if (ba->bmp) { +		ba->bmp->active = NULL; +		bmp_close(ba->bmp); +		bmp_free(ba->bmp); +	} +	if (ba->socket != -1) +		close(ba->socket); + +	XFREE(MTYPE_TMP, ba->hostname); +	XFREE(MTYPE_BMP_ACTIVE, ba); +} + +static void bmp_active_setup(struct bmp_active *ba); + +static void bmp_active_connect(struct bmp_active *ba) +{ +	enum connect_result res; +	char buf[SU_ADDRSTRLEN]; + +	for (; ba->addrpos < ba->addrtotal; ba->addrpos++) { +		ba->socket = sockunion_socket(&ba->addrs[ba->addrpos]); +		if (ba->socket < 0) { +			zlog_warn("bmp[%s]: failed to create socket", +				  ba->hostname); +			continue; +		} +		 +		set_nonblocking(ba->socket); +		res = sockunion_connect(ba->socket, &ba->addrs[ba->addrpos], +				      htons(ba->port), 0); +		switch (res) { +		case connect_error: +			sockunion2str(&ba->addrs[ba->addrpos], buf, +				      sizeof(buf)); +			zlog_warn("bmp[%s]: failed to connect to %s:%d", +				  ba->hostname, buf, ba->port); +			close(ba->socket); +			ba->socket = -1; +			continue; +		case connect_success: +			break; +		case connect_in_progress: +			bmp_active_setup(ba); +			return; +		} +	} + +	/* exhausted all addresses */ +	ba->curretry += ba->curretry / 2; +	bmp_active_setup(ba); +} + +static void bmp_active_resolved(struct resolver_query *resq, int numaddrs, +				union sockunion *addr) +{ +	struct bmp_active *ba = container_of(resq, struct bmp_active, resq); +	unsigned i; + +	if (numaddrs <= 0) { +		zlog_warn("bmp[%s]: hostname resolution failed", ba->hostname); +		ba->curretry += ba->curretry / 2; +		bmp_active_setup(ba); +		return; +	} +	if (numaddrs > (int)array_size(ba->addrs)) +		numaddrs = array_size(ba->addrs); + +	ba->addrpos = 0; +	ba->addrtotal = numaddrs; +	for (i = 0; i < ba->addrtotal; i++) +		memcpy(&ba->addrs[i], &addr[i], sizeof(ba->addrs[0])); + +	bmp_active_connect(ba); +} + +static int bmp_active_thread(struct thread *t) +{ +	struct bmp_active *ba = THREAD_ARG(t); +	socklen_t slen; +	int status, ret; +	char buf[SU_ADDRSTRLEN]; + +	/* all 3 end up here, though only timer or read+write are active +	 * at a time */ +	THREAD_OFF(ba->t_timer); +	THREAD_OFF(ba->t_read); +	THREAD_OFF(ba->t_write); + +	if (ba->socket == -1) { +		resolver_resolve(&ba->resq, AF_UNSPEC, ba->hostname, +				 bmp_active_resolved); +		return 0; +	} + +	slen = sizeof(status); +	ret = getsockopt(ba->socket, SOL_SOCKET, SO_ERROR, (void *)&status, +			 &slen); + +	sockunion2str(&ba->addrs[ba->addrpos], buf, sizeof(buf)); +	if (ret < 0 || status != 0) { +		zlog_warn("bmp[%s]: failed to connect to %s:%d", +			  ba->hostname, buf, ba->port); +		goto out_next; +	} + +	zlog_warn("bmp[%s]: outbound connection to %s:%d", +		  ba->hostname, buf, ba->port); + +	ba->bmp = bmp_open(ba->targets, ba->socket); +	if (!ba->bmp) +		goto out_next; + +	ba->bmp->active = ba; +	ba->socket = -1; +	ba->curretry = ba->minretry; +	return 0; + +out_next: +	close(ba->socket); +	ba->socket = -1; +	ba->addrpos++; +	bmp_active_connect(ba); +	return 0; +} + +static void bmp_active_disconnected(struct bmp_active *ba) +{ +	ba->bmp = NULL; +	bmp_active_setup(ba); +} + +static void bmp_active_setup(struct bmp_active *ba) +{ +	THREAD_OFF(ba->t_timer); +	THREAD_OFF(ba->t_read); +	THREAD_OFF(ba->t_write); + +	if (ba->bmp) +		return; +	if (ba->resq.callback) +		return; + +	if (ba->curretry > ba->maxretry) +		ba->curretry = ba->maxretry; + +	if (ba->socket == -1) +		thread_add_timer_msec(bm->master, bmp_active_thread, ba, +				      ba->curretry, &ba->t_timer); +	else { +		thread_add_read(bm->master, bmp_active_thread, ba, ba->socket, +				&ba->t_read); +		thread_add_write(bm->master, bmp_active_thread, ba, ba->socket, +				&ba->t_write); +	} +} + +static struct cmd_node bmp_node = {BMP_NODE, "%s(config-bgp-bmp)# "}; + +#define BMP_STR "BGP Monitoring Protocol\n" + +#ifndef VTYSH_EXTRACT_PL +#include "bgp_bmp_clippy.c" +#endif + +DEFPY_NOSH(bmp_targets_main, +      bmp_targets_cmd, +      "bmp targets BMPTARGETS", +      BMP_STR +      "Create BMP target group\n" +      "Name of the BMP target group\n") +{ +	VTY_DECLVAR_CONTEXT(bgp, bgp); +	struct bmp_targets *bt; + +	bt = bmp_targets_get(bgp, bmptargets); + +	VTY_PUSH_CONTEXT_SUB(BMP_NODE, bt); +	return CMD_SUCCESS; +} + +DEFPY(no_bmp_targets_main, +      no_bmp_targets_cmd, +      "no bmp targets BMPTARGETS", +      NO_STR +      BMP_STR +      "Delete BMP target group\n" +      "Name of the BMP target group\n") +{ +	VTY_DECLVAR_CONTEXT(bgp, bgp); +	struct bmp_targets *bt; + +	bt = bmp_targets_find1(bgp, bmptargets); +	if (!bt) { +		vty_out(vty, "%% BMP target group not found\n"); +		return CMD_WARNING; +	} +	bmp_targets_put(bt); +	return CMD_SUCCESS; +} + +DEFPY(bmp_listener_main, +      bmp_listener_cmd, +      "bmp listener <X:X::X:X|A.B.C.D> port (1-65535)", +      BMP_STR +      "Listen for inbound BMP connections\n" +      "IPv6 address to listen on\n" +      "IPv4 address to listen on\n" +      "TCP Port number\n" +      "TCP Port number\n") +{ +	VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt); +	struct bmp_listener *bl; + +	bl = bmp_listener_get(bt, listener, port); +	if (bl->sock == -1) +		bmp_listener_start(bl); + +	return CMD_SUCCESS; +} + +DEFPY(no_bmp_listener_main, +      no_bmp_listener_cmd, +      "no bmp listener <X:X::X:X|A.B.C.D> port (1-65535)", +      NO_STR +      BMP_STR +      "Create BMP listener\n" +      "IPv6 address to listen on\n" +      "IPv4 address to listen on\n" +      "TCP Port number\n" +      "TCP Port number\n") +{ +	VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt); +	struct bmp_listener *bl; + +	bl = bmp_listener_find(bt, listener, port); +	if (!bl) { +		vty_out(vty, "%% BMP listener not found\n"); +		return CMD_WARNING; +	} +	bmp_listener_stop(bl); +	bmp_listener_put(bl); +	return CMD_SUCCESS; +} + +DEFPY(bmp_connect, +      bmp_connect_cmd, +      "[no] bmp connect HOSTNAME port (1-65535) " +		"{min-retry (100-86400000)" +		"|max-retry (100-86400000)}", +      NO_STR +      BMP_STR +      "Actively establish connection to monitoring station\n" +      "Monitoring station hostname or address\n" +      "TCP port\n" +      "TCP port\n" +      "Minimum connection retry interval\n" +      "Minimum connection retry interval (milliseconds)\n" +      "Maximum connection retry interval\n" +      "Maximum connection retry interval (milliseconds)\n") +{ +	VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt); +	struct bmp_active *ba; + +	if (no) { +		ba = bmp_active_find(bt, hostname, port); +		if (!ba) { +			vty_out(vty, "%% No such active connection found\n"); +			return CMD_WARNING; +		} +		bmp_active_put(ba); +		return CMD_SUCCESS; +	} + +	ba = bmp_active_get(bt, hostname, port); +	if (min_retry_str) +		ba->minretry = min_retry; +	if (max_retry_str) +		ba->maxretry = max_retry; +	ba->curretry = ba->minretry; +	bmp_active_setup(ba); + +	return CMD_SUCCESS; +} + +DEFPY(bmp_acl, +      bmp_acl_cmd, +      "[no] <ip|ipv6>$af access-list WORD", +      NO_STR +      IP_STR +      IPV6_STR +      "Access list to restrict BMP sessions\n" +      "Access list name\n") +{ +	VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt); +	char **what; + +	if (no) +		access_list = NULL; +	if (!strcmp(af, "ipv6")) +		what = &bt->acl6_name; +	else +		what = &bt->acl_name; + +	XFREE(MTYPE_BMP_ACLNAME, *what); +	if (access_list) +		*what = XSTRDUP(MTYPE_BMP_ACLNAME, access_list); + +	return CMD_SUCCESS; +} + +DEFPY(bmp_stats_cfg, +      bmp_stats_cmd, +      "[no] bmp stats [interval (100-86400000)]", +      NO_STR +      BMP_STR +      "Send BMP statistics messages\n" +      "Specify BMP stats interval\n" +      "Interval (milliseconds) to send BMP Stats in\n") +{ +	VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt); + +	THREAD_OFF(bt->t_stats); +	if (no) +		bt->stat_msec = 0; +	else if (interval_str) +		bt->stat_msec = interval; +	else +		bt->stat_msec = BMP_STAT_DEFAULT_TIMER; + +	if (bt->stat_msec) +		thread_add_timer_msec(bm->master, bmp_stats, bt, bt->stat_msec, +				      &bt->t_stats); +	return CMD_SUCCESS; +} + +DEFPY(bmp_monitor_cfg, +      bmp_monitor_cmd, +      "[no] bmp monitor "BGP_AFI_CMD_STR" <unicast|multicast> <pre-policy|post-policy>$policy", +      NO_STR +      BMP_STR +      "Send BMP route monitoring messages\n" +      BGP_AFI_HELP_STR +      "Address family modifier\n" +      "Address family modifier\n" +      "Send state before policy and filter processing\n" +      "Send state with policy and filters applied\n") +{ +	int index = 0; +	uint8_t flag, prev; +	afi_t afi; +	safi_t safi; + +	VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt); +	struct bmp *bmp; + +	argv_find_and_parse_afi(argv, argc, &index, &afi); +	argv_find_and_parse_safi(argv, argc, &index, &safi); + +	if (policy[1] == 'r') +		flag = BMP_MON_PREPOLICY; +	else +		flag = BMP_MON_POSTPOLICY; + +	prev = bt->afimon[afi][safi]; +	if (no) +		bt->afimon[afi][safi] &= ~flag; +	else +		bt->afimon[afi][safi] |= flag; + +	if (prev == bt->afimon[afi][safi]) +		return CMD_SUCCESS; + +	frr_each (bmp_session, &bt->sessions, bmp) { +		if (bmp->syncafi == afi && bmp->syncsafi == safi) { +			bmp->syncafi = AFI_MAX; +			bmp->syncsafi = SAFI_MAX; +		} + +		if (!bt->afimon[afi][safi]) { +			bmp->afistate[afi][safi] = BMP_AFI_INACTIVE; +			continue; +		} + +		bmp->afistate[afi][safi] = BMP_AFI_NEEDSYNC; +	} + +	return CMD_SUCCESS; +} + +DEFPY(bmp_mirror_cfg, +      bmp_mirror_cmd, +      "[no] bmp mirror", +      NO_STR +      BMP_STR +      "Send BMP route mirroring messages\n") +{ +	VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt); +	struct bmp *bmp; + +	if (bt->mirror == !no) +		return CMD_SUCCESS; + +	bt->mirror = !no; +	if (bt->mirror) +		return CMD_SUCCESS; + +	frr_each (bmp_session, &bt->sessions, bmp) { +		struct bmp_mirrorq *bmq; + +		while ((bmq = bmp_pull_mirror(bmp))) +			if (!bmq->refcount) +				XFREE(MTYPE_BMP_MIRRORQ, bmq); +	} +	return CMD_SUCCESS; +} + +DEFPY(bmp_mirror_limit_cfg, +      bmp_mirror_limit_cmd, +      "bmp mirror buffer-limit (0-4294967294)", +      BMP_STR +      "Route Mirroring settings\n" +      "Configure maximum memory used for buffered mirroring messages\n" +      "Limit in bytes\n") +{ +	VTY_DECLVAR_CONTEXT(bgp, bgp); +	struct bmp_bgp *bmpbgp; + +	bmpbgp = bmp_bgp_get(bgp); +	bmpbgp->mirror_qsizelimit = buffer_limit; + +	return CMD_SUCCESS; +} + +DEFPY(no_bmp_mirror_limit_cfg, +      no_bmp_mirror_limit_cmd, +      "no bmp mirror buffer-limit [(0-4294967294)]", +      NO_STR +      BMP_STR +      "Route Mirroring settings\n" +      "Configure maximum memory used for buffered mirroring messages\n" +      "Limit in bytes\n") +{ +	VTY_DECLVAR_CONTEXT(bgp, bgp); +	struct bmp_bgp *bmpbgp; + +	bmpbgp = bmp_bgp_get(bgp); +	bmpbgp->mirror_qsizelimit = ~0UL; + +	return CMD_SUCCESS; +} + + +DEFPY(show_bmp, +      show_bmp_cmd, +      "show bmp", +      SHOW_STR +      BMP_STR) +{ +	struct bmp_bgp *bmpbgp; +	struct bmp_targets *bt; +	struct bmp_listener *bl; +	struct bmp *bmp; +	struct ttable *tt; +	char buf[SU_ADDRSTRLEN]; + +	frr_each(bmp_bgph, &bmp_bgph, bmpbgp) { +		vty_out(vty, "BMP state for BGP %s:\n\n", +				bmpbgp->bgp->name_pretty); +		vty_out(vty, "  Route Mirroring %9zu bytes (%zu messages) pending\n", +				bmpbgp->mirror_qsize, +				bmp_mirrorq_count(&bmpbgp->mirrorq)); +		vty_out(vty, "                  %9zu bytes maximum buffer used\n", +				bmpbgp->mirror_qsizemax); +		if (bmpbgp->mirror_qsizelimit != ~0UL) +			vty_out(vty, "                  %9zu bytes buffer size limit\n", +					bmpbgp->mirror_qsizelimit); +		vty_out(vty, "\n"); + +		frr_each(bmp_targets, &bmpbgp->targets, bt) { +			vty_out(vty, "  Targets \"%s\":\n", bt->name); +			vty_out(vty, "    Route Mirroring %sabled\n", +				bt->mirror ? "en" : "dis"); + +			afi_t afi; +			safi_t safi; + +			FOREACH_AFI_SAFI (afi, safi) { +				const char *str = NULL; + +				switch (bt->afimon[afi][safi]) { +				case BMP_MON_PREPOLICY: +					str = "pre-policy"; +					break; +				case BMP_MON_POSTPOLICY: +					str = "post-policy"; +					break; +				case BMP_MON_PREPOLICY | BMP_MON_POSTPOLICY: +					str = "pre-policy and post-policy"; +					break; +				} +				if (!str) +					continue; +				vty_out(vty, "    Route Monitoring %s %s %s\n", +					afi2str(afi), safi2str(safi), str); +			} + +			vty_out(vty, "    Listeners:\n"); +			frr_each (bmp_listeners, &bt->listeners, bl) +				vty_out(vty, "      %s:%d\n", +					sockunion2str(&bl->addr, buf, +						      SU_ADDRSTRLEN), bl->port); + +			vty_out(vty, "\n    %zu connected clients:\n", +					bmp_session_count(&bt->sessions)); +			tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); +			ttable_add_row(tt, "remote|uptime|MonSent|MirrSent|MirrLost|ByteSent|ByteQ|ByteQKernel"); +			ttable_rowseps(tt, 0, BOTTOM, true, '-'); + +			frr_each (bmp_session, &bt->sessions, bmp) { +				uint64_t total; +				size_t q, kq; + +				pullwr_stats(bmp->pullwr, &total, &q, &kq); + +				ttable_add_row(tt, "%s|-|%Lu|%Lu|%Lu|%Lu|%zu|%zu", +					       bmp->remote, +					       bmp->cnt_update, +					       bmp->cnt_mirror, +					       bmp->cnt_mirror_overruns, +					       total, q, kq); +			} +			char *out = ttable_dump(tt, "\n"); +			vty_out(vty, "%s", out); +			XFREE(MTYPE_TMP, out); +			ttable_del(tt); +			vty_out(vty, "\n"); +		} +	} + +	return CMD_SUCCESS; +} + +static int bmp_config_write(struct bgp *bgp, struct vty *vty) +{ +	struct bmp_bgp *bmpbgp = bmp_bgp_find(bgp); +	struct bmp_targets *bt; +	struct bmp_listener *bl; +	struct bmp_active *ba; +	char buf[SU_ADDRSTRLEN]; +	afi_t afi; +	safi_t safi; + +	if (!bmpbgp) +		return 0; + +	if (bmpbgp->mirror_qsizelimit != ~0UL) +		vty_out(vty, " !\n bmp mirror buffer-limit %zu\n", +			bmpbgp->mirror_qsizelimit); + +	frr_each(bmp_targets, &bmpbgp->targets, bt) { +		vty_out(vty, " !\n bmp targets %s\n", bt->name); + +		if (bt->acl6_name) +			vty_out(vty, "  ipv6 access-list %s\n", bt->acl6_name); +		if (bt->acl_name) +			vty_out(vty, "  ip access-list %s\n", bt->acl_name); + +		if (bt->stat_msec) +			vty_out(vty, "  bmp stats interval %d\n", +					bt->stat_msec); + +		if (bt->mirror) +			vty_out(vty, "  bmp mirror\n"); + +		FOREACH_AFI_SAFI (afi, safi) { +			const char *afi_str = (afi == AFI_IP) ? "ipv4" : "ipv6"; + +			if (bt->afimon[afi][safi] & BMP_MON_PREPOLICY) +				vty_out(vty, "  bmp monitor %s %s pre-policy\n", +					afi_str, safi2str(safi)); +			if (bt->afimon[afi][safi] & BMP_MON_POSTPOLICY) +				vty_out(vty, "  bmp monitor %s %s post-policy\n", +					afi_str, safi2str(safi)); +		} +		frr_each (bmp_listeners, &bt->listeners, bl) +			vty_out(vty, " \n  bmp listener %s port %d\n", +				sockunion2str(&bl->addr, buf, SU_ADDRSTRLEN), +				bl->port); + +		frr_each (bmp_actives, &bt->actives, ba) +			vty_out(vty, "  bmp connect %s port %u min-retry %u max-retry %u\n", +				ba->hostname, ba->port, ba->minretry, ba->maxretry); +	} + +	return 0; +} + +static int bgp_bmp_init(struct thread_master *tm) +{ +	install_node(&bmp_node, NULL); +	install_default(BMP_NODE); +	install_element(BGP_NODE, &bmp_targets_cmd); +	install_element(BGP_NODE, &no_bmp_targets_cmd); + +	install_element(BMP_NODE, &bmp_listener_cmd); +	install_element(BMP_NODE, &no_bmp_listener_cmd); +	install_element(BMP_NODE, &bmp_connect_cmd); +	install_element(BMP_NODE, &bmp_acl_cmd); +	install_element(BMP_NODE, &bmp_stats_cmd); +	install_element(BMP_NODE, &bmp_monitor_cmd); +	install_element(BMP_NODE, &bmp_mirror_cmd); + +	install_element(BGP_NODE, &bmp_mirror_limit_cmd); +	install_element(BGP_NODE, &no_bmp_mirror_limit_cmd); + +	install_element(VIEW_NODE, &show_bmp_cmd); + +	resolver_init(tm); +	return 0; +} + +static int bgp_bmp_module_init(void) +{ +	hook_register(bgp_packet_dump, bmp_mirror_packet); +	hook_register(bgp_packet_send, bmp_outgoing_packet); +	hook_register(peer_established, bmp_peer_established); +	hook_register(peer_backward_transition, bmp_peer_backward); +	hook_register(bgp_process, bmp_process); +	hook_register(bgp_inst_config_write, bmp_config_write); +	hook_register(bgp_inst_delete, bmp_bgp_del); +	hook_register(frr_late_init, bgp_bmp_init); +	return 0; +} + +FRR_MODULE_SETUP(.name = "bgpd_bmp", .version = FRR_VERSION, +		 .description = "bgpd BMP module", +		 .init = bgp_bmp_module_init) diff --git a/bgpd/bgp_bmp.h b/bgpd/bgp_bmp.h new file mode 100644 index 0000000000..9d270e808c --- /dev/null +++ b/bgpd/bgp_bmp.h @@ -0,0 +1,303 @@ +/* BMP support. + * Copyright (C) 2018 Yasuhiro Ohara + * Copyright (C) 2019 David Lamparter for NetDEF, Inc. + * + * 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 _BGP_BMP_H_ +#define _BGP_BMP_H_ + +#include "zebra.h" +#include "typesafe.h" +#include "pullwr.h" +#include "qobj.h" +#include "resolver.h" + +#define BMP_VERSION_3	3 + +#define BMP_LENGTH_POS  1 + +/* BMP message types */ +#define BMP_TYPE_ROUTE_MONITORING       0 +#define BMP_TYPE_STATISTICS_REPORT      1 +#define BMP_TYPE_PEER_DOWN_NOTIFICATION 2 +#define BMP_TYPE_PEER_UP_NOTIFICATION   3 +#define BMP_TYPE_INITIATION             4 +#define BMP_TYPE_TERMINATION            5 +#define BMP_TYPE_ROUTE_MIRRORING        6 + +#define BMP_READ_BUFSIZ	1024 + +/* bmp->state */ +#define BMP_None        0 +#define BMP_PeerUp      2 +#define BMP_Run         3 + +/* This one is for BMP Route Monitoring messages, i.e. delivering updates + * in somewhat processed (as opposed to fully raw, see mirroring below) form. + * RFC explicitly says that we can skip old updates if we haven't sent them out + * yet and another newer update for the same prefix arrives. + * + * So, at most one of these can exist for each (bgp, afi, safi, prefix, peerid) + * tuple; if some prefix is "re-added" to the queue, the existing entry is + * instead moved to the end of the queue.  This ensures that the queue size is + * bounded by the BGP table size. + * + * bmp_qlist is the queue itself while bmp_qhash is used to efficiently check + * whether a tuple is already on the list.  The queue is maintained per + * bmp_target. + * + * refcount = number of "struct bmp *" whose queue position is before this + * entry, i.e. number of BMP sessions where we still want to send this out. + * Decremented on send so we know when we're done with an entry (i.e. this + * always happens from the front of the queue.) + */ + +PREDECL_DLIST(bmp_qlist) +PREDECL_HASH(bmp_qhash) + +struct bmp_queue_entry { +	struct bmp_qlist_item bli; +	struct bmp_qhash_item bhi; + +	struct prefix p; +	uint64_t peerid; +	afi_t afi; +	safi_t safi; + +	size_t refcount; +}; + +/* This is for BMP Route Mirroring, which feeds fully raw BGP PDUs out to BMP + * receivers.  So, this goes directly off packet RX/TX handling instead of + * grabbing bits from tables. + * + * There is *one* queue for each "struct bgp *" where we throw everything on, + * with a size limit.  Refcount works the same as for monitoring above. + */ + +PREDECL_LIST(bmp_mirrorq) + +struct bmp_mirrorq { +	struct bmp_mirrorq_item bmi; + +	size_t refcount; +	uint64_t peerid; +	struct timeval tv; + +	size_t len; +	uint8_t data[0]; +}; + +enum { +	BMP_AFI_INACTIVE = 0, +	BMP_AFI_NEEDSYNC, +	BMP_AFI_SYNC, +	BMP_AFI_LIVE, +}; + +PREDECL_LIST(bmp_session) + +struct bmp_active; +struct bmp_targets; + +/* an established BMP session to a peer */ +struct bmp { +	struct bmp_session_item bsi; +	struct bmp_targets *targets; +	struct bmp_active *active; + +	int socket; +	char remote[SU_ADDRSTRLEN + 6]; +	struct thread *t_read; + +	struct pullwr *pullwr; + +	int state; + +	/* queue positions must remain synced with refcounts in the items. +	 * Whenever appending a queue item, we need to know the correct number +	 * of "struct bmp *" that want it, and when moving these positions +	 * ahead we need to make sure that refcount is decremented.  Also, on +	 * disconnects we need to walk the queue and drop our reference. +	 */ +	struct bmp_queue_entry *queuepos; +	struct bmp_mirrorq *mirrorpos; +	bool mirror_lost; + +	/* enum BMP_AFI_* */ +	uint8_t afistate[AFI_MAX][SAFI_MAX]; + +	/* counters for the various BMP packet types */ +	uint64_t cnt_update, cnt_mirror; +	/* number of times this peer wasn't fast enough in consuming the +	 * mirror queue +	 */ +	uint64_t cnt_mirror_overruns; +	struct timeval t_up; + +	/* synchronization / startup works by repeatedly finding the next +	 * table entry, the sync* fields note down what we sent last +	 */ +	struct prefix syncpos; +	uint64_t syncpeerid; +	afi_t syncafi; +	safi_t syncsafi; +}; + +/* config & state for an active outbound connection.  When the connection + * succeeds, "bmp" is set up. + */ + +PREDECL_SORTLIST_UNIQ(bmp_actives) + +#define BMP_DFLT_MINRETRY	30000 +#define BMP_DFLT_MAXRETRY	720000 + +struct bmp_active { +	struct bmp_actives_item bai; +	struct bmp_targets *targets; +	struct bmp *bmp; + +	char *hostname; +	int port; +	unsigned minretry, maxretry; + +	struct resolver_query resq; + +	unsigned curretry; +	unsigned addrpos, addrtotal; +	union sockunion addrs[8]; +	int socket; +	struct thread *t_timer, *t_read, *t_write; +}; + +/* config & state for passive / listening sockets */ +PREDECL_SORTLIST_UNIQ(bmp_listeners) + +struct bmp_listener { +	struct bmp_listeners_item bli; + +	struct bmp_targets *targets; + +	union sockunion addr; +	int port; + +	struct thread *t_accept; +	int sock; +}; + +/* bmp_targets - plural since it may contain multiple bmp_listener & + * bmp_active items.  If they have the same config, BMP session should be + * put in the same targets since that's a bit more effective. + */ +PREDECL_SORTLIST_UNIQ(bmp_targets) + +struct bmp_targets { +	struct bmp_targets_item bti; + +	struct bmp_bgp *bmpbgp; +	struct bgp *bgp; +	char *name; + +	struct bmp_listeners_head listeners; + +	char *acl_name; +	char *acl6_name; +#define BMP_STAT_DEFAULT_TIMER	60000 +	int stat_msec; + +	/* only IPv4 & IPv6 / unicast & multicast supported for now */ +#define BMP_MON_PREPOLICY	(1 << 0) +#define BMP_MON_POSTPOLICY	(1 << 1) +	uint8_t afimon[AFI_MAX][SAFI_MAX]; +	bool mirror; + +	struct bmp_actives_head actives; + +	struct thread *t_stats; +	struct bmp_session_head sessions; + +	struct bmp_qhash_head updhash; +	struct bmp_qlist_head updlist; + +	uint64_t cnt_accept, cnt_aclrefused; + +	QOBJ_FIELDS +}; +DECLARE_QOBJ_TYPE(bmp_targets) + +/* per struct peer * data.  Lookup by peer->qobj_node.nid, created on demand, + * deleted in peer_backward hook. */ +PREDECL_HASH(bmp_peerh) + +struct bmp_bgp_peer { +	struct bmp_peerh_item bpi; + +	uint64_t peerid; +	/* struct peer *peer; */ + +	uint8_t *open_rx; +	size_t open_rx_len; + +	uint8_t *open_tx; +	size_t open_tx_len; +}; + +/* per struct bgp * data */ +PREDECL_HASH(bmp_bgph) + +struct bmp_bgp { +	struct bmp_bgph_item bbi; + +	struct bgp *bgp; +	struct bmp_targets_head targets; + +	struct bmp_mirrorq_head mirrorq; +	size_t mirror_qsize, mirror_qsizemax; + +	size_t mirror_qsizelimit; +}; + +enum { +	BMP_PEERDOWN_LOCAL_NOTIFY       = 1, +	BMP_PEERDOWN_LOCAL_FSM          = 2, +	BMP_PEERDOWN_REMOTE_NOTIFY      = 3, +	BMP_PEERDOWN_REMOTE_CLOSE       = 4, +	BMP_PEERDOWN_ENDMONITOR         = 5, +}; + +enum { +	BMP_STATS_PFX_REJECTED          = 0, +	BMP_STATS_PFX_DUP_ADV           = 1, +	BMP_STATS_PFX_DUP_WITHDRAW      = 2, +	BMP_STATS_UPD_LOOP_CLUSTER      = 3, +	BMP_STATS_UPD_LOOP_ASPATH       = 4, +	BMP_STATS_UPD_LOOP_ORIGINATOR   = 5, +	BMP_STATS_UPD_LOOP_CONFED       = 6, +	BMP_STATS_SIZE_ADJ_RIB_IN       = 7, +	BMP_STATS_SIZE_LOC_RIB          = 8, +	BMP_STATS_SIZE_ADJ_RIB_IN_SAFI  = 9, +	BMP_STATS_SIZE_LOC_RIB_IN_SAFI  = 10, +	BMP_STATS_UPD_7606_WITHDRAW     = 11, +	BMP_STATS_PFX_7606_WITHDRAW     = 12, +	BMP_STATS_UPD_DUP               = 13, +	BMP_STATS_FRR_NH_INVALID        = 65531, +}; + +DECLARE_MGROUP(BMP) + +#endif /*_BGP_BMP_H_*/ diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index 4949519a9b..6c77f18f33 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -4466,7 +4466,8 @@ void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf,  					  struct ecommunity *ecomadd)  {  	/* uninstall routes from vrf */ -	uninstall_routes_for_vrf(bgp_vrf); +	if (is_l3vni_live(bgp_vrf)) +		uninstall_routes_for_vrf(bgp_vrf);  	/* Cleanup the RT to VRF mapping */  	bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf); @@ -4478,11 +4479,11 @@ void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf,  	listnode_add_sort(bgp_vrf->vrf_import_rtl, ecomadd);  	SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD); -	/* map VRF to its RTs */ -	bgp_evpn_map_vrf_to_its_rts(bgp_vrf); - -	/* install routes matching the new VRF */ -	install_routes_for_vrf(bgp_vrf); +	/* map VRF to its RTs and install routes matching the new RTs */ +	if (is_l3vni_live(bgp_vrf)) { +		bgp_evpn_map_vrf_to_its_rts(bgp_vrf); +		install_routes_for_vrf(bgp_vrf); +	}  }  void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf, @@ -4492,7 +4493,8 @@ void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf,  	struct ecommunity *ecom = NULL;  	/* uninstall routes from vrf */ -	uninstall_routes_for_vrf(bgp_vrf); +	if (is_l3vni_live(bgp_vrf)) +		uninstall_routes_for_vrf(bgp_vrf);  	/* Cleanup the RT to VRF mapping */  	bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf); @@ -4516,11 +4518,11 @@ void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf,  		evpn_auto_rt_import_add_for_vrf(bgp_vrf);  	} -	/* map VRFs to its RTs */ -	bgp_evpn_map_vrf_to_its_rts(bgp_vrf); - -	/* install routes matching this new RT */ -	install_routes_for_vrf(bgp_vrf); +	/* map VRFs to its RTs and install routes matching this new RT */ +	if (is_l3vni_live(bgp_vrf)) { +		bgp_evpn_map_vrf_to_its_rts(bgp_vrf); +		install_routes_for_vrf(bgp_vrf); +	}  }  void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf, diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index a5a091242f..f6bde2e9fa 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -275,6 +275,11 @@ static inline int is_vni_live(struct bgpevpn *vpn)  	return (CHECK_FLAG(vpn->flags, VNI_FLAG_LIVE));  } +static inline int is_l3vni_live(struct bgp *bgp_vrf) +{ +	return (bgp_vrf->l3vni && bgp_vrf->l3vni_svi_ifindex); +} +  static inline int is_rd_configured(struct bgpevpn *vpn)  {  	return (CHECK_FLAG(vpn->flags, VNI_FLAG_RD_CFGD)); diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index e6d81e54c4..30380f6add 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -3696,7 +3696,7 @@ DEFUN(show_bgp_l2vpn_evpn_es,   */  DEFUN(show_bgp_l2vpn_evpn_summary,        show_bgp_l2vpn_evpn_summary_cmd, -      "show bgp [vrf VRFNAME] l2vpn evpn summary [json]", +      "show bgp [vrf VRFNAME] l2vpn evpn summary [failed] [json]",        SHOW_STR        BGP_STR        "bgp vrf\n" @@ -3704,15 +3704,20 @@ DEFUN(show_bgp_l2vpn_evpn_summary,        L2VPN_HELP_STR        EVPN_HELP_STR        "Summary of BGP neighbor status\n" +      "Show only sessions not in Established state\n"        JSON_STR)  {  	int idx_vrf = 0;  	bool uj = use_json(argc, argv);  	char *vrf = NULL; +	bool show_failed = false;  	if (argv_find(argv, argc, "vrf", &idx_vrf))  		vrf = argv[++idx_vrf]->arg; -	return bgp_show_summary_vty(vty, vrf, AFI_L2VPN, SAFI_EVPN, uj); +	if (argv_find(argv, argc, "failed", &idx_vrf)) +		show_failed = true; +	return bgp_show_summary_vty(vty, vrf, AFI_L2VPN, SAFI_EVPN, +				    show_failed, uj);  }  /* diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index c9c6fc157e..cc7f81d9ff 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -179,9 +179,7 @@ static struct peer *peer_xfer_conn(struct peer *from_peer)  	 * on various buffers. Those need to be transferred or dropped,  	 * otherwise we'll get spurious failures during session establishment.  	 */ -	pthread_mutex_lock(&peer->io_mtx); -	pthread_mutex_lock(&from_peer->io_mtx); -	{ +	frr_with_mutex(&peer->io_mtx, &from_peer->io_mtx) {  		fd = peer->fd;  		peer->fd = from_peer->fd;  		from_peer->fd = fd; @@ -222,8 +220,6 @@ static struct peer *peer_xfer_conn(struct peer *from_peer)  		ringbuf_copy(peer->ibuf_work, from_peer->ibuf_work,  			     ringbuf_remain(from_peer->ibuf_work));  	} -	pthread_mutex_unlock(&from_peer->io_mtx); -	pthread_mutex_unlock(&peer->io_mtx);  	peer->as = from_peer->as;  	peer->v_holdtime = from_peer->v_holdtime; @@ -244,6 +240,7 @@ static struct peer *peer_xfer_conn(struct peer *from_peer)  	from_peer->last_event = last_evt;  	from_peer->last_major_event = last_maj_evt;  	peer->remote_id = from_peer->remote_id; +	peer->last_reset = from_peer->last_reset;  	if (from_peer->hostname != NULL) {  		if (peer->hostname) { @@ -551,7 +548,11 @@ const char *peer_down_str[] = {"",  			       "Intf peering v6only config change",  			       "BFD down received",  			       "Interface down", -			       "Neighbor address lost"}; +			       "Neighbor address lost", +			       "Waiting for NHT", +			       "Waiting for Peer IPv6 LLA", +			       "Waiting for VRF to be initialized", +			       "No AFI/SAFI activated for peer"};  static int bgp_graceful_restart_timer_expire(struct thread *thread)  { @@ -1162,8 +1163,7 @@ int bgp_stop(struct peer *peer)  	BGP_TIMER_OFF(peer->t_routeadv);  	/* Clear input and output buffer.  */ -	pthread_mutex_lock(&peer->io_mtx); -	{ +	frr_with_mutex(&peer->io_mtx) {  		if (peer->ibuf)  			stream_fifo_clean(peer->ibuf);  		if (peer->obuf) @@ -1179,7 +1179,6 @@ int bgp_stop(struct peer *peer)  			peer->curr = NULL;  		}  	} -	pthread_mutex_unlock(&peer->io_mtx);  	/* Close of file descriptor. */  	if (peer->fd >= 0) { @@ -1410,6 +1409,7 @@ int bgp_start(struct peer *peer)  			zlog_debug(  				"%s [FSM] Unable to get neighbor's IP address, waiting...",  				peer->host); +		peer->last_reset = PEER_DOWN_NBR_ADDR;  		return -1;  	} @@ -1455,6 +1455,7 @@ int bgp_start(struct peer *peer)  				EC_BGP_FSM,  				"%s [FSM] In a VRF that is not initialised yet",  				peer->host); +		peer->last_reset = PEER_DOWN_VRF_UNINIT;  		return -1;  	} @@ -1466,7 +1467,7 @@ int bgp_start(struct peer *peer)  			if (bgp_debug_neighbor_events(peer))  				zlog_debug("%s [FSM] Waiting for NHT",  					   peer->host); - +			peer->last_reset = PEER_DOWN_WAITING_NHT;  			BGP_EVENT_ADD(peer, TCP_connection_open_failed);  			return 0;  		} diff --git a/bgpd/bgp_io.c b/bgpd/bgp_io.c index f111822e53..c2d06a4b5a 100644 --- a/bgpd/bgp_io.c +++ b/bgpd/bgp_io.c @@ -132,12 +132,10 @@ static int bgp_process_writes(struct thread *thread)  	struct frr_pthread *fpt = bgp_pth_io; -	pthread_mutex_lock(&peer->io_mtx); -	{ +	frr_with_mutex(&peer->io_mtx) {  		status = bgp_write(peer);  		reschedule = (stream_fifo_head(peer->obuf) != NULL);  	} -	pthread_mutex_unlock(&peer->io_mtx);  	/* no problem */  	if (CHECK_FLAG(status, BGP_IO_TRANS_ERR)) { @@ -184,11 +182,9 @@ static int bgp_process_reads(struct thread *thread)  	struct frr_pthread *fpt = bgp_pth_io; -	pthread_mutex_lock(&peer->io_mtx); -	{ +	frr_with_mutex(&peer->io_mtx) {  		status = bgp_read(peer);  	} -	pthread_mutex_unlock(&peer->io_mtx);  	/* error checking phase */  	if (CHECK_FLAG(status, BGP_IO_TRANS_ERR)) { @@ -237,11 +233,9 @@ static int bgp_process_reads(struct thread *thread)  			assert(ringbuf_get(ibw, pktbuf, pktsize) == pktsize);  			stream_put(pkt, pktbuf, pktsize); -			pthread_mutex_lock(&peer->io_mtx); -			{ +			frr_with_mutex(&peer->io_mtx) {  				stream_fifo_push(peer->ibuf, pkt);  			} -			pthread_mutex_unlock(&peer->io_mtx);  			added_pkt = true;  		} else diff --git a/bgpd/bgp_keepalives.c b/bgpd/bgp_keepalives.c index bec3bdcb8d..6de1c216a6 100644 --- a/bgpd/bgp_keepalives.c +++ b/bgpd/bgp_keepalives.c @@ -245,8 +245,7 @@ void bgp_keepalives_on(struct peer *peer)  	 */  	assert(peerhash_mtx); -	pthread_mutex_lock(peerhash_mtx); -	{ +	frr_with_mutex(peerhash_mtx) {  		holder.peer = peer;  		if (!hash_lookup(peerhash, &holder)) {  			struct pkat *pkat = pkat_new(peer); @@ -255,7 +254,6 @@ void bgp_keepalives_on(struct peer *peer)  		}  		SET_FLAG(peer->thread_flags, PEER_THREAD_KEEPALIVES_ON);  	} -	pthread_mutex_unlock(peerhash_mtx);  	bgp_keepalives_wake();  } @@ -275,8 +273,7 @@ void bgp_keepalives_off(struct peer *peer)  	 */  	assert(peerhash_mtx); -	pthread_mutex_lock(peerhash_mtx); -	{ +	frr_with_mutex(peerhash_mtx) {  		holder.peer = peer;  		struct pkat *res = hash_release(peerhash, &holder);  		if (res) { @@ -285,16 +282,13 @@ void bgp_keepalives_off(struct peer *peer)  		}  		UNSET_FLAG(peer->thread_flags, PEER_THREAD_KEEPALIVES_ON);  	} -	pthread_mutex_unlock(peerhash_mtx);  }  void bgp_keepalives_wake(void)  { -	pthread_mutex_lock(peerhash_mtx); -	{ +	frr_with_mutex(peerhash_mtx) {  		pthread_cond_signal(peerhash_cond);  	} -	pthread_mutex_unlock(peerhash_mtx);  }  int bgp_keepalives_stop(struct frr_pthread *fpt, void **result) diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index 1dadf00e8f..887caee95e 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -122,7 +122,7 @@ static int bgp_md5_set_connect(int socket, union sockunion *su,  	int ret = -1;  #if HAVE_DECL_TCP_MD5SIG -	frr_elevate_privs(&bgpd_privs) { +	frr_with_privs(&bgpd_privs) {  		ret = bgp_md5_set_socket(socket, su, prefixlen, password);  	}  #endif /* HAVE_TCP_MD5SIG */ @@ -140,8 +140,7 @@ static int bgp_md5_set_password(struct peer *peer, const char *password)  	 * Set or unset the password on the listen socket(s). Outbound  	 * connections are taken care of in bgp_connect() below.  	 */ -	frr_elevate_privs(&bgpd_privs) -	{ +	frr_with_privs(&bgpd_privs) {  		for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, listener))  			if (listener->su.sa.sa_family  			    == peer->su.sa.sa_family) { @@ -167,8 +166,7 @@ int bgp_md5_set_prefix(struct prefix *p, const char *password)  	struct bgp_listener *listener;  	/* Set or unset the password on the listen socket(s). */ -	frr_elevate_privs(&bgpd_privs) -	{ +	frr_with_privs(&bgpd_privs) {  		for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, listener))  			if (listener->su.sa.sa_family == p->family) {  				prefix2sockunion(p, &su); @@ -610,7 +608,7 @@ int bgp_connect(struct peer *peer)  		zlog_debug("Peer address not learnt: Returning from connect");  		return 0;  	} -	frr_elevate_privs(&bgpd_privs) { +	frr_with_privs(&bgpd_privs) {  	/* Make socket for the peer. */  		peer->fd = vrf_sockunion_socket(&peer->su, peer->bgp->vrf_id,  						bgp_get_bound_name(peer)); @@ -630,7 +628,7 @@ int bgp_connect(struct peer *peer)  	sockopt_reuseport(peer->fd);  #ifdef IPTOS_PREC_INTERNETCONTROL -	frr_elevate_privs(&bgpd_privs) { +	frr_with_privs(&bgpd_privs) {  		if (sockunion_family(&peer->su) == AF_INET)  			setsockopt_ipv4_tos(peer->fd,  					    IPTOS_PREC_INTERNETCONTROL); @@ -708,7 +706,7 @@ static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen,  	sockopt_reuseaddr(sock);  	sockopt_reuseport(sock); -	frr_elevate_privs(&bgpd_privs) { +	frr_with_privs(&bgpd_privs) {  #ifdef IPTOS_PREC_INTERNETCONTROL  		if (sa->sa_family == AF_INET) @@ -767,7 +765,7 @@ int bgp_socket(struct bgp *bgp, unsigned short port, const char *address)  	snprintf(port_str, sizeof(port_str), "%d", port);  	port_str[sizeof(port_str) - 1] = '\0'; -	frr_elevate_privs(&bgpd_privs) { +	frr_with_privs(&bgpd_privs) {  		ret = vrf_getaddrinfo(address, port_str, &req, &ainfo_save,  				      bgp->vrf_id);  	} @@ -788,7 +786,7 @@ int bgp_socket(struct bgp *bgp, unsigned short port, const char *address)  		if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6)  			continue; -		frr_elevate_privs(&bgpd_privs) { +		frr_with_privs(&bgpd_privs) {  			sock = vrf_socket(ainfo->ai_family,  					  ainfo->ai_socktype,  					  ainfo->ai_protocol, bgp->vrf_id, diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index 64529f6ef3..7e5e07099d 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -503,7 +503,7 @@ static int bgp_capability_restart(struct peer *peer,  			if (bgp_debug_neighbor_events(peer))  				zlog_debug(  					"%s Address family %s is%spreserved", -					peer->host, afi_safi_print(afi, safi), +					peer->host, get_afi_safi_str(afi, safi, false),  					CHECK_FLAG(  						peer->af_cap[afi][safi],  						PEER_CAP_RESTART_AF_PRESERVE_RCV) diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 99522a6522..c7c1780c21 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -41,6 +41,7 @@  #include "bgpd/bgpd.h"  #include "bgpd/bgp_table.h"  #include "bgpd/bgp_dump.h" +#include "bgpd/bgp_bmp.h"  #include "bgpd/bgp_attr.h"  #include "bgpd/bgp_debug.h"  #include "bgpd/bgp_errors.h" @@ -123,9 +124,9 @@ int bgp_packet_set_size(struct stream *s)   */  static void bgp_packet_add(struct peer *peer, struct stream *s)  { -	pthread_mutex_lock(&peer->io_mtx); -	stream_fifo_push(peer->obuf, s); -	pthread_mutex_unlock(&peer->io_mtx); +	frr_with_mutex(&peer->io_mtx) { +		stream_fifo_push(peer->obuf, s); +	}  }  static struct stream *bgp_update_packet_eor(struct peer *peer, afi_t afi, @@ -140,7 +141,7 @@ static struct stream *bgp_update_packet_eor(struct peer *peer, afi_t afi,  	if (bgp_debug_neighbor_events(peer))  		zlog_debug("send End-of-RIB for %s to %s", -			   afi_safi_print(afi, safi), peer->host); +			   get_afi_safi_str(afi, safi, false), peer->host);  	s = stream_new(BGP_MAX_PACKET_SIZE); @@ -664,7 +665,7 @@ void bgp_notify_send_with_data(struct peer *peer, uint8_t code,  	struct stream *s;  	/* Lock I/O mutex to prevent other threads from pushing packets */ -	pthread_mutex_lock(&peer->io_mtx); +	frr_mutex_lock_autounlock(&peer->io_mtx);  	/* ============================================== */  	/* Allocate new stream. */ @@ -755,9 +756,6 @@ void bgp_notify_send_with_data(struct peer *peer, uint8_t code,  	stream_fifo_push(peer->obuf, s);  	bgp_write_notify(peer); - -	/* ============================================== */ -	pthread_mutex_unlock(&peer->io_mtx);  }  /* @@ -1660,7 +1658,7 @@ static int bgp_update_receive(struct peer *peer, bgp_size_t size)  				bgp_clear_stale_route(peer, afi, safi);  			zlog_info("%%NOTIFICATION: rcvd End-of-RIB for %s from %s in vrf %s", -				  afi_safi_print(afi, safi), peer->host, +				  get_afi_safi_str(afi, safi, false), peer->host,  				  vrf ? vrf->name : VRF_DEFAULT_NAME);  		}  	} @@ -2236,11 +2234,9 @@ int bgp_process_packet(struct thread *thread)  		bgp_size_t size;  		char notify_data_length[2]; -		pthread_mutex_lock(&peer->io_mtx); -		{ +		frr_with_mutex(&peer->io_mtx) {  			peer->curr = stream_fifo_pop(peer->ibuf);  		} -		pthread_mutex_unlock(&peer->io_mtx);  		if (peer->curr == NULL) // no packets to process, hmm...  			return 0; @@ -2359,15 +2355,13 @@ int bgp_process_packet(struct thread *thread)  	if (fsm_update_result != FSM_PEER_TRANSFERRED  	    && fsm_update_result != FSM_PEER_STOPPED) { -		pthread_mutex_lock(&peer->io_mtx); -		{ +		frr_with_mutex(&peer->io_mtx) {  			// more work to do, come back later  			if (peer->ibuf->count > 0)  				thread_add_timer_msec(  					bm->master, bgp_process_packet, peer, 0,  					&peer->t_process_packet);  		} -		pthread_mutex_unlock(&peer->io_mtx);  	}  	return 0; diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 183debddba..32c9fb16f3 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -2750,7 +2750,7 @@ int bgp_maximum_prefix_overflow(struct peer *peer, afi_t afi, safi_t safi,  		zlog_info(  			"%%MAXPFXEXCEED: No. of %s prefix received from %s %ld exceed, "  			"limit %ld", -			afi_safi_print(afi, safi), peer->host, +			get_afi_safi_str(afi, safi, false), peer->host,  			peer->pcount[afi][safi], peer->pmax[afi][safi]);  		SET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_LIMIT); @@ -2811,7 +2811,7 @@ int bgp_maximum_prefix_overflow(struct peer *peer, afi_t afi, safi_t safi,  		zlog_info(  			"%%MAXPFX: No. of %s prefix received from %s reaches %ld, max %ld", -			afi_safi_print(afi, safi), peer->host, +			get_afi_safi_str(afi, safi, false), peer->host,  			peer->pcount[afi][safi], peer->pmax[afi][safi]);  		SET_FLAG(peer->af_sflags[afi][safi],  			 PEER_STATUS_PREFIX_THRESHOLD); @@ -10696,7 +10696,7 @@ static int bgp_table_stats(struct vty *vty, struct bgp *bgp, afi_t afi,  		return CMD_WARNING;  	} -	vty_out(vty, "BGP %s RIB statistics\n", afi_safi_print(afi, safi)); +	vty_out(vty, "BGP %s RIB statistics\n", get_afi_safi_str(afi, safi, false));  	/* labeled-unicast routes live in the unicast table */  	if (safi == SAFI_LABELED_UNICAST) @@ -10895,7 +10895,7 @@ static int bgp_peer_counts(struct vty *vty, struct peer *peer, afi_t afi,  	if (use_json) {  		json_object_string_add(json, "prefixCountsFor", peer->host);  		json_object_string_add(json, "multiProtocol", -				       afi_safi_print(afi, safi)); +				       get_afi_safi_str(afi, safi, true));  		json_object_int_add(json, "pfxCounter",  				    peer->pcount[afi][safi]); @@ -10921,10 +10921,10 @@ static int bgp_peer_counts(struct vty *vty, struct peer *peer, afi_t afi,  		    && bgp_flag_check(peer->bgp, BGP_FLAG_SHOW_HOSTNAME)) {  			vty_out(vty, "Prefix counts for %s/%s, %s\n",  				peer->hostname, peer->host, -				afi_safi_print(afi, safi)); +				get_afi_safi_str(afi, safi, false));  		} else {  			vty_out(vty, "Prefix counts for %s, %s\n", peer->host, -				afi_safi_print(afi, safi)); +				get_afi_safi_str(afi, safi, false));  		}  		vty_out(vty, "PfxCt: %ld\n", peer->pcount[afi][safi]); @@ -11552,7 +11552,7 @@ DEFUN (show_ip_bgp_neighbor_received_prefix_filter,  	if (count) {  		if (!uj)  			vty_out(vty, "Address Family: %s\n", -				afi_safi_print(afi, safi)); +				get_afi_safi_str(afi, safi, false));  		prefix_bgp_show_prefix_list(vty, afi, name, uj);  	} else {  		if (uj) diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index aaae9e380e..545ca19762 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -3057,7 +3057,7 @@ static int bgp_route_match_add(struct vty *vty, const char *command,  {  	VTY_DECLVAR_CONTEXT(route_map_index, index);  	int retval = CMD_SUCCESS; -	int ret; +	enum rmap_compile_rets ret;  	ret = route_map_add_match(index, command, arg, type);  	switch (ret) { @@ -3074,6 +3074,11 @@ static int bgp_route_match_add(struct vty *vty, const char *command,  			route_map_upd8_dependency(type, arg, index->map->name);  		}  		break; +	case RMAP_DUPLICATE_RULE: +		/* +		 * Intentionally doing nothing here. +		 */ +		break;  	}  	return retval; @@ -3084,7 +3089,7 @@ static int bgp_route_match_delete(struct vty *vty, const char *command,  				  const char *arg, route_map_event_t type)  {  	VTY_DECLVAR_CONTEXT(route_map_index, index); -	int ret; +	enum rmap_compile_rets ret;  	int retval = CMD_SUCCESS;  	char *dep_name = NULL;  	const char *tmpstr; @@ -3117,6 +3122,11 @@ static int bgp_route_match_delete(struct vty *vty, const char *command,  		if (type != RMAP_EVENT_MATCH_DELETED && dep_name)  			route_map_upd8_dependency(type, dep_name, rmap_name);  		break; +	case RMAP_DUPLICATE_RULE: +		/* +		 * Nothing to do here +		 */ +		break;  	}  	XFREE(MTYPE_ROUTE_MAP_RULE, dep_name); diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c index 22840d54c6..2cfd65896c 100644 --- a/bgpd/bgp_rpki.c +++ b/bgpd/bgp_rpki.c @@ -51,12 +51,6 @@  #include "lib/thread.h"  #ifndef VTYSH_EXTRACT_PL  #include "rtrlib/rtrlib.h" -#include "rtrlib/rtr_mgr.h" -#include "rtrlib/lib/ip.h" -#include "rtrlib/transport/tcp/tcp_transport.h" -#if defined(FOUND_SSH) -#include "rtrlib/transport/ssh/ssh_transport.h" -#endif  #endif  #include "hook.h"  #include "libfrr.h" @@ -76,8 +70,6 @@ DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE_GROUP, "BGP RPKI Cache server group")  #define POLLING_PERIOD_DEFAULT 3600  #define EXPIRE_INTERVAL_DEFAULT 7200  #define RETRY_INTERVAL_DEFAULT 600 -#define TIMEOUT_DEFAULT 600 -#define INITIAL_SYNCHRONISATION_TIMEOUT_DEFAULT 30  #define RPKI_DEBUG(...)                                                        \  	if (rpki_debug) {                                                      \ @@ -147,8 +139,6 @@ static int rpki_debug;  static unsigned int polling_period;  static unsigned int expire_interval;  static unsigned int retry_interval; -static unsigned int timeout; -static unsigned int initial_synchronisation_timeout;  static int rpki_sync_socket_rtr;  static int rpki_sync_socket_bgpd; @@ -538,9 +528,6 @@ static int bgp_rpki_init(struct thread_master *master)  	polling_period = POLLING_PERIOD_DEFAULT;  	expire_interval = EXPIRE_INTERVAL_DEFAULT;  	retry_interval = RETRY_INTERVAL_DEFAULT; -	timeout = TIMEOUT_DEFAULT; -	initial_synchronisation_timeout = -		INITIAL_SYNCHRONISATION_TIMEOUT_DEFAULT;  	install_cli_commands();  	rpki_init_sync_socket();  	return 0; @@ -756,8 +743,6 @@ static int add_cache(struct cache *cache)  	group.sockets_len = 1;  	group.sockets = &cache->rtr_socket; -	listnode_add(cache_list, cache); -  	if (rtr_is_running) {  		init_tr_socket(cache); @@ -767,6 +752,8 @@ static int add_cache(struct cache *cache)  		}  	} +	listnode_add(cache_list, cache); +  	return SUCCESS;  } @@ -793,7 +780,12 @@ static int add_tcp_cache(const char *host, const char *port,  	cache->rtr_socket = rtr_socket;  	cache->preference = preference; -	return add_cache(cache); +	int ret = add_cache(cache); +	if (ret != SUCCESS) { +		free_cache(cache); +	} + +	return ret;  }  #if defined(FOUND_SSH) @@ -829,7 +821,12 @@ static int add_ssh_cache(const char *host, const unsigned int port,  	cache->rtr_socket = rtr_socket;  	cache->preference = preference; -	return add_cache(cache); +	int ret = add_cache(cache); +	if (ret != SUCCESS) { +		free_cache(cache); +	} + +	return ret;  }  #endif @@ -869,9 +866,6 @@ static int config_write(struct vty *vty)  		vty_out(vty, "!\n");  		vty_out(vty, "rpki\n");  		vty_out(vty, "  rpki polling_period %d\n", polling_period); -		vty_out(vty, "  rpki timeout %d\n", timeout); -		vty_out(vty, "  rpki initial-synchronisation-timeout %d\n", -			initial_synchronisation_timeout);  		for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {  			switch (cache->type) {  				struct tr_tcp_config *tcp_config; @@ -1020,48 +1014,64 @@ DEFUN (no_rpki_retry_interval,  	return CMD_SUCCESS;  } -DEFPY (rpki_timeout, +#if (CONFDATE > 20200901) +CPP_NOTICE("bgpd: time to remove rpki timeout") +CPP_NOTICE("bgpd: this includes rpki_timeout and rpki_synchronisation_timeout") +#endif + +DEFPY_HIDDEN (rpki_timeout,         rpki_timeout_cmd,         "rpki timeout (1-4294967295)$to_arg",         RPKI_OUTPUT_STRING         "Set timeout\n"         "Timeout value\n")  { -	timeout = to_arg; +	vty_out(vty, +		"This config option is deprecated, and is scheduled for removal.\n"); +	vty_out(vty, +		"This functionality has also already been removed because it caused bugs and was pointless\n");  	return CMD_SUCCESS;  } -DEFUN (no_rpki_timeout, +DEFUN_HIDDEN (no_rpki_timeout,         no_rpki_timeout_cmd,         "no rpki timeout",         NO_STR         RPKI_OUTPUT_STRING         "Set timeout back to default\n")  { -	timeout = TIMEOUT_DEFAULT; +	vty_out(vty, +		"This config option is deprecated, and is scheduled for removal.\n"); +	vty_out(vty, +		"This functionality has also already been removed because it caused bugs and was pointless\n");  	return CMD_SUCCESS;  } -DEFPY (rpki_synchronisation_timeout, +DEFPY_HIDDEN (rpki_synchronisation_timeout,         rpki_synchronisation_timeout_cmd,         "rpki initial-synchronisation-timeout (1-4294967295)$ito_arg",         RPKI_OUTPUT_STRING         "Set a timeout for the initial synchronisation of prefix validation data\n"         "Timeout value\n")  { -	initial_synchronisation_timeout = ito_arg; +	vty_out(vty, +		"This config option is deprecated, and is scheduled for removal.\n"); +	vty_out(vty, +		"This functionality has also already been removed because it caused bugs and was pointless\n");  	return CMD_SUCCESS;  } -DEFUN (no_rpki_synchronisation_timeout, +DEFUN_HIDDEN (no_rpki_synchronisation_timeout,         no_rpki_synchronisation_timeout_cmd,         "no rpki initial-synchronisation-timeout",         NO_STR         RPKI_OUTPUT_STRING         "Set the initial synchronisation timeout back to default (30 sec.)\n")  { -	initial_synchronisation_timeout = -		INITIAL_SYNCHRONISATION_TIMEOUT_DEFAULT; +	vty_out(vty, +		"This config option is deprecated, and is scheduled for removal.\n"); +	vty_out(vty, +		"This functionality has also already been removed because it caused bugs and was pointless\n");  	return CMD_SUCCESS;  } @@ -1083,6 +1093,18 @@ DEFPY (rpki_cache,         "Preference value\n")  {  	int return_value; +	struct listnode *cache_node; +	struct cache *current_cache; + +	for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, current_cache)) { +		if (current_cache->preference == preference) { +			vty_out(vty, +				"Cache with preference %ld is already configured\n", +				preference); +			return CMD_WARNING; +		} +	} +  	// use ssh connection  	if (ssh_uname) { @@ -1128,11 +1150,11 @@ DEFPY (no_rpki_cache,  		return CMD_WARNING;  	} -	if (rtr_is_running) { +	if (rtr_is_running && listcount(cache_list) == 1) { +		stop(); +	} else if (rtr_is_running) {  		if (rtr_mgr_remove_group(rtr_config, preference) == RTR_ERROR) {  			vty_out(vty, "Could not remove cache %ld", preference); -			if (listcount(cache_list) == 1) -				vty_out(vty, " because it is the last cache");  			vty_out(vty, "\n");  			return CMD_WARNING; @@ -1385,7 +1407,7 @@ DEFUN (match_rpki,         "Prefix not found\n")  {  	VTY_DECLVAR_CONTEXT(route_map_index, index); -	int ret; +	enum rmap_compile_rets ret;  	ret = route_map_add_match(index, "rpki", argv[2]->arg,  				  RMAP_EVENT_MATCH_ADDED); @@ -1397,6 +1419,12 @@ DEFUN (match_rpki,  		case RMAP_COMPILE_ERROR:  			vty_out(vty, "%% BGP Argument is malformed.\n");  			return CMD_WARNING_CONFIG_FAILED; +		case RMAP_COMPILE_SUCCESS: +		case RMAP_DUPLICATE_RULE: +			/* +			 * Intentionally doing nothing here +			 */ +			break;  		}  	}  	return CMD_SUCCESS; @@ -1413,7 +1441,7 @@ DEFUN (no_match_rpki,         "Prefix not found\n")  {  	VTY_DECLVAR_CONTEXT(route_map_index, index); -	int ret; +	enum rmap_compile_rets ret;  	ret = route_map_delete_match(index, "rpki", argv[3]->arg);  	if (ret) { @@ -1424,6 +1452,12 @@ DEFUN (no_match_rpki,  		case RMAP_COMPILE_ERROR:  			vty_out(vty, "%% BGP Argument is malformed.\n");  			break; +		case RMAP_COMPILE_SUCCESS: +		case RMAP_DUPLICATE_RULE: +			/* +			 * Nothing to do here +			 */ +			break;  		}  		return CMD_WARNING_CONFIG_FAILED;  	} diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 0d0e433980..17bc83ed2e 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -22,6 +22,7 @@  #include "command.h"  #include "lib/json.h" +#include "lib_errors.h"  #include "lib/zclient.h"  #include "prefix.h"  #include "plist.h" @@ -130,6 +131,80 @@ static enum node_type bgp_node_type(afi_t afi, safi_t safi)  	return BGP_IPV4_NODE;  } +static const char *get_afi_safi_vty_str(afi_t afi, safi_t safi) +{ +	if (afi == AFI_IP && safi == SAFI_UNICAST) +		return "IPv4 Unicast"; +	else if (afi == AFI_IP && safi == SAFI_MULTICAST) +		return "IPv4 Multicast"; +	else if (afi == AFI_IP && safi == SAFI_LABELED_UNICAST) +		return "IPv4 Labeled Unicast"; +	else if (afi == AFI_IP && safi == SAFI_MPLS_VPN) +		return "IPv4 VPN"; +	else if (afi == AFI_IP && safi == SAFI_ENCAP) +		return "IPv4 Encap"; +	else if (afi == AFI_IP && safi == SAFI_FLOWSPEC) +		return "IPv4 Flowspec"; +	else if (afi == AFI_IP6 && safi == SAFI_UNICAST) +		return "IPv6 Unicast"; +	else if (afi == AFI_IP6 && safi == SAFI_MULTICAST) +		return "IPv6 Multicast"; +	else if (afi == AFI_IP6 && safi == SAFI_LABELED_UNICAST) +		return "IPv6 Labeled Unicast"; +	else if (afi == AFI_IP6 && safi == SAFI_MPLS_VPN) +		return "IPv6 VPN"; +	else if (afi == AFI_IP6 && safi == SAFI_ENCAP) +		return "IPv6 Encap"; +	else if (afi == AFI_IP6 && safi == SAFI_FLOWSPEC) +		return "IPv6 Flowspec"; +	else if (afi == AFI_L2VPN && safi == SAFI_EVPN) +		return "L2VPN EVPN"; +	else { +		flog_err(EC_LIB_DEVELOPMENT, "New afi/safi that needs to be taken care of?"); +		return "Unknown"; +	} +} + +/* + * Please note that we have intentionally camelCased + * the return strings here.  So if you want + * to use this function, please ensure you + * are doing this within json output + */ +static const char *get_afi_safi_json_str(afi_t afi, safi_t safi) +{ +	if (afi == AFI_IP && safi == SAFI_UNICAST) +		return "ipv4Unicast"; +	else if (afi == AFI_IP && safi == SAFI_MULTICAST) +		return "ipv4Multicast"; +	else if (afi == AFI_IP && safi == SAFI_LABELED_UNICAST) +		return "ipv4LabeledUnicast"; +	else if (afi == AFI_IP && safi == SAFI_MPLS_VPN) +		return "ipv4Vpn"; +	else if (afi == AFI_IP && safi == SAFI_ENCAP) +		return "ipv4Encap"; +	else if (afi == AFI_IP && safi == SAFI_FLOWSPEC) +		return "ipv4Flowspec"; +	else if (afi == AFI_IP6 && safi == SAFI_UNICAST) +		return "ipv6Unicast"; +	else if (afi == AFI_IP6 && safi == SAFI_MULTICAST) +		return "ipv6Multicast"; +	else if (afi == AFI_IP6 && safi == SAFI_LABELED_UNICAST) +		return "ipv6LabeledUnicast"; +	else if (afi == AFI_IP6 && safi == SAFI_MPLS_VPN) +		return "ipv6Vpn"; +	else if (afi == AFI_IP6 && safi == SAFI_ENCAP) +		return "ipv6Encap"; +	else if (afi == AFI_IP6 && safi == SAFI_FLOWSPEC) +		return "ipv6Flowspec"; +	else if (afi == AFI_L2VPN && safi == SAFI_EVPN) +		return "l2VpnEvpn"; +	else { +		flog_err(EC_LIB_DEVELOPMENT, "New afi/safi that needs to be taken care of?"); +		return "Unknown"; +	} +} +  /* Utility function to get address family from current node.  */  afi_t bgp_node_afi(struct vty *vty)  { @@ -584,7 +659,7 @@ static void bgp_clear_vty_error(struct vty *vty, struct peer *peer, afi_t afi,  	case BGP_ERR_AF_UNCONFIGURED:  		vty_out(vty,  			"%%BGP: Enable %s address family for the neighbor %s\n", -			afi_safi_print(afi, safi), peer->host); +			get_afi_safi_str(afi, safi, false), peer->host);  		break;  	case BGP_ERR_SOFT_RECONFIG_UNCONFIGURED:  		vty_out(vty, @@ -738,7 +813,7 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,  		if (!found)  			vty_out(vty,  				"%%BGP: No %s peer belonging to peer-group %s is configured\n", -				afi_safi_print(afi, safi), arg); +				get_afi_safi_str(afi, safi, false), arg);  		return CMD_SUCCESS;  	} @@ -760,7 +835,7 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,  		if (!found)  			vty_out(vty,  				"%%BGP: No external %s peer is configured\n", -				afi_safi_print(afi, safi)); +				get_afi_safi_str(afi, safi, false));  		return CMD_SUCCESS;  	} @@ -784,7 +859,7 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,  		if (!found)  			vty_out(vty,  				"%%BGP: No %s peer is configured with AS %s\n", -				afi_safi_print(afi, safi), arg); +				get_afi_safi_str(afi, safi, false), arg);  		return CMD_SUCCESS;  	} @@ -7794,9 +7869,145 @@ static void bgp_show_bestpath_json(struct bgp *bgp, json_object *json)  	json_object_object_add(json, "bestPath", bestpath);  } +/* Print the error code/subcode for why the peer is down */ +static void bgp_show_peer_reset(struct vty * vty, struct peer *peer, +				json_object *json_peer, bool use_json) +{ +	const char *code_str; +	const char *subcode_str; + +	if (use_json) { +		if (peer->last_reset == PEER_DOWN_NOTIFY_SEND +		    || peer->last_reset == PEER_DOWN_NOTIFY_RECEIVED) { +			char errorcodesubcode_hexstr[5]; +			char errorcodesubcode_str[256]; + +			code_str = bgp_notify_code_str(peer->notify.code); +			subcode_str = bgp_notify_subcode_str( +					 peer->notify.code, +					 peer->notify.subcode); + +			sprintf(errorcodesubcode_hexstr, "%02X%02X", +				peer->notify.code, peer->notify.subcode); +			json_object_string_add(json_peer, +					       "lastErrorCodeSubcode", +					       errorcodesubcode_hexstr); +			snprintf(errorcodesubcode_str, 255, "%s%s", +				 code_str, subcode_str); +			json_object_string_add(json_peer, +					       "lastNotificationReason", +					       errorcodesubcode_str); +			if (peer->last_reset == PEER_DOWN_NOTIFY_RECEIVED +			    && peer->notify.code == BGP_NOTIFY_CEASE +			    && (peer->notify.subcode +				== BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN +				|| peer->notify.subcode +				== BGP_NOTIFY_CEASE_ADMIN_RESET) +			    && peer->notify.length) { +				char msgbuf[1024]; +				const char *msg_str; + +				msg_str = bgp_notify_admin_message( +					     msgbuf, sizeof(msgbuf), +					     (uint8_t *)peer->notify.data, +					     peer->notify.length); +				if (msg_str) +					json_object_string_add( +					   json_peer, +					   "lastShutdownDescription", +					   msg_str); +			} + +		}  +		json_object_string_add(json_peer, "lastResetDueTo", +				       peer_down_str[(int)peer->last_reset]); +		json_object_int_add(json_peer, "lastResetCode", +				    peer->last_reset); +	} else { +		if (peer->last_reset == PEER_DOWN_NOTIFY_SEND +		    || peer->last_reset == PEER_DOWN_NOTIFY_RECEIVED) { +			code_str = bgp_notify_code_str(peer->notify.code); +			subcode_str = +				bgp_notify_subcode_str(peer->notify.code, +						       peer->notify.subcode); +			vty_out(vty, "  Notification %s (%s%s)\n", +				peer->last_reset == PEER_DOWN_NOTIFY_SEND +				? "sent" +				: "received", +				code_str, subcode_str); +		} else { +			vty_out(vty, "  %s\n", +				peer_down_str[(int)peer->last_reset]); +		} +	} +} + +static inline bool bgp_has_peer_failed(struct peer *peer, afi_t afi, +				       safi_t safi) +{ +	return ((peer->status != Established) || +		!peer->afc_recv[afi][safi]); +} + +static void bgp_show_failed_summary(struct vty *vty, struct bgp *bgp, +				    struct peer *peer, json_object *json_peer, +				    int max_neighbor_width, bool use_json) +{ +	char timebuf[BGP_UPTIME_LEN], dn_flag[2]; +	int len; + +	if (use_json) { +		if (peer_dynamic_neighbor(peer)) +			json_object_boolean_true_add(json_peer, +						     "dynamicPeer"); +		if (peer->hostname) +			json_object_string_add(json_peer, "hostname", +					       peer->hostname); + +		if (peer->domainname) +			json_object_string_add(json_peer, "domainname", +					       peer->domainname); +		json_object_int_add(json_peer, "connectionsEstablished", +				    peer->established); +		json_object_int_add(json_peer, "connectionsDropped", +				    peer->dropped); +		peer_uptime(peer->uptime, timebuf, BGP_UPTIME_LEN, +			    use_json, json_peer); +		if (peer->status == Established) +			json_object_string_add(json_peer, "lastResetDueTo", +					       "AFI/SAFI Not Negotiated"); +		else +			bgp_show_peer_reset(NULL, peer, json_peer, true); +	} else { +		dn_flag[1] = '\0'; +		dn_flag[0] = peer_dynamic_neighbor(peer) ? '*' : '\0'; +		if (peer->hostname +		    && bgp_flag_check(bgp, BGP_FLAG_SHOW_HOSTNAME)) +			len = vty_out(vty, "%s%s(%s)", dn_flag, +				      peer->hostname, peer->host); +		else +			len = vty_out(vty, "%s%s", dn_flag, peer->host); + +		/* pad the neighbor column with spaces */ +		if (len < max_neighbor_width) +			vty_out(vty, "%*s", max_neighbor_width - len, +				" "); +		vty_out(vty, "%7d %7d %8s", peer->established, +			peer->dropped, +			peer_uptime(peer->uptime, timebuf, +				    BGP_UPTIME_LEN, 0, NULL)); +		if (peer->status == Established) +			vty_out(vty, "  AFI/SAFI Not Negotiated\n"); +		else +			bgp_show_peer_reset(vty, peer, NULL, +					    false); +	} +} +				  +  /* Show BGP peer's summary information. */  static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi, -			    bool use_json) +			    bool show_failed, bool use_json)  {  	struct peer *peer;  	struct listnode *node, *nnode; @@ -7804,7 +8015,7 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi,  	char timebuf[BGP_UPTIME_LEN], dn_flag[2];  	char neighbor_buf[VTY_BUFSIZ];  	int neighbor_col_default_width = 16; -	int len; +	int len, failed_count = 0;  	int max_neighbor_width = 0;  	int pfx_rcd_safi;  	json_object *json = NULL; @@ -7816,6 +8027,7 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi,  	 * to  	 * display the correct PfxRcd value we must look at SAFI_UNICAST  	 */ +  	if (safi == SAFI_LABELED_UNICAST)  		pfx_rcd_safi = SAFI_UNICAST;  	else @@ -7824,6 +8036,20 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi,  	if (use_json) {  		json = json_object_new_object();  		json_peers = json_object_new_object(); +		for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) { +			if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) +				continue; + +			if (peer->afc[afi][safi]) { +				/* See if we have at least a single failed peer */ +				if (bgp_has_peer_failed(peer, afi, safi)) +					failed_count++; +				count++; +			} +			if (peer_dynamic_neighbor(peer)) +				dn_count++; +		} +				  	} else {  		/* Loop over all neighbors that will be displayed to determine  		 * how many @@ -7852,6 +8078,11 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi,  				if (len > max_neighbor_width)  					max_neighbor_width = len; +				 +				/* See if we have at least a single failed peer */ +				if (bgp_has_peer_failed(peer, afi, safi)) +					failed_count++; +				count++;  			}  		} @@ -7862,6 +8093,23 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi,  			max_neighbor_width = neighbor_col_default_width;  	} +	if (show_failed && !failed_count) { +		if (use_json) { +			json_object_int_add(json, "failedPeersCount", 0); +			json_object_int_add(json, "dynamicPeers", dn_count); +			json_object_int_add(json, "totalPeers", count);			 + +			vty_out(vty, "%s\n", json_object_to_json_string_ext( +					     json, JSON_C_TO_STRING_PRETTY)); +			json_object_free(json); +		} else { +			vty_out(vty, "%% No failed BGP neighbors found\n"); +			vty_out(vty, "\nTotal number of neighbors %d\n", count); +		} +		return CMD_SUCCESS; +	} +		 +	count = 0;		/* Reset the value as its used again */  	for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {  		if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))  			continue; @@ -8063,78 +8311,97 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi,  				vty_out(vty, "Neighbor");  				vty_out(vty, "%*s", max_neighbor_width - 8,  					" "); -				vty_out(vty, +				if (show_failed) +					vty_out(vty, "EstdCnt DropCnt ResetTime Reason\n"); +				else +					vty_out(vty,  					"V         AS MsgRcvd MsgSent   TblVer  InQ OutQ  Up/Down State/PfxRcd\n");  			}  		}  		count++; +		/* Works for both failed & successful cases */ +		if (peer_dynamic_neighbor(peer)) +			dn_count++;  		if (use_json) { -			json_peer = json_object_new_object(); +			json_peer = NULL; + +			if (show_failed && +			    bgp_has_peer_failed(peer, afi, safi)) { +				json_peer = json_object_new_object(); +				bgp_show_failed_summary(vty, bgp, peer, +							json_peer, 0, use_json); +			} else if (!show_failed) { +				json_peer = json_object_new_object(); +				if (peer_dynamic_neighbor(peer)) { +					json_object_boolean_true_add(json_peer, +								     "dynamicPeer"); +				} -			if (peer_dynamic_neighbor(peer)) { -				dn_count++; -				json_object_boolean_true_add(json_peer, -							     "dynamicPeer"); +				if (peer->hostname) +					json_object_string_add(json_peer, "hostname", +							       peer->hostname); + +				if (peer->domainname) +					json_object_string_add(json_peer, "domainname", +							       peer->domainname); + +				json_object_int_add(json_peer, "remoteAs", peer->as); +				json_object_int_add(json_peer, "version", 4); +				json_object_int_add(json_peer, "msgRcvd", +						    PEER_TOTAL_RX(peer)); +				json_object_int_add(json_peer, "msgSent", +						    PEER_TOTAL_TX(peer)); + +				json_object_int_add(json_peer, "tableVersion", +						    peer->version[afi][safi]); +				json_object_int_add(json_peer, "outq", +						    peer->obuf->count); +				json_object_int_add(json_peer, "inq", 0); +				peer_uptime(peer->uptime, timebuf, BGP_UPTIME_LEN, +					    use_json, json_peer); + +				/* +				 * Adding "pfxRcd" field to match with the corresponding +				 * CLI. "prefixReceivedCount" will be deprecated in +				 * future. +				 */ +				json_object_int_add(json_peer, "prefixReceivedCount", +						    peer->pcount[afi][pfx_rcd_safi]); +				json_object_int_add(json_peer, "pfxRcd", +						    peer->pcount[afi][pfx_rcd_safi]); + +				paf = peer_af_find(peer, afi, pfx_rcd_safi); +				if (paf && PAF_SUBGRP(paf)) +					json_object_int_add(json_peer, +							    "pfxSnt", +							    (PAF_SUBGRP(paf))->scount); +				if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)) +					json_object_string_add(json_peer, "state", +							       "Idle (Admin)"); +				else if (peer->afc_recv[afi][safi]) +					json_object_string_add( +							       json_peer, "state", +							       lookup_msg(bgp_status_msg, peer->status, +									  NULL)); +				else if (CHECK_FLAG(peer->sflags, +						    PEER_STATUS_PREFIX_OVERFLOW)) +					json_object_string_add(json_peer, "state", +							       "Idle (PfxCt)"); +				else +					json_object_string_add( +							       json_peer, "state", +							       lookup_msg(bgp_status_msg, peer->status, +									  NULL)); +				json_object_int_add(json_peer, "connectionsEstablished", +						    peer->established); +				json_object_int_add(json_peer, "connectionsDropped", +						    peer->dropped);  			} - -			if (peer->hostname) -				json_object_string_add(json_peer, "hostname", -						       peer->hostname); - -			if (peer->domainname) -				json_object_string_add(json_peer, "domainname", -						       peer->domainname); - -			json_object_int_add(json_peer, "remoteAs", peer->as); -			json_object_int_add(json_peer, "version", 4); -			json_object_int_add(json_peer, "msgRcvd", -					    PEER_TOTAL_RX(peer)); -			json_object_int_add(json_peer, "msgSent", -					    PEER_TOTAL_TX(peer)); - -			json_object_int_add(json_peer, "tableVersion", -					    peer->version[afi][safi]); -			json_object_int_add(json_peer, "outq", -					    peer->obuf->count); -			json_object_int_add(json_peer, "inq", 0); -			peer_uptime(peer->uptime, timebuf, BGP_UPTIME_LEN, -				    use_json, json_peer); - -			/* -			 * Adding "pfxRcd" field to match with the corresponding -			 * CLI. "prefixReceivedCount" will be deprecated in -			 * future. -			 */ -			json_object_int_add(json_peer, "prefixReceivedCount", -					    peer->pcount[afi][pfx_rcd_safi]); -			json_object_int_add(json_peer, "pfxRcd", -					peer->pcount[afi][pfx_rcd_safi]); - -			paf = peer_af_find(peer, afi, pfx_rcd_safi); -			if (paf && PAF_SUBGRP(paf)) -				json_object_int_add(json_peer, -						"pfxSnt", -						(PAF_SUBGRP(paf))->scount); - -			if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)) -				json_object_string_add(json_peer, "state", -						       "Idle (Admin)"); -			else if (peer->afc_recv[afi][safi]) -				json_object_string_add( -					json_peer, "state", -					lookup_msg(bgp_status_msg, peer->status, -						   NULL)); -			else if (CHECK_FLAG(peer->sflags, -					    PEER_STATUS_PREFIX_OVERFLOW)) -				json_object_string_add(json_peer, "state", -						       "Idle (PfxCt)"); -			else -				json_object_string_add( -					json_peer, "state", -					lookup_msg(bgp_status_msg, peer->status, -						   NULL)); +			/* Avoid creating empty peer dicts in JSON */ +			if (json_peer == NULL) +				continue;  			if (peer->conf_if)  				json_object_string_add(json_peer, "idType", @@ -8145,65 +8412,72 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi,  			else if (peer->su.sa.sa_family == AF_INET6)  				json_object_string_add(json_peer, "idType",  						       "ipv6"); -  			json_object_object_add(json_peers, peer->host,  					       json_peer);  		} else { -			memset(dn_flag, '\0', sizeof(dn_flag)); -			if (peer_dynamic_neighbor(peer)) { -				dn_count++; -				dn_flag[0] = '*'; -			} - -			if (peer->hostname -			    && bgp_flag_check(bgp, BGP_FLAG_SHOW_HOSTNAME)) -				len = vty_out(vty, "%s%s(%s)", dn_flag, -					      peer->hostname, peer->host); -			else -				len = vty_out(vty, "%s%s", dn_flag, peer->host); - -			/* pad the neighbor column with spaces */ -			if (len < max_neighbor_width) -				vty_out(vty, "%*s", max_neighbor_width - len, -					" "); - -			vty_out(vty, "4 %10u %7u %7u %8" PRIu64 " %4d %4zd %8s", -				peer->as, PEER_TOTAL_RX(peer), -				PEER_TOTAL_TX(peer), peer->version[afi][safi], -				0, peer->obuf->count, -				peer_uptime(peer->uptime, timebuf, -					    BGP_UPTIME_LEN, 0, NULL)); +			if (show_failed && +			    bgp_has_peer_failed(peer, afi, safi)) { +				bgp_show_failed_summary(vty, bgp, peer, NULL, +							max_neighbor_width, +							use_json); +			} else if (!show_failed) { +				memset(dn_flag, '\0', sizeof(dn_flag)); +				if (peer_dynamic_neighbor(peer)) { +					dn_flag[0] = '*'; +				} -			if (peer->status == Established) -				if (peer->afc_recv[afi][safi]) -					vty_out(vty, " %12ld", -						peer->pcount[afi] -							    [pfx_rcd_safi]); -				else -					vty_out(vty, " NoNeg"); -			else { -				if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)) -					vty_out(vty, " Idle (Admin)"); -				else if (CHECK_FLAG( -						 peer->sflags, -						 PEER_STATUS_PREFIX_OVERFLOW)) -					vty_out(vty, " Idle (PfxCt)"); +				if (peer->hostname +				    && bgp_flag_check(bgp, BGP_FLAG_SHOW_HOSTNAME)) +					len = vty_out(vty, "%s%s(%s)", dn_flag, +						      peer->hostname, peer->host);  				else -					vty_out(vty, " %12s", -						lookup_msg(bgp_status_msg, -							   peer->status, NULL)); +					len = vty_out(vty, "%s%s", dn_flag, peer->host); + +				/* pad the neighbor column with spaces */ +				if (len < max_neighbor_width) +					vty_out(vty, "%*s", max_neighbor_width - len, +						" "); + +				vty_out(vty, "4 %10u %7u %7u %8" PRIu64 " %4d %4zd %8s", +					peer->as, PEER_TOTAL_RX(peer), +					PEER_TOTAL_TX(peer), peer->version[afi][safi], +					0, peer->obuf->count, +					peer_uptime(peer->uptime, timebuf, +						    BGP_UPTIME_LEN, 0, NULL)); + +				if (peer->status == Established) +					if (peer->afc_recv[afi][safi]) +						vty_out(vty, " %12ld", +							peer->pcount[afi] +							[pfx_rcd_safi]); +					else +						vty_out(vty, " NoNeg"); +				else { +					if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)) +						vty_out(vty, " Idle (Admin)"); +					else if (CHECK_FLAG( +							    peer->sflags, +							    PEER_STATUS_PREFIX_OVERFLOW)) +						vty_out(vty, " Idle (PfxCt)"); +					else +						vty_out(vty, " %12s", +							lookup_msg(bgp_status_msg, +								   peer->status, NULL)); +				} +				vty_out(vty, "\n");  			} -			vty_out(vty, "\n"); +  		}  	}  	if (use_json) {  		json_object_object_add(json, "peers", json_peers); - +		json_object_int_add(json, "failedPeers", failed_count);  		json_object_int_add(json, "totalPeers", count);  		json_object_int_add(json, "dynamicPeers", dn_count); -		bgp_show_bestpath_json(bgp, json); +		if (!show_failed) +			bgp_show_bestpath_json(bgp, json);  		vty_out(vty, "%s\n", json_object_to_json_string_ext(  					     json, JSON_C_TO_STRING_PRETTY)); @@ -8213,7 +8487,7 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi,  			vty_out(vty, "\nTotal number of neighbors %d\n", count);  		else {  			vty_out(vty, "No %s neighbor is configured\n", -				afi_safi_print(afi, safi)); +				get_afi_safi_str(afi, safi, false));  		}  		if (dn_count) { @@ -8227,7 +8501,7 @@ static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi,  }  static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi, -				      int safi, bool use_json) +				      int safi, bool show_failed, bool use_json)  {  	int is_first = 1;  	int afi_wildcard = (afi == AFI_MAX); @@ -8260,15 +8534,18 @@ static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi,  							is_first = 0;  						vty_out(vty, "\"%s\":", -							afi_safi_json(afi, -								      safi)); +							get_afi_safi_str(afi, +									 safi, +									 true));  					} else {  						vty_out(vty, "\n%s Summary:\n", -							afi_safi_print(afi, -								       safi)); +							get_afi_safi_str(afi, +									 safi, +									 false));  					}  				} -				bgp_show_summary(vty, bgp, afi, safi, use_json); +				bgp_show_summary(vty, bgp, afi, safi, show_failed, +						 use_json);  			}  			safi++;  			if (!safi_wildcard) @@ -8290,7 +8567,8 @@ static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi,  }  static void bgp_show_all_instances_summary_vty(struct vty *vty, afi_t afi, -					       safi_t safi, bool use_json) +					       safi_t safi, bool show_failed, +					       bool use_json)  {  	struct listnode *node, *nnode;  	struct bgp *bgp; @@ -8318,7 +8596,8 @@ static void bgp_show_all_instances_summary_vty(struct vty *vty, afi_t afi,  					? VRF_DEFAULT_NAME  					: bgp->name);  		} -		bgp_show_summary_afi_safi(vty, bgp, afi, safi, use_json); +		bgp_show_summary_afi_safi(vty, bgp, afi, safi, show_failed, +					  use_json);  	}  	if (use_json) @@ -8328,13 +8607,14 @@ static void bgp_show_all_instances_summary_vty(struct vty *vty, afi_t afi,  }  int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, -			 safi_t safi, bool use_json) +			 safi_t safi, bool show_failed, bool use_json)  {  	struct bgp *bgp;  	if (name) {  		if (strmatch(name, "all")) {  			bgp_show_all_instances_summary_vty(vty, afi, safi, +							   show_failed,  							   use_json);  			return CMD_SUCCESS;  		} else { @@ -8350,7 +8630,7 @@ int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi,  			}  			bgp_show_summary_afi_safi(vty, bgp, afi, safi, -						  use_json); +						  show_failed, use_json);  			return CMD_SUCCESS;  		}  	} @@ -8358,7 +8638,8 @@ int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi,  	bgp = bgp_get_default();  	if (bgp) -		bgp_show_summary_afi_safi(vty, bgp, afi, safi, use_json); +		bgp_show_summary_afi_safi(vty, bgp, afi, safi, show_failed, +					  use_json);  	else {  		if (use_json)  			vty_out(vty, "{}\n"); @@ -8373,7 +8654,7 @@ int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi,  /* `show [ip] bgp summary' commands. */  DEFUN (show_ip_bgp_summary,         show_ip_bgp_summary_cmd, -       "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] summary [json]", +       "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] summary [failed] [json]",         SHOW_STR         IP_STR         BGP_STR @@ -8381,11 +8662,13 @@ DEFUN (show_ip_bgp_summary,         BGP_AFI_HELP_STR         BGP_SAFI_WITH_LABEL_HELP_STR         "Summary of BGP neighbor status\n" +       "Show only sessions not in Established state\n"         JSON_STR)  {  	char *vrf = NULL;  	afi_t afi = AFI_MAX;  	safi_t safi = SAFI_MAX; +	bool show_failed = false;  	int idx = 0; @@ -8405,79 +8688,20 @@ DEFUN (show_ip_bgp_summary,  		argv_find_and_parse_safi(argv, argc, &idx, &safi);  	} +	if (argv_find(argv, argc, "failed", &idx)) +		show_failed = true; +  	bool uj = use_json(argc, argv); -	return bgp_show_summary_vty(vty, vrf, afi, safi, uj); +	return bgp_show_summary_vty(vty, vrf, afi, safi, show_failed, uj);  } -const char *afi_safi_print(afi_t afi, safi_t safi) +const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json)  { -	if (afi == AFI_IP && safi == SAFI_UNICAST) -		return "IPv4 Unicast"; -	else if (afi == AFI_IP && safi == SAFI_MULTICAST) -		return "IPv4 Multicast"; -	else if (afi == AFI_IP && safi == SAFI_LABELED_UNICAST) -		return "IPv4 Labeled Unicast"; -	else if (afi == AFI_IP && safi == SAFI_MPLS_VPN) -		return "IPv4 VPN"; -	else if (afi == AFI_IP && safi == SAFI_ENCAP) -		return "IPv4 Encap"; -	else if (afi == AFI_IP && safi == SAFI_FLOWSPEC) -		return "IPv4 Flowspec"; -	else if (afi == AFI_IP6 && safi == SAFI_UNICAST) -		return "IPv6 Unicast"; -	else if (afi == AFI_IP6 && safi == SAFI_MULTICAST) -		return "IPv6 Multicast"; -	else if (afi == AFI_IP6 && safi == SAFI_LABELED_UNICAST) -		return "IPv6 Labeled Unicast"; -	else if (afi == AFI_IP6 && safi == SAFI_MPLS_VPN) -		return "IPv6 VPN"; -	else if (afi == AFI_IP6 && safi == SAFI_ENCAP) -		return "IPv6 Encap"; -	else if (afi == AFI_IP6 && safi == SAFI_FLOWSPEC) -		return "IPv6 Flowspec"; -	else if (afi == AFI_L2VPN && safi == SAFI_EVPN) -		return "L2VPN EVPN"; -	else -		return "Unknown"; -} - -/* - * Please note that we have intentionally camelCased - * the return strings here.  So if you want - * to use this function, please ensure you - * are doing this within json output - */ -const char *afi_safi_json(afi_t afi, safi_t safi) -{ -	if (afi == AFI_IP && safi == SAFI_UNICAST) -		return "ipv4Unicast"; -	else if (afi == AFI_IP && safi == SAFI_MULTICAST) -		return "ipv4Multicast"; -	else if (afi == AFI_IP && safi == SAFI_LABELED_UNICAST) -		return "ipv4LabeledUnicast"; -	else if (afi == AFI_IP && safi == SAFI_MPLS_VPN) -		return "ipv4Vpn"; -	else if (afi == AFI_IP && safi == SAFI_ENCAP) -		return "ipv4Encap"; -	else if (afi == AFI_IP && safi == SAFI_FLOWSPEC) -		return "ipv4Flowspec"; -	else if (afi == AFI_IP6 && safi == SAFI_UNICAST) -		return "ipv6Unicast"; -	else if (afi == AFI_IP6 && safi == SAFI_MULTICAST) -		return "ipv6Multicast"; -	else if (afi == AFI_IP6 && safi == SAFI_LABELED_UNICAST) -		return "ipv6LabeledUnicast"; -	else if (afi == AFI_IP6 && safi == SAFI_MPLS_VPN) -		return "ipv6Vpn"; -	else if (afi == AFI_IP6 && safi == SAFI_ENCAP) -		return "ipv6Encap"; -	else if (afi == AFI_IP6 && safi == SAFI_FLOWSPEC) -		return "ipv6Flowspec"; -	else if (afi == AFI_L2VPN && safi == SAFI_EVPN) -		return "l2VpnEvpn"; +	if (for_json) +		return get_afi_safi_json_str(afi, safi);  	else -		return "Unknown"; +		return get_afi_safi_vty_str(afi, safi);  }  /* Show BGP peer's information. */ @@ -8855,14 +9079,14 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi,  					"prefixAllowedRestartIntervalMsecs",  					p->pmax_restart[afi][safi] * 60000);  		} -		json_object_object_add(json_neigh, afi_safi_print(afi, safi), +		json_object_object_add(json_neigh, get_afi_safi_str(afi, safi, true),  				       json_addr);  	} else {  		filter = &p->filter[afi][safi];  		vty_out(vty, " For address family: %s\n", -			afi_safi_print(afi, safi)); +			get_afi_safi_str(afi, safi, false));  		if (peer_group_active(p))  			vty_out(vty, "  %s peer-group member\n", @@ -9150,8 +9374,6 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,  	char buf1[PREFIX2STR_BUFFER], buf[SU_ADDRSTRLEN];  	char timebuf[BGP_UPTIME_LEN];  	char dn_flag[2]; -	const char *subcode_str; -	const char *code_str;  	afi_t afi;  	safi_t safi;  	uint16_t i; @@ -9569,8 +9791,8 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,  						json_object *json_sub = NULL;  						json_sub =  							json_object_new_object(); -						print_store = afi_safi_print( -							afi, safi); +						print_store = get_afi_safi_str( +							afi, safi, true);  						if (CHECK_FLAG(  							    p->af_cap[afi] @@ -9748,9 +9970,9 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,  									    [AFI_IP]  									    [safi],  								    PEER_CAP_ENHE_AF_RCV)) { -								print_store = afi_safi_print( +								print_store = get_afi_safi_str(  									AFI_IP, -									safi); +									safi, true);  								json_object_string_add(  									json_nxt,  									print_store, @@ -9850,8 +10072,9 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,  						json_object_object_add(  							json_multi, -							afi_safi_print(afi, -								       safi), +							get_afi_safi_str(afi, +									 safi, +									 true),  							json_exten);  					}  				} @@ -9957,9 +10180,10 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,  								restart_af_count++;  								json_object_object_add(  									json_restart, -									afi_safi_print( +									get_afi_safi_str(  										afi, -										safi), +										safi, +										true),  									json_sub);  							}  						} @@ -10018,9 +10242,10 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,  							       PEER_CAP_ADDPATH_AF_TX_RCV)) {  							vty_out(vty,  								"      %s: TX ", -								afi_safi_print( +								get_afi_safi_str(  									afi, -									safi)); +									safi, +									false));  							if (CHECK_FLAG(  								    p->af_cap @@ -10029,9 +10254,10 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,  								    PEER_CAP_ADDPATH_AF_TX_ADV))  								vty_out(vty,  									"advertised %s", -									afi_safi_print( +									get_afi_safi_str(  										afi, -										safi)); +										safi, +										false));  							if (CHECK_FLAG(  								    p->af_cap @@ -10061,9 +10287,10 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,  							       PEER_CAP_ADDPATH_AF_RX_RCV)) {  							vty_out(vty,  								"      %s: RX ", -								afi_safi_print( +								get_afi_safi_str(  									afi, -									safi)); +									safi, +									false));  							if (CHECK_FLAG(  								    p->af_cap @@ -10072,9 +10299,10 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,  								    PEER_CAP_ADDPATH_AF_RX_ADV))  								vty_out(vty,  									"advertised %s", -									afi_safi_print( +									get_afi_safi_str(  										afi, -										safi)); +										safi, +										false));  							if (CHECK_FLAG(  								    p->af_cap @@ -10145,9 +10373,10 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,  								    PEER_CAP_ENHE_AF_RCV))  								vty_out(vty,  									"           %s\n", -									afi_safi_print( +									get_afi_safi_str(  										AFI_IP, -										safi)); +										safi, +										false));  					}  				} @@ -10194,8 +10423,10 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,  					    || p->afc_recv[afi][safi]) {  						vty_out(vty,  							"    Address Family %s:", -							afi_safi_print(afi, -								       safi)); +							get_afi_safi_str( +								   afi, +								   safi, +								   false));  						if (p->afc_adv[afi][safi])  							vty_out(vty,  								" advertised"); @@ -10280,9 +10511,10 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,  									restart_af_count  										? ", "  										: "", -									afi_safi_print( +									get_afi_safi_str(  										afi, -										safi), +										safi, +										false),  									CHECK_FLAG(  										p->af_cap  											[afi] @@ -10321,8 +10553,9 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,  						       PEER_STATUS_EOR_SEND)) {  						json_object_boolean_true_add(  							json_grace_send, -							afi_safi_print(afi, -								       safi)); +							get_afi_safi_str(afi, +									 safi, +									 true));  						eor_send_af_count++;  					}  				} @@ -10332,8 +10565,9 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,  						    PEER_STATUS_EOR_RECEIVED)) {  						json_object_boolean_true_add(  							json_grace_recv, -							afi_safi_print(afi, -								       safi)); +							get_afi_safi_str(afi, +									 safi, +									 true));  						eor_receive_af_count++;  					}  				} @@ -10371,8 +10605,9 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,  						vty_out(vty, "%s%s",  							eor_send_af_count ? ", "  									  : "", -							afi_safi_print(afi, -								       safi)); +							get_afi_safi_str(afi, +								         safi, +									 false));  						eor_send_af_count++;  					}  				} @@ -10386,8 +10621,9 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,  							eor_receive_af_count  								? ", "  								: "", -							afi_safi_print(afi, -								       safi)); +							get_afi_safi_str(afi, +									 safi, +									 false));  						eor_receive_af_count++;  					}  				} @@ -10573,88 +10809,13 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,  					    (tm->tm_sec * 1000)  						    + (tm->tm_min * 60000)  						    + (tm->tm_hour * 3600000)); -			json_object_string_add( -				json_neigh, "lastResetDueTo", -				peer_down_str[(int)p->last_reset]); -			if (p->last_reset == PEER_DOWN_NOTIFY_SEND -			    || p->last_reset == PEER_DOWN_NOTIFY_RECEIVED) { -				char errorcodesubcode_hexstr[5]; -				char errorcodesubcode_str[256]; - -				code_str = bgp_notify_code_str(p->notify.code); -				subcode_str = bgp_notify_subcode_str( -					p->notify.code, p->notify.subcode); - -				sprintf(errorcodesubcode_hexstr, "%02X%02X", -					p->notify.code, p->notify.subcode); -				json_object_string_add(json_neigh, -						       "lastErrorCodeSubcode", -						       errorcodesubcode_hexstr); -				snprintf(errorcodesubcode_str, 255, "%s%s", -					 code_str, subcode_str); -				json_object_string_add(json_neigh, -						       "lastNotificationReason", -						       errorcodesubcode_str); -				if (p->last_reset == PEER_DOWN_NOTIFY_RECEIVED -				    && p->notify.code == BGP_NOTIFY_CEASE -				    && (p->notify.subcode -						== BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN -					|| p->notify.subcode -						   == BGP_NOTIFY_CEASE_ADMIN_RESET) -				    && p->notify.length) { -					char msgbuf[1024]; -					const char *msg_str; - -					msg_str = bgp_notify_admin_message( -						msgbuf, sizeof(msgbuf), -						(uint8_t *)p->notify.data, -						p->notify.length); -					if (msg_str) -						json_object_string_add( -							json_neigh, -							"lastShutdownDescription", -							msg_str); -				} -			} +			bgp_show_peer_reset(NULL, p, json_neigh, true);  		} else {  			vty_out(vty, "  Last reset %s, ",  				peer_uptime(p->resettime, timebuf,  					    BGP_UPTIME_LEN, 0, NULL)); -			if (p->last_reset == PEER_DOWN_NOTIFY_SEND -			    || p->last_reset == PEER_DOWN_NOTIFY_RECEIVED) { -				code_str = bgp_notify_code_str(p->notify.code); -				subcode_str = bgp_notify_subcode_str( -					p->notify.code, p->notify.subcode); -				vty_out(vty, "due to NOTIFICATION %s (%s%s)\n", -					p->last_reset == PEER_DOWN_NOTIFY_SEND -						? "sent" -						: "received", -					code_str, subcode_str); -				if (p->last_reset == PEER_DOWN_NOTIFY_RECEIVED -				    && p->notify.code == BGP_NOTIFY_CEASE -				    && (p->notify.subcode -						== BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN -					|| p->notify.subcode -						   == BGP_NOTIFY_CEASE_ADMIN_RESET) -				    && p->notify.length) { -					char msgbuf[1024]; -					const char *msg_str; - -					msg_str = bgp_notify_admin_message( -						msgbuf, sizeof(msgbuf), -						(uint8_t *)p->notify.data, -						p->notify.length); -					if (msg_str) -						vty_out(vty, -							"    Message: \"%s\"\n", -							msg_str); -				} -			} else { -				vty_out(vty, "due to %s\n", -					peer_down_str[(int)p->last_reset]); -			} - +			bgp_show_peer_reset(vty, p, NULL, false);  			if (p->last_reset_cause_size) {  				msg = p->last_reset_cause;  				vty_out(vty, @@ -11319,7 +11480,7 @@ static int bgp_show_route_leak_vty(struct vty *vty, const char *name,  		/* Provide context for the block */  		json_object_string_add(json, "vrf", name ? name : "default");  		json_object_string_add(json, "afiSafi", -				       afi_safi_print(afi, safi)); +				       get_afi_safi_str(afi, safi, true));  		if (!CHECK_FLAG(bgp->af_flags[afi][safi],  				BGP_CONFIG_VRF_TO_VRF_IMPORT)) { @@ -11400,11 +11561,11 @@ static int bgp_show_route_leak_vty(struct vty *vty, const char *name,  				BGP_CONFIG_VRF_TO_VRF_IMPORT))  			vty_out(vty,  		     "This VRF is not importing %s routes from any other VRF\n", -		      afi_safi_print(afi, safi)); +		      get_afi_safi_str(afi, safi, false));  		else {  			vty_out(vty,  		   "This VRF is importing %s routes from the following VRFs:\n", -		    afi_safi_print(afi, safi)); +		    get_afi_safi_str(afi, safi, false));  			for (ALL_LIST_ELEMENTS_RO(  						bgp->vpn_policy[afi].import_vrf, @@ -11428,11 +11589,11 @@ static int bgp_show_route_leak_vty(struct vty *vty, const char *name,  				BGP_CONFIG_VRF_TO_VRF_EXPORT))  			vty_out(vty,  		       "This VRF is not exporting %s routes to any other VRF\n", -			afi_safi_print(afi, safi)); +			get_afi_safi_str(afi, safi, false));  		else {  			vty_out(vty,  		       "This VRF is exporting %s routes to the following VRFs:\n", -			afi_safi_print(afi, safi)); +			get_afi_safi_str(afi, safi, false));  			for (ALL_LIST_ELEMENTS_RO(  						bgp->vpn_policy[afi].export_vrf, @@ -11809,7 +11970,7 @@ static int bgp_show_one_peer_group(struct vty *vty, struct peer_group *group)  	FOREACH_AFI_SAFI (afi, safi) {  		if (conf->afc[afi][safi]) {  			af_cfgd = 1; -			vty_out(vty, " %s;", afi_safi_print(afi, safi)); +			vty_out(vty, " %s;", get_afi_safi_str(afi, safi, false));  		}  	}  	if (!af_cfgd) diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index d9df2b4cfe..27b5ea47b2 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -45,8 +45,7 @@ struct bgp;  	"Address Family modifier\n"  extern void bgp_vty_init(void); -extern const char *afi_safi_print(afi_t afi, safi_t safi); -extern const char *afi_safi_json(afi_t afi, safi_t safi); +extern const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json);  extern void bgp_config_write_update_delay(struct vty *vty, struct bgp *bgp);  extern void bgp_config_write_wpkt_quanta(struct vty *vty, struct bgp *bgp);  extern void bgp_config_write_rpkt_quanta(struct vty *vty, struct bgp *bgp); @@ -72,7 +71,7 @@ extern int bgp_vty_find_and_parse_afi_safi_bgp(struct vty *vty,  					       safi_t *safi, struct bgp **bgp,  					       bool use_json);  extern int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi, -				safi_t safi, bool use_json); +				safi_t safi, bool show_failed, bool use_json);  extern void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp,  					    afi_t afi);  #endif /* _QUAGGA_BGP_VTY_H */ diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index b5f267cc38..5b31fbb3a8 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1582,6 +1582,12 @@ struct peer *peer_create(union sockunion *su, const char *conf_if,  	}  	active = peer_active(peer); +	if (!active) { +		if (peer->su.sa.sa_family == AF_UNSPEC) +			peer->last_reset = PEER_DOWN_NBR_ADDR; +		else +			peer->last_reset = PEER_DOWN_NOAFI_ACTIVATED; +	}  	/* Last read and reset time set */  	peer->readtime = peer->resettime = bgp_clock(); @@ -2221,10 +2227,12 @@ int peer_delete(struct peer *peer)  	bgp = peer->bgp;  	accept_peer = CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER); +	bgp_keepalives_off(peer);  	bgp_reads_off(peer);  	bgp_writes_off(peer);  	assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_WRITES_ON));  	assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_READS_ON)); +	assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_KEEPALIVES_ON));  	if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT))  		peer_nsf_stop(peer); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 9e05fd5629..477c036009 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -1194,6 +1194,10 @@ struct peer {  #define PEER_DOWN_BFD_DOWN              24 /* BFD down */  #define PEER_DOWN_IF_DOWN               25 /* Interface down */  #define PEER_DOWN_NBR_ADDR_DEL          26 /* Peer address lost */ +#define PEER_DOWN_WAITING_NHT           27 /* Waiting for NHT to resolve */ +#define PEER_DOWN_NBR_ADDR              28 /* Waiting for peer IPv6 IP Addr */ +#define PEER_DOWN_VRF_UNINIT            29 /* Associated VRF is not init yet */ +#define PEER_DOWN_NOAFI_ACTIVATED       30 /* No AFI/SAFI activated for peer */  	size_t last_reset_cause_size;  	uint8_t last_reset_cause[BGP_MAX_PACKET_SIZE]; diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c index e7905e5622..9b8f64ee67 100644 --- a/bgpd/rfapi/rfapi.c +++ b/bgpd/rfapi/rfapi.c @@ -1282,8 +1282,7 @@ static int rfapi_open_inner(struct rfapi_descriptor *rfd, struct bgp *bgp,  	 * since this peer is not on the I/O thread, this lock is not strictly  	 * necessary, but serves as a reminder to those who may meddle...  	 */ -	pthread_mutex_lock(&rfd->peer->io_mtx); -	{ +	frr_with_mutex(&rfd->peer->io_mtx) {  		// we don't need any I/O related facilities  		if (rfd->peer->ibuf)  			stream_fifo_free(rfd->peer->ibuf); @@ -1300,7 +1299,6 @@ static int rfapi_open_inner(struct rfapi_descriptor *rfd, struct bgp *bgp,  		rfd->peer->obuf_work = NULL;  		rfd->peer->ibuf_work = NULL;  	} -	pthread_mutex_unlock(&rfd->peer->io_mtx);  	{ /* base code assumes have valid host pointer */  		char buf[BUFSIZ]; diff --git a/bgpd/rfapi/vnc_zebra.c b/bgpd/rfapi/vnc_zebra.c index 481500dfb4..80a590f56a 100644 --- a/bgpd/rfapi/vnc_zebra.c +++ b/bgpd/rfapi/vnc_zebra.c @@ -191,8 +191,7 @@ static void vnc_redistribute_add(struct prefix *p, uint32_t metric,  			 * is not strictly necessary, but serves as a reminder  			 * to those who may meddle...  			 */ -			pthread_mutex_lock(&vncHD1VR.peer->io_mtx); -			{ +			frr_with_mutex(&vncHD1VR.peer->io_mtx) {  				// we don't need any I/O related facilities  				if (vncHD1VR.peer->ibuf)  					stream_fifo_free(vncHD1VR.peer->ibuf); @@ -209,7 +208,6 @@ static void vnc_redistribute_add(struct prefix *p, uint32_t metric,  				vncHD1VR.peer->obuf_work = NULL;  				vncHD1VR.peer->ibuf_work = NULL;  			} -			pthread_mutex_unlock(&vncHD1VR.peer->io_mtx);  			/* base code assumes have valid host pointer */  			vncHD1VR.peer->host = diff --git a/bgpd/subdir.am b/bgpd/subdir.am index d281fe4e59..b338fd4f3d 100644 --- a/bgpd/subdir.am +++ b/bgpd/subdir.am @@ -27,6 +27,7 @@ vtysh_scan += \  # can be loaded as DSO - always include for vtysh  vtysh_scan += $(top_srcdir)/bgpd/bgp_rpki.c +vtysh_scan += $(top_srcdir)/bgpd/bgp_bmp.c  if ENABLE_BGP_VNC  vtysh_scan += \ @@ -42,6 +43,9 @@ endif  if RPKI  module_LTLIBRARIES += bgpd/bgpd_rpki.la  endif +if BGP_BMP +module_LTLIBRARIES += bgpd/bgpd_bmp.la +endif  man8 += $(MANBUILD)/bgpd.8  endif @@ -129,6 +133,7 @@ noinst_HEADERS += \  	bgpd/bgp_damp.h \  	bgpd/bgp_debug.h \  	bgpd/bgp_dump.h \ +	bgpd/bgp_bmp.h \  	bgpd/bgp_ecommunity.h \  	bgpd/bgp_encap_tlv.h \  	bgpd/bgp_encap_types.h \ @@ -216,6 +221,10 @@ bgpd_bgpd_rpki_la_CFLAGS = $(WERROR) $(RTRLIB_CFLAGS)  bgpd_bgpd_rpki_la_LDFLAGS = -avoid-version -module -shared -export-dynamic  bgpd_bgpd_rpki_la_LIBADD = $(RTRLIB_LIBS) +bgpd_bgpd_bmp_la_SOURCES = bgpd/bgp_bmp.c +bgpd_bgpd_bmp_la_LIBADD = lib/libfrrcares.la +bgpd_bgpd_bmp_la_LDFLAGS = -avoid-version -module -shared -export-dynamic +  bgpd/bgp_evpn_vty_clippy.c: $(CLIPPY_DEPS)  bgpd/bgp_evpn_vty.$(OBJEXT): bgpd/bgp_evpn_vty_clippy.c  bgpd/bgp_vty_clippy.c: $(CLIPPY_DEPS) @@ -229,3 +238,5 @@ bgpd/bgp_routemap.$(OBJEXT): bgpd/bgp_routemap_clippy.c  bgpd/bgp_rpki_clippy.c: $(CLIPPY_DEPS)  $(AUTOMAKE_DUMMY)bgpd/bgpd_bgpd_rpki_la-bgp_rpki.lo: bgpd/bgp_rpki_clippy.c  $(AUTOMAKE_DUMMY)bgpd/bgpd_rpki_la-bgp_rpki.lo: bgpd/bgp_rpki_clippy.c +bgpd/bgp_bmp_clippy.c: $(CLIPPY_DEPS) +bgpd/bgp_bmp.lo: bgpd/bgp_bmp_clippy.c diff --git a/configure.ac b/configure.ac index 134c8692d4..6c1b35b5f2 100755 --- a/configure.ac +++ b/configure.ac @@ -479,12 +479,14 @@ AC_ARG_ENABLE([staticd],    AS_HELP_STRING([--disable-staticd], [do not build staticd]))  AC_ARG_ENABLE([fabricd],    AS_HELP_STRING([--disable-fabricd], [do not build fabricd])) -AC_ARG_ENABLE([bgp-announce], -  AS_HELP_STRING([--disable-bgp-announce,], [turn off BGP route announcement]))  AC_ARG_ENABLE([vrrpd],    AS_HELP_STRING([--disable-vrrpd], [do not build vrrpd])) +AC_ARG_ENABLE([bgp-announce], +  AS_HELP_STRING([--disable-bgp-announce,], [turn off BGP route announcement]))  AC_ARG_ENABLE([bgp-vnc],    AS_HELP_STRING([--disable-bgp-vnc],[turn off BGP VNC support])) +AC_ARG_ENABLE([bgp-bmp], +  AS_HELP_STRING([--disable-bgp-bmp],[turn off BGP BMP support]))  AC_ARG_ENABLE([snmp],    AS_HELP_STRING([--enable-snmp], [enable SNMP support for agentx]))  AC_ARG_ENABLE([config_rollbacks], @@ -1061,6 +1063,7 @@ case "$host_os" in      AC_CHECK_LIB([nsl], [main])      AC_CHECK_LIB([umem], [main])      SOLARIS="solaris" +    AC_MSG_WARN([--Solaris support is being considered for deprecation, please let us know if you are still using this--])      ;;    linux*)      AC_MSG_RESULT([Linux]) @@ -1449,6 +1452,16 @@ if test "x$enable_pcreposix" = "xyes"; then  fi  AC_SUBST([HAVE_LIBPCREPOSIX]) +dnl ------------------ +dnl check C-Ares library +dnl ------------------ +PKG_CHECK_MODULES([CARES], [libcares], [ +  c_ares_found=true +],[ +  c_ares_found=false +]) +AM_CONDITIONAL([CARES], [$c_ares_found]) +  dnl ##########################################################################  dnl test "${enable_clippy_only}" != "yes"  fi @@ -1518,9 +1531,21 @@ fi  NHRPD=""  case "$host_os" in    linux*) -    if test "${enable_nhrpd}" != "no"; then -      NHRPD="nhrpd" -    fi +    case "${enable_nhrpd}" in +      no) +        ;; +      yes) +        if test "$c_ares_found" != "true" ; then +          AC_MSG_ERROR([nhrpd requires libcares.  Please install c-ares and its -dev headers.]) +        fi +        NHRPD="nhrpd" +        ;; +      *) +        if test "$c_ares_found" = "true" ; then +          NHRPD="nhrpd" +        fi +        ;; +    esac      ;;    *)      if test "${enable_nhrpd}" = "yes"; then @@ -1554,22 +1579,29 @@ if test "${enable_bgp_vnc}" != "no";then    AC_DEFINE([ENABLE_BGP_VNC], [1], [Enable BGP VNC support])  fi +bgpd_bmp=false +case "${enable_bmp}" in +  no) +    ;; +  yes) +    if test "$c_ares_found" != "true" ; then +      AC_MSG_ERROR([BMP support requires libcares.  Please install c-ares and its -dev headers.]) +    fi +    bgpd_bmp=true +    ;; +  *) +    if test "$c_ares_found" = "true" ; then +      bgpd_bmp=true +    fi +    ;; +esac +  dnl ##########################################################################  dnl LARGE if block  if test "${enable_clippy_only}" != "yes"; then  dnl ##########################################################################  dnl ------------------ -dnl check C-Ares library -dnl ------------------ -if test "${NHRPD}" != ""; then -  PKG_CHECK_MODULES([CARES], [libcares], ,[ -    AC_MSG_ERROR([trying to build nhrpd, but libcares not found. install c-ares and its -dev headers.]) -  ]) -fi -AM_CONDITIONAL([CARES], [test "${NHRPD}" != ""]) - -dnl ------------------  dnl check Net-SNMP library  dnl ------------------  if test "${enable_snmp}" != "" -a "${enable_snmp}" != "no"; then @@ -2006,6 +2038,20 @@ if test "${enable_capabilities}" != "no"; then         -o x"${frr_ac_lcaps}" = x"yes"; then      AC_DEFINE([HAVE_CAPABILITIES], [1], [capabilities])    fi + +  case "$host_os" in +  linux*) +    if test "$frr_ac_lcaps" != "yes"; then +      AC_MSG_ERROR([libcap and/or its headers were not found.  Running FRR without libcap support built in causes a huge performance penalty.]) +    fi +    ;; +  esac +else +  case "$host_os" in +  linux*) +    AC_MSG_WARN([Running FRR without libcap support built in causes a huge performance penalty.]) +    ;; +  esac  fi  AC_SUBST([LIBCAP]) @@ -2192,6 +2238,7 @@ AC_DEFINE_UNQUOTED([WATCHFRR_SH_PATH], ["${CFG_SBIN%/}/watchfrr.sh"], [path to w  dnl various features  AM_CONDITIONAL([SUPPORT_REALMS], [test "${enable_realms}" = "yes"])  AM_CONDITIONAL([ENABLE_BGP_VNC], [test x${enable_bgp_vnc} != xno]) +AM_CONDITIONAL([BGP_BMP], [$bgpd_bmp])  dnl northbound  AM_CONDITIONAL([SQLITE3], [$SQLITE3])  AM_CONDITIONAL([CONFD], [test "x$enable_confd" != "x"]) diff --git a/debian/frr.install b/debian/frr.install index fe34b23d02..09bddf0fc6 100644 --- a/debian/frr.install +++ b/debian/frr.install @@ -10,6 +10,7 @@ usr/lib/frr/watchfrr  usr/lib/frr/zebra  usr/lib/*/frr/modules/zebra_irdp.so  usr/lib/*/frr/modules/zebra_fpm.so +usr/lib/*/frr/modules/bgpd_bmp.so  usr/share/doc/frr/examples  usr/share/man/  usr/share/yang/ diff --git a/doc/developer/library.rst b/doc/developer/library.rst index 7cd493ccc4..a904a4e778 100644 --- a/doc/developer/library.rst +++ b/doc/developer/library.rst @@ -11,6 +11,7 @@ Library Facilities (libfrr)     rcu     lists     logging +   locking     hooks     cli     modules diff --git a/doc/developer/locking.rst b/doc/developer/locking.rst new file mode 100644 index 0000000000..aee05aae06 --- /dev/null +++ b/doc/developer/locking.rst @@ -0,0 +1,73 @@ +Locking +======= + +FRR ships two small wrappers around ``pthread_mutex_lock()`` / +``pthread_mutex_unlock``.  Use ``#include "frr_pthread.h"`` to get these +macros. + +.. c:function:: frr_with_mutex(pthread_mutex_t *mutex) + +   Begin a C statement block that is executed with the mutex locked.  Any +   exit from the block (``break``, ``return``, ``goto``, end of block) will +   cause the mutex to be unlocked:: + +      int somefunction(int option) +      { +          frr_with_mutex(&my_mutex) { +              /* mutex will be locked */ + +              if (!option) +                  /* mutex will be unlocked before return */ +                  return -1; + +              if (something(option)) +                  /* mutex will be unlocked before goto */ +                  goto out_err; + +              somethingelse(); + +              /* mutex will be unlocked at end of block */ +          } + +          return 0; + +      out_err: +          somecleanup(); +          return -1; +      } + +   This is a macro that internally uses a ``for`` loop.  It is explicitly +   acceptable to use ``break`` to get out of the block.  Even though a single +   statement works correctly, FRR coding style requires that this macro always +   be used with a ``{ ... }`` block. + +.. c:function:: frr_mutex_lock_autounlock(pthread_mutex_t *mutex) + +   Lock mutex and unlock at the end of the current C statement block:: + +      int somefunction(int option) +      { +          frr_mutex_lock_autounlock(&my_mutex); +          /* mutex will be locked */ + +          ... +          if (error) +            /* mutex will be unlocked before return */ +            return -1; +          ... + +          /* mutex will be unlocked before return */ +          return 0; +      } + +   This is a macro that internally creates a variable with a destructor. +   When the variable goes out of scope (i.e. the block ends), the mutex is +   released. + +   .. warning:: + +      This macro should only used when :c:func:`frr_with_mutex` would +      result in excessively/weirdly nested code.  This generally is an +      indicator that the code might be trying to do too many things with +      the lock held.  Try any possible venues to reduce the amount of +      code covered by the lock and move to :c:func:`frr_with_mutex`. diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am index 1fc593e566..557a41c51f 100644 --- a/doc/developer/subdir.am +++ b/doc/developer/subdir.am @@ -31,6 +31,7 @@ dev_RSTFILES = \  	doc/developer/index.rst \  	doc/developer/library.rst \  	doc/developer/lists.rst \ +	doc/developer/locking.rst \  	doc/developer/logging.rst \  	doc/developer/maintainer-release-build.rst \  	doc/developer/memtypes.rst \ diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst index 07c43ac2de..3c6887fbac 100644 --- a/doc/developer/workflow.rst +++ b/doc/developer/workflow.rst @@ -767,6 +767,28 @@ ways that can be unexpected for the original implementor. As such debugs  ability to turn on/off debugs from the CLI and it is expected that the  developer will use this convention to allow control of their debugs. +Custom syntax-like block macros +------------------------------- + +FRR uses some macros that behave like the ``for`` or ``if`` C keywords.  These +macros follow these patterns: + +- loop-style macros are named ``frr_each_*`` (and ``frr_each``) +- single run macros are named ``frr_with_*`` +- to avoid confusion, ``frr_with_*`` macros must always use a ``{ ... }`` +  block even if the block only contains one statement.  The ``frr_each`` +  constructs are assumed to be well-known enough to use normal ``for`` rules. +- ``break``, ``return`` and ``goto`` all work correctly.  For loop-style +  macros, ``continue`` works correctly too. + +Both the ``each`` and ``with`` keywords are inspired by other (more +higher-level) programming languages that provide these constructs. + +There are also some older iteration macros, e.g. ``ALL_LIST_ELEMENTS`` and +``FOREACH_AFI_SAFI``.  These macros in some cases do **not** fulfill the above +pattern (e.g. ``break`` does not work in ``FOREACH_AFI_SAFI`` because it +expands to 2 nested loops.) +  Static Analysis and Sanitizers  ------------------------------  Clang/LLVM and GCC come with a variety of tools that can be used to help find diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index 92d4126cec..da339b4409 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -2251,6 +2251,12 @@ structure is extended with :clicmd:`show bgp [afi] [safi]`.     Show a bgp peer summary for the specified address family, and subsequent     address-family. +.. index:: show bgp [afi] [safi] summary failed [json] +.. clicmd:: show bgp [afi] [safi] summary failed [json] + +   Show a bgp peer summary for peers that are not succesfully exchanging routes +   for the specified address family, and subsequent address-family. +  .. index:: show bgp [afi] [safi] neighbor [PEER]  .. clicmd:: show bgp [afi] [safi] neighbor [PEER] diff --git a/doc/user/bmp.rst b/doc/user/bmp.rst new file mode 100644 index 0000000000..061800c14e --- /dev/null +++ b/doc/user/bmp.rst @@ -0,0 +1,170 @@ +.. _bmp: + +*** +BMP +*** + +:abbr:`BMP` (BGP Monitoring Protocol, :rfc:`7854`) is used to send monitoring +data from BGP routers to network management entities. + +Implementation characteristics +============================== + +The `BMP` implementation in FRR has the following properties: + +- only the :rfc:`7854` features are currently implemented.  This means protocol +  version 3 without any extensions.  It is not possible to use an older draft +  protocol version of BMP. + +- the following statistics codes are implemented: + +  - 0: count of prefixes rejected +  - 2: count of duplicate prefix withdrawals +  - 3: count of **prefixes** with loop in cluster id +  - 4: count of **prefixes** with loop in AS-path +  - 5: count of **prefixes** with loop in originator +  - 11: count of updates subjected to :rfc:`7607` "treat as withdrawal" +    handling due to errors +  - 65531: *experimental* count of prefixes rejected due to invalid next-hop + +  Note that stat items 3, 4 and 5 are specified to count updates, but FRR +  implements them as prefix-based counters. + +- **route mirroring** is fully implemented, however BGP OPEN messages are not +  currently included in route mirroring messages.  Their contents can be +  extracted from the "peer up" notification for sessions that established +  successfully.  OPEN messages for failed sessions cannot currently be +  mirrored. + +- **route monitoring** is available for IPv4 and IPv6 AFIs, unicast and +  multicast SAFIs.  Other SAFIs (VPN, Labeled-Unicast, Flowspec, etc.) are not +  currently supported. + +- monitoring peers that have BGP **add-path** enabled on the session will +  result in somewhat unpredictable behaviour.  Currently, the outcome is: + +  - route mirroring functions as intended, messages are copied verbatim +  - the add-path ID is never included in route monitoring messages +  - if multiple paths were received from a peer, an unpredictable path is +    picked and sent on the BMP session.  The selection will differ for +    pre-policy and post-policy monitoring sessions. +  - as long as any path is present, something will be advertised on BMP +    sessions.  Only after the last path is gone a withdrawal will be sent on +    BMP sessions. +  - updates to additional paths will trigger BMP route monitoring messages. +    There is no guarantee on consistency regarding which path is sent in these +    messages. + +- monitoring peers with :rfc:`5549` extended next-hops has not been tested. + +Starting BMP +============ + +BMP is implemented as a loadable module.  This means that to use BMP, ``bgpd`` +must be started with the ``-M bmp`` option.  It is not possible to enable BMP +if ``bgpd`` was started without this option. + +Configuring BMP +=============== + +All of FRR's BMP configuration options are located inside the +:clicmd:`router bgp ASN` block.  Configure BGP first before proceeding to BMP +setup. + +There is one option that applies to the BGP instance as a whole: + +.. index:: bmp mirror buffer-limit(0-4294967294) +.. clicmd:: [no] bmp mirror buffer-limit(0-4294967294) + +   This sets the maximum amount of memory used for buffering BGP messages +   (updates, keepalives, ...) for sending in BMP Route Mirroring. + +   The buffer is for the entire BGP instance; if multiple BMP targets are +   configured they reference the same buffer and do not consume additional +   memory.  Queue overhead is included in accounting this memory, so the +   actual space available for BGP messages is slightly less than the value +   configured here. + +   If the buffer fills up, the oldest messages are removed from the buffer and +   any BMP sessions where the now-removed messages were still pending have +   their **entire** queue flushed and a "Mirroring Messages Lost" BMP message +   is sent. + +   BMP Route Monitoring is not affected by this option. + +All other configuration is managed per targets: + +.. index:: bmp targets NAME +.. clicmd:: [no] bmp targets NAME + +   Create/delete a targets group.  As implied by the plural name, targets may +   cover multiple outbound active BMP sessions as well as inbound passive +   listeners. + +   If BMP sessions have the same configuration, putting them in the same +   ``bmp targets`` will reduce overhead. + +BMP session configuration +------------------------- + +Inside a ``bmp targets`` block, the following commands control session +establishment: + +.. index:: bmp connect HOSTNAME port (1-65535) {min-retry MSEC|max-retry MSEC} +.. clicmd:: [no] bmp connect HOSTNAME port (1-65535) {min-retry MSEC|max-retry MSEC} + +   Add/remove an active outbound BMP session.  HOSTNAME is resolved via DNS, +   if multiple addresses are returned they are tried in nondeterministic +   order.  Only one connection will be established even if multiple addresses +   are returned.  ``min-retry`` and ``max-retry`` specify (in milliseconds) +   bounds for exponential backoff. + +.. warning:: + +   ``ip access-list`` and ``ipv6 access-list`` are checked for outbound +   connections resulting from ``bmp connect`` statements. + +.. index:: bmp listener <X:X::X:X|A.B.C.D> port (1-65535) +.. clicmd:: [no] bmp listener <X:X::X:X|A.B.C.D> port (1-65535) + +   Accept incoming BMP sessions on the specified address and port.  You can +   use ``0.0.0.0`` and ``::`` to listen on all IPv4/IPv6 addresses. + +.. clicmd:: [no] ip access-list NAME +.. clicmd:: [no] ipv6 access-list NAME + +   Restrict BMP sessions to the addresses allowed by the respective access +   lists.  The access lists are checked for both passive and active BMP +   sessions.  Changes do not affect currently established sessions. + +BMP data feed configuration +--------------------------- + +The following commands configure what BMP messages are sent on sessions +associated with a particular ``bmp targets``: + +.. index:: bmp stats [interval (100-86400000)] +.. clicmd:: [no] bmp stats [interval (100-86400000)] + +   Send BMP Statistics (counter) messages at the specified interval (in +   milliseconds.) + +.. index:: bmp monitor AFI SAFI <pre-policy|post-policy> +.. clicmd:: [no] bmp monitor AFI SAFI <pre-policy|post-policy> + +   Perform Route Monitoring for the specified AFI and SAFI.  Only IPv4 and +   IPv6 are currently valid for AFI, and only unicast and multicast are valid +   for SAFI.  Other AFI/SAFI combinations may be added in the future. + +   All BGP neighbors are included in Route Monitoring.  Options to select +   a subset of BGP sessions may be added in the future. + +.. index:: bmp mirror +.. clicmd:: [no] bmp mirror + +   Perform Route Mirroring for all BGP neighbors.  Since this provides a +   direct feed of BGP messages, there are no AFI/SAFI options to be +   configured. + +   All BGP neighbors are included in Route Mirroring.  Options to select +   a subset of BGP sessions may be added in the future. diff --git a/doc/user/eigrpd.rst b/doc/user/eigrpd.rst index 330267a8ee..915270a562 100644 --- a/doc/user/eigrpd.rst +++ b/doc/user/eigrpd.rst @@ -65,15 +65,16 @@ Certain signals have special meanings to *eigrpd*.  EIGRP Configuration  =================== -.. index:: router eigrp (1-65535) -.. clicmd:: router eigrp (1-65535) +.. index:: router eigrp (1-65535) [vrf NAME] +.. clicmd:: router eigrp (1-65535) [vrf NAME]     The `router eigrp` command is necessary to enable EIGRP. To disable EIGRP,     use the `no router eigrp (1-65535)` command. EIGRP must be enabled before -   carrying out any of the EIGRP commands. +   carrying out any of the EIGRP commands.  Specify vrf NAME if you want +   eigrp to work within the specified vrf. -.. index:: no router eigrp (1-65535) -.. clicmd:: no router eigrp (1-65535) +.. index:: no router eigrp (1-65535) [vrf NAME] +.. clicmd:: no router eigrp (1-65535) [vrf NAME]     Disable EIGRP. @@ -189,8 +190,8 @@ How to Announce EIGRP route  Show EIGRP Information  ====================== -.. index:: show ip eigrp topology -.. clicmd:: show ip eigrp topology +.. index:: show ip eigrp [vrf NAME] topology +.. clicmd:: show ip eigrp [vrf NAME] topology     Display current EIGRP status. @@ -207,6 +208,17 @@ Show EIGRP Information        P  10.0.2.0/24, 1 successors, FD is 256256, serno: 0               via Connected, enp0s3 +.. index:: show ip eigrp [vrf NAME] interface +.. clicmd:: show ip eigrp [vrf NAME] interface + +   Display the list of interfaces associated with a particular eigrp +   instance. + +..index:: show ip eigrp [vrf NAME] neighbor +..clicmd:: show ip eigrp [vrf NAME] neighbor + +   Display the list of neighbors that have been established within +   a particular eigrp instance.  EIGRP Debug Commands  ==================== diff --git a/doc/user/index.rst b/doc/user/index.rst index 4e14de6737..6c3b14e062 100644 --- a/doc/user/index.rst +++ b/doc/user/index.rst @@ -57,6 +57,7 @@ Protocols     static     vnc     vrrp +   bmp  ########  Appendix diff --git a/doc/user/pbr.rst b/doc/user/pbr.rst index 6230bf777a..fab4343f50 100644 --- a/doc/user/pbr.rst +++ b/doc/user/pbr.rst @@ -91,6 +91,12 @@ end destination.     both v4 and v6 prefixes.  This command is used in conjunction of the     :clicmd:`match src-ip PREFIX` command for matching. +.. clicmd:: match mark (1-4294967295) + +   Select the mark to match.  This is a linux only command and if attempted +   on another platform it will be denied.  This mark translates to the +   underlying `ip rule .... fwmark XXXX` command. +  .. clicmd:: set nexthop-group NAME     Use the nexthop-group NAME as the place to forward packets when the match diff --git a/doc/user/rpki.rst b/doc/user/rpki.rst index ca6b46d3cf..dfac10b4f2 100644 --- a/doc/user/rpki.rst +++ b/doc/user/rpki.rst @@ -112,31 +112,6 @@ The following commands are independent of a specific cache server.     The default value is 300 seconds. -.. index:: rpki timeout <1-4,294,967,296> -.. clicmd:: rpki timeout <1-4,294,967,296> - -.. index:: no rpki timeout -.. clicmd:: no rpki timeout - -   Set the number of seconds the router waits for the cache reply. If the cache -   server is not replying within this time period, the router deletes all -   received prefix records from the prefix table. - -   The default value is 600 seconds. - -.. index:: rpki initial-synchronisation-timeout <1-4,294,967,296> -.. clicmd:: rpki initial-synchronisation-timeout <1-4,294,967,296> - -.. index:: no rpki initial-synchronisation-timeout -.. clicmd:: no rpki initial-synchronisation-timeout - -   Set the number of seconds until the first synchronization with the cache -   server needs to be completed. If the timeout expires, BGP routing is started -   without RPKI. The router will try to establish the cache server connection in -   the background. - -   The default value is 30 seconds. -     The following commands configure one or multiple cache servers.  .. index:: rpki cache (A.B.C.D|WORD) PORT [SSH_USERNAME] [SSH_PRIVKEY_PATH] [SSH_PUBKEY_PATH] [KNOWN_HOSTS_PATH] PREFERENCE diff --git a/doc/user/subdir.am b/doc/user/subdir.am index 1e4d86c722..0f0a8a0774 100644 --- a/doc/user/subdir.am +++ b/doc/user/subdir.am @@ -7,6 +7,7 @@ user_RSTFILES = \  	doc/user/ldpd.rst \  	doc/user/basic.rst \  	doc/user/bgp.rst \ +	doc/user/bmp.rst \  	doc/user/bugs.rst \  	doc/user/conf.py \  	doc/user/eigrpd.rst \ diff --git a/eigrpd/eigrp_cli.c b/eigrpd/eigrp_cli.c index ba657a7d5d..a93d4c8280 100644 --- a/eigrpd/eigrp_cli.c +++ b/eigrpd/eigrp_cli.c @@ -40,17 +40,18 @@  DEFPY_NOSH(  	router_eigrp,  	router_eigrp_cmd, -	"router eigrp (1-65535)$as", +	"router eigrp (1-65535)$as [vrf NAME]",  	ROUTER_STR  	EIGRP_STR -	AS_STR) +	AS_STR +	VRF_CMD_HELP_STR)  {  	char xpath[XPATH_MAXLEN];  	int rv;  	snprintf(xpath, sizeof(xpath), -		 "/frr-eigrpd:eigrpd/instance[asn='%s'][vrf='']", -		 as_str); +		 "/frr-eigrpd:eigrpd/instance[asn='%s'][vrf='%s']", +		 as_str, vrf ? vrf : VRF_DEFAULT_NAME);  	nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);  	rv = nb_cli_apply_changes(vty, NULL); @@ -60,20 +61,21 @@ DEFPY_NOSH(  	return rv;  } -DEFPY_NOSH( +DEFPY(  	no_router_eigrp,  	no_router_eigrp_cmd, -	"no router eigrp (1-65535)$as", +	"no router eigrp (1-65535)$as [vrf NAME]",  	NO_STR  	ROUTER_STR  	EIGRP_STR -	AS_STR) +	AS_STR +	VRF_CMD_HELP_STR)  {  	char xpath[XPATH_MAXLEN];  	snprintf(xpath, sizeof(xpath), -		 "/frr-eigrpd:eigrpd/instance[asn='%s'][vrf='']", -		 as_str); +		 "/frr-eigrpd:eigrpd/instance[asn='%s'][vrf='%s']", +		 as_str, vrf ? vrf : VRF_DEFAULT_NAME);  	nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);  	return nb_cli_apply_changes(vty, NULL); @@ -83,8 +85,12 @@ void eigrp_cli_show_header(struct vty *vty, struct lyd_node *dnode,  			   bool show_defaults)  {  	const char *asn = yang_dnode_get_string(dnode, "./asn"); +	const char *vrf = yang_dnode_get_string(dnode, "./vrf"); -	vty_out(vty, "router eigrp %s\n", asn); +	vty_out(vty, "router eigrp %s", asn); +	if (strcmp(vrf, VRF_DEFAULT_NAME)) +		vty_out(vty, " vrf %s", vrf); +	vty_out(vty, "\n");  }  void eigrp_cli_show_end_header(struct vty *vty, struct lyd_node *dnode) diff --git a/eigrpd/eigrp_filter.c b/eigrpd/eigrp_filter.c index 93eed9452c..9d5d45ca50 100644 --- a/eigrpd/eigrp_filter.c +++ b/eigrpd/eigrp_filter.c @@ -65,17 +65,15 @@  void eigrp_distribute_update(struct distribute_ctx *ctx,  			     struct distribute *dist)  { +	struct eigrp *e = eigrp_lookup(ctx->vrf->vrf_id);  	struct interface *ifp;  	struct eigrp_interface *ei = NULL;  	struct access_list *alist;  	struct prefix_list *plist;  	// struct route_map *routemap; -	struct eigrp *e;  	/* if no interface address is present, set list to eigrp process struct  	 */ -	e = eigrp_lookup(); -	assert(e != NULL);  	/* Check if distribute-list was set for process or interface */  	if (!dist->ifname) { @@ -174,7 +172,7 @@ void eigrp_distribute_update(struct distribute_ctx *ctx,  		return;  	} -	ifp = if_lookup_by_name(dist->ifname, VRF_DEFAULT); +	ifp = if_lookup_by_name(dist->ifname, e->vrf_id);  	if (ifp == NULL)  		return; @@ -288,7 +286,7 @@ void eigrp_distribute_update_interface(struct interface *ifp)  	struct distribute *dist;  	struct eigrp *eigrp; -	eigrp = eigrp_lookup(); +	eigrp = eigrp_lookup(ifp->vrf_id);  	if (!eigrp)  		return;  	dist = distribute_lookup(eigrp->distribute_ctx, ifp->name); @@ -302,11 +300,13 @@ void eigrp_distribute_update_interface(struct interface *ifp)   */  void eigrp_distribute_update_all(struct prefix_list *notused)  { -	struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); +	struct vrf *vrf;  	struct interface *ifp; -	FOR_ALL_INTERFACES (vrf, ifp) -		eigrp_distribute_update_interface(ifp); +	RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { +		FOR_ALL_INTERFACES (vrf, ifp) +			eigrp_distribute_update_interface(ifp); +	}  }  /* diff --git a/eigrpd/eigrp_filter.h b/eigrpd/eigrp_filter.h index 34d00ecc13..03f45cedbf 100644 --- a/eigrpd/eigrp_filter.h +++ b/eigrpd/eigrp_filter.h @@ -35,10 +35,10 @@  extern void eigrp_distribute_update(struct distribute_ctx *ctx,  				    struct distribute *dist); -extern void eigrp_distribute_update_interface(struct interface *); -extern void eigrp_distribute_update_all(struct prefix_list *); -extern void eigrp_distribute_update_all_wrapper(struct access_list *); -extern int eigrp_distribute_timer_process(struct thread *); -extern int eigrp_distribute_timer_interface(struct thread *); +extern void eigrp_distribute_update_interface(struct interface *ifp); +extern void eigrp_distribute_update_all(struct prefix_list *plist); +extern void eigrp_distribute_update_all_wrapper(struct access_list *alist); +extern int eigrp_distribute_timer_process(struct thread *thread); +extern int eigrp_distribute_timer_interface(struct thread *thread);  #endif /* EIGRPD_EIGRP_FILTER_H_ */ diff --git a/eigrpd/eigrp_fsm.c b/eigrpd/eigrp_fsm.c index 4d6d73e202..cc6d47f488 100644 --- a/eigrpd/eigrp_fsm.c +++ b/eigrpd/eigrp_fsm.c @@ -445,7 +445,7 @@ int eigrp_fsm_event_nq_fcn(struct eigrp_fsm_action_message *msg)  	prefix->rdistance = prefix->distance = prefix->fdistance = ne->distance;  	prefix->reported_metric = ne->total_metric; -	if (eigrp_nbr_count_get()) { +	if (eigrp_nbr_count_get(eigrp)) {  		prefix->req_action |= EIGRP_FSM_NEED_QUERY;  		listnode_add(eigrp->topology_changes_internalIPV4, prefix);  	} else { @@ -471,7 +471,7 @@ int eigrp_fsm_event_q_fcn(struct eigrp_fsm_action_message *msg)  	prefix->state = EIGRP_FSM_STATE_ACTIVE_3;  	prefix->rdistance = prefix->distance = prefix->fdistance = ne->distance;  	prefix->reported_metric = ne->total_metric; -	if (eigrp_nbr_count_get()) { +	if (eigrp_nbr_count_get(eigrp)) {  		prefix->req_action |= EIGRP_FSM_NEED_QUERY;  		listnode_add(eigrp->topology_changes_internalIPV4, prefix);  	} else { @@ -486,7 +486,7 @@ int eigrp_fsm_event_q_fcn(struct eigrp_fsm_action_message *msg)  int eigrp_fsm_event_keep_state(struct eigrp_fsm_action_message *msg)  { -	struct eigrp *eigrp; +	struct eigrp *eigrp = msg->eigrp;  	struct eigrp_prefix_entry *prefix = msg->prefix;  	struct eigrp_nexthop_entry *ne = listnode_head(prefix->entries); @@ -499,13 +499,11 @@ int eigrp_fsm_event_keep_state(struct eigrp_fsm_action_message *msg)  			if (msg->packet_type == EIGRP_OPC_QUERY)  				eigrp_send_reply(msg->adv_router, prefix);  			prefix->req_action |= EIGRP_FSM_NEED_UPDATE; -			eigrp = eigrp_lookup(); -			assert(eigrp);  			listnode_add(eigrp->topology_changes_internalIPV4,  				     prefix);  		} -		eigrp_topology_update_node_flags(prefix); -		eigrp_update_routing_table(prefix); +		eigrp_topology_update_node_flags(eigrp, prefix); +		eigrp_update_routing_table(eigrp, prefix);  	}  	if (msg->packet_type == EIGRP_OPC_QUERY) @@ -536,9 +534,10 @@ int eigrp_fsm_event_lr(struct eigrp_fsm_action_message *msg)  	prefix->state = EIGRP_FSM_STATE_PASSIVE;  	prefix->req_action |= EIGRP_FSM_NEED_UPDATE;  	listnode_add(eigrp->topology_changes_internalIPV4, prefix); -	eigrp_topology_update_node_flags(prefix); -	eigrp_update_routing_table(prefix); -	eigrp_update_topology_table_prefix(eigrp->topology_table, prefix); +	eigrp_topology_update_node_flags(eigrp, prefix); +	eigrp_update_routing_table(eigrp, prefix); +	eigrp_update_topology_table_prefix(eigrp, eigrp->topology_table, +					   prefix);  	return 1;  } @@ -588,9 +587,10 @@ int eigrp_fsm_event_lr_fcs(struct eigrp_fsm_action_message *msg)  	}  	prefix->req_action |= EIGRP_FSM_NEED_UPDATE;  	listnode_add(eigrp->topology_changes_internalIPV4, prefix); -	eigrp_topology_update_node_flags(prefix); -	eigrp_update_routing_table(prefix); -	eigrp_update_topology_table_prefix(eigrp->topology_table, prefix); +	eigrp_topology_update_node_flags(eigrp, prefix); +	eigrp_update_routing_table(eigrp, prefix); +	eigrp_update_topology_table_prefix(eigrp, eigrp->topology_table, +					   prefix);  	return 1;  } @@ -612,7 +612,7 @@ int eigrp_fsm_event_lr_fcn(struct eigrp_fsm_action_message *msg)  	prefix->rdistance = prefix->distance = best_successor->distance;  	prefix->reported_metric = best_successor->total_metric; -	if (eigrp_nbr_count_get()) { +	if (eigrp_nbr_count_get(eigrp)) {  		prefix->req_action |= EIGRP_FSM_NEED_QUERY;  		listnode_add(eigrp->topology_changes_internalIPV4, prefix);  	} else { diff --git a/eigrpd/eigrp_hello.c b/eigrpd/eigrp_hello.c index dacd5caeb5..6f93cd7b3e 100644 --- a/eigrpd/eigrp_hello.c +++ b/eigrpd/eigrp_hello.c @@ -147,7 +147,7 @@ eigrp_hello_parameter_decode(struct eigrp_neighbor *nbr,  			zlog_info("Neighbor %s (%s) is pending: new adjacency",  				  inet_ntoa(nbr->src),  				  ifindex2ifname(nbr->ei->ifp->ifindex, -						 VRF_DEFAULT)); +						 eigrp->vrf_id));  			/* Expedited hello sent */  			eigrp_hello_send(nbr->ei, EIGRP_HELLO_NORMAL, NULL); @@ -167,7 +167,7 @@ eigrp_hello_parameter_decode(struct eigrp_neighbor *nbr,  					"Neighbor %s (%s) is down: Interface PEER-TERMINATION received",  					inet_ntoa(nbr->src),  					ifindex2ifname(nbr->ei->ifp->ifindex, -						       VRF_DEFAULT)); +						       eigrp->vrf_id));  				eigrp_nbr_delete(nbr);  				return NULL;  			} else { @@ -175,7 +175,7 @@ eigrp_hello_parameter_decode(struct eigrp_neighbor *nbr,  					"Neighbor %s (%s) going down: Kvalue mismatch",  					inet_ntoa(nbr->src),  					ifindex2ifname(nbr->ei->ifp->ifindex, -						       VRF_DEFAULT)); +						       eigrp->vrf_id));  				eigrp_nbr_state_set(nbr, EIGRP_NEIGHBOR_DOWN);  			}  		} @@ -245,6 +245,7 @@ static void eigrp_sw_version_decode(struct eigrp_neighbor *nbr,  static void eigrp_peer_termination_decode(struct eigrp_neighbor *nbr,  					  struct eigrp_tlv_hdr_type *tlv)  { +	struct eigrp *eigrp = nbr->ei->eigrp;  	struct TLV_Peer_Termination_type *param =  		(struct TLV_Peer_Termination_type *)tlv; @@ -254,7 +255,7 @@ static void eigrp_peer_termination_decode(struct eigrp_neighbor *nbr,  	if (my_ip == received_ip) {  		zlog_info("Neighbor %s (%s) is down: Peer Termination received",  			  inet_ntoa(nbr->src), -			  ifindex2ifname(nbr->ei->ifp->ifindex, VRF_DEFAULT)); +			  ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id));  		/* set neighbor to DOWN */  		nbr->state = EIGRP_NEIGHBOR_DOWN;  		/* delete neighbor */ @@ -330,7 +331,7 @@ void eigrp_hello_receive(struct eigrp *eigrp, struct ip *iph,  	if (IS_DEBUG_EIGRP_PACKET(eigrph->opcode - 1, RECV))  		zlog_debug("Processing Hello size[%u] int(%s) nbr(%s)", size, -			   ifindex2ifname(nbr->ei->ifp->ifindex, VRF_DEFAULT), +			   ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id),  			   inet_ntoa(nbr->src));  	size -= EIGRP_HEADER_LEN; @@ -484,21 +485,15 @@ static uint16_t eigrp_tidlist_encode(struct stream *s)   * Part of conditional receive process   *   */ -static uint16_t eigrp_sequence_encode(struct stream *s) +static uint16_t eigrp_sequence_encode(struct eigrp *eigrp, struct stream *s)  {  	uint16_t length = EIGRP_TLV_SEQ_BASE_LEN; -	struct eigrp *eigrp;  	struct eigrp_interface *ei;  	struct listnode *node, *node2, *nnode2;  	struct eigrp_neighbor *nbr;  	size_t backup_end, size_end;  	int found; -	eigrp = eigrp_lookup(); -	if (eigrp == NULL) { -		return 0; -	} -  	// add in the parameters TLV  	backup_end = stream_get_endp(s);  	stream_putw(s, EIGRP_TLV_SEQ); @@ -541,15 +536,10 @@ static uint16_t eigrp_sequence_encode(struct stream *s)   * Part of conditional receive process   *   */ -static uint16_t eigrp_next_sequence_encode(struct stream *s) +static uint16_t eigrp_next_sequence_encode(struct eigrp *eigrp, +					   struct stream *s)  {  	uint16_t length = EIGRP_NEXT_SEQUENCE_TLV_SIZE; -	struct eigrp *eigrp; - -	eigrp = eigrp_lookup(); -	if (eigrp == NULL) { -		return 0; -	}  	// add in the parameters TLV  	stream_putw(s, EIGRP_TLV_NEXT_MCAST_SEQ); @@ -659,8 +649,8 @@ static struct eigrp_packet *eigrp_hello_encode(struct eigrp_interface *ei,  		length += eigrp_sw_version_encode(ep->s);  		if (flags & EIGRP_HELLO_ADD_SEQUENCE) { -			length += eigrp_sequence_encode(ep->s); -			length += eigrp_next_sequence_encode(ep->s); +			length += eigrp_sequence_encode(ei->eigrp, ep->s); +			length += eigrp_next_sequence_encode(ei->eigrp, ep->s);  		}  		// add in the TID list if doing multi-topology diff --git a/eigrpd/eigrp_interface.c b/eigrpd/eigrp_interface.c index c52a98ee25..fd1d3f5cb9 100644 --- a/eigrpd/eigrp_interface.c +++ b/eigrpd/eigrp_interface.c @@ -206,7 +206,7 @@ int eigrp_if_up(struct eigrp_interface *ei)  		eigrp_prefix_entry_add(eigrp->topology_table, pe);  		listnode_add(eigrp->topology_changes_internalIPV4, pe); -		eigrp_nexthop_entry_add(pe, ne); +		eigrp_nexthop_entry_add(eigrp, pe, ne);  		for (ALL_LIST_ELEMENTS(eigrp->eiflist, node, nnode, ei2)) {  			eigrp_update_send(ei2); @@ -218,7 +218,7 @@ int eigrp_if_up(struct eigrp_interface *ei)  		struct eigrp_fsm_action_message msg;  		ne->prefix = pe; -		eigrp_nexthop_entry_add(pe, ne); +		eigrp_nexthop_entry_add(eigrp, pe, ne);  		msg.packet_type = EIGRP_OPC_UPDATE;  		msg.eigrp = eigrp; @@ -329,10 +329,7 @@ void eigrp_if_free(struct eigrp_interface *ei, int source)  {  	struct prefix dest_addr;  	struct eigrp_prefix_entry *pe; -	struct eigrp *eigrp = eigrp_lookup(); - -	if (!eigrp) -		return; +	struct eigrp *eigrp = ei->eigrp;  	if (source == INTERFACE_DOWN_BY_VTY) {  		THREAD_OFF(ei->t_hello); @@ -344,7 +341,7 @@ void eigrp_if_free(struct eigrp_interface *ei, int source)  	pe = eigrp_topology_table_lookup_ipv4(eigrp->topology_table,  					      &dest_addr);  	if (pe) -		eigrp_prefix_entry_delete(eigrp->topology_table, pe); +		eigrp_prefix_entry_delete(eigrp, eigrp->topology_table, pe);  	eigrp_if_down(ei); diff --git a/eigrpd/eigrp_main.c b/eigrpd/eigrp_main.c index 1781a88173..299825dd1b 100644 --- a/eigrpd/eigrp_main.c +++ b/eigrpd/eigrp_main.c @@ -65,6 +65,7 @@  #include "eigrpd/eigrp_snmp.h"  #include "eigrpd/eigrp_filter.h"  #include "eigrpd/eigrp_errors.h" +#include "eigrpd/eigrp_vrf.h"  //#include "eigrpd/eigrp_routemap.h"  /* eigprd privileges */ @@ -182,6 +183,7 @@ int main(int argc, char **argv, char **envp)  	master = eigrp_om->master;  	eigrp_error_init(); +	eigrp_vrf_init();  	vrf_init(NULL, NULL, NULL, NULL, NULL);  	/*EIGRPd init*/ diff --git a/eigrpd/eigrp_neighbor.c b/eigrpd/eigrp_neighbor.c index 66dd5f3419..2ae3997fae 100644 --- a/eigrpd/eigrp_neighbor.c +++ b/eigrpd/eigrp_neighbor.c @@ -194,13 +194,12 @@ void eigrp_nbr_delete(struct eigrp_neighbor *nbr)  int holddown_timer_expired(struct thread *thread)  { -	struct eigrp_neighbor *nbr; - -	nbr = THREAD_ARG(thread); +	struct eigrp_neighbor *nbr = THREAD_ARG(thread); +	struct eigrp *eigrp = nbr->ei->eigrp;  	zlog_info("Neighbor %s (%s) is down: holding time expired",  		  inet_ntoa(nbr->src), -		  ifindex2ifname(nbr->ei->ifp->ifindex, VRF_DEFAULT)); +		  ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id));  	nbr->state = EIGRP_NEIGHBOR_DOWN;  	eigrp_nbr_delete(nbr); @@ -297,19 +296,13 @@ void eigrp_nbr_state_update(struct eigrp_neighbor *nbr)  	}  } -int eigrp_nbr_count_get(void) +int eigrp_nbr_count_get(struct eigrp *eigrp)  {  	struct eigrp_interface *iface;  	struct listnode *node, *node2, *nnode2;  	struct eigrp_neighbor *nbr; -	struct eigrp *eigrp = eigrp_lookup();  	uint32_t counter; -	if (eigrp == NULL) { -		zlog_debug("EIGRP Routing Process not enabled"); -		return 0; -	} -  	counter = 0;  	for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, iface)) {  		for (ALL_LIST_ELEMENTS(iface->nbrs, node2, nnode2, nbr)) { @@ -335,20 +328,16 @@ int eigrp_nbr_count_get(void)   */  void eigrp_nbr_hard_restart(struct eigrp_neighbor *nbr, struct vty *vty)  { -	if (nbr == NULL) { -		flog_err(EC_EIGRP_CONFIG, -			 "Nbr Hard restart: Neighbor not specified."); -		return; -	} +	struct eigrp *eigrp = nbr->ei->eigrp;  	zlog_debug("Neighbor %s (%s) is down: manually cleared",  		   inet_ntoa(nbr->src), -		   ifindex2ifname(nbr->ei->ifp->ifindex, VRF_DEFAULT)); +		   ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id));  	if (vty != NULL) {  		vty_time_print(vty, 0);  		vty_out(vty, "Neighbor %s (%s) is down: manually cleared\n",  			inet_ntoa(nbr->src), -			ifindex2ifname(nbr->ei->ifp->ifindex, VRF_DEFAULT)); +			ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id));  	}  	/* send Hello with Peer Termination TLV */ diff --git a/eigrpd/eigrp_neighbor.h b/eigrpd/eigrp_neighbor.h index 21a8c6a004..1c308fa981 100644 --- a/eigrpd/eigrp_neighbor.h +++ b/eigrpd/eigrp_neighbor.h @@ -33,24 +33,25 @@  #define _ZEBRA_EIGRP_NEIGHBOR_H  /* Prototypes */ -extern struct eigrp_neighbor *eigrp_nbr_get(struct eigrp_interface *, -					    struct eigrp_header *, struct ip *); -extern struct eigrp_neighbor *eigrp_nbr_new(struct eigrp_interface *); -extern void eigrp_nbr_delete(struct eigrp_neighbor *); +extern struct eigrp_neighbor *eigrp_nbr_get(struct eigrp_interface *ei, +					    struct eigrp_header *, +					    struct ip *addr); +extern struct eigrp_neighbor *eigrp_nbr_new(struct eigrp_interface *ei); +extern void eigrp_nbr_delete(struct eigrp_neighbor *neigh); -extern int holddown_timer_expired(struct thread *); +extern int holddown_timer_expired(struct thread *thread); -extern int eigrp_neighborship_check(struct eigrp_neighbor *, -				    struct TLV_Parameter_Type *); -extern void eigrp_nbr_state_update(struct eigrp_neighbor *); -extern void eigrp_nbr_state_set(struct eigrp_neighbor *, uint8_t state); -extern uint8_t eigrp_nbr_state_get(struct eigrp_neighbor *); -extern int eigrp_nbr_count_get(void); -extern const char *eigrp_nbr_state_str(struct eigrp_neighbor *); -extern struct eigrp_neighbor *eigrp_nbr_lookup_by_addr(struct eigrp_interface *, -						       struct in_addr *); -extern struct eigrp_neighbor *eigrp_nbr_lookup_by_addr_process(struct eigrp *, -							       struct in_addr); +extern int eigrp_neighborship_check(struct eigrp_neighbor *neigh, +				    struct TLV_Parameter_Type *tlv); +extern void eigrp_nbr_state_update(struct eigrp_neighbor *neigh); +extern void eigrp_nbr_state_set(struct eigrp_neighbor *neigh, uint8_t state); +extern uint8_t eigrp_nbr_state_get(struct eigrp_neighbor *neigh); +extern int eigrp_nbr_count_get(struct eigrp *eigrp); +extern const char *eigrp_nbr_state_str(struct eigrp_neighbor *neigh); +extern struct eigrp_neighbor * +eigrp_nbr_lookup_by_addr(struct eigrp_interface *ei, struct in_addr *addr); +extern struct eigrp_neighbor * +eigrp_nbr_lookup_by_addr_process(struct eigrp *eigrp, struct in_addr addr);  extern void eigrp_nbr_hard_restart(struct eigrp_neighbor *nbr, struct vty *vty);  extern int eigrp_nbr_split_horizon_check(struct eigrp_nexthop_entry *ne, diff --git a/eigrpd/eigrp_network.c b/eigrpd/eigrp_network.c index bbb9487b4d..c7ffbf9f0e 100644 --- a/eigrpd/eigrp_network.c +++ b/eigrpd/eigrp_network.c @@ -53,7 +53,7 @@ static int eigrp_network_match_iface(const struct prefix *connected_prefix,  static void eigrp_network_run_interface(struct eigrp *, struct prefix *,  					struct interface *); -int eigrp_sock_init(void) +int eigrp_sock_init(struct vrf *vrf)  {  	int eigrp_sock;  	int ret; @@ -61,8 +61,10 @@ int eigrp_sock_init(void)  	int hincl = 1;  #endif -	frr_elevate_privs(&eigrpd_privs) { -		eigrp_sock = socket(AF_INET, SOCK_RAW, IPPROTO_EIGRPIGP); +	frr_with_privs(&eigrpd_privs) { +		eigrp_sock = vrf_socket( +			AF_INET, SOCK_RAW, IPPROTO_EIGRPIGP, vrf->vrf_id, +			vrf->vrf_id != VRF_DEFAULT ? vrf->name : NULL);  		if (eigrp_sock < 0) {  			zlog_err("eigrp_read_sock_init: socket: %s",  				 safe_strerror(errno)); @@ -209,7 +211,7 @@ int eigrp_if_drop_allspfrouters(struct eigrp *top, struct prefix *p,  int eigrp_network_set(struct eigrp *eigrp, struct prefix *p)  { -	struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); +	struct vrf *vrf = vrf_lookup_by_id(eigrp->vrf_id);  	struct route_node *rn;  	struct interface *ifp; @@ -290,6 +292,9 @@ void eigrp_if_update(struct interface *ifp)  	 * we need to check eac one and add the interface as approperate  	 */  	for (ALL_LIST_ELEMENTS(eigrp_om->eigrp, node, nnode, eigrp)) { +		if (ifp->vrf_id != eigrp->vrf_id) +			continue; +  		/* EIGRP must be on and Router-ID must be configured. */  		if (eigrp->router_id.s_addr == 0)  			continue; diff --git a/eigrpd/eigrp_network.h b/eigrpd/eigrp_network.h index b3c76bbecc..7839fc946b 100644 --- a/eigrpd/eigrp_network.h +++ b/eigrpd/eigrp_network.h @@ -30,7 +30,7 @@  /* Prototypes */ -extern int eigrp_sock_init(void); +extern int eigrp_sock_init(struct vrf *vrf);  extern int eigrp_if_ipmulticast(struct eigrp *, struct prefix *, unsigned int);  extern int eigrp_network_set(struct eigrp *eigrp, struct prefix *p);  extern int eigrp_network_unset(struct eigrp *eigrp, struct prefix *p); diff --git a/eigrpd/eigrp_northbound.c b/eigrpd/eigrp_northbound.c index 5f0c91809e..4ccce2ebb8 100644 --- a/eigrpd/eigrp_northbound.c +++ b/eigrpd/eigrp_northbound.c @@ -79,13 +79,18 @@ static int eigrpd_instance_create(enum nb_event event,  				  union nb_resource *resource)  {  	struct eigrp *eigrp; +	const char *vrf; +	vrf_id_t vrfid;  	switch (event) {  	case NB_EV_VALIDATE:  		/* NOTHING */  		break;  	case NB_EV_PREPARE: -		eigrp = eigrp_get(yang_dnode_get_string(dnode, "./asn")); +		vrf = yang_dnode_get_string(dnode, "./vrf"); +		vrfid = vrf_name_to_id(vrf); + +		eigrp = eigrp_get(yang_dnode_get_uint16(dnode, "./asn"), vrfid);  		resource->ptr = eigrp;  		break;  	case NB_EV_ABORT: @@ -745,14 +750,17 @@ static int eigrpd_instance_redistribute_create(enum nb_event event,  					       union nb_resource *resource)  {  	struct eigrp_metrics metrics; +	const char *vrfname;  	struct eigrp *eigrp;  	uint32_t proto; +	vrf_id_t vrfid;  	switch (event) {  	case NB_EV_VALIDATE:  		proto = yang_dnode_get_enum(dnode, "./protocol"); -		if (vrf_bitmap_check(zclient->redist[AFI_IP][proto], -				     VRF_DEFAULT)) +		vrfname = yang_dnode_get_string(dnode, "../vrf"); +		vrfid = vrf_name_to_id(vrfname); +		if (vrf_bitmap_check(zclient->redist[AFI_IP][proto], vrfid))  			return NB_ERR_INCONSISTENCY;  		break;  	case NB_EV_PREPARE: @@ -1181,7 +1189,8 @@ static int lib_interface_eigrp_instance_create(enum nb_event event,  			break;  		} -		eigrp = eigrp_get(yang_dnode_get_string(dnode, "./asn")); +		eigrp = eigrp_get(yang_dnode_get_uint16(dnode, "./asn"), +				  ifp->vrf_id);  		eif = eigrp_interface_lookup(eigrp, ifp->name);  		if (eif == NULL)  			return NB_ERR_INCONSISTENCY; @@ -1192,7 +1201,8 @@ static int lib_interface_eigrp_instance_create(enum nb_event event,  		break;  	case NB_EV_APPLY:  		ifp = nb_running_get_entry(dnode, NULL, true); -		eigrp = eigrp_get(yang_dnode_get_string(dnode, "./asn")); +		eigrp = eigrp_get(yang_dnode_get_uint16(dnode, "./asn"), +				  ifp->vrf_id);  		eif = eigrp_interface_lookup(eigrp, ifp->name);  		if (eif == NULL)  			return NB_ERR_INCONSISTENCY; diff --git a/eigrpd/eigrp_packet.c b/eigrpd/eigrp_packet.c index 4efb91e4a0..ba8271d46e 100644 --- a/eigrpd/eigrp_packet.c +++ b/eigrpd/eigrp_packet.c @@ -77,11 +77,13 @@ const struct message eigrp_packet_type_str[] = {  static unsigned char zeropad[16] = {0};  /* Forward function reference*/ -static struct stream *eigrp_recv_packet(int, struct interface **, -					struct stream *); -static int eigrp_verify_header(struct stream *, struct eigrp_interface *, -			       struct ip *, struct eigrp_header *); -static int eigrp_check_network_mask(struct eigrp_interface *, struct in_addr); +static struct stream *eigrp_recv_packet(struct eigrp *eigrp, int fd, +					struct interface **ifp, +					struct stream *s); +static int eigrp_verify_header(struct stream *s, struct eigrp_interface *ei, +			       struct ip *addr, struct eigrp_header *header); +static int eigrp_check_network_mask(struct eigrp_interface *ei, +				    struct in_addr mask);  static int eigrp_retrans_count_exceeded(struct eigrp_packet *ep,  					struct eigrp_neighbor *nbr) @@ -495,7 +497,7 @@ int eigrp_read(struct thread *thread)  	thread_add_read(master, eigrp_read, eigrp, eigrp->fd, &eigrp->t_read);  	stream_reset(eigrp->ibuf); -	if (!(ibuf = eigrp_recv_packet(eigrp->fd, &ifp, eigrp->ibuf))) { +	if (!(ibuf = eigrp_recv_packet(eigrp, eigrp->fd, &ifp, eigrp->ibuf))) {  		/* This raw packet is known to be at least as big as its IP  		 * header. */  		return -1; @@ -525,7 +527,7 @@ int eigrp_read(struct thread *thread)  		   ifindex  		   retrieval but do not. */  		c = if_lookup_address((void *)&iph->ip_src, AF_INET, -				      VRF_DEFAULT); +				      eigrp->vrf_id);  		if (c == NULL)  			return 0; @@ -706,7 +708,8 @@ int eigrp_read(struct thread *thread)  	return 0;  } -static struct stream *eigrp_recv_packet(int fd, struct interface **ifp, +static struct stream *eigrp_recv_packet(struct eigrp *eigrp, +					int fd, struct interface **ifp,  					struct stream *ibuf)  {  	int ret; @@ -774,7 +777,7 @@ static struct stream *eigrp_recv_packet(int fd, struct interface **ifp,  	ifindex = getsockopt_ifindex(AF_INET, &msgh); -	*ifp = if_lookup_by_index(ifindex, VRF_DEFAULT); +	*ifp = if_lookup_by_index(ifindex, eigrp->vrf_id);  	if (ret != ip_len) {  		zlog_warn( diff --git a/eigrpd/eigrp_routemap.c b/eigrpd/eigrp_routemap.c index bac7494774..d78588644f 100644 --- a/eigrpd/eigrp_routemap.c +++ b/eigrpd/eigrp_routemap.c @@ -135,7 +135,8 @@ void eigrp_rmap_update(const char *notused)  static int eigrp_route_match_add(struct vty *vty, struct route_map_index *index,  				 const char *command, const char *arg)  { -	int ret; +	enum rmap_compile_rets ret; +  	ret = route_map_add_match(index, command, arg, type);  	switch (ret) {  	case RMAP_RULE_MISSING: @@ -147,6 +148,10 @@ static int eigrp_route_match_add(struct vty *vty, struct route_map_index *index,  		return CMD_WARNING_CONFIG_FAILED;  		break;  	case RMAP_COMPILE_SUCCESS: +	case RMAP_DUPLICATE_RULE: +		/* +		 * Intentionally not handling these cases +		 */  		break;  	} @@ -158,7 +163,8 @@ static int eigrp_route_match_delete(struct vty *vty,  				    struct route_map_index *index,  				    const char *command, const char *arg)  { -	int ret; +	enum rmap_compile_rets ret; +  	ret = route_map_delete_match(index, command, arg);  	switch (ret) {  	case RMAP_RULE_MISSING: @@ -170,6 +176,10 @@ static int eigrp_route_match_delete(struct vty *vty,  		return CMD_WARNING_CONFIG_FAILED;  		break;  	case RMAP_COMPILE_SUCCESS: +	case RMAP_DUPLICATE_RULE: +		/* +		 * These cases intentionally ignored +		 */  		break;  	} @@ -180,7 +190,7 @@ static int eigrp_route_match_delete(struct vty *vty,  static int eigrp_route_set_add(struct vty *vty, struct route_map_index *index,  			       const char *command, const char *arg)  { -	int ret; +	enum rmap_compile_rets ret;  	ret = route_map_add_set(index, command, arg);  	switch (ret) { @@ -201,6 +211,10 @@ static int eigrp_route_set_add(struct vty *vty, struct route_map_index *index,  		}  		break;  	case RMAP_COMPILE_SUCCESS: +	case RMAP_DUPLICATE_RULE: +		/* +		 * These cases intentionally left blank here +		 */  		break;  	} @@ -212,7 +226,7 @@ static int eigrp_route_set_delete(struct vty *vty,  				  struct route_map_index *index,  				  const char *command, const char *arg)  { -	int ret; +	enum rmap_compile_rets ret;  	ret = route_map_delete_set(index, command, arg);  	switch (ret) { @@ -225,6 +239,10 @@ static int eigrp_route_set_delete(struct vty *vty,  		return CMD_WARNING_CONFIG_FAILED;  		break;  	case RMAP_COMPILE_SUCCESS: +	case RMAP_DUPLICATE_RULE: +		/* +		 * These cases intentionally not handled +		 */  		break;  	} diff --git a/eigrpd/eigrp_structs.h b/eigrpd/eigrp_structs.h index 1b9186f011..e50858f071 100644 --- a/eigrpd/eigrp_structs.h +++ b/eigrpd/eigrp_structs.h @@ -69,6 +69,8 @@ struct eigrp_metrics {  };  struct eigrp { +	vrf_id_t vrf_id; +  	uint16_t AS;	 /* Autonomous system number */  	uint16_t vrid;       /* Virtual Router ID */  	uint8_t k_values[6]; /*Array for K values configuration*/ @@ -85,7 +87,7 @@ struct eigrp {  	struct list *eiflist;		  /* eigrp interfaces */  	uint8_t passive_interface_default; /* passive-interface default */ -	unsigned int fd; +	int fd;  	unsigned int maxsndbuflen;  	uint32_t sequence_number; /*Global EIGRP sequence number*/ diff --git a/eigrpd/eigrp_topology.c b/eigrpd/eigrp_topology.c index e861cdb333..9cc612eaf1 100644 --- a/eigrpd/eigrp_topology.c +++ b/eigrpd/eigrp_topology.c @@ -117,9 +117,9 @@ struct eigrp_nexthop_entry *eigrp_nexthop_entry_new(void)  /*   * Freeing topology table list   */ -void eigrp_topology_free(struct route_table *table) +void eigrp_topology_free(struct eigrp *eigrp, struct route_table *table)  { -	eigrp_topology_delete_all(table); +	eigrp_topology_delete_all(eigrp, table);  	route_table_finish(table);  } @@ -150,7 +150,8 @@ void eigrp_prefix_entry_add(struct route_table *topology,  /*   * Adding topology entry to topology node   */ -void eigrp_nexthop_entry_add(struct eigrp_prefix_entry *node, +void eigrp_nexthop_entry_add(struct eigrp *eigrp, +			     struct eigrp_prefix_entry *node,  			     struct eigrp_nexthop_entry *entry)  {  	struct list *l = list_new(); @@ -161,7 +162,8 @@ void eigrp_nexthop_entry_add(struct eigrp_prefix_entry *node,  		listnode_add_sort(node->entries, entry);  		entry->prefix = node; -		eigrp_zebra_route_add(node->destination, l, node->fdistance); +		eigrp_zebra_route_add(eigrp, node->destination, +				      l, node->fdistance);  	}  	list_delete(&l); @@ -170,10 +172,9 @@ void eigrp_nexthop_entry_add(struct eigrp_prefix_entry *node,  /*   * Deleting topology node from topology table   */ -void eigrp_prefix_entry_delete(struct route_table *table, +void eigrp_prefix_entry_delete(struct eigrp *eigrp, struct route_table *table,  			       struct eigrp_prefix_entry *pe)  { -	struct eigrp *eigrp = eigrp_lookup();  	struct eigrp_nexthop_entry *ne;  	struct listnode *node, *nnode;  	struct route_node *rn; @@ -192,10 +193,10 @@ void eigrp_prefix_entry_delete(struct route_table *table,  	listnode_delete(eigrp->topology_changes_internalIPV4, pe);  	for (ALL_LIST_ELEMENTS(pe->entries, node, nnode, ne)) -		eigrp_nexthop_entry_delete(pe, ne); +		eigrp_nexthop_entry_delete(eigrp, pe, ne);  	list_delete(&pe->entries);  	list_delete(&pe->rij); -	eigrp_zebra_route_delete(pe->destination); +	eigrp_zebra_route_delete(eigrp, pe->destination);  	prefix_free(pe->destination);  	rn->info = NULL; @@ -207,12 +208,13 @@ void eigrp_prefix_entry_delete(struct route_table *table,  /*   * Deleting topology entry from topology node   */ -void eigrp_nexthop_entry_delete(struct eigrp_prefix_entry *node, +void eigrp_nexthop_entry_delete(struct eigrp *eigrp, +				struct eigrp_prefix_entry *node,  				struct eigrp_nexthop_entry *entry)  {  	if (listnode_lookup(node->entries, entry) != NULL) {  		listnode_delete(node->entries, entry); -		eigrp_zebra_route_delete(node->destination); +		eigrp_zebra_route_delete(eigrp, node->destination);  		XFREE(MTYPE_EIGRP_NEXTHOP_ENTRY, entry);  	}  } @@ -220,7 +222,8 @@ void eigrp_nexthop_entry_delete(struct eigrp_prefix_entry *node,  /*   * Deleting all nodes from topology table   */ -void eigrp_topology_delete_all(struct route_table *topology) +void eigrp_topology_delete_all(struct eigrp *eigrp, +			       struct route_table *topology)  {  	struct route_node *rn;  	struct eigrp_prefix_entry *pe; @@ -231,7 +234,7 @@ void eigrp_topology_delete_all(struct route_table *topology)  		if (!pe)  			continue; -		eigrp_prefix_entry_delete(topology, pe); +		eigrp_prefix_entry_delete(eigrp, topology, pe);  	}  } @@ -426,17 +429,15 @@ void eigrp_topology_update_all_node_flags(struct eigrp *eigrp)  		if (!pe)  			continue; -		eigrp_topology_update_node_flags(pe); +		eigrp_topology_update_node_flags(eigrp, pe);  	}  } -void eigrp_topology_update_node_flags(struct eigrp_prefix_entry *dest) +void eigrp_topology_update_node_flags(struct eigrp *eigrp, +				      struct eigrp_prefix_entry *dest)  {  	struct listnode *node;  	struct eigrp_nexthop_entry *entry; -	struct eigrp *eigrp = eigrp_lookup(); - -	assert(eigrp);  	for (ALL_LIST_ELEMENTS_RO(dest->entries, node, entry)) {  		if (entry->reported_distance < dest->fdistance) { @@ -464,27 +465,24 @@ void eigrp_topology_update_node_flags(struct eigrp_prefix_entry *dest)  	}  } -void eigrp_update_routing_table(struct eigrp_prefix_entry *prefix) +void eigrp_update_routing_table(struct eigrp *eigrp, +				struct eigrp_prefix_entry *prefix)  { -	struct eigrp *eigrp = eigrp_lookup();  	struct list *successors;  	struct listnode *node;  	struct eigrp_nexthop_entry *entry; -	if (!eigrp) -		return; -  	successors = eigrp_topology_get_successor_max(prefix, eigrp->max_paths);  	if (successors) { -		eigrp_zebra_route_add(prefix->destination, successors, +		eigrp_zebra_route_add(eigrp, prefix->destination, successors,  				      prefix->fdistance);  		for (ALL_LIST_ELEMENTS_RO(successors, node, entry))  			entry->flags |= EIGRP_NEXTHOP_ENTRY_INTABLE_FLAG;  		list_delete(&successors);  	} else { -		eigrp_zebra_route_delete(prefix->destination); +		eigrp_zebra_route_delete(eigrp, prefix->destination);  		for (ALL_LIST_ELEMENTS_RO(prefix->entries, node, entry))  			entry->flags &= ~EIGRP_NEXTHOP_ENTRY_INTABLE_FLAG;  	} @@ -525,7 +523,8 @@ void eigrp_topology_neighbor_down(struct eigrp *eigrp,  	eigrp_update_send_all(eigrp, nbr->ei);  } -void eigrp_update_topology_table_prefix(struct route_table *table, +void eigrp_update_topology_table_prefix(struct eigrp *eigrp, +					struct route_table *table,  					struct eigrp_prefix_entry *prefix)  {  	struct listnode *node1, *node2; @@ -533,11 +532,11 @@ void eigrp_update_topology_table_prefix(struct route_table *table,  	struct eigrp_nexthop_entry *entry;  	for (ALL_LIST_ELEMENTS(prefix->entries, node1, node2, entry)) {  		if (entry->distance == EIGRP_MAX_METRIC) { -			eigrp_nexthop_entry_delete(prefix, entry); +			eigrp_nexthop_entry_delete(eigrp, prefix, entry);  		}  	}  	if (prefix->distance == EIGRP_MAX_METRIC  	    && prefix->nt != EIGRP_TOPOLOGY_TYPE_CONNECTED) { -		eigrp_prefix_entry_delete(table, prefix); +		eigrp_prefix_entry_delete(eigrp, table, prefix);  	}  } diff --git a/eigrpd/eigrp_topology.h b/eigrpd/eigrp_topology.h index 16bf2261cc..718cece403 100644 --- a/eigrpd/eigrp_topology.h +++ b/eigrpd/eigrp_topology.h @@ -37,34 +37,41 @@ extern struct route_table *eigrp_topology_new(void);  extern void eigrp_topology_init(struct route_table *table);  extern struct eigrp_prefix_entry *eigrp_prefix_entry_new(void);  extern struct eigrp_nexthop_entry *eigrp_nexthop_entry_new(void); -extern void eigrp_topology_free(struct route_table *table); +extern void eigrp_topology_free(struct eigrp *eigrp, struct route_table *table);  extern void eigrp_prefix_entry_add(struct route_table *table,  				   struct eigrp_prefix_entry *pe); -extern void eigrp_nexthop_entry_add(struct eigrp_prefix_entry *, -				    struct eigrp_nexthop_entry *); -extern void eigrp_prefix_entry_delete(struct route_table *table, +extern void eigrp_nexthop_entry_add(struct eigrp *eigrp, +				    struct eigrp_prefix_entry *pe, +				    struct eigrp_nexthop_entry *ne); +extern void eigrp_prefix_entry_delete(struct eigrp *eigrp, +				      struct route_table *table,  				      struct eigrp_prefix_entry *pe); -extern void eigrp_nexthop_entry_delete(struct eigrp_prefix_entry *, -				       struct eigrp_nexthop_entry *); -extern void eigrp_topology_delete_all(struct route_table *table); +extern void eigrp_nexthop_entry_delete(struct eigrp *eigrp, +				       struct eigrp_prefix_entry *pe, +				       struct eigrp_nexthop_entry *ne); +extern void eigrp_topology_delete_all(struct eigrp *eigrp, +				      struct route_table *table);  extern struct eigrp_prefix_entry *  eigrp_topology_table_lookup_ipv4(struct route_table *table, struct prefix *p); -extern struct list *eigrp_topology_get_successor(struct eigrp_prefix_entry *); +extern struct list *eigrp_topology_get_successor(struct eigrp_prefix_entry *pe);  extern struct list *  eigrp_topology_get_successor_max(struct eigrp_prefix_entry *pe,  				 unsigned int maxpaths);  extern struct eigrp_nexthop_entry * -eigrp_prefix_entry_lookup(struct list *, struct eigrp_neighbor *); -extern struct list *eigrp_neighbor_prefixes_lookup(struct eigrp *, -						   struct eigrp_neighbor *); -extern void eigrp_topology_update_all_node_flags(struct eigrp *); -extern void eigrp_topology_update_node_flags(struct eigrp_prefix_entry *); +eigrp_prefix_entry_lookup(struct list *entries, struct eigrp_neighbor *neigh); +extern struct list *eigrp_neighbor_prefixes_lookup(struct eigrp *eigrp, +						   struct eigrp_neighbor *n); +extern void eigrp_topology_update_all_node_flags(struct eigrp *eigrp); +extern void eigrp_topology_update_node_flags(struct eigrp *eigrp, +					     struct eigrp_prefix_entry *pe);  extern enum metric_change -eigrp_topology_update_distance(struct eigrp_fsm_action_message *); -extern void eigrp_update_routing_table(struct eigrp_prefix_entry *); -extern void eigrp_topology_neighbor_down(struct eigrp *, -					 struct eigrp_neighbor *); -extern void eigrp_update_topology_table_prefix(struct route_table *table, +eigrp_topology_update_distance(struct eigrp_fsm_action_message *msg); +extern void eigrp_update_routing_table(struct eigrp *eigrp, +				       struct eigrp_prefix_entry *pe); +extern void eigrp_topology_neighbor_down(struct eigrp *eigrp, +					 struct eigrp_neighbor *neigh); +extern void eigrp_update_topology_table_prefix(struct eigrp *eigrp, +					       struct route_table *table,  					       struct eigrp_prefix_entry *pe);  #endif diff --git a/eigrpd/eigrp_update.c b/eigrpd/eigrp_update.c index 8db4903077..6e2a81e32a 100644 --- a/eigrpd/eigrp_update.c +++ b/eigrpd/eigrp_update.c @@ -211,7 +211,7 @@ void eigrp_update_receive(struct eigrp *eigrp, struct ip *iph,  		zlog_debug(  			"Processing Update size[%u] int(%s) nbr(%s) seq [%u] flags [%0x]",  			size, -			ifindex2ifname(nbr->ei->ifp->ifindex, VRF_DEFAULT), +			ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id),  			inet_ntoa(nbr->src), nbr->recv_sequence_number, flags); @@ -221,7 +221,7 @@ void eigrp_update_receive(struct eigrp *eigrp, struct ip *iph,  		zlog_info("Neighbor %s (%s) is resync: peer graceful-restart",  			  inet_ntoa(nbr->src), -			  ifindex2ifname(nbr->ei->ifp->ifindex, VRF_DEFAULT)); +			  ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id));  		/* get all prefixes from neighbor from topology table */  		nbr_prefixes = eigrp_neighbor_prefixes_lookup(eigrp, nbr); @@ -233,7 +233,7 @@ void eigrp_update_receive(struct eigrp *eigrp, struct ip *iph,  		zlog_info("Neighbor %s (%s) is resync: peer graceful-restart",  			  inet_ntoa(nbr->src), -			  ifindex2ifname(nbr->ei->ifp->ifindex, VRF_DEFAULT)); +			  ifindex2ifname(nbr->ei->ifp->ifindex, eigrp->vrf_id));  		/* get all prefixes from neighbor from topology table */  		nbr_prefixes = eigrp_neighbor_prefixes_lookup(eigrp, nbr); @@ -282,12 +282,12 @@ void eigrp_update_receive(struct eigrp *eigrp, struct ip *iph,  			zlog_info("Neighbor %s (%s) is down: peer restarted",  				  inet_ntoa(nbr->src),  				  ifindex2ifname(nbr->ei->ifp->ifindex, -						 VRF_DEFAULT)); +						 eigrp->vrf_id));  			eigrp_nbr_state_set(nbr, EIGRP_NEIGHBOR_PENDING);  			zlog_info("Neighbor %s (%s) is pending: new adjacency",  				  inet_ntoa(nbr->src),  				  ifindex2ifname(nbr->ei->ifp->ifindex, -						 VRF_DEFAULT)); +						 eigrp->vrf_id));  			eigrp_update_send_init(nbr);  		}  	} @@ -366,11 +366,11 @@ void eigrp_update_receive(struct eigrp *eigrp, struct ip *iph,  				eigrp_prefix_entry_add(eigrp->topology_table,  						       pe); -				eigrp_nexthop_entry_add(pe, ne); +				eigrp_nexthop_entry_add(eigrp, pe, ne);  				pe->distance = pe->fdistance = pe->rdistance =  					ne->distance;  				pe->reported_metric = ne->total_metric; -				eigrp_topology_update_node_flags(pe); +				eigrp_topology_update_node_flags(eigrp, pe);  				pe->req_action |= EIGRP_FSM_NEED_UPDATE;  				listnode_add( @@ -965,19 +965,20 @@ void eigrp_update_send_GR(struct eigrp_neighbor *nbr, enum GR_type gr_type,  		zlog_info(  			"Neighbor %s (%s) is resync: route configuration changed",  			inet_ntoa(nbr->src), -			ifindex2ifname(ei->ifp->ifindex, VRF_DEFAULT)); +			ifindex2ifname(ei->ifp->ifindex, eigrp->vrf_id));  	} else if (gr_type == EIGRP_GR_MANUAL) {  		/* Graceful restart was called manually */  		zlog_info("Neighbor %s (%s) is resync: manually cleared",  			  inet_ntoa(nbr->src), -			  ifindex2ifname(ei->ifp->ifindex, VRF_DEFAULT)); +			  ifindex2ifname(ei->ifp->ifindex, eigrp->vrf_id));  		if (vty != NULL) {  			vty_time_print(vty, 0);  			vty_out(vty,  				"Neighbor %s (%s) is resync: manually cleared\n",  				inet_ntoa(nbr->src), -				ifindex2ifname(ei->ifp->ifindex, VRF_DEFAULT)); +				ifindex2ifname(ei->ifp->ifindex, +					       eigrp->vrf_id));  		}  	} diff --git a/eigrpd/eigrp_vrf.c b/eigrpd/eigrp_vrf.c new file mode 100644 index 0000000000..c8c8491056 --- /dev/null +++ b/eigrpd/eigrp_vrf.c @@ -0,0 +1,50 @@ +/* + * eigrp - vrf code + * Copyright (C) 2019 Cumulus Networks, Inc. + *               Donald Sharp + * + * 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 "vrf.h" + +#include "eigrpd/eigrp_vrf.h" + +static int eigrp_vrf_new(struct vrf *vrf) +{ +	return 0; +} + +static int eigrp_vrf_enable(struct vrf *vrf) +{ +	return 0; +} + +static int eigrp_vrf_disable(struct vrf *vrf) +{ +	return 0; +} + +static int eigrp_vrf_delete(struct vrf *vrf) +{ +	return 0; +} + +void eigrp_vrf_init(void) +{ +	vrf_init(eigrp_vrf_new, eigrp_vrf_enable, +		 eigrp_vrf_disable, eigrp_vrf_delete, NULL); +} diff --git a/eigrpd/eigrp_vrf.h b/eigrpd/eigrp_vrf.h new file mode 100644 index 0000000000..423a4be551 --- /dev/null +++ b/eigrpd/eigrp_vrf.h @@ -0,0 +1,23 @@ +/* + * eigrp - vrf code + * Copyright (C) 2019 Cumulus Networks, Inc. + *               Donald Sharp + * + * 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 __EIGRP_VRF_H__ + +extern void eigrp_vrf_init(void); +#endif diff --git a/eigrpd/eigrp_vty.c b/eigrpd/eigrp_vty.c index 207622e659..e4a499425b 100644 --- a/eigrpd/eigrp_vty.c +++ b/eigrpd/eigrp_vty.c @@ -83,12 +83,31 @@ static void eigrp_vty_display_prefix_entry(struct vty *vty,  	}  } +static struct eigrp *eigrp_vty_get_eigrp(struct vty *vty, const char *vrf_name) +{ +	struct vrf *vrf; + +	if (vrf_name) +		vrf = vrf_lookup_by_name(vrf_name); +	else +		vrf = vrf_lookup_by_id(VRF_DEFAULT); + +	if (!vrf) { +		vty_out(vty, "VRF %s specified does not exist", +			vrf_name ? vrf_name : VRF_DEFAULT_NAME); +		return NULL; +	} + +	return eigrp_lookup(vrf->vrf_id); +} +  DEFPY (show_ip_eigrp_topology_all,         show_ip_eigrp_topology_all_cmd, -       "show ip eigrp topology [all-links$all]", +       "show ip eigrp [vrf NAME] topology [all-links$all]",         SHOW_STR         IP_STR         "IP-EIGRP show commands\n" +       VRF_CMD_HELP_STR         "IP-EIGRP topology\n"         "Show all links in topology table\n")  { @@ -96,7 +115,7 @@ DEFPY (show_ip_eigrp_topology_all,  	struct eigrp_prefix_entry *tn;  	struct route_node *rn; -	eigrp = eigrp_lookup(); +	eigrp = eigrp_vty_get_eigrp(vty, vrf);  	if (eigrp == NULL) {  		vty_out(vty, " EIGRP Routing Process not enabled\n");  		return CMD_SUCCESS; @@ -119,10 +138,11 @@ DEFPY (show_ip_eigrp_topology_all,  DEFPY (show_ip_eigrp_topology,         show_ip_eigrp_topology_cmd, -       "show ip eigrp topology <A.B.C.D$address|A.B.C.D/M$prefix>", +       "show ip eigrp [vrf NAME] topology <A.B.C.D$address|A.B.C.D/M$prefix>",         SHOW_STR         IP_STR         "IP-EIGRP show commands\n" +       VRF_CMD_HELP_STR         "IP-EIGRP topology\n"         "For a specific address\n"         "For a specific prefix\n") @@ -132,7 +152,7 @@ DEFPY (show_ip_eigrp_topology,  	struct route_node *rn;  	struct prefix cmp; -	eigrp = eigrp_lookup(); +	eigrp = eigrp_vty_get_eigrp(vty, vrf);  	if (eigrp == NULL) {  		vty_out(vty, " EIGRP Routing Process not enabled\n");  		return CMD_SUCCESS; @@ -167,12 +187,13 @@ DEFPY (show_ip_eigrp_topology,  	return CMD_SUCCESS;  } -DEFUN (show_ip_eigrp_interfaces, +DEFPY (show_ip_eigrp_interfaces,         show_ip_eigrp_interfaces_cmd, -       "show ip eigrp interfaces [IFNAME] [detail]", +       "show ip eigrp [vrf NAME] interfaces [IFNAME] [detail]$detail",         SHOW_STR         IP_STR         "IP-EIGRP show commands\n" +       VRF_CMD_HELP_STR         "IP-EIGRP interfaces\n"         "Interface name to look at\n"         "Detailed information\n") @@ -180,22 +201,13 @@ DEFUN (show_ip_eigrp_interfaces,  	struct eigrp_interface *ei;  	struct eigrp *eigrp;  	struct listnode *node; -	int idx = 0; -	bool detail = false; -	const char *ifname = NULL; -	eigrp = eigrp_lookup(); +	eigrp = eigrp_vty_get_eigrp(vty, vrf);  	if (eigrp == NULL) {  		vty_out(vty, "EIGRP Routing Process not enabled\n");  		return CMD_SUCCESS;  	} -	if (argv_find(argv, argc, "IFNAME", &idx)) -		ifname = argv[idx]->arg; - -	if (argv_find(argv, argc, "detail", &idx)) -		detail = true; -  	if (!ifname)  		show_ip_eigrp_interface_header(vty, eigrp); @@ -210,12 +222,13 @@ DEFUN (show_ip_eigrp_interfaces,  	return CMD_SUCCESS;  } -DEFUN (show_ip_eigrp_neighbors, +DEFPY (show_ip_eigrp_neighbors,         show_ip_eigrp_neighbors_cmd, -       "show ip eigrp neighbors [IFNAME] [detail]", +       "show ip eigrp [vrf NAME] neighbors [IFNAME] [detail]$detail",         SHOW_STR         IP_STR         "IP-EIGRP show commands\n" +       VRF_CMD_HELP_STR         "IP-EIGRP neighbors\n"         "Interface to show on\n"         "Detailed Information\n") @@ -224,21 +237,13 @@ DEFUN (show_ip_eigrp_neighbors,  	struct eigrp_interface *ei;  	struct listnode *node, *node2, *nnode2;  	struct eigrp_neighbor *nbr; -	bool detail = false; -	int idx = 0; -	const char *ifname = NULL; -	eigrp = eigrp_lookup(); +	eigrp = eigrp_vty_get_eigrp(vty, vrf);  	if (eigrp == NULL) {  		vty_out(vty, " EIGRP Routing Process not enabled\n");  		return CMD_SUCCESS;  	} -	if (argv_find(argv, argc, "IFNAME", &idx)) -		ifname = argv[idx]->arg; - -	detail = (argv_find(argv, argc, "detail", &idx)); -  	show_ip_eigrp_neighbor_header(vty, eigrp);  	for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, ei)) { @@ -246,7 +251,7 @@ DEFUN (show_ip_eigrp_neighbors,  			for (ALL_LIST_ELEMENTS(ei->nbrs, node2, nnode2, nbr)) {  				if (detail || (nbr->state == EIGRP_NEIGHBOR_UP))  					show_ip_eigrp_neighbor_sub(vty, nbr, -								   detail); +								   !!detail);  			}  		}  	} @@ -257,12 +262,13 @@ DEFUN (show_ip_eigrp_neighbors,  /*   * Execute hard restart for all neighbors   */ -DEFUN (clear_ip_eigrp_neighbors, +DEFPY (clear_ip_eigrp_neighbors,         clear_ip_eigrp_neighbors_cmd, -       "clear ip eigrp neighbors", +       "clear ip eigrp [vrf NAME] neighbors",         CLEAR_STR         IP_STR         "Clear IP-EIGRP\n" +       VRF_CMD_HELP_STR         "Clear IP-EIGRP neighbors\n")  {  	struct eigrp *eigrp; @@ -271,7 +277,7 @@ DEFUN (clear_ip_eigrp_neighbors,  	struct eigrp_neighbor *nbr;  	/* Check if eigrp process is enabled */ -	eigrp = eigrp_lookup(); +	eigrp = eigrp_vty_get_eigrp(vty, vrf);  	if (eigrp == NULL) {  		vty_out(vty, " EIGRP Routing Process not enabled\n");  		return CMD_SUCCESS; @@ -289,13 +295,13 @@ DEFUN (clear_ip_eigrp_neighbors,  					"Neighbor %s (%s) is down: manually cleared",  					inet_ntoa(nbr->src),  					ifindex2ifname(nbr->ei->ifp->ifindex, -						       VRF_DEFAULT)); +						       eigrp->vrf_id));  				vty_time_print(vty, 0);  				vty_out(vty,  					"Neighbor %s (%s) is down: manually cleared\n",  					inet_ntoa(nbr->src),  					ifindex2ifname(nbr->ei->ifp->ifindex, -						       VRF_DEFAULT)); +						       eigrp->vrf_id));  				/* set neighbor to DOWN */  				nbr->state = EIGRP_NEIGHBOR_DOWN; @@ -311,12 +317,13 @@ DEFUN (clear_ip_eigrp_neighbors,  /*   * Execute hard restart for all neighbors on interface   */ -DEFUN (clear_ip_eigrp_neighbors_int, +DEFPY (clear_ip_eigrp_neighbors_int,         clear_ip_eigrp_neighbors_int_cmd, -       "clear ip eigrp neighbors IFNAME", +       "clear ip eigrp [vrf NAME] neighbors IFNAME",         CLEAR_STR         IP_STR         "Clear IP-EIGRP\n" +       VRF_CMD_HELP_STR         "Clear IP-EIGRP neighbors\n"         "Interface's name\n")  { @@ -324,20 +331,18 @@ DEFUN (clear_ip_eigrp_neighbors_int,  	struct eigrp_interface *ei;  	struct listnode *node2, *nnode2;  	struct eigrp_neighbor *nbr; -	int idx = 0;  	/* Check if eigrp process is enabled */ -	eigrp = eigrp_lookup(); +	eigrp = eigrp_vty_get_eigrp(vty, vrf);  	if (eigrp == NULL) {  		vty_out(vty, " EIGRP Routing Process not enabled\n");  		return CMD_SUCCESS;  	}  	/* lookup interface by specified name */ -	argv_find(argv, argc, "IFNAME", &idx); -	ei = eigrp_if_lookup_by_name(eigrp, argv[idx]->arg); +	ei = eigrp_if_lookup_by_name(eigrp, ifname);  	if (ei == NULL) { -		vty_out(vty, " Interface (%s) doesn't exist\n", argv[idx]->arg); +		vty_out(vty, " Interface (%s) doesn't exist\n", ifname);  		return CMD_WARNING;  	} @@ -350,13 +355,13 @@ DEFUN (clear_ip_eigrp_neighbors_int,  			zlog_debug("Neighbor %s (%s) is down: manually cleared",  				   inet_ntoa(nbr->src),  				   ifindex2ifname(nbr->ei->ifp->ifindex, -						  VRF_DEFAULT)); +						  eigrp->vrf_id));  			vty_time_print(vty, 0);  			vty_out(vty,  				"Neighbor %s (%s) is down: manually cleared\n",  				inet_ntoa(nbr->src),  				ifindex2ifname(nbr->ei->ifp->ifindex, -					       VRF_DEFAULT)); +					       eigrp->vrf_id));  			/* set neighbor to DOWN */  			nbr->state = EIGRP_NEIGHBOR_DOWN; @@ -371,26 +376,21 @@ DEFUN (clear_ip_eigrp_neighbors_int,  /*   * Execute hard restart for neighbor specified by IP   */ -DEFUN (clear_ip_eigrp_neighbors_IP, +DEFPY (clear_ip_eigrp_neighbors_IP,         clear_ip_eigrp_neighbors_IP_cmd, -       "clear ip eigrp neighbors A.B.C.D", +       "clear ip eigrp [vrf NAME] neighbors A.B.C.D$nbr_addr",         CLEAR_STR         IP_STR         "Clear IP-EIGRP\n" +       VRF_CMD_HELP_STR         "Clear IP-EIGRP neighbors\n"         "IP-EIGRP neighbor address\n")  {  	struct eigrp *eigrp;  	struct eigrp_neighbor *nbr; -	struct in_addr nbr_addr; - -	if (!inet_aton(argv[4]->arg, &nbr_addr)) { -		vty_out(vty, "Unable to parse %s", argv[4]->arg); -		return CMD_WARNING; -	}  	/* Check if eigrp process is enabled */ -	eigrp = eigrp_lookup(); +	eigrp = eigrp_vty_get_eigrp(vty, vrf);  	if (eigrp == NULL) {  		vty_out(vty, " EIGRP Routing Process not enabled\n");  		return CMD_SUCCESS; @@ -414,19 +414,20 @@ DEFUN (clear_ip_eigrp_neighbors_IP,  /*   * Execute graceful restart for all neighbors   */ -DEFUN (clear_ip_eigrp_neighbors_soft, +DEFPY (clear_ip_eigrp_neighbors_soft,         clear_ip_eigrp_neighbors_soft_cmd, -       "clear ip eigrp neighbors soft", +       "clear ip eigrp [vrf NAME] neighbors soft",         CLEAR_STR         IP_STR         "Clear IP-EIGRP\n" +       VRF_CMD_HELP_STR         "Clear IP-EIGRP neighbors\n"         "Resync with peers without adjacency reset\n")  {  	struct eigrp *eigrp;  	/* Check if eigrp process is enabled */ -	eigrp = eigrp_lookup(); +	eigrp = eigrp_vty_get_eigrp(vty, vrf);  	if (eigrp == NULL) {  		vty_out(vty, " EIGRP Routing Process not enabled\n");  		return CMD_SUCCESS; @@ -441,12 +442,13 @@ DEFUN (clear_ip_eigrp_neighbors_soft,  /*   * Execute graceful restart for all neighbors on interface   */ -DEFUN (clear_ip_eigrp_neighbors_int_soft, +DEFPY (clear_ip_eigrp_neighbors_int_soft,         clear_ip_eigrp_neighbors_int_soft_cmd, -       "clear ip eigrp neighbors IFNAME soft", +       "clear ip eigrp [vrf NAME] neighbors IFNAME soft",         CLEAR_STR         IP_STR         "Clear IP-EIGRP\n" +       VRF_CMD_HELP_STR         "Clear IP-EIGRP neighbors\n"         "Interface's name\n"         "Resync with peer without adjacency reset\n") @@ -455,14 +457,14 @@ DEFUN (clear_ip_eigrp_neighbors_int_soft,  	struct eigrp_interface *ei;  	/* Check if eigrp process is enabled */ -	eigrp = eigrp_lookup(); +	eigrp = eigrp_vty_get_eigrp(vty, vrf);  	if (eigrp == NULL) {  		vty_out(vty, " EIGRP Routing Process not enabled\n");  		return CMD_SUCCESS;  	}  	/* lookup interface by specified name */ -	ei = eigrp_if_lookup_by_name(eigrp, argv[4]->arg); +	ei = eigrp_if_lookup_by_name(eigrp, ifname);  	if (ei == NULL) {  		vty_out(vty, " Interface (%s) doesn't exist\n", argv[4]->arg);  		return CMD_WARNING; @@ -476,27 +478,23 @@ DEFUN (clear_ip_eigrp_neighbors_int_soft,  /*   * Execute graceful restart for neighbor specified by IP   */ -DEFUN (clear_ip_eigrp_neighbors_IP_soft, +DEFPY (clear_ip_eigrp_neighbors_IP_soft,         clear_ip_eigrp_neighbors_IP_soft_cmd, -       "clear ip eigrp neighbors A.B.C.D soft", +       "clear ip eigrp [vrf NAME] neighbors A.B.C.D$nbr_addr soft",         CLEAR_STR         IP_STR         "Clear IP-EIGRP\n" +       VRF_CMD_HELP_STR         "Clear IP-EIGRP neighbors\n"         "IP-EIGRP neighbor address\n"         "Resync with peer without adjacency reset\n")  {  	struct eigrp *eigrp;  	struct eigrp_neighbor *nbr; -	struct in_addr nbr_addr; -	if (!inet_aton(argv[4]->arg, &nbr_addr)) { -		vty_out(vty, "Unable to parse: %s", argv[4]->arg); -		return CMD_WARNING; -	}  	/* Check if eigrp process is enabled */ -	eigrp = eigrp_lookup(); +	eigrp = eigrp_vty_get_eigrp(vty, vrf);  	if (eigrp == NULL) {  		vty_out(vty, " EIGRP Routing Process not enabled\n");  		return CMD_SUCCESS; diff --git a/eigrpd/eigrp_zebra.c b/eigrpd/eigrp_zebra.c index 0a74e86263..63cbe84ef2 100644 --- a/eigrpd/eigrp_zebra.c +++ b/eigrpd/eigrp_zebra.c @@ -59,7 +59,8 @@ static int eigrp_interface_address_add(ZAPI_CALLBACK_ARGS);  static int eigrp_interface_address_delete(ZAPI_CALLBACK_ARGS);  static int eigrp_interface_state_up(ZAPI_CALLBACK_ARGS);  static int eigrp_interface_state_down(ZAPI_CALLBACK_ARGS); -static struct interface *zebra_interface_if_lookup(struct stream *); +static struct interface *zebra_interface_if_lookup(struct stream *, +						   vrf_id_t vrf_id);  static int eigrp_zebra_read_route(ZAPI_CALLBACK_ARGS); @@ -79,7 +80,7 @@ static int eigrp_router_id_update_zebra(ZAPI_CALLBACK_ARGS)  	router_id_zebra = router_id.u.prefix4; -	eigrp = eigrp_lookup(); +	eigrp = eigrp_lookup(vrf_id);  	if (eigrp != NULL)  		eigrp_router_id_update(eigrp); @@ -137,7 +138,7 @@ static int eigrp_zebra_read_route(ZAPI_CALLBACK_ARGS)  	if (IPV4_NET127(ntohl(api.prefix.u.prefix4.s_addr)))  		return 0; -	eigrp = eigrp_lookup(); +	eigrp = eigrp_lookup(vrf_id);  	if (eigrp == NULL)  		return 0; @@ -257,7 +258,7 @@ static int eigrp_interface_state_up(ZAPI_CALLBACK_ARGS)  {  	struct interface *ifp; -	ifp = zebra_interface_if_lookup(zclient->ibuf); +	ifp = zebra_interface_if_lookup(zclient->ibuf, vrf_id);  	if (ifp == NULL)  		return 0; @@ -328,7 +329,8 @@ static int eigrp_interface_state_down(ZAPI_CALLBACK_ARGS)  	return 0;  } -static struct interface *zebra_interface_if_lookup(struct stream *s) +static struct interface *zebra_interface_if_lookup(struct stream *s, +						   vrf_id_t vrf_id)  {  	char ifname_tmp[INTERFACE_NAMSIZ]; @@ -336,11 +338,11 @@ static struct interface *zebra_interface_if_lookup(struct stream *s)  	stream_get(ifname_tmp, s, INTERFACE_NAMSIZ);  	/* And look it up. */ -	return if_lookup_by_name(ifname_tmp, VRF_DEFAULT); +	return if_lookup_by_name(ifname_tmp, vrf_id);  } -void eigrp_zebra_route_add(struct prefix *p, struct list *successors, -			   uint32_t distance) +void eigrp_zebra_route_add(struct eigrp *eigrp, struct prefix *p, +			   struct list *successors, uint32_t distance)  {  	struct zapi_route api;  	struct zapi_nexthop *api_nh; @@ -352,7 +354,7 @@ void eigrp_zebra_route_add(struct prefix *p, struct list *successors,  		return;  	memset(&api, 0, sizeof(api)); -	api.vrf_id = VRF_DEFAULT; +	api.vrf_id = eigrp->vrf_id;  	api.type = ZEBRA_ROUTE_EIGRP;  	api.safi = SAFI_UNICAST;  	api.metric = distance; @@ -366,7 +368,7 @@ void eigrp_zebra_route_add(struct prefix *p, struct list *successors,  		if (count >= MULTIPATH_NUM)  			break;  		api_nh = &api.nexthops[count]; -		api_nh->vrf_id = VRF_DEFAULT; +		api_nh->vrf_id = eigrp->vrf_id;  		if (te->adv_router->src.s_addr) {  			api_nh->gate.ipv4 = te->adv_router->src;  			api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX; @@ -388,7 +390,7 @@ void eigrp_zebra_route_add(struct prefix *p, struct list *successors,  	zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api);  } -void eigrp_zebra_route_delete(struct prefix *p) +void eigrp_zebra_route_delete(struct eigrp *eigrp, struct prefix *p)  {  	struct zapi_route api; @@ -396,7 +398,7 @@ void eigrp_zebra_route_delete(struct prefix *p)  		return;  	memset(&api, 0, sizeof(api)); -	api.vrf_id = VRF_DEFAULT; +	api.vrf_id = eigrp->vrf_id;  	api.type = ZEBRA_ROUTE_EIGRP;  	api.safi = SAFI_UNICAST;  	memcpy(&api.prefix, p, sizeof(*p)); @@ -411,20 +413,20 @@ void eigrp_zebra_route_delete(struct prefix *p)  	return;  } -int eigrp_is_type_redistributed(int type) +static int eigrp_is_type_redistributed(int type, vrf_id_t vrf_id)  {  	return ((DEFAULT_ROUTE_TYPE(type))  			? vrf_bitmap_check(zclient->default_information[AFI_IP], -					   VRF_DEFAULT) +					   vrf_id)  			: vrf_bitmap_check(zclient->redist[AFI_IP][type], -					   VRF_DEFAULT)); +					   vrf_id));  }  int eigrp_redistribute_set(struct eigrp *eigrp, int type,  			   struct eigrp_metrics metric)  { -	if (eigrp_is_type_redistributed(type)) { +	if (eigrp_is_type_redistributed(type, eigrp->vrf_id)) {  		if (eigrp_metrics_is_same(metric, eigrp->dmetric[type])) {  			eigrp->dmetric[type] = metric;  		} @@ -443,7 +445,7 @@ int eigrp_redistribute_set(struct eigrp *eigrp, int type,  	eigrp->dmetric[type] = metric;  	zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, type, 0, -			     VRF_DEFAULT); +			     eigrp->vrf_id);  	++eigrp->redistribute; @@ -453,10 +455,10 @@ int eigrp_redistribute_set(struct eigrp *eigrp, int type,  int eigrp_redistribute_unset(struct eigrp *eigrp, int type)  { -	if (eigrp_is_type_redistributed(type)) { +	if (eigrp_is_type_redistributed(type, eigrp->vrf_id)) {  		memset(&eigrp->dmetric[type], 0, sizeof(struct eigrp_metrics));  		zclient_redistribute(ZEBRA_REDISTRIBUTE_DELETE, zclient, AFI_IP, -				     type, 0, VRF_DEFAULT); +				     type, 0, eigrp->vrf_id);  		--eigrp->redistribute;  	} diff --git a/eigrpd/eigrp_zebra.h b/eigrpd/eigrp_zebra.h index 86b337cfe6..112f2584ce 100644 --- a/eigrpd/eigrp_zebra.h +++ b/eigrpd/eigrp_zebra.h @@ -33,11 +33,10 @@  extern void eigrp_zebra_init(void); -extern void eigrp_zebra_route_add(struct prefix *, struct list *, -				  uint32_t distance); -extern void eigrp_zebra_route_delete(struct prefix *); +extern void eigrp_zebra_route_add(struct eigrp *eigrp, struct prefix *p, +				  struct list *successors, uint32_t distance); +extern void eigrp_zebra_route_delete(struct eigrp *eigrp, struct prefix *);  extern int eigrp_redistribute_set(struct eigrp *, int, struct eigrp_metrics);  extern int eigrp_redistribute_unset(struct eigrp *, int); -extern int eigrp_is_type_redistributed(int);  #endif /* _ZEBRA_EIGRP_ZEBRA_H_ */ diff --git a/eigrpd/eigrpd.c b/eigrpd/eigrpd.c index 93f8b6f90e..e93dc0cbfa 100644 --- a/eigrpd/eigrpd.c +++ b/eigrpd/eigrpd.c @@ -64,8 +64,6 @@ static struct eigrp_master eigrp_master;  struct eigrp_master *eigrp_om; -static struct eigrp *eigrp_new(const char *); -  extern struct zclient *zclient;  extern struct in_addr router_id_zebra; @@ -95,7 +93,7 @@ extern struct in_addr router_id_zebra;   */  void eigrp_router_id_update(struct eigrp *eigrp)  { -	struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); +	struct vrf *vrf = vrf_lookup_by_id(eigrp->vrf_id);  	struct interface *ifp;  	struct in_addr router_id, router_id_old; @@ -136,14 +134,14 @@ void eigrp_master_init(void)  }  /* Allocate new eigrp structure. */ -static struct eigrp *eigrp_new(const char *AS) +static struct eigrp *eigrp_new(uint16_t as, vrf_id_t vrf_id)  {  	struct eigrp *eigrp = XCALLOC(MTYPE_EIGRP_TOP, sizeof(struct eigrp)); -	int eigrp_socket;  	/* init information relevant to peers */ +	eigrp->vrf_id = vrf_id;  	eigrp->vrid = 0; -	eigrp->AS = atoi(AS); +	eigrp->AS = as;  	eigrp->router_id.s_addr = 0;  	eigrp->router_id_static.s_addr = 0;  	eigrp->sequence_number = 1; @@ -161,14 +159,15 @@ static struct eigrp *eigrp_new(const char *AS)  	eigrp->passive_interface_default = EIGRP_IF_ACTIVE;  	eigrp->networks = eigrp_topology_new(); -	if ((eigrp_socket = eigrp_sock_init()) < 0) { +	eigrp->fd = eigrp_sock_init(vrf_lookup_by_id(vrf_id)); + +	if (eigrp->fd < 0) {  		flog_err_sys(  			EC_LIB_SOCKET,  			"eigrp_new: fatal error: eigrp_sock_init was unable to open a socket");  		exit(1);  	} -	eigrp->fd = eigrp_socket;  	eigrp->maxsndbuflen = getsockopt_so_sendbuf(eigrp->fd);  	eigrp->ibuf = stream_new(EIGRP_PACKET_MAX_LEN + 1); @@ -200,16 +199,15 @@ static struct eigrp *eigrp_new(const char *AS)  	eigrp->routemap[EIGRP_FILTER_OUT] = NULL;  	/* Distribute list install. */ -	eigrp->distribute_ctx = distribute_list_ctx_create( -					   vrf_lookup_by_id(VRF_DEFAULT)); +	eigrp->distribute_ctx = +		distribute_list_ctx_create(vrf_lookup_by_id(eigrp->vrf_id));  	distribute_list_add_hook(eigrp->distribute_ctx,  				 eigrp_distribute_update);  	distribute_list_delete_hook(eigrp->distribute_ctx,  				    eigrp_distribute_update);  	/* -	  eigrp->if_rmap_ctx = if_rmap_ctx_create( -	                               VRF_DEFAULT_NAME); +	  eigrp->if_rmap_ctx = if_rmap_ctx_create(eigrp->vrf_id);  	  if_rmap_hook_add (eigrp_if_rmap_update);  	  if_rmap_hook_delete (eigrp_if_rmap_update);  	*/ @@ -217,13 +215,13 @@ static struct eigrp *eigrp_new(const char *AS)  	return eigrp;  } -struct eigrp *eigrp_get(const char *AS) +struct eigrp *eigrp_get(uint16_t as, vrf_id_t vrf_id)  {  	struct eigrp *eigrp; -	eigrp = eigrp_lookup(); +	eigrp = eigrp_lookup(vrf_id);  	if (eigrp == NULL) { -		eigrp = eigrp_new(AS); +		eigrp = eigrp_new(as, vrf_id);  		listnode_add(eigrp_om->eigrp, eigrp);  	} @@ -285,7 +283,7 @@ void eigrp_finish_final(struct eigrp *eigrp)  	list_delete(&eigrp->eiflist);  	list_delete(&eigrp->oi_write_q); -	eigrp_topology_free(eigrp->topology_table); +	eigrp_topology_free(eigrp, eigrp->topology_table);  	eigrp_nbr_delete(eigrp->neighbor_self); @@ -300,10 +298,14 @@ void eigrp_finish_final(struct eigrp *eigrp)  }  /*Look for existing eigrp process*/ -struct eigrp *eigrp_lookup(void) +struct eigrp *eigrp_lookup(vrf_id_t vrf_id)  { -	if (listcount(eigrp_om->eigrp) == 0) -		return NULL; +	struct eigrp *eigrp; +	struct listnode *node, *nnode; + +	for (ALL_LIST_ELEMENTS(eigrp_om->eigrp, node, nnode, eigrp)) +		if (eigrp->vrf_id == vrf_id) +			return eigrp; -	return listgetdata(listhead(eigrp_om->eigrp)); +	return NULL;  } diff --git a/eigrpd/eigrpd.h b/eigrpd/eigrpd.h index 3ef3a9c0cc..6b4d45d1fc 100644 --- a/eigrpd/eigrpd.h +++ b/eigrpd/eigrpd.h @@ -48,8 +48,8 @@ extern void eigrp_master_init(void);  extern void eigrp_terminate(void);  extern void eigrp_finish_final(struct eigrp *);  extern void eigrp_finish(struct eigrp *); -extern struct eigrp *eigrp_get(const char *); -extern struct eigrp *eigrp_lookup(void); +extern struct eigrp *eigrp_get(uint16_t as, vrf_id_t vrf_id); +extern struct eigrp *eigrp_lookup(vrf_id_t vrf_id);  extern void eigrp_router_id_update(struct eigrp *);  /* eigrp_cli.c */ diff --git a/eigrpd/subdir.am b/eigrpd/subdir.am index cc46766586..5a65c654e9 100644 --- a/eigrpd/subdir.am +++ b/eigrpd/subdir.am @@ -35,6 +35,7 @@ eigrpd_libeigrp_a_SOURCES = \  	eigrpd/eigrp_snmp.c \  	eigrpd/eigrp_topology.c \  	eigrpd/eigrp_update.c \ +	eigrpd/eigrp_vrf.c \  	eigrpd/eigrp_vty.c \  	eigrpd/eigrp_zebra.c \  	eigrpd/eigrpd.c \ @@ -66,6 +67,7 @@ noinst_HEADERS += \  	eigrpd/eigrp_packet.h \  	eigrpd/eigrp_snmp.h \  	eigrpd/eigrp_structs.h \ +	eigrpd/eigrp_vrf.h \  	eigrpd/eigrp_vty.h \  	eigrpd/eigrp_zebra.h \  	# end diff --git a/isisd/isis_bpf.c b/isisd/isis_bpf.c index 4e9aef47ad..d6b85b2fa3 100644 --- a/isisd/isis_bpf.c +++ b/isisd/isis_bpf.c @@ -187,7 +187,7 @@ int isis_sock_init(struct isis_circuit *circuit)  {  	int retval = ISIS_OK; -	frr_elevate_privs(&isisd_privs) { +	frr_with_privs(&isisd_privs) {  		retval = open_bpf_dev(circuit); diff --git a/isisd/isis_dlpi.c b/isisd/isis_dlpi.c index a96dd93804..7d3dfcb01e 100644 --- a/isisd/isis_dlpi.c +++ b/isisd/isis_dlpi.c @@ -467,7 +467,7 @@ int isis_sock_init(struct isis_circuit *circuit)  {  	int retval = ISIS_OK; -	frr_elevate_privs(&isisd_privs) { +	frr_with_privs(&isisd_privs) {  		retval = open_dlpi_dev(circuit); diff --git a/isisd/isis_memory.c b/isisd/isis_memory.c index 7d1ad6b049..2725459767 100644 --- a/isisd/isis_memory.c +++ b/isisd/isis_memory.c @@ -39,7 +39,6 @@ DEFINE_MTYPE(ISISD, ISIS_SPFTREE, "ISIS SPFtree")  DEFINE_MTYPE(ISISD, ISIS_VERTEX, "ISIS vertex")  DEFINE_MTYPE(ISISD, ISIS_ROUTE_INFO, "ISIS route info")  DEFINE_MTYPE(ISISD, ISIS_NEXTHOP, "ISIS nexthop") -DEFINE_MTYPE(ISISD, ISIS_NEXTHOP6, "ISIS nexthop6")  DEFINE_MTYPE(ISISD, ISIS_DICT, "ISIS dictionary")  DEFINE_MTYPE(ISISD, ISIS_DICT_NODE, "ISIS dictionary node")  DEFINE_MTYPE(ISISD, ISIS_EXT_ROUTE, "ISIS redistributed route") diff --git a/isisd/isis_memory.h b/isisd/isis_memory.h index 4078c7a671..e672340e84 100644 --- a/isisd/isis_memory.h +++ b/isisd/isis_memory.h @@ -38,7 +38,6 @@ DECLARE_MTYPE(ISIS_SPFTREE)  DECLARE_MTYPE(ISIS_VERTEX)  DECLARE_MTYPE(ISIS_ROUTE_INFO)  DECLARE_MTYPE(ISIS_NEXTHOP) -DECLARE_MTYPE(ISIS_NEXTHOP6)  DECLARE_MTYPE(ISIS_DICT)  DECLARE_MTYPE(ISIS_DICT_NODE)  DECLARE_MTYPE(ISIS_EXT_ROUTE) diff --git a/isisd/isis_northbound.c b/isisd/isis_northbound.c index 0982a468a6..c1b630eb2d 100644 --- a/isisd/isis_northbound.c +++ b/isisd/isis_northbound.c @@ -1524,29 +1524,64 @@ static int lib_interface_isis_create(enum nb_event event,  	struct interface *ifp;  	struct isis_circuit *circuit;  	const char *area_tag = yang_dnode_get_string(dnode, "./area-tag"); +	uint32_t min_mtu, actual_mtu; -	if (event != NB_EV_APPLY) -		return NB_OK; +	switch (event) { +	case NB_EV_PREPARE: +	case NB_EV_ABORT: +		break; +	case NB_EV_VALIDATE: +		/* check if interface mtu is sufficient. If the area has not +		 * been created yet, assume default MTU for the area +		 */ +		ifp = nb_running_get_entry(dnode, NULL, true); +		/* zebra might not know yet about the MTU - nothing we can do */ +		if (ifp->mtu == 0) +			break; +		actual_mtu = +			if_is_broadcast(ifp) ? ifp->mtu - LLC_LEN : ifp->mtu; +		area = isis_area_lookup(area_tag); +		if (area) +			min_mtu = area->lsp_mtu; +		else +#ifndef FABRICD +			min_mtu = yang_get_default_uint16( +				"/frr-isisd:isis/instance/lsp/mtu"); +#else +			min_mtu = DEFAULT_LSP_MTU; +#endif /* ifndef FABRICD */ +		if (actual_mtu < min_mtu) { +			flog_warn(EC_LIB_NB_CB_CONFIG_VALIDATE, +				  "Interface %s has MTU %" PRIu32 +				  ", minimum MTU for the area is %" PRIu32 "", +				  ifp->name, actual_mtu, min_mtu); +			return NB_ERR_VALIDATION; +		} +		break; +	case NB_EV_APPLY: +		area = isis_area_lookup(area_tag); +		/* The area should have already be created. We are +		 * setting the priority of the global isis area creation +		 * slightly lower, so it should be executed first, but I +		 * cannot rely on that so here I have to check. +		 */ +		if (!area) { +			flog_err( +				EC_LIB_NB_CB_CONFIG_APPLY, +				"%s: attempt to create circuit for area %s before the area has been created", +				__func__, area_tag); +			abort(); +		} -	area = isis_area_lookup(area_tag); -	/* The area should have already be created. We are -	 * setting the priority of the global isis area creation -	 * slightly lower, so it should be executed first, but I -	 * cannot rely on that so here I have to check. -	 */ -	if (!area) { -		flog_err( -			EC_LIB_NB_CB_CONFIG_APPLY, -			"%s: attempt to create circuit for area %s before the area has been created", -			__func__, area_tag); -		abort(); +		ifp = nb_running_get_entry(dnode, NULL, true); +		circuit = isis_circuit_create(area, ifp); +		assert(circuit +		       && (circuit->state == C_STATE_CONF +			   || circuit->state == C_STATE_UP)); +		nb_running_set_entry(dnode, circuit); +		break;  	} -	ifp = nb_running_get_entry(dnode, NULL, true); -	circuit = isis_circuit_create(area, ifp); -	assert(circuit->state == C_STATE_CONF || circuit->state == C_STATE_UP); -	nb_running_set_entry(dnode, circuit); -  	return NB_OK;  } @@ -1561,21 +1596,8 @@ static int lib_interface_isis_destroy(enum nb_event event,  	circuit = nb_running_unset_entry(dnode);  	if (!circuit)  		return NB_ERR_INCONSISTENCY; -	/* delete circuit through csm changes */ -	switch (circuit->state) { -	case C_STATE_UP: -		isis_csm_state_change(IF_DOWN_FROM_Z, circuit, -				      circuit->interface); -		isis_csm_state_change(ISIS_DISABLE, circuit, circuit->area); -		break; -	case C_STATE_CONF: +	if (circuit->state == C_STATE_UP || circuit->state == C_STATE_CONF)  		isis_csm_state_change(ISIS_DISABLE, circuit, circuit->area); -		break; -	case C_STATE_INIT: -		isis_csm_state_change(IF_DOWN_FROM_Z, circuit, -				      circuit->interface); -		break; -	}  	return NB_OK;  } diff --git a/isisd/isis_pfpacket.c b/isisd/isis_pfpacket.c index ea66e6950e..69ac3fc555 100644 --- a/isisd/isis_pfpacket.c +++ b/isisd/isis_pfpacket.c @@ -183,7 +183,7 @@ int isis_sock_init(struct isis_circuit *circuit)  {  	int retval = ISIS_OK; -	frr_elevate_privs(&isisd_privs) { +	frr_with_privs(&isisd_privs) {  		retval = open_packet_socket(circuit); diff --git a/isisd/isis_route.c b/isisd/isis_route.c index 281eaf11bc..636a63e290 100644 --- a/isisd/isis_route.c +++ b/isisd/isis_route.c @@ -28,6 +28,7 @@  #include "linklist.h"  #include "vty.h"  #include "log.h" +#include "lib_errors.h"  #include "memory.h"  #include "prefix.h"  #include "hash.h" @@ -48,26 +49,25 @@  #include "isis_route.h"  #include "isis_zebra.h" -static struct isis_nexthop *isis_nexthop_create(struct in_addr *ip, +static struct isis_nexthop *nexthoplookup(struct list *nexthops, int family, +					  union g_addr *ip, ifindex_t ifindex); + +static struct isis_nexthop *isis_nexthop_create(int family, union g_addr *ip,  						ifindex_t ifindex)  { -	struct listnode *node;  	struct isis_nexthop *nexthop; -	for (ALL_LIST_ELEMENTS_RO(isis->nexthops, node, nexthop)) { -		if (nexthop->ifindex != ifindex) -			continue; -		if (ip && memcmp(&nexthop->ip, ip, sizeof(struct in_addr)) != 0) -			continue; - +	nexthop = nexthoplookup(isis->nexthops, family, ip, ifindex); +	if (nexthop) {  		nexthop->lock++;  		return nexthop;  	}  	nexthop = XCALLOC(MTYPE_ISIS_NEXTHOP, sizeof(struct isis_nexthop)); +	nexthop->family = family;  	nexthop->ifindex = ifindex; -	memcpy(&nexthop->ip, ip, sizeof(struct in_addr)); +	nexthop->ip = *ip;  	listnode_add(isis->nexthops, nexthop);  	nexthop->lock++; @@ -85,116 +85,79 @@ static void isis_nexthop_delete(struct isis_nexthop *nexthop)  	return;  } -static int nexthoplookup(struct list *nexthops, struct in_addr *ip, -			 ifindex_t ifindex) +static struct isis_nexthop *nexthoplookup(struct list *nexthops, int family, +					  union g_addr *ip, ifindex_t ifindex)  {  	struct listnode *node;  	struct isis_nexthop *nh;  	for (ALL_LIST_ELEMENTS_RO(nexthops, node, nh)) { -		if (!(memcmp(ip, &nh->ip, sizeof(struct in_addr))) -		    && ifindex == nh->ifindex) -			return 1; -	} - -	return 0; -} - -static struct isis_nexthop6 *isis_nexthop6_new(struct in6_addr *ip6, -					       ifindex_t ifindex) -{ -	struct isis_nexthop6 *nexthop6; - -	nexthop6 = XCALLOC(MTYPE_ISIS_NEXTHOP6, sizeof(struct isis_nexthop6)); - -	nexthop6->ifindex = ifindex; -	memcpy(&nexthop6->ip6, ip6, sizeof(struct in6_addr)); -	nexthop6->lock++; - -	return nexthop6; -} - -static struct isis_nexthop6 *isis_nexthop6_create(struct in6_addr *ip6, -						  ifindex_t ifindex) -{ -	struct listnode *node; -	struct isis_nexthop6 *nexthop6; - -	for (ALL_LIST_ELEMENTS_RO(isis->nexthops6, node, nexthop6)) { -		if (nexthop6->ifindex != ifindex) +		if (nh->family != family)  			continue; -		if (ip6 -		    && memcmp(&nexthop6->ip6, ip6, sizeof(struct in6_addr)) -			       != 0) +		if (nh->ifindex != ifindex)  			continue; -		nexthop6->lock++; -		return nexthop6; -	} - -	nexthop6 = isis_nexthop6_new(ip6, ifindex); - -	return nexthop6; -} - -static void isis_nexthop6_delete(struct isis_nexthop6 *nexthop6) -{ - -	nexthop6->lock--; -	if (nexthop6->lock == 0) { -		listnode_delete(isis->nexthops6, nexthop6); -		XFREE(MTYPE_ISIS_NEXTHOP6, nexthop6); -	} - -	return; -} - -static int nexthop6lookup(struct list *nexthops6, struct in6_addr *ip6, -			  ifindex_t ifindex) -{ -	struct listnode *node; -	struct isis_nexthop6 *nh6; +		switch (family) { +		case AF_INET: +			if (IPV4_ADDR_CMP(&nh->ip.ipv4, &ip->ipv4)) +				continue; +			break; +		case AF_INET6: +			if (IPV6_ADDR_CMP(&nh->ip.ipv6, &ip->ipv6)) +				continue; +			break; +		default: +			flog_err(EC_LIB_DEVELOPMENT, +				 "%s: unknown address family [%d]", __func__, +				 family); +			exit(1); +		} -	for (ALL_LIST_ELEMENTS_RO(nexthops6, node, nh6)) { -		if (!(memcmp(ip6, &nh6->ip6, sizeof(struct in6_addr))) -		    && ifindex == nh6->ifindex) -			return 1; +		return nh;  	} -	return 0; +	return NULL;  } -static void adjinfo2nexthop(struct list *nexthops, struct isis_adjacency *adj) +static void adjinfo2nexthop(int family, struct list *nexthops, +			    struct isis_adjacency *adj)  {  	struct isis_nexthop *nh; - -	for (unsigned int i = 0; i < adj->ipv4_address_count; i++) { -		struct in_addr *ipv4_addr = &adj->ipv4_addresses[i]; -		if (!nexthoplookup(nexthops, ipv4_addr, -				   adj->circuit->interface->ifindex)) { -			nh = isis_nexthop_create( -				ipv4_addr, adj->circuit->interface->ifindex); -			nh->router_address = adj->router_address; -			listnode_add(nexthops, nh); -			return; +	union g_addr ip = {}; + +	switch (family) { +	case AF_INET: +		for (unsigned int i = 0; i < adj->ipv4_address_count; i++) { +			ip.ipv4 = adj->ipv4_addresses[i]; + +			if (!nexthoplookup(nexthops, AF_INET, &ip, +					   adj->circuit->interface->ifindex)) { +				nh = isis_nexthop_create( +					AF_INET, &ip, +					adj->circuit->interface->ifindex); +				listnode_add(nexthops, nh); +				break; +			}  		} -	} -} - -static void adjinfo2nexthop6(struct list *nexthops6, struct isis_adjacency *adj) -{ -	struct isis_nexthop6 *nh6; - -	for (unsigned int i = 0; i < adj->ipv6_address_count; i++) { -		struct in6_addr *ipv6_addr = &adj->ipv6_addresses[i]; -		if (!nexthop6lookup(nexthops6, ipv6_addr, -				    adj->circuit->interface->ifindex)) { -			nh6 = isis_nexthop6_create( -				ipv6_addr, adj->circuit->interface->ifindex); -			nh6->router_address6 = adj->router_address6; -			listnode_add(nexthops6, nh6); -			return; +		break; +	case AF_INET6: +		for (unsigned int i = 0; i < adj->ipv6_address_count; i++) { +			ip.ipv6 = adj->ipv6_addresses[i]; + +			if (!nexthoplookup(nexthops, AF_INET6, &ip, +					   adj->circuit->interface->ifindex)) { +				nh = isis_nexthop_create( +					AF_INET6, &ip, +					adj->circuit->interface->ifindex); +				listnode_add(nexthops, nh); +				break; +			}  		} +		break; +	default: +		flog_err(EC_LIB_DEVELOPMENT, "%s: unknown address family [%d]", +			 __func__, family); +		exit(1);  	}  } @@ -210,35 +173,32 @@ static struct isis_route_info *isis_route_info_new(struct prefix *prefix,  	rinfo = XCALLOC(MTYPE_ISIS_ROUTE_INFO, sizeof(struct isis_route_info)); -	if (prefix->family == AF_INET) { -		rinfo->nexthops = list_new(); -		for (ALL_LIST_ELEMENTS_RO(adjacencies, node, adj)) { -			/* check for force resync this route */ -			if (CHECK_FLAG(adj->circuit->flags, -				       ISIS_CIRCUIT_FLAPPED_AFTER_SPF)) -				SET_FLAG(rinfo->flag, -					 ISIS_ROUTE_FLAG_ZEBRA_RESYNC); -			/* update neighbor router address */ +	rinfo->nexthops = list_new(); +	for (ALL_LIST_ELEMENTS_RO(adjacencies, node, adj)) { +		/* check for force resync this route */ +		if (CHECK_FLAG(adj->circuit->flags, +			       ISIS_CIRCUIT_FLAPPED_AFTER_SPF)) +			SET_FLAG(rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC); + +		/* update neighbor router address */ +		switch (prefix->family) { +		case AF_INET:  			if (depth == 2 && prefix->prefixlen == 32)  				adj->router_address = prefix->u.prefix4; -			adjinfo2nexthop(rinfo->nexthops, adj); -		} -	} -	if (prefix->family == AF_INET6) { -		rinfo->nexthops6 = list_new(); -		for (ALL_LIST_ELEMENTS_RO(adjacencies, node, adj)) { -			/* check for force resync this route */ -			if (CHECK_FLAG(adj->circuit->flags, -				       ISIS_CIRCUIT_FLAPPED_AFTER_SPF)) -				SET_FLAG(rinfo->flag, -					 ISIS_ROUTE_FLAG_ZEBRA_RESYNC); -			/* update neighbor router address */ +			break; +		case AF_INET6:  			if (depth == 2 && prefix->prefixlen == 128  			    && (!src_p || !src_p->prefixlen)) {  				adj->router_address6 = prefix->u.prefix6;  			} -			adjinfo2nexthop6(rinfo->nexthops6, adj); +			break; +		default: +			flog_err(EC_LIB_DEVELOPMENT, +				 "%s: unknown address family [%d]", __func__, +				 prefix->family); +			exit(1);  		} +		adjinfo2nexthop(prefix->family, rinfo->nexthops, adj);  	}  	rinfo->cost = cost; @@ -255,12 +215,6 @@ static void isis_route_info_delete(struct isis_route_info *route_info)  		list_delete(&route_info->nexthops);  	} -	if (route_info->nexthops6) { -		route_info->nexthops6->del = -			(void (*)(void *))isis_nexthop6_delete; -		list_delete(&route_info->nexthops6); -	} -  	XFREE(MTYPE_ISIS_ROUTE_INFO, route_info);  } @@ -280,7 +234,6 @@ static int isis_route_info_same(struct isis_route_info *new,  {  	struct listnode *node;  	struct isis_nexthop *nexthop; -	struct isis_nexthop6 *nexthop6;  	if (!CHECK_FLAG(old->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED))  		return 0; @@ -291,31 +244,15 @@ static int isis_route_info_same(struct isis_route_info *new,  	if (!isis_route_info_same_attrib(new, old))  		return 0; -	if (family == AF_INET) { -		for (ALL_LIST_ELEMENTS_RO(new->nexthops, node, nexthop)) -			if (nexthoplookup(old->nexthops, &nexthop->ip, -					  nexthop->ifindex) -			    == 0) -				return 0; - -		for (ALL_LIST_ELEMENTS_RO(old->nexthops, node, nexthop)) -			if (nexthoplookup(new->nexthops, &nexthop->ip, -					  nexthop->ifindex) -			    == 0) -				return 0; -	} else if (family == AF_INET6) { -		for (ALL_LIST_ELEMENTS_RO(new->nexthops6, node, nexthop6)) -			if (nexthop6lookup(old->nexthops6, &nexthop6->ip6, -					   nexthop6->ifindex) -			    == 0) -				return 0; - -		for (ALL_LIST_ELEMENTS_RO(old->nexthops6, node, nexthop6)) -			if (nexthop6lookup(new->nexthops6, &nexthop6->ip6, -					   nexthop6->ifindex) -			    == 0) -				return 0; -	} +	for (ALL_LIST_ELEMENTS_RO(new->nexthops, node, nexthop)) +		if (!nexthoplookup(old->nexthops, nexthop->family, &nexthop->ip, +				   nexthop->ifindex)) +			return 0; + +	for (ALL_LIST_ELEMENTS_RO(old->nexthops, node, nexthop)) +		if (!nexthoplookup(new->nexthops, nexthop->family, &nexthop->ip, +				   nexthop->ifindex)) +			return 0;  	return 1;  } diff --git a/isisd/isis_route.h b/isisd/isis_route.h index 9d6858586b..a20a7e038f 100644 --- a/isisd/isis_route.h +++ b/isisd/isis_route.h @@ -25,17 +25,12 @@  #ifndef _ZEBRA_ISIS_ROUTE_H  #define _ZEBRA_ISIS_ROUTE_H -struct isis_nexthop6 { -	ifindex_t ifindex; -	struct in6_addr ip6; -	struct in6_addr router_address6; -	unsigned int lock; -}; +#include "lib/nexthop.h"  struct isis_nexthop {  	ifindex_t ifindex; -	struct in_addr ip; -	struct in_addr router_address; +	int family; +	union g_addr ip;  	unsigned int lock;  }; @@ -47,7 +42,6 @@ struct isis_route_info {  	uint32_t cost;  	uint32_t depth;  	struct list *nexthops; -	struct list *nexthops6;  };  struct isis_route_info *isis_route_create(struct prefix *prefix, diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index 488dfedae4..ee253c7a31 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -22,7 +22,9 @@   */  #include <zebra.h> +#ifdef CRYPTO_INTERNAL  #include "md5.h" +#endif  #include "memory.h"  #include "stream.h"  #include "sbuf.h" @@ -2770,8 +2772,16 @@ static void update_auth_hmac_md5(struct isis_auth *auth, struct stream *s,  		safe_auth_md5(s, &checksum, &rem_lifetime);  	memset(STREAM_DATA(s) + auth->offset, 0, 16); +#ifdef CRYPTO_OPENSSL +	uint8_t *result = (uint8_t *)HMAC(EVP_md5(), auth->passwd, +					  auth->plength, STREAM_DATA(s), +					  stream_get_endp(s), NULL, NULL); + +	memcpy(digest, result, 16); +#elif CRYPTO_INTERNAL  	hmac_md5(STREAM_DATA(s), stream_get_endp(s), auth->passwd,  		 auth->plength, digest); +#endif  	memcpy(auth->value, digest, 16);  	memcpy(STREAM_DATA(s) + auth->offset, digest, 16); @@ -3310,8 +3320,16 @@ static bool auth_validator_hmac_md5(struct isis_passwd *passwd,  		safe_auth_md5(stream, &checksum, &rem_lifetime);  	memset(STREAM_DATA(stream) + auth->offset, 0, 16); +#ifdef CRYPTO_OPENSSL +	uint8_t *result = (uint8_t *)HMAC(EVP_md5(), passwd->passwd, +					  passwd->len, STREAM_DATA(stream), +					  stream_get_endp(stream), NULL, NULL); + +	memcpy(digest, result, 16); +#elif CRYPTO_INTERNAL  	hmac_md5(STREAM_DATA(stream), stream_get_endp(stream), passwd->passwd,  		 passwd->len, digest); +#endif  	memcpy(STREAM_DATA(stream) + auth->offset, auth->value, 16);  	bool rv = !memcmp(digest, auth->value, 16); diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index e2ef934696..e8481a558b 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -27,6 +27,7 @@  #include "command.h"  #include "memory.h"  #include "log.h" +#include "lib_errors.h"  #include "if.h"  #include "network.h"  #include "prefix.h" @@ -225,7 +226,6 @@ static void isis_zebra_route_add_route(struct prefix *prefix,  	struct zapi_route api;  	struct zapi_nexthop *api_nh;  	struct isis_nexthop *nexthop; -	struct isis_nexthop6 *nexthop6;  	struct listnode *node;  	int count = 0; @@ -250,47 +250,41 @@ static void isis_zebra_route_add_route(struct prefix *prefix,  #endif  	/* Nexthops */ -	switch (prefix->family) { -	case AF_INET: -		for (ALL_LIST_ELEMENTS_RO(route_info->nexthops, node, -					  nexthop)) { -			if (count >= MULTIPATH_NUM) -				break; -			api_nh = &api.nexthops[count]; -			if (fabricd) -				api_nh->onlink = true; -			api_nh->vrf_id = VRF_DEFAULT; +	for (ALL_LIST_ELEMENTS_RO(route_info->nexthops, node, nexthop)) { +		if (count >= MULTIPATH_NUM) +			break; +		api_nh = &api.nexthops[count]; +		if (fabricd) +			api_nh->onlink = true; +		api_nh->vrf_id = VRF_DEFAULT; + +		switch (nexthop->family) { +		case AF_INET:  			/* FIXME: can it be ? */ -			if (nexthop->ip.s_addr != INADDR_ANY) { +			if (nexthop->ip.ipv4.s_addr != INADDR_ANY) {  				api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX; -				api_nh->gate.ipv4 = nexthop->ip; +				api_nh->gate.ipv4 = nexthop->ip.ipv4;  			} else {  				api_nh->type = NEXTHOP_TYPE_IFINDEX;  			} -			api_nh->ifindex = nexthop->ifindex; -			count++; -		} -		break; -	case AF_INET6: -		for (ALL_LIST_ELEMENTS_RO(route_info->nexthops6, node, -					  nexthop6)) { -			if (count >= MULTIPATH_NUM) -				break; -			if (!IN6_IS_ADDR_LINKLOCAL(&nexthop6->ip6) -			    && !IN6_IS_ADDR_UNSPECIFIED(&nexthop6->ip6)) { +			break; +		case AF_INET6: +			if (!IN6_IS_ADDR_LINKLOCAL(&nexthop->ip.ipv6) +			    && !IN6_IS_ADDR_UNSPECIFIED(&nexthop->ip.ipv6)) {  				continue;  			} - -			api_nh = &api.nexthops[count]; -			if (fabricd) -				api_nh->onlink = true; -			api_nh->vrf_id = VRF_DEFAULT; -			api_nh->gate.ipv6 = nexthop6->ip6; -			api_nh->ifindex = nexthop6->ifindex; +			api_nh->gate.ipv6 = nexthop->ip.ipv6;  			api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX; -			count++; +			break; +		default: +			flog_err(EC_LIB_DEVELOPMENT, +				 "%s: unknown address family [%d]", __func__, +				 nexthop->family); +			exit(1);  		} -		break; + +		api_nh->ifindex = nexthop->ifindex; +		count++;  	}  	if (!count)  		return; diff --git a/isisd/isisd.c b/isisd/isisd.c index bee3b6deb5..67f557ab50 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -88,7 +88,6 @@ void isis_new(unsigned long process_id)  	isis->init_circ_list = list_new();  	isis->uptime = time(NULL);  	isis->nexthops = list_new(); -	isis->nexthops6 = list_new();  	dyn_cache_init();  	/*  	 * uncomment the next line for full debugs diff --git a/isisd/isisd.h b/isisd/isisd.h index f8486ae0d6..393b1d67c7 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -69,8 +69,7 @@ struct isis {  	uint32_t router_id;		/* Router ID from zebra */  	struct list *area_list;	/* list of IS-IS areas */  	struct list *init_circ_list; -	struct list *nexthops;		  /* IPv4 next hops from this IS */ -	struct list *nexthops6;		  /* IPv6 next hops from this IS */ +	struct list *nexthops;		  /* IP next hops from this IS */  	uint8_t max_area_addrs;		  /* maximumAreaAdresses */  	struct area_addr *man_area_addrs; /* manualAreaAddresses */  	uint32_t debugs;		  /* bitmap for debug */ diff --git a/ldpd/socket.c b/ldpd/socket.c index b31db2c7bc..8706d03c6f 100644 --- a/ldpd/socket.c +++ b/ldpd/socket.c @@ -79,7 +79,7 @@ ldp_create_socket(int af, enum socket_type type)  		sock_set_bindany(fd, 1);  		break;  	} -	frr_elevate_privs(&ldpd_privs) { +	frr_with_privs(&ldpd_privs) {  		if (sock_set_reuse(fd, 1) == -1) {  			close(fd);  			return (-1); @@ -254,7 +254,7 @@ int  sock_set_bindany(int fd, int enable)  {  #ifdef HAVE_SO_BINDANY -	frr_elevate_privs(&ldpd_privs) { +	frr_with_privs(&ldpd_privs) {  		if (setsockopt(fd, SOL_SOCKET, SO_BINDANY, &enable,  			       sizeof(int)) < 0) {  			log_warn("%s: error setting SO_BINDANY", __func__); @@ -269,7 +269,7 @@ sock_set_bindany(int fd, int enable)  	}  	return (0);  #elif defined(IP_BINDANY) -	frr_elevate_privs(&ldpd_privs) { +	frr_with_privs(&ldpd_privs) {  		if (setsockopt(fd, IPPROTO_IP, IP_BINDANY, &enable, sizeof(int))  		    < 0) {  			log_warn("%s: error setting IP_BINDANY", __func__); @@ -304,7 +304,7 @@ sock_set_md5sig(int fd, int af, union ldpd_addr *addr, const char *password)  #if HAVE_DECL_TCP_MD5SIG  	addr2sa(af, addr, 0, &su); -	frr_elevate_privs(&ldpe_privs) { +	frr_with_privs(&ldpe_privs) {  		ret = sockopt_tcp_signature(fd, &su, password);  		save_errno = errno;  	} diff --git a/lib/command.c b/lib/command.c index 9dabc2af7e..de28a726b0 100644 --- a/lib/command.c +++ b/lib/command.c @@ -151,6 +151,7 @@ const char *node_names[] = {  	"bfd peer",		 /* BFD_PEER_NODE */  	"openfabric",		    // OPENFABRIC_NODE  	"vrrp",			    /* VRRP_NODE */ +	"bmp",			 /* BMP_NODE */  };  /* clang-format on */ @@ -975,6 +976,7 @@ enum node_type node_parent(enum node_type node)  	case BGP_IPV6M_NODE:  	case BGP_EVPN_NODE:  	case BGP_IPV6L_NODE: +	case BMP_NODE:  		ret = BGP_NODE;  		break;  	case BGP_EVPN_VNI_NODE: @@ -1491,6 +1493,7 @@ void cmd_exit(struct vty *vty)  	case BGP_IPV6M_NODE:  	case BGP_EVPN_NODE:  	case BGP_IPV6L_NODE: +	case BMP_NODE:  		vty->node = BGP_NODE;  		break;  	case BGP_EVPN_VNI_NODE: @@ -2708,15 +2711,66 @@ DEFUN (no_banner_motd,  DEFUN(find,        find_cmd, -      "find COMMAND...", -      "Find CLI command containing text\n" -      "Text to search for\n") +      "find REGEX", +      "Find CLI command matching a regular expression\n" +      "Search pattern (POSIX regex)\n")  { -	char *text = argv_concat(argv, argc, 1); +	char *pattern = argv[1]->arg;  	const struct cmd_node *node;  	const struct cmd_element *cli;  	vector clis; +	regex_t exp = {}; + +	int cr = regcomp(&exp, pattern, REG_NOSUB | REG_EXTENDED); + +	if (cr != 0) { +		switch (cr) { +		case REG_BADBR: +			vty_out(vty, "%% Invalid {...} expression\n"); +			break; +		case REG_BADRPT: +			vty_out(vty, "%% Bad repetition operator\n"); +			break; +		case REG_BADPAT: +			vty_out(vty, "%% Regex syntax error\n"); +			break; +		case REG_ECOLLATE: +			vty_out(vty, "%% Invalid collating element\n"); +			break; +		case REG_ECTYPE: +			vty_out(vty, "%% Invalid character class name\n"); +			break; +		case REG_EESCAPE: +			vty_out(vty, +				"%% Regex ended with escape character (\\)\n"); +			break; +		case REG_ESUBREG: +			vty_out(vty, +				"%% Invalid number in \\digit construction\n"); +			break; +		case REG_EBRACK: +			vty_out(vty, "%% Unbalanced square brackets\n"); +			break; +		case REG_EPAREN: +			vty_out(vty, "%% Unbalanced parentheses\n"); +			break; +		case REG_EBRACE: +			vty_out(vty, "%% Unbalanced braces\n"); +			break; +		case REG_ERANGE: +			vty_out(vty, +				"%% Invalid endpoint in range expression\n"); +			break; +		case REG_ESPACE: +			vty_out(vty, "%% Failed to compile (out of memory)\n"); +			break; +		} + +		goto done; +	} + +  	for (unsigned int i = 0; i < vector_active(cmdvec); i++) {  		node = vector_slot(cmdvec, i);  		if (!node) @@ -2724,14 +2778,15 @@ DEFUN(find,  		clis = node->cmd_vector;  		for (unsigned int j = 0; j < vector_active(clis); j++) {  			cli = vector_slot(clis, j); -			if (strcasestr(cli->string, text)) + +			if (regexec(&exp, cli->string, 0, NULL, 0) == 0)  				vty_out(vty, "  (%s)  %s\n",  					node_names[node->node], cli->string);  		}  	} -	XFREE(MTYPE_TMP, text); - +done: +	regfree(&exp);  	return CMD_SUCCESS;  } diff --git a/lib/command.h b/lib/command.h index 8dc35a0fdc..137d3748ae 100644 --- a/lib/command.h +++ b/lib/command.h @@ -159,6 +159,7 @@ enum node_type {  	BFD_PEER_NODE,		 /* BFD peer configuration mode. */  	OPENFABRIC_NODE,	/* OpenFabric router configuration node */  	VRRP_NODE,		 /* VRRP node */ +	BMP_NODE,		/* BMP config under router bgp */  	NODE_TYPE_MAX, /* maximum */  }; diff --git a/lib/compiler.h b/lib/compiler.h index 6700ca9e8b..e430925e69 100644 --- a/lib/compiler.h +++ b/lib/compiler.h @@ -121,6 +121,49 @@ extern "C" {  #define macro_inline	static inline __attribute__((unused))  #define macro_pure	static inline __attribute__((unused, pure)) + +/* variadic macros, use like: + * #define V_0()  ... + * #define V_1(x) ... + * #define V(...) MACRO_VARIANT(V, ##__VA_ARGS__)(__VA_ARGS__) + */ +#define _MACRO_VARIANT(A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10, N, ...) N + +#define _CONCAT2(a, b) a ## b +#define _CONCAT(a, b) _CONCAT2(a,b) + +#define MACRO_VARIANT(NAME, ...) \ +	_CONCAT(NAME, _MACRO_VARIANT(0, ##__VA_ARGS__, \ +			_10, _9, _8, _7, _6, _5, _4, _3, _2, _1, _0)) + +#define NAMECTR(name) _CONCAT(name, __COUNTER__) + +/* per-arg repeat macros, use like: + * #define PERARG(n) ...n... + * #define FOO(...) MACRO_REPEAT(PERARG, ##__VA_ARGS__) + */ + +#define _MACRO_REPEAT_0(NAME) +#define _MACRO_REPEAT_1(NAME, A1) \ +	NAME(A1) +#define _MACRO_REPEAT_2(NAME, A1, A2) \ +	NAME(A1) NAME(A2) +#define _MACRO_REPEAT_3(NAME, A1, A2, A3) \ +	NAME(A1) NAME(A2) NAME(A3) +#define _MACRO_REPEAT_4(NAME, A1, A2, A3, A4) \ +	NAME(A1) NAME(A2) NAME(A3) NAME(A4) +#define _MACRO_REPEAT_5(NAME, A1, A2, A3, A4, A5) \ +	NAME(A1) NAME(A2) NAME(A3) NAME(A4) NAME(A5) +#define _MACRO_REPEAT_6(NAME, A1, A2, A3, A4, A5, A6) \ +	NAME(A1) NAME(A2) NAME(A3) NAME(A4) NAME(A5) NAME(A6) +#define _MACRO_REPEAT_7(NAME, A1, A2, A3, A4, A5, A6, A7) \ +	NAME(A1) NAME(A2) NAME(A3) NAME(A4) NAME(A5) NAME(A6) NAME(A7) +#define _MACRO_REPEAT_8(NAME, A1, A2, A3, A4, A5, A6, A7, A8) \ +	NAME(A1) NAME(A2) NAME(A3) NAME(A4) NAME(A5) NAME(A6) NAME(A7) NAME(A8) + +#define MACRO_REPEAT(NAME, ...) \ +	MACRO_VARIANT(_MACRO_REPEAT, ##__VA_ARGS__)(NAME, ##__VA_ARGS__) +  /*   * for warnings on macros, put in the macro content like this:   *   #define MACRO BLA CPP_WARN("MACRO has been deprecated") diff --git a/lib/ferr.c b/lib/ferr.c index fd5fb50172..ccf63dea17 100644 --- a/lib/ferr.c +++ b/lib/ferr.c @@ -33,6 +33,7 @@  #include "command.h"  #include "json.h"  #include "linklist.h" +#include "frr_pthread.h"  DEFINE_MTYPE_STATIC(LIB, ERRINFO, "error information") @@ -83,14 +84,12 @@ void log_ref_add(struct log_ref *ref)  {  	uint32_t i = 0; -	pthread_mutex_lock(&refs_mtx); -	{ +	frr_with_mutex(&refs_mtx) {  		while (ref[i].code != END_FERR) {  			hash_get(refs, &ref[i], hash_alloc_intern);  			i++;  		}  	} -	pthread_mutex_unlock(&refs_mtx);  }  struct log_ref *log_ref_get(uint32_t code) @@ -99,11 +98,9 @@ struct log_ref *log_ref_get(uint32_t code)  	struct log_ref *ref;  	holder.code = code; -	pthread_mutex_lock(&refs_mtx); -	{ +	frr_with_mutex(&refs_mtx) {  		ref = hash_lookup(refs, &holder);  	} -	pthread_mutex_unlock(&refs_mtx);  	return ref;  } @@ -118,11 +115,9 @@ void log_ref_display(struct vty *vty, uint32_t code, bool json)  	if (json)  		top = json_object_new_object(); -	pthread_mutex_lock(&refs_mtx); -	{ +	frr_with_mutex(&refs_mtx) {  		errlist = code ? list_new() : hash_to_list(refs);  	} -	pthread_mutex_unlock(&refs_mtx);  	if (code) {  		ref = log_ref_get(code); @@ -189,23 +184,19 @@ DEFUN_NOSH(show_error_code,  void log_ref_init(void)  { -	pthread_mutex_lock(&refs_mtx); -	{ +	frr_with_mutex(&refs_mtx) {  		refs = hash_create(ferr_hash_key, ferr_hash_cmp,  				   "Error Reference Texts");  	} -	pthread_mutex_unlock(&refs_mtx);  }  void log_ref_fini(void)  { -	pthread_mutex_lock(&refs_mtx); -	{ +	frr_with_mutex(&refs_mtx) {  		hash_clean(refs, NULL);  		hash_free(refs);  		refs = NULL;  	} -	pthread_mutex_unlock(&refs_mtx);  }  void log_ref_vty_init(void) diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c index bdb6c2a397..21dfc9256f 100644 --- a/lib/frr_pthread.c +++ b/lib/frr_pthread.c @@ -49,21 +49,17 @@ static struct list *frr_pthread_list;  void frr_pthread_init(void)  { -	pthread_mutex_lock(&frr_pthread_list_mtx); -	{ +	frr_with_mutex(&frr_pthread_list_mtx) {  		frr_pthread_list = list_new();  		frr_pthread_list->del = (void (*)(void *))&frr_pthread_destroy;  	} -	pthread_mutex_unlock(&frr_pthread_list_mtx);  }  void frr_pthread_finish(void)  { -	pthread_mutex_lock(&frr_pthread_list_mtx); -	{ +	frr_with_mutex(&frr_pthread_list_mtx) {  		list_delete(&frr_pthread_list);  	} -	pthread_mutex_unlock(&frr_pthread_list_mtx);  }  struct frr_pthread *frr_pthread_new(struct frr_pthread_attr *attr, @@ -94,11 +90,9 @@ struct frr_pthread *frr_pthread_new(struct frr_pthread_attr *attr,  	pthread_mutex_init(fpt->running_cond_mtx, NULL);  	pthread_cond_init(fpt->running_cond, NULL); -	pthread_mutex_lock(&frr_pthread_list_mtx); -	{ +	frr_with_mutex(&frr_pthread_list_mtx) {  		listnode_add(frr_pthread_list, fpt);  	} -	pthread_mutex_unlock(&frr_pthread_list_mtx);  	return fpt;  } @@ -162,23 +156,19 @@ int frr_pthread_run(struct frr_pthread *fpt, const pthread_attr_t *attr)  void frr_pthread_wait_running(struct frr_pthread *fpt)  { -	pthread_mutex_lock(fpt->running_cond_mtx); -	{ +	frr_with_mutex(fpt->running_cond_mtx) {  		while (!fpt->running)  			pthread_cond_wait(fpt->running_cond,  					  fpt->running_cond_mtx);  	} -	pthread_mutex_unlock(fpt->running_cond_mtx);  }  void frr_pthread_notify_running(struct frr_pthread *fpt)  { -	pthread_mutex_lock(fpt->running_cond_mtx); -	{ +	frr_with_mutex(fpt->running_cond_mtx) {  		fpt->running = true;  		pthread_cond_signal(fpt->running_cond);  	} -	pthread_mutex_unlock(fpt->running_cond_mtx);  }  int frr_pthread_stop(struct frr_pthread *fpt, void **result) @@ -190,14 +180,12 @@ int frr_pthread_stop(struct frr_pthread *fpt, void **result)  void frr_pthread_stop_all(void)  { -	pthread_mutex_lock(&frr_pthread_list_mtx); -	{ +	frr_with_mutex(&frr_pthread_list_mtx) {  		struct listnode *n;  		struct frr_pthread *fpt;  		for (ALL_LIST_ELEMENTS_RO(frr_pthread_list, n, fpt))  			frr_pthread_stop(fpt, NULL);  	} -	pthread_mutex_unlock(&frr_pthread_list_mtx);  }  /* diff --git a/lib/frr_pthread.h b/lib/frr_pthread.h index 6096a50370..f70c8a0db4 100644 --- a/lib/frr_pthread.h +++ b/lib/frr_pthread.h @@ -215,6 +215,54 @@ void frr_pthread_stop_all(void);  #define pthread_condattr_setclock(A, B)  #endif +/* mutex auto-lock/unlock */ + +/* variant 1: + * (for short blocks, multiple mutexes supported) + * break & return can be used for aborting the block + * + * frr_with_mutex(&mtx, &mtx2) { + *    if (error) + *       break; + *    ... + * } + */ +#define _frr_with_mutex(mutex)                                                 \ +	*NAMECTR(_mtx_) __attribute__((                                        \ +		unused, cleanup(_frr_mtx_unlock))) = _frr_mtx_lock(mutex),     \ +	/* end */ + +#define frr_with_mutex(...)                                                    \ +	for (pthread_mutex_t MACRO_REPEAT(_frr_with_mutex, ##__VA_ARGS__)      \ +	     *_once = NULL; _once == NULL; _once = (void *)1)                  \ +	/* end */ + +/* variant 2: + * (more suitable for long blocks, no extra indentation) + * + * frr_mutex_lock_autounlock(&mtx); + * ... + */ +#define frr_mutex_lock_autounlock(mutex)                                       \ +	pthread_mutex_t *NAMECTR(_mtx_)                                        \ +		__attribute__((unused, cleanup(_frr_mtx_unlock))) =            \ +				    _frr_mtx_lock(mutex)                       \ +	/* end */ + +static inline pthread_mutex_t *_frr_mtx_lock(pthread_mutex_t *mutex) +{ +	pthread_mutex_lock(mutex); +	return mutex; +} + +static inline void _frr_mtx_unlock(pthread_mutex_t **mutex) +{ +	if (!*mutex) +		return; +	pthread_mutex_unlock(*mutex); +	*mutex = NULL; +} +  #ifdef __cplusplus  }  #endif diff --git a/lib/frrcu.c b/lib/frrcu.c index 7e6475b648..54626f909d 100644 --- a/lib/frrcu.c +++ b/lib/frrcu.c @@ -55,7 +55,6 @@  #include "atomlist.h"  DEFINE_MTYPE_STATIC(LIB, RCU_THREAD,    "RCU thread") -DEFINE_MTYPE_STATIC(LIB, RCU_NEXT,      "RCU sequence barrier")  DECLARE_ATOMLIST(rcu_heads, struct rcu_head, head) @@ -226,7 +225,7 @@ static void rcu_bump(void)  {  	struct rcu_next *rn; -	rn = XMALLOC(MTYPE_RCU_NEXT, sizeof(*rn)); +	rn = XMALLOC(MTYPE_RCU_THREAD, sizeof(*rn));  	/* note: each RCUA_NEXT item corresponds to exactly one seqno bump.  	 * This means we don't need to communicate which seqno is which @@ -269,7 +268,7 @@ static void rcu_bump(void)  	 * "last item is being deleted - start over" case, and then we may end  	 * up accessing old RCU queue items that are already free'd.  	 */ -	rcu_free_internal(MTYPE_RCU_NEXT, rn, head_free); +	rcu_free_internal(MTYPE_RCU_THREAD, rn, head_free);  	/* Only allow the RCU sweeper to run after these 2 items are queued.  	 * diff --git a/lib/hash.c b/lib/hash.c index 9d9d39702e..7f8a237047 100644 --- a/lib/hash.c +++ b/lib/hash.c @@ -28,6 +28,7 @@  #include "vty.h"  #include "command.h"  #include "libfrr.h" +#include "frr_pthread.h"  DEFINE_MTYPE_STATIC(LIB, HASH, "Hash")  DEFINE_MTYPE_STATIC(LIB, HASH_BACKET, "Hash Bucket") @@ -54,14 +55,12 @@ struct hash *hash_create_size(unsigned int size,  	hash->name = name ? XSTRDUP(MTYPE_HASH, name) : NULL;  	hash->stats.empty = hash->size; -	pthread_mutex_lock(&_hashes_mtx); -	{ +	frr_with_mutex(&_hashes_mtx) {  		if (!_hashes)  			_hashes = list_new();  		listnode_add(_hashes, hash);  	} -	pthread_mutex_unlock(&_hashes_mtx);  	return hash;  } @@ -311,8 +310,7 @@ struct list *hash_to_list(struct hash *hash)  void hash_free(struct hash *hash)  { -	pthread_mutex_lock(&_hashes_mtx); -	{ +	frr_with_mutex(&_hashes_mtx) {  		if (_hashes) {  			listnode_delete(_hashes, hash);  			if (_hashes->count == 0) { @@ -320,7 +318,6 @@ void hash_free(struct hash *hash)  			}  		}  	} -	pthread_mutex_unlock(&_hashes_mtx);  	XFREE(MTYPE_HASH, hash->name); @@ -31,6 +31,7 @@  #include "lib_errors.h"  #include "lib/hook.h"  #include "printfrr.h" +#include "frr_pthread.h"  #ifndef SUNOS_5  #include <sys/un.h> @@ -83,89 +84,70 @@ static int zlog_filter_lookup(const char *lookup)  void zlog_filter_clear(void)  { -	pthread_mutex_lock(&loglock); -	zlog_filter_count = 0; -	pthread_mutex_unlock(&loglock); +	frr_with_mutex(&loglock) { +		zlog_filter_count = 0; +	}  }  int zlog_filter_add(const char *filter)  { -	pthread_mutex_lock(&loglock); +	frr_with_mutex(&loglock) { +		if (zlog_filter_count >= ZLOG_FILTERS_MAX) +			return 1; -	int ret = 0; +		if (zlog_filter_lookup(filter) != -1) +			/* Filter already present */ +			return -1; -	if (zlog_filter_count >= ZLOG_FILTERS_MAX) { -		ret = 1; -		goto done; -	} +		strlcpy(zlog_filters[zlog_filter_count], filter, +			sizeof(zlog_filters[0])); -	if (zlog_filter_lookup(filter) != -1) { -		/* Filter already present */ -		ret = -1; -		goto done; -	} +		if (zlog_filters[zlog_filter_count][0] == '\0') +			/* Filter was either empty or didn't get copied +			 * correctly +			 */ +			return -1; -	strlcpy(zlog_filters[zlog_filter_count], filter, -		sizeof(zlog_filters[0])); - -	if (zlog_filters[zlog_filter_count][0] == '\0') { -		/* Filter was either empty or didn't get copied correctly */ -		ret = -1; -		goto done; +		zlog_filter_count++;  	} - -	zlog_filter_count++; - -done: -	pthread_mutex_unlock(&loglock); -	return ret; +	return 0;  }  int zlog_filter_del(const char *filter)  { -	pthread_mutex_lock(&loglock); - -	int found_idx = zlog_filter_lookup(filter); -	int last_idx = zlog_filter_count - 1; -	int ret = 0; +	frr_with_mutex(&loglock) { +		int found_idx = zlog_filter_lookup(filter); +		int last_idx = zlog_filter_count - 1; -	if (found_idx == -1) { -		/* Didn't find the filter to delete */ -		ret = -1; -		goto done; -	} - -	/* Adjust the filter array */ -	memmove(zlog_filters[found_idx], zlog_filters[found_idx + 1], -		(last_idx - found_idx) * sizeof(zlog_filters[0])); +		if (found_idx == -1) +			/* Didn't find the filter to delete */ +			return -1; -	zlog_filter_count--; +		/* Adjust the filter array */ +		memmove(zlog_filters[found_idx], zlog_filters[found_idx + 1], +			(last_idx - found_idx) * sizeof(zlog_filters[0])); -done: -	pthread_mutex_unlock(&loglock); -	return ret; +		zlog_filter_count--; +	} +	return 0;  }  /* Dump all filters to buffer, delimited by new line */  int zlog_filter_dump(char *buf, size_t max_size)  { -	pthread_mutex_lock(&loglock); - -	int ret = 0;  	int len = 0; -	for (int i = 0; i < zlog_filter_count; i++) { -		ret = snprintf(buf + len, max_size - len, " %s\n", -			       zlog_filters[i]); -		len += ret; -		if ((ret < 0) || ((size_t)len >= max_size)) { -			len = -1; -			goto done; +	frr_with_mutex(&loglock) { +		for (int i = 0; i < zlog_filter_count; i++) { +			int ret; +			ret = snprintf(buf + len, max_size - len, " %s\n", +				       zlog_filters[i]); +			len += ret; +			if ((ret < 0) || ((size_t)len >= max_size)) +				return -1;  		}  	} -done: -	pthread_mutex_unlock(&loglock);  	return len;  } @@ -363,7 +345,7 @@ search:  /* va_list version of zlog. */  void vzlog(int priority, const char *format, va_list args)  { -	pthread_mutex_lock(&loglock); +	frr_mutex_lock_autounlock(&loglock);  	char proto_str[32] = "";  	int original_errno = errno; @@ -430,36 +412,31 @@ out:  	if (msg != buf)  		XFREE(MTYPE_TMP, msg);  	errno = original_errno; -	pthread_mutex_unlock(&loglock);  }  int vzlog_test(int priority)  { -	pthread_mutex_lock(&loglock); - -	int ret = 0; +	frr_mutex_lock_autounlock(&loglock);  	struct zlog *zl = zlog_default;  	/* When zlog_default is also NULL, use stderr for logging. */  	if (zl == NULL) -		ret = 1; +		return 1;  	/* Syslog output */  	else if (priority <= zl->maxlvl[ZLOG_DEST_SYSLOG]) -		ret = 1; +		return 1;  	/* File output. */  	else if ((priority <= zl->maxlvl[ZLOG_DEST_FILE]) && zl->fp) -		ret = 1; +		return 1;  	/* stdout output. */  	else if (priority <= zl->maxlvl[ZLOG_DEST_STDOUT]) -		ret = 1; +		return 1;  	/* Terminal monitor. */  	else if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR]) -		ret = 1; - -	pthread_mutex_unlock(&loglock); +		return 1; -	return ret; +	return 0;  }  /* @@ -870,9 +847,9 @@ void openzlog(const char *progname, const char *protoname,  	openlog(progname, syslog_flags, zl->facility); -	pthread_mutex_lock(&loglock); -	zlog_default = zl; -	pthread_mutex_unlock(&loglock); +	frr_with_mutex(&loglock) { +		zlog_default = zl; +	}  #ifdef HAVE_GLIBC_BACKTRACE  	/* work around backtrace() using lazily resolved dynamically linked @@ -889,7 +866,8 @@ void openzlog(const char *progname, const char *protoname,  void closezlog(void)  { -	pthread_mutex_lock(&loglock); +	frr_mutex_lock_autounlock(&loglock); +  	struct zlog *zl = zlog_default;  	closelog(); @@ -901,15 +879,14 @@ void closezlog(void)  	XFREE(MTYPE_ZLOG, zl);  	zlog_default = NULL; -	pthread_mutex_unlock(&loglock);  }  /* Called from command.c. */  void zlog_set_level(zlog_dest_t dest, int log_level)  { -	pthread_mutex_lock(&loglock); -	zlog_default->maxlvl[dest] = log_level; -	pthread_mutex_unlock(&loglock); +	frr_with_mutex(&loglock) { +		zlog_default->maxlvl[dest] = log_level; +	}  }  int zlog_set_file(const char *filename, int log_level) @@ -929,15 +906,15 @@ int zlog_set_file(const char *filename, int log_level)  	if (fp == NULL) {  		ret = 0;  	} else { -		pthread_mutex_lock(&loglock); -		zl = zlog_default; - -		/* Set flags. */ -		zl->filename = XSTRDUP(MTYPE_ZLOG, filename); -		zl->maxlvl[ZLOG_DEST_FILE] = log_level; -		zl->fp = fp; -		logfile_fd = fileno(fp); -		pthread_mutex_unlock(&loglock); +		frr_with_mutex(&loglock) { +			zl = zlog_default; + +			/* Set flags. */ +			zl->filename = XSTRDUP(MTYPE_ZLOG, filename); +			zl->maxlvl[ZLOG_DEST_FILE] = log_level; +			zl->fp = fp; +			logfile_fd = fileno(fp); +		}  	}  	return ret; @@ -946,7 +923,7 @@ int zlog_set_file(const char *filename, int log_level)  /* Reset opend file. */  int zlog_reset_file(void)  { -	pthread_mutex_lock(&loglock); +	frr_mutex_lock_autounlock(&loglock);  	struct zlog *zl = zlog_default; @@ -959,8 +936,6 @@ int zlog_reset_file(void)  	XFREE(MTYPE_ZLOG, zl->filename);  	zl->filename = NULL; -	pthread_mutex_unlock(&loglock); -  	return 1;  } diff --git a/lib/monotime.h b/lib/monotime.h index ca27c45dc6..e246f177de 100644 --- a/lib/monotime.h +++ b/lib/monotime.h @@ -84,6 +84,20 @@ static inline int64_t monotime_until(const struct timeval *ref,  	return (int64_t)tv.tv_sec * 1000000LL + tv.tv_usec;  } +static inline time_t monotime_to_realtime(const struct timeval *mono, +					  struct timeval *realout) +{ +	struct timeval delta, real; + +	monotime_since(mono, &delta); +	gettimeofday(&real, NULL); + +	timersub(&real, &delta, &real); +	if (realout) +		*realout = real; +	return real.tv_sec; +} +  /* Char buffer size for time-to-string api */  #define MONOTIME_STRLEN 32 diff --git a/lib/nexthop.c b/lib/nexthop.c index 0984c1a168..cf5bed3d62 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -65,9 +65,8 @@ static int _nexthop_labels_cmp(const struct nexthop *nh1,  	return memcmp(nhl1->label, nhl2->label, nhl1->num_labels);  } -static int _nexthop_g_addr_cmp(enum nexthop_types_t type, -			       const union g_addr *addr1, -			       const union g_addr *addr2) +int nexthop_g_addr_cmp(enum nexthop_types_t type, const union g_addr *addr1, +		       const union g_addr *addr2)  {  	int ret = 0; @@ -92,13 +91,13 @@ static int _nexthop_g_addr_cmp(enum nexthop_types_t type,  static int _nexthop_gateway_cmp(const struct nexthop *nh1,  				const struct nexthop *nh2)  { -	return _nexthop_g_addr_cmp(nh1->type, &nh1->gate, &nh2->gate); +	return nexthop_g_addr_cmp(nh1->type, &nh1->gate, &nh2->gate);  }  static int _nexthop_source_cmp(const struct nexthop *nh1,  			       const struct nexthop *nh2)  { -	return _nexthop_g_addr_cmp(nh1->type, &nh1->src, &nh2->src); +	return nexthop_g_addr_cmp(nh1->type, &nh1->src, &nh2->src);  }  static int _nexthop_cmp_no_labels(const struct nexthop *next1, diff --git a/lib/nexthop.h b/lib/nexthop.h index 20401cd581..9dd5fc6fd3 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -142,6 +142,9 @@ extern bool nexthop_same(const struct nexthop *nh1, const struct nexthop *nh2);  extern bool nexthop_same_no_labels(const struct nexthop *nh1,  				   const struct nexthop *nh2);  extern int nexthop_cmp(const struct nexthop *nh1, const struct nexthop *nh2); +extern int nexthop_g_addr_cmp(enum nexthop_types_t type, +			      const union g_addr *addr1, +			      const union g_addr *addr2);  extern const char *nexthop_type_to_str(enum nexthop_types_t nh_type);  extern bool nexthop_labels_match(const struct nexthop *nh1, diff --git a/lib/nexthop_group.c b/lib/nexthop_group.c index 5602018b30..abe2096cec 100644 --- a/lib/nexthop_group.c +++ b/lib/nexthop_group.c @@ -60,6 +60,16 @@ nexthop_group_cmd_compare(const struct nexthop_group_cmd *nhgc1,  	return strcmp(nhgc1->name, nhgc2->name);  } +static struct nexthop *nexthop_group_tail(const struct nexthop_group *nhg) +{ +	struct nexthop *nexthop = nhg->nexthop; + +	while (nexthop && nexthop->next) +		nexthop = nexthop->next; + +	return nexthop; +} +  uint8_t nexthop_group_nexthop_num(const struct nexthop_group *nhg)  {  	struct nexthop *nhop; @@ -129,7 +139,20 @@ void _nexthop_add(struct nexthop **target, struct nexthop *nexthop)  void _nexthop_group_add_sorted(struct nexthop_group *nhg,  			       struct nexthop *nexthop)  { -	struct nexthop *position, *prev; +	struct nexthop *position, *prev, *tail; + +	/* Try to just append to the end first +	 * This trust it is already sorted +	 */ + +	tail = nexthop_group_tail(nhg); + +	if (tail && (nexthop_cmp(tail, nexthop) < 0)) { +		tail->next = nexthop; +		nexthop->prev = tail; + +		return; +	}  	for (position = nhg->nexthop, prev = NULL; position;  	     prev = position, position = position->next) { diff --git a/lib/northbound.c b/lib/northbound.c index 48b450e969..a814f23e14 100644 --- a/lib/northbound.c +++ b/lib/northbound.c @@ -26,6 +26,7 @@  #include "command.h"  #include "debug.h"  #include "db.h" +#include "frr_pthread.h"  #include "northbound.h"  #include "northbound_cli.h"  #include "northbound_db.h" @@ -723,8 +724,7 @@ int nb_running_lock(enum nb_client client, const void *user)  {  	int ret = -1; -	pthread_mutex_lock(&running_config_mgmt_lock.mtx); -	{ +	frr_with_mutex(&running_config_mgmt_lock.mtx) {  		if (!running_config_mgmt_lock.locked) {  			running_config_mgmt_lock.locked = true;  			running_config_mgmt_lock.owner_client = client; @@ -732,7 +732,6 @@ int nb_running_lock(enum nb_client client, const void *user)  			ret = 0;  		}  	} -	pthread_mutex_unlock(&running_config_mgmt_lock.mtx);  	return ret;  } @@ -741,8 +740,7 @@ int nb_running_unlock(enum nb_client client, const void *user)  {  	int ret = -1; -	pthread_mutex_lock(&running_config_mgmt_lock.mtx); -	{ +	frr_with_mutex(&running_config_mgmt_lock.mtx) {  		if (running_config_mgmt_lock.locked  		    && running_config_mgmt_lock.owner_client == client  		    && running_config_mgmt_lock.owner_user == user) { @@ -752,7 +750,6 @@ int nb_running_unlock(enum nb_client client, const void *user)  			ret = 0;  		}  	} -	pthread_mutex_unlock(&running_config_mgmt_lock.mtx);  	return ret;  } @@ -761,14 +758,12 @@ int nb_running_lock_check(enum nb_client client, const void *user)  {  	int ret = -1; -	pthread_mutex_lock(&running_config_mgmt_lock.mtx); -	{ +	frr_with_mutex(&running_config_mgmt_lock.mtx) {  		if (!running_config_mgmt_lock.locked  		    || (running_config_mgmt_lock.owner_client == client  			&& running_config_mgmt_lock.owner_user == user))  			ret = 0;  	} -	pthread_mutex_unlock(&running_config_mgmt_lock.mtx);  	return ret;  } diff --git a/lib/northbound_sysrepo.c b/lib/northbound_sysrepo.c index 44a55137f8..77183282ba 100644 --- a/lib/northbound_sysrepo.c +++ b/lib/northbound_sysrepo.c @@ -425,7 +425,7 @@ static int frr_sr_state_cb(const char *xpath, sr_val_t **values,  exit:  	list_delete(&elements);  	*values = NULL; -	values_cnt = 0; +	*values_cnt = 0;  	return SR_ERR_OK;  } diff --git a/lib/prefix.c b/lib/prefix.c index ad8dea273e..35b679ab90 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -441,7 +441,7 @@ void prefix_hexdump(const struct prefix *p)  	zlog_hexdump(p, sizeof(struct prefix));  } -int is_zero_mac(struct ethaddr *mac) +int is_zero_mac(const struct ethaddr *mac)  {  	int i = 0; diff --git a/lib/prefix.h b/lib/prefix.h index e338140f1a..24c146e022 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -470,7 +470,7 @@ extern void masklen2ip6(const int, struct in6_addr *);  extern const char *inet6_ntoa(struct in6_addr); -extern int is_zero_mac(struct ethaddr *mac); +extern int is_zero_mac(const struct ethaddr *mac);  extern int prefix_str2mac(const char *str, struct ethaddr *mac);  extern char *prefix_mac2str(const struct ethaddr *mac, char *buf, int size); diff --git a/lib/privs.c b/lib/privs.c index a3314c6c3c..09efedf684 100644 --- a/lib/privs.c +++ b/lib/privs.c @@ -24,6 +24,7 @@  #include "log.h"  #include "privs.h"  #include "memory.h" +#include "frr_pthread.h"  #include "lib_errors.h"  #include "lib/queue.h" @@ -760,8 +761,7 @@ struct zebra_privs_t *_zprivs_raise(struct zebra_privs_t *privs,  	 * Serialize 'raise' operations; particularly important for  	 * OSes where privs are process-wide.  	 */ -	pthread_mutex_lock(&(privs->mutex)); -	{ +	frr_with_mutex(&(privs->mutex)) {  		/* Locate ref-counting object to use */  		refs = get_privs_refs(privs); @@ -775,7 +775,6 @@ struct zebra_privs_t *_zprivs_raise(struct zebra_privs_t *privs,  			refs->raised_in_funcname = funcname;  		}  	} -	pthread_mutex_unlock(&(privs->mutex));  	return privs;  } @@ -791,8 +790,7 @@ void _zprivs_lower(struct zebra_privs_t **privs)  	/* Serialize 'lower privs' operation - particularly important  	 * when OS privs are process-wide.  	 */ -	pthread_mutex_lock(&(*privs)->mutex); -	{ +	frr_with_mutex(&(*privs)->mutex) {  		refs = get_privs_refs(*privs);  		if (--(refs->refcount) == 0) { @@ -806,7 +804,6 @@ void _zprivs_lower(struct zebra_privs_t **privs)  			refs->raised_in_funcname = NULL;  		}  	} -	pthread_mutex_unlock(&(*privs)->mutex);  	*privs = NULL;  } diff --git a/lib/privs.h b/lib/privs.h index 2b0b44b3f2..db5707d675 100644 --- a/lib/privs.h +++ b/lib/privs.h @@ -109,16 +109,16 @@ extern void zprivs_get_ids(struct zprivs_ids_t *);  /*   * Wrapper around zprivs, to be used as: - *   frr_elevate_privs(&privs) { + *   frr_with_privs(&privs) {   *     ... code ...   *     if (error)   *       break;         -- break can be used to get out of the block   *     ... code ...   *   }   * - * The argument to frr_elevate_privs() can be NULL to leave privileges as-is + * The argument to frr_with_privs() can be NULL to leave privileges as-is   * (mostly useful for conditional privilege-raising, i.e.:) - *   frr_elevate_privs(cond ? &privs : NULL) {} + *   frr_with_privs(cond ? &privs : NULL) {}   *   * NB: The code block is always executed, regardless of whether privileges   * could be raised or not, or whether NULL was given or not.  This is fully @@ -138,7 +138,7 @@ extern struct zebra_privs_t *_zprivs_raise(struct zebra_privs_t *privs,  					   const char *funcname);  extern void _zprivs_lower(struct zebra_privs_t **privs); -#define frr_elevate_privs(privs)                                               \ +#define frr_with_privs(privs)                                               \  	for (struct zebra_privs_t *_once = NULL,                               \  				  *_privs __attribute__(                       \  					  (unused, cleanup(_zprivs_lower))) =  \ diff --git a/lib/pullwr.c b/lib/pullwr.c new file mode 100644 index 0000000000..0c326f29d4 --- /dev/null +++ b/lib/pullwr.c @@ -0,0 +1,275 @@ +/* + * Pull-driven write event handler + * Copyright (C) 2019  David Lamparter + * + * 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 "pullwr.h" +#include "memory.h" +#include "monotime.h" + +/* defaults */ +#define PULLWR_THRESH	16384	/* size at which we start to call write() */ +#define PULLWR_MAXSPIN	2500	/* max µs to spend grabbing more data */ + +struct pullwr { +	int fd; +	struct thread_master *tm; +	/* writer == NULL <=> we're idle */ +	struct thread *writer; + +	void *arg; +	void (*fill)(void *, struct pullwr *); +	void (*err)(void *, struct pullwr *, bool); + +	/* ring buffer (although it's "un-ringed" on resizing, it WILL wrap +	 * around if data is trickling in while keeping it at a constant size) +	 */ +	size_t bufsz, valid, pos; +	uint64_t total_written; +	char *buffer; + +	size_t thresh;		/* PULLWR_THRESH */ +	int64_t maxspin;	/* PULLWR_MAXSPIN */ +}; + +DEFINE_MTYPE_STATIC(LIB, PULLWR_HEAD, "pull-driven write controller") +DEFINE_MTYPE_STATIC(LIB, PULLWR_BUF,  "pull-driven write buffer") + +static int pullwr_run(struct thread *t); + +struct pullwr *_pullwr_new(struct thread_master *tm, int fd, +		void *arg, +		void (*fill)(void *, struct pullwr *), +		void (*err)(void *, struct pullwr *, bool)) +{ +	struct pullwr *pullwr; + +	pullwr = XCALLOC(MTYPE_PULLWR_HEAD, sizeof(*pullwr)); +	pullwr->fd = fd; +	pullwr->tm = tm; +	pullwr->arg = arg; +	pullwr->fill = fill; +	pullwr->err = err; + +	pullwr->thresh = PULLWR_THRESH; +	pullwr->maxspin = PULLWR_MAXSPIN; + +	return pullwr; +} + +void pullwr_del(struct pullwr *pullwr) +{ +	THREAD_OFF(pullwr->writer); + +	XFREE(MTYPE_PULLWR_BUF, pullwr->buffer); +	XFREE(MTYPE_PULLWR_HEAD, pullwr); +} + +void pullwr_cfg(struct pullwr *pullwr, int64_t max_spin_usec, +		size_t write_threshold) +{ +	pullwr->maxspin = max_spin_usec ?: PULLWR_MAXSPIN; +	pullwr->thresh = write_threshold ?: PULLWR_THRESH; +} + +void pullwr_bump(struct pullwr *pullwr) +{ +	if (pullwr->writer) +		return; + +	thread_add_timer(pullwr->tm, pullwr_run, pullwr, 0, &pullwr->writer); +} + +static size_t pullwr_iov(struct pullwr *pullwr, struct iovec *iov) +{ +	size_t len1; + +	if (pullwr->valid == 0) +		return 0; + +	if (pullwr->pos + pullwr->valid <= pullwr->bufsz) { +		iov[0].iov_base = pullwr->buffer + pullwr->pos; +		iov[0].iov_len = pullwr->valid; +		return 1; +	} + +	len1 = pullwr->bufsz - pullwr->pos; + +	iov[0].iov_base = pullwr->buffer + pullwr->pos; +	iov[0].iov_len = len1; +	iov[1].iov_base = pullwr->buffer; +	iov[1].iov_len = pullwr->valid - len1; +	return 2; +} + +static void pullwr_resize(struct pullwr *pullwr, size_t need) +{ +	struct iovec iov[2]; +	size_t niov, newsize; +	char *newbuf; + +	/* the buffer is maintained at pullwr->thresh * 2 since we'll be +	 * trying to fill it as long as it's anywhere below pullwr->thresh. +	 * That means we frequently end up a little short of it and then write +	 * something that goes over the threshold.  So, just use double. +	 */ +	if (need) { +		/* resize up */ +		if (pullwr->bufsz - pullwr->valid >= need) +			return; + +		newsize = MAX((pullwr->valid + need) * 2, pullwr->thresh * 2); +		newbuf = XMALLOC(MTYPE_PULLWR_BUF, newsize); +	} else if (!pullwr->valid) { +		/* resize down, buffer empty */ +		newsize = 0; +		newbuf = NULL; +	} else { +		/* resize down */ +		if (pullwr->bufsz - pullwr->valid < pullwr->thresh) +			return; +		newsize = MAX(pullwr->valid, pullwr->thresh * 2); +		newbuf = XMALLOC(MTYPE_PULLWR_BUF, newsize); +	} + +	niov = pullwr_iov(pullwr, iov); +	if (niov >= 1) { +		memcpy(newbuf, iov[0].iov_base, iov[0].iov_len); +		if (niov >= 2) +			memcpy(newbuf + iov[0].iov_len, +				iov[1].iov_base, iov[1].iov_len); +	} + +	XFREE(MTYPE_PULLWR_BUF, pullwr->buffer); +	pullwr->buffer = newbuf; +	pullwr->bufsz = newsize; +	pullwr->pos = 0; +} + +void pullwr_write(struct pullwr *pullwr, const void *data, size_t len) +{ +	pullwr_resize(pullwr, len); + +	if (pullwr->pos + pullwr->valid > pullwr->bufsz) { +		size_t pos; + +		pos = (pullwr->pos + pullwr->valid) % pullwr->bufsz; +		memcpy(pullwr->buffer + pos, data, len); +	} else { +		size_t max1, len1; +		max1 = pullwr->bufsz - (pullwr->pos + pullwr->valid); +		max1 = MIN(max1, len); + +		memcpy(pullwr->buffer + pullwr->pos + pullwr->valid, +				data, max1); +		len1 = len - max1; + +		if (len1) +			memcpy(pullwr->buffer, (char *)data + max1, len1); + +	} +	pullwr->valid += len; + +	pullwr_bump(pullwr); +} + +static int pullwr_run(struct thread *t) +{ +	struct pullwr *pullwr = THREAD_ARG(t); +	struct iovec iov[2]; +	size_t niov, lastvalid; +	ssize_t nwr; +	struct timeval t0; +	bool maxspun = false; + +	monotime(&t0); + +	do { +		lastvalid = pullwr->valid - 1; +		while (pullwr->valid < pullwr->thresh +				&& pullwr->valid != lastvalid +				&& !maxspun) { +			lastvalid = pullwr->valid; +			pullwr->fill(pullwr->arg, pullwr); + +			/* check after doing at least one fill() call so we +			 * don't spin without making progress on slow boxes +			 */ +			if (!maxspun && monotime_since(&t0, NULL) +					>= pullwr->maxspin) +				maxspun = true; +		} + +		if (pullwr->valid == 0) { +			/* we made a fill() call above that didn't feed any +			 * data in, and we have nothing more queued, so we go +			 * into idle, i.e. no calling thread_add_write() +			 */ +			pullwr_resize(pullwr, 0); +			return 0; +		} + +		niov = pullwr_iov(pullwr, iov); +		assert(niov); + +		nwr = writev(pullwr->fd, iov, niov); +		if (nwr < 0) { +			if (errno == EAGAIN || errno == EWOULDBLOCK) +				break; +			pullwr->err(pullwr->arg, pullwr, false); +			return 0; +		} + +		if (nwr == 0) { +			pullwr->err(pullwr->arg, pullwr, true); +			return 0; +		} + +		pullwr->total_written += nwr; +		pullwr->valid -= nwr; +		pullwr->pos += nwr; +		pullwr->pos %= pullwr->bufsz; +	} while (pullwr->valid == 0 && !maxspun); +	/* pullwr->valid != 0 implies we did an incomplete write, i.e. socket +	 * is full and we go wait until it's available for writing again. +	 */ + +	thread_add_write(pullwr->tm, pullwr_run, pullwr, pullwr->fd, +			&pullwr->writer); + +	/* if we hit the time limit, just keep the buffer, we'll probably need +	 * it anyway & another run is already coming up. +	 */ +	if (!maxspun) +		pullwr_resize(pullwr, 0); +	return 0; +} + +void pullwr_stats(struct pullwr *pullwr, uint64_t *total_written, +		  size_t *pending, size_t *kernel_pending) +{ +	int tmp; + +	*total_written = pullwr->total_written; +	*pending = pullwr->valid; + +	if (ioctl(pullwr->fd, TIOCOUTQ, &tmp) != 0) +		tmp = 0; +	*kernel_pending = tmp; +} diff --git a/lib/pullwr.h b/lib/pullwr.h new file mode 100644 index 0000000000..601eac1b79 --- /dev/null +++ b/lib/pullwr.h @@ -0,0 +1,110 @@ +/* + * Pull-driven write event handler + * Copyright (C) 2019  David Lamparter + * + * 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 _WRITEPOLL_H +#define _WRITEPOLL_H + +#include <stdbool.h> +#include <stdint.h> + +#include "thread.h" +#include "stream.h" + +struct pullwr; + +/* This is a "pull-driven" write event handler.  Instead of having some buffer + * or being driven by the availability of data, it triggers on the space being + * available on the socket for data to be written on and then calls fill() to + * get data to be sent. + * + * pullwr_* maintains an "idle" vs. "active" state, going into idle when a + * fill() call completes without feeing more data into it.  The overall + * semantics are: + * - to put data out, call pullwr_write().  This is possible from both inside + *   fill() callbacks or anywhere else.  Doing so puts the pullwr into + *   active state. + * - in active state, the fill() callback will be called and should feed more + *   data in.  It should NOT loop to push out more than one "unit" of data; + *   the pullwr code handles this by calling fill() until it has enough data. + * - if there's nothing more to be sent, fill() returns without doing anything + *   and pullwr goes into idle state after flushing all buffered data out. + * - when new data becomes available, pullwr_bump() should be called to put + *   the pullwr back into active mode so it will collect data from fill(), + *   or you can directly call pullwr_write(). + * - only calling pullwr_write() from within fill() is the cleanest way of + *   doing things. + * + * When the err() callback is called, the pullwr should be considered unusable + * and released with pullwr_del().  This can be done from inside the callback, + * the pullwr code holds no more references on it when calling err(). + */ +extern struct pullwr *_pullwr_new(struct thread_master *tm, int fd, +		void *arg, +		void (*fill)(void *, struct pullwr *), +		void (*err)(void *, struct pullwr *, bool eof)); +extern void pullwr_del(struct pullwr *pullwr); + +/* type-checking wrapper.  makes sure fill() and err() take a first argument + * whose type is identical to the type of arg. + * => use "void fill(struct mystruct *arg, ...)" - no "void *arg" + */ +#define pullwr_new(tm, fd, arg, fill, err) ({                                  \ +	void (*fill_typechk)(typeof(arg), struct pullwr *) = fill;          \ +	void (*err_typechk)(typeof(arg), struct pullwr *, bool) = err;      \ +	_pullwr_new(tm, fd, arg, (void *)fill_typechk, (void *)err_typechk);   \ +}) + +/* max_spin_usec is the time after which the pullwr event handler will stop + *   trying to get more data from fill() and yield control back to the + *   thread_master.  It does reschedule itself to continue later; this is + *   only to make sure we don't freeze the entire process if we're piping a + *   lot of data to a local endpoint that reads quickly (i.e. no backpressure) + * + *   default: 2500 (2.5 ms) + * + * write_threshold is the amount of data buffered from fill() calls at which + *   the pullwr code starts calling write().  But this is not a "limit". + *   pullwr will keep poking fill() for more data until + *   (a) max_spin_usec is reached; fill() will be called again later after + *       returning to the thread_master to give other events a chance to run + *   (b) fill() returns without pushing any data onto the pullwr with + *       pullwr_write(), so fill() will NOT be called again until a call to + *       pullwr_bump() or pullwr_write() comes in. + * + *   default: 16384 (16 kB) + * + * passing 0 for either value (or not calling it at all) uses the default. + */ +extern void pullwr_cfg(struct pullwr *pullwr, int64_t max_spin_usec, +		       size_t write_threshold); + +extern void pullwr_bump(struct pullwr *pullwr); +extern void pullwr_write(struct pullwr *pullwr, +		const void *data, size_t len); + +static inline void pullwr_write_stream(struct pullwr *pullwr, +		struct stream *s) +{ +	pullwr_write(pullwr, s->data, stream_get_endp(s)); +} + +extern void pullwr_stats(struct pullwr *pullwr, uint64_t *total_written, +			 size_t *pending, size_t *kernel_pending); + +#endif /* _WRITEPOLL_H */ diff --git a/lib/routemap.c b/lib/routemap.c index eca02e8366..fc15183bf9 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -474,7 +474,7 @@ int generic_match_add(struct vty *vty, struct route_map_index *index,  		      const char *command, const char *arg,  		      route_map_event_t type)  { -	int ret; +	enum rmap_compile_rets ret;  	ret = route_map_add_match(index, command, arg, type);  	switch (ret) { @@ -493,6 +493,11 @@ int generic_match_add(struct vty *vty, struct route_map_index *index,  			frr_protonameinst);  		return CMD_WARNING_CONFIG_FAILED;  		break; +	case RMAP_DUPLICATE_RULE: +		/* +		 * Nothing to do here move along +		 */ +		break;  	}  	return CMD_SUCCESS; @@ -502,7 +507,7 @@ int generic_match_delete(struct vty *vty, struct route_map_index *index,  			 const char *command, const char *arg,  			 route_map_event_t type)  { -	int ret; +	enum rmap_compile_rets ret;  	int retval = CMD_SUCCESS;  	char *dep_name = NULL;  	const char *tmpstr; @@ -537,6 +542,11 @@ int generic_match_delete(struct vty *vty, struct route_map_index *index,  		if (type != RMAP_EVENT_MATCH_DELETED && dep_name)  			route_map_upd8_dependency(type, dep_name, rmap_name);  		break; +	case RMAP_DUPLICATE_RULE: +		/* +		 * Nothing to do here +		 */ +		break;  	}  	XFREE(MTYPE_ROUTE_MAP_RULE, dep_name); @@ -548,7 +558,7 @@ int generic_match_delete(struct vty *vty, struct route_map_index *index,  int generic_set_add(struct vty *vty, struct route_map_index *index,  		    const char *command, const char *arg)  { -	int ret; +	enum rmap_compile_rets ret;  	ret = route_map_add_set(index, command, arg);  	switch (ret) { @@ -563,6 +573,7 @@ int generic_set_add(struct vty *vty, struct route_map_index *index,  		return CMD_WARNING_CONFIG_FAILED;  		break;  	case RMAP_COMPILE_SUCCESS: +	case RMAP_DUPLICATE_RULE:  		break;  	} @@ -572,7 +583,7 @@ int generic_set_add(struct vty *vty, struct route_map_index *index,  int generic_set_delete(struct vty *vty, struct route_map_index *index,  		       const char *command, const char *arg)  { -	int ret; +	enum rmap_compile_rets ret;  	ret = route_map_delete_set(index, command, arg);  	switch (ret) { @@ -587,6 +598,7 @@ int generic_set_delete(struct vty *vty, struct route_map_index *index,  		return CMD_WARNING_CONFIG_FAILED;  		break;  	case RMAP_COMPILE_SUCCESS: +	case RMAP_DUPLICATE_RULE:  		break;  	} @@ -1388,8 +1400,10 @@ static route_map_event_t get_route_map_delete_event(route_map_event_t type)  }  /* Add match statement to route map. */ -int route_map_add_match(struct route_map_index *index, const char *match_name, -			const char *match_arg, route_map_event_t type) +enum rmap_compile_rets route_map_add_match(struct route_map_index *index, +					   const char *match_name, +					   const char *match_arg, +					   route_map_event_t type)  {  	struct route_map_rule *rule;  	struct route_map_rule *next; @@ -1464,15 +1478,16 @@ int route_map_add_match(struct route_map_index *index, const char *match_name,  }  /* Delete specified route match rule. */ -int route_map_delete_match(struct route_map_index *index, -			   const char *match_name, const char *match_arg) +enum rmap_compile_rets route_map_delete_match(struct route_map_index *index, +					      const char *match_name, +					      const char *match_arg)  {  	struct route_map_rule *rule;  	struct route_map_rule_cmd *cmd;  	cmd = route_map_lookup_match(match_name);  	if (cmd == NULL) -		return 1; +		return RMAP_RULE_MISSING;  	for (rule = index->match_list.head; rule; rule = rule->next)  		if (rule->cmd == cmd && (rulecmp(rule->rule_str, match_arg) == 0 @@ -1485,15 +1500,16 @@ int route_map_delete_match(struct route_map_index *index,  					index->map->name,  					RMAP_EVENT_CALL_ADDED);  			} -			return 0; +			return RMAP_COMPILE_SUCCESS;  		}  	/* Can't find matched rule. */ -	return 1; +	return RMAP_RULE_MISSING;  }  /* Add route-map set statement to the route map. */ -int route_map_add_set(struct route_map_index *index, const char *set_name, -		      const char *set_arg) +enum rmap_compile_rets route_map_add_set(struct route_map_index *index, +					 const char *set_name, +					 const char *set_arg)  {  	struct route_map_rule *rule;  	struct route_map_rule *next; @@ -1543,15 +1559,16 @@ int route_map_add_set(struct route_map_index *index, const char *set_name,  }  /* Delete route map set rule. */ -int route_map_delete_set(struct route_map_index *index, const char *set_name, -			 const char *set_arg) +enum rmap_compile_rets route_map_delete_set(struct route_map_index *index, +					    const char *set_name, +					    const char *set_arg)  {  	struct route_map_rule *rule;  	struct route_map_rule_cmd *cmd;  	cmd = route_map_lookup_set(set_name);  	if (cmd == NULL) -		return 1; +		return RMAP_RULE_MISSING;  	for (rule = index->set_list.head; rule; rule = rule->next)  		if ((rule->cmd == cmd) && (rulecmp(rule->rule_str, set_arg) == 0 @@ -1564,10 +1581,10 @@ int route_map_delete_set(struct route_map_index *index, const char *set_name,  					index->map->name,  					RMAP_EVENT_CALL_ADDED);  			} -			return 0; +			return RMAP_COMPILE_SUCCESS;  		}  	/* Can't find matched rule. */ -	return 1; +	return RMAP_RULE_MISSING;  }  static enum route_map_cmd_result_t diff --git a/lib/routemap.h b/lib/routemap.h index f9ad0f64a9..40525987e9 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -126,7 +126,7 @@ struct route_map_rule_cmd {  };  /* Route map apply error. */ -enum { +enum rmap_compile_rets {  	RMAP_COMPILE_SUCCESS,  	/* Route map rule is missing. */ @@ -220,25 +220,28 @@ extern void route_map_init(void);  extern void route_map_finish(void);  /* Add match statement to route map. */ -extern int route_map_add_match(struct route_map_index *index, -			       const char *match_name, const char *match_arg, -			       route_map_event_t type); +extern enum rmap_compile_rets route_map_add_match(struct route_map_index *index, +						  const char *match_name, +						  const char *match_arg, +						  route_map_event_t type);  /* Delete specified route match rule. */ -extern int route_map_delete_match(struct route_map_index *index, -				  const char *match_name, -				  const char *match_arg); +extern enum rmap_compile_rets +route_map_delete_match(struct route_map_index *index, +		       const char *match_name, const char *match_arg);  extern const char *route_map_get_match_arg(struct route_map_index *index,  					   const char *match_name);  /* Add route-map set statement to the route map. */ -extern int route_map_add_set(struct route_map_index *index, -			     const char *set_name, const char *set_arg); +extern enum rmap_compile_rets route_map_add_set(struct route_map_index *index, +						const char *set_name, +						const char *set_arg);  /* Delete route map set rule. */ -extern int route_map_delete_set(struct route_map_index *index, -				const char *set_name, const char *set_arg); +extern enum rmap_compile_rets +route_map_delete_set(struct route_map_index *index, +		     const char *set_name, const char *set_arg);  /* Install rule command to the match list. */  extern void route_map_install_match(struct route_map_rule_cmd *cmd); diff --git a/lib/stream.c b/lib/stream.c index dfd13ca186..2e1a0193a2 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -28,6 +28,7 @@  #include "network.h"  #include "prefix.h"  #include "log.h" +#include "frr_pthread.h"  #include "lib_errors.h"  DEFINE_MTYPE_STATIC(LIB, STREAM, "Stream") @@ -1136,11 +1137,9 @@ void stream_fifo_push(struct stream_fifo *fifo, struct stream *s)  void stream_fifo_push_safe(struct stream_fifo *fifo, struct stream *s)  { -	pthread_mutex_lock(&fifo->mtx); -	{ +	frr_with_mutex(&fifo->mtx) {  		stream_fifo_push(fifo, s);  	} -	pthread_mutex_unlock(&fifo->mtx);  }  /* Delete first stream from fifo. */ @@ -1170,11 +1169,9 @@ struct stream *stream_fifo_pop_safe(struct stream_fifo *fifo)  {  	struct stream *ret; -	pthread_mutex_lock(&fifo->mtx); -	{ +	frr_with_mutex(&fifo->mtx) {  		ret = stream_fifo_pop(fifo);  	} -	pthread_mutex_unlock(&fifo->mtx);  	return ret;  } @@ -1188,11 +1185,9 @@ struct stream *stream_fifo_head_safe(struct stream_fifo *fifo)  {  	struct stream *ret; -	pthread_mutex_lock(&fifo->mtx); -	{ +	frr_with_mutex(&fifo->mtx) {  		ret = stream_fifo_head(fifo);  	} -	pthread_mutex_unlock(&fifo->mtx);  	return ret;  } @@ -1212,11 +1207,9 @@ void stream_fifo_clean(struct stream_fifo *fifo)  void stream_fifo_clean_safe(struct stream_fifo *fifo)  { -	pthread_mutex_lock(&fifo->mtx); -	{ +	frr_with_mutex(&fifo->mtx) {  		stream_fifo_clean(fifo);  	} -	pthread_mutex_unlock(&fifo->mtx);  }  size_t stream_fifo_count_safe(struct stream_fifo *fifo) diff --git a/lib/subdir.am b/lib/subdir.am index 2be7537bcc..e0f1352380 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -65,6 +65,7 @@ lib_libfrr_la_SOURCES = \  	lib/prefix.c \  	lib/privs.c \  	lib/ptm_lib.c \ +	lib/pullwr.c \  	lib/qobj.c \  	lib/ringbuf.c \  	lib/routemap.c \ @@ -203,6 +204,7 @@ pkginclude_HEADERS += \  	lib/printfrr.h \  	lib/privs.h \  	lib/ptm_lib.h \ +	lib/pullwr.h \  	lib/pw.h \  	lib/qobj.h \  	lib/queue.h \ diff --git a/lib/thread.c b/lib/thread.c index 943b849ebf..90c794e88d 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -33,6 +33,7 @@  #include "network.h"  #include "jhash.h"  #include "frratomic.h" +#include "frr_pthread.h"  #include "lib_errors.h"  DEFINE_MTYPE_STATIC(LIB, THREAD, "Thread") @@ -173,8 +174,7 @@ static void cpu_record_print(struct vty *vty, uint8_t filter)  	tmp.funcname = "TOTAL";  	tmp.types = filter; -	pthread_mutex_lock(&masters_mtx); -	{ +	frr_with_mutex(&masters_mtx) {  		for (ALL_LIST_ELEMENTS_RO(masters, ln, m)) {  			const char *name = m->name ? m->name : "main"; @@ -206,7 +206,6 @@ static void cpu_record_print(struct vty *vty, uint8_t filter)  			vty_out(vty, "\n");  		}  	} -	pthread_mutex_unlock(&masters_mtx);  	vty_out(vty, "\n");  	vty_out(vty, "Total thread statistics\n"); @@ -240,11 +239,9 @@ static void cpu_record_clear(uint8_t filter)  	struct thread_master *m;  	struct listnode *ln; -	pthread_mutex_lock(&masters_mtx); -	{ +	frr_with_mutex(&masters_mtx) {  		for (ALL_LIST_ELEMENTS_RO(masters, ln, m)) { -			pthread_mutex_lock(&m->mtx); -			{ +			frr_with_mutex(&m->mtx) {  				void *args[2] = {tmp, m->cpu_record};  				hash_iterate(  					m->cpu_record, @@ -252,10 +249,8 @@ static void cpu_record_clear(uint8_t filter)  						  void *))cpu_record_hash_clear,  					args);  			} -			pthread_mutex_unlock(&m->mtx);  		}  	} -	pthread_mutex_unlock(&masters_mtx);  }  static uint8_t parse_filter(const char *filterstr) @@ -370,13 +365,11 @@ DEFUN (show_thread_poll,  	struct listnode *node;  	struct thread_master *m; -	pthread_mutex_lock(&masters_mtx); -	{ +	frr_with_mutex(&masters_mtx) {  		for (ALL_LIST_ELEMENTS_RO(masters, node, m)) {  			show_thread_poll_helper(vty, m);  		}  	} -	pthread_mutex_unlock(&masters_mtx);  	return CMD_SUCCESS;  } @@ -487,26 +480,22 @@ struct thread_master *thread_master_create(const char *name)  				   sizeof(struct pollfd) * rv->handler.pfdsize);  	/* add to list of threadmasters */ -	pthread_mutex_lock(&masters_mtx); -	{ +	frr_with_mutex(&masters_mtx) {  		if (!masters)  			masters = list_new();  		listnode_add(masters, rv);  	} -	pthread_mutex_unlock(&masters_mtx);  	return rv;  }  void thread_master_set_name(struct thread_master *master, const char *name)  { -	pthread_mutex_lock(&master->mtx); -	{ +	frr_with_mutex(&master->mtx) {  		XFREE(MTYPE_THREAD_MASTER, master->name);  		master->name = XSTRDUP(MTYPE_THREAD_MASTER, name);  	} -	pthread_mutex_unlock(&master->mtx);  }  #define THREAD_UNUSED_DEPTH 10 @@ -569,13 +558,11 @@ static void thread_array_free(struct thread_master *m,   */  void thread_master_free_unused(struct thread_master *m)  { -	pthread_mutex_lock(&m->mtx); -	{ +	frr_with_mutex(&m->mtx) {  		struct thread *t;  		while ((t = thread_list_pop(&m->unuse)))  			thread_free(m, t);  	} -	pthread_mutex_unlock(&m->mtx);  }  /* Stop thread scheduler. */ @@ -583,14 +570,12 @@ void thread_master_free(struct thread_master *m)  {  	struct thread *t; -	pthread_mutex_lock(&masters_mtx); -	{ +	frr_with_mutex(&masters_mtx) {  		listnode_delete(masters, m);  		if (masters->count == 0) {  			list_delete(&masters);  		}  	} -	pthread_mutex_unlock(&masters_mtx);  	thread_array_free(m, m->read);  	thread_array_free(m, m->write); @@ -621,11 +606,9 @@ unsigned long thread_timer_remain_msec(struct thread *thread)  {  	int64_t remain; -	pthread_mutex_lock(&thread->mtx); -	{ +	frr_with_mutex(&thread->mtx) {  		remain = monotime_until(&thread->u.sands, NULL) / 1000LL;  	} -	pthread_mutex_unlock(&thread->mtx);  	return remain < 0 ? 0 : remain;  } @@ -642,11 +625,9 @@ unsigned long thread_timer_remain_second(struct thread *thread)  struct timeval thread_timer_remain(struct thread *thread)  {  	struct timeval remain; -	pthread_mutex_lock(&thread->mtx); -	{ +	frr_with_mutex(&thread->mtx) {  		monotime_until(&thread->u.sands, &remain);  	} -	pthread_mutex_unlock(&thread->mtx);  	return remain;  } @@ -770,14 +751,10 @@ struct thread *funcname_thread_add_read_write(int dir, struct thread_master *m,  	struct thread **thread_array;  	assert(fd >= 0 && fd < m->fd_limit); -	pthread_mutex_lock(&m->mtx); -	{ -		if (t_ptr -		    && *t_ptr) // thread is already scheduled; don't reschedule -		{ -			pthread_mutex_unlock(&m->mtx); -			return NULL; -		} +	frr_with_mutex(&m->mtx) { +		if (t_ptr && *t_ptr) +			// thread is already scheduled; don't reschedule +			break;  		/* default to a new pollfd */  		nfds_t queuepos = m->handler.pfdcount; @@ -817,12 +794,10 @@ struct thread *funcname_thread_add_read_write(int dir, struct thread_master *m,  			m->handler.pfdcount++;  		if (thread) { -			pthread_mutex_lock(&thread->mtx); -			{ +			frr_with_mutex(&thread->mtx) {  				thread->u.fd = fd;  				thread_array[thread->u.fd] = thread;  			} -			pthread_mutex_unlock(&thread->mtx);  			if (t_ptr) {  				*t_ptr = thread; @@ -832,7 +807,6 @@ struct thread *funcname_thread_add_read_write(int dir, struct thread_master *m,  		AWAKEN(m);  	} -	pthread_mutex_unlock(&m->mtx);  	return thread;  } @@ -850,19 +824,14 @@ funcname_thread_add_timer_timeval(struct thread_master *m,  	assert(type == THREAD_TIMER);  	assert(time_relative); -	pthread_mutex_lock(&m->mtx); -	{ -		if (t_ptr -		    && *t_ptr) // thread is already scheduled; don't reschedule -		{ -			pthread_mutex_unlock(&m->mtx); +	frr_with_mutex(&m->mtx) { +		if (t_ptr && *t_ptr) +			// thread is already scheduled; don't reschedule  			return NULL; -		}  		thread = thread_get(m, type, func, arg, debugargpass); -		pthread_mutex_lock(&thread->mtx); -		{ +		frr_with_mutex(&thread->mtx) {  			monotime(&thread->u.sands);  			timeradd(&thread->u.sands, time_relative,  				 &thread->u.sands); @@ -872,11 +841,9 @@ funcname_thread_add_timer_timeval(struct thread_master *m,  				thread->ref = t_ptr;  			}  		} -		pthread_mutex_unlock(&thread->mtx);  		AWAKEN(m);  	} -	pthread_mutex_unlock(&m->mtx);  	return thread;  } @@ -933,26 +900,20 @@ struct thread *funcname_thread_add_event(struct thread_master *m,  					 void *arg, int val,  					 struct thread **t_ptr, debugargdef)  { -	struct thread *thread; +	struct thread *thread = NULL;  	assert(m != NULL); -	pthread_mutex_lock(&m->mtx); -	{ -		if (t_ptr -		    && *t_ptr) // thread is already scheduled; don't reschedule -		{ -			pthread_mutex_unlock(&m->mtx); -			return NULL; -		} +	frr_with_mutex(&m->mtx) { +		if (t_ptr && *t_ptr) +			// thread is already scheduled; don't reschedule +			break;  		thread = thread_get(m, THREAD_EVENT, func, arg, debugargpass); -		pthread_mutex_lock(&thread->mtx); -		{ +		frr_with_mutex(&thread->mtx) {  			thread->u.val = val;  			thread_list_add_tail(&m->event, thread);  		} -		pthread_mutex_unlock(&thread->mtx);  		if (t_ptr) {  			*t_ptr = thread; @@ -961,7 +922,6 @@ struct thread *funcname_thread_add_event(struct thread_master *m,  		AWAKEN(m);  	} -	pthread_mutex_unlock(&m->mtx);  	return thread;  } @@ -1143,15 +1103,13 @@ void thread_cancel_event(struct thread_master *master, void *arg)  {  	assert(master->owner == pthread_self()); -	pthread_mutex_lock(&master->mtx); -	{ +	frr_with_mutex(&master->mtx) {  		struct cancel_req *cr =  			XCALLOC(MTYPE_TMP, sizeof(struct cancel_req));  		cr->eventobj = arg;  		listnode_add(master->cancel_req, cr);  		do_thread_cancel(master);  	} -	pthread_mutex_unlock(&master->mtx);  }  /** @@ -1167,15 +1125,13 @@ void thread_cancel(struct thread *thread)  	assert(master->owner == pthread_self()); -	pthread_mutex_lock(&master->mtx); -	{ +	frr_with_mutex(&master->mtx) {  		struct cancel_req *cr =  			XCALLOC(MTYPE_TMP, sizeof(struct cancel_req));  		cr->thread = thread;  		listnode_add(master->cancel_req, cr);  		do_thread_cancel(master);  	} -	pthread_mutex_unlock(&master->mtx);  }  /** @@ -1208,8 +1164,7 @@ void thread_cancel_async(struct thread_master *master, struct thread **thread,  	assert(!(thread && eventobj) && (thread || eventobj));  	assert(master->owner != pthread_self()); -	pthread_mutex_lock(&master->mtx); -	{ +	frr_with_mutex(&master->mtx) {  		master->canceled = false;  		if (thread) { @@ -1228,7 +1183,6 @@ void thread_cancel_async(struct thread_master *master, struct thread **thread,  		while (!master->canceled)  			pthread_cond_wait(&master->cancel_cond, &master->mtx);  	} -	pthread_mutex_unlock(&master->mtx);  }  /* ------------------------------------------------------------------------- */ @@ -1527,22 +1481,18 @@ unsigned long thread_consumed_time(RUSAGE_T *now, RUSAGE_T *start,  int thread_should_yield(struct thread *thread)  {  	int result; -	pthread_mutex_lock(&thread->mtx); -	{ +	frr_with_mutex(&thread->mtx) {  		result = monotime_since(&thread->real, NULL)  			 > (int64_t)thread->yield;  	} -	pthread_mutex_unlock(&thread->mtx);  	return result;  }  void thread_set_yield_time(struct thread *thread, unsigned long yield_time)  { -	pthread_mutex_lock(&thread->mtx); -	{ +	frr_with_mutex(&thread->mtx) {  		thread->yield = yield_time;  	} -	pthread_mutex_unlock(&thread->mtx);  }  void thread_getrusage(RUSAGE_T *r) @@ -1637,20 +1587,16 @@ void funcname_thread_execute(struct thread_master *m,  	struct thread *thread;  	/* Get or allocate new thread to execute. */ -	pthread_mutex_lock(&m->mtx); -	{ +	frr_with_mutex(&m->mtx) {  		thread = thread_get(m, THREAD_EVENT, func, arg, debugargpass);  		/* Set its event value. */ -		pthread_mutex_lock(&thread->mtx); -		{ +		frr_with_mutex(&thread->mtx) {  			thread->add_type = THREAD_EXECUTE;  			thread->u.val = val;  			thread->ref = &thread;  		} -		pthread_mutex_unlock(&thread->mtx);  	} -	pthread_mutex_unlock(&m->mtx);  	/* Execute thread doing all accounting. */  	thread_call(thread); @@ -755,7 +755,7 @@ DEFUN_NOSH (vrf_netns,  	if (!pathname)  		return CMD_WARNING_CONFIG_FAILED; -	frr_elevate_privs(vrf_daemon_privs) { +	frr_with_privs(vrf_daemon_privs) {  		ret = vrf_netns_handler_create(vty, vrf, pathname,  					       NS_UNKNOWN, NS_UNKNOWN);  	} @@ -348,6 +348,15 @@ void vty_hello(struct vty *vty)  			vty_out(vty, "MOTD file not found\n");  	} else if (host.motd)  		vty_out(vty, "%s", host.motd); + +#if CONFDATE > 20200901 +	CPP_NOTICE("Please remove solaris code from system as it is deprecated"); +#endif +#ifdef SUNOS_5 +	zlog_warn("If you are using FRR on Solaris, the FRR developers would love to hear from you\n"); +	zlog_warn("Please send email to dev@lists.frrouting.org about this message\n"); +	zlog_warn("We are considering deprecating Solaris and want to find users of Solaris systems\n"); +#endif  }  /* Put out prompt and wait input from user. */ diff --git a/lib/zclient.c b/lib/zclient.c index 2d79d9b3c5..f809704f86 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -766,6 +766,92 @@ int zclient_route_send(uint8_t cmd, struct zclient *zclient,  	return zclient_send_message(zclient);  } +static int zapi_nexthop_labels_cmp(const struct zapi_nexthop *next1, +				   const struct zapi_nexthop *next2) +{ +	if (next1->label_num > next2->label_num) +		return 1; + +	if (next1->label_num < next2->label_num) +		return -1; + +	return memcmp(next1->labels, next2->labels, next1->label_num); +} + +static int zapi_nexthop_cmp_no_labels(const struct zapi_nexthop *next1, +				      const struct zapi_nexthop *next2) +{ +	int ret = 0; + +	if (next1->vrf_id < next2->vrf_id) +		return -1; + +	if (next1->vrf_id > next2->vrf_id) +		return 1; + +	if (next1->type < next2->type) +		return -1; + +	if (next1->type > next2->type) +		return 1; + +	switch (next1->type) { +	case NEXTHOP_TYPE_IPV4: +	case NEXTHOP_TYPE_IPV6: +		ret = nexthop_g_addr_cmp(next1->type, &next1->gate, +					 &next2->gate); +		if (ret != 0) +			return ret; +		break; +	case NEXTHOP_TYPE_IPV4_IFINDEX: +	case NEXTHOP_TYPE_IPV6_IFINDEX: +		ret = nexthop_g_addr_cmp(next1->type, &next1->gate, +					 &next2->gate); +		if (ret != 0) +			return ret; +		/* Intentional Fall-Through */ +	case NEXTHOP_TYPE_IFINDEX: +		if (next1->ifindex < next2->ifindex) +			return -1; + +		if (next1->ifindex > next2->ifindex) +			return 1; +		break; +	case NEXTHOP_TYPE_BLACKHOLE: +		if (next1->bh_type < next2->bh_type) +			return -1; + +		if (next1->bh_type > next2->bh_type) +			return 1; +		break; +	} + +	return 0; +} + +static int zapi_nexthop_cmp(const void *item1, const void *item2) +{ +	int ret = 0; + +	const struct zapi_nexthop *next1 = item1; +	const struct zapi_nexthop *next2 = item2; + +	ret = zapi_nexthop_cmp_no_labels(next1, next2); +	if (ret != 0) +		return ret; + +	ret = zapi_nexthop_labels_cmp(next1, next2); + +	return ret; +} + +static void zapi_nexthop_group_sort(struct zapi_nexthop *nh_grp, +				    uint16_t nexthop_num) +{ +	qsort(nh_grp, nexthop_num, sizeof(struct zapi_nexthop), +	      &zapi_nexthop_cmp); +} +  int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api)  {  	struct zapi_nexthop *api_nh; @@ -821,6 +907,8 @@ int zapi_route_encode(uint8_t cmd, struct stream *s, struct zapi_route *api)  			return -1;  		} +		zapi_nexthop_group_sort(api->nexthops, api->nexthop_num); +  		stream_putw(s, api->nexthop_num);  		for (i = 0; i < api->nexthop_num; i++) { diff --git a/lib/zebra.h b/lib/zebra.h index 789a93a3c4..b17ef700b4 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -136,6 +136,7 @@ typedef unsigned char uint8_t;  #ifdef CRYPTO_OPENSSL  #include <openssl/evp.h> +#include <openssl/hmac.h>  #endif  #include "openbsd-tree.h" diff --git a/ospf6d/ospf6_asbr.c b/ospf6d/ospf6_asbr.c index 33b9f71b5f..4d1c085081 100644 --- a/ospf6d/ospf6_asbr.c +++ b/ospf6d/ospf6_asbr.c @@ -1581,7 +1581,7 @@ static struct route_map_rule_cmd ospf6_routemap_rule_set_tag_cmd = {  	route_map_rule_tag_free,  }; -static int route_map_command_status(struct vty *vty, int ret) +static int route_map_command_status(struct vty *vty, enum rmap_compile_rets ret)  {  	switch (ret) {  	case RMAP_RULE_MISSING: @@ -1593,6 +1593,7 @@ static int route_map_command_status(struct vty *vty, int ret)  		return CMD_WARNING_CONFIG_FAILED;  		break;  	case RMAP_COMPILE_SUCCESS: +	case RMAP_DUPLICATE_RULE:  		break;  	} @@ -1610,8 +1611,10 @@ DEFUN (ospf6_routemap_set_metric_type,  {  	VTY_DECLVAR_CONTEXT(route_map_index, route_map_index);  	int idx_external = 2; -	int ret = route_map_add_set(route_map_index, "metric-type", -				    argv[idx_external]->arg); +	enum rmap_compile_rets ret = route_map_add_set(route_map_index, +						       "metric-type", +						       argv[idx_external]->arg); +  	return route_map_command_status(vty, ret);  } @@ -1627,7 +1630,9 @@ DEFUN (ospf6_routemap_no_set_metric_type,  {  	VTY_DECLVAR_CONTEXT(route_map_index, route_map_index);  	char *ext = (argc == 4) ? argv[3]->text : NULL; -	int ret = route_map_delete_set(route_map_index, "metric-type", ext); +	enum rmap_compile_rets ret = route_map_delete_set(route_map_index, +							  "metric-type", ext); +  	return route_map_command_status(vty, ret);  } @@ -1641,8 +1646,10 @@ DEFUN (ospf6_routemap_set_forwarding,  {  	VTY_DECLVAR_CONTEXT(route_map_index, route_map_index);  	int idx_ipv6 = 2; -	int ret = route_map_add_set(route_map_index, "forwarding-address", -				    argv[idx_ipv6]->arg); +	enum rmap_compile_rets ret = route_map_add_set(route_map_index, +						       "forwarding-address", +						       argv[idx_ipv6]->arg); +  	return route_map_command_status(vty, ret);  } @@ -1657,8 +1664,10 @@ DEFUN (ospf6_routemap_no_set_forwarding,  {  	VTY_DECLVAR_CONTEXT(route_map_index, route_map_index);  	int idx_ipv6 = 3; -	int ret = route_map_delete_set(route_map_index, "forwarding-address", -				       argv[idx_ipv6]->arg); +	enum rmap_compile_rets ret = route_map_delete_set(route_map_index, +							  "forwarding-address", +							  argv[idx_ipv6]->arg); +  	return route_map_command_status(vty, ret);  } diff --git a/ospf6d/ospf6_network.c b/ospf6d/ospf6_network.c index 625ad884f2..9a18680b8b 100644 --- a/ospf6d/ospf6_network.c +++ b/ospf6d/ospf6_network.c @@ -85,7 +85,7 @@ void ospf6_serv_close(void)  /* Make ospf6d's server socket. */  int ospf6_serv_sock(void)  { -	frr_elevate_privs(&ospf6d_privs) { +	frr_with_privs(&ospf6d_privs) {  		ospf6_sock = socket(AF_INET6, SOCK_RAW, IPPROTO_OSPFIGP);  		if (ospf6_sock < 0) { diff --git a/ospfd/ospf_interface.c b/ospfd/ospf_interface.c index ce1604a5b1..3877708708 100644 --- a/ospfd/ospf_interface.c +++ b/ospfd/ospf_interface.c @@ -225,12 +225,14 @@ struct ospf_interface *ospf_if_new(struct ospf *ospf, struct interface *ifp,  {  	struct ospf_interface *oi; -	if ((oi = ospf_if_table_lookup(ifp, p)) == NULL) { -		oi = XCALLOC(MTYPE_OSPF_IF, sizeof(struct ospf_interface)); -		memset(oi, 0, sizeof(struct ospf_interface)); -	} else +	oi = ospf_if_table_lookup(ifp, p); +	if (oi)  		return oi; +	oi = XCALLOC(MTYPE_OSPF_IF, sizeof(struct ospf_interface)); + +	oi->obuf = ospf_fifo_new(); +  	/* Set zebra interface pointer. */  	oi->ifp = ifp;  	oi->address = p; @@ -264,8 +266,6 @@ struct ospf_interface *ospf_if_new(struct ospf *ospf, struct interface *ifp,  	oi->ospf = ospf; -	ospf_if_stream_set(oi); -  	QOBJ_REG(oi, ospf_interface);  	if (IS_DEBUG_OSPF_EVENT) @@ -325,8 +325,7 @@ void ospf_if_free(struct ospf_interface *oi)  {  	ospf_if_down(oi); -	if (oi->obuf) -		ospf_fifo_free(oi->obuf); +	ospf_fifo_free(oi->obuf);  	assert(oi->state == ISM_Down); @@ -490,29 +489,20 @@ static void ospf_if_reset_stats(struct ospf_interface *oi)  	oi->ls_ack_in = oi->ls_ack_out = 0;  } -void ospf_if_stream_set(struct ospf_interface *oi) -{ -	/* set output fifo queue. */ -	if (oi->obuf == NULL) -		oi->obuf = ospf_fifo_new(); -} -  void ospf_if_stream_unset(struct ospf_interface *oi)  {  	struct ospf *ospf = oi->ospf; -	if (oi->obuf) { -		/* flush the interface packet queue */ -		ospf_fifo_flush(oi->obuf); -		/*reset protocol stats */ -		ospf_if_reset_stats(oi); - -		if (oi->on_write_q) { -			listnode_delete(ospf->oi_write_q, oi); -			if (list_isempty(ospf->oi_write_q)) -				OSPF_TIMER_OFF(ospf->t_write); -			oi->on_write_q = 0; -		} +	/* flush the interface packet queue */ +	ospf_fifo_flush(oi->obuf); +	/*reset protocol stats */ +	ospf_if_reset_stats(oi); + +	if (oi->on_write_q) { +		listnode_delete(ospf->oi_write_q, oi); +		if (list_isempty(ospf->oi_write_q)) +			OSPF_TIMER_OFF(ospf->t_write); +		oi->on_write_q = 0;  	}  } @@ -903,8 +893,6 @@ struct ospf_interface *ospf_vl_new(struct ospf *ospf,  	ospf_area_add_if(voi->area, voi); -	ospf_if_stream_set(voi); -  	if (IS_DEBUG_OSPF_EVENT)  		zlog_debug("ospf_vl_new(): Stop");  	return voi; diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h index b88d405875..0c903954d3 100644 --- a/ospfd/ospf_interface.h +++ b/ospfd/ospf_interface.h @@ -285,7 +285,6 @@ extern void ospf_if_update_params(struct interface *, struct in_addr);  extern int ospf_if_new_hook(struct interface *);  extern void ospf_if_init(void); -extern void ospf_if_stream_set(struct ospf_interface *);  extern void ospf_if_stream_unset(struct ospf_interface *);  extern void ospf_if_reset_variables(struct ospf_interface *);  extern int ospf_if_is_enable(struct ospf_interface *); diff --git a/ospfd/ospf_ism.h b/ospfd/ospf_ism.h index 5ae99ab320..8d21403695 100644 --- a/ospfd/ospf_ism.h +++ b/ospfd/ospf_ism.h @@ -53,8 +53,9 @@  			listnode_add((O)->oi_write_q, oi);                     \  			oi->on_write_q = 1;                                    \  		}                                                              \ -		thread_add_write(master, ospf_write, (O), (O)->fd,             \ -				 &(O)->t_write);                               \ +		if (!list_isempty((O)->oi_write_q))                            \ +			thread_add_write(master, ospf_write, (O), (O)->fd,     \ +					 &(O)->t_write);                       \  	} while (0)  /* Macro for OSPF ISM timer turn on. */ diff --git a/ospfd/ospf_network.c b/ospfd/ospf_network.c index 1415a6e8b7..b8e2dac70e 100644 --- a/ospfd/ospf_network.c +++ b/ospfd/ospf_network.c @@ -190,7 +190,7 @@ int ospf_sock_init(struct ospf *ospf)  		/* silently return since VRF is not ready */  		return -1;  	} -	frr_elevate_privs(&ospfd_privs) { +	frr_with_privs(&ospfd_privs) {  		ospf_sock = vrf_socket(AF_INET, SOCK_RAW, IPPROTO_OSPFIGP,  				       ospf->vrf_id, ospf->name);  		if (ospf_sock < 0) { diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index 62b0444796..5a29c1fb07 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -132,7 +132,7 @@ static int ospf_auth_type(struct ospf_interface *oi)  	return auth_type;  } -struct ospf_packet *ospf_packet_new(size_t size) +static struct ospf_packet *ospf_packet_new(size_t size)  {  	struct ospf_packet *new; @@ -231,22 +231,8 @@ void ospf_fifo_free(struct ospf_fifo *fifo)  	XFREE(MTYPE_OSPF_FIFO, fifo);  } -void ospf_packet_add(struct ospf_interface *oi, struct ospf_packet *op) +static void ospf_packet_add(struct ospf_interface *oi, struct ospf_packet *op)  { -	if (!oi->obuf) { -		flog_err( -			EC_OSPF_PKT_PROCESS, -			"ospf_packet_add(interface %s in state %d [%s], packet type %s, " -			"destination %s) called with NULL obuf, ignoring " -			"(please report this bug)!\n", -			IF_NAME(oi), oi->state, -			lookup_msg(ospf_ism_state_msg, oi->state, NULL), -			lookup_msg(ospf_packet_type_str, -				   stream_getc_from(op->s, 1), NULL), -			inet_ntoa(op->dst)); -		return; -	} -  	/* Add packet to end of queue. */  	ospf_fifo_push(oi->obuf, op); @@ -257,20 +243,6 @@ void ospf_packet_add(struct ospf_interface *oi, struct ospf_packet *op)  static void ospf_packet_add_top(struct ospf_interface *oi,  				struct ospf_packet *op)  { -	if (!oi->obuf) { -		flog_err( -			EC_OSPF_PKT_PROCESS, -			"ospf_packet_add(interface %s in state %d [%s], packet type %s, " -			"destination %s) called with NULL obuf, ignoring " -			"(please report this bug)!\n", -			IF_NAME(oi), oi->state, -			lookup_msg(ospf_ism_state_msg, oi->state, NULL), -			lookup_msg(ospf_packet_type_str, -				   stream_getc_from(op->s, 1), NULL), -			inet_ntoa(op->dst)); -		return; -	} -  	/* Add packet to head of queue. */  	ospf_fifo_push_head(oi->obuf, op); @@ -278,7 +250,7 @@ static void ospf_packet_add_top(struct ospf_interface *oi,  	/* ospf_fifo_debug (oi->obuf); */  } -void ospf_packet_delete(struct ospf_interface *oi) +static void ospf_packet_delete(struct ospf_interface *oi)  {  	struct ospf_packet *op; @@ -288,7 +260,7 @@ void ospf_packet_delete(struct ospf_interface *oi)  		ospf_packet_free(op);  } -struct ospf_packet *ospf_packet_dup(struct ospf_packet *op) +static struct ospf_packet *ospf_packet_dup(struct ospf_packet *op)  {  	struct ospf_packet *new; @@ -698,12 +670,9 @@ static int ospf_write(struct thread *thread)  		return -1;  	} -	ospf->t_write = NULL; -  	node = listhead(ospf->oi_write_q);  	assert(node);  	oi = listgetdata(node); -	assert(oi);  #ifdef WANT_OSPF_WRITE_FRAGMENT  	/* seed ipid static with low order bits of time */ @@ -906,9 +875,7 @@ static int ospf_write(struct thread *thread)  		/* Setup to service from the head of the queue again */  		if (!list_isempty(ospf->oi_write_q)) {  			node = listhead(ospf->oi_write_q); -			assert(node);  			oi = listgetdata(node); -			assert(oi);  		}  	} @@ -4062,6 +4029,23 @@ static void ospf_ls_upd_queue_send(struct ospf_interface *oi,  			oi->on_write_q = 1;  		}  		ospf_write(&os_packet_thd); +		/* +		 * We are fake calling ospf_write with a fake +		 * thread.  Imagine that we have oi_a already +		 * enqueued and we have turned on the write +		 * thread(t_write). +		 * Now this function calls this for oi_b +		 * so the on_write_q has oi_a and oi_b on +		 * it, ospf_write runs and clears the packets +		 * for both oi_a and oi_b.  Removing them from +		 * the on_write_q.  After this thread of execution +		 * finishes we will execute the t_write thread +		 * with nothing in the on_write_q causing an +		 * assert.  So just make sure that the t_write +		 * is actually turned off. +		 */ +		if (list_isempty(oi->ospf->oi_write_q)) +			OSPF_TIMER_OFF(oi->ospf->t_write);  	} else {  		/* Hook thread to write packet. */  		OSPF_ISM_WRITE_ON(oi->ospf); diff --git a/ospfd/ospf_packet.h b/ospfd/ospf_packet.h index a508727968..5a3e029f2e 100644 --- a/ospfd/ospf_packet.h +++ b/ospfd/ospf_packet.h @@ -131,8 +131,6 @@ struct ospf_ls_update {  #define IS_SET_DD_ALL(X)        ((X) & OSPF_DD_FLAG_ALL)  /* Prototypes. */ -extern void ospf_output_forward(struct stream *, int); -extern struct ospf_packet *ospf_packet_new(size_t);  extern void ospf_packet_free(struct ospf_packet *);  extern struct ospf_fifo *ospf_fifo_new(void);  extern void ospf_fifo_push(struct ospf_fifo *, struct ospf_packet *); @@ -140,10 +138,6 @@ extern struct ospf_packet *ospf_fifo_pop(struct ospf_fifo *);  extern struct ospf_packet *ospf_fifo_head(struct ospf_fifo *);  extern void ospf_fifo_flush(struct ospf_fifo *);  extern void ospf_fifo_free(struct ospf_fifo *); -extern void ospf_packet_add(struct ospf_interface *, struct ospf_packet *); -extern void ospf_packet_delete(struct ospf_interface *); -extern struct stream *ospf_stream_dup(struct stream *); -extern struct ospf_packet *ospf_packet_dup(struct ospf_packet *);  extern int ospf_read(struct thread *);  extern void ospf_hello_send(struct ospf_interface *); diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index b91a55f635..e48a5b4d36 100644 --- a/ospfd/ospfd.c +++ b/ospfd/ospfd.c @@ -2097,7 +2097,7 @@ static int ospf_vrf_enable(struct vrf *vrf)  				old_vrf_id);  		if (old_vrf_id != ospf->vrf_id) { -			frr_elevate_privs(&ospfd_privs) { +			frr_with_privs(&ospfd_privs) {  				/* stop zebra redist to us for old vrf */  				zclient_send_dereg_requests(zclient,  							    old_vrf_id); diff --git a/pbrd/pbr_map.c b/pbrd/pbr_map.c index 5e67990d5e..1a8461c6c1 100644 --- a/pbrd/pbr_map.c +++ b/pbrd/pbr_map.c @@ -316,7 +316,7 @@ struct pbr_map_sequence *pbrms_get(const char *name, uint32_t seqno)  		pbrms->ruleno = pbr_nht_get_next_rule(seqno);  		pbrms->parent = pbrm;  		pbrms->reason = -			PBR_MAP_INVALID_SRCDST | +			PBR_MAP_INVALID_EMPTY |  			PBR_MAP_INVALID_NO_NEXTHOPS;  		QOBJ_REG(pbrms, pbr_map_sequence); @@ -350,10 +350,10 @@ pbr_map_sequence_check_nexthops_valid(struct pbr_map_sequence *pbrms)  	}  } -static void pbr_map_sequence_check_src_dst_valid(struct pbr_map_sequence *pbrms) +static void pbr_map_sequence_check_not_empty(struct pbr_map_sequence *pbrms)  { -	if (!pbrms->src && !pbrms->dst) -		pbrms->reason |= PBR_MAP_INVALID_SRCDST; +	if (!pbrms->src && !pbrms->dst && !pbrms->mark) +		pbrms->reason |= PBR_MAP_INVALID_EMPTY;  }  /* @@ -364,7 +364,7 @@ static void pbr_map_sequence_check_valid(struct pbr_map_sequence *pbrms)  {  	pbr_map_sequence_check_nexthops_valid(pbrms); -	pbr_map_sequence_check_src_dst_valid(pbrms); +	pbr_map_sequence_check_not_empty(pbrms);  }  static bool pbr_map_check_valid_internal(struct pbr_map *pbrm) diff --git a/pbrd/pbr_map.h b/pbrd/pbr_map.h index 945f76bb2b..112acfe44e 100644 --- a/pbrd/pbr_map.h +++ b/pbrd/pbr_map.h @@ -87,6 +87,7 @@ struct pbr_map_sequence {  	 */  	struct prefix *src;  	struct prefix *dst; +	uint32_t mark;  	/*  	 * Family of the src/dst.  Needed when deleting since we clear them @@ -126,7 +127,7 @@ struct pbr_map_sequence {  #define PBR_MAP_INVALID_NEXTHOP        (1 << 1)  #define PBR_MAP_INVALID_NO_NEXTHOPS    (1 << 2)  #define PBR_MAP_INVALID_BOTH_NHANDGRP  (1 << 3) -#define PBR_MAP_INVALID_SRCDST         (1 << 4) +#define PBR_MAP_INVALID_EMPTY          (1 << 4)  	uint64_t reason;  	QOBJ_FIELDS diff --git a/pbrd/pbr_vty.c b/pbrd/pbr_vty.c index 95f38563b1..5e7addc9d2 100644 --- a/pbrd/pbr_vty.c +++ b/pbrd/pbr_vty.c @@ -172,6 +172,33 @@ DEFPY(pbr_map_match_dst, pbr_map_match_dst_cmd,  	return CMD_SUCCESS;  } +DEFPY(pbr_map_match_mark, pbr_map_match_mark_cmd, +	"[no] match mark (1-4294967295)$mark", +	NO_STR +	"Match the rest of the command\n" +	"Choose the mark value to use\n" +	"mark\n") +{ +	struct pbr_map_sequence *pbrms = VTY_GET_CONTEXT(pbr_map_sequence); + +#ifndef GNU_LINUX +	vty_out(vty, "pbr marks are not supported on this platform"); +	return CMD_WARNING_CONFIG_FAILED; +#endif + +	if (!no) { +		if (pbrms->mark == (uint32_t) mark) +			return CMD_SUCCESS; +		pbrms->mark = (uint32_t) mark; +	} else { +		pbrms->mark = 0; +	} + +	pbr_map_check(pbrms); + +	return CMD_SUCCESS; + } +  DEFPY(pbr_map_nexthop_group, pbr_map_nexthop_group_cmd,  	"[no] set nexthop-group NHGNAME$name",  	NO_STR @@ -453,6 +480,8 @@ DEFPY (show_pbr_map,  				vty_out(vty, "\tDST Match: %s\n",  					prefix2str(pbrms->dst, buf,  						   sizeof(buf))); +			if (pbrms->mark) +				vty_out(vty, "\tMARK Match: %u\n", pbrms->mark);  			if (pbrms->nhgrp_name) {  				vty_out(vty, @@ -632,6 +661,9 @@ static int pbr_vty_map_config_write_sequence(struct vty *vty,  		vty_out(vty, " match dst-ip %s\n",  			prefix2str(pbrms->dst, buff, sizeof(buff))); +	if (pbrms->mark) +		vty_out(vty, " match mark %u\n", pbrms->mark); +  	if (pbrms->nhgrp_name)  		vty_out(vty, " set nexthop-group %s\n", pbrms->nhgrp_name); @@ -704,6 +736,7 @@ void pbr_vty_init(void)  	install_element(INTERFACE_NODE, &pbr_policy_cmd);  	install_element(PBRMAP_NODE, &pbr_map_match_src_cmd);  	install_element(PBRMAP_NODE, &pbr_map_match_dst_cmd); +	install_element(PBRMAP_NODE, &pbr_map_match_mark_cmd);  	install_element(PBRMAP_NODE, &pbr_map_nexthop_group_cmd);  	install_element(PBRMAP_NODE, &pbr_map_nexthop_cmd);  	install_element(VIEW_NODE, &show_pbr_cmd); diff --git a/pbrd/pbr_zebra.c b/pbrd/pbr_zebra.c index 466a9a13ae..d74d0fcd23 100644 --- a/pbrd/pbr_zebra.c +++ b/pbrd/pbr_zebra.c @@ -526,7 +526,7 @@ static void pbr_encode_pbr_map_sequence(struct stream *s,  	stream_putw(s, 0);  /* src port */  	pbr_encode_pbr_map_sequence_prefix(s, pbrms->dst, family);  	stream_putw(s, 0);  /* dst port */ -	stream_putl(s, 0);  /* fwmark */ +	stream_putl(s, pbrms->mark);  	if (pbrms->nhgrp_name)  		stream_putl(s, pbr_nht_get_table(pbrms->nhgrp_name));  	else if (pbrms->nhg) diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c index 1c66007fbb..f7f4b54aea 100644 --- a/pimd/pim_mroute.c +++ b/pimd/pim_mroute.c @@ -57,7 +57,7 @@ static int pim_mroute_set(struct pim_instance *pim, int enable)  	 * We need to create the VRF table for the pim mroute_socket  	 */  	if (pim->vrf_id != VRF_DEFAULT) { -		frr_elevate_privs(&pimd_privs) { +		frr_with_privs(&pimd_privs) {  			data = pim->vrf->data.l.table_id;  			err = setsockopt(pim->mroute_socket, IPPROTO_IP, @@ -75,7 +75,7 @@ static int pim_mroute_set(struct pim_instance *pim, int enable)  		}  	} -	frr_elevate_privs(&pimd_privs) { +	frr_with_privs(&pimd_privs) {  		opt = enable ? MRT_INIT : MRT_DONE;  		/*  		 * *BSD *cares* about what value we pass down @@ -735,7 +735,7 @@ int pim_mroute_socket_enable(struct pim_instance *pim)  {  	int fd; -	frr_elevate_privs(&pimd_privs) { +	frr_with_privs(&pimd_privs) {  		fd = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP); diff --git a/pimd/pim_msdp_socket.c b/pimd/pim_msdp_socket.c index b1f7cfd2c6..22eb8bc7b4 100644 --- a/pimd/pim_msdp_socket.c +++ b/pimd/pim_msdp_socket.c @@ -175,7 +175,7 @@ int pim_msdp_sock_listen(struct pim_instance *pim)  		}  	} -	frr_elevate_privs(&pimd_privs) { +	frr_with_privs(&pimd_privs) {  		/* bind to well known TCP port */  		rc = bind(sock, (struct sockaddr *)&sin, socklen);  	} diff --git a/pimd/pim_sock.c b/pimd/pim_sock.c index c4538a4ac5..82255cd3b0 100644 --- a/pimd/pim_sock.c +++ b/pimd/pim_sock.c @@ -46,7 +46,7 @@ int pim_socket_raw(int protocol)  {  	int fd; -	frr_elevate_privs(&pimd_privs) { +	frr_with_privs(&pimd_privs) {  		fd = socket(AF_INET, SOCK_RAW, protocol); @@ -65,7 +65,7 @@ void pim_socket_ip_hdr(int fd)  {  	const int on = 1; -	frr_elevate_privs(&pimd_privs) { +	frr_with_privs(&pimd_privs) {  		if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)))  			zlog_err("%s: Could not turn on IP_HDRINCL option: %s", @@ -83,7 +83,7 @@ int pim_socket_bind(int fd, struct interface *ifp)  	int ret = 0;  #ifdef SO_BINDTODEVICE -	frr_elevate_privs(&pimd_privs) { +	frr_with_privs(&pimd_privs) {  		ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifp->name,  				 strlen(ifp->name)); diff --git a/pimd/pim_vty.c b/pimd/pim_vty.c index d1935195df..1c4ecf299f 100644 --- a/pimd/pim_vty.c +++ b/pimd/pim_vty.c @@ -81,7 +81,7 @@ int pim_debug_config_write(struct vty *vty)  		++writes;  	} -	if (PIM_DEBUG_MROUTE_DETAIL) { +	if (PIM_DEBUG_MROUTE_DETAIL_ONLY) {  		vty_out(vty, "debug mroute detail\n");  		++writes;  	} @@ -107,7 +107,7 @@ int pim_debug_config_write(struct vty *vty)  		vty_out(vty, "debug pim trace\n");  		++writes;  	} -	if (PIM_DEBUG_PIM_TRACE_DETAIL) { +	if (PIM_DEBUG_PIM_TRACE_DETAIL_ONLY) {  		vty_out(vty, "debug pim trace detail\n");  		++writes;  	} diff --git a/pimd/pimd.h b/pimd/pimd.h index 175936e0a7..3b83d3b6c7 100644 --- a/pimd/pimd.h +++ b/pimd/pimd.h @@ -163,6 +163,8 @@ extern uint8_t qpim_ecmp_rebalance_enable;  #define PIM_DEBUG_PIM_TRACE (router->debugs & PIM_MASK_PIM_TRACE)  #define PIM_DEBUG_PIM_TRACE_DETAIL                                             \  	(router->debugs & (PIM_MASK_PIM_TRACE_DETAIL | PIM_MASK_PIM_TRACE)) +#define PIM_DEBUG_PIM_TRACE_DETAIL_ONLY                                        \ +	(router->debugs & PIM_MASK_PIM_TRACE_DETAIL)  #define PIM_DEBUG_IGMP_EVENTS (router->debugs & PIM_MASK_IGMP_EVENTS)  #define PIM_DEBUG_IGMP_PACKETS (router->debugs & PIM_MASK_IGMP_PACKETS)  #define PIM_DEBUG_IGMP_TRACE (router->debugs & PIM_MASK_IGMP_TRACE) @@ -173,6 +175,7 @@ extern uint8_t qpim_ecmp_rebalance_enable;  #define PIM_DEBUG_MROUTE (router->debugs & PIM_MASK_MROUTE)  #define PIM_DEBUG_MROUTE_DETAIL                                                \  	(router->debugs & (PIM_MASK_MROUTE_DETAIL | PIM_MASK_MROUTE)) +#define PIM_DEBUG_MROUTE_DETAIL_ONLY (router->debugs & PIM_MASK_MROUTE_DETAIL)  #define PIM_DEBUG_PIM_HELLO (router->debugs & PIM_MASK_PIM_HELLO)  #define PIM_DEBUG_PIM_J_P (router->debugs & PIM_MASK_PIM_J_P)  #define PIM_DEBUG_PIM_REG (router->debugs & PIM_MASK_PIM_REG) diff --git a/python/clidef.py b/python/clidef.py index bc2f5caebf..baa6ed52b2 100644 --- a/python/clidef.py +++ b/python/clidef.py @@ -351,6 +351,7 @@ if __name__ == '__main__':      macros = Macros()      macros.load('lib/route_types.h')      macros.load(os.path.join(basepath, 'lib/command.h')) +    macros.load(os.path.join(basepath, 'bgpd/bgp_vty.h'))      # sigh :(      macros['PROTO_REDIST_STR'] = 'FRR_REDIST_STR_ISISD' diff --git a/redhat/frr.spec.in b/redhat/frr.spec.in index 014cae02ee..fa0a6d8a0a 100644 --- a/redhat/frr.spec.in +++ b/redhat/frr.spec.in @@ -634,6 +634,7 @@ fi      %{_libdir}/frr/modules/bgpd_rpki.so  %endif  %{_libdir}/frr/modules/zebra_irdp.so +%{_libdir}/frr/modules/bgpd_bmp.so  %{_bindir}/*  %config(noreplace) %{configdir}/[!v]*.conf*  %config(noreplace) %attr(750,%{frr_user},%{frr_user}) %{configdir}/daemons diff --git a/ripd/ripd.c b/ripd/ripd.c index 561fbcb52d..ad373aebdf 100644 --- a/ripd/ripd.c +++ b/ripd/ripd.c @@ -1395,7 +1395,7 @@ int rip_create_socket(struct vrf *vrf)  	/* Make datagram socket. */  	if (vrf->vrf_id != VRF_DEFAULT)  		vrf_dev = vrf->name; -	frr_elevate_privs(&ripd_privs) { +	frr_with_privs(&ripd_privs) {  		sock = vrf_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, vrf->vrf_id,  				  vrf_dev);  		if (sock < 0) { @@ -1415,7 +1415,7 @@ int rip_create_socket(struct vrf *vrf)  #endif  	setsockopt_so_recvbuf(sock, RIP_UDP_RCV_BUF); -	frr_elevate_privs(&ripd_privs) { +	frr_with_privs(&ripd_privs) {  		if ((ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr)))  		    < 0) {  			zlog_err("%s: Can't bind socket %d to %s port %d: %s", diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c index 49ed13a2c2..9ed9dc28fe 100644 --- a/ripngd/ripng_interface.c +++ b/ripngd/ripng_interface.c @@ -75,7 +75,7 @@ static int ripng_multicast_join(struct interface *ifp, int sock)  		 * While this is bogus, privs are available and easy to use  		 * for this call as a workaround.  		 */ -		frr_elevate_privs(&ripngd_privs) { +		frr_with_privs(&ripngd_privs) {  			ret = setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,  					 (char *)&mreq, sizeof(mreq)); diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index 3314892e74..49f7dda646 100644 --- a/ripngd/ripngd.c +++ b/ripngd/ripngd.c @@ -120,8 +120,7 @@ int ripng_make_socket(struct vrf *vrf)  	/* Make datagram socket. */  	if (vrf->vrf_id != VRF_DEFAULT)  		vrf_dev = vrf->name; -	frr_elevate_privs(&ripngd_privs) -	{ +	frr_with_privs(&ripngd_privs) {  		sock = vrf_socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP,  				  vrf->vrf_id, vrf_dev);  		if (sock < 0) { @@ -160,7 +159,7 @@ int ripng_make_socket(struct vrf *vrf)  #endif /* SIN6_LEN */  	ripaddr.sin6_port = htons(RIPNG_PORT_DEFAULT); -	frr_elevate_privs(&ripngd_privs) { +	frr_with_privs(&ripngd_privs) {  		ret = bind(sock, (struct sockaddr *)&ripaddr, sizeof(ripaddr));  		if (ret < 0) {  			zlog_err("Can't bind ripng socket: %s.", diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c index 13c04259d7..27605da63f 100644 --- a/staticd/static_zebra.c +++ b/staticd/static_zebra.c @@ -130,6 +130,7 @@ static int interface_state_up(ZAPI_CALLBACK_ARGS)  		/* Install any static reliant on this interface coming up */  		static_install_intf_nh(ifp); +		static_ifindex_update(ifp, true);  	}  	return 0; @@ -137,7 +138,12 @@ static int interface_state_up(ZAPI_CALLBACK_ARGS)  static int interface_state_down(ZAPI_CALLBACK_ARGS)  { -	zebra_interface_state_read(zclient->ibuf, vrf_id); +	struct interface *ifp; + +	ifp = zebra_interface_state_read(zclient->ibuf, vrf_id); + +	if (ifp) +		static_ifindex_update(ifp, false);  	return 0;  } diff --git a/tests/bgpd/test_peer_attr.c b/tests/bgpd/test_peer_attr.c index 78016dc9ce..8e1b62ac15 100644 --- a/tests/bgpd/test_peer_attr.c +++ b/tests/bgpd/test_peer_attr.c @@ -1170,7 +1170,7 @@ static void test_peer_attr(struct test *test, struct test_peer_attr *pa)  	/* Test Preparation: Switch and activate address-family. */  	if (!is_attr_type_global(pa->type)) {  		test_log(test, "prepare: switch address-family to [%s]", -			 afi_safi_print(pa->afi, pa->safi)); +			 get_afi_safi_str(pa->afi, pa->safi, false));  		test_execute(test, "address-family %s %s",  			     str_from_afi(pa->afi), str_from_safi(pa->safi));  		test_execute(test, "neighbor %s activate", g->name); @@ -1237,7 +1237,7 @@ static void test_peer_attr(struct test *test, struct test_peer_attr *pa)  	/* Test Preparation: Switch and activate address-family. */  	if (!is_attr_type_global(pa->type)) {  		test_log(test, "prepare: switch address-family to [%s]", -			 afi_safi_print(pa->afi, pa->safi)); +			 get_afi_safi_str(pa->afi, pa->safi, false));  		test_execute(test, "address-family %s %s",  			     str_from_afi(pa->afi), str_from_safi(pa->safi));  		test_execute(test, "neighbor %s activate", g->name); @@ -1285,7 +1285,7 @@ static void test_peer_attr(struct test *test, struct test_peer_attr *pa)  	/* Test Preparation: Switch and activate address-family. */  	if (!is_attr_type_global(pa->type)) {  		test_log(test, "prepare: switch address-family to [%s]", -			 afi_safi_print(pa->afi, pa->safi)); +			 get_afi_safi_str(pa->afi, pa->safi, false));  		test_execute(test, "address-family %s %s",  			     str_from_afi(pa->afi), str_from_safi(pa->safi));  		test_execute(test, "neighbor %s activate", g->name); diff --git a/tests/lib/test_privs.c b/tests/lib/test_privs.c index fc3d908661..de638bc67a 100644 --- a/tests/lib/test_privs.c +++ b/tests/lib/test_privs.c @@ -113,7 +113,7 @@ int main(int argc, char **argv)  	((test_privs.current_state() == ZPRIVS_RAISED) ? "Raised" : "Lowered")  	printf("%s\n", PRIV_STATE()); -	frr_elevate_privs(&test_privs) { +	frr_with_privs(&test_privs) {  		printf("%s\n", PRIV_STATE());  	} @@ -125,7 +125,7 @@ int main(int argc, char **argv)  	/* but these should continue to work... */  	printf("%s\n", PRIV_STATE()); -	frr_elevate_privs(&test_privs) { +	frr_with_privs(&test_privs) {  		printf("%s\n", PRIV_STATE());  	} diff --git a/tests/topotests/Dockerfile b/tests/topotests/Dockerfile index ea6fa4b9e0..4602688782 100644 --- a/tests/topotests/Dockerfile +++ b/tests/topotests/Dockerfile @@ -40,12 +40,12 @@ RUN export DEBIAN_FRONTEND=noninteractive \          pytest  RUN cd /tmp \ -    && wget -q https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-1/Ubuntu-18.04-x86_64-Packages/libyang-dev_0.16.46_amd64.deb \ +    && wget -q https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/Debian-AMD64-Packages/libyang-dev_0.16.105-1_amd64.deb \           -O libyang-dev.deb \ -    && wget -q https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-1/Ubuntu-18.04-x86_64-Packages/libyang_0.16.46_amd64.deb \ +    && wget -q https://ci1.netdef.org/artifact/LIBYANG-YANGRELEASE/shared/build-10/Debian-AMD64-Packages/libyang0.16_0.16.105-1_amd64.deb \           -O libyang.deb \ -    && echo "039252cc66eb254a97e160b1c325af669470cde8a02d73ec9f7b920ed3c7997c  libyang.deb" | sha256sum -c - \ -    && echo "e7e2d5bfc7b33b3218df8bef404432970f9b4ad10d6dbbdcb0e0be2babbb68e9  libyang-dev.deb" | sha256sum -c - \ +    && echo "34bef017e527a590020185f05dc39203bdf1c86223e0d990839623ec629d8598  libyang.deb" | sha256sum -c - \ +    && echo "fe9cc6e3b173ca56ef49428c281e96bf76c0f910aa75cf85098076411484e8f4  libyang-dev.deb" | sha256sum -c - \      && dpkg -i libyang*.deb \      && rm libyang*.deb diff --git a/tests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py b/tests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py index 095ebe3344..e99111d90b 100755 --- a/tests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py +++ b/tests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py @@ -375,7 +375,7 @@ def test_static_routes(request):      # Verifying RIB routes      dut = 'r3'      protocol = 'bgp' -    next_hop = '10.0.0.2' +    next_hop = ['10.0.0.2', '10.0.0.5']      result = verify_rib(tgen, 'ipv4', dut, input_dict, next_hop=next_hop,                          protocol=protocol)      assert result is True, "Testcase {} :Failed \n Error: {}". \ diff --git a/tests/topotests/bgp-ecmp-topo2/ebgp_ecmp_topo2.json b/tests/topotests/bgp-ecmp-topo2/ebgp_ecmp_topo2.json new file mode 100755 index 0000000000..50797f130a --- /dev/null +++ b/tests/topotests/bgp-ecmp-topo2/ebgp_ecmp_topo2.json @@ -0,0 +1,664 @@ +{ +    "address_types": [ +        "ipv4", +        "ipv6" +    ], +    "ipv4base": "10.0.0.0", +    "ipv4mask": 24, +    "ipv6base": "fd00::", +    "ipv6mask": 64, +    "link_ip_start": { +        "ipv4": "10.0.0.0", +        "v4mask": 24, +        "ipv6": "fd00::", +        "v6mask": 64 +    }, +    "lo_prefix": { +        "ipv4": "1.0.", +        "v4mask": 32, +        "ipv6": "2001:DB8:F::", +        "v6mask": 128 +    }, +    "routers": { +        "r1": { +            "links": { +                "lo": { +                    "ipv4": "auto", +                    "ipv6": "auto", +                    "type": "loopback" +                }, +                "r2-link1": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                } +            }, +            "bgp": { +                "local_as": "100", +                "address_family": { +                    "ipv4": { +                        "unicast": { +                            "neighbor": { +                                "r2": { +                                    "dest_link": { +                                        "r1-link1": {} +                                    } +                                } +                            } +                        } +                    }, +                    "ipv6": { +                        "unicast": { +                            "neighbor": { +                                "r2": { +                                    "dest_link": { +                                        "r1-link1": {} +                                    } +                                } +                            } +                        } +                    } +                } +            } +        }, +        "r2": { +            "links": { +                "lo": { +                    "ipv4": "auto", +                    "ipv6": "auto", +                    "type": "loopback" +                }, +                "r1-link1": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link1": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link2": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link3": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link4": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link5": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link6": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link7": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link8": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link9": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link10": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link11": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link12": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link13": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link14": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link15": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link16": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link17": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link18": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link19": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link20": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link21": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link22": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link23": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link24": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link25": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link26": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link27": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link28": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link29": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link30": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link31": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link32": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                } +            }, +            "bgp": { +                "local_as": "200", +                "address_family": { +                    "ipv4": { +                        "unicast": { +                            "neighbor": { +                                "r1": { +                                    "dest_link": { +                                        "r2-link1": {} +                                    } +                                }, +                                "r3": { +                                    "dest_link": { +                                        "r2-link1": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link2": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link3": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link4": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link5": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link6": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link7": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link8": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link9": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link10": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link11": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link12": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link13": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link14": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link15": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link16": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link17": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link18": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link19": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link20": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link21": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link22": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link23": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link24": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link25": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link26": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link27": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link28": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link29": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link30": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link31": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link32": { +                                            "next_hop_self": true +                                        } +                                    } +                                } +                            } +                        } +                    }, +                    "ipv6": { +                        "unicast": { +                            "neighbor": { +                                "r1": { +                                    "dest_link": { +                                        "r2-link1": {} +                                    } +                                }, +                                "r3": { +                                    "dest_link": { +                                        "r2-link1": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link2": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link3": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link4": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link5": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link6": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link7": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link8": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link9": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link10": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link11": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link12": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link13": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link14": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link15": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link16": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link17": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link18": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link19": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link20": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link21": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link22": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link23": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link24": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link25": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link26": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link27": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link28": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link29": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link30": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link31": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link32": { +                                            "next_hop_self": true +                                        } +                                    } +                                } +                            } +                        } +                    } +                } +            } +        }, +        "r3": { +            "links": { +                "lo": { +                    "ipv4": "auto", +                    "ipv6": "auto", +                    "type": "loopback" +                }, +                "r2-link1": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link2": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link3": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link4": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link5": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link6": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link7": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link8": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link9": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link10": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link11": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link12": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link13": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link14": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link15": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link16": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link17": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link18": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link19": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link20": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link21": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link22": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link23": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link24": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link25": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link26": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link27": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link28": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link29": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link30": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link31": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link32": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                } +            }, +            "bgp": { +                "local_as": "300", +                "address_family": { +                    "ipv4": { +                        "unicast": { +                            "maximum_paths": { +                                "ebgp": 32 +                            }, +                            "neighbor": { +                                "r2": { +                                    "dest_link": { +                                        "r3-link1": {}, +                                        "r3-link2": {}, +                                        "r3-link3": {}, +                                        "r3-link4": {}, +                                        "r3-link5": {}, +                                        "r3-link6": {}, +                                        "r3-link7": {}, +                                        "r3-link8": {}, +                                        "r3-link9": {}, +                                        "r3-link10": {}, +                                        "r3-link11": {}, +                                        "r3-link12": {}, +                                        "r3-link13": {}, +                                        "r3-link14": {}, +                                        "r3-link15": {}, +                                        "r3-link16": {}, +                                        "r3-link17": {}, +                                        "r3-link18": {}, +                                        "r3-link19": {}, +                                        "r3-link20": {}, +                                        "r3-link21": {}, +                                        "r3-link22": {}, +                                        "r3-link23": {}, +                                        "r3-link24": {}, +                                        "r3-link25": {}, +                                        "r3-link26": {}, +                                        "r3-link27": {}, +                                        "r3-link28": {}, +                                        "r3-link29": {}, +                                        "r3-link30": {}, +                                        "r3-link31": {}, +                                        "r3-link32": {} +                                    } +                                } +                            } +                        } +                    }, +                    "ipv6": { +                        "unicast": { +                            "maximum_paths": { +                                "ebgp": 32 +                            }, +                            "neighbor": { +                                "r2": { +                                    "dest_link": { +                                        "r3-link1": {}, +                                        "r3-link2": {}, +                                        "r3-link3": {}, +                                        "r3-link4": {}, +                                        "r3-link5": {}, +                                        "r3-link6": {}, +                                        "r3-link7": {}, +                                        "r3-link8": {}, +                                        "r3-link9": {}, +                                        "r3-link10": {}, +                                        "r3-link11": {}, +                                        "r3-link12": {}, +                                        "r3-link13": {}, +                                        "r3-link14": {}, +                                        "r3-link15": {}, +                                        "r3-link16": {}, +                                        "r3-link17": {}, +                                        "r3-link18": {}, +                                        "r3-link19": {}, +                                        "r3-link20": {}, +                                        "r3-link21": {}, +                                        "r3-link22": {}, +                                        "r3-link23": {}, +                                        "r3-link24": {}, +                                        "r3-link25": {}, +                                        "r3-link26": {}, +                                        "r3-link27": {}, +                                        "r3-link28": {}, +                                        "r3-link29": {}, +                                        "r3-link30": {}, +                                        "r3-link31": {}, +                                        "r3-link32": {} +                                    } +                                } +                            } +                        } +                    } +                } +            } +        } +    } +}
\ No newline at end of file diff --git a/tests/topotests/bgp-ecmp-topo2/ibgp_ecmp_topo2.json b/tests/topotests/bgp-ecmp-topo2/ibgp_ecmp_topo2.json new file mode 100755 index 0000000000..9010b8c273 --- /dev/null +++ b/tests/topotests/bgp-ecmp-topo2/ibgp_ecmp_topo2.json @@ -0,0 +1,674 @@ +{ +    "address_types": [ +        "ipv4", +        "ipv6" +    ], +    "ipv4base": "10.0.0.0", +    "ipv4mask": 24, +    "ipv6base": "fd00::", +    "ipv6mask": 64, +    "link_ip_start": { +        "ipv4": "10.0.0.0", +        "v4mask": 24, +        "ipv6": "fd00::", +        "v6mask": 64 +    }, +    "lo_prefix": { +        "ipv4": "1.0.", +        "v4mask": 32, +        "ipv6": "2001:DB8:F::", +        "v6mask": 128 +    }, +    "routers": { +        "r1": { +            "links": { +                "lo": { +                    "ipv4": "auto", +                    "ipv6": "auto", +                    "type": "loopback" +                }, +                "r2-link1": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                } +            }, +            "bgp": { +                "local_as": "100", +                "address_family": { +                    "ipv4": { +                        "unicast": { +                            "neighbor": { +                                "r2": { +                                    "dest_link": { +                                        "r1-link1": {} +                                    } +                                } +                            } +                        } +                    }, +                    "ipv6": { +                        "unicast": { +                            "neighbor": { +                                "r2": { +                                    "dest_link": { +                                        "r1-link1": {} +                                    } +                                } +                            } +                        } +                    } +                } +            } +        }, +        "r2": { +            "links": { +                "lo": { +                    "ipv4": "auto", +                    "ipv6": "auto", +                    "type": "loopback" +                }, +                "r1-link1": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link1": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link2": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link3": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link4": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link5": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link6": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link7": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link8": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link9": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link10": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link11": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link12": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link13": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link14": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link15": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link16": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link17": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link18": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link19": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link20": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link21": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link22": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link23": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link24": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link25": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link26": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link27": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link28": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link29": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link30": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link31": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r3-link32": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                } +            }, +            "bgp": { +                "local_as": "100", +                "address_family": { +                    "ipv4": { +                        "unicast": { +                            "neighbor": { +                                "r1": { +                                    "dest_link": { +                                        "r2-link1": {} +                                    } +                                }, +                                "r3": { +                                    "dest_link": { +                                        "r2-link1": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link2": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link3": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link4": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link5": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link6": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link7": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link8": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link9": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link10": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link11": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link12": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link13": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link14": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link15": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link16": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link17": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link18": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link19": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link20": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link21": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link22": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link23": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link24": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link25": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link26": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link27": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link28": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link29": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link30": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link31": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link32": { +                                            "next_hop_self": true +                                        } +                                    } +                                } +                            }, +                            "redistribute": [ +                                { +                                    "redist_type": "static" +                                } +                            ] +                        } +                    }, +                    "ipv6": { +                        "unicast": { +                            "neighbor": { +                                "r1": { +                                    "dest_link": { +                                        "r2-link1": {} +                                    } +                                }, +                                "r3": { +                                    "dest_link": { +                                        "r2-link1": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link2": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link3": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link4": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link5": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link6": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link7": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link8": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link9": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link10": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link11": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link12": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link13": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link14": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link15": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link16": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link17": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link18": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link19": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link20": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link21": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link22": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link23": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link24": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link25": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link26": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link27": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link28": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link29": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link30": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link31": { +                                            "next_hop_self": true +                                        }, +                                        "r2-link32": { +                                            "next_hop_self": true +                                        } +                                    } +                                } +                            }, +                            "redistribute": [ +                                { +                                    "redist_type": "static" +                                } +                            ] +                        } +                    } +                } +            } +        }, +        "r3": { +            "links": { +                "lo": { +                    "ipv4": "auto", +                    "ipv6": "auto", +                    "type": "loopback" +                }, +                "r2-link1": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link2": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link3": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link4": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link5": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link6": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link7": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link8": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link9": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link10": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link11": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link12": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link13": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link14": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link15": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link16": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link17": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link18": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link19": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link20": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link21": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link22": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link23": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link24": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link25": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link26": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link27": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link28": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link29": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link30": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link31": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                }, +                "r2-link32": { +                    "ipv4": "auto", +                    "ipv6": "auto" +                } +            }, +            "bgp": { +                "local_as": "100", +                "address_family": { +                    "ipv4": { +                        "unicast": { +                            "maximum_paths": { +                                "ibgp": 32 +                            }, +                            "neighbor": { +                                "r2": { +                                    "dest_link": { +                                        "r3-link1": {}, +                                        "r3-link2": {}, +                                        "r3-link3": {}, +                                        "r3-link4": {}, +                                        "r3-link5": {}, +                                        "r3-link6": {}, +                                        "r3-link7": {}, +                                        "r3-link8": {}, +                                        "r3-link9": {}, +                                        "r3-link10": {}, +                                        "r3-link11": {}, +                                        "r3-link12": {}, +                                        "r3-link13": {}, +                                        "r3-link14": {}, +                                        "r3-link15": {}, +                                        "r3-link16": {}, +                                        "r3-link17": {}, +                                        "r3-link18": {}, +                                        "r3-link19": {}, +                                        "r3-link20": {}, +                                        "r3-link21": {}, +                                        "r3-link22": {}, +                                        "r3-link23": {}, +                                        "r3-link24": {}, +                                        "r3-link25": {}, +                                        "r3-link26": {}, +                                        "r3-link27": {}, +                                        "r3-link28": {}, +                                        "r3-link29": {}, +                                        "r3-link30": {}, +                                        "r3-link31": {}, +                                        "r3-link32": {} +                                    } +                                } +                            } +                        } +                    }, +                    "ipv6": { +                        "unicast": { +                            "maximum_paths": { +                                "ibgp": 32 +                            }, +                            "neighbor": { +                                "r2": { +                                    "dest_link": { +                                        "r3-link1": {}, +                                        "r3-link2": {}, +                                        "r3-link3": {}, +                                        "r3-link4": {}, +                                        "r3-link5": {}, +                                        "r3-link6": {}, +                                        "r3-link7": {}, +                                        "r3-link8": {}, +                                        "r3-link9": {}, +                                        "r3-link10": {}, +                                        "r3-link11": {}, +                                        "r3-link12": {}, +                                        "r3-link13": {}, +                                        "r3-link14": {}, +                                        "r3-link15": {}, +                                        "r3-link16": {}, +                                        "r3-link17": {}, +                                        "r3-link18": {}, +                                        "r3-link19": {}, +                                        "r3-link20": {}, +                                        "r3-link21": {}, +                                        "r3-link22": {}, +                                        "r3-link23": {}, +                                        "r3-link24": {}, +                                        "r3-link25": {}, +                                        "r3-link26": {}, +                                        "r3-link27": {}, +                                        "r3-link28": {}, +                                        "r3-link29": {}, +                                        "r3-link30": {}, +                                        "r3-link31": {}, +                                        "r3-link32": {} +                                    } +                                } +                            } +                        } +                    } +                } +            } +        } +    } +}
\ No newline at end of file diff --git a/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py b/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py new file mode 100755 index 0000000000..4b9f419bf2 --- /dev/null +++ b/tests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py @@ -0,0 +1,817 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +""" +Following tests are covered to test ecmp functionality on EBGP. +1. Verify routes installed as per maximum-paths configuration (8/16/32) +2. Disable/Shut selected paths nexthops and verify other next are installed in +   the RIB of DUT. Enable interfaces and verify RIB count. +3. Verify BGP table and RIB in DUT after clear BGP routes and neighbors. +4. Verify routes are cleared from BGP and RIB table of DUT when +   redistribute static configuration is removed. +5. Shut BGP neigbors one by one and verify BGP and routing table updated +   accordingly in DUT +6. Delete static routes and verify routers are cleared from BGP table and RIB +   of DUT. +7. Verify routes are cleared from BGP and RIB table of DUT when advertise +   network configuration is removed. +""" +import os +import sys +import time +import json +import pytest +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) +sys.path.append(os.path.join(CWD, '../../')) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( +    start_topology, write_test_header, +    write_test_footer, +    verify_rib, create_static_routes, check_address_types, +    interface_status, reset_config_on_routers +) +from lib.topolog import logger +from lib.bgp import ( +    verify_bgp_convergence, create_router_bgp, +    clear_bgp_and_verify) +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology and configuration creation +jsonFile = "{}/ebgp_ecmp_topo2.json".format(CWD) + +try: +    with open(jsonFile, "r") as topoJson: +        topo = json.load(topoJson) +except IOError: +    assert False, "Could not read file {}".format(jsonFile) + +# Global variables +NEXT_HOPS = {"ipv4": [], "ipv6": []} +INTF_LIST_R3 = [] +INTF_LIST_R2 = [] +NETWORK = {"ipv4": "11.0.20.1/32", "ipv6": "1::/64"} +NEXT_HOP_IP = {"ipv4": "10.0.0.1", "ipv6": "fd00::1"} +BGP_CONVERGENCE = False + + +class CreateTopo(Topo): +    """ +    Test topology builder. + +    * `Topo`: Topology object +    """ + +    def build(self, *_args, **_opts): +        """Build function.""" +        tgen = get_topogen(self) + +        # Building topology from json file +        build_topo_from_json(tgen, topo) + + +def setup_module(mod): +    """ +    Sets up the pytest environment. + +    * `mod`: module name +    """ +    global NEXT_HOPS, INTF_LIST_R3, INTF_LIST_R2, TEST_STATIC +    global ADDR_TYPES + +    testsuite_run_time = time.asctime(time.localtime(time.time())) +    logger.info("Testsuite start time: {}".format(testsuite_run_time)) +    logger.info("=" * 40) + +    logger.info("Running setup_module to create topology") + +    # This function initiates the topology build with Topogen... +    tgen = Topogen(CreateTopo, mod.__name__) + +    # Starting topology, create tmp files which are loaded to routers +    #  to start deamons and then start routers +    start_topology(tgen) + +    # Creating configuration from JSON +    build_config_from_json(tgen, topo) + +    # Don't run this test if we have any failure. +    if tgen.routers_have_failure(): +        pytest.skip(tgen.errors) + +    # tgen.mininet_cli() +    # Api call verify whether BGP is converged +    ADDR_TYPES = check_address_types() + +    BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) +    assert BGP_CONVERGENCE is True, ("setup_module :Failed \n Error:" +                                     " {}".format(BGP_CONVERGENCE)) + +    link_data = [val for links, val in +                 topo["routers"]["r2"]["links"].iteritems() +                 if "r3" in links] +    for adt in ADDR_TYPES: +        NEXT_HOPS[adt] = [val[adt].split("/")[0] for val in link_data] +        if adt == "ipv4": +            NEXT_HOPS[adt] = sorted( +                NEXT_HOPS[adt], key=lambda x: int(x.split(".")[2])) +        elif adt == "ipv6": +            NEXT_HOPS[adt] = sorted( +                NEXT_HOPS[adt], key=lambda x: int(x.split(':')[-3], 16)) + +    INTF_LIST_R2 = [val["interface"].split("/")[0] for val in link_data] +    INTF_LIST_R2 = sorted(INTF_LIST_R2, key=lambda x: int(x.split("eth")[1])) + +    link_data = [val for links, val in +                 topo["routers"]["r3"]["links"].iteritems() +                 if "r2" in links] +    INTF_LIST_R3 = [val["interface"].split("/")[0] for val in link_data] +    INTF_LIST_R3 = sorted(INTF_LIST_R3, key=lambda x: int(x.split("eth")[1])) + +    # STATIC_ROUTE = True +    logger.info("Running setup_module() done") + + +def teardown_module(): +    """ +    Teardown the pytest environment. + +    * `mod`: module name +    """ + +    logger.info("Running teardown_module to delete topology") + +    tgen = get_topogen() + +    # Stop toplogy and Remove tmp files +    tgen.stop_topology() + + +def static_or_nw(tgen, topo, tc_name, test_type, dut): + +    if test_type == "redist_static": +        input_dict_static = { +            dut: { +                "static_routes": [ +                    { +                        "network": NETWORK["ipv4"], +                        "next_hop": NEXT_HOP_IP["ipv4"] +                    }, +                    { +                        "network": NETWORK["ipv6"], +                        "next_hop": NEXT_HOP_IP["ipv6"] +                    } +                ] +            } +        } +        logger.info("Configuring static route on router %s", dut) +        result = create_static_routes(tgen, input_dict_static) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +        input_dict_2 = { +            dut: { +                "bgp": { +                    "address_family": { +                        "ipv4": { +                            "unicast": { +                                "redistribute": [{ +                                    "redist_type": "static" +                                }] +                            } +                        }, +                        "ipv6": { +                            "unicast": { +                                "redistribute": [{ +                                    "redist_type": "static" +                                }] +                            } +                        } +                    } +                } +            } +        } + +        logger.info("Configuring redistribute static route on router %s", dut) +        result = create_router_bgp(tgen, topo, input_dict_2) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +    elif test_type == "advertise_nw": +        input_dict_nw = { +            dut: { +                "bgp": { +                    "address_family": { +                        "ipv4": { +                            "unicast": { +                                "advertise_networks": [ +                                    {"network": NETWORK["ipv4"]} +                                ] +                            } +                        }, +                        "ipv6": { +                            "unicast": { +                                "advertise_networks": [ +                                    {"network": NETWORK["ipv6"]} +                                ] +                            } +                        } +                    } +                } +            } +        } + +        logger.info("Advertising networks %s %s from router %s", +                    NETWORK["ipv4"], NETWORK["ipv6"], dut) +        result = create_router_bgp(tgen, topo, input_dict_nw) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + + +@pytest.mark.parametrize("ecmp_num", ["8", "16", "32"]) +@pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"]) +def test_modify_ecmp_max_paths(request, ecmp_num, test_type): +    """ +    Verify routes installed as per maximum-paths +    configuration (8/16/32). +    """ + +    tc_name = request.node.name +    write_test_header(tc_name) +    tgen = get_topogen() + +    reset_config_on_routers(tgen) + +    static_or_nw(tgen, topo, tc_name, test_type, "r2") + +    input_dict = { +        "r3": { +            "bgp": { +                "address_family": { +                    "ipv4": { +                        "unicast": { +                            "maximum_paths": { +                                "ebgp": ecmp_num, +                            } +                        } +                    }, +                    "ipv6": { +                        "unicast": { +                            "maximum_paths": { +                                "ebgp": ecmp_num, +                            } +                        } +                    } +                } +            } +        } +    } + +    logger.info("Configuring bgp maximum-paths %s on router r3", ecmp_num) +    result = create_router_bgp(tgen, topo, input_dict) +    assert result is True, "Testcase {} : Failed \n Error: {}".format( +        tc_name, result) + +    # Verifying RIB routes +    dut = "r3" +    protocol = "bgp" + +    for addr_type in ADDR_TYPES: +        input_dict_1 = { +            "r3": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type] +                    } +                ] +            } +        } + +        logger.info("Verifying %s routes on r3", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict_1, +                            next_hop=NEXT_HOPS[addr_type], +                            protocol=protocol) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +    write_test_footer(tc_name) + + +def test_ecmp_after_clear_bgp(request): +    """ Verify BGP table and RIB in DUT after clear BGP routes and neighbors""" + +    tc_name = request.node.name +    write_test_header(tc_name) +    tgen = get_topogen() + +    reset_config_on_routers(tgen) + +    # Verifying RIB routes +    dut = "r3" +    protocol = "bgp" + +    static_or_nw(tgen, topo, tc_name, "redist_static", "r2") +    for addr_type in ADDR_TYPES: +        input_dict_1 = { +            "r3": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type] +                    } +                ] +            } +        } + +        logger.info("Verifying %s routes on r3", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict_1, +                            next_hop=NEXT_HOPS[addr_type], +                            protocol=protocol) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +    # Clear bgp +    result = clear_bgp_and_verify(tgen, topo, dut) +    assert result is True, "Testcase {} : Failed \n Error: {}".format( +        tc_name, result) + +    for addr_type in ADDR_TYPES: +        input_dict_1 = { +            "r3": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type] +                    } +                ] +            } +        } +        logger.info("Verifying %s routes on r3", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict_1, +                            next_hop=NEXT_HOPS[addr_type], +                            protocol=protocol) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +    write_test_footer(tc_name) + + +def test_ecmp_remove_redistribute_static(request): +    """ Verify routes are cleared from BGP and RIB table of DUT when +        redistribute static configuration is removed.""" + +    tc_name = request.node.name +    write_test_header(tc_name) +    tgen = get_topogen() + +    reset_config_on_routers(tgen) +    static_or_nw(tgen, topo, tc_name, "redist_static", "r2") +    for addr_type in ADDR_TYPES: + +        # Verifying RIB routes +        dut = "r3" +        protocol = "bgp" +        input_dict_1 = { +            "r3": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type] +                    } +                ] +            } +        } + +        logger.info("Verifying %s routes on r3", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict_1, +                            next_hop=NEXT_HOPS[addr_type], +                            protocol=protocol) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +    input_dict_2 = { +        "r2": { +            "bgp": { +                "address_family": { +                    "ipv4": { +                        "unicast": { +                            "redistribute": [{ +                                "redist_type": "static", +                                "delete": True + +                            }] +                        } +                    }, +                    "ipv6": { +                        "unicast": { +                            "redistribute": [{ +                                "redist_type": "static", +                                "delete": True + +                            }] +                        } +                    } +                } +            } +        } +    } + +    logger.info("Remove redistribute static") +    result = create_router_bgp(tgen, topo, input_dict_2) +    assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +    for addr_type in ADDR_TYPES: + +        # Verifying RIB routes +        dut = "r3" +        protocol = "bgp" +        input_dict_1 = { +            "r3": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type] +                    } +                ] +            } +        } + +        logger.info("Verifying %s routes on r3 are deleted", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict_1, +                            next_hop=[], protocol=protocol, expected=False) +        assert result is not True, "Testcase {} : Failed \n Routes still" \ +                                   " present in RIB".format(tc_name) + +    logger.info("Enable redistribute static") +    input_dict_2 = { +        "r2": { +            "bgp": { +                "address_family": { +                    "ipv4": { +                        "unicast": { +                            "redistribute": [{ +                                "redist_type": "static" +                            }] +                        } +                    }, +                    "ipv6": { +                        "unicast": { +                            "redistribute": [{ +                                "redist_type": "static" +                            }] +                        } +                    } +                } +            } +        } +    } +    result = create_router_bgp(tgen, topo, input_dict_2) +    assert result is True, "Testcase {} : Failed \n Error: {}".format( +        tc_name, result) + +    for addr_type in ADDR_TYPES: +        # Verifying RIB routes +        dut = "r3" +        protocol = "bgp" +        input_dict_1 = { +            "r3": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type] +                    } +                ] +            } +        } +        logger.info("Verifying %s routes on r3", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict_1, +                            next_hop=NEXT_HOPS[addr_type], +                            protocol=protocol) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +    write_test_footer(tc_name) + + +def test_ecmp_shut_bgp_neighbor(request): +    """ +    Disable/Shut selected paths nexthops and verify other next are installed in +    the RIB of DUT. Enable interfaces and verify RIB count. + +    Shut BGP neigbors one by one and verify BGP and routing table updated +    accordingly in DUT +    """ + +    tc_name = request.node.name +    write_test_header(tc_name) +    tgen = get_topogen() + +    logger.info(INTF_LIST_R2) +    # Verifying RIB routes +    dut = "r3" +    protocol = "bgp" + +    reset_config_on_routers(tgen) +    static_or_nw(tgen, topo, tc_name, "redist_static", "r2") + +    for addr_type in ADDR_TYPES: +        input_dict = { +            "r3": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type] +                    } +                ] +            } +        } + +        logger.info("Verifying %s routes on r3", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict, +                            next_hop=NEXT_HOPS[addr_type], +                            protocol=protocol) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +    for intf_num in range(len(INTF_LIST_R2)+1, 16): +        intf_val = INTF_LIST_R2[intf_num:intf_num+16] + +        input_dict_1 = { +            "r2": { +                "interface_list": [intf_val], +                "status": "down" +            } +        } +        logger.info("Shutting down neighbor interface {} on r2". +                    format(intf_val)) +        result = interface_status(tgen, topo, input_dict_1) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +        for addr_type in ADDR_TYPES: +            if intf_num + 16 < 32: +                check_hops = NEXT_HOPS[addr_type] +            else: +                check_hops = [] + +            input_dict = { +                "r3": { +                    "static_routes": [ +                        { +                            "network": NETWORK[addr_type] +                        } +                    ] +                } +            } +            logger.info("Verifying %s routes on r3", addr_type) +            result = verify_rib(tgen, addr_type, dut, input_dict, +                                next_hop=check_hops, +                                protocol=protocol) +            assert result is True, "Testcase {} : Failed \n Error: {}".format( +                tc_name, result) + +    input_dict_1 = { +        "r2": { +            "interface_list": INTF_LIST_R2, +            "status": "up" +        } +    } + +    logger.info("Enabling all neighbor interface {} on r2") +    result = interface_status(tgen, topo, input_dict_1) +    assert result is True, "Testcase {} : Failed \n Error: {}".format( +        tc_name, result) + +    static_or_nw(tgen, topo, tc_name, "redist_static", "r2") +    for addr_type in ADDR_TYPES: +        input_dict = { +            "r3": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type] +                    } +                ] +            } +        } + +        logger.info("Verifying %s routes on r3", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict, +                            next_hop=NEXT_HOPS[addr_type], +                            protocol=protocol) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +    write_test_footer(tc_name) + + +def test_ecmp_remove_static_route(request): +    """ +    Delete static routes and verify routers are cleared from BGP table, +    and RIB of DUT. +    """ + +    tc_name = request.node.name +    write_test_header(tc_name) +    tgen = get_topogen() + +    # Verifying RIB routes +    dut = "r3" +    protocol = "bgp" + +    reset_config_on_routers(tgen) + +    static_or_nw(tgen, topo, tc_name, "redist_static", "r2") +    for addr_type in ADDR_TYPES: +        input_dict_1 = { +            "r3": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type] +                    } +                ] +            } +        } + +        logger.info("Verifying %s routes on r3", addr_type) +        result = verify_rib( +            tgen, addr_type, dut, input_dict_1, +            next_hop=NEXT_HOPS[addr_type], protocol=protocol) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +    for addr_type in ADDR_TYPES: +        input_dict_2 = { +            "r2": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type], +                        "next_hop": NEXT_HOP_IP[addr_type], +                        "delete": True +                    } +                ] +            } +        } + +        logger.info("Remove static routes") +        result = create_static_routes(tgen, input_dict_2) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +        logger.info("Verifying %s routes on r3 are removed", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict_2, +                            next_hop=[], protocol=protocol, expected=False) +        assert result is not True, "Testcase {} : Failed \n Routes still" \ +                                   " present in RIB".format(tc_name) + +    for addr_type in ADDR_TYPES: +        # Enable static routes +        input_dict_4 = { +            "r2": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type], +                        "next_hop": NEXT_HOP_IP[addr_type] +                    } +                ] +            } +        } + +        logger.info("Enable static route") +        result = create_static_routes(tgen, input_dict_4) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +        logger.info("Verifying %s routes on r3", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict_4, +                            next_hop=NEXT_HOPS[addr_type], +                            protocol=protocol) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + + +def test_ecmp_remove_nw_advertise(request): +    """ +    Verify routes are cleared from BGP and RIB table of DUT, +    when advertise network configuration is removed +    """ + +    tc_name = request.node.name +    write_test_header(tc_name) +    tgen = get_topogen() + +    # Verifying RIB routes +    dut = "r3" +    protocol = "bgp" + +    reset_config_on_routers(tgen) +    static_or_nw(tgen, topo, tc_name, "advertise_nw", "r2") +    for addr_type in ADDR_TYPES: +        input_dict = { +            "r3": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type] +                    } +                ] +            } +        } + +        logger.info("Verifying %s routes on r3", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict, +                            next_hop=NEXT_HOPS[addr_type], +                            protocol=protocol) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +    input_dict_3 = { +        "r2": { +            "bgp": { +                "address_family": { +                    "ipv4": { +                        "unicast": { +                            "advertise_networks": [{ +                                    "network": NETWORK["ipv4"], +                                    "delete": True +                                }] +                            } +                        }, +                    "ipv6": { +                        "unicast": { +                            "advertise_networks": [{ +                                    "network": NETWORK["ipv6"], +                                    "delete": True +                                }] +                            } +                        } +                    } +                } +            } +        } + +    logger.info("Withdraw advertised networks") +    result = create_router_bgp(tgen, topo, input_dict_3) +    assert result is True, "Testcase {} : Failed \n Error: {}".format( +        tc_name, result) + +    for addr_type in ADDR_TYPES: +        input_dict = { +            "r3": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type] +                    } +                ] +            } +        } + +        logger.info("Verifying %s routes on r3", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict, +                            next_hop=[], protocol=protocol, expected=False) +        assert result is not True, "Testcase {} : Failed \n Routes still" \ +                                   " present in RIB".format(tc_name) + +    static_or_nw(tgen, topo, tc_name, "advertise_nw", "r2") +    for addr_type in ADDR_TYPES: +        input_dict = { +            "r3": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type] +                    } +                ] +            } +        } +        logger.info("Verifying %s routes on r3", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict, +                            next_hop=NEXT_HOPS[addr_type], +                            protocol=protocol) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +    write_test_footer(tc_name) + + +if __name__ == "__main__": +    args = ["-s"] + sys.argv[1:] +    sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py b/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py new file mode 100755 index 0000000000..a9f18ed1fa --- /dev/null +++ b/tests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py @@ -0,0 +1,813 @@ +#!/usr/bin/env python + +# +# Copyright (c) 2019 by VMware, Inc. ("VMware") +# Used Copyright (c) 2018 by Network Device Education Foundation, Inc. +# ("NetDEF") in this file. +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND VMWARE DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL VMWARE BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + + +""" +Following tests are covered to test ecmp functionality on EBGP. +1. Verify routes installed as per maximum-paths configuration (8/16/32) +2. Disable/Shut selected paths nexthops and verify other next are installed in +   the RIB of DUT. Enable interfaces and verify RIB count. +3. Verify BGP table and RIB in DUT after clear BGP routes and neighbors. +4. Verify routes are cleared from BGP and RIB table of DUT when +   redistribute static configuration is removed. +5. Shut BGP neigbors one by one and verify BGP and routing table updated +   accordingly in DUT +6. Delete static routes and verify routers are cleared from BGP table and RIB +   of DUT. +7. Verify routes are cleared from BGP and RIB table of DUT when advertise +   network configuration is removed. +""" +import os +import sys +import time +import json +import pytest +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, '../')) +sys.path.append(os.path.join(CWD, '../../')) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib.topogen import Topogen, get_topogen +from mininet.topo import Topo + +from lib.common_config import ( +    start_topology, write_test_header, +    write_test_footer, +    verify_rib, create_static_routes, check_address_types, +    interface_status, reset_config_on_routers +) +from lib.topolog import logger +from lib.bgp import ( +    verify_bgp_convergence, create_router_bgp, +    clear_bgp_and_verify) +from lib.topojson import build_topo_from_json, build_config_from_json + +# Reading the data from JSON File for topology and configuration creation +jsonFile = "{}/ibgp_ecmp_topo2.json".format(CWD) + +try: +    with open(jsonFile, "r") as topoJson: +        topo = json.load(topoJson) +except IOError: +    assert False, "Could not read file {}".format(jsonFile) + +# Global variables +NEXT_HOPS = {"ipv4": [], "ipv6": []} +INTF_LIST_R3 = [] +INTF_LIST_R2 = [] +NETWORK = {"ipv4": "11.0.20.1/32", "ipv6": "1::/64"} +NEXT_HOP_IP = {"ipv4": "10.0.0.1", "ipv6": "fd00::1"} +BGP_CONVERGENCE = False + + +class CreateTopo(Topo): +    """ +    Test topology builder. + +    * `Topo`: Topology object +    """ + +    def build(self, *_args, **_opts): +        """Build function.""" +        tgen = get_topogen(self) + +        # Building topology from json file +        build_topo_from_json(tgen, topo) + + +def setup_module(mod): +    """ +    Sets up the pytest environment. + +    * `mod`: module name +    """ +    global NEXT_HOPS, INTF_LIST_R3, INTF_LIST_R2, TEST_STATIC +    global ADDR_TYPES + +    testsuite_run_time = time.asctime(time.localtime(time.time())) +    logger.info("Testsuite start time: {}".format(testsuite_run_time)) +    logger.info("=" * 40) + +    logger.info("Running setup_module to create topology") + +    # This function initiates the topology build with Topogen... +    tgen = Topogen(CreateTopo, mod.__name__) + +    # Starting topology, create tmp files which are loaded to routers +    #  to start deamons and then start routers +    start_topology(tgen) + +    # Creating configuration from JSON +    build_config_from_json(tgen, topo) + +    # Don't run this test if we have any failure. +    if tgen.routers_have_failure(): +        pytest.skip(tgen.errors) + +    # tgen.mininet_cli() +    # Api call verify whether BGP is converged +    ADDR_TYPES = check_address_types() + +    for addr_type in ADDR_TYPES: +        BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo) +        assert BGP_CONVERGENCE is True, ("setup_module :Failed \n Error:" +                                         " {}".format(BGP_CONVERGENCE)) + +    link_data = [val for links, val in +                 topo["routers"]["r2"]["links"].iteritems() +                 if "r3" in links] +    for adt in ADDR_TYPES: +        NEXT_HOPS[adt] = [val[adt].split("/")[0] for val in link_data] +        if adt == "ipv4": +            NEXT_HOPS[adt] = sorted( +                NEXT_HOPS[adt], key=lambda x: int(x.split(".")[2])) +        elif adt == "ipv6": +            NEXT_HOPS[adt] = sorted( +                NEXT_HOPS[adt], key=lambda x: int(x.split(':')[-3], 16)) + +    INTF_LIST_R2 = [val["interface"].split("/")[0] for val in link_data] +    INTF_LIST_R2 = sorted(INTF_LIST_R2, key=lambda x: int(x.split("eth")[1])) + +    link_data = [val for links, val in +                 topo["routers"]["r3"]["links"].iteritems() +                 if "r2" in links] +    INTF_LIST_R3 = [val["interface"].split("/")[0] for val in link_data] +    INTF_LIST_R3 = sorted(INTF_LIST_R3, key=lambda x: int(x.split("eth")[1])) + +    # STATIC_ROUTE = True +    logger.info("Running setup_module() done") + + +def teardown_module(): +    """ +    Teardown the pytest environment. + +    * `mod`: module name +    """ + +    logger.info("Running teardown_module to delete topology") + +    tgen = get_topogen() + +    # Stop toplogy and Remove tmp files +    tgen.stop_topology() + + +def static_or_nw(tgen, topo, tc_name, test_type, dut): + +    if test_type == "redist_static": +        input_dict_static = { +            dut: { +                "static_routes": [ +                    { +                        "network": NETWORK["ipv4"], +                        "next_hop": NEXT_HOP_IP["ipv4"] +                    }, +                    { +                        "network": NETWORK["ipv6"], +                        "next_hop": NEXT_HOP_IP["ipv6"] +                    } +                ] +            } +        } +        logger.info("Configuring static route on router %s", dut) +        result = create_static_routes(tgen, input_dict_static) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +        input_dict_2 = { +            dut: { +                "bgp": { +                    "address_family": { +                        "ipv4": { +                            "unicast": { +                                "redistribute": [{ +                                    "redist_type": "static" +                                }] +                            } +                        }, +                        "ipv6": { +                            "unicast": { +                                "redistribute": [{ +                                    "redist_type": "static" +                                }] +                            } +                        } +                    } +                } +            } +        } + +        logger.info("Configuring redistribute static route on router %s", dut) +        result = create_router_bgp(tgen, topo, input_dict_2) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +    elif test_type == "advertise_nw": +        input_dict_nw = { +            dut: { +                "bgp": { +                    "address_family": { +                        "ipv4": { +                            "unicast": { +                                "advertise_networks": [ +                                    {"network": NETWORK["ipv4"]} +                                ] +                            } +                        }, +                        "ipv6": { +                            "unicast": { +                                "advertise_networks": [ +                                    {"network": NETWORK["ipv6"]} +                                ] +                            } +                        } +                    } +                } +            } +        } + +        logger.info("Advertising networks %s %s from router %s", +                    NETWORK["ipv4"], NETWORK["ipv6"], dut) +        result = create_router_bgp(tgen, topo, input_dict_nw) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + + +@pytest.mark.parametrize("ecmp_num", ["8", "16", "32"]) +@pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"]) +def test_modify_ecmp_max_paths(request, ecmp_num, test_type): +    """ +    Verify routes installed as per maximum-paths +    configuration (8/16/32). +    """ + +    tc_name = request.node.name +    write_test_header(tc_name) +    tgen = get_topogen() + +    reset_config_on_routers(tgen) + +    static_or_nw(tgen, topo, tc_name, test_type, "r2") + +    input_dict = { +        "r3": { +            "bgp": { +                "address_family": { +                    "ipv4": { +                        "unicast": { +                            "maximum_paths": { +                                "ibgp": ecmp_num, +                            } +                        } +                    }, +                    "ipv6": { +                        "unicast": { +                            "maximum_paths": { +                                "ibgp": ecmp_num, +                            } +                        } +                    } +                } +            } +        } +    } + +    logger.info("Configuring bgp maximum-paths %s on router r3", ecmp_num) +    result = create_router_bgp(tgen, topo, input_dict) +    assert result is True, "Testcase {} : Failed \n Error: {}".format( +        tc_name, result) + +    # Verifying RIB routes +    dut = "r3" +    protocol = "bgp" + +    for addr_type in ADDR_TYPES: +        input_dict_1 = { +            "r3": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type] +                    } +                ] +            } +        } + +        logger.info("Verifying %s routes on r3", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict_1, +                            next_hop=NEXT_HOPS[addr_type], +                            protocol=protocol) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +    write_test_footer(tc_name) + + +def test_ecmp_after_clear_bgp(request): +    """ Verify BGP table and RIB in DUT after clear BGP routes and neighbors""" + +    tc_name = request.node.name +    write_test_header(tc_name) +    tgen = get_topogen() + +    reset_config_on_routers(tgen) + +    # Verifying RIB routes +    dut = "r3" +    protocol = "bgp" + +    static_or_nw(tgen, topo, tc_name, "redist_static", "r2") +    for addr_type in ADDR_TYPES: +        input_dict_1 = { +            "r3": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type] +                    } +                ] +            } +        } + +        logger.info("Verifying %s routes on r3", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict_1, +                            next_hop=NEXT_HOPS[addr_type], +                            protocol=protocol) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +    # Clear bgp +    result = clear_bgp_and_verify(tgen, topo, dut) +    assert result is True, "Testcase {} : Failed \n Error: {}".format( +        tc_name, result) + +    for addr_type in ADDR_TYPES: +        input_dict_1 = { +            "r3": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type] +                    } +                ] +            } +        } +        logger.info("Verifying %s routes on r3", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict_1, +                            next_hop=NEXT_HOPS[addr_type], +                            protocol=protocol) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +    write_test_footer(tc_name) + + +def test_ecmp_remove_redistribute_static(request): +    """ Verify routes are cleared from BGP and RIB table of DUT when +        redistribute static configuration is removed.""" + +    tc_name = request.node.name +    write_test_header(tc_name) +    tgen = get_topogen() + +    reset_config_on_routers(tgen) +    static_or_nw(tgen, topo, tc_name, "redist_static", "r2") +    for addr_type in ADDR_TYPES: + +        # Verifying RIB routes +        dut = "r3" +        protocol = "bgp" +        input_dict_1 = { +            "r3": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type] +                    } +                ] +            } +        } + +        logger.info("Verifying %s routes on r3", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict_1, +                            next_hop=NEXT_HOPS[addr_type], +                            protocol=protocol) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +    input_dict_2 = { +        "r2": { +            "bgp": { +                "address_family": { +                    "ipv4": { +                        "unicast": { +                            "redistribute": [{ +                                "redist_type": "static", +                                "delete": True + +                            }] +                        } +                    }, +                    "ipv6": { +                        "unicast": { +                            "redistribute": [{ +                                "redist_type": "static", +                                "delete": True + +                            }] +                        } +                    } +                } +            } +        } +    } + +    logger.info("Remove redistribute static") +    result = create_router_bgp(tgen, topo, input_dict_2) +    assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +    for addr_type in ADDR_TYPES: + +        # Verifying RIB routes +        dut = "r3" +        protocol = "bgp" +        input_dict_1 = { +            "r3": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type] +                    } +                ] +            } +        } + +        logger.info("Verifying %s routes on r3 are deleted", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict_1, +                            next_hop=[], protocol=protocol, expected=False) +        assert result is not True, "Testcase {} : Failed \n Routes still" \ +                                   " present in RIB".format(tc_name) + +    logger.info("Enable redistribute static") +    input_dict_2 = { +        "r2": { +            "bgp": { +                "address_family": { +                    "ipv4": { +                        "unicast": { +                            "redistribute": [{ +                                "redist_type": "static" +                            }] +                        } +                    }, +                    "ipv6": { +                        "unicast": { +                            "redistribute": [{ +                                "redist_type": "static" +                            }] +                        } +                    } +                } +            } +        } +    } +    result = create_router_bgp(tgen, topo, input_dict_2) +    assert result is True, "Testcase {} : Failed \n Error: {}".format( +        tc_name, result) + +    for addr_type in ADDR_TYPES: +        # Verifying RIB routes +        dut = "r3" +        protocol = "bgp" +        input_dict_1 = { +            "r3": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type] +                    } +                ] +            } +        } +        logger.info("Verifying %s routes on r3", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict_1, +                            next_hop=NEXT_HOPS[addr_type], +                            protocol=protocol) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +    write_test_footer(tc_name) + + +def test_ecmp_shut_bgp_neighbor(request): +    """ Shut BGP neigbors one by one and verify BGP and routing table updated +        accordingly in DUT """ + +    tc_name = request.node.name +    write_test_header(tc_name) +    tgen = get_topogen() + +    logger.info(INTF_LIST_R2) +    # Verifying RIB routes +    dut = "r3" +    protocol = "bgp" + +    reset_config_on_routers(tgen) +    static_or_nw(tgen, topo, tc_name, "redist_static", "r2") + +    for addr_type in ADDR_TYPES: +        input_dict = { +            "r3": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type] +                    } +                ] +            } +        } + +        logger.info("Verifying %s routes on r3", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict, +                            next_hop=NEXT_HOPS[addr_type], +                            protocol=protocol) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +    for intf_num in range(len(INTF_LIST_R2)+1, 16): +        intf_val = INTF_LIST_R2[intf_num:intf_num+16] + +        input_dict_1 = { +            "r2": { +                "interface_list": [intf_val], +                "status": "down" +            } +        } +        logger.info("Shutting down neighbor interface {} on r2". +                    format(intf_val)) +        result = interface_status(tgen, topo, input_dict_1) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +        for addr_type in ADDR_TYPES: +            if intf_num + 16 < 32: +                check_hops = NEXT_HOPS[addr_type] +            else: +                check_hops = [] + +            input_dict = { +                "r3": { +                    "static_routes": [ +                        { +                            "network": NETWORK[addr_type] +                        } +                    ] +                } +            } +            logger.info("Verifying %s routes on r3", addr_type) +            result = verify_rib(tgen, addr_type, dut, input_dict, +                                next_hop=check_hops, +                                protocol=protocol) +            assert result is True, "Testcase {} : Failed \n Error: {}".format( +                tc_name, result) + +    input_dict_1 = { +        "r2": { +            "interface_list": INTF_LIST_R2, +            "status": "up" +        } +    } + +    logger.info("Enabling all neighbor interface {} on r2") +    result = interface_status(tgen, topo, input_dict_1) +    assert result is True, "Testcase {} : Failed \n Error: {}".format( +        tc_name, result) + +    static_or_nw(tgen, topo, tc_name, "redist_static", "r2") +    for addr_type in ADDR_TYPES: +        input_dict = { +            "r3": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type] +                    } +                ] +            } +        } + +        logger.info("Verifying %s routes on r3", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict, +                            next_hop=NEXT_HOPS[addr_type], +                            protocol=protocol) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +    write_test_footer(tc_name) + + +def test_ecmp_remove_static_route(request): +    """ +    Delete static routes and verify routers are cleared from BGP table, +    and RIB of DUT. +    """ + +    tc_name = request.node.name +    write_test_header(tc_name) +    tgen = get_topogen() + +    # Verifying RIB routes +    dut = "r3" +    protocol = "bgp" + +    reset_config_on_routers(tgen) + +    static_or_nw(tgen, topo, tc_name, "redist_static", "r2") +    for addr_type in ADDR_TYPES: +        input_dict_1 = { +            "r3": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type] +                    } +                ] +            } +        } + +        logger.info("Verifying %s routes on r3", addr_type) +        result = verify_rib( +            tgen, addr_type, dut, input_dict_1, +            next_hop=NEXT_HOPS[addr_type], protocol=protocol) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +    for addr_type in ADDR_TYPES: +        input_dict_2 = { +            "r2": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type], +                        "next_hop": NEXT_HOP_IP[addr_type], +                        "delete": True +                    } +                ] +            } +        } + +        logger.info("Remove static routes") +        result = create_static_routes(tgen, input_dict_2) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +        logger.info("Verifying %s routes on r3 are removed", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict_2, +                            next_hop=[], protocol=protocol, expected=False) +        assert result is not True, "Testcase {} : Failed \n Routes still" \ +                                   " present in RIB".format(tc_name) + +    for addr_type in ADDR_TYPES: +        # Enable static routes +        input_dict_4 = { +            "r2": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type], +                        "next_hop": NEXT_HOP_IP[addr_type] +                    } +                ] +            } +        } + +        logger.info("Enable static route") +        result = create_static_routes(tgen, input_dict_4) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +        logger.info("Verifying %s routes on r3", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict_4, +                            next_hop=NEXT_HOPS[addr_type], +                            protocol=protocol) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +    write_test_footer(tc_name) + + +def test_ecmp_remove_nw_advertise(request): +    """ +    Verify routes are cleared from BGP and RIB table of DUT, +    when advertise network configuration is removed +    """ + +    tc_name = request.node.name +    write_test_header(tc_name) +    tgen = get_topogen() + +    # Verifying RIB routes +    dut = "r3" +    protocol = "bgp" + +    reset_config_on_routers(tgen) +    static_or_nw(tgen, topo, tc_name, "advertise_nw", "r2") +    for addr_type in ADDR_TYPES: +        input_dict = { +            "r3": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type] +                    } +                ] +            } +        } + +        logger.info("Verifying %s routes on r3", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict, +                            next_hop=NEXT_HOPS[addr_type], +                            protocol=protocol) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + +    input_dict_3 = { +        "r2": { +            "bgp": { +                "address_family": { +                    "ipv4": { +                        "unicast": { +                            "advertise_networks": [{ +                                    "network": NETWORK["ipv4"], +                                    "delete": True +                                }] +                            } +                        }, +                    "ipv6": { +                        "unicast": { +                            "advertise_networks": [{ +                                    "network": NETWORK["ipv6"], +                                    "delete": True +                                }] +                            } +                        } +                    } +                } +            } +        } + +    logger.info("Withdraw advertised networks") +    result = create_router_bgp(tgen, topo, input_dict_3) +    assert result is True, "Testcase {} : Failed \n Error: {}".format( +        tc_name, result) + +    for addr_type in ADDR_TYPES: +        input_dict = { +            "r3": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type] +                    } +                ] +            } +        } + +        logger.info("Verifying %s routes on r3", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict, +                            next_hop=[], protocol=protocol, expected=False) +        assert result is not True, "Testcase {} : Failed \n Routes still" \ +                                   " present in RIB".format(tc_name) + +    static_or_nw(tgen, topo, tc_name, "advertise_nw", "r2") +    for addr_type in ADDR_TYPES: +        input_dict = { +            "r3": { +                "static_routes": [ +                    { +                        "network": NETWORK[addr_type] +                    } +                ] +            } +        } +        logger.info("Verifying %s routes on r3", addr_type) +        result = verify_rib(tgen, addr_type, dut, input_dict, +                            next_hop=NEXT_HOPS[addr_type], +                            protocol=protocol) +        assert result is True, "Testcase {} : Failed \n Error: {}".format( +            tc_name, result) + + +if __name__ == "__main__": +    args = ["-s"] + sys.argv[1:] +    sys.exit(pytest.main(args)) diff --git a/tests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py b/tests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py index 2b9c411ff2..9f92b4b290 100755 --- a/tests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py +++ b/tests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py @@ -219,7 +219,8 @@ def test_next_hop_attribute(request):      dut = "r1"      protocol = "bgp"      result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False) -    assert result is not True +    assert result is not True, "Testcase {} : Failed \n Error: Routes still" \ +                               " present in RIB".format(tc_name)      # Configure next-hop-self to bgp neighbor      input_dict_1 = { @@ -484,7 +485,7 @@ def test_localpref_attribute(request):                              "neighbor": {                                  "r1": {                                      "dest_link": { -                                        "r3": { +                                        "r2": {                                              "route_maps": [                                                  {"name": "RMAP_LOCAL_PREF",                                                   "direction": "in"} @@ -499,6 +500,7 @@ def test_localpref_attribute(request):              }          }      } +      result = create_router_bgp(tgen, topo, input_dict_4)      assert result is True, "Testcase {} : Failed \n Error: {}".format(          tc_name, result) diff --git a/tests/topotests/bgp-prefix-list-topo1/test_prefix_lists.py b/tests/topotests/bgp-prefix-list-topo1/test_prefix_lists.py index d3892e9d07..b8975997ea 100755 --- a/tests/topotests/bgp-prefix-list-topo1/test_prefix_lists.py +++ b/tests/topotests/bgp-prefix-list-topo1/test_prefix_lists.py @@ -386,8 +386,8 @@ def test_ip_prefix_lists_out_permit(request):          tc_name, result)      result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False) -    assert result is not True, "Testcase {} : Failed \n Error: {}".format( -        tc_name, result) +    assert result is not True, "Testcase {} : Failed \n Error: Routes still" \ +                               " present in RIB".format(tc_name)      write_test_footer(tc_name) @@ -497,8 +497,8 @@ def test_ip_prefix_lists_in_deny_and_permit_any(request):      dut = "r3"      protocol = "bgp"      result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False) -    assert result is not True, "Testcase {} : Failed \n Error: {}".format( -        tc_name, result) +    assert result is not True, "Testcase {} : Failed \n Error: Routes still" \ +                               " present in RIB".format(tc_name)      write_test_footer(tc_name) @@ -542,7 +542,6 @@ def test_delete_prefix_lists(request):      result = verify_prefix_lists(tgen, input_dict_2)      assert result is not True, "Testcase {} : Failed \n Error: {}".format(          tc_name, result) -    logger.info(result)      # Delete prefix list      input_dict_2 = { @@ -714,9 +713,8 @@ def test_ip_prefix_lists_out_deny_and_permit_any(request):      dut = "r4"      protocol = "bgp"      result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False) -    assert result is not True, "Testcase {} : Failed \n Error: {}".format( -        tc_name, result) - +    assert result is not True, "Testcase {} : Failed \n Error: Routes still" \ +                               " present in RIB".format(tc_name)      write_test_footer(tc_name) @@ -859,8 +857,8 @@ def test_modify_prefix_lists_in_permit_to_deny(request):      dut = "r3"      protocol = "bgp"      result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False) -    assert result is not True, "Testcase {} : Failed \n Error: {}".format( -        tc_name, result) +    assert result is not True, "Testcase {} : Failed \n Error: Routes still" \ +                               " present in RIB".format(tc_name)      write_test_footer(tc_name) @@ -972,8 +970,8 @@ def test_modify_prefix_lists_in_deny_to_permit(request):      dut = "r3"      protocol = "bgp"      result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False) -    assert result is not True, "Testcase {} : Failed \n Error: {}".format( -        tc_name, result) +    assert result is not True, "Testcase {} : Failed \n Error: Routes still" \ +                               " present in RIB".format(tc_name)      # Modify  ip prefix list      input_dict_1 = { @@ -1152,8 +1150,8 @@ def test_modify_prefix_lists_out_permit_to_deny(request):      dut = "r4"      protocol = "bgp"      result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False) -    assert result is not True, "Testcase {} : Failed \n Error: {}".format( -        tc_name, result) +    assert result is not True, "Testcase {} : Failed \n Error: Routes still" \ +                               " present in RIB".format(tc_name)      write_test_footer(tc_name) @@ -1265,8 +1263,8 @@ def test_modify_prefix_lists_out_deny_to_permit(request):      dut = "r4"      protocol = "bgp"      result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False) -    assert result is not True, "Testcase {} : Failed \n Error: {}".format( -        tc_name, result) +    assert result is not True, "Testcase {} : Failed \n Error: Routes still" \ +                               " present in RIB".format(tc_name)      # Modify ip prefix list      input_dict_1 = { @@ -1439,8 +1437,8 @@ def test_ip_prefix_lists_implicit_deny(request):      dut = "r4"      protocol = "bgp"      result = verify_rib(tgen, "ipv4", dut, input_dict_1, protocol=protocol, expected=False) -    assert result is not True, "Testcase {} : Failed \n Error: {}".format( -        tc_name, result) +    assert result is not True, "Testcase {} : Failed \n Error: Routes still" \ +                               " present in RIB".format(tc_name)      write_test_footer(tc_name) diff --git a/tests/topotests/bgp_comm-list_delete/test_bgp_comm-list_delete.py b/tests/topotests/bgp_comm-list_delete/test_bgp_comm-list_delete.py index de6c35ba8f..ed350ebfeb 100644 --- a/tests/topotests/bgp_comm-list_delete/test_bgp_comm-list_delete.py +++ b/tests/topotests/bgp_comm-list_delete/test_bgp_comm-list_delete.py @@ -88,7 +88,7 @@ def test_bgp_maximum_prefix_invalid():          while True:              output = json.loads(tgen.gears[router].vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))              if output['192.168.255.1']['bgpState'] == 'Established': -                if output['192.168.255.1']['addressFamilyInfo']['IPv4 Unicast']['acceptedPrefixCounter'] == 2: +                if output['192.168.255.1']['addressFamilyInfo']['ipv4Unicast']['acceptedPrefixCounter'] == 2:                      return True      def _bgp_comm_list_delete(router): diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/adjacencies.py b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/adjacencies.py index 7b3a883afa..1317a510d1 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/adjacencies.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/adjacencies.py @@ -2,16 +2,16 @@ from lutil import luCommand  luCommand('ce1','ping 192.168.1.1 -c 1',' 0. packet loss','pass','CE->PE ping')  luCommand('ce2','ping 192.168.1.1 -c 1',' 0. packet loss','pass','CE->PE ping')  luCommand('ce3','ping 192.168.1.1 -c 1',' 0. packet loss','pass','CE->PE ping') -luCommand('ce1','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up',90) -luCommand('ce2','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up') -luCommand('ce3','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up') +luCommand('ce1','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up',180) +luCommand('ce2','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up',180) +luCommand('ce3','vtysh -c "show bgp summary"',' 00:0','wait','Adjacencies up',180)  luCommand('r1','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60)  luCommand('r3','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60)  luCommand('r4','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60) -luCommand('r2','vtysh -c "show bgp summary"',' 00:0.* 00:0.* 00:0','wait','Core adjacencies up') -luCommand('r1','vtysh -c "show bgp summary"',' 00:0','pass','Core adjacencies up') -luCommand('r3','vtysh -c "show bgp summary"',' 00:0','pass','Core adjacencies up') -luCommand('r4','vtysh -c "show bgp summary"',' 00:0','pass','Core adjacencies up') +luCommand('r2','vtysh -c "show bgp summary"',' 00:0.* 00:0.* 00:0','wait','Core adjacencies up',180) +luCommand('r1','vtysh -c "show bgp summary"',' 00:0','wait','Core adjacencies up',180) +luCommand('r3','vtysh -c "show bgp summary"',' 00:0','wait','Core adjacencies up',180) +luCommand('r4','vtysh -c "show bgp summary"',' 00:0','wait','Core adjacencies up',180)  luCommand('r1','vtysh -c "show bgp vrf all summary"',' 00:0.* 00:0','pass','All adjacencies up')  luCommand('r3','vtysh -c "show bgp vrf all summary"',' 00:0.* 00:0','pass','All adjacencies up')  luCommand('r4','vtysh -c "show bgp vrf all summary"',' 00:0.* 00:0','pass','All adjacencies up') diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py index 5674120b9c..c2b0cf9e7a 100644 --- a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py +++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py @@ -6,10 +6,10 @@ luCommand('ce4','vtysh -c "show bgp vrf all summary"',' 00:0','wait','Adjacencie  luCommand('r1','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60)  luCommand('r3','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60)  luCommand('r4','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60) -luCommand('r2','vtysh -c "show bgp summary"',' 00:0.* 00:0.* 00:0','wait','Core adjacencies up',300) -luCommand('r1','vtysh -c "show bgp summary"',' 00:0','pass','Core adjacencies up') -luCommand('r3','vtysh -c "show bgp summary"',' 00:0','pass','Core adjacencies up') -luCommand('r4','vtysh -c "show bgp summary"',' 00:0','pass','Core adjacencies up') +luCommand('r2','vtysh -c "show bgp summary"',' 00:0.* 00:0.* 00:0','wait','Core adjacencies up',180) +luCommand('r1','vtysh -c "show bgp summary"',' 00:0','wait','Core adjacencies up', 180) +luCommand('r3','vtysh -c "show bgp summary"',' 00:0','wait','Core adjacencies up', 180) +luCommand('r4','vtysh -c "show bgp summary"',' 00:0','wait','Core adjacencies up', 180)  luCommand('r1','vtysh -c "show bgp vrf all summary"',' 00:0.* 00:0','pass','All adjacencies up')  luCommand('r3','vtysh -c "show bgp vrf all summary"',' 00:0.* 00:0','pass','All adjacencies up')  luCommand('r4','vtysh -c "show bgp vrf all summary"',' 00:0.* 00:0.* 00:0','pass','All adjacencies up') diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/adjacencies.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/adjacencies.py index 1f53791f6a..6fbe4ff1c0 100644 --- a/tests/topotests/bgp_rfapi_basic_sanity/scripts/adjacencies.py +++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/adjacencies.py @@ -1,10 +1,10 @@  luCommand('r1','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60)  luCommand('r3','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60)  luCommand('r4','ping 2.2.2.2 -c 1',' 0. packet loss','wait','PE->P2 (loopback) ping',60) -luCommand('r2','vtysh -c "show bgp summary"',' 00:0.* 00:0.* 00:0','wait','Core adjacencies up',30) -luCommand('r1','vtysh -c "show bgp vrf all summary"',' 00:0','pass','All adjacencies up') -luCommand('r3','vtysh -c "show bgp vrf all summary"',' 00:0','pass','All adjacencies up') -luCommand('r4','vtysh -c "show bgp vrf all summary"',' 00:0','pass','All adjacencies up') +luCommand('r2','vtysh -c "show bgp summary"',' 00:0.* 00:0.* 00:0','wait','Core adjacencies up',180) +luCommand('r1','vtysh -c "show bgp vrf all summary"',' 00:0','wait','All adjacencies up',180) +luCommand('r3','vtysh -c "show bgp vrf all summary"',' 00:0','wait','All adjacencies up',180) +luCommand('r4','vtysh -c "show bgp vrf all summary"',' 00:0','wait','All adjacencies up',180)  luCommand('r1','ping 3.3.3.3 -c 1',' 0. packet loss','wait','PE->PE3 (loopback) ping')  luCommand('r1','ping 4.4.4.4 -c 1',' 0. packet loss','wait','PE->PE4 (loopback) ping')  #luCommand('r4','ping 3.3.3.3 -c 1',' 0. packet loss','wait','PE->PE3 (loopback) ping') diff --git a/tests/topotests/docker/inner/compile_frr.sh b/tests/topotests/docker/inner/compile_frr.sh index 2d72082c1e..dee0ec8118 100755 --- a/tests/topotests/docker/inner/compile_frr.sh +++ b/tests/topotests/docker/inner/compile_frr.sh @@ -84,6 +84,7 @@ if [ ! -e Makefile ]; then  		--enable-static-bin \  		--enable-static \  		--enable-shared \ +		--enable-dev-build \  		--with-moduledir=/usr/lib/frr/modules \  		--prefix=/usr \  		--localstatedir=/var/run/frr \ diff --git a/tests/topotests/example-topojson-test/test_topo_json_single_link_loopback/test_example_topojson.py b/tests/topotests/example-topojson-test/test_topo_json_single_link_loopback/test_example_topojson.py index b794b96a63..cd069aaec5 100755 --- a/tests/topotests/example-topojson-test/test_topo_json_single_link_loopback/test_example_topojson.py +++ b/tests/topotests/example-topojson-test/test_topo_json_single_link_loopback/test_example_topojson.py @@ -178,7 +178,7 @@ def test_static_routes(request):      # Static routes are created as part of initial configuration,      # verifying RIB      dut = 'r3' -    next_hop = '10.0.0.1' +    next_hop = ['10.0.0.1', '10.0.0.5']      input_dict = {          "r1": {              "static_routes": [ diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py index 2613f45f1c..c47dddb8d4 100644 --- a/tests/topotests/lib/bgp.py +++ b/tests/topotests/lib/bgp.py @@ -32,7 +32,8 @@ from lib.common_config import (create_common_configuration,                                 load_config_to_router,                                 check_address_types,                                 generate_ips, -                               find_interface_with_greater_ip) +                               find_interface_with_greater_ip, +                               run_frr_cmd, retry)  BGP_CONVERGENCE_TIMEOUT = 10 @@ -116,8 +117,8 @@ def create_router_bgp(tgen, topo, input_dict=None, build=False):              logger.debug("Router %s: 'bgp' not present in input_dict", router)              continue -        result = __create_bgp_global(tgen, input_dict, router, build) -        if result is True: +        data_all_bgp = __create_bgp_global(tgen, input_dict, router, build) +        if data_all_bgp:              bgp_data = input_dict[router]["bgp"]              bgp_addr_data = bgp_data.setdefault("address_family", {}) @@ -134,8 +135,18 @@ def create_router_bgp(tgen, topo, input_dict=None, build=False):                      or ipv6_data.setdefault("unicast", {}) else False                  if neigh_unicast: -                    result = __create_bgp_unicast_neighbor( -                        tgen, topo, input_dict, router, build) +                    data_all_bgp = __create_bgp_unicast_neighbor( +                        tgen, topo, input_dict, router, +                        config_data=data_all_bgp) + +        try: +            result = create_common_configuration(tgen, router, data_all_bgp, +                                                 "bgp", build) +        except InvalidCLIError: +            # Traceback +            errormsg = traceback.format_exc() +            logger.error(errormsg) +            return errormsg      logger.debug("Exiting lib API: create_router_bgp()")      return result @@ -157,77 +168,66 @@ def __create_bgp_global(tgen, input_dict, router, build=False):      True or False      """ -    result = False      logger.debug("Entering lib API: __create_bgp_global()") -    try: - -        bgp_data = input_dict[router]["bgp"] -        del_bgp_action = bgp_data.setdefault("delete", False) -        if del_bgp_action: -            config_data = ["no router bgp"] -            result = create_common_configuration(tgen, router, config_data, -                                                 "bgp", build=build) -            return result -        config_data = [] +    bgp_data = input_dict[router]["bgp"] +    del_bgp_action = bgp_data.setdefault("delete", False) +    if del_bgp_action: +        config_data = ["no router bgp"] -        if "local_as" not in bgp_data and build: -            logger.error("Router %s: 'local_as' not present in input_dict" -                         "for BGP", router) -            return False +        return config_data -        local_as = bgp_data.setdefault("local_as", "") -        cmd = "router bgp {}".format(local_as) -        vrf_id = bgp_data.setdefault("vrf", None) -        if vrf_id: -            cmd = "{} vrf {}".format(cmd, vrf_id) - -        config_data.append(cmd) +    config_data = [] -        router_id = bgp_data.setdefault("router_id", None) -        del_router_id = bgp_data.setdefault("del_router_id", False) -        if del_router_id: -            config_data.append("no bgp router-id") -        if router_id: -            config_data.append("bgp router-id {}".format( -                router_id)) +    if "local_as" not in bgp_data and build: +        logger.error("Router %s: 'local_as' not present in input_dict" +                     "for BGP", router) +        return False -        aggregate_address = bgp_data.setdefault("aggregate_address", -                                                {}) -        if aggregate_address: -            network = aggregate_address.setdefault("network", None) -            if not network: -                logger.error("Router %s: 'network' not present in " -                             "input_dict for BGP", router) -            else: -                cmd = "aggregate-address {}".format(network) +    local_as = bgp_data.setdefault("local_as", "") +    cmd = "router bgp {}".format(local_as) +    vrf_id = bgp_data.setdefault("vrf", None) +    if vrf_id: +        cmd = "{} vrf {}".format(cmd, vrf_id) + +    config_data.append(cmd) + +    router_id = bgp_data.setdefault("router_id", None) +    del_router_id = bgp_data.setdefault("del_router_id", False) +    if del_router_id: +        config_data.append("no bgp router-id") +    if router_id: +        config_data.append("bgp router-id {}".format( +            router_id)) + +    aggregate_address = bgp_data.setdefault("aggregate_address", +                                            {}) +    if aggregate_address: +        network = aggregate_address.setdefault("network", None) +        if not network: +            logger.error("Router %s: 'network' not present in " +                         "input_dict for BGP", router) +        else: +            cmd = "aggregate-address {}".format(network) -                as_set = aggregate_address.setdefault("as_set", False) -                summary = aggregate_address.setdefault("summary", False) -                del_action = aggregate_address.setdefault("delete", False) -                if as_set: -                    cmd = "{} {}".format(cmd, "as-set") -                if summary: -                    cmd = "{} {}".format(cmd, "summary") +            as_set = aggregate_address.setdefault("as_set", False) +            summary = aggregate_address.setdefault("summary", False) +            del_action = aggregate_address.setdefault("delete", False) +            if as_set: +                cmd = "{} {}".format(cmd, "as-set") +            if summary: +                cmd = "{} {}".format(cmd, "summary") -                if del_action: -                    cmd = "no {}".format(cmd) +            if del_action: +                cmd = "no {}".format(cmd) -                config_data.append(cmd) +            config_data.append(cmd) -        result = create_common_configuration(tgen, router, config_data, -                                             "bgp", build=build) -    except InvalidCLIError: -        # Traceback -        errormsg = traceback.format_exc() -        logger.error(errormsg) -        return errormsg - -    logger.debug("Exiting lib API: create_bgp_global()") -    return result +    return config_data -def __create_bgp_unicast_neighbor(tgen, topo, input_dict, router, build=False): +def __create_bgp_unicast_neighbor(tgen, topo, input_dict, router, +                                  config_data=None):      """      Helper API to create configuration for address-family unicast @@ -240,124 +240,118 @@ def __create_bgp_unicast_neighbor(tgen, topo, input_dict, router, build=False):      * `build` : Only for initial setup phase this is set as True.      """ -    result = False      logger.debug("Entering lib API: __create_bgp_unicast_neighbor()") -    try: -        config_data = ["router bgp"] -        bgp_data = input_dict[router]["bgp"]["address_family"] -        for addr_type, addr_dict in bgp_data.iteritems(): -            if not addr_dict: -                continue +    add_neigh = True +    if "router bgp "in config_data: +        add_neigh = False +    bgp_data = input_dict[router]["bgp"]["address_family"] -            if not check_address_types(addr_type): -                continue +    for addr_type, addr_dict in bgp_data.iteritems(): +        if not addr_dict: +            continue +        if not check_address_types(addr_type): +            continue + +        addr_data = addr_dict["unicast"] +        if addr_data:              config_data.append("address-family {} unicast".format(                  addr_type              )) -            addr_data = addr_dict["unicast"] -            advertise_network = addr_data.setdefault("advertise_networks", -                                                     []) -            for advertise_network_dict in advertise_network: -                network = advertise_network_dict["network"] -                if type(network) is not list: -                    network = [network] - -                if "no_of_network" in advertise_network_dict: -                    no_of_network = advertise_network_dict["no_of_network"] -                else: -                    no_of_network = 1 - -                del_action = advertise_network_dict.setdefault("delete", -                                                               False) +        advertise_network = addr_data.setdefault("advertise_networks", +                                                 []) +        for advertise_network_dict in advertise_network: +            network = advertise_network_dict["network"] +            if type(network) is not list: +                network = [network] + +            if "no_of_network" in advertise_network_dict: +                no_of_network = advertise_network_dict["no_of_network"] +            else: +                no_of_network = 1 -                # Generating IPs for verification -                prefix = str( -                    ipaddr.IPNetwork(unicode(network[0])).prefixlen) -                network_list = generate_ips(network, no_of_network) -                for ip in network_list: -                    ip = str(ipaddr.IPNetwork(unicode(ip)).network) +            del_action = advertise_network_dict.setdefault("delete", +                                                           False) -                    cmd = "network {}/{}\n".format(ip, prefix) -                    if del_action: -                        cmd = "no {}".format(cmd) +            # Generating IPs for verification +            prefix = str( +                ipaddr.IPNetwork(unicode(network[0])).prefixlen) +            network_list = generate_ips(network, no_of_network) +            for ip in network_list: +                ip = str(ipaddr.IPNetwork(unicode(ip)).network) -                    config_data.append(cmd) +                cmd = "network {}/{}".format(ip, prefix) +                if del_action: +                    cmd = "no {}".format(cmd) -            max_paths = addr_data.setdefault("maximum_paths", {}) -            if max_paths: -                ibgp = max_paths.setdefault("ibgp", None) -                ebgp = max_paths.setdefault("ebgp", None) -                if ibgp: -                    config_data.append("maximum-paths ibgp {}".format( -                        ibgp -                    )) -                if ebgp: -                    config_data.append("maximum-paths {}".format( -                        ebgp -                    )) - -            aggregate_address = addr_data.setdefault("aggregate_address", -                                                     {}) -            if aggregate_address: -                ip = aggregate_address("network", None) -                attribute = aggregate_address("attribute", None) -                if ip: -                    cmd = "aggregate-address {}".format(ip) -                    if attribute: -                        cmd = "{} {}".format(cmd, attribute) +                config_data.append(cmd) -                    config_data.append(cmd) +        max_paths = addr_data.setdefault("maximum_paths", {}) +        if max_paths: +            ibgp = max_paths.setdefault("ibgp", None) +            ebgp = max_paths.setdefault("ebgp", None) +            if ibgp: +                config_data.append("maximum-paths ibgp {}".format( +                    ibgp +                )) +            if ebgp: +                config_data.append("maximum-paths {}".format( +                    ebgp +                )) + +        aggregate_address = addr_data.setdefault("aggregate_address", +                                                 {}) +        if aggregate_address: +            ip = aggregate_address("network", None) +            attribute = aggregate_address("attribute", None) +            if ip: +                cmd = "aggregate-address {}".format(ip) +                if attribute: +                    cmd = "{} {}".format(cmd, attribute) -            redistribute_data = addr_data.setdefault("redistribute", {}) -            if redistribute_data: -                for redistribute in redistribute_data: -                    if "redist_type" not in redistribute: -                        logger.error("Router %s: 'redist_type' not present in " -                                     "input_dict", router) -                    else: -                        cmd = "redistribute {}".format( -                            redistribute["redist_type"]) -                        redist_attr = redistribute.setdefault("attribute", -                                                              None) -                        if redist_attr: -                            cmd = "{} {}".format(cmd, redist_attr) -                        del_action = redistribute.setdefault("delete", False) -                        if del_action: -                            cmd = "no {}".format(cmd) -                        config_data.append(cmd) +                config_data.append(cmd) -            if "neighbor" in addr_data: -                neigh_data = __create_bgp_neighbor(topo, input_dict, -                                                   router, addr_type) -                config_data.extend(neigh_data) +        redistribute_data = addr_data.setdefault("redistribute", {}) +        if redistribute_data: +            for redistribute in redistribute_data: +                if "redist_type" not in redistribute: +                    logger.error("Router %s: 'redist_type' not present in " +                                 "input_dict", router) +                else: +                    cmd = "redistribute {}".format( +                        redistribute["redist_type"]) +                    redist_attr = redistribute.setdefault("attribute", +                                                          None) +                    if redist_attr: +                        cmd = "{} {}".format(cmd, redist_attr) +                    del_action = redistribute.setdefault("delete", False) +                    if del_action: +                        cmd = "no {}".format(cmd) +                    config_data.append(cmd) -        for addr_type, addr_dict in bgp_data.iteritems(): -            if not addr_dict or not check_address_types(addr_type): -                continue +        if "neighbor" in addr_data: +            neigh_data = __create_bgp_neighbor(topo, input_dict, +                                               router, addr_type, add_neigh) +            config_data.extend(neigh_data) -            addr_data = addr_dict["unicast"] -            if "neighbor" in addr_data: -                neigh_addr_data = __create_bgp_unicast_address_family( -                    topo, input_dict, router, addr_type) +    for addr_type, addr_dict in bgp_data.iteritems(): +        if not addr_dict or not check_address_types(addr_type): +            continue -                config_data.extend(neigh_addr_data) +        addr_data = addr_dict["unicast"] +        if "neighbor" in addr_data: +            neigh_addr_data = __create_bgp_unicast_address_family( +                topo, input_dict, router, addr_type, add_neigh) -        result = create_common_configuration(tgen, router, config_data, -                                             None, build=build) +            config_data.extend(neigh_addr_data) -    except InvalidCLIError: -        # Traceback -        errormsg = traceback.format_exc() -        logger.error(errormsg) -        return errormsg      logger.debug("Exiting lib API: __create_bgp_unicast_neighbor()") -    return result +    return config_data -def __create_bgp_neighbor(topo, input_dict, router, addr_type): +def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True):      """      Helper API to create neighbor specific configuration @@ -391,7 +385,8 @@ def __create_bgp_neighbor(topo, input_dict, router, addr_type):              neigh_cxt = "neighbor {}".format(ip_addr) -            config_data.append("{} remote-as {}".format(neigh_cxt, remote_as)) +            if add_neigh: +                config_data.append("{} remote-as {}".format(neigh_cxt, remote_as))              if addr_type == "ipv6":                  config_data.append("address-family ipv6 unicast")                  config_data.append("{} activate".format(neigh_cxt)) @@ -429,7 +424,8 @@ def __create_bgp_neighbor(topo, input_dict, router, addr_type):      return config_data -def __create_bgp_unicast_address_family(topo, input_dict, router, addr_type): +def __create_bgp_unicast_address_family(topo, input_dict, router, addr_type, +                                        add_neigh=True):      """      API prints bgp global config to bgp_json file. @@ -531,6 +527,7 @@ def __create_bgp_unicast_address_family(topo, input_dict, router, addr_type):  #############################################  # Verification APIs  ############################################# +@retry(attempts=3, wait=2, return_is_str=True)  def verify_router_id(tgen, topo, input_dict):      """      Running command "show ip bgp json" for DUT and reading router-id @@ -565,7 +562,7 @@ def verify_router_id(tgen, topo, input_dict):      errormsg(str) or True      """ -    logger.info("Entering lib API: verify_router_id()") +    logger.debug("Entering lib API: verify_router_id()")      for router in input_dict.keys():          if router not in tgen.routers():              continue @@ -576,9 +573,9 @@ def verify_router_id(tgen, topo, input_dict):              "del_router_id", False)          logger.info("Checking router %s router-id", router) -        show_bgp_json = rnode.vtysh_cmd("show ip bgp json", +        show_bgp_json = run_frr_cmd(rnode, "show bgp summary json",                                          isjson=True) -        router_id_out = show_bgp_json["routerId"] +        router_id_out = show_bgp_json["ipv4Unicast"]["routerId"]          router_id_out = ipaddr.IPv4Address(unicode(router_id_out))          # Once router-id is deleted, highest interface ip should become @@ -598,100 +595,84 @@ def verify_router_id(tgen, topo, input_dict):                                                   router_id_out)              return errormsg -    logger.info("Exiting lib API: verify_router_id()") +    logger.debug("Exiting lib API: verify_router_id()")      return True +@retry(attempts=20, wait=2, return_is_str=True)  def verify_bgp_convergence(tgen, topo):      """      API will verify if BGP is converged with in the given time frame.      Running "show bgp summary json" command and verify bgp neighbor      state is established, -      Parameters      ----------      * `tgen`: topogen object      * `topo`: input json file data      * `addr_type`: ip_type, ipv4/ipv6 -      Usage      -----      # To veriry is BGP is converged for all the routers used in      topology      results = verify_bgp_convergence(tgen, topo, "ipv4") -      Returns      -------      errormsg(str) or True      """ -    logger.info("Entering lib API: verify_bgp_confergence()") +    logger.debug("Entering lib API: verify_bgp_convergence()")      for router, rnode in tgen.routers().iteritems(): -        logger.info("Verifying BGP Convergence on router %s:", router) - -        for retry in range(1, 11): -            show_bgp_json = rnode.vtysh_cmd("show bgp summary json", -                                            isjson=True) -            # Verifying output dictionary show_bgp_json is empty or not -            if not bool(show_bgp_json): -                errormsg = "BGP is not running" -                return errormsg +        logger.info("Verifying BGP Convergence on router %s", router) +        show_bgp_json = run_frr_cmd(rnode, "show bgp summary json", +                                    isjson=True) +        # Verifying output dictionary show_bgp_json is empty or not +        if not bool(show_bgp_json): +            errormsg = "BGP is not running" +            return errormsg -            # To find neighbor ip type +        # To find neighbor ip type +        bgp_addr_type = topo["routers"][router]["bgp"]["address_family"] +        for addr_type in bgp_addr_type.keys(): +            if not check_address_types(addr_type): +                continue              total_peer = 0 -            bgp_addr_type = topo["routers"][router]["bgp"]["address_family"] -            for addr_type in bgp_addr_type.keys(): -                if not check_address_types(addr_type): -                    continue - -                bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] - -                for bgp_neighbor in bgp_neighbors: -                    total_peer += len(bgp_neighbors[bgp_neighbor]["dest_link"]) - -            for addr_type in bgp_addr_type.keys(): -                bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] - -                no_of_peer = 0 -                for bgp_neighbor, peer_data in bgp_neighbors.iteritems(): -                    for dest_link in peer_data["dest_link"].keys(): -                        data = topo["routers"][bgp_neighbor]["links"] -                        if dest_link in data: -                            neighbor_ip = \ -                                data[dest_link][addr_type].split("/")[0] -                            if addr_type == "ipv4": -                                ipv4_data = show_bgp_json["ipv4Unicast"][ -                                    "peers"] -                                nh_state = ipv4_data[neighbor_ip]["state"] -                            else: -                                ipv6_data = show_bgp_json["ipv6Unicast"][ -                                    "peers"] -                                nh_state = ipv6_data[neighbor_ip]["state"] - -                            if nh_state == "Established": -                                no_of_peer += 1 -            if no_of_peer == total_peer: -                logger.info("BGP is Converged for router %s", router) -                break -            else: -                logger.warning("BGP is not yet Converged for router %s", -                               router) -                sleeptime = 2 * retry -                if sleeptime <= BGP_CONVERGENCE_TIMEOUT: -                    # Waiting for BGP to converge -                    logger.info("Waiting for %s sec for BGP to converge on" -                                " router %s...", sleeptime, router) -                    sleep(sleeptime) -                else: -                    show_bgp_summary = rnode.vtysh_cmd("show bgp summary") -                    errormsg = "TIMEOUT!! BGP is not converged in {} " \ -                               "seconds  for router {} \n {}".format( -                                   BGP_CONVERGENCE_TIMEOUT, router, -                                   show_bgp_summary) -                    return errormsg +            bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] + +            for bgp_neighbor in bgp_neighbors: +                total_peer += len(bgp_neighbors[bgp_neighbor]["dest_link"]) + +        for addr_type in bgp_addr_type.keys(): +            if not check_address_types(addr_type): +                continue +            bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"] + +            no_of_peer = 0 +            for bgp_neighbor, peer_data in bgp_neighbors.iteritems(): +                for dest_link in peer_data["dest_link"].keys(): +                    data = topo["routers"][bgp_neighbor]["links"] +                    if dest_link in data: +                        neighbor_ip = \ +                            data[dest_link][addr_type].split("/")[0] +                        if addr_type == "ipv4": +                            ipv4_data = show_bgp_json["ipv4Unicast"][ +                                "peers"] +                            nh_state = ipv4_data[neighbor_ip]["state"] +                        else: +                            ipv6_data = show_bgp_json["ipv6Unicast"][ +                                "peers"] +                            nh_state = ipv6_data[neighbor_ip]["state"] + +                        if nh_state == "Established": +                            no_of_peer += 1 +        if no_of_peer == total_peer: +            logger.info("BGP is Converged for router %s", router) +        else: +            errormsg = "BGP is not converged for router {}".format( +                router) +            return errormsg -    logger.info("Exiting API: verify_bgp_confergence()") +    logger.debug("Exiting API: verify_bgp_convergence()")      return True @@ -723,7 +704,7 @@ def modify_as_number(tgen, topo, input_dict):      errormsg(str) or True      """ -    logger.info("Entering lib API: modify_as_number()") +    logger.debug("Entering lib API: modify_as_number()")      try:          new_topo = deepcopy(topo["routers"]) @@ -757,11 +738,12 @@ def modify_as_number(tgen, topo, input_dict):          logger.error(errormsg)          return errormsg -    logger.info("Exiting lib API: modify_as_number()") +    logger.debug("Exiting lib API: modify_as_number()")      return True +@retry(attempts=3, wait=2, return_is_str=True)  def verify_as_numbers(tgen, topo, input_dict):      """      This API is to verify AS numbers for given DUT by running @@ -791,7 +773,7 @@ def verify_as_numbers(tgen, topo, input_dict):      errormsg(str) or True      """ -    logger.info("Entering lib API: verify_as_numbers()") +    logger.debug("Entering lib API: verify_as_numbers()")      for router in input_dict.keys():          if router not in tgen.routers():              continue @@ -800,7 +782,7 @@ def verify_as_numbers(tgen, topo, input_dict):          logger.info("Verifying AS numbers for  dut %s:", router) -        show_ip_bgp_neighbor_json = rnode.vtysh_cmd( +        show_ip_bgp_neighbor_json = run_frr_cmd(rnode,              "show ip bgp neighbor json", isjson=True)          local_as = input_dict[router]["bgp"]["local_as"]          bgp_addr_type = topo["routers"][router]["bgp"]["address_family"] @@ -846,7 +828,7 @@ def verify_as_numbers(tgen, topo, input_dict):                                      "neighbor %s, found expected: %s",                                      router, bgp_neighbor, remote_as) -    logger.info("Exiting lib API: verify_AS_numbers()") +    logger.debug("Exiting lib API: verify_AS_numbers()")      return True @@ -873,7 +855,7 @@ def clear_bgp_and_verify(tgen, topo, router):      errormsg(str) or True      """ -    logger.info("Entering lib API: clear_bgp_and_verify()") +    logger.debug("Entering lib API: clear_bgp_and_verify()")      if router not in tgen.routers():          return False @@ -883,20 +865,14 @@ def clear_bgp_and_verify(tgen, topo, router):      peer_uptime_before_clear_bgp = {}      # Verifying BGP convergence before bgp clear command      for retry in range(1, 11): -        sleeptime = 2 * retry -        if sleeptime <= BGP_CONVERGENCE_TIMEOUT: -            # Waiting for BGP to converge -            logger.info("Waiting for %s sec for BGP to converge on router" -                        " %s...", sleeptime, router) -            sleep(sleeptime) -        else: -            errormsg = "TIMEOUT!! BGP is not converged in {} seconds for" \ -                       " router {}".format(BGP_CONVERGENCE_TIMEOUT, router) -            return errormsg +        sleeptime = 3 +        # Waiting for BGP to converge +        logger.info("Waiting for %s sec for BGP to converge on router" +                    " %s...", sleeptime, router) +        sleep(sleeptime) -        show_bgp_json = rnode.vtysh_cmd("show bgp summary json", +        show_bgp_json = run_frr_cmd(rnode, "show bgp summary json",                                          isjson=True) -        logger.info(show_bgp_json)          # Verifying output dictionary show_bgp_json is empty or not          if not bool(show_bgp_json):              errormsg = "BGP is not running" @@ -950,33 +926,33 @@ def clear_bgp_and_verify(tgen, topo, router):                          " clear", router)              break          else: -            logger.warning("BGP is not yet Converged for router %s " -                           "before bgp clear", router) +            logger.info("BGP is not yet Converged for router %s " +                        "before bgp clear", router) +    else: +        errormsg = "TIMEOUT!! BGP is not converged in 30 seconds for" \ +                   " router {}".format(router) +        return errormsg      logger.info(peer_uptime_before_clear_bgp)      # Clearing BGP      logger.info("Clearing BGP neighborship for router %s..", router)      for addr_type in bgp_addr_type.keys():          if addr_type == "ipv4": -            rnode.vtysh_cmd("clear ip bgp *") +            run_frr_cmd(rnode, "clear ip bgp *")          elif addr_type == "ipv6": -            rnode.vtysh_cmd("clear bgp ipv6 *") +            run_frr_cmd(rnode, "clear bgp ipv6 *")      peer_uptime_after_clear_bgp = {}      # Verifying BGP convergence after bgp clear command -    for retry in range(1, 11): -        sleeptime = 2 * retry -        if sleeptime <= BGP_CONVERGENCE_TIMEOUT: -            # Waiting for BGP to converge -            logger.info("Waiting for %s sec for BGP to converge on router" -                        " %s...", sleeptime, router) -            sleep(sleeptime) -        else: -            errormsg = "TIMEOUT!! BGP is not converged in {} seconds for" \ -                       " router {}".format(BGP_CONVERGENCE_TIMEOUT, router) -            return errormsg +    for retry in range(11): +        sleeptime = 3 +        # Waiting for BGP to converge +        logger.info("Waiting for %s sec for BGP to converge on router" +                    " %s...", sleeptime, router) +        sleep(sleeptime) + -        show_bgp_json = rnode.vtysh_cmd("show bgp summary json", +        show_bgp_json = run_frr_cmd(rnode, "show bgp summary json",                                          isjson=True)          # Verifying output dictionary show_bgp_json is empty or not          if not bool(show_bgp_json): @@ -1028,9 +1004,12 @@ def clear_bgp_and_verify(tgen, topo, router):                          router)              break          else: -            logger.warning("BGP is not yet Converged for router %s after" -                           " bgp clear", router) - +            logger.info("BGP is not yet Converged for router %s after" +                        " bgp clear", router) +    else: +        errormsg = "TIMEOUT!! BGP is not converged in 30 seconds for" \ +                   " router {}".format(router) +        return errormsg      logger.info(peer_uptime_after_clear_bgp)      # Comparing peerUptimeEstablishedEpoch dictionaries      if peer_uptime_before_clear_bgp != peer_uptime_after_clear_bgp: @@ -1041,7 +1020,7 @@ def clear_bgp_and_verify(tgen, topo, router):                     " {}".format(router)          return errormsg -    logger.info("Exiting lib API: clear_bgp_and_verify()") +    logger.debug("Exiting lib API: clear_bgp_and_verify()")      return True @@ -1077,7 +1056,7 @@ def verify_bgp_timers_and_functionality(tgen, topo, input_dict):      errormsg(str) or True      """ -    logger.info("Entering lib API: verify_bgp_timers_and_functionality()") +    logger.debug("Entering lib API: verify_bgp_timers_and_functionality()")      sleep(5)      router_list = tgen.routers()      for router in input_dict.keys(): @@ -1090,7 +1069,7 @@ def verify_bgp_timers_and_functionality(tgen, topo, input_dict):                      router)          show_ip_bgp_neighbor_json = \ -            rnode.vtysh_cmd("show ip bgp neighbor json", isjson=True) +            run_frr_cmd(rnode, "show ip bgp neighbor json", isjson=True)          bgp_addr_type = input_dict[router]["bgp"]["address_family"] @@ -1178,7 +1157,7 @@ def verify_bgp_timers_and_functionality(tgen, topo, input_dict):                      sleep(keepalivetimer)                      sleep(2)                      show_bgp_json = \ -                        rnode.vtysh_cmd("show bgp summary json", +                        run_frr_cmd(rnode, "show bgp summary json",                                          isjson=True)                      if addr_type == "ipv4": @@ -1192,17 +1171,13 @@ def verify_bgp_timers_and_functionality(tgen, topo, input_dict):                              (holddowntimer - keepalivetimer):                          if nh_state != "Established":                              errormsg = "BGP neighborship has not  gone " \ -                                       "down in {} sec for neighbor {}\n" \ -                                       "show_bgp_json: \n {} ".format( -                                timer, bgp_neighbor, -                                show_bgp_json) +                                       "down in {} sec for neighbor {}" \ +                                .format(timer, bgp_neighbor)                              return errormsg                          else:                              logger.info("BGP neighborship is intact in %s" -                                        " sec for neighbor %s \n " -                                        "show_bgp_json : \n %s", -                                        timer, bgp_neighbor, -                                        show_bgp_json) +                                        " sec for neighbor %s", +                                        timer, bgp_neighbor)                  ####################                  # Shutting down peer interface and verifying that BGP @@ -1229,7 +1204,7 @@ def verify_bgp_timers_and_functionality(tgen, topo, input_dict):                      sleep(keepalivetimer)                      sleep(2)                      show_bgp_json = \ -                        rnode.vtysh_cmd("show bgp summary json", +                        run_frr_cmd(rnode, "show bgp summary json",                                          isjson=True)                      if addr_type == "ipv4": @@ -1242,22 +1217,19 @@ def verify_bgp_timers_and_functionality(tgen, topo, input_dict):                      if timer == holddowntimer:                          if nh_state == "Established":                              errormsg = "BGP neighborship has not gone " \ -                                       "down in {} sec for neighbor {}\n" \ -                                       "show_bgp_json: \n {} ".format( -                                timer, bgp_neighbor, -                                show_bgp_json) +                                       "down in {} sec for neighbor {}" \ +                                .format(timer, bgp_neighbor)                              return errormsg                          else:                              logger.info("BGP neighborship has gone down in" -                                        " %s sec for neighbor %s \n" -                                        "show_bgp_json : \n %s", -                                        timer, bgp_neighbor, -                                        show_bgp_json) +                                        " %s sec for neighbor %s", +                                        timer, bgp_neighbor) -    logger.info("Exiting lib API: verify_bgp_timers_and_functionality()") +    logger.debug("Exiting lib API: verify_bgp_timers_and_functionality()")      return True +@retry(attempts=3, wait=2, return_is_str=True)  def verify_best_path_as_per_bgp_attribute(tgen, addr_type, router, input_dict,                                            attribute):      """ @@ -1319,7 +1291,7 @@ def verify_best_path_as_per_bgp_attribute(tgen, addr_type, router, input_dict,      sleep(2)      logger.info("Verifying router %s RIB for best path:", router) -    sh_ip_bgp_json = rnode.vtysh_cmd(command, isjson=True) +    sh_ip_bgp_json = run_frr_cmd(rnode, command, isjson=True)      for route_val in input_dict.values():          net_data = route_val["bgp"]["address_family"]["ipv4"]["unicast"] @@ -1380,7 +1352,7 @@ def verify_best_path_as_per_bgp_attribute(tgen, addr_type, router, input_dict,              else:                  command = "show ipv6 route json" -            rib_routes_json = rnode.vtysh_cmd(command, isjson=True) +            rib_routes_json = run_frr_cmd(rnode, command, isjson=True)              # Verifying output dictionary rib_routes_json is not empty              if not bool(rib_routes_json): @@ -1417,6 +1389,7 @@ def verify_best_path_as_per_bgp_attribute(tgen, addr_type, router, input_dict,      return True +@retry(attempts=3, wait=2, return_is_str=True)  def verify_best_path_as_per_admin_distance(tgen, addr_type, router, input_dict,                                             attribute):      """ @@ -1451,7 +1424,7 @@ def verify_best_path_as_per_admin_distance(tgen, addr_type, router, input_dict,      errormsg(str) or True      """ -    logger.info("Entering lib API: verify_best_path_as_per_admin_distance()") +    logger.debug("Entering lib API: verify_best_path_as_per_admin_distance()")      router_list = tgen.routers()      if router not in router_list:          return False @@ -1490,7 +1463,7 @@ def verify_best_path_as_per_admin_distance(tgen, addr_type, router, input_dict,              compare = "LOWEST"          # Show ip route -        rib_routes_json = rnode.vtysh_cmd(command, isjson=True) +        rib_routes_json = run_frr_cmd(rnode, command, isjson=True)          # Verifying output dictionary rib_routes_json is not empty          if not bool(rib_routes_json): diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py index 75880cfd28..f2d33f94ae 100644 --- a/tests/topotests/lib/common_config.py +++ b/tests/topotests/lib/common_config.py @@ -23,24 +23,32 @@ from datetime import datetime  from time import sleep  from subprocess import call  from subprocess import STDOUT as SUB_STDOUT +from subprocess import PIPE as SUB_PIPE +from subprocess import Popen +from functools import wraps +from re import search as re_search +  import StringIO  import os  import ConfigParser  import traceback  import socket  import ipaddr +import re  from lib import topotest  from functools import partial  from lib.topolog import logger, logger_config  from lib.topogen import TopoRouter +from lib.topotest import interface_set_status  FRRCFG_FILE = "frr_json.conf"  FRRCFG_BKUP_FILE = "frr_json_initial.conf"  ERROR_LIST = ["Malformed", "Failure", "Unknown"] +ROUTER_LIST = []  ####  CD = os.path.dirname(os.path.realpath(__file__)) @@ -142,6 +150,35 @@ class InvalidCLIError(Exception):      pass +def run_frr_cmd(rnode, cmd, isjson=False): +    """ +    Execute frr show commands in priviledged mode + +    * `rnode`: router node on which commands needs to executed +    * `cmd`: Command to be executed on frr +    * `isjson`: If command is to get json data or not + +    :return str: +    """ + +    if cmd: +        ret_data = rnode.vtysh_cmd(cmd, isjson=isjson) + +        if True: +            if isjson: +                logger.debug(ret_data) +                print_data = rnode.vtysh_cmd(cmd.rstrip("json"), isjson=False) +            else: +                print_data = ret_data + +            logger.info('Output for command [ %s] on router %s:\n%s', +                        cmd.rstrip("json"), rnode.name, print_data) +        return ret_data + +    else: +        raise InvalidCLIError('No actual cmd passed') + +  def create_common_configuration(tgen, router, data, config_type=None,                                  build=False):      """ @@ -186,6 +223,7 @@ def create_common_configuration(tgen, router, data, config_type=None,              frr_cfg_fd.write(config_map[config_type])          for line in data:              frr_cfg_fd.write("{} \n".format(str(line))) +        frr_cfg_fd.write("\n")      except IOError as err:          logger.error("Unable to open FRR Config File. error(%s): %s" % @@ -215,10 +253,13 @@ def reset_config_on_routers(tgen, routerName=None):      logger.debug("Entering API: reset_config_on_routers")      router_list = tgen.routers() -    for rname, router in router_list.iteritems(): +    for rname in ROUTER_LIST:          if routerName and routerName != rname:              continue +        router = router_list[rname] +        logger.info("Configuring router %s to initial test configuration", +                    rname)          cfg = router.run("vtysh -c 'show running'")          fname = "{}/{}/frr.sav".format(TMPDIR, rname)          dname = "{}/{}/delta.conf".format(TMPDIR, rname) @@ -235,16 +276,35 @@ def reset_config_on_routers(tgen, routerName=None):          f.close() -        command = "/usr/lib/frr/frr-reload.py  --input {}/{}/frr.sav" \ -                  " --test {}/{}/frr_json_initial.conf > {}". \ -            format(TMPDIR, rname, TMPDIR, rname, dname) -        result = call(command, shell=True, stderr=SUB_STDOUT) +        run_cfg_file = "{}/{}/frr.sav".format(TMPDIR, rname) +        init_cfg_file = "{}/{}/frr_json_initial.conf".format(TMPDIR, rname) +        command = "/usr/lib/frr/frr-reload.py  --input {} --test {} > {}". \ +            format(run_cfg_file, init_cfg_file, dname) +        result = call(command, shell=True, stderr=SUB_STDOUT, +                      stdout=SUB_PIPE)          # Assert if command fail          if result > 0: -            errormsg = ("Command:{} is failed due to non-zero exit" -                        " code".format(command)) -            return errormsg +            logger.error("Delta file creation failed. Command executed %s", +                         command) +            with open(run_cfg_file, 'r') as fd: +                logger.info('Running configuration saved in %s is:\n%s', +                             run_cfg_file, fd.read()) +            with open(init_cfg_file, 'r') as fd: +                logger.info('Test configuration saved in %s is:\n%s', +                             init_cfg_file, fd.read()) + +            err_cmd = ['/usr/bin/vtysh', '-m', '-f', run_cfg_file] +            result = Popen(err_cmd, stdout=SUB_PIPE, stderr=SUB_PIPE) +            output = result.communicate() +            for out_data in output: +                temp_data = out_data.decode('utf-8').lower() +                for out_err in ERROR_LIST: +                    if out_err.lower() in temp_data: +                        logger.error("Found errors while validating data in" +                                     " %s", run_cfg_file) +                        raise InvalidCLIError(out_data) +            raise InvalidCLIError("Unknown error in %s", output)          f = open(dname, "r")          delta = StringIO.StringIO() @@ -264,7 +324,7 @@ def reset_config_on_routers(tgen, routerName=None):          delta.write("end\n")          output = router.vtysh_multicmd(delta.getvalue(),                                         pretty_output=False) -        logger.info("New configuration for router {}:".format(rname)) +          delta.close()          delta = StringIO.StringIO()          cfg = router.run("vtysh -c 'show running'") @@ -276,6 +336,8 @@ def reset_config_on_routers(tgen, routerName=None):          # Router current configuration to log file or console if          # "show_router_config" is defined in "pytest.ini"          if show_router_config: +            logger.info("Configuration on router {} after config reset:". +                        format(rname))              logger.info(delta.getvalue())          delta.close() @@ -297,34 +359,39 @@ def load_config_to_router(tgen, routerName, save_bkup=False):      logger.debug("Entering API: load_config_to_router")      router_list = tgen.routers() -    for rname, router in router_list.iteritems(): -        if rname == routerName: -            try: -                frr_cfg_file = "{}/{}/{}".format(TMPDIR, rname, FRRCFG_FILE) -                frr_cfg_bkup = "{}/{}/{}".format(TMPDIR, rname, -                                                 FRRCFG_BKUP_FILE) -                with open(frr_cfg_file, "r") as cfg: -                    data = cfg.read() -                    if save_bkup: -                        with open(frr_cfg_bkup, "w") as bkup: -                            bkup.write(data) - -                    output = router.vtysh_multicmd(data, pretty_output=False) -                    for out_err in ERROR_LIST: -                        if out_err.lower() in output.lower(): -                            raise InvalidCLIError("%s" % output) -            except IOError as err: -                errormsg = ("Unable to open config File. error(%s):" -                            "  %s", (err.errno, err.strerror)) -                return errormsg +    for rname in ROUTER_LIST: +        if routerName and routerName != rname: +            continue -            logger.info("New configuration for router {}:".format(rname)) -            new_config = router.run("vtysh -c 'show running'") +        router = router_list[rname] +        try: +            frr_cfg_file = "{}/{}/{}".format(TMPDIR, rname, FRRCFG_FILE) +            frr_cfg_bkup = "{}/{}/{}".format(TMPDIR, rname, +                                             FRRCFG_BKUP_FILE) +            with open(frr_cfg_file, "r+") as cfg: +                data = cfg.read() +                logger.info("Applying following configuration on router" +                            " {}:\n{}".format(rname, data)) +                if save_bkup: +                    with open(frr_cfg_bkup, "w") as bkup: +                        bkup.write(data) + +                output = router.vtysh_multicmd(data, pretty_output=False) +                for out_err in ERROR_LIST: +                    if out_err.lower() in output.lower(): +                        raise InvalidCLIError("%s" % output) + +                cfg.truncate(0) +        except IOError as err: +            errormsg = ("Unable to open config File. error(%s):" +                        "  %s", (err.errno, err.strerror)) +            return errormsg -            # Router current configuration to log file or console if -            # "show_router_config" is defined in "pytest.ini" -            if show_router_config: -                logger.info(new_config) +        # Router current configuration to log file or console if +        # "show_router_config" is defined in "pytest.ini" +        if show_router_config: +            new_config = router.run("vtysh -c 'show running'") +            logger.info(new_config)      logger.debug("Exting API: load_config_to_router")      return True @@ -337,21 +404,25 @@ def start_topology(tgen):      * `tgen`  : topogen object      """ -    global TMPDIR +    global TMPDIR, ROUTER_LIST      # Starting topology      tgen.start_topology()      # Starting deamons +      router_list = tgen.routers() +    ROUTER_LIST = sorted(router_list.keys(), +                         key=lambda x: int(re_search('\d+', x).group(0)))      TMPDIR = os.path.join(LOGDIR, tgen.modname) -    for rname, router in router_list.iteritems(): +    router_list = tgen.routers() +    for rname in ROUTER_LIST: +        router = router_list[rname]          try:              os.chdir(TMPDIR) -            # Creating rouer named dir and empty zebra.conf bgpd.conf files +            # Creating router named dir and empty zebra.conf bgpd.conf files              # inside the current directory -              if os.path.isdir('{}'.format(rname)):                  os.system("rm -rf {}".format(rname))                  os.mkdir('{}'.format(rname)) @@ -371,13 +442,11 @@ def start_topology(tgen):          router.load_config(              TopoRouter.RD_ZEBRA,              '{}/{}/zebra.conf'.format(TMPDIR, rname) -            # os.path.join(tmpdir, '{}/zebra.conf'.format(rname))          )          # Loading empty bgpd.conf file to router, to start the bgp deamon          router.load_config(              TopoRouter.RD_BGP,              '{}/{}/bgpd.conf'.format(TMPDIR, rname) -            # os.path.join(tmpdir, '{}/bgpd.conf'.format(rname))          )          # Starting routers @@ -446,27 +515,31 @@ def validate_ip_address(ip_address):                                           " address" % ip_address) -def check_address_types(addr_type): +def check_address_types(addr_type=None):      """      Checks environment variable set and compares with the current address type      """ -    global ADDRESS_TYPES -    if ADDRESS_TYPES is None: -        ADDRESS_TYPES = "dual" - -    if ADDRESS_TYPES == "dual": -        ADDRESS_TYPES = ["ipv4", "ipv6"] -    elif ADDRESS_TYPES == "ipv4": -        ADDRESS_TYPES = ["ipv4"] -    elif ADDRESS_TYPES == "ipv6": -        ADDRESS_TYPES = ["ipv6"] - -    if addr_type not in ADDRESS_TYPES: + +    addr_types_env = os.environ.get("ADDRESS_TYPES") +    if not addr_types_env: +        addr_types_env = "dual" + +    if addr_types_env == "dual": +        addr_types = ["ipv4", "ipv6"] +    elif addr_types_env == "ipv4": +        addr_types = ["ipv4"] +    elif addr_types_env == "ipv6": +        addr_types = ["ipv6"] + +    if addr_type is None: +        return addr_types + +    if addr_type not in addr_types:          logger.error("{} not in supported/configured address types {}". -                     format(addr_type, ADDRESS_TYPES)) +                     format(addr_type, addr_types))          return False -    return ADDRESS_TYPES +    return True  def generate_ips(network, no_of_ips): @@ -548,7 +621,7 @@ def write_test_header(tc_name):      """ Display message at beginning of test case"""      count = 20      logger.info("*"*(len(tc_name)+count)) -    logger.info("START -> Testcase : %s", tc_name) +    logger.info("START -> Testcase : %s" % tc_name)      logger.info("*"*(len(tc_name)+count)) @@ -556,10 +629,169 @@ def write_test_footer(tc_name):      """ Display message at end of test case"""      count = 21      logger.info("="*(len(tc_name)+count)) -    logger.info("PASSED -> Testcase : %s", tc_name) +    logger.info("Testcase : %s -> PASSED", tc_name)      logger.info("="*(len(tc_name)+count)) +def interface_status(tgen, topo, input_dict): +    """ +    Delete ip route maps from device + +    * `tgen`  : Topogen object +    * `topo`  : json file data +    * `input_dict` :  for which router, route map has to be deleted + +    Usage +    ----- +    input_dict = { +        "r3": { +            "interface_list": ['eth1-r1-r2', 'eth2-r1-r3'], +            "status": "down" +        } +    } +    Returns +    ------- +    errormsg(str) or True +    """ +    logger.debug("Entering lib API: interface_status()") + +    try: +        global frr_cfg +        for router in input_dict.keys(): + +            interface_list = input_dict[router]['interface_list'] +            status = input_dict[router].setdefault('status', 'up') +            for intf in interface_list: +                rnode = tgen.routers()[router] +                interface_set_status(rnode, intf, status) + +            # Load config to router +            load_config_to_router(tgen, router) + +    except Exception as e: +        # handle any exception +        logger.error("Error %s occured. Arguments %s.", e.message, e.args) + +        # Traceback +        errormsg = traceback.format_exc() +        logger.error(errormsg) +        return errormsg + +    logger.debug("Exiting lib API: interface_status()") +    return True + + +def retry(attempts=3, wait=2, return_is_str=True, initial_wait=0): +    """ +    Retries function execution, if return is an errormsg or exception + +    * `attempts`: Number of attempts to make +    * `wait`: Number of seconds to wait between each attempt +    * `return_is_str`: Return val is an errormsg in case of failure +    * `initial_wait`: Sleeps for this much seconds before executing function + +    """ + +    def _retry(func): + +        @wraps(func) +        def func_retry(*args, **kwargs): +            _wait = kwargs.pop('wait', wait) +            _attempts = kwargs.pop('attempts', attempts) +            _attempts = int(_attempts) +            if _attempts < 0: +                raise ValueError("attempts must be 0 or greater") + +            if initial_wait > 0: +                logger.info("Waiting for [%s]s as initial delay", initial_wait) +                sleep(initial_wait) + +            _return_is_str = kwargs.pop('return_is_str', return_is_str) +            for i in range(1, _attempts + 1): +                try: +                    _expected = kwargs.setdefault('expected', True) +                    kwargs.pop('expected') +                    ret = func(*args, **kwargs) +                    logger.debug("Function returned %s" % ret) +                    if return_is_str and isinstance(ret, bool): +                        return ret +                    elif return_is_str and _expected is False: +                        return ret + +                    if _attempts == i: +                        return ret +                except Exception as err: +                    if _attempts == i: +                        logger.info("Max number of attempts (%r) reached", +                                    _attempts) +                        raise +                    else: +                        logger.info("Function returned %s", err) +                if i < _attempts: +                    logger.info("Retry [#%r] after sleeping for %ss" +                                % (i, _wait)) +                    sleep(_wait) +        func_retry._original = func +        return func_retry +    return _retry + + +def disable_v6_link_local(tgen, router, intf_name=None): +    """ +    Disables ipv6 link local addresses for a particular interface or +    all interfaces + +    * `tgen`: tgen onject +    * `router` : router for which hightest interface should be +                 calculated +    * `intf_name` : Interface name for which v6 link local needs to +                    be disabled +    """ + +    router_list = tgen.routers() +    for rname, rnode in router_list.iteritems(): +        if rname != router: +            continue + +        linklocal = [] + +        ifaces = router_list[router].run('ip -6 address') + +        # Fix newlines (make them all the same) +        ifaces = ('\n'.join(ifaces.splitlines()) + '\n').splitlines() + +        interface = None +        ll_per_if_count = 0 +        for line in ifaces: +            # Interface name +            m = re.search('[0-9]+: ([^:]+)[@if0-9:]+ <', line) +            if m: +                interface = m.group(1).split("@")[0] +                ll_per_if_count = 0 + +            # Interface ip +            m = re.search('inet6 (fe80::[0-9a-f]+:[0-9a-f]+:[0-9a-f]+' +                          ':[0-9a-f]+[/0-9]*) scope link', line) +            if m: +                local = m.group(1) +                ll_per_if_count += 1 +                if ll_per_if_count > 1: +                    linklocal += [["%s-%s" % (interface, ll_per_if_count), local]] +                else: +                    linklocal += [[interface, local]] + +        if len(linklocal[0]) > 1: +            link_local_dict = {item[0]: item[1] for item in linklocal} + +            for lname, laddr in link_local_dict.items(): + +                if intf_name is not None and lname != intf_name: +                    continue + +                cmd = "ip addr del {} dev {}".format(laddr, lname) +                router_list[router].run(cmd) + +  #############################################  # These APIs,  will used by testcase  ############################################# @@ -589,19 +821,22 @@ def create_interfaces_cfg(tgen, topo, build=False):                      interface_name = destRouterLink                  else:                      interface_name = data["interface"] -                interface_data.append("interface {}\n".format( +                    if "ipv6" in data: +                        disable_v6_link_local(tgen, c_router, interface_name) +                interface_data.append("interface {}".format(                      str(interface_name)                  ))                  if "ipv4" in data:                      intf_addr = c_data["links"][destRouterLink]["ipv4"] -                    interface_data.append("ip address {}\n".format( +                    interface_data.append("ip address {}".format(                          intf_addr                      ))                  if "ipv6" in data:                      intf_addr = c_data["links"][destRouterLink]["ipv6"] -                    interface_data.append("ipv6 address {}\n".format( +                    interface_data.append("ipv6 address {}".format(                          intf_addr                      )) +              result = create_common_configuration(tgen, c_router,                                                   interface_data,                                                   "interface_config", @@ -662,7 +897,7 @@ def create_static_routes(tgen, input_dict, build=False):          for router in input_dict.keys():              if "static_routes" not in input_dict[router]:                  errormsg = "static_routes not present in input_dict" -                logger.info(errormsg) +                logger.debug(errormsg)                  continue              static_routes_list = [] @@ -768,7 +1003,7 @@ def create_prefix_lists(tgen, input_dict, build=False):          for router in input_dict.keys():              if "prefix_lists" not in input_dict[router]:                  errormsg = "prefix_lists not present in input_dict" -                logger.info(errormsg) +                logger.debug(errormsg)                  continue              config_data = [] @@ -922,7 +1157,7 @@ def create_route_maps(tgen, input_dict, build=False):          for router in input_dict.keys():              if "route_maps" not in input_dict[router]:                  errormsg = "route_maps not present in input_dict" -                logger.info(errormsg) +                logger.debug(errormsg)                  continue              rmap_data = []              for rmap_name, rmap_value in \ @@ -1014,7 +1249,7 @@ def create_route_maps(tgen, input_dict, build=False):                          # Weight                          if weight: -                            rmap_data.append("set weight {} \n".format( +                            rmap_data.append("set weight {}".format(                                  weight))                      # Adding MATCH and SET sequence to RMAP if defined @@ -1092,7 +1327,8 @@ def create_route_maps(tgen, input_dict, build=False):  #############################################  # Verification APIs  ############################################# -def _verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None): +@retry(attempts=10, return_is_str=True, initial_wait=2) +def verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None):      """      Data will be read from input_dict or input JSON file, API will generate      same prefixes, which were redistributed by either create_static_routes() or @@ -1140,7 +1376,7 @@ def _verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None):      errormsg(str) or True      """ -    logger.info("Entering lib API: verify_rib()") +    logger.debug("Entering lib API: verify_rib()")      router_list = tgen.routers()      for routerInput in input_dict.keys(): @@ -1160,9 +1396,8 @@ def _verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None):                  else:                      command = "show ipv6 route json" -            sleep(10)              logger.info("Checking router %s RIB:", router) -            rib_routes_json = rnode.vtysh_cmd(command, isjson=True) +            rib_routes_json = run_frr_cmd(rnode, command, isjson=True)              # Verifying output dictionary rib_routes_json is not empty              if bool(rib_routes_json) is False: @@ -1181,7 +1416,7 @@ def _verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None):                      if "no_of_ip" in static_route:                          no_of_ip = static_route["no_of_ip"]                      else: -                        no_of_ip = 0 +                        no_of_ip = 1                      # Generating IPs for verification                      ip_list = generate_ips(network, no_of_ip) @@ -1199,9 +1434,9 @@ def _verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None):                                  found_hops = [rib_r["ip"] for rib_r in                                                rib_routes_json[st_rt][0][                                                    "nexthops"]] -                                for nh in next_hop: +                                for nh in found_hops:                                      nh_found = False -                                    if nh and nh in found_hops: +                                    if nh and nh in next_hop:                                          nh_found = True                                      else:                                          errormsg = ("Nexthop {} is Missing for {}" @@ -1257,30 +1492,10 @@ def _verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None):                  logger.info("Verified routes in router %s RIB, found routes"                              " are: %s", dut, found_routes) -    logger.info("Exiting lib API: verify_rib()") +    logger.debug("Exiting lib API: verify_rib()")      return True -def verify_rib(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None, expected=True): -    """ -    Wrapper function for `_verify_rib` that tries multiple time to get results. - -    When the expected result is `False` we actually should expect for an string instead. -    """ - -    # Use currying to hide the parameters and create a test function. -    test_func = partial(_verify_rib, tgen, addr_type, dut, input_dict, next_hop, protocol) - -    # Call the test function and expect it to return True, otherwise try it again. -    if expected is True: -        _, result = topotest.run_and_expect(test_func, True, count=20, wait=6) -    else: -        _, result = topotest.run_and_expect_type(test_func, str, count=20, wait=6) - -    # Return as normal. -    return result - -  def verify_admin_distance_for_static_routes(tgen, input_dict):      """      API to verify admin distance for static routes as defined in input_dict/ @@ -1311,7 +1526,7 @@ def verify_admin_distance_for_static_routes(tgen, input_dict):      errormsg(str) or True      """ -    logger.info("Entering lib API: verify_admin_distance_for_static_routes()") +    logger.debug("Entering lib API: verify_admin_distance_for_static_routes()")      for router in input_dict.keys():          if router not in tgen.routers(): @@ -1326,7 +1541,7 @@ def verify_admin_distance_for_static_routes(tgen, input_dict):                  command = "show ip route json"              else:                  command = "show ipv6 route json" -            show_ip_route_json = rnode.vtysh_cmd(command, isjson=True) +            show_ip_route_json = run_frr_cmd(rnode, command, isjson=True)              logger.info("Verifying admin distance for static route %s"                          " under dut %s:", static_route, router) @@ -1356,7 +1571,7 @@ def verify_admin_distance_for_static_routes(tgen, input_dict):                              format(network, router))                  return errormsg -    logger.info("Exiting lib API: verify_admin_distance_for_static_routes()") +    logger.debug("Exiting lib API: verify_admin_distance_for_static_routes()")      return True @@ -1384,7 +1599,7 @@ def verify_prefix_lists(tgen, input_dict):      errormsg(str) or True      """ -    logger.info("Entering lib API: verify_prefix_lists()") +    logger.debug("Entering lib API: verify_prefix_lists()")      for router in input_dict.keys():          if router not in tgen.routers(): @@ -1393,7 +1608,7 @@ def verify_prefix_lists(tgen, input_dict):          rnode = tgen.routers()[router]          # Show ip prefix list -        show_prefix_list = rnode.vtysh_cmd("show ip prefix-list") +        show_prefix_list = run_frr_cmd(rnode, "show ip prefix-list")          # Verify Prefix list is deleted          prefix_lists_addr = input_dict[router]["prefix_lists"] @@ -1403,12 +1618,12 @@ def verify_prefix_lists(tgen, input_dict):              for prefix_list in prefix_lists_addr[addr_type].keys():                  if prefix_list in show_prefix_list: -                    errormsg = ("Prefix list {} is not deleted from router" +                    errormsg = ("Prefix list {} is/are present in the router"                                  " {}".format(prefix_list, router))                      return errormsg -                logger.info("Prefix list %s is/are deleted successfully" +                logger.info("Prefix list %s is/are not present in the router"                              " from router %s", prefix_list, router) -    logger.info("Exiting lib API: verify_prefix_lissts()") +    logger.debug("Exiting lib API: verify_prefix_lissts()")      return True diff --git a/tests/topotests/lib/topojson.py b/tests/topotests/lib/topojson.py index 4130451d2e..7a00fe4c50 100644 --- a/tests/topotests/lib/topojson.py +++ b/tests/topotests/lib/topojson.py @@ -20,6 +20,7 @@  from collections import OrderedDict  from json import dumps as json_dumps +from re import search as re_search  import ipaddr  import pytest @@ -38,6 +39,9 @@ from lib.common_config import (  from lib.bgp import create_router_bgp +ROUTER_LIST = [] + +  def build_topo_from_json(tgen, topo):      """      Reads configuration from JSON file. Adds routers, creates interface @@ -48,13 +52,15 @@ def build_topo_from_json(tgen, topo):      * `topo`: json file data      """ -    listRouters = [] -    for routerN in sorted(topo['routers'].iteritems()): -        logger.info('Topo: Add router {}'.format(routerN[0])) -        tgen.add_router(routerN[0]) -        listRouters.append(routerN[0]) +    ROUTER_LIST = sorted(topo['routers'].keys(), +        key=lambda x: int(re_search('\d+', x).group(0))) + +    listRouters = ROUTER_LIST[:] +    for routerN in ROUTER_LIST: +        logger.info('Topo: Add router {}'.format(routerN)) +        tgen.add_router(routerN) +        listRouters.append(routerN) -    listRouters.sort()      if 'ipv4base' in topo:          ipv4Next = ipaddr.IPv4Address(topo['link_ip_start']['ipv4'])          ipv4Step = 2 ** (32 - topo['link_ip_start']['v4mask']) @@ -78,7 +84,7 @@ def build_topo_from_json(tgen, topo):                  elif 'link' in x:                      return int(x.split('-link')[1])                  else: -                    return int(x.split('r')[1]) +                    return int(re_search('\d+', x).group(0))              for destRouterLink, data in sorted(topo['routers'][curRouter]['links']. \                                                 iteritems(),                                                 key=lambda x: link_sort(x[0])): @@ -179,12 +185,13 @@ def build_config_from_json(tgen, topo, save_bkup=True):      data = topo["routers"]      for func_type in func_dict.keys(): -        logger.info('Building configuration for {}'.format(func_type)) +        logger.info('Checking for {} configuration in input data'.format( +            func_type))          func_dict.get(func_type)(tgen, data, build=True)      for router in sorted(topo['routers'].keys()): -        logger.info('Configuring router {}...'.format(router)) +        logger.debug('Configuring router {}...'.format(router))          result = load_config_to_router(tgen, router, save_bkup)          if not result: diff --git a/tests/topotests/pytest.ini b/tests/topotests/pytest.ini index 7ea38491d8..ade5bfd501 100644 --- a/tests/topotests/pytest.ini +++ b/tests/topotests/pytest.ini @@ -1,6 +1,6 @@  # Skip pytests example directory  [pytest] -norecursedirs = .git example-test lib docker +norecursedirs = .git example-test example-topojson-test lib docker  [topogen]  # Default configuration values @@ -15,7 +15,7 @@ norecursedirs = .git example-test lib docker  # Display router current configuration during test execution,  # by default configuration will not be shown -show_router_config = True +# show_router_config = True  # Default daemons binaries path.  #frrdir = /usr/lib/frr diff --git a/tools/checkpatch.pl b/tools/checkpatch.pl index 1b48d10a09..c0624d933e 100755 --- a/tools/checkpatch.pl +++ b/tools/checkpatch.pl @@ -3351,7 +3351,7 @@ sub process {  # if/while/etc brace do not go on next line, unless defining a do while loop,  # or if that brace on the next line is for something else -		if ($line =~ /(.*)\b((?:if|while|for|switch|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) { +		if ($line =~ /(.*)\b((?:if|while|for|switch|(?:[a-z_]+|)frr_(each|with)[a-z_]+)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) {  			my $pre_ctx = "$1$2";  			my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0); @@ -3397,7 +3397,7 @@ sub process {  		}  # Check relative indent for conditionals and blocks. -		if ($line =~ /\b(?:(?:if|while|for|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|(?:do|else)\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) { +		if ($line =~ /\b(?:(?:if|while|for|(?:[a-z_]+|)frr_(each|with)[a-z_]+)\s*\(|(?:do|else)\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) {  			($stat, $cond, $line_nr_next, $remain_next, $off_next) =  				ctx_statement_block($linenr, $realcnt, 0)  					if (!defined $stat); @@ -5177,6 +5177,31 @@ sub process {  			}  		} +		if (!defined $suppress_ifbraces{$linenr - 1} && +					$line =~ /\b(frr_with_)/) { +			my ($level, $endln, @chunks) = +				ctx_statement_full($linenr, $realcnt, $-[0]); + +			# Check the condition. +			my ($cond, $block) = @{$chunks[0]}; +			#print "CHECKING<$linenr> cond<$cond> block<$block>\n"; +			if (defined $cond) { +				substr($block, 0, length($cond), ''); +			} + +			if ($level == 0 && $block !~ /^\s*\{/) { +				my $herectx = $here . "\n"; +				my $cnt = statement_rawlines($block); + +				for (my $n = 0; $n < $cnt; $n++) { +					$herectx .= raw_line($linenr, $n) . "\n"; +				} + +				WARN("BRACES", +				     "braces {} are mandatory for frr_with_* blocks\n" . $herectx); +			} +		} +  # check for single line unbalanced braces  		if ($sline =~ /^.\s*\}\s*else\s*$/ ||  		    $sline =~ /^.\s*else\s*\{\s*$/) { diff --git a/tools/coccinelle/frr_with_mutex.cocci b/tools/coccinelle/frr_with_mutex.cocci new file mode 100644 index 0000000000..ec8b73917c --- /dev/null +++ b/tools/coccinelle/frr_with_mutex.cocci @@ -0,0 +1,23 @@ +@@ +expression E; +iterator name frr_with_mutex; +@@ + +- pthread_mutex_lock(E); ++ frr_with_mutex(E) { +- { +    ... +- } +- pthread_mutex_unlock(E); ++ } + + +@@ +expression E; +@@ + +- pthread_mutex_lock(E); ++ frr_with_mutex(E) { +  ... +- pthread_mutex_unlock(E); ++ } diff --git a/tools/coccinelle/zprivs.cocci b/tools/coccinelle/zprivs.cocci index 76d13c3f0d..11628a7eae 100644 --- a/tools/coccinelle/zprivs.cocci +++ b/tools/coccinelle/zprivs.cocci @@ -2,12 +2,12 @@  identifier change;  identifier end;  expression E, f, g; -iterator name frr_elevate_privs; +iterator name frr_with_privs;  @@  - if (E.change(ZPRIVS_RAISE))  -   f; -+ frr_elevate_privs(&E) { ++ frr_with_privs(&E) {    <+...  -   goto end;  +   break; @@ -20,7 +20,7 @@ iterator name frr_elevate_privs;  @@  identifier change, errno, safe_strerror, exit;  expression E, f1, f2, f3, ret, fn; -iterator name frr_elevate_privs; +iterator name frr_with_privs;  @@    if (E.change(ZPRIVS_RAISE)) @@ -44,7 +44,7 @@ iterator name frr_elevate_privs;  @@  identifier change;  expression E, f1, f2, f3, ret; -iterator name frr_elevate_privs; +iterator name frr_with_privs;  @@    if (E.change(ZPRIVS_RAISE)) @@ -64,12 +64,12 @@ iterator name frr_elevate_privs;  @@  identifier change;  expression E, f, g; -iterator name frr_elevate_privs; +iterator name frr_with_privs;  @@  - if (E.change(ZPRIVS_RAISE))  -   f; -+ frr_elevate_privs(&E) { ++ frr_with_privs(&E) {    ...  - if (E.change(ZPRIVS_LOWER))  -   g; diff --git a/vrrpd/vrrp.c b/vrrpd/vrrp.c index 951ad3f58f..b4049b55eb 100644 --- a/vrrpd/vrrp.c +++ b/vrrpd/vrrp.c @@ -1065,8 +1065,7 @@ static int vrrp_socket(struct vrrp_router *r)  	int ret;  	bool failed = false; -	frr_elevate_privs(&vrrp_privs) -	{ +	frr_with_privs(&vrrp_privs) {  		r->sock_rx = socket(r->family, SOCK_RAW, IPPROTO_VRRP);  		r->sock_tx = socket(r->family, SOCK_RAW, IPPROTO_VRRP);  	} @@ -1102,8 +1101,7 @@ static int vrrp_socket(struct vrrp_router *r)  		setsockopt_ipv4_multicast_loop(r->sock_tx, 0);  		/* Bind Rx socket to exact interface */ -		frr_elevate_privs(&vrrp_privs) -		{ +		frr_with_privs(&vrrp_privs) {  			ret = setsockopt(r->sock_rx, SOL_SOCKET,  					 SO_BINDTODEVICE, r->vr->ifp->name,  					 strlen(r->vr->ifp->name)); @@ -1213,8 +1211,7 @@ static int vrrp_socket(struct vrrp_router *r)  		setsockopt_ipv6_multicast_loop(r->sock_tx, 0);  		/* Bind Rx socket to exact interface */ -		frr_elevate_privs(&vrrp_privs) -		{ +		frr_with_privs(&vrrp_privs) {  			ret = setsockopt(r->sock_rx, SOL_SOCKET,  					 SO_BINDTODEVICE, r->vr->ifp->name,  					 strlen(r->vr->ifp->name)); diff --git a/vrrpd/vrrp_arp.c b/vrrpd/vrrp_arp.c index 78e153a082..750050e8c3 100644 --- a/vrrpd/vrrp_arp.c +++ b/vrrpd/vrrp_arp.c @@ -188,7 +188,7 @@ void vrrp_garp_init(void)  	/* Create the socket descriptor */  	/* FIXME: why ETH_P_RARP? */  	errno = 0; -	frr_elevate_privs(&vrrp_privs) { +	frr_with_privs(&vrrp_privs) {  		garp_fd = socket(PF_PACKET, SOCK_RAW | SOCK_CLOEXEC,  				 htons(ETH_P_RARP));  	} diff --git a/vrrpd/vrrp_ndisc.c b/vrrpd/vrrp_ndisc.c index 348958509a..dc546b09a2 100644 --- a/vrrpd/vrrp_ndisc.c +++ b/vrrpd/vrrp_ndisc.c @@ -214,8 +214,7 @@ int vrrp_ndisc_una_send_all(struct vrrp_router *r)  void vrrp_ndisc_init(void)  { -	frr_elevate_privs(&vrrp_privs) -	{ +	frr_with_privs(&vrrp_privs) {  		ndisc_fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_IPV6));  	} diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index b053392bff..a762e9555c 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -1260,6 +1260,8 @@ static struct cmd_node bgp_vrf_policy_node = {BGP_VRF_POLICY_NODE,  static struct cmd_node bgp_vnc_l2_group_node = {  	BGP_VNC_L2_GROUP_NODE, "%s(config-router-vnc-l2-group)# "}; +static struct cmd_node bmp_node = {BMP_NODE, "%s(config-bgp-bmp)# "}; +  static struct cmd_node ospf_node = {OSPF_NODE, "%s(config-router)# "};  static struct cmd_node eigrp_node = {EIGRP_NODE, "%s(config-router)# "}; @@ -1335,7 +1337,7 @@ DEFUNSH(VTYSH_REALLYALL, vtysh_end_all, vtysh_end_all_cmd, "end",  }  DEFUNSH(VTYSH_BGPD, router_bgp, router_bgp_cmd, -	"router bgp [(1-4294967295) [<view|vrf> WORD]]", +	"router bgp [(1-4294967295)$instasn [<view|vrf> WORD]]",  	ROUTER_STR BGP_STR AS_STR  	"BGP view\nBGP VRF\n"  	"View/VRF name\n") @@ -1478,6 +1480,18 @@ DEFUNSH(VTYSH_BGPD,  	return CMD_SUCCESS;  } +DEFUNSH(VTYSH_BGPD, +	bmp_targets, +	bmp_targets_cmd, +	"bmp targets BMPTARGETS", +	"BGP Monitoring Protocol\n" +	"Create BMP target group\n" +	"Name of the BMP target group\n") +{ +	vty->node = BMP_NODE; +	return CMD_SUCCESS; +} +  DEFUNSH(VTYSH_BGPD, address_family_evpn, address_family_evpn_cmd,  	"address-family <l2vpn evpn>",  	"Enter Address Family command mode\n" @@ -1585,10 +1599,11 @@ DEFUNSH(VTYSH_OSPFD, router_ospf, router_ospf_cmd,  	return CMD_SUCCESS;  } -DEFUNSH(VTYSH_EIGRPD, router_eigrp, router_eigrp_cmd, "router eigrp (1-65535)", +DEFUNSH(VTYSH_EIGRPD, router_eigrp, router_eigrp_cmd, "router eigrp (1-65535) [vrf NAME]",  	"Enable a routing process\n"  	"Start EIGRP configuration\n" -	"AS number to use\n") +	"AS number to use\n" +	VRF_CMD_HELP_STR)  {  	vty->node = EIGRP_NODE;  	return CMD_SUCCESS; @@ -1842,6 +1857,7 @@ static int vtysh_exit(struct vty *vty)  	case BGP_VNC_DEFAULTS_NODE:  	case BGP_VNC_NVE_GROUP_NODE:  	case BGP_VNC_L2_GROUP_NODE: +	case BMP_NODE:  		vty->node = BGP_NODE;  		break;  	case BGP_EVPN_VNI_NODE: @@ -1932,6 +1948,19 @@ DEFUNSH(VTYSH_BGPD, rpki_quit, rpki_quit_cmd, "quit",  	return rpki_exit(self, vty, argc, argv);  } +DEFUNSH(VTYSH_BGPD, bmp_exit, bmp_exit_cmd, "exit", +	"Exit current mode and down to previous mode\n") +{ +	vtysh_exit(vty); +	return CMD_SUCCESS; +} + +DEFUNSH(VTYSH_BGPD, bmp_quit, bmp_quit_cmd, "quit", +	"Exit current mode and down to previous mode\n") +{ +	return bmp_exit(self, vty, argc, argv); +} +  DEFUNSH(VTYSH_VRF, exit_vrf_config, exit_vrf_config_cmd, "exit-vrf",  	"Exit from VRF configuration mode\n")  { @@ -3259,15 +3288,66 @@ DEFUN (no_vtysh_output_file,  DEFUN(find,        find_cmd, -      "find COMMAND...", -      "Find CLI command containing text\n" -      "Text to search for\n") +      "find REGEX", +      "Find CLI command matching a regular expression\n" +      "Search pattern (POSIX regex)\n")  { -	char *text = argv_concat(argv, argc, 1); +	char *pattern = argv[1]->arg;  	const struct cmd_node *node;  	const struct cmd_element *cli;  	vector clis; +	regex_t exp = {}; + +	int cr = regcomp(&exp, pattern, REG_NOSUB | REG_EXTENDED); + +	if (cr != 0) { +		switch (cr) { +		case REG_BADBR: +			vty_out(vty, "%% Invalid \\{...\\} expression\n"); +			break; +		case REG_BADRPT: +			vty_out(vty, "%% Bad repetition operator\n"); +			break; +		case REG_BADPAT: +			vty_out(vty, "%% Regex syntax error\n"); +			break; +		case REG_ECOLLATE: +			vty_out(vty, "%% Invalid collating element\n"); +			break; +		case REG_ECTYPE: +			vty_out(vty, "%% Invalid character class name\n"); +			break; +		case REG_EESCAPE: +			vty_out(vty, +				"%% Regex ended with escape character (\\)\n"); +			break; +		case REG_ESUBREG: +			vty_out(vty, +				"%% Invalid number in \\digit construction\n"); +			break; +		case REG_EBRACK: +			vty_out(vty, "%% Unbalanced square brackets\n"); +			break; +		case REG_EPAREN: +			vty_out(vty, "%% Unbalanced parentheses\n"); +			break; +		case REG_EBRACE: +			vty_out(vty, "%% Unbalanced braces\n"); +			break; +		case REG_ERANGE: +			vty_out(vty, +				"%% Invalid endpoint in range expression\n"); +			break; +		case REG_ESPACE: +			vty_out(vty, "%% Failed to compile (out of memory)\n"); +			break; +		} + +		goto done; +	} + +  	for (unsigned int i = 0; i < vector_active(cmdvec); i++) {  		node = vector_slot(cmdvec, i);  		if (!node) @@ -3275,14 +3355,15 @@ DEFUN(find,  		clis = node->cmd_vector;  		for (unsigned int j = 0; j < vector_active(clis); j++) {  			cli = vector_slot(clis, j); -			if (strcasestr(cli->string, text)) + +			if (regexec(&exp, cli->string, 0, NULL, 0) == 0)  				vty_out(vty, "  (%s)  %s\n",  					node_names[node->node], cli->string);  		}  	} -	XFREE(MTYPE_TMP, text); - +done: +	regfree(&exp);  	return CMD_SUCCESS;  } @@ -3620,6 +3701,7 @@ void vtysh_init_vty(void)  	install_node(&openfabric_node, NULL);  	install_node(&vty_node, NULL);  	install_node(&rpki_node, NULL); +	install_node(&bmp_node, NULL);  #if HAVE_BFDD > 0  	install_node(&bfd_node, NULL);  	install_node(&bfd_peer_node, NULL); @@ -3853,6 +3935,11 @@ void vtysh_init_vty(void)  	install_element(BGP_FLOWSPECV4_NODE, &exit_address_family_cmd);  	install_element(BGP_FLOWSPECV6_NODE, &exit_address_family_cmd); +	install_element(BGP_NODE, &bmp_targets_cmd); +	install_element(BMP_NODE, &bmp_exit_cmd); +	install_element(BMP_NODE, &bmp_quit_cmd); +	install_element(BMP_NODE, &vtysh_end_all_cmd); +  	install_element(CONFIG_NODE, &rpki_cmd);  	install_element(RPKI_NODE, &rpki_exit_cmd);  	install_element(RPKI_NODE, &rpki_quit_cmd); diff --git a/zebra/connected.c b/zebra/connected.c index ffc991861c..6b92945c63 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -235,7 +235,7 @@ void connected_up(struct interface *ifp, struct connected *ifc)  			return;  		break;  	case AFI_IP6: -#ifndef LINUX +#ifndef GNU_LINUX  		/* XXX: It is already done by rib_bogus_ipv6 within rib_add */  		if (IN6_IS_ADDR_UNSPECIFIED(&p.u.prefix6))  			return; diff --git a/zebra/if_ioctl_solaris.c b/zebra/if_ioctl_solaris.c index 8b539a9049..2a2504ebf8 100644 --- a/zebra/if_ioctl_solaris.c +++ b/zebra/if_ioctl_solaris.c @@ -60,7 +60,7 @@ static int interface_list_ioctl(int af)  	size_t needed, lastneeded = 0;  	char *buf = NULL; -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		sock = socket(af, SOCK_DGRAM, 0);  	} @@ -72,7 +72,7 @@ static int interface_list_ioctl(int af)  	}  calculate_lifc_len: -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		lifn.lifn_family = af;  		lifn.lifn_flags = LIFC_NOXMIT;  		/* we want NOXMIT interfaces too */ @@ -107,7 +107,7 @@ calculate_lifc_len:  	lifconf.lifc_len = needed;  	lifconf.lifc_buf = buf; -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		ret = ioctl(sock, SIOCGLIFCONF, &lifconf);  	} diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index e157c2d70a..c71b95f753 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -385,7 +385,7 @@ static int get_iflink_speed(struct interface *interface)  	ifdata.ifr_data = (caddr_t)&ecmd;  	/* use ioctl to get IP address of an interface */ -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		sd = vrf_socket(PF_INET, SOCK_DGRAM, IPPROTO_IP,  				interface->vrf_id,  				NULL); diff --git a/zebra/ioctl.c b/zebra/ioctl.c index 8202e076af..b461a08881 100644 --- a/zebra/ioctl.c +++ b/zebra/ioctl.c @@ -57,7 +57,7 @@ int if_ioctl(unsigned long request, caddr_t buffer)  	int ret;  	int err = 0; -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		sock = socket(AF_INET, SOCK_DGRAM, 0);  		if (sock < 0) {  			zlog_err("Cannot create UDP socket: %s", @@ -83,7 +83,7 @@ int vrf_if_ioctl(unsigned long request, caddr_t buffer, vrf_id_t vrf_id)  	int ret;  	int err = 0; -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		sock = vrf_socket(AF_INET, SOCK_DGRAM, 0, vrf_id, NULL);  		if (sock < 0) {  			zlog_err("Cannot create UDP socket: %s", @@ -110,7 +110,7 @@ static int if_ioctl_ipv6(unsigned long request, caddr_t buffer)  	int ret;  	int err = 0; -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		sock = socket(AF_INET6, SOCK_DGRAM, 0);  		if (sock < 0) {  			zlog_err("Cannot create IPv6 datagram socket: %s", diff --git a/zebra/ioctl_solaris.c b/zebra/ioctl_solaris.c index 1f96fa23ea..2c71d949f7 100644 --- a/zebra/ioctl_solaris.c +++ b/zebra/ioctl_solaris.c @@ -66,7 +66,7 @@ int if_ioctl(unsigned long request, caddr_t buffer)  	int ret;  	int err; -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		sock = socket(AF_INET, SOCK_DGRAM, 0);  		if (sock < 0) { @@ -96,7 +96,7 @@ int if_ioctl_ipv6(unsigned long request, caddr_t buffer)  	int ret;  	int err; -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		sock = socket(AF_INET6, SOCK_DGRAM, 0);  		if (sock < 0) { diff --git a/zebra/ipforward_proc.c b/zebra/ipforward_proc.c index 8f44c377b3..709d2176aa 100644 --- a/zebra/ipforward_proc.c +++ b/zebra/ipforward_proc.c @@ -76,7 +76,7 @@ int ipforward_on(void)  {  	FILE *fp; -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		fp = fopen(proc_ipv4_forwarding, "w"); @@ -97,7 +97,7 @@ int ipforward_off(void)  {  	FILE *fp; -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		fp = fopen(proc_ipv4_forwarding, "w"); @@ -143,7 +143,7 @@ int ipforward_ipv6_on(void)  {  	FILE *fp; -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		fp = fopen(proc_ipv6_forwarding, "w"); @@ -165,7 +165,7 @@ int ipforward_ipv6_off(void)  {  	FILE *fp; -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		fp = fopen(proc_ipv6_forwarding, "w"); diff --git a/zebra/ipforward_solaris.c b/zebra/ipforward_solaris.c index 1bb743059c..1a45328248 100644 --- a/zebra/ipforward_solaris.c +++ b/zebra/ipforward_solaris.c @@ -83,7 +83,7 @@ static int solaris_nd(const int cmd, const char *parameter, const int value)  	strioctl.ic_len = ND_BUFFER_SIZE;  	strioctl.ic_dp = nd_buf; -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		if ((fd = open(device, O_RDWR)) < 0) {  			flog_err_sys(EC_LIB_SYSTEM_CALL,  				     "failed to open device %s - %s", device, diff --git a/zebra/ipforward_sysctl.c b/zebra/ipforward_sysctl.c index cc9421c275..ac8f537075 100644 --- a/zebra/ipforward_sysctl.c +++ b/zebra/ipforward_sysctl.c @@ -56,7 +56,7 @@ int ipforward_on(void)  	int ipforwarding = 1;  	len = sizeof ipforwarding; -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		if (sysctl(mib, MIB_SIZ, NULL, NULL, &ipforwarding, len) < 0) {  			flog_err_sys(EC_LIB_SYSTEM_CALL,  				     "Can't set ipforwarding on"); @@ -72,7 +72,7 @@ int ipforward_off(void)  	int ipforwarding = 0;  	len = sizeof ipforwarding; -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		if (sysctl(mib, MIB_SIZ, NULL, NULL, &ipforwarding, len) < 0) {  			flog_err_sys(EC_LIB_SYSTEM_CALL,  				     "Can't set ipforwarding on"); @@ -97,7 +97,7 @@ int ipforward_ipv6(void)  	int ip6forwarding = 0;  	len = sizeof ip6forwarding; -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		if (sysctl(mib_ipv6, MIB_SIZ, &ip6forwarding, &len, 0, 0) < 0) {  			flog_err_sys(EC_LIB_SYSTEM_CALL,  				     "can't get ip6forwarding value"); @@ -113,7 +113,7 @@ int ipforward_ipv6_on(void)  	int ip6forwarding = 1;  	len = sizeof ip6forwarding; -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		if (sysctl(mib_ipv6, MIB_SIZ, NULL, NULL, &ip6forwarding, len)  		    < 0) {  			flog_err_sys(EC_LIB_SYSTEM_CALL, @@ -130,7 +130,7 @@ int ipforward_ipv6_off(void)  	int ip6forwarding = 0;  	len = sizeof ip6forwarding; -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		if (sysctl(mib_ipv6, MIB_SIZ, NULL, NULL, &ip6forwarding, len)  		    < 0) {  			flog_err_sys(EC_LIB_SYSTEM_CALL, diff --git a/zebra/irdp_main.c b/zebra/irdp_main.c index 38d241eaa5..0de618625d 100644 --- a/zebra/irdp_main.c +++ b/zebra/irdp_main.c @@ -82,7 +82,7 @@ int irdp_sock_init(void)  	int save_errno;  	int sock; -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);  		save_errno = errno; diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 2c306434a3..f52b4746ae 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -183,7 +183,7 @@ static int netlink_recvbuf(struct nlsock *nl, uint32_t newsize)  	}  	/* Try force option (linux >= 2.6.14) and fall back to normal set */ -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		ret = setsockopt(nl->sock, SOL_SOCKET, SO_RCVBUFFORCE,  				 &nl_rcvbufsize,  				 sizeof(nl_rcvbufsize)); @@ -220,7 +220,7 @@ static int netlink_socket(struct nlsock *nl, unsigned long groups,  	int sock;  	int namelen; -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		sock = ns_socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE, ns_id);  		if (sock < 0) {  			zlog_err("Can't open %s socket: %s", nl->name, @@ -352,7 +352,7 @@ static void netlink_write_incoming(const char *buf, const unsigned int size,  	FILE *f;  	snprintf(fname, MAXPATHLEN, "%s/%s_%u", frr_vtydir, "netlink", counter); -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		f = fopen(fname, "w");  	}  	if (f) { @@ -373,7 +373,7 @@ static long netlink_read_file(char *buf, const char *fname)  	FILE *f;  	long file_bytes = -1; -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		f = fopen(fname, "r");  	}  	if (f) { @@ -989,7 +989,7 @@ int netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),  			n->nlmsg_flags);  	/* Send message to netlink interface. */ -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		status = sendmsg(nl->sock, &msg, 0);  		save_errno = errno;  	} @@ -1056,7 +1056,7 @@ int netlink_request(struct nlsock *nl, struct nlmsghdr *n)  	snl.nl_family = AF_NETLINK;  	/* Raise capabilities and send message, then lower capabilities. */ -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		ret = sendto(nl->sock, (void *)n, n->nlmsg_len, 0,  			     (struct sockaddr *)&snl, sizeof snl);  	} diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 156ce50725..60fbbcc059 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -1426,7 +1426,7 @@ static int kernel_read(struct thread *thread)  /* Make routing socket. */  static void routing_socket(struct zebra_ns *zns)  { -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		routing_sock = ns_socket(AF_ROUTE, SOCK_RAW, 0, zns->ns_id);  		dplane_routing_sock = diff --git a/zebra/rt.h b/zebra/rt.h index 727d2d0c55..59b42fed18 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -57,6 +57,8 @@ enum zebra_dplane_result kernel_address_update_ctx(  enum zebra_dplane_result kernel_mac_update_ctx(struct zebra_dplane_ctx *ctx); +enum zebra_dplane_result kernel_neigh_update_ctx(struct zebra_dplane_ctx *ctx); +  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, @@ -66,15 +68,6 @@ extern int mpls_kernel_init(void);  extern uint32_t kernel_get_speed(struct interface *ifp);  extern int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *mroute); -extern int kernel_add_vtep(vni_t vni, struct interface *ifp, -			   struct in_addr *vtep_ip); -extern int kernel_del_vtep(vni_t vni, struct interface *ifp, -			   struct in_addr *vtep_ip); -extern int kernel_add_neigh(struct interface *ifp, struct ipaddr *ip, -			    struct ethaddr *mac, uint8_t flags); -extern int kernel_del_neigh(struct interface *ifp, struct ipaddr *ip); -extern int kernel_upd_neigh(struct interface *ifp, struct ipaddr *ip, -                           struct ethaddr *mac, uint8_t flags, uint16_t state);  /*   * Southbound Initialization routines to get initial starting diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 3655763164..5edcf9bb8a 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -92,6 +92,41 @@ void rt_netlink_init(void)  	inet_pton(AF_INET, ipv4_ll_buf, &ipv4_ll);  } +/* + * Mapping from dataplane neighbor flags to netlink flags + */ +static uint8_t neigh_flags_to_netlink(uint8_t dplane_flags) +{ +	uint8_t flags = 0; + +	if (dplane_flags & DPLANE_NTF_EXT_LEARNED) +		flags |= NTF_EXT_LEARNED; +	if (dplane_flags & DPLANE_NTF_ROUTER) +		flags |= NTF_ROUTER; + +	return flags; +} + +/* + * Mapping from dataplane neighbor state to netlink state + */ +static uint16_t neigh_state_to_netlink(uint16_t dplane_state) +{ +	uint16_t state = 0; + +	if (dplane_state & DPLANE_NUD_REACHABLE) +		state |= NUD_REACHABLE; +	if (dplane_state & DPLANE_NUD_STALE) +		state |= NUD_STALE; +	if (dplane_state & DPLANE_NUD_NOARP) +		state |= NUD_NOARP; +	if (dplane_state & DPLANE_NUD_PROBE) +		state |= NUD_PROBE; + +	return state; +} + +  static inline int is_selfroute(int proto)  {  	if ((proto == RTPROT_BGP) || (proto == RTPROT_OSPF) @@ -1019,33 +1054,28 @@ static void _netlink_route_build_singlepath(const char *routedesc, int bytelen,  	label_buf[0] = '\0';  	assert(nexthop); -	for (const struct nexthop *nh = nexthop; nh; nh = nh->rparent) { -		char label_buf1[20]; +	char label_buf1[20]; -		nh_label = nh->nh_label; -		if (!nh_label || !nh_label->num_labels) -			continue; +	nh_label = nexthop->nh_label; -		for (int i = 0; i < nh_label->num_labels; i++) { -			if (nh_label->label[i] == MPLS_LABEL_IMPLICIT_NULL) -				continue; +	for (int i = 0; nh_label && 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)); -				} +		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));  			} - -			out_lse[num_labels] = -				mpls_lse_encode(nh_label->label[i], 0, 0, 0); -			num_labels++;  		} + +		out_lse[num_labels] = +			mpls_lse_encode(nh_label->label[i], 0, 0, 0); +		num_labels++;  	}  	if (num_labels) { @@ -1210,33 +1240,28 @@ static void _netlink_route_build_multipath(const char *routedesc, int bytelen,  	label_buf[0] = '\0';  	assert(nexthop); -	for (const struct nexthop *nh = nexthop; nh; nh = nh->rparent) { -		char label_buf1[20]; +	char label_buf1[20]; -		nh_label = nh->nh_label; -		if (!nh_label || !nh_label->num_labels) -			continue; +	nh_label = nexthop->nh_label; -		for (int i = 0; i < nh_label->num_labels; i++) { -			if (nh_label->label[i] == MPLS_LABEL_IMPLICIT_NULL) -				continue; +	for (int i = 0; nh_label && 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)); -				} +		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));  			} - -			out_lse[num_labels] = -				mpls_lse_encode(nh_label->label[i], 0, 0, 0); -			num_labels++;  		} + +		out_lse[num_labels] = +			mpls_lse_encode(nh_label->label[i], 0, 0, 0); +		num_labels++;  	}  	if (num_labels) { @@ -1902,19 +1927,17 @@ int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla,   * Add remote VTEP to the flood list for this VxLAN interface (VNI). This   * is done by adding an FDB entry with a MAC of 00:00:00:00:00:00.   */ -static int netlink_vxlan_flood_list_update(struct interface *ifp, -					   struct in_addr *vtep_ip, int cmd) +static int netlink_vxlan_flood_update_ctx(const struct zebra_dplane_ctx *ctx, +					  int cmd)  { -	struct zebra_ns *zns;  	struct {  		struct nlmsghdr n;  		struct ndmsg ndm;  		char buf[256];  	} req;  	uint8_t dst_mac[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; -	struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); +	const struct ipaddr *addr; -	zns = zvrf->zns;  	memset(&req, 0, sizeof(req));  	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg)); @@ -1928,39 +1951,14 @@ static int netlink_vxlan_flood_list_update(struct interface *ifp,  	addattr_l(&req.n, sizeof(req), NDA_LLADDR, &dst_mac, 6); -	req.ndm.ndm_ifindex = ifp->ifindex; -	addattr_l(&req.n, sizeof(req), NDA_DST, &vtep_ip->s_addr, 4); +	req.ndm.ndm_ifindex = dplane_ctx_get_ifindex(ctx); -	return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, -			    0); -} +	addr = dplane_ctx_neigh_get_ipaddr(ctx); -/* - * Add remote VTEP for this VxLAN interface (VNI). In Linux, this involves - * adding - * a "flood" MAC FDB entry. - */ -int kernel_add_vtep(vni_t vni, struct interface *ifp, struct in_addr *vtep_ip) -{ -	if (IS_ZEBRA_DEBUG_VXLAN) -		zlog_debug("Install %s into flood list for VNI %u intf %s(%u)", -			   inet_ntoa(*vtep_ip), vni, ifp->name, ifp->ifindex); +	addattr_l(&req.n, sizeof(req), NDA_DST, &(addr->ipaddr_v4), 4); -	return netlink_vxlan_flood_list_update(ifp, vtep_ip, RTM_NEWNEIGH); -} - -/* - * Remove remote VTEP for this VxLAN interface (VNI). In Linux, this involves - * deleting the "flood" MAC FDB entry. - */ -int kernel_del_vtep(vni_t vni, struct interface *ifp, struct in_addr *vtep_ip) -{ -	if (IS_ZEBRA_DEBUG_VXLAN) -		zlog_debug( -			"Uninstall %s from flood list for VNI %u intf %s(%u)", -			inet_ntoa(*vtep_ip), vni, ifp->name, ifp->ifindex); - -	return netlink_vxlan_flood_list_update(ifp, vtep_ip, RTM_DELNEIGH); +	return netlink_talk_info(netlink_talk_filter, &req.n, +				 dplane_ctx_get_ns(ctx), 0);  }  #ifndef NDA_RTA @@ -2778,9 +2776,11 @@ int netlink_neigh_change(struct nlmsghdr *h, ns_id_t ns_id)  	return 0;  } -static int netlink_neigh_update2(struct interface *ifp, struct ipaddr *ip, -				 struct ethaddr *mac, uint8_t flags, -				 uint16_t state, int cmd) +/* + * Utility neighbor-update function, using info from dplane context. + */ +static int netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx, +				    int cmd)  {  	struct {  		struct nlmsghdr n; @@ -2788,15 +2788,23 @@ static int netlink_neigh_update2(struct interface *ifp, struct ipaddr *ip,  		char buf[256];  	} req;  	int ipa_len; - -	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); +	const struct ipaddr *ip; +	const struct ethaddr *mac; +	uint8_t flags; +	uint16_t state; -	zns = zvrf->zns;  	memset(&req, 0, sizeof(req)); +	ip = dplane_ctx_neigh_get_ipaddr(ctx); +	mac = dplane_ctx_neigh_get_mac(ctx); +	if (is_zero_mac(mac)) +		mac = NULL; + +	flags = neigh_flags_to_netlink(dplane_ctx_neigh_get_flags(ctx)); +	state = neigh_state_to_netlink(dplane_ctx_neigh_get_state(ctx)); +  	req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));  	req.n.nlmsg_flags = NLM_F_REQUEST;  	if (cmd == RTM_NEWNEIGH) @@ -2804,7 +2812,7 @@ static int netlink_neigh_update2(struct interface *ifp, struct ipaddr *ip,  	req.n.nlmsg_type = cmd; // RTM_NEWNEIGH or RTM_DELNEIGH  	req.ndm.ndm_family = IS_IPADDR_V4(ip) ? AF_INET : AF_INET6;  	req.ndm.ndm_state = state; -	req.ndm.ndm_ifindex = ifp->ifindex; +	req.ndm.ndm_ifindex = dplane_ctx_get_ifindex(ctx);  	req.ndm.ndm_type = RTN_UNICAST;  	req.ndm.ndm_flags = flags; @@ -2816,13 +2824,16 @@ static int netlink_neigh_update2(struct interface *ifp, struct ipaddr *ip,  	if (IS_ZEBRA_DEBUG_KERNEL)  		zlog_debug("Tx %s family %s IF %s(%u) Neigh %s MAC %s flags 0x%x state 0x%x",  			   nl_msg_type_to_str(cmd), -			   nl_family_to_str(req.ndm.ndm_family), ifp->name, -			   ifp->ifindex, ipaddr2str(ip, buf, sizeof(buf)), +			   nl_family_to_str(req.ndm.ndm_family), +			   dplane_ctx_get_ifname(ctx), +			   dplane_ctx_get_ifindex(ctx), +			   ipaddr2str(ip, buf, sizeof(buf)),  			   mac ? prefix_mac2str(mac, buf2, sizeof(buf2)) -			       : "null", flags, state); +			       : "null", +			   flags, state); -	return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, -			    0); +	return netlink_talk_info(netlink_talk_filter, &req.n, +				 dplane_ctx_get_ns(ctx), 0);  }  /* @@ -2833,23 +2844,30 @@ enum zebra_dplane_result kernel_mac_update_ctx(struct zebra_dplane_ctx *ctx)  	return netlink_macfdb_update_ctx(ctx);  } -int kernel_add_neigh(struct interface *ifp, struct ipaddr *ip, -		     struct ethaddr *mac, uint8_t flags) +enum zebra_dplane_result kernel_neigh_update_ctx(struct zebra_dplane_ctx *ctx)  { -	return netlink_neigh_update2(ifp, ip, mac, flags, -				     NUD_NOARP, RTM_NEWNEIGH); -} +	int ret = -1; -int kernel_del_neigh(struct interface *ifp, struct ipaddr *ip) -{ -	return netlink_neigh_update2(ifp, ip, NULL, 0, 0, RTM_DELNEIGH); -} +	switch (dplane_ctx_get_op(ctx)) { +	case DPLANE_OP_NEIGH_INSTALL: +	case DPLANE_OP_NEIGH_UPDATE: +		ret = netlink_neigh_update_ctx(ctx, RTM_NEWNEIGH); +		break; +	case DPLANE_OP_NEIGH_DELETE: +		ret = netlink_neigh_update_ctx(ctx, RTM_DELNEIGH); +		break; +	case DPLANE_OP_VTEP_ADD: +		ret = netlink_vxlan_flood_update_ctx(ctx, RTM_NEWNEIGH); +		break; +	case DPLANE_OP_VTEP_DELETE: +		ret = netlink_vxlan_flood_update_ctx(ctx, RTM_DELNEIGH); +		break; +	default: +		break; +	} -int kernel_upd_neigh(struct interface *ifp, struct ipaddr *ip, -		     struct ethaddr *mac, uint8_t flags, uint16_t state) -{ -	return netlink_neigh_update2(ifp, ip, mac, flags, -				     state, RTM_NEWNEIGH); +	return (ret == 0 ? +		ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE);  }  /* diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 7e9a42a617..dc0f29bdbc 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -314,7 +314,7 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx)  	type = dplane_ctx_get_type(ctx);  	old_type = dplane_ctx_get_old_type(ctx); -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE) {  			if (!RSYSTEM_ROUTE(type)) @@ -371,17 +371,13 @@ int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla,  	return 0;  } -extern int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *mroute) -{ -	return 0; -} - -int kernel_add_vtep(vni_t vni, struct interface *ifp, struct in_addr *vtep_ip) +/* NYI on routing-socket platforms, but we've always returned 'success'... */ +enum zebra_dplane_result kernel_neigh_update_ctx(struct zebra_dplane_ctx *ctx)  { -	return 0; +	return ZEBRA_DPLANE_REQUEST_SUCCESS;  } -int kernel_del_vtep(vni_t vni, struct interface *ifp, struct in_addr *vtep_ip) +extern int kernel_get_ipmr_sg_stats(struct zebra_vrf *zvrf, void *mroute)  {  	return 0;  } @@ -394,17 +390,6 @@ enum zebra_dplane_result kernel_mac_update_ctx(struct zebra_dplane_ctx *ctx)  	return ZEBRA_DPLANE_REQUEST_SUCCESS;  } -int kernel_add_neigh(struct interface *ifp, struct ipaddr *ip, -		     struct ethaddr *mac, uint8_t flags) -{ -	return 0; -} - -int kernel_del_neigh(struct interface *ifp, struct ipaddr *ip) -{ -	return 0; -} -  extern int kernel_interface_set_master(struct interface *master,  				       struct interface *slave)  { diff --git a/zebra/rtadv.c b/zebra/rtadv.c index 5841c44b03..b084fb99ca 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -760,7 +760,7 @@ static int rtadv_make_socket(ns_id_t ns_id)  	int ret = 0;  	struct icmp6_filter filter; -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		sock = ns_socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6, ns_id); diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index b6a8ee950c..fa6a2f62ec 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -2507,7 +2507,7 @@ static void zserv_write_incoming(struct stream *orig, uint16_t command)  	snprintf(fname, MAXPATHLEN, "%s/%u", frr_vtydir, command); -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		fd = open(fname, O_CREAT | O_WRONLY | O_EXCL, 0644);  	}  	stream_flush(copy, fd); diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 512a9b4021..2bf541617c 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -148,7 +148,7 @@ struct dplane_intf_info {  };  /* - * MAC address info for the dataplane. + * EVPN MAC address info for the dataplane.   */  struct dplane_mac_info {  	vlanid_t vid; @@ -159,6 +159,16 @@ struct dplane_mac_info {  };  /* + * EVPN neighbor info for the dataplane + */ +struct dplane_neigh_info { +	struct ipaddr ip_addr; +	struct ethaddr mac; +	uint32_t flags; +	uint16_t state; +}; + +/*   * The context block used to exchange info about route updates across   * the boundary between the zebra main context (and pthread) and the   * dataplane layer (and pthread). @@ -204,6 +214,7 @@ struct zebra_dplane_ctx {  		struct dplane_pw_info pw;  		struct dplane_intf_info intf;  		struct dplane_mac_info macinfo; +		struct dplane_neigh_info neigh;  	} u;  	/* Namespace info, used especially for netlink kernel communication */ @@ -321,6 +332,9 @@ static struct zebra_dplane_globals {  	_Atomic uint32_t dg_macs_in;  	_Atomic uint32_t dg_mac_errors; +	_Atomic uint32_t dg_neighs_in; +	_Atomic uint32_t dg_neigh_errors; +  	_Atomic uint32_t dg_update_yields;  	/* Dataplane pthread */ @@ -365,6 +379,12 @@ static enum zebra_dplane_result mac_update_internal(  	enum dplane_op_e op, const struct interface *ifp,  	vlanid_t vid, const struct ethaddr *mac,  	struct in_addr vtep_ip,	bool sticky); +static enum zebra_dplane_result neigh_update_internal( +	enum dplane_op_e op, +	const struct interface *ifp, +	const struct ethaddr *mac, +	const struct ipaddr *ip, +	uint32_t flags, uint16_t state);  /*   * Public APIs @@ -485,6 +505,11 @@ static void dplane_ctx_free(struct zebra_dplane_ctx **pctx)  	case DPLANE_OP_MAC_INSTALL:  	case DPLANE_OP_MAC_DELETE: +	case DPLANE_OP_NEIGH_INSTALL: +	case DPLANE_OP_NEIGH_UPDATE: +	case DPLANE_OP_NEIGH_DELETE: +	case DPLANE_OP_VTEP_ADD: +	case DPLANE_OP_VTEP_DELETE:  	case DPLANE_OP_NONE:  		break;  	} @@ -651,6 +676,22 @@ const char *dplane_op2str(enum dplane_op_e op)  	case DPLANE_OP_MAC_DELETE:  		ret = "MAC_DELETE";  		break; + +	case DPLANE_OP_NEIGH_INSTALL: +		ret = "NEIGH_INSTALL"; +		break; +	case DPLANE_OP_NEIGH_UPDATE: +		ret = "NEIGH_UPDATE"; +		break; +	case DPLANE_OP_NEIGH_DELETE: +		ret = "NEIGH_DELETE"; +		break; +	case DPLANE_OP_VTEP_ADD: +		ret = "VTEP_ADD"; +		break; +	case DPLANE_OP_VTEP_DELETE: +		ret = "VTEP_DELETE"; +		break;  	}  	return ret; @@ -1231,6 +1272,33 @@ const struct in_addr *dplane_ctx_mac_get_vtep_ip(  	return &(ctx->u.macinfo.vtep_ip);  } +/* Accessors for neighbor information */ +const struct ipaddr *dplane_ctx_neigh_get_ipaddr( +	const struct zebra_dplane_ctx *ctx) +{ +	DPLANE_CTX_VALID(ctx); +	return &(ctx->u.neigh.ip_addr); +} + +const struct ethaddr *dplane_ctx_neigh_get_mac( +	const struct zebra_dplane_ctx *ctx) +{ +	DPLANE_CTX_VALID(ctx); +	return &(ctx->u.neigh.mac); +} + +uint32_t dplane_ctx_neigh_get_flags(const struct zebra_dplane_ctx *ctx) +{ +	DPLANE_CTX_VALID(ctx); +	return ctx->u.neigh.flags; +} + +uint16_t dplane_ctx_neigh_get_state(const struct zebra_dplane_ctx *ctx) +{ +	DPLANE_CTX_VALID(ctx); +	return ctx->u.neigh.state; +} +  /*   * End of dplane context accessors   */ @@ -2161,6 +2229,165 @@ mac_update_internal(enum dplane_op_e op,  }  /* + * Enqueue evpn neighbor add for the dataplane. + */ +enum zebra_dplane_result dplane_neigh_add(const struct interface *ifp, +					  const struct ipaddr *ip, +					  const struct ethaddr *mac, +					  uint32_t flags) +{ +	enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + +	result = neigh_update_internal(DPLANE_OP_NEIGH_INSTALL, +				       ifp, mac, ip, flags, 0); + +	return result; +} + +/* + * Enqueue evpn neighbor update for the dataplane. + */ +enum zebra_dplane_result dplane_neigh_update(const struct interface *ifp, +					     const struct ipaddr *ip, +					     const struct ethaddr *mac) +{ +	enum zebra_dplane_result result; + +	result = neigh_update_internal(DPLANE_OP_NEIGH_UPDATE, +				       ifp, mac, ip, 0, DPLANE_NUD_PROBE); + +	return result; +} + +/* + * Enqueue evpn neighbor delete for the dataplane. + */ +enum zebra_dplane_result dplane_neigh_delete(const struct interface *ifp, +					     const struct ipaddr *ip) +{ +	enum zebra_dplane_result result; + +	result = neigh_update_internal(DPLANE_OP_NEIGH_DELETE, +				       ifp, NULL, ip, 0, 0); + +	return result; +} + +/* + * Enqueue evpn VTEP add for the dataplane. + */ +enum zebra_dplane_result dplane_vtep_add(const struct interface *ifp, +					 const struct in_addr *ip, +					 vni_t vni) +{ +	enum zebra_dplane_result result; +	struct ethaddr mac = { {0, 0, 0, 0, 0, 0} }; +	struct ipaddr addr; + +	if (IS_ZEBRA_DEBUG_VXLAN) +		zlog_debug("Install %s into flood list for VNI %u intf %s(%u)", +			   inet_ntoa(*ip), vni, ifp->name, ifp->ifindex); + +	SET_IPADDR_V4(&addr); +	addr.ipaddr_v4 = *ip; + +	result = neigh_update_internal(DPLANE_OP_VTEP_ADD, +				       ifp, &mac, &addr, 0, 0); + +	return result; +} + +/* + * Enqueue evpn VTEP add for the dataplane. + */ +enum zebra_dplane_result dplane_vtep_delete(const struct interface *ifp, +					    const struct in_addr *ip, +					    vni_t vni) +{ +	enum zebra_dplane_result result; +	struct ethaddr mac = { {0, 0, 0, 0, 0, 0} }; +	struct ipaddr addr; + +	if (IS_ZEBRA_DEBUG_VXLAN) +		zlog_debug( +			"Uninstall %s from flood list for VNI %u intf %s(%u)", +			inet_ntoa(*ip), vni, ifp->name, ifp->ifindex); + +	SET_IPADDR_V4(&addr); +	addr.ipaddr_v4 = *ip; + +	result = neigh_update_internal(DPLANE_OP_VTEP_DELETE, +				       ifp, &mac, &addr, 0, 0); + +	return result; +} + +/* + * Common helper api for evpn neighbor updates + */ +static enum zebra_dplane_result +neigh_update_internal(enum dplane_op_e op, +		      const struct interface *ifp, +		      const struct ethaddr *mac, +		      const struct ipaddr *ip, +		      uint32_t flags, uint16_t state) +{ +	enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; +	int ret; +	struct zebra_dplane_ctx *ctx = NULL; +	struct zebra_ns *zns; + +	if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { +		char buf1[ETHER_ADDR_STRLEN], buf2[PREFIX_STRLEN]; + +		zlog_debug("init neigh ctx %s: ifp %s, mac %s, ip %s", +			   dplane_op2str(op), +			   prefix_mac2str(mac, buf1, sizeof(buf1)), +			   ifp->name, +			   ipaddr2str(ip, buf2, sizeof(buf2))); +	} + +	ctx = dplane_ctx_alloc(); + +	ctx->zd_op = op; +	ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; +	ctx->zd_vrf_id = ifp->vrf_id; + +	zns = zebra_ns_lookup(ifp->vrf_id); +	dplane_ctx_ns_init(ctx, zns, false); + +	strlcpy(ctx->zd_ifname, ifp->name, sizeof(ctx->zd_ifname)); +	ctx->zd_ifindex = ifp->ifindex; + +	/* Init the neighbor-specific data area */ +	memset(&ctx->u.neigh, 0, sizeof(ctx->u.neigh)); + +	ctx->u.neigh.ip_addr = *ip; +	if (mac) +		ctx->u.neigh.mac = *mac; +	ctx->u.neigh.flags = flags; +	ctx->u.neigh.state = state; + +	/* Enqueue for processing on the dplane pthread */ +	ret = dplane_update_enqueue(ctx); + +	/* Increment counter */ +	atomic_fetch_add_explicit(&zdplane_info.dg_neighs_in, 1, +				  memory_order_relaxed); + +	if (ret == AOK) +		result = ZEBRA_DPLANE_REQUEST_QUEUED; +	else { +		/* Error counter */ +		atomic_fetch_add_explicit(&zdplane_info.dg_neigh_errors, 1, +					  memory_order_relaxed); +		dplane_ctx_free(&ctx); +	} + +	return result; +} + +/*   * Handler for 'show dplane'   */  int dplane_show_helper(struct vty *vty, bool detailed) @@ -2223,6 +2450,13 @@ int dplane_show_helper(struct vty *vty, bool detailed)  	vty_out(vty, "EVPN MAC updates:         %"PRIu64"\n", incoming);  	vty_out(vty, "EVPN MAC errors:          %"PRIu64"\n", errs); +	incoming = atomic_load_explicit(&zdplane_info.dg_neighs_in, +					memory_order_relaxed); +	errs = atomic_load_explicit(&zdplane_info.dg_neigh_errors, +				    memory_order_relaxed); +	vty_out(vty, "EVPN neigh updates:       %"PRIu64"\n", incoming); +	vty_out(vty, "EVPN neigh errors:        %"PRIu64"\n", errs); +  	return CMD_SUCCESS;  } @@ -2616,7 +2850,7 @@ kernel_dplane_address_update(struct zebra_dplane_ctx *ctx)  }  /* - * Handler for kernel-facing MAC address updates + * Handler for kernel-facing EVPN MAC address updates   */  static enum zebra_dplane_result  kernel_dplane_mac_update(struct zebra_dplane_ctx *ctx) @@ -2644,6 +2878,34 @@ kernel_dplane_mac_update(struct zebra_dplane_ctx *ctx)  }  /* + * Handler for kernel-facing EVPN neighbor updates + */ +static enum zebra_dplane_result +kernel_dplane_neigh_update(struct zebra_dplane_ctx *ctx) +{ +	enum zebra_dplane_result res; + +	if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { +		char buf[PREFIX_STRLEN]; + +		ipaddr2str(dplane_ctx_neigh_get_ipaddr(ctx), buf, +			   sizeof(buf)); + +		zlog_debug("Dplane %s, ip %s, ifindex %u", +			   dplane_op2str(dplane_ctx_get_op(ctx)), +			   buf, dplane_ctx_get_ifindex(ctx)); +	} + +	res = kernel_neigh_update_ctx(ctx); + +	if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) +		atomic_fetch_add_explicit(&zdplane_info.dg_neigh_errors, +					  1, memory_order_relaxed); + +	return res; +} + +/*   * Kernel provider callback   */  static int kernel_dplane_process_func(struct zebra_dplane_provider *prov) @@ -2702,6 +2964,14 @@ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)  			res = kernel_dplane_mac_update(ctx);  			break; +		case DPLANE_OP_NEIGH_INSTALL: +		case DPLANE_OP_NEIGH_UPDATE: +		case DPLANE_OP_NEIGH_DELETE: +		case DPLANE_OP_VTEP_ADD: +		case DPLANE_OP_VTEP_DELETE: +			res = kernel_dplane_neigh_update(ctx); +			break; +  		/* Ignore 'notifications' - no-op */  		case DPLANE_OP_SYS_ROUTE_ADD:  		case DPLANE_OP_SYS_ROUTE_DELETE: diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 912fda45d3..31f0fc98b3 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -129,8 +129,33 @@ enum dplane_op_e {  	/* MAC address update */  	DPLANE_OP_MAC_INSTALL,  	DPLANE_OP_MAC_DELETE, + +	/* EVPN neighbor updates */ +	DPLANE_OP_NEIGH_INSTALL, +	DPLANE_OP_NEIGH_UPDATE, +	DPLANE_OP_NEIGH_DELETE, + +	/* EVPN VTEP updates */ +	DPLANE_OP_VTEP_ADD, +	DPLANE_OP_VTEP_DELETE,  }; +/* + * The vxlan/evpn neighbor management code needs some values to use + * when programming neighbor changes. Offer some platform-neutral values + * here for use within the dplane apis and plugins. + */ + +/* Neighbor cache flags */ +#define DPLANE_NTF_EXT_LEARNED    0x01 +#define DPLANE_NTF_ROUTER         0x02 + +/* Neighbor cache states */ +#define DPLANE_NUD_REACHABLE      0x01 +#define DPLANE_NUD_STALE          0x02 +#define DPLANE_NUD_NOARP          0x04 +#define DPLANE_NUD_PROBE          0x08 +  /* Enable system route notifications */  void dplane_enable_sys_route_notifs(void); @@ -304,6 +329,14 @@ const struct ethaddr *dplane_ctx_mac_get_addr(  const struct in_addr *dplane_ctx_mac_get_vtep_ip(  	const struct zebra_dplane_ctx *ctx); +/* Accessors for neighbor information */ +const struct ipaddr *dplane_ctx_neigh_get_ipaddr( +	const struct zebra_dplane_ctx *ctx); +const struct ethaddr *dplane_ctx_neigh_get_mac( +	const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_neigh_get_flags(const struct zebra_dplane_ctx *ctx); +uint16_t dplane_ctx_neigh_get_state(const struct zebra_dplane_ctx *ctx); +  /* Namespace info - esp. for netlink communication */  const struct zebra_dplane_info *dplane_ctx_get_ns(  	const struct zebra_dplane_ctx *ctx); @@ -379,6 +412,30 @@ enum zebra_dplane_result dplane_mac_del(const struct interface *ifp,  					const struct ethaddr *mac,  					struct in_addr vtep_ip); +/* + * Enqueue evpn neighbor updates for the dataplane. + */ +enum zebra_dplane_result dplane_neigh_add(const struct interface *ifp, +					  const struct ipaddr *ip, +					  const struct ethaddr *mac, +					  uint32_t flags); +enum zebra_dplane_result dplane_neigh_update(const struct interface *ifp, +					     const struct ipaddr *ip, +					     const struct ethaddr *mac); +enum zebra_dplane_result dplane_neigh_delete(const struct interface *ifp, +					     const struct ipaddr *ip); + +/* + * Enqueue evpn VTEP updates for the dataplane. + */ +enum zebra_dplane_result dplane_vtep_add(const struct interface *ifp, +					 const struct in_addr *ip, +					 vni_t vni); +enum zebra_dplane_result dplane_vtep_delete(const struct interface *ifp, +					    const struct in_addr *ip, +					    vni_t vni); + +  /* Retrieve the limit on the number of pending, unprocessed updates. */  uint32_t dplane_get_in_queue_limit(void); diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c index eaf43095bc..4144c0afe0 100644 --- a/zebra/zebra_fpm.c +++ b/zebra/zebra_fpm.c @@ -1927,6 +1927,9 @@ static inline void zfpm_init_message_format(const char *format)  				"FPM protobuf message format is not available");  			return;  		} +		flog_warn(EC_ZEBRA_PROTOBUF_NOT_AVAILABLE, +			  "FPM protobuf message format is deprecated and scheduled to be removed. " +			  "Please convert to using netlink format or contact dev@lists.frrouting.org with your use case.");  		zfpm_g->message_format = ZFPM_MSG_FORMAT_PROTOBUF;  		return;  	} diff --git a/zebra/zebra_mpls_openbsd.c b/zebra/zebra_mpls_openbsd.c index 9f3ea70c77..fcd476dc2c 100644 --- a/zebra/zebra_mpls_openbsd.c +++ b/zebra/zebra_mpls_openbsd.c @@ -119,7 +119,7 @@ static int kernel_send_rtmsg_v4(int action, mpls_label_t in_label,  			hdr.rtm_mpls = MPLS_OP_SWAP;  	} -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		ret = writev(kr_state.fd, iov, iovcnt);  	} @@ -226,7 +226,7 @@ static int kernel_send_rtmsg_v6(int action, mpls_label_t in_label,  			hdr.rtm_mpls = MPLS_OP_SWAP;  	} -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		ret = writev(kr_state.fd, iov, iovcnt);  	} diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c index 476638591b..d42cf3d60a 100644 --- a/zebra/zebra_netns_notify.c +++ b/zebra/zebra_netns_notify.c @@ -77,7 +77,7 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name)  	if (netnspath == NULL)  		return; -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		ns_id = zebra_ns_id_get(netnspath);  	}  	if (ns_id == NS_UNKNOWN) @@ -97,7 +97,7 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name)  		ns_map_nsid_with_external(ns_id, false);  		return;  	} -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		ret = vrf_netns_handler_create(NULL, vrf, netnspath,  					       ns_id_external, ns_id);  	} @@ -202,14 +202,14 @@ static int zebra_ns_ready_read(struct thread *t)  	netnspath = zns_info->netnspath;  	if (--zns_info->retries == 0)  		stop_retry = 1; -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		err = ns_switch_to_netns(netnspath);  	}  	if (err < 0)  		return zebra_ns_continue_read(zns_info, stop_retry);  	/* go back to default ns */ -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		err = ns_switchback_to_initial();  	}  	if (err < 0) diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index f4b86f3cfe..ee2956d3ea 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -25,6 +25,7 @@  #include "lib/nexthop.h"  #include "lib/nexthop_group_private.h"  #include "lib/routemap.h" +#include "lib/mpls.h"  #include "zebra/connected.h"  #include "zebra/debug.h" @@ -38,6 +39,10 @@ static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop,  				 struct nexthop *nexthop)  {  	struct nexthop *resolved_hop; +	uint8_t num_labels = 0; +	mpls_label_t labels[MPLS_MAX_LABELS]; +	enum lsp_types_t label_type = ZEBRA_LSP_NONE; +	int i = 0;  	resolved_hop = nexthop_new();  	SET_FLAG(resolved_hop->flags, NEXTHOP_FLAG_ACTIVE); @@ -94,11 +99,24 @@ static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop,  	if (newhop->flags & NEXTHOP_FLAG_ONLINK)  		resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; -	/* Copy labels of the resolved route */ -	if (newhop->nh_label) -		nexthop_add_labels(resolved_hop, newhop->nh_label_type, -				   newhop->nh_label->num_labels, -				   &newhop->nh_label->label[0]); +	/* Copy labels of the resolved route and the parent resolving to it */ +	if (newhop->nh_label) { +		for (i = 0; i < newhop->nh_label->num_labels; i++) +			labels[num_labels++] = newhop->nh_label->label[i]; +		label_type = newhop->nh_label_type; +	} + +	if (nexthop->nh_label) { +		for (i = 0; i < nexthop->nh_label->num_labels; i++) +			labels[num_labels++] = nexthop->nh_label->label[i]; + +		/* If the parent has labels, use its type */ +		label_type = nexthop->nh_label_type; +	} + +	if (num_labels) +		nexthop_add_labels(resolved_hop, label_type, num_labels, +				   labels);  	resolved_hop->rparent = nexthop;  	_nexthop_add(&nexthop->resolved, resolved_hop); @@ -120,6 +138,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re,  	struct nexthop *newhop;  	struct interface *ifp;  	rib_dest_t *dest; +	struct zebra_vrf *zvrf;  	if ((nexthop->type == NEXTHOP_TYPE_IPV4)  	    || nexthop->type == NEXTHOP_TYPE_IPV6) @@ -194,7 +213,9 @@ static int nexthop_active(afi_t afi, struct route_entry *re,  	}  	/* Lookup table.  */  	table = zebra_vrf_table(afi, SAFI_UNICAST, nexthop->vrf_id); -	if (!table) { +	/* get zvrf */ +	zvrf = zebra_vrf_lookup_by_id(nexthop->vrf_id); +	if (!table || !zvrf) {  		if (IS_ZEBRA_DEBUG_RIB_DETAILED)  			zlog_debug("\t%s: Table not found",  				   __PRETTY_FUNCTION__); @@ -224,7 +245,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re,  		/* However, do not resolve over default route unless explicitly  		 * allowed. */  		if (is_default_prefix(&rn->p) -		    && !rnh_resolve_via_default(p.family)) { +		    && !rnh_resolve_via_default(zvrf, p.family)) {  			if (IS_ZEBRA_DEBUG_RIB_DETAILED)  				zlog_debug(  					"\t:%s: Resolved against default route", diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 94918365a3..37f53bf911 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -180,7 +180,7 @@ int zebra_ns_init(const char *optional_default_name)  	dzns = zebra_ns_alloc(); -	frr_elevate_privs(&zserv_privs) { +	frr_with_privs(&zserv_privs) {  		ns_id = zebra_ns_id_get_default();  	}  	ns_id_external = ns_map_nsid_with_external(ns_id, true); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 335cc8294c..157c67fa62 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -37,6 +37,7 @@  #include "vrf.h"  #include "workqueue.h"  #include "nexthop_group_private.h" +#include "frr_pthread.h"  #include "zebra/zebra_router.h"  #include "zebra/connected.h" @@ -3204,12 +3205,10 @@ static int rib_process_dplane_results(struct thread *thread)  		TAILQ_INIT(&ctxlist);  		/* Take lock controlling queue of results */ -		pthread_mutex_lock(&dplane_mutex); -		{ +		frr_with_mutex(&dplane_mutex) {  			/* Dequeue list of context structs */  			dplane_ctx_list_append(&ctxlist, &rib_dplane_q);  		} -		pthread_mutex_unlock(&dplane_mutex);  		/* Dequeue context block */  		ctx = dplane_ctx_dequeue(&ctxlist); @@ -3275,10 +3274,19 @@ static int rib_process_dplane_results(struct thread *thread)  				zebra_vxlan_handle_result(ctx);  				break; -			default: +			/* Some op codes not handled here */ +			case DPLANE_OP_ADDR_INSTALL: +			case DPLANE_OP_ADDR_UNINSTALL: +			case DPLANE_OP_NEIGH_INSTALL: +			case DPLANE_OP_NEIGH_UPDATE: +			case DPLANE_OP_NEIGH_DELETE: +			case DPLANE_OP_VTEP_ADD: +			case DPLANE_OP_VTEP_DELETE: +			case DPLANE_OP_NONE:  				/* Don't expect this: just return the struct? */  				dplane_ctx_fini(&ctx);  				break; +  			} /* Dispatch by op code */  			ctx = dplane_ctx_dequeue(&ctxlist); @@ -3300,12 +3308,10 @@ static int rib_process_dplane_results(struct thread *thread)  static int rib_dplane_results(struct dplane_ctx_q *ctxlist)  {  	/* Take lock controlling queue of results */ -	pthread_mutex_lock(&dplane_mutex); -	{ +	frr_with_mutex(&dplane_mutex) {  		/* Enqueue context blocks */  		dplane_ctx_list_append(&rib_dplane_q, ctxlist);  	} -	pthread_mutex_unlock(&dplane_mutex);  	/* Ensure event is signalled to zebra main pthread */  	thread_add_event(zrouter.master, rib_process_dplane_results, NULL, 0, diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index da2fe4a30c..666ebb70e8 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -62,9 +62,6 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type,  static void print_rnh(struct route_node *rn, struct vty *vty);  static int zebra_client_cleanup_rnh(struct zserv *client); -int zebra_rnh_ip_default_route = 0; -int zebra_rnh_ipv6_default_route = 0; -  void zebra_rnh_init(void)  {  	hook_register(zserv_client_close, zebra_client_cleanup_rnh); @@ -656,7 +653,7 @@ zebra_rnh_resolve_nexthop_entry(struct zebra_vrf *zvrf, afi_t afi,  		 * match route to be exact if so specified  		 */  		if (is_default_prefix(&rn->p) -		    && !rnh_resolve_via_default(rn->p.family)) { +		    && !rnh_resolve_via_default(zvrf, rn->p.family)) {  			if (IS_ZEBRA_DEBUG_NHT_DETAILED)  				zlog_debug(  					"\tNot allowed to resolve through default prefix"); @@ -1213,3 +1210,12 @@ static int zebra_client_cleanup_rnh(struct zserv *client)  	return 0;  } + +int rnh_resolve_via_default(struct zebra_vrf *zvrf, int family) +{ +	if (((family == AF_INET) && zvrf->zebra_rnh_ip_default_route) +	    || ((family == AF_INET6) && zvrf->zebra_rnh_ipv6_default_route)) +		return 1; +	else +		return 0; +} diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h index c7d2c0d298..6e2dab8d9f 100644 --- a/zebra/zebra_rnh.h +++ b/zebra/zebra_rnh.h @@ -29,20 +29,8 @@  extern "C" {  #endif -extern int zebra_rnh_ip_default_route; -extern int zebra_rnh_ipv6_default_route; -  extern void zebra_rnh_init(void); -static inline int rnh_resolve_via_default(int family) -{ -	if (((family == AF_INET) && zebra_rnh_ip_default_route) -	    || ((family == AF_INET6) && zebra_rnh_ipv6_default_route)) -		return 1; -	else -		return 0; -} -  static inline const char *rnh_type2str(rnh_type_t type)  {  	switch (type) { @@ -72,6 +60,8 @@ extern void zebra_print_rnh_table(vrf_id_t vrfid, afi_t afi, struct vty *vty,  				  rnh_type_t type, struct prefix *p);  extern char *rnh_str(struct rnh *rnh, char *buf, int size); +extern int rnh_resolve_via_default(struct zebra_vrf *zvrf, int family); +  #ifdef __cplusplus  }  #endif diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index cee2c8980f..88d2091394 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -64,7 +64,7 @@ static int zebra_route_match_add(struct vty *vty, const char *command,  				 const char *arg, route_map_event_t type)  {  	VTY_DECLVAR_CONTEXT(route_map_index, index); -	int ret; +	enum rmap_compile_rets ret;  	int retval = CMD_SUCCESS;  	ret = route_map_add_match(index, command, arg, type); @@ -82,6 +82,11 @@ static int zebra_route_match_add(struct vty *vty, const char *command,  			route_map_upd8_dependency(type, arg, index->map->name);  		}  		break; +	case RMAP_DUPLICATE_RULE: +		/* +		 * Nothing to do here +		 */ +		break;  	}  	return retval; @@ -92,7 +97,7 @@ static int zebra_route_match_delete(struct vty *vty, const char *command,  				    const char *arg, route_map_event_t type)  {  	VTY_DECLVAR_CONTEXT(route_map_index, index); -	int ret; +	enum rmap_compile_rets ret;  	int retval = CMD_SUCCESS;  	char *dep_name = NULL;  	const char *tmpstr; @@ -125,6 +130,11 @@ static int zebra_route_match_delete(struct vty *vty, const char *command,  		if (type != RMAP_EVENT_MATCH_DELETED && dep_name)  			route_map_upd8_dependency(type, dep_name, rmap_name);  		break; +	case RMAP_DUPLICATE_RULE: +		/* +		 * Nothing to do here +		 */ +		break;  	}  	XFREE(MTYPE_ROUTE_MAP_RULE, dep_name); diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index fcc94a7be9..72d0b6866d 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -488,6 +488,11 @@ static int vrf_config_write(struct vty *vty)  		if (zvrf_id(zvrf) == VRF_DEFAULT) {  			if (zvrf->l3vni)  				vty_out(vty, "vni %u\n", zvrf->l3vni); +			if (zvrf->zebra_rnh_ip_default_route) +				vty_out(vty, "ip nht resolve-via-default\n"); + +			if (zvrf->zebra_rnh_ipv6_default_route) +				vty_out(vty, "ipv6 nht resolve-via-default\n");  		} else {  			vty_frame(vty, "vrf %s\n", zvrf_name(zvrf));  			if (zvrf->l3vni) @@ -497,8 +502,14 @@ static int vrf_config_write(struct vty *vty)  						? " prefix-routes-only"  						: "");  			zebra_ns_config_write(vty, (struct ns *)vrf->ns_ctxt); +			if (zvrf->zebra_rnh_ip_default_route) +				vty_out(vty, " ip nht resolve-via-default\n"); + +			if (zvrf->zebra_rnh_ipv6_default_route) +				vty_out(vty, " ipv6 nht resolve-via-default\n");  		} +  		zebra_routemap_config_write_protocol(vty, zvrf);  		if (zvrf_id(zvrf) != VRF_DEFAULT) diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index f92e1a010b..6c80f9bcb4 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -174,6 +174,9 @@ struct zebra_vrf {  #if defined(HAVE_RTADV)  	struct rtadv rtadv;  #endif /* HAVE_RTADV */ + +	int zebra_rnh_ip_default_route; +	int zebra_rnh_ipv6_default_route;  };  #define PROTO_RM_NAME(zvrf, afi, rtype) zvrf->proto_rm[afi][rtype].name  #define NHT_RM_NAME(zvrf, afi, rtype) zvrf->nht_rm[afi][rtype].name diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 5c0dc27380..38de01e228 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -455,6 +455,10 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,  				    re->status);  		json_object_int_add(json_route, "internalFlags",  				    re->flags); +		json_object_int_add(json_route, "internalNextHopNum", +				    re->nexthop_num); +		json_object_int_add(json_route, "internalNextHopActiveNum", +				    re->nexthop_active_num);  		if (uptime < ONE_DAY_SECOND)  			sprintf(buf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min,  				tm->tm_sec); @@ -1086,10 +1090,10 @@ DEFUN (ip_nht_default_route,  	if (!zvrf)  		return CMD_WARNING; -	if (zebra_rnh_ip_default_route) +	if (zvrf->zebra_rnh_ip_default_route)  		return CMD_SUCCESS; -	zebra_rnh_ip_default_route = 1; +	zvrf->zebra_rnh_ip_default_route = 1;  	zebra_evaluate_rnh(zvrf, AFI_IP, 1, RNH_NEXTHOP_TYPE, NULL);  	return CMD_SUCCESS; @@ -1108,10 +1112,10 @@ DEFUN (no_ip_nht_default_route,  	if (!zvrf)  		return CMD_WARNING; -	if (!zebra_rnh_ip_default_route) +	if (!zvrf->zebra_rnh_ip_default_route)  		return CMD_SUCCESS; -	zebra_rnh_ip_default_route = 0; +	zvrf->zebra_rnh_ip_default_route = 0;  	zebra_evaluate_rnh(zvrf, AFI_IP, 1, RNH_NEXTHOP_TYPE, NULL);  	return CMD_SUCCESS;  } @@ -1128,10 +1132,10 @@ DEFUN (ipv6_nht_default_route,  	if (!zvrf)  		return CMD_WARNING; -	if (zebra_rnh_ipv6_default_route) +	if (zvrf->zebra_rnh_ipv6_default_route)  		return CMD_SUCCESS; -	zebra_rnh_ipv6_default_route = 1; +	zvrf->zebra_rnh_ipv6_default_route = 1;  	zebra_evaluate_rnh(zvrf, AFI_IP6, 1, RNH_NEXTHOP_TYPE, NULL);  	return CMD_SUCCESS;  } @@ -1150,10 +1154,10 @@ DEFUN (no_ipv6_nht_default_route,  	if (!zvrf)  		return CMD_WARNING; -	if (!zebra_rnh_ipv6_default_route) +	if (!zvrf->zebra_rnh_ipv6_default_route)  		return CMD_SUCCESS; -	zebra_rnh_ipv6_default_route = 0; +	zvrf->zebra_rnh_ipv6_default_route = 0;  	zebra_evaluate_rnh(zvrf, AFI_IP6, 1, RNH_NEXTHOP_TYPE, NULL);  	return CMD_SUCCESS;  } @@ -2625,12 +2629,6 @@ static int config_write_protocol(struct vty *vty)  	if (allow_delete)  		vty_out(vty, "allow-external-route-update\n"); -	if (zebra_rnh_ip_default_route) -		vty_out(vty, "ip nht resolve-via-default\n"); - -	if (zebra_rnh_ipv6_default_route) -		vty_out(vty, "ipv6 nht resolve-via-default\n"); -  	if (zrouter.ribq->spec.hold != ZEBRA_RIB_PROCESS_HOLD_TIME)  		vty_out(vty, "zebra work-queue %u\n", zrouter.ribq->spec.hold); diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index bb31247b6a..bb8b61e7e3 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -2515,9 +2515,7 @@ static int zvni_neigh_install(zebra_vni_t *zvni, zebra_neigh_t *n)  	struct zebra_if *zif;  	struct zebra_l2info_vxlan *vxl;  	struct interface *vlan_if; -#ifdef GNU_LINUX -	uint8_t flags; -#endif +	int flags;  	int ret = 0;  	if (!(n->flags & ZEBRA_NEIGH_REMOTE)) @@ -2531,13 +2529,14 @@ static int zvni_neigh_install(zebra_vni_t *zvni, zebra_neigh_t *n)  	vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if);  	if (!vlan_if)  		return -1; -#ifdef GNU_LINUX -	flags = NTF_EXT_LEARNED; + +	flags = DPLANE_NTF_EXT_LEARNED;  	if (n->flags & ZEBRA_NEIGH_ROUTER_FLAG) -		flags |= NTF_ROUTER; +		flags |= DPLANE_NTF_ROUTER;  	ZEBRA_NEIGH_SET_ACTIVE(n); -	ret = kernel_add_neigh(vlan_if, &n->ip, &n->emac, flags); -#endif + +	dplane_neigh_add(vlan_if, &n->ip, &n->emac, flags); +  	return ret;  } @@ -2569,7 +2568,10 @@ static int zvni_neigh_uninstall(zebra_vni_t *zvni, zebra_neigh_t *n)  	ZEBRA_NEIGH_SET_INACTIVE(n);  	n->loc_seq = 0; -	return kernel_del_neigh(vlan_if, &n->ip); + +	dplane_neigh_delete(vlan_if, &n->ip); + +	return 0;  }  /* @@ -2590,12 +2592,9 @@ static int zvni_neigh_probe(zebra_vni_t *zvni, zebra_neigh_t *n)  	if (!vlan_if)  		return -1; -#ifdef GNU_LINUX -	return kernel_upd_neigh(vlan_if, &n->ip, &n->emac, -				0, NUD_PROBE); -#else +	dplane_neigh_update(vlan_if, &n->ip, &n->emac); +  	return 0; -#endif  }  /* @@ -4291,9 +4290,13 @@ static int zvni_vtep_del_all(zebra_vni_t *zvni, int uninstall)  static int zvni_vtep_install(zebra_vni_t *zvni, zebra_vtep_t *zvtep)  {  	if (is_vxlan_flooding_head_end() && -			(zvtep->flood_control == VXLAN_FLOOD_HEAD_END_REPL)) -		return kernel_add_vtep(zvni->vni, zvni->vxlan_if, -				&zvtep->vtep_ip); +	    (zvtep->flood_control == VXLAN_FLOOD_HEAD_END_REPL)) { +		if (ZEBRA_DPLANE_REQUEST_FAILURE == +		    dplane_vtep_add(zvni->vxlan_if, +				    &zvtep->vtep_ip, zvni->vni)) +			return -1; +	} +  	return 0;  } @@ -4308,7 +4311,11 @@ static int zvni_vtep_uninstall(zebra_vni_t *zvni, struct in_addr *vtep_ip)  		return -1;  	} -	return kernel_del_vtep(zvni->vni, zvni->vxlan_if, vtep_ip); +	if (ZEBRA_DPLANE_REQUEST_FAILURE == +	    dplane_vtep_delete(zvni->vxlan_if, vtep_ip, zvni->vni)) +		return -1; + +	return 0;  }  /* @@ -4678,9 +4685,7 @@ static int zl3vni_nh_del(zebra_l3vni_t *zl3vni, zebra_neigh_t *n)   */  static int zl3vni_nh_install(zebra_l3vni_t *zl3vni, zebra_neigh_t *n)  { -#ifdef GNU_LINUX  	uint8_t flags; -#endif  	int ret = 0;  	if (!is_l3vni_oper_up(zl3vni)) @@ -4689,12 +4694,13 @@ static int zl3vni_nh_install(zebra_l3vni_t *zl3vni, zebra_neigh_t *n)  	if (!(n->flags & ZEBRA_NEIGH_REMOTE)  	    || !(n->flags & ZEBRA_NEIGH_REMOTE_NH))  		return 0; -#ifdef GNU_LINUX -	flags = NTF_EXT_LEARNED; + +	flags = DPLANE_NTF_EXT_LEARNED;  	if (n->flags & ZEBRA_NEIGH_ROUTER_FLAG) -		flags |= NTF_ROUTER; -	ret = kernel_add_neigh(zl3vni->svi_if, &n->ip, &n->emac, flags); -#endif +		flags |= DPLANE_NTF_ROUTER; + +	dplane_neigh_add(zl3vni->svi_if, &n->ip, &n->emac, flags); +  	return ret;  } @@ -4710,7 +4716,9 @@ static int zl3vni_nh_uninstall(zebra_l3vni_t *zl3vni, zebra_neigh_t *n)  	if (!zl3vni->svi_if || !if_is_operative(zl3vni->svi_if))  		return 0; -	return kernel_del_neigh(zl3vni->svi_if, &n->ip); +	dplane_neigh_delete(zl3vni->svi_if, &n->ip); + +	return 0;  }  /* add remote vtep as a neigh entry */ diff --git a/zebra/zserv.c b/zebra/zserv.c index 70b4594813..c008441d6a 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -231,13 +231,11 @@ static int zserv_write(struct thread *thread)  	cache = stream_fifo_new(); -	pthread_mutex_lock(&client->obuf_mtx); -	{ +	frr_with_mutex(&client->obuf_mtx) {  		while (stream_fifo_head(client->obuf_fifo))  			stream_fifo_push(cache,  					 stream_fifo_pop(client->obuf_fifo));  	} -	pthread_mutex_unlock(&client->obuf_mtx);  	if (cache->tail) {  		msg = cache->tail; @@ -427,13 +425,11 @@ static int zserv_read(struct thread *thread)  				      memory_order_relaxed);  		/* publish read packets on client's input queue */ -		pthread_mutex_lock(&client->ibuf_mtx); -		{ +		frr_with_mutex(&client->ibuf_mtx) {  			while (cache->head)  				stream_fifo_push(client->ibuf_fifo,  						 stream_fifo_pop(cache));  		} -		pthread_mutex_unlock(&client->ibuf_mtx);  		/* Schedule job to process those packets */  		zserv_event(client, ZSERV_PROCESS_MESSAGES); @@ -499,8 +495,7 @@ static int zserv_process_messages(struct thread *thread)  	uint32_t p2p = zrouter.packets_to_process;  	bool need_resched = false; -	pthread_mutex_lock(&client->ibuf_mtx); -	{ +	frr_with_mutex(&client->ibuf_mtx) {  		uint32_t i;  		for (i = 0; i < p2p && stream_fifo_head(client->ibuf_fifo);  		     ++i) { @@ -516,7 +511,6 @@ static int zserv_process_messages(struct thread *thread)  		if (stream_fifo_head(client->ibuf_fifo))  			need_resched = true;  	} -	pthread_mutex_unlock(&client->ibuf_mtx);  	while (stream_fifo_head(cache)) {  		msg = stream_fifo_pop(cache); @@ -535,11 +529,9 @@ static int zserv_process_messages(struct thread *thread)  int zserv_send_message(struct zserv *client, struct stream *msg)  { -	pthread_mutex_lock(&client->obuf_mtx); -	{ +	frr_with_mutex(&client->obuf_mtx) {  		stream_fifo_push(client->obuf_fifo, msg);  	} -	pthread_mutex_unlock(&client->obuf_mtx);  	zserv_client_event(client, ZSERV_CLIENT_WRITE); @@ -790,7 +782,7 @@ void zserv_start(char *path)  	setsockopt_so_recvbuf(zsock, 1048576);  	setsockopt_so_sendbuf(zsock, 1048576); -	frr_elevate_privs((sa.ss_family != AF_UNIX) ? &zserv_privs : NULL) { +	frr_with_privs((sa.ss_family != AF_UNIX) ? &zserv_privs : NULL) {  		ret = bind(zsock, (struct sockaddr *)&sa, sa_len);  	}  	if (ret < 0) {  | 
