summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_advertise.c1
-rw-r--r--bgpd/bgp_advertise.h3
-rw-r--r--bgpd/bgp_aspath.c2
-rw-r--r--bgpd/bgp_attr.c3
-rw-r--r--bgpd/bgp_bmp.c2240
-rw-r--r--bgpd/bgp_bmp.h303
-rw-r--r--bgpd/bgp_evpn.c26
-rw-r--r--bgpd/bgp_evpn_private.h5
-rw-r--r--bgpd/bgp_evpn_vty.c181
-rw-r--r--bgpd/bgp_fsm.c46
-rw-r--r--bgpd/bgp_lcommunity.c6
-rw-r--r--bgpd/bgp_nexthop.c13
-rw-r--r--bgpd/bgp_open.c2
-rw-r--r--bgpd/bgp_packet.c5
-rw-r--r--bgpd/bgp_packet.h3
-rw-r--r--bgpd/bgp_route.c108
-rw-r--r--bgpd/bgp_routemap.c62
-rw-r--r--bgpd/bgp_rpki.c24
-rw-r--r--bgpd/bgp_table.c14
-rw-r--r--bgpd/bgp_vpn.c4
-rw-r--r--bgpd/bgp_vty.c974
-rw-r--r--bgpd/bgp_vty.h5
-rw-r--r--bgpd/bgpd.c60
-rw-r--r--bgpd/bgpd.h7
-rw-r--r--bgpd/subdir.am11
-rwxr-xr-xconfigure.ac77
-rw-r--r--debian/frr.install1
-rw-r--r--doc/developer/library.rst1
-rw-r--r--doc/developer/lists.rst32
-rw-r--r--doc/developer/rcu.rst269
-rw-r--r--doc/developer/subdir.am1
-rw-r--r--doc/user/bgp.rst55
-rw-r--r--doc/user/bmp.rst170
-rw-r--r--doc/user/index.rst1
-rw-r--r--doc/user/subdir.am1
-rw-r--r--isisd/isis_bfd.c129
-rw-r--r--isisd/isis_cli.c2
-rw-r--r--isisd/isis_memory.c1
-rw-r--r--isisd/isis_memory.h1
-rw-r--r--isisd/isis_northbound.c15
-rw-r--r--isisd/isis_route.c249
-rw-r--r--isisd/isis_route.h12
-rw-r--r--isisd/isis_zebra.c60
-rw-r--r--isisd/isisd.c1
-rw-r--r--isisd/isisd.h3
-rw-r--r--lib/command.c3
-rw-r--r--lib/command.h1
-rw-r--r--lib/ferr.c2
-rw-r--r--lib/frr_pthread.c15
-rw-r--r--lib/frr_pthread.h3
-rw-r--r--lib/frrcu.c526
-rw-r--r--lib/frrcu.h172
-rw-r--r--lib/if.c99
-rw-r--r--lib/if.h9
-rw-r--r--lib/libfrr.c4
-rw-r--r--lib/log.c4
-rw-r--r--lib/monotime.h14
-rw-r--r--lib/northbound.h9
-rw-r--r--lib/northbound_cli.c14
-rw-r--r--lib/pqueue.c185
-rw-r--r--lib/pqueue.h54
-rw-r--r--lib/prefix.c8
-rw-r--r--lib/pullwr.c275
-rw-r--r--lib/pullwr.h110
-rw-r--r--lib/seqlock.c190
-rw-r--r--lib/seqlock.h40
-rw-r--r--lib/sigevent.c42
-rw-r--r--lib/stream.c22
-rw-r--r--lib/stream.h8
-rw-r--r--lib/subdir.am6
-rw-r--r--lib/thread.c106
-rw-r--r--lib/thread.h7
-rw-r--r--lib/vty.c9
-rw-r--r--lib/zclient.c11
-rw-r--r--lib/zebra.h5
-rw-r--r--m4/ax_python.m46
-rw-r--r--ospf6d/ospf6_zebra.c2
-rw-r--r--ospfd/ospf_errors.c2
-rw-r--r--ospfd/ospf_packet.c30
-rw-r--r--ospfd/ospf_vty.c67
-rw-r--r--ospfd/ospf_zebra.c3
-rw-r--r--pbrd/pbrd.conf.sample16
-rw-r--r--pimd/pim_bsm.c4
-rw-r--r--pimd/pim_neighbor.c5
-rw-r--r--pimd/pim_nht.c12
-rw-r--r--python/clidef.py1
-rw-r--r--redhat/frr.spec.in3
-rw-r--r--ripd/ripd.c30
-rw-r--r--sharpd/sharpd.conf.sample3
-rw-r--r--staticd/static_routes.c24
-rw-r--r--staticd/staticd.conf.sample4
-rw-r--r--tests/bgpd/test_bgp_table.c2
-rw-r--r--tests/bgpd/test_bgp_table.py2
-rw-r--r--tests/bgpd/test_peer_attr.c6
-rw-r--r--tests/lib/cxxcompat.c1
-rw-r--r--tests/lib/test_atomlist.c10
-rw-r--r--tests/lib/test_seqlock.c34
-rw-r--r--tests/lib/test_timer_correctness.c1
-rw-r--r--tests/lib/test_timer_performance.c1
-rw-r--r--tests/topotests/Dockerfile8
-rw-r--r--tests/topotests/all-protocol-startup/r1/ipv4_routes.ref8
-rw-r--r--tests/topotests/all-protocol-startup/r1/ipv6_routes.ref6
-rw-r--r--tests/topotests/all-protocol-startup/r1/zebra.conf20
-rwxr-xr-xtests/topotests/bgp-basic-functionality-topo1/test_bgp_basic_functionality.py2
-rwxr-xr-xtests/topotests/bgp-ecmp-topo2/ebgp_ecmp_topo2.json664
-rwxr-xr-xtests/topotests/bgp-ecmp-topo2/ibgp_ecmp_topo2.json674
-rwxr-xr-xtests/topotests/bgp-ecmp-topo2/test_ebgp_ecmp_topo2.py817
-rwxr-xr-xtests/topotests/bgp-ecmp-topo2/test_ibgp_ecmp_topo2.py813
-rwxr-xr-xtests/topotests/bgp-path-attributes-topo1/test_bgp_path_attributes.py6
-rwxr-xr-xtests/topotests/bgp-prefix-list-topo1/test_prefix_lists.py34
-rw-r--r--tests/topotests/bgp_comm-list_delete/test_bgp_comm-list_delete.py2
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/adjacencies.py14
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py8
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/scripts/adjacencies.py8
-rwxr-xr-xtests/topotests/docker/inner/compile_frr.sh1
-rwxr-xr-xtests/topotests/example-topojson-test/test_topo_json_single_link_loopback/test_example_topojson.py2
-rw-r--r--tests/topotests/lib/bgp.py559
-rw-r--r--tests/topotests/lib/common_config.py417
-rw-r--r--tests/topotests/lib/topojson.py25
-rw-r--r--tests/topotests/pytest.ini4
-rw-r--r--vtysh/vtysh.c56
-rw-r--r--yang/frr-bfdd.yang12
-rw-r--r--yang/frr-isisd.yang22
-rw-r--r--zebra/connected.c92
-rw-r--r--zebra/connected.h8
-rw-r--r--zebra/if_netlink.c32
-rw-r--r--zebra/interface.c41
-rw-r--r--zebra/redistribute.c14
-rw-r--r--zebra/rt.h10
-rw-r--r--zebra/rt_netlink.c186
-rw-r--r--zebra/rt_socket.c14
-rw-r--r--zebra/zebra_dplane.c305
-rw-r--r--zebra/zebra_dplane.h31
-rw-r--r--zebra/zebra_errors.c2
-rw-r--r--zebra/zebra_fpm_netlink.c2
-rw-r--r--zebra/zebra_l2.c11
-rw-r--r--zebra/zebra_l2.h2
-rw-r--r--zebra/zebra_mpls_openbsd.c4
-rw-r--r--zebra/zebra_nhg.c30
-rw-r--r--zebra/zebra_rib.c7
-rw-r--r--zebra/zebra_vty.c6
-rw-r--r--zebra/zebra_vxlan.c88
-rw-r--r--zebra/zebra_vxlan.h3
143 files changed, 10290 insertions, 2191 deletions
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_aspath.c b/bgpd/bgp_aspath.c
index 1385345d64..f5652b07c5 100644
--- a/bgpd/bgp_aspath.c
+++ b/bgpd/bgp_aspath.c
@@ -1466,7 +1466,7 @@ struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2)
/* Not reached */
}
-/* Iterate over AS_PATH segments and wipe all occurences of the
+/* Iterate over AS_PATH segments and wipe all occurrences of the
* listed AS numbers. Hence some segments may lose some or even
* all data on the way, the operation is implemented as a smarter
* version of aspath_dup(), which allocates memory to hold the new
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index c64d153f1b..e21c84355e 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -2956,7 +2956,8 @@ void bgp_packet_mpattr_prefix(struct stream *s, afi_t afi, safi_t safi,
addpath_encode, addpath_tx_id);
} else if (safi == SAFI_LABELED_UNICAST) {
/* Prefix write with label. */
- stream_put_labeled_prefix(s, p, label);
+ stream_put_labeled_prefix(s, p, label, addpath_encode,
+ addpath_tx_id);
} else if (safi == SAFI_FLOWSPEC) {
if (PSIZE (p->prefixlen)+2 < FLOWSPEC_NLRI_SIZELIMIT)
stream_putc(s, PSIZE (p->prefixlen)+2);
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 3bc3d74de6..4d02e39ae2 100644
--- a/bgpd/bgp_evpn.c
+++ b/bgpd/bgp_evpn.c
@@ -4459,7 +4459,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);
@@ -4471,11 +4472,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,
@@ -4485,7 +4486,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);
@@ -4509,11 +4511,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 a22082c072..30380f6add 100644
--- a/bgpd/bgp_evpn_vty.c
+++ b/bgpd/bgp_evpn_vty.c
@@ -1069,11 +1069,9 @@ static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd,
pi = pi->next) {
total_count++;
if (type == bgp_show_type_neighbor) {
- union sockunion *su = output_arg;
+ struct peer *peer = output_arg;
- if (pi->peer->su_remote == NULL
- || !sockunion_same(
- pi->peer->su_remote, su))
+ if (peer_cmp(peer, pi->peer) != 0)
continue;
}
if (header) {
@@ -1292,29 +1290,41 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_tags,
0);
}
-DEFUN(show_ip_bgp_l2vpn_evpn_all_neighbor_routes,
- show_ip_bgp_l2vpn_evpn_all_neighbor_routes_cmd,
- "show [ip] bgp l2vpn evpn all neighbors A.B.C.D routes [json]",
+DEFUN(show_ip_bgp_l2vpn_evpn_neighbor_routes,
+ show_ip_bgp_l2vpn_evpn_neighbor_routes_cmd,
+ "show [ip] bgp l2vpn evpn neighbors <A.B.C.D|X:X::X:X|WORD> routes [json]",
SHOW_STR
IP_STR
BGP_STR
L2VPN_HELP_STR
EVPN_HELP_STR
- "Display information about all EVPN NLRIs\n"
"Detailed information on TCP and BGP neighbor connections\n"
- "Neighbor to display information about\n"
+ "IPv4 Neighbor to display information about\n"
+ "IPv6 Neighbor to display information about\n"
+ "Neighbor on BGP configured interface\n"
"Display routes learned from neighbor\n" JSON_STR)
{
- int idx_ipv4 = 0;
- union sockunion su;
+ int idx = 0;
struct peer *peer;
- int ret;
+ char *peerstr = NULL;
bool uj = use_json(argc, argv);
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct bgp *bgp = NULL;
- argv_find(argv, argc, "A.B.C.D", &idx_ipv4);
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+ if (!idx) {
+ vty_out(vty, "No index\n");
+ return CMD_WARNING;
+ }
+
+ /* neighbors <A.B.C.D|X:X::X:X|WORD> */
+ argv_find(argv, argc, "neighbors", &idx);
+ peerstr = argv[++idx]->arg;
- ret = str2sockunion(argv[idx_ipv4]->arg, &su);
- if (ret < 0) {
+ peer = peer_lookup_in_view(vty, bgp, peerstr, uj);
+ if (!peer) {
if (uj) {
json_object *json_no = NULL;
json_no = json_object_new_object();
@@ -1325,11 +1335,9 @@ DEFUN(show_ip_bgp_l2vpn_evpn_all_neighbor_routes,
json_object_free(json_no);
} else
vty_out(vty, "Malformed address: %s\n",
- argv[idx_ipv4]->arg);
+ argv[idx]->arg);
return CMD_WARNING;
}
-
- peer = peer_lookup(NULL, &su);
if (!peer || !peer->afc[AFI_L2VPN][SAFI_EVPN]) {
if (uj) {
json_object *json_no = NULL;
@@ -1345,13 +1353,13 @@ DEFUN(show_ip_bgp_l2vpn_evpn_all_neighbor_routes,
return CMD_WARNING;
}
- return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_neighbor, &su, 0,
+ return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_neighbor, peer, 0,
uj);
}
DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_routes,
show_ip_bgp_l2vpn_evpn_rd_neighbor_routes_cmd,
- "show [ip] bgp l2vpn evpn rd ASN:NN_OR_IP-ADDRESS:NN neighbors A.B.C.D routes [json]",
+ "show [ip] bgp l2vpn evpn rd ASN:NN_OR_IP-ADDRESS:NN neighbors <A.B.C.D|X:X::X:X|WORD> routes [json]",
SHOW_STR
IP_STR
BGP_STR
@@ -1360,20 +1368,30 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_routes,
"Display information for a route distinguisher\n"
"VPN Route Distinguisher\n"
"Detailed information on TCP and BGP neighbor connections\n"
- "Neighbor to display information about\n"
+ "IPv4 Neighbor to display information about\n"
+ "IPv6 Neighbor to display information about\n"
+ "Neighbor on BGP configured interface\n"
"Display routes learned from neighbor\n" JSON_STR)
{
int idx_ext_community = 0;
- int idx_ipv4 = 0;
+ int idx = 0;
int ret;
- union sockunion su;
struct peer *peer;
+ char *peerstr = NULL;
struct prefix_rd prd;
bool uj = use_json(argc, argv);
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct bgp *bgp = NULL;
- argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", &idx_ext_community);
- argv_find(argv, argc, "A.B.C.D", &idx_ipv4);
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+ if (!idx) {
+ vty_out(vty, "No index\n");
+ return CMD_WARNING;
+ }
+ argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", &idx_ext_community);
ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
if (!ret) {
if (uj) {
@@ -1389,8 +1407,12 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_routes,
return CMD_WARNING;
}
- ret = str2sockunion(argv[idx_ipv4]->arg, &su);
- if (ret < 0) {
+ /* neighbors <A.B.C.D|X:X::X:X|WORD> */
+ argv_find(argv, argc, "neighbors", &idx);
+ peerstr = argv[++idx]->arg;
+
+ peer = peer_lookup_in_view(vty, bgp, peerstr, uj);
+ if (!peer) {
if (uj) {
json_object *json_no = NULL;
json_no = json_object_new_object();
@@ -1401,11 +1423,9 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_routes,
json_object_free(json_no);
} else
vty_out(vty, "Malformed address: %s\n",
- argv[idx_ext_community]->arg);
+ argv[idx]->arg);
return CMD_WARNING;
}
-
- peer = peer_lookup(NULL, &su);
if (!peer || !peer->afc[AFI_L2VPN][SAFI_EVPN]) {
if (uj) {
json_object *json_no = NULL;
@@ -1421,33 +1441,48 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_routes,
return CMD_WARNING;
}
- return bgp_show_ethernet_vpn(vty, &prd, bgp_show_type_neighbor, &su, 0,
+ return bgp_show_ethernet_vpn(vty, &prd, bgp_show_type_neighbor, peer, 0,
uj);
}
-DEFUN(show_ip_bgp_l2vpn_evpn_all_neighbor_advertised_routes,
- show_ip_bgp_l2vpn_evpn_all_neighbor_advertised_routes_cmd,
- "show [ip] bgp l2vpn evpn all neighbors A.B.C.D advertised-routes [json]",
+DEFUN(show_ip_bgp_l2vpn_evpn_neighbor_advertised_routes,
+ show_ip_bgp_l2vpn_evpn_neighbor_advertised_routes_cmd,
+ "show [ip] bgp l2vpn evpn neighbors <A.B.C.D|X:X::X:X|WORD> advertised-routes [json]",
SHOW_STR
IP_STR
BGP_STR
L2VPN_HELP_STR
EVPN_HELP_STR
- "Display information about all EVPN NLRIs\n"
"Detailed information on TCP and BGP neighbor connections\n"
- "Neighbor to display information about\n"
+ "IPv4 Neighbor to display information about\n"
+ "IPv6 Neighbor to display information about\n"
+ "Neighbor on BGP configured interface\n"
"Display the routes advertised to a BGP neighbor\n" JSON_STR)
{
- int idx_ipv4 = 0;
- int ret;
+ int idx = 0;
struct peer *peer;
- union sockunion su;
bool uj = use_json(argc, argv);
+ struct bgp *bgp = NULL;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ char *peerstr = NULL;
+
+ if (uj)
+ argc--;
- argv_find(argv, argc, "A.B.C.D", &idx_ipv4);
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+ if (!idx) {
+ vty_out(vty, "No index\n");
+ return CMD_WARNING;
+ }
+
+ /* neighbors <A.B.C.D|X:X::X:X|WORD> */
+ argv_find(argv, argc, "neighbors", &idx);
+ peerstr = argv[++idx]->arg;
- ret = str2sockunion(argv[idx_ipv4]->arg, &su);
- if (ret < 0) {
+ peer = peer_lookup_in_view(vty, bgp, peerstr, uj);
+ if (!peer) {
if (uj) {
json_object *json_no = NULL;
json_no = json_object_new_object();
@@ -1458,10 +1493,9 @@ DEFUN(show_ip_bgp_l2vpn_evpn_all_neighbor_advertised_routes,
json_object_free(json_no);
} else
vty_out(vty, "Malformed address: %s\n",
- argv[idx_ipv4]->arg);
+ argv[idx]->arg);
return CMD_WARNING;
}
- peer = peer_lookup(NULL, &su);
if (!peer || !peer->afc[AFI_L2VPN][SAFI_EVPN]) {
if (uj) {
json_object *json_no = NULL;
@@ -1482,7 +1516,7 @@ DEFUN(show_ip_bgp_l2vpn_evpn_all_neighbor_advertised_routes,
DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes,
show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes_cmd,
- "show [ip] bgp l2vpn evpn rd ASN:NN_OR_IP-ADDRESS:NN neighbors A.B.C.D advertised-routes [json]",
+ "show [ip] bgp l2vpn evpn rd ASN:NN_OR_IP-ADDRESS:NN neighbors <A.B.C.D|X:X::X:X|WORD> advertised-routes [json]",
SHOW_STR
IP_STR
BGP_STR
@@ -1491,22 +1525,43 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes,
"Display information for a route distinguisher\n"
"VPN Route Distinguisher\n"
"Detailed information on TCP and BGP neighbor connections\n"
- "Neighbor to display information about\n"
+ "IPv4 Neighbor to display information about\n"
+ "IPv6 Neighbor to display information about\n"
+ "Neighbor on BGP configured interface\n"
"Display the routes advertised to a BGP neighbor\n" JSON_STR)
{
int idx_ext_community = 0;
- int idx_ipv4 = 0;
+ int idx = 0;
int ret;
struct peer *peer;
struct prefix_rd prd;
- union sockunion su;
+ struct bgp *bgp = NULL;
bool uj = use_json(argc, argv);
+ char *peerstr = NULL;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+
+ if (uj)
+ argc--;
+
+ if (uj)
+ argc--;
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+ if (!idx) {
+ vty_out(vty, "No index\n");
+ return CMD_WARNING;
+ }
argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", &idx_ext_community);
- argv_find(argv, argc, "A.B.C.D", &idx_ipv4);
- ret = str2sockunion(argv[idx_ipv4]->arg, &su);
- if (ret < 0) {
+ /* neighbors <A.B.C.D|X:X::X:X|WORD> */
+ argv_find(argv, argc, "neighbors", &idx);
+ peerstr = argv[++idx]->arg;
+
+ peer = peer_lookup_in_view(vty, bgp, peerstr, uj);
+ if (!peer) {
if (uj) {
json_object *json_no = NULL;
json_no = json_object_new_object();
@@ -1517,10 +1572,9 @@ DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes,
json_object_free(json_no);
} else
vty_out(vty, "Malformed address: %s\n",
- argv[idx_ext_community]->arg);
+ argv[idx]->arg);
return CMD_WARNING;
}
- peer = peer_lookup(NULL, &su);
if (!peer || !peer->afc[AFI_L2VPN][SAFI_EVPN]) {
if (uj) {
json_object *json_no = NULL;
@@ -1599,7 +1653,7 @@ DEFUN(show_ip_bgp_evpn_rd_overlay,
use_json(argc, argv));
}
-/* For testing purpose, static route of MPLS-VPN. */
+/* For testing purpose, static route of EVPN RT-5. */
DEFUN(evpnrt5_network,
evpnrt5_network_cmd,
"network <A.B.C.D/M|X:X::X:X/M> rd ASN:NN_OR_IP-ADDRESS:NN ethtag WORD label WORD esi WORD gwip <A.B.C.D|X:X::X:X> routermac WORD [route-map WORD]",
@@ -1638,7 +1692,7 @@ DEFUN(evpnrt5_network,
argv[idx_routermac]->arg);
}
-/* For testing purpose, static route of MPLS-VPN. */
+/* For testing purpose, static route of EVPN RT-5. */
DEFUN(no_evpnrt5_network,
no_evpnrt5_network_cmd,
"no network <A.B.C.D/M|X:X::X:X/M> rd ASN:NN_OR_IP-ADDRESS:NN ethtag WORD label WORD esi WORD gwip <A.B.C.D|X:X::X:X>",
@@ -3642,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"
@@ -3650,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);
}
/*
@@ -3703,9 +3762,9 @@ DEFUN(show_bgp_l2vpn_evpn_route,
type = BGP_EVPN_MAC_IP_ROUTE;
else if (strncmp(argv[type_idx + 1]->arg, "mu", 2) == 0)
type = BGP_EVPN_IMET_ROUTE;
- else if (strncmp(argv[type_idx + 1]->arg, "es", 2) == 0)
+ else if (strncmp(argv[type_idx + 1]->arg, "e", 1) == 0)
type = BGP_EVPN_ES_ROUTE;
- else if (strncmp(argv[type_idx + 1]->arg, "pr", 2) == 0)
+ else if (strncmp(argv[type_idx + 1]->arg, "p", 1) == 0)
type = BGP_EVPN_IP_PREFIX_ROUTE;
else
return CMD_WARNING;
@@ -5323,12 +5382,12 @@ void bgp_ethernetvpn_init(void)
install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_all_tags_cmd);
install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_rd_tags_cmd);
install_element(VIEW_NODE,
- &show_ip_bgp_l2vpn_evpn_all_neighbor_routes_cmd);
+ &show_ip_bgp_l2vpn_evpn_neighbor_routes_cmd);
install_element(VIEW_NODE,
&show_ip_bgp_l2vpn_evpn_rd_neighbor_routes_cmd);
install_element(
VIEW_NODE,
- &show_ip_bgp_l2vpn_evpn_all_neighbor_advertised_routes_cmd);
+ &show_ip_bgp_l2vpn_evpn_neighbor_advertised_routes_cmd);
install_element(
VIEW_NODE,
&show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes_cmd);
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index 4348e6b240..4d395e3749 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -96,6 +96,21 @@ static int bgp_holdtime_timer(struct thread *);
/* BGP FSM functions. */
static int bgp_start(struct peer *);
+/* Register peer with NHT */
+static int bgp_peer_reg_with_nht(struct peer *peer)
+{
+ int connected = 0;
+
+ if (peer->sort == BGP_PEER_EBGP && peer->ttl == 1
+ && !CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)
+ && !bgp_flag_check(peer->bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK))
+ connected = 1;
+
+ return bgp_find_or_add_nexthop(
+ peer->bgp, peer->bgp, family2afi(peer->su.sa.sa_family),
+ NULL, peer, connected);
+}
+
static void peer_xfer_stats(struct peer *peer_dst, struct peer *peer_src)
{
/* Copy stats over. These are only the pre-established state stats */
@@ -229,6 +244,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) {
@@ -293,6 +309,11 @@ static struct peer *peer_xfer_conn(struct peer *from_peer)
if (from_peer)
peer_xfer_stats(peer, from_peer);
+ /* Register peer for NHT. This is to allow RAs to be enabled when
+ * needed, even on a passive connection.
+ */
+ bgp_peer_reg_with_nht(peer);
+
bgp_reads_on(peer);
bgp_writes_on(peer);
thread_add_timer_msec(bm->master, bgp_process_packet, peer, 0,
@@ -531,7 +552,10 @@ 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 Addr",
+ "Waiting for VRF to be initialized"};
static int bgp_graceful_restart_timer_expire(struct thread *thread)
{
@@ -1382,7 +1406,6 @@ static int bgp_connect_fail(struct peer *peer)
int bgp_start(struct peer *peer)
{
int status;
- int connected = 0;
bgp_peer_conf_if_to_su_update(peer);
@@ -1391,6 +1414,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;
}
@@ -1436,25 +1460,19 @@ 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;
}
- /* Register to be notified on peer up */
- if (peer->sort == BGP_PEER_EBGP && peer->ttl == 1
- && !CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)
- && !bgp_flag_check(peer->bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK))
- connected = 1;
- else
- connected = 0;
-
- if (!bgp_find_or_add_nexthop(peer->bgp, peer->bgp,
- family2afi(peer->su.sa.sa_family), NULL,
- peer, connected)) {
+ /* Register peer for NHT. If next hop is already resolved, proceed
+ * with connection setup, else wait.
+ */
+ if (!bgp_peer_reg_with_nht(peer)) {
if (bgp_zebra_num_connects()) {
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_lcommunity.c b/bgpd/bgp_lcommunity.c
index 2b09a2954e..aeb290719a 100644
--- a/bgpd/bgp_lcommunity.c
+++ b/bgpd/bgp_lcommunity.c
@@ -439,7 +439,8 @@ struct lcommunity *lcommunity_str2com(const char *str)
enum lcommunity_token token = lcommunity_token_unknown;
struct lcommunity_val lval;
- while ((str = lcommunity_gettoken(str, &lval, &token))) {
+ do {
+ str = lcommunity_gettoken(str, &lval, &token);
switch (token) {
case lcommunity_token_val:
if (lcom == NULL)
@@ -452,7 +453,8 @@ struct lcommunity *lcommunity_str2com(const char *str)
lcommunity_free(&lcom);
return NULL;
}
- }
+ } while (str);
+
return lcom;
}
diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c
index a8c507832c..812f0ce16e 100644
--- a/bgpd/bgp_nexthop.c
+++ b/bgpd/bgp_nexthop.c
@@ -680,17 +680,23 @@ static void bgp_show_nexthops(struct vty *vty, struct bgp *bgp, int detail,
continue;
for (rn = bgp_table_top(table[afi]); rn;
rn = bgp_route_next(rn)) {
+ struct peer *peer;
+
bnc = bgp_node_get_bgp_nexthop_info(rn);
if (!bnc)
continue;
+ peer = (struct peer *)bnc->nht_info;
if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID)) {
vty_out(vty,
- " %s valid [IGP metric %d], #paths %d\n",
+ " %s valid [IGP metric %d], #paths %d",
inet_ntop(rn->p.family,
&rn->p.u.prefix, buf,
sizeof(buf)),
bnc->metric, bnc->path_count);
+ if (peer)
+ vty_out(vty, ", peer %s", peer->host);
+ vty_out(vty, "\n");
if (!detail)
continue;
@@ -698,10 +704,13 @@ static void bgp_show_nexthops(struct vty *vty, struct bgp *bgp, int detail,
bgp_show_nexthops_detail(vty, bgp, bnc);
} else {
- vty_out(vty, " %s invalid\n",
+ vty_out(vty, " %s invalid",
inet_ntop(rn->p.family,
&rn->p.u.prefix, buf,
sizeof(buf)));
+ if (peer)
+ vty_out(vty, ", peer %s", peer->host);
+ vty_out(vty, "\n");
if (CHECK_FLAG(bnc->flags,
BGP_NEXTHOP_CONNECTED))
vty_out(vty, " Must be Connected\n");
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..cd94f421ef 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"
@@ -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);
@@ -1660,7 +1661,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);
}
}
diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h
index e8eacee589..fc6fc66a4c 100644
--- a/bgpd/bgp_packet.h
+++ b/bgpd/bgp_packet.h
@@ -57,9 +57,6 @@ extern void bgp_notify_send_with_data(struct peer *, uint8_t, uint8_t,
extern void bgp_route_refresh_send(struct peer *, afi_t, safi_t, uint8_t,
uint8_t, int);
extern void bgp_capability_send(struct peer *, afi_t, safi_t, int, int);
-extern void bgp_default_update_send(struct peer *, struct attr *, afi_t, safi_t,
- struct peer *);
-extern void bgp_default_withdraw_send(struct peer *, afi_t, safi_t);
extern int bgp_capability_receive(struct peer *, bgp_size_t);
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index a5591e29f2..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);
@@ -5332,6 +5332,13 @@ static void bgp_purge_af_static_redist_routes(struct bgp *bgp, afi_t afi,
struct bgp_node *rn;
struct bgp_path_info *pi;
+ /* Do not install the aggregate route if BGP is in the
+ * process of termination.
+ */
+ if (bgp_flag_check(bgp, BGP_FLAG_DELETE_IN_PROGRESS) ||
+ (bgp->peer_self == NULL))
+ return;
+
table = bgp->rib[afi][safi];
for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) {
for (pi = bgp_node_get_bgp_path_info(rn); pi; pi = pi->next) {
@@ -8158,20 +8165,25 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
json_nexthop_global = json_object_new_object();
}
- if (!json_paths && safi == SAFI_EVPN) {
+ if (!json_paths && path->extra) {
char tag_buf[30];
- bgp_evpn_route2str((struct prefix_evpn *)&bn->p,
- buf2, sizeof(buf2));
- vty_out(vty, " Route %s", buf2);
+ buf2[0] = '\0';
tag_buf[0] = '\0';
if (path->extra && path->extra->num_labels) {
bgp_evpn_label2str(path->extra->label,
path->extra->num_labels, tag_buf,
sizeof(tag_buf));
- vty_out(vty, " VNI %s", tag_buf);
}
- vty_out(vty, "\n");
+ if (safi == SAFI_EVPN) {
+ bgp_evpn_route2str((struct prefix_evpn *)&bn->p,
+ buf2, sizeof(buf2));
+ vty_out(vty, " Route %s", buf2);
+ if (tag_buf[0] != '\0')
+ vty_out(vty, " VNI %s", tag_buf);
+ vty_out(vty, "\n");
+ }
+
if (path->extra && path->extra->parent) {
struct bgp_path_info *parent_ri;
struct bgp_node *rn, *prn;
@@ -8180,11 +8192,14 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
rn = parent_ri->net;
if (rn && rn->prn) {
prn = rn->prn;
- vty_out(vty, " Imported from %s:%s\n",
- prefix_rd2str(
- (struct prefix_rd *)&prn->p,
- buf1, sizeof(buf1)),
- buf2);
+ prefix_rd2str((struct prefix_rd *)&prn->p,
+ buf1, sizeof(buf1));
+ if (is_pi_family_evpn(parent_ri)) {
+ bgp_evpn_route2str((struct prefix_evpn *)&rn->p,
+ buf2, sizeof(buf2));
+ vty_out(vty, " Imported from %s:%s, VNI %s\n", buf1, buf2, tag_buf);
+ } else
+ vty_out(vty, " Imported from %s:%s\n", buf1, buf2);
}
}
}
@@ -10493,63 +10508,6 @@ static int bgp_show_prefix_longer(struct vty *vty, struct bgp *bgp,
return ret;
}
-static struct peer *peer_lookup_in_view(struct vty *vty, struct bgp *bgp,
- const char *ip_str, bool use_json)
-{
- int ret;
- struct peer *peer;
- union sockunion su;
-
- /* Get peer sockunion. */
- ret = str2sockunion(ip_str, &su);
- if (ret < 0) {
- peer = peer_lookup_by_conf_if(bgp, ip_str);
- if (!peer) {
- peer = peer_lookup_by_hostname(bgp, ip_str);
-
- if (!peer) {
- if (use_json) {
- json_object *json_no = NULL;
- json_no = json_object_new_object();
- json_object_string_add(
- json_no,
- "malformedAddressOrName",
- ip_str);
- vty_out(vty, "%s\n",
- json_object_to_json_string_ext(
- json_no,
- JSON_C_TO_STRING_PRETTY));
- json_object_free(json_no);
- } else
- vty_out(vty,
- "%% Malformed address or name: %s\n",
- ip_str);
- return NULL;
- }
- }
- return peer;
- }
-
- /* Peer structure lookup. */
- peer = peer_lookup(bgp, &su);
- if (!peer) {
- if (use_json) {
- json_object *json_no = NULL;
- json_no = json_object_new_object();
- json_object_string_add(json_no, "warning",
- "No such neighbor in this view/vrf");
- vty_out(vty, "%s\n",
- json_object_to_json_string_ext(
- json_no, JSON_C_TO_STRING_PRETTY));
- json_object_free(json_no);
- } else
- vty_out(vty, "No such neighbor in this view/vrf\n");
- return NULL;
- }
-
- return peer;
-}
-
enum bgp_stats {
BGP_STATS_MAXBITLEN = 0,
BGP_STATS_RIB,
@@ -10738,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)
@@ -10937,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]);
@@ -10963,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]);
@@ -11594,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 5ffc416dc5..aaae9e380e 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -3406,31 +3406,34 @@ int bgp_route_map_update_timer(struct thread *thread)
static void bgp_route_map_mark_update(const char *rmap_name)
{
- if (bm->t_rmap_update == NULL) {
- struct listnode *node, *nnode;
- struct bgp *bgp;
-
- /* rmap_update_timer of 0 means don't do route updates */
- if (bm->rmap_update_timer) {
- bm->t_rmap_update = NULL;
- thread_add_timer(bm->master, bgp_route_map_update_timer,
- NULL, bm->rmap_update_timer,
- &bm->t_rmap_update);
-
- /* Signal the groups that a route-map update event has
- * started */
- for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
- update_group_policy_update(bgp,
- BGP_POLICY_ROUTE_MAP,
- rmap_name, 1, 1);
- } else {
- for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
- bgp_route_map_process_update(bgp, rmap_name, 0);
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+
+ /* If new update is received before the current timer timed out,
+ * turn it off and start a new timer.
+ */
+ if (bm->t_rmap_update != NULL)
+ THREAD_OFF(bm->t_rmap_update);
+
+ /* rmap_update_timer of 0 means don't do route updates */
+ if (bm->rmap_update_timer) {
+ thread_add_timer(bm->master, bgp_route_map_update_timer,
+ NULL, bm->rmap_update_timer,
+ &bm->t_rmap_update);
+
+ /* Signal the groups that a route-map update event has
+ * started */
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
+ update_group_policy_update(bgp,
+ BGP_POLICY_ROUTE_MAP,
+ rmap_name, 1, 1);
+ } else {
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
+ bgp_route_map_process_update(bgp, rmap_name, 0);
#if ENABLE_BGP_VNC
- zlog_debug("%s: calling vnc_routemap_update", __func__);
- vnc_routemap_update(bgp, __func__);
+ zlog_debug("%s: calling vnc_routemap_update", __func__);
+ vnc_routemap_update(bgp, __func__);
#endif
- }
}
}
@@ -4155,6 +4158,18 @@ DEFUN (no_set_aspath_prepend,
return ret;
}
+DEFUN (no_set_aspath_prepend_lastas,
+ no_set_aspath_prepend_lastas_cmd,
+ "no set as-path prepend last-as [(1-10)]",
+ NO_STR
+ SET_STR
+ "Transform BGP AS_PATH attribute\n"
+ "Prepend to the as-path\n"
+ "Use the peers AS-number\n"
+ "Number of times to insert\n")
+{
+ return no_set_aspath_prepend(self, vty, argc, argv);
+}
DEFUN (set_aspath_exclude,
set_aspath_exclude_cmd,
@@ -5120,6 +5135,7 @@ void bgp_route_map_init(void)
install_element(RMAP_NODE, &set_aspath_prepend_lastas_cmd);
install_element(RMAP_NODE, &set_aspath_exclude_cmd);
install_element(RMAP_NODE, &no_set_aspath_prepend_cmd);
+ install_element(RMAP_NODE, &no_set_aspath_prepend_lastas_cmd);
install_element(RMAP_NODE, &no_set_aspath_exclude_cmd);
install_element(RMAP_NODE, &no_set_aspath_exclude_all_cmd);
install_element(RMAP_NODE, &set_origin_cmd);
diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c
index 408d423aac..22840d54c6 100644
--- a/bgpd/bgp_rpki.c
+++ b/bgpd/bgp_rpki.c
@@ -142,7 +142,6 @@ static struct rtr_mgr_config *rtr_config;
static struct list *cache_list;
static int rtr_is_running;
static int rtr_is_stopping;
-static int rtr_is_starting;
static _Atomic int rtr_update_overflow;
static int rpki_debug;
static unsigned int polling_period;
@@ -478,7 +477,7 @@ static void rpki_update_cb_sync_rtr(struct pfx_table *p __attribute__((unused)),
const struct pfx_record rec,
const bool added __attribute__((unused)))
{
- if (rtr_is_stopping || rtr_is_starting
+ if (rtr_is_stopping
|| atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst))
return;
@@ -570,11 +569,9 @@ static int bgp_rpki_module_init(void)
static int start(void)
{
- unsigned int waiting_time = 0;
int ret;
rtr_is_stopping = 0;
- rtr_is_starting = 1;
rtr_update_overflow = 0;
if (list_isempty(cache_list)) {
@@ -603,23 +600,6 @@ static int start(void)
return ERROR;
}
rtr_is_running = 1;
- RPKI_DEBUG("Waiting for rtr connection to synchronize.");
- while (waiting_time++ <= initial_synchronisation_timeout) {
- if (rtr_mgr_conf_in_sync(rtr_config))
- break;
-
- sleep(1);
- }
- if (rtr_mgr_conf_in_sync(rtr_config)) {
- RPKI_DEBUG("Got synchronisation with at least one RPKI cache!");
- RPKI_DEBUG("Forcing revalidation.");
- rtr_is_starting = 0;
- revalidate_all_routes();
- } else {
- RPKI_DEBUG(
- "Timeout expired! Proceeding without RPKI validation data.");
- rtr_is_starting = 0;
- }
XFREE(MTYPE_BGP_RPKI_CACHE_GROUP, groups);
@@ -1200,7 +1180,7 @@ DEFPY (show_rpki_prefix,
{
if (!is_synchronized()) {
- vty_out(vty, "No Conection to RPKI cache server.\n");
+ vty_out(vty, "No Connection to RPKI cache server.\n");
return CMD_WARNING;
}
diff --git a/bgpd/bgp_table.c b/bgpd/bgp_table.c
index ecde71279d..53175bfccf 100644
--- a/bgpd/bgp_table.c
+++ b/bgpd/bgp_table.c
@@ -156,8 +156,10 @@ void bgp_table_range_lookup(const struct bgp_table *table, struct prefix *p,
struct bgp_node *node = bgp_node_from_rnode(table->route_table->top);
struct bgp_node *matched = NULL;
- while (node && node->p.prefixlen <= p->prefixlen
- && prefix_match(&node->p, p)) {
+ if (node == NULL)
+ return;
+
+ while (node->p.prefixlen <= p->prefixlen && prefix_match(&node->p, p)) {
if (bgp_node_has_bgp_path_info_data(node)
&& node->p.prefixlen == p->prefixlen) {
matched = node;
@@ -167,10 +169,10 @@ void bgp_table_range_lookup(const struct bgp_table *table, struct prefix *p,
&p->u.prefix, node->p.prefixlen)]);
}
- if (node == NULL)
- return;
-
- if ((matched == NULL && node->p.prefixlen > maxlen) || !node->parent)
+ if (matched == NULL && node->p.prefixlen <= maxlen
+ && prefix_match(p, &node->p) && node->parent == NULL)
+ matched = node;
+ else if ((matched == NULL && node->p.prefixlen > maxlen) || !node->parent)
return;
else if (matched == NULL)
matched = node = bgp_node_from_rnode(node->parent);
diff --git a/bgpd/bgp_vpn.c b/bgpd/bgp_vpn.c
index 54ca980cad..09b1cb429b 100644
--- a/bgpd/bgp_vpn.c
+++ b/bgpd/bgp_vpn.c
@@ -74,7 +74,7 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer,
json_object_string_add(json_ocode, "incomplete", "?");
}
- for (rn = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); rn;
+ for (rn = bgp_table_top(bgp->rib[afi][safi]); rn;
rn = bgp_route_next(rn)) {
if (prd && memcmp(rn->p.u.val, prd->val, 8) != 0)
continue;
@@ -200,7 +200,7 @@ int show_adj_route_vpn(struct vty *vty, struct peer *peer,
json_array);
} else {
route_vty_out_tmp(vty, &rm->p, path->attr,
- SAFI_MPLS_VPN, use_json,
+ safi, use_json,
json_array);
}
}
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 58a202d510..7e7c0ea2d7 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,
@@ -596,12 +671,61 @@ static void bgp_clear_vty_error(struct vty *vty, struct peer *peer, afi_t afi,
}
}
+static int bgp_peer_clear(struct peer *peer, afi_t afi, safi_t safi,
+ struct listnode *nnode, enum bgp_clear_type stype)
+{
+ int ret = 0;
+
+ /* if afi/.safi not specified, spin thru all of them */
+ if ((afi == AFI_UNSPEC) && (safi == SAFI_UNSPEC)) {
+ afi_t tmp_afi;
+ safi_t tmp_safi;
+
+ FOREACH_AFI_SAFI (tmp_afi, tmp_safi) {
+ if (!peer->afc[tmp_afi][tmp_safi])
+ continue;
+
+ if (stype == BGP_CLEAR_SOFT_NONE)
+ ret = peer_clear(peer, &nnode);
+ else
+ ret = peer_clear_soft(peer, tmp_afi, tmp_safi,
+ stype);
+ }
+ /* if afi specified and safi not, spin thru safis on this afi */
+ } else if (safi == SAFI_UNSPEC) {
+ safi_t tmp_safi;
+
+ for (tmp_safi = SAFI_UNICAST;
+ tmp_safi < SAFI_MAX; tmp_safi++) {
+ if (!peer->afc[afi][tmp_safi])
+ continue;
+
+ if (stype == BGP_CLEAR_SOFT_NONE)
+ ret = peer_clear(peer, &nnode);
+ else
+ ret = peer_clear_soft(peer, afi,
+ tmp_safi, stype);
+ }
+ /* both afi/safi specified, let the caller know if not defined */
+ } else {
+ if (!peer->afc[afi][safi])
+ return 1;
+
+ if (stype == BGP_CLEAR_SOFT_NONE)
+ ret = peer_clear(peer, &nnode);
+ else
+ ret = peer_clear_soft(peer, afi, safi, stype);
+ }
+
+ return ret;
+}
+
/* `clear ip bgp' functions. */
static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
enum clear_sort sort, enum bgp_clear_type stype,
const char *arg)
{
- int ret;
+ int ret = 0;
bool found = false;
struct peer *peer;
struct listnode *node, *nnode;
@@ -614,13 +738,8 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
*/
if (sort == clear_all) {
for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
- if (!peer->afc[afi][safi])
- continue;
-
- if (stype == BGP_CLEAR_SOFT_NONE)
- ret = peer_clear(peer, &nnode);
- else
- ret = peer_clear_soft(peer, afi, safi, stype);
+ ret = bgp_peer_clear(peer, afi, safi, nnode,
+ stype);
if (ret < 0)
bgp_clear_vty_error(vty, peer, afi, safi, ret);
@@ -660,12 +779,11 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
}
}
- if (!peer->afc[afi][safi])
+ ret = bgp_peer_clear(peer, afi, safi, NULL, stype);
+
+ /* if afi/safi not defined for this peer, let caller know */
+ if (ret == 1)
ret = BGP_ERR_AF_UNCONFIGURED;
- else if (stype == BGP_CLEAR_SOFT_NONE)
- ret = peer_clear(peer, NULL);
- else
- ret = peer_clear_soft(peer, afi, safi, stype);
if (ret < 0)
bgp_clear_vty_error(vty, peer, afi, safi, ret);
@@ -684,13 +802,7 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
}
for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
- if (!peer->afc[afi][safi])
- continue;
-
- if (stype == BGP_CLEAR_SOFT_NONE)
- ret = peer_clear(peer, NULL);
- else
- ret = peer_clear_soft(peer, afi, safi, stype);
+ ret = bgp_peer_clear(peer, afi, safi, nnode, stype);
if (ret < 0)
bgp_clear_vty_error(vty, peer, afi, safi, ret);
@@ -701,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;
}
@@ -712,13 +824,7 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
if (peer->sort == BGP_PEER_IBGP)
continue;
- if (!peer->afc[afi][safi])
- continue;
-
- if (stype == BGP_CLEAR_SOFT_NONE)
- ret = peer_clear(peer, &nnode);
- else
- ret = peer_clear_soft(peer, afi, safi, stype);
+ ret = bgp_peer_clear(peer, afi, safi, nnode, stype);
if (ret < 0)
bgp_clear_vty_error(vty, peer, afi, safi, ret);
@@ -729,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;
}
@@ -742,12 +848,7 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
if (peer->as != as)
continue;
- if (!peer->afc[afi][safi])
- ret = BGP_ERR_AF_UNCONFIGURED;
- else if (stype == BGP_CLEAR_SOFT_NONE)
- ret = peer_clear(peer, &nnode);
- else
- ret = peer_clear_soft(peer, afi, safi, stype);
+ ret = bgp_peer_clear(peer, afi, safi, nnode, stype);
if (ret < 0)
bgp_clear_vty_error(vty, peer, afi, safi, ret);
@@ -758,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;
}
@@ -6069,7 +6170,7 @@ DEFUN (neighbor_allowas_in,
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"Accept as-path with my AS present in it\n"
- "Number of occurences of AS number\n"
+ "Number of occurrences of AS number\n"
"Only accept my AS in the as-path if the route was originated in my AS\n")
{
int idx_peer = 1;
@@ -6103,7 +6204,7 @@ ALIAS_HIDDEN(
"neighbor <A.B.C.D|X:X::X:X|WORD> allowas-in [<(1-10)|origin>]",
NEIGHBOR_STR NEIGHBOR_ADDR_STR2
"Accept as-path with my AS present in it\n"
- "Number of occurences of AS number\n"
+ "Number of occurrences of AS number\n"
"Only accept my AS in the as-path if the route was originated in my AS\n")
DEFUN (no_neighbor_allowas_in,
@@ -6113,7 +6214,7 @@ DEFUN (no_neighbor_allowas_in,
NEIGHBOR_STR
NEIGHBOR_ADDR_STR2
"allow local ASN appears in aspath attribute\n"
- "Number of occurences of AS number\n"
+ "Number of occurrences of AS number\n"
"Only accept my AS in the as-path if the route was originated in my AS\n")
{
int idx_peer = 2;
@@ -6135,7 +6236,7 @@ ALIAS_HIDDEN(
"no neighbor <A.B.C.D|X:X::X:X|WORD> allowas-in [<(1-10)|origin>]",
NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
"allow local ASN appears in aspath attribute\n"
- "Number of occurences of AS number\n"
+ "Number of occurrences of AS number\n"
"Only accept my AS in the as-path if the route was originated in my AS\n")
DEFUN (neighbor_ttl_security,
@@ -7238,8 +7339,8 @@ DEFUN (clear_ip_bgp_all,
{
char *vrf = NULL;
- afi_t afi = AFI_IP6;
- safi_t safi = SAFI_UNICAST;
+ afi_t afi = AFI_UNSPEC;
+ safi_t safi = SAFI_UNSPEC;
enum clear_sort clr_sort = clear_peer;
enum bgp_clear_type clr_type;
char *clr_arg = NULL;
@@ -7768,9 +7869,143 @@ 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]);
+ } 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;
@@ -7778,7 +8013,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;
@@ -7790,6 +8025,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
@@ -7798,6 +8034,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
@@ -7826,6 +8076,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++;
}
}
@@ -7836,6 +8091,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;
@@ -8037,78 +8309,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",
@@ -8119,65 +8410,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));
@@ -8187,7 +8485,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) {
@@ -8201,7 +8499,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);
@@ -8234,15 +8532,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)
@@ -8264,7 +8565,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;
@@ -8292,7 +8594,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)
@@ -8302,13 +8605,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 {
@@ -8324,7 +8628,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;
}
}
@@ -8332,7 +8636,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");
@@ -8347,7 +8652,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
@@ -8355,11 +8660,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;
@@ -8379,79 +8686,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. */
@@ -8829,14 +9077,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",
@@ -9124,8 +9372,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;
@@ -9543,8 +9789,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]
@@ -9722,9 +9968,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,
@@ -9824,8 +10070,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);
}
}
@@ -9931,9 +10178,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);
}
}
@@ -9992,9 +10240,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
@@ -10003,9 +10252,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
@@ -10035,9 +10285,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
@@ -10046,9 +10297,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
@@ -10119,9 +10371,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));
}
}
@@ -10168,8 +10421,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");
@@ -10254,9 +10509,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]
@@ -10295,8 +10551,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++;
}
}
@@ -10306,8 +10563,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++;
}
}
@@ -10345,8 +10603,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++;
}
}
@@ -10360,8 +10619,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++;
}
}
@@ -10547,88 +10807,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,
@@ -11293,7 +11478,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)) {
@@ -11374,11 +11559,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,
@@ -11402,11 +11587,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,
@@ -11783,7 +11968,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)
@@ -14452,12 +14637,13 @@ ALIAS (show_community_list,
DEFUN (show_community_list_arg,
show_bgp_community_list_arg_cmd,
- "show bgp community-list <(1-500)|WORD>",
+ "show bgp community-list <(1-500)|WORD> detail",
SHOW_STR
BGP_STR
"List community-list\n"
"Community-list number\n"
- "Community-list name\n")
+ "Community-list name\n"
+ "Detailed information on community-list\n")
{
int idx_comm_list = 3;
struct community_list *list;
@@ -14466,8 +14652,8 @@ DEFUN (show_community_list_arg,
if (argv_find(argv, argc, "ip", &idx)) {
vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n");
vty_out(vty, "if you are using this please migrate to the below command.\n");
- vty_out(vty, "'show bgp community-list <(1-500)|WORD>'\n");
- zlog_warn("Deprecated option: 'ip show community-list <(1-500)|WORD>' being used");
+ vty_out(vty, "'show bgp community-list <(1-500)|WORD> detail'\n");
+ zlog_warn("Deprecated option: 'show ip community-list <(1-500)|WORD>' being used");
}
list = community_list_lookup(bgp_clist, argv[idx_comm_list]->arg, 0,
COMMUNITY_LIST_MASTER);
@@ -14606,28 +14792,6 @@ CPP_NOTICE("bgpd: remove deprecated 'ip large-community-list <(1-99)|(100-500)|s
#endif
DEFUN (lcommunity_list_standard,
bgp_lcommunity_list_standard_cmd,
- "bgp large-community-list (1-99) <deny|permit>",
- BGP_STR
- LCOMMUNITY_LIST_STR
- "Large Community list number (standard)\n"
- "Specify large community to reject\n"
- "Specify large community to accept\n")
-{
- return lcommunity_list_set_vty(vty, argc, argv,
- LARGE_COMMUNITY_LIST_STANDARD, 0);
-}
-
-ALIAS (lcommunity_list_standard,
- ip_lcommunity_list_standard_cmd,
- "ip large-community-list (1-99) <deny|permit>",
- IP_STR
- LCOMMUNITY_LIST_STR
- "Large Community list number (standard)\n"
- "Specify large community to reject\n"
- "Specify large community to accept\n")
-
-DEFUN (lcommunity_list_standard1,
- bgp_lcommunity_list_standard1_cmd,
"bgp large-community-list (1-99) <deny|permit> AA:BB:CC...",
BGP_STR
LCOMMUNITY_LIST_STR
@@ -14640,8 +14804,8 @@ DEFUN (lcommunity_list_standard1,
LARGE_COMMUNITY_LIST_STANDARD, 0);
}
-ALIAS (lcommunity_list_standard1,
- ip_lcommunity_list_standard1_cmd,
+ALIAS (lcommunity_list_standard,
+ ip_lcommunity_list_standard_cmd,
"ip large-community-list (1-99) <deny|permit> AA:BB:CC...",
IP_STR
LCOMMUNITY_LIST_STR
@@ -14676,30 +14840,6 @@ ALIAS (lcommunity_list_expanded,
DEFUN (lcommunity_list_name_standard,
bgp_lcommunity_list_name_standard_cmd,
- "bgp large-community-list standard WORD <deny|permit>",
- BGP_STR
- LCOMMUNITY_LIST_STR
- "Specify standard large-community-list\n"
- "Large Community list name\n"
- "Specify large community to reject\n"
- "Specify large community to accept\n")
-{
- return lcommunity_list_set_vty(vty, argc, argv,
- LARGE_COMMUNITY_LIST_STANDARD, 1);
-}
-
-ALIAS (lcommunity_list_name_standard,
- ip_lcommunity_list_name_standard_cmd,
- "ip large-community-list standard WORD <deny|permit>",
- IP_STR
- LCOMMUNITY_LIST_STR
- "Specify standard large-community-list\n"
- "Large Community list name\n"
- "Specify large community to reject\n"
- "Specify large community to accept\n")
-
-DEFUN (lcommunity_list_name_standard1,
- bgp_lcommunity_list_name_standard1_cmd,
"bgp large-community-list standard WORD <deny|permit> AA:BB:CC...",
BGP_STR
LCOMMUNITY_LIST_STR
@@ -14713,8 +14853,8 @@ DEFUN (lcommunity_list_name_standard1,
LARGE_COMMUNITY_LIST_STANDARD, 1);
}
-ALIAS (lcommunity_list_name_standard1,
- ip_lcommunity_list_name_standard1_cmd,
+ALIAS (lcommunity_list_name_standard,
+ ip_lcommunity_list_name_standard_cmd,
"ip large-community-list standard WORD <deny|permit> AA:BB:CC...",
IP_STR
LCOMMUNITY_LIST_STR
@@ -14977,12 +15117,13 @@ ALIAS (show_lcommunity_list,
DEFUN (show_lcommunity_list_arg,
show_bgp_lcommunity_list_arg_cmd,
- "show bgp large-community-list <(1-500)|WORD>",
+ "show bgp large-community-list <(1-500)|WORD> detail",
SHOW_STR
BGP_STR
"List large-community list\n"
- "large-community-list number\n"
- "large-community-list name\n")
+ "Large-community-list number\n"
+ "Large-community-list name\n"
+ "Detailed information on large-community-list\n")
{
struct community_list *list;
int idx = 0;
@@ -14990,14 +15131,14 @@ DEFUN (show_lcommunity_list_arg,
if (argv_find(argv, argc, "ip", &idx)) {
vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n");
vty_out(vty, "if you are using this please migrate to the below command.\n");
- vty_out(vty, "'show bgp large-community-list <(1-500)|WORD>'\n");
- zlog_warn("Deprecated option: 'ip show large-community-list <(1-500)|WORD>' being used");
+ vty_out(vty, "'show bgp large-community-list <(1-500)|WORD> detail'\n");
+ zlog_warn("Deprecated option: 'show ip large-community-list <(1-500)|WORD>' being used");
}
list = community_list_lookup(bgp_clist, argv[3]->arg, 0,
LARGE_COMMUNITY_LIST_MASTER);
if (!list) {
- vty_out(vty, "%% Can't find extcommunity-list\n");
+ vty_out(vty, "%% Can't find large-community-list\n");
return CMD_WARNING;
}
@@ -15378,12 +15519,13 @@ ALIAS (show_extcommunity_list,
DEFUN (show_extcommunity_list_arg,
show_bgp_extcommunity_list_arg_cmd,
- "show bgp extcommunity-list <(1-500)|WORD>",
+ "show bgp extcommunity-list <(1-500)|WORD> detail",
SHOW_STR
BGP_STR
"List extended-community list\n"
"Extcommunity-list number\n"
- "Extcommunity-list name\n")
+ "Extcommunity-list name\n"
+ "Detailed information on extcommunity-list\n")
{
int idx_comm_list = 3;
struct community_list *list;
@@ -15392,8 +15534,8 @@ DEFUN (show_extcommunity_list_arg,
if (argv_find(argv, argc, "ip", &idx)) {
vty_out(vty, "This config option is deprecated, and is scheduled for removal.\n");
vty_out(vty, "if you are using this please migrate to the below command.\n");
- vty_out(vty, "'show bgp extcommunity-list <(1-500)|WORD>'\n");
- zlog_warn("Deprecated option: 'ip show extcommunity-list <(1-500)|WORD>' being used");
+ vty_out(vty, "'show bgp extcommunity-list <(1-500)|WORD> detail'\n");
+ zlog_warn("Deprecated option: 'show ip extcommunity-list <(1-500)|WORD>' being used");
}
list = community_list_lookup(bgp_clist, argv[idx_comm_list]->arg, 0,
EXTCOMMUNITY_LIST_MASTER);
@@ -15540,10 +15682,8 @@ static void community_list_vty(void)
/* Large Community List */
install_element(CONFIG_NODE, &bgp_lcommunity_list_standard_cmd);
- install_element(CONFIG_NODE, &bgp_lcommunity_list_standard1_cmd);
install_element(CONFIG_NODE, &bgp_lcommunity_list_expanded_cmd);
install_element(CONFIG_NODE, &bgp_lcommunity_list_name_standard_cmd);
- install_element(CONFIG_NODE, &bgp_lcommunity_list_name_standard1_cmd);
install_element(CONFIG_NODE, &bgp_lcommunity_list_name_expanded_cmd);
install_element(CONFIG_NODE, &no_bgp_lcommunity_list_standard_all_cmd);
install_element(CONFIG_NODE,
@@ -15555,10 +15695,8 @@ static void community_list_vty(void)
install_element(VIEW_NODE, &show_bgp_lcommunity_list_cmd);
install_element(VIEW_NODE, &show_bgp_lcommunity_list_arg_cmd);
install_element(CONFIG_NODE, &ip_lcommunity_list_standard_cmd);
- install_element(CONFIG_NODE, &ip_lcommunity_list_standard1_cmd);
install_element(CONFIG_NODE, &ip_lcommunity_list_expanded_cmd);
install_element(CONFIG_NODE, &ip_lcommunity_list_name_standard_cmd);
- install_element(CONFIG_NODE, &ip_lcommunity_list_name_standard1_cmd);
install_element(CONFIG_NODE, &ip_lcommunity_list_name_expanded_cmd);
install_element(CONFIG_NODE, &no_ip_lcommunity_list_standard_all_cmd);
install_element(CONFIG_NODE,
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 d79a68dcab..fed8cd4d86 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -2221,10 +2221,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);
@@ -8047,3 +8049,61 @@ void bgp_terminate(void)
bgp_mac_finish();
}
+
+struct peer *peer_lookup_in_view(struct vty *vty, struct bgp *bgp,
+ const char *ip_str, bool use_json)
+{
+ int ret;
+ struct peer *peer;
+ union sockunion su;
+
+ /* Get peer sockunion. */
+ ret = str2sockunion(ip_str, &su);
+ if (ret < 0) {
+ peer = peer_lookup_by_conf_if(bgp, ip_str);
+ if (!peer) {
+ peer = peer_lookup_by_hostname(bgp, ip_str);
+
+ if (!peer) {
+ if (use_json) {
+ json_object *json_no = NULL;
+ json_no = json_object_new_object();
+ json_object_string_add(
+ json_no,
+ "malformedAddressOrName",
+ ip_str);
+ vty_out(vty, "%s\n",
+ json_object_to_json_string_ext(
+ json_no,
+ JSON_C_TO_STRING_PRETTY));
+ json_object_free(json_no);
+ } else
+ vty_out(vty,
+ "%% Malformed address or name: %s\n",
+ ip_str);
+ return NULL;
+ }
+ }
+ return peer;
+ }
+
+ /* Peer structure lookup. */
+ peer = peer_lookup(bgp, &su);
+ if (!peer) {
+ if (use_json) {
+ json_object *json_no = NULL;
+ json_no = json_object_new_object();
+ json_object_string_add(json_no, "warning",
+ "No such neighbor in this view/vrf");
+ vty_out(vty, "%s\n",
+ json_object_to_json_string_ext(
+ json_no, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json_no);
+ } else
+ vty_out(vty, "No such neighbor in this view/vrf\n");
+ return NULL;
+ }
+
+ return peer;
+}
+
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 777db0ce22..880fceb8f1 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -1194,6 +1194,9 @@ 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 */
size_t last_reset_cause_size;
uint8_t last_reset_cause[BGP_MAX_PACKET_SIZE];
@@ -1927,4 +1930,8 @@ extern void bgp_unset_redist_vrf_bitmaps(struct bgp *, vrf_id_t);
/* For benefit of rfapi */
extern struct peer *peer_new(struct bgp *bgp);
+
+extern struct peer *peer_lookup_in_view(struct vty *vty, struct bgp *bgp,
+ const char *ip_str, bool use_json);
+
#endif /* _QUAGGA_BGPD_H */
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 961336fbd0..a940739ae4 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],
@@ -572,6 +574,20 @@ AC_ARG_ENABLE([thread-sanitizer],
AS_HELP_STRING([--enable-thread-sanitizer], [enable ThreadSanitizer support for detecting data races]))
AC_ARG_ENABLE([memory-sanitizer],
AS_HELP_STRING([--enable-memory-sanitizer], [enable MemorySanitizer support for detecting uninitialized memory reads]))
+AC_ARG_WITH([crypto],
+ AS_HELP_STRING([--with-crypto=<internal|openssl>], [choose between different implementations of cryptographic functions(default value is --with-crypto=internal)]))
+
+#if openssl, else use the internal
+AS_IF([test x"${with_crypto}" = x"openssl"], [
+AC_CHECK_LIB([crypto], [EVP_DigestInit], [LIBS="$LIBS -lcrypto"], [], [])
+if test $ac_cv_lib_crypto_EVP_DigestInit = no; then
+ AC_MSG_ERROR([build with openssl has been specified but openssl library was not found on your system])
+else
+ AC_DEFINE([CRYPTO_OPENSSL], [1], [Compile with openssl support])
+fi
+], [test x"${with_crypto}" = x"internal" || test x"${with_crypto}" = x"" ], [AC_DEFINE([CRYPTO_INTERNAL], [1], [Compile with internal cryptographic implementation])
+], [AC_MSG_ERROR([Unknown value for --with-crypto])]
+)
AS_IF([test "${enable_clippy_only}" != "yes"], [
AC_CHECK_HEADERS([json-c/json.h])
@@ -1047,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])
@@ -1435,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
@@ -1504,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
@@ -1540,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
@@ -2178,6 +2224,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 4ba0c0ebc6..7cd493ccc4 100644
--- a/doc/developer/library.rst
+++ b/doc/developer/library.rst
@@ -8,6 +8,7 @@ Library Facilities (libfrr)
:maxdepth: 2
memtypes
+ rcu
lists
logging
hooks
diff --git a/doc/developer/lists.rst b/doc/developer/lists.rst
index fc47a67e42..5f020060ce 100644
--- a/doc/developer/lists.rst
+++ b/doc/developer/lists.rst
@@ -611,6 +611,38 @@ Head removal (pop) and deallocation:
* note nothing between wrlock() and unlock() */
XFREE(MTYPE_ITEM, i);
+FAQ
+---
+
+Why is the list head not ``const`` in the list APIs?
+ The semantics that a ``const`` list head would imply are not obvious. It
+ could mean any of the following:
+
+ * the list just shouldn't be allocated/deallocated, but may be modified.
+ This doesn't actually work since the list head needs to be modified for
+ inserting or deleting items.
+
+ * the list shouldn't be modified, but items can. This may make sense for
+ iterating, but it's not exactly consistent - an item might be on more
+ than one list, does it apply to all of them? If not, which one?
+
+ * neither the list nor the items should be modified. This is consistent,
+ but hard to do without creating a ``const`` copy of every single list
+ function. Ease of use trumps this.
+
+Why is there no "is this item on a/the list" test?
+ It's slow for several of the data structures, and the work of adding it
+ just hasn't been done. It can certainly be added if it's needed.
+
+Why is it ``PREDECL`` + ``DECLARE`` instead of ``DECLARE`` + ``DEFINE``?
+ The rule is that a ``DEFINE`` must be in a ``.c`` file, and linked exactly
+ once because it defines some kind of global symbol. This is not the case
+ for the data structure macros; they only define ``static`` symbols and it
+ is perfectly fine to include both ``PREDECL`` and ``DECLARE`` in a header
+ file. It is also perfectly fine to have the same ``DECLARE`` statement in
+ 2 ``.c`` files, but only **if the macro arguments are identical.** Maybe
+ don't do that unless you really need it.
+
FRR lists
---------
diff --git a/doc/developer/rcu.rst b/doc/developer/rcu.rst
new file mode 100644
index 0000000000..c2ddf93f53
--- /dev/null
+++ b/doc/developer/rcu.rst
@@ -0,0 +1,269 @@
+.. highlight:: c
+
+RCU
+===
+
+Introduction
+------------
+
+RCU (Read-Copy-Update) is, fundamentally, a paradigm of multithreaded
+operation (and not a set of APIs.) The core ideas are:
+
+* longer, complicated updates to structures are made only on private,
+ "invisible" copies. Other threads, when they access the structure, see an
+ older (but consistent) copy.
+
+* once done, the updated copy is swapped in in a single operation so that
+ other threads see either the old or the new data but no inconsistent state
+ between.
+
+* the old instance is only released after making sure that it is impossible
+ any other thread might still be reading it.
+
+For more information, please search for general or Linux kernel RCU
+documentation; there is no way this doc can be comprehensive in explaining the
+interactions:
+
+* https://en.wikipedia.org/wiki/Read-copy-update
+* https://www.kernel.org/doc/html/latest/kernel-hacking/locking.html#avoiding-locks-read-copy-update
+* https://lwn.net/Articles/262464/
+* http://www.rdrop.com/users/paulmck/RCU/rclock_OLS.2001.05.01c.pdf
+* http://lse.sourceforge.net/locking/rcupdate.html
+
+RCU, the TL;DR
+^^^^^^^^^^^^^^
+
+#. data structures are always consistent for reading. That's the "R" part.
+#. reading never blocks / takes a lock.
+#. rcu_read_lock is not a lock in the traditional sense. Think of it as a
+ "reservation"; it notes what the *oldest* possible thing the thread might
+ be seeing is, and which thus can't be deleted yet.
+#. you create some object, finish it up, and then publish it.
+#. publishing is an ``atomic_*`` call with ``memory_order_release``, which
+ tells the compiler to make sure prior memory writes have completed before
+ doing the atomic op.
+#. ``ATOMLIST_*`` ``add`` operations do the ``memory_order_release`` for you.
+#. you can't touch the object after it is published, except with atomic ops.
+#. because you can't touch it, if you want to change it you make a new copy,
+ work on that, and then publish the new copy. That's the "CU" part.
+#. deleting the object is also an atomic op.
+#. other threads that started working before you published / deleted an object
+ might not see the new object / still see the deleted object.
+#. because other threads may still see deleted objects, the ``free()`` needs
+ to be delayed. That's what :c:func:`rcu_free()` is for.
+
+
+When (not) to use RCU
+^^^^^^^^^^^^^^^^^^^^^
+
+RCU is designed for read-heavy workloads where objects are updated relatively
+rarely, but frequently accessed. Do *not* indiscriminately replace locking by
+RCU patterns.
+
+The "copy" part of RCU implies that, while updating, several copies of a given
+object exist in parallel. Even after the updated copy is swapped in, the old
+object remains queued for freeing until all other threads are guaranteed to
+not be accessing it anymore, due to passing a sequence point. In addition to
+the increased memory usage, there may be some bursted (due to batching) malloc
+contention when the RCU cleanup thread does its thing and frees memory.
+
+Other useful patterns
+^^^^^^^^^^^^^^^^^^^^^
+
+In addition to the full "copy object, apply changes, atomically update"
+approach, there are 2 "reduced" usage cases that can be done:
+
+* atomically updating single pieces of a particular object, e.g. some flags
+ or configuration piece
+
+* straight up read-only / immutable objects
+
+Both of these cases can be considered RCU "subsets". For example, when
+maintaining an atomic list of items, but these items only have a single
+integer value that needs to be updated, that value can be atomically updated
+without copying the entire object. However, the object still needs to be
+free'd through :c:func:`rcu_free()` since reading/updating and deleting might
+be happening concurrently. The same applies for immutable objects; deletion
+might still race with reading so they need to be free'd through RCU.
+
+FRR API
+-------
+
+Before diving into detail on the provided functions, it is important to note
+that the FRR RCU API covers the **cleanup part of RCU, not the read-copy-update
+paradigm itself**. These parts are handled by standard C11 atomic operations,
+and by extension through the atomic data structures (ATOMLIST, ATOMSORT & co.)
+
+The ``rcu_*`` functions only make sense in conjunction with these RCU access
+patterns. If you're calling the RCU API but not using these, something is
+wrong. The other way around is not necessarily true; it is possible to use
+atomic ops & datastructures with other types of locking, e.g. rwlocks.
+
+.. c:function:: void rcu_read_lock()
+.. c:function:: void rcu_read_unlock()
+
+ These functions acquire / release the RCU read-side lock. All access to
+ RCU-guarded data must be inside a block guarded by these. Any number of
+ threads may hold the RCU read-side lock at a given point in time, including
+ both no threads at all and all threads.
+
+ The functions implement a depth counter, i.e. can be nested. The nested
+ calls are cheap, since they only increment/decrement the counter.
+ Therefore, any place that uses RCU data and doesn't have a guarantee that
+ the caller holds RCU (e.g. ``lib/`` code) should just have its own
+ rcu_read_lock/rcu_read_unlock pair.
+
+ At the "root" level (e.g. un-nested), these calls can incur the cost of one
+ syscall (to ``futex()``). That puts them on about the same cost as a
+ mutex lock/unlock.
+
+ The ``thread_master`` code currently always holds RCU everywhere, except
+ while doing the actual ``poll()`` syscall. This is both an optimization as
+ well as an "easement" into getting RCU going. The current implementation
+ contract is that any ``struct thread *`` callback is called with a RCU
+ holding depth of 1, and that this is owned by the thread so it may (should)
+ drop and reacquire it when doing some longer-running work.
+
+ .. warning::
+
+ The RCU read-side lock must be held **continuously** for the entire time
+ any piece of RCU data is used. This includes any access to RCU data
+ after the initial ``atomic_load``. If the RCU read-side lock is
+ released, any RCU-protected pointers as well as the data they refer to
+ become invalid, as another thread may have called :c:func:`rcu_free` on
+ them.
+
+.. c:type:: struct rcu_head
+.. c:type:: struct rcu_head_close
+.. c:type:: struct rcu_action
+
+ The ``rcu_head`` structures are small (16-byte) bits that contain the
+ queueing machinery for the RCU sweeper/cleanup mechanisms.
+
+ Any piece of data that is cleaned up by RCU needs to have a matching
+ ``rcu_head`` embedded in it. If there is more than one cleanup operation
+ to be done (e.g. closing a file descriptor), more than one ``rcu_head`` may
+ be embedded.
+
+ .. warning::
+
+ It is not possible to reuse a ``rcu_head``. It is owned by the RCU code
+ as soon as ``rcu_*`` is called on it.
+
+ The ``_close`` variant carries an extra ``int fd`` field to store the fd to
+ be closed.
+
+ To minimize the amount of memory used for ``rcu_head``, details about the
+ RCU operation to be performed are moved into the ``rcu_action`` structure.
+ It contains e.g. the MTYPE for :c:func:`rcu_free` calls. The pointer to be
+ freed is stored as an offset relative to the ``rcu_head``, which means it
+ must be embedded as a struct field so the offset is constant.
+
+ The ``rcu_action`` structure is an implementation detail. Using
+ ``rcu_free`` or ``rcu_close`` will set it up correctly without further
+ code needed.
+
+ The ``rcu_head`` may be put in an union with other data if the other data
+ is only used during "life" of the data, since the ``rcu_head`` is used only
+ for the "death" of data. But note that other threads may still be reading
+ a piece of data while a thread is working to free it.
+
+.. c:function:: void rcu_free(struct memtype *mtype, struct X *ptr, field)
+
+ Free a block of memory after RCU has ensured no other thread can be
+ accessing it anymore. The pointer remains valid for any other thread that
+ has called :c:func:`rcu_read_lock` before the ``rcu_free`` call.
+
+ .. warning::
+
+ In some other RCU implementations, the pointer remains valid to the
+ *calling* thread if it is holding the RCU read-side lock. This is not
+ the case in FRR, particularly when running single-threaded. Enforcing
+ this rule also allows static analysis to find use-after-free issues.
+
+ ``mtype`` is the libfrr ``MTYPE_FOO`` allocation type to pass to
+ :c:func:`XFREE`.
+
+ ``field`` must be the name of a ``struct rcu_head`` member field in ``ptr``.
+ The offset of this field (which must be constant) is used to reduce the
+ memory size of ``struct rcu_head``.
+
+ .. note::
+
+ ``rcu_free`` (and ``rcu_close``) calls are more efficient if they are
+ put close to each other. When freeing several RCU'd resources, try to
+ move the calls next to each other (even if the data structures do not
+ directly point to each other.)
+
+ Having the calls bundled reduces the cost of adding the ``rcu_head`` to
+ the RCU queue; the RCU queue is an atomic data structure whose usage
+ will require the CPU to acquire an exclusive hold on relevant cache
+ lines.
+
+.. c:function:: void rcu_close(struct rcu_head_close *head, int fd)
+
+ Close a file descriptor after ensuring no other thread might be using it
+ anymore. Same as :c:func:`rcu_free`, except it calls ``close`` instead of
+ ``free``.
+
+Internals
+^^^^^^^^^
+
+.. c:type:: struct rcu_thread
+
+ Per-thread state maintained by the RCU code, set up by the following
+ functions. A pointer to a thread's own ``rcu_thread`` is saved in
+ thread-local storage.
+
+.. c:function:: struct rcu_thread *rcu_thread_prepare(void)
+.. c:function:: void rcu_thread_unprepare(struct rcu_thread *rcu_thread)
+.. c:function:: void rcu_thread_start(struct rcu_thread *rcu_thread)
+
+ Since the RCU code needs to have a list of all active threads, these
+ functions are used by the ``frr_pthread`` code to set up threads. Teardown
+ is automatic. It should not be necessary to call these functions.
+
+ Any thread that accesses RCU-protected data needs to be registered with
+ these functions. Threads that do not access RCU-protected data may call
+ these functions but do not need to.
+
+ Note that passing a pointer to RCU-protected data to some library which
+ accesses that pointer makes the library "access RCU-protected data". In
+ that case, either all of the library's threads must be registered for RCU,
+ or the code must instead pass a (non-RCU) copy of the data to the library.
+
+.. c:function:: void rcu_shutdown(void)
+
+ Stop the RCU sweeper thread and make sure all cleanup has finished.
+
+ This function is called on daemon exit by the libfrr code to ensure pending
+ RCU operations are completed. This is mostly to get a clean exit without
+ memory leaks from queued RCU operations. It should not be necessary to
+ call this function as libfrr handles this.
+
+FRR specifics and implementation details
+----------------------------------------
+
+The FRR RCU infrastructure has the following characteristics:
+
+* it is Epoch-based with a 32-bit wrapping counter. (This is somewhat
+ different from other Epoch-based approaches which may be designed to only
+ use 3 counter values, but works out to a simple implementation.)
+
+* instead of tracking CPUs as the Linux kernel does, threads are tracked. This
+ has exactly zero semantic impact, RCU just cares about "threads of
+ execution", which the kernel can optimize to CPUs but we can't. But it
+ really boils down to the same thing.
+
+* there are no ``rcu_dereference`` and ``rcu_assign_pointer`` - use
+ ``atomic_load`` and ``atomic_store`` instead. (These didn't exist when the
+ Linux RCU code was created.)
+
+* there is no ``synchronize_rcu``; this is a design choice but may be revisited
+ at a later point. ``synchronize_rcu`` blocks a thread until it is guaranteed
+ that no other threads might still be accessing data structures that they may
+ have access to at the beginning of the function call. This is a blocking
+ design and probably not appropriate for FRR. Instead, ``rcu_call`` can be
+ used to have the RCU sweeper thread make a callback after the same constraint
+ is fulfilled in an asynchronous way. Most needs should be covered by
+ ``rcu_free`` and ``rcu_close``.
diff --git a/doc/developer/subdir.am b/doc/developer/subdir.am
index 996f12d47f..1fc593e566 100644
--- a/doc/developer/subdir.am
+++ b/doc/developer/subdir.am
@@ -42,6 +42,7 @@ dev_RSTFILES = \
doc/developer/packaging-debian.rst \
doc/developer/packaging-redhat.rst \
doc/developer/packaging.rst \
+ doc/developer/rcu.rst \
doc/developer/testing.rst \
doc/developer/topotests-snippets.rst \
doc/developer/topotests.rst \
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index f2b1328075..da339b4409 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -1169,19 +1169,24 @@ AS path access list is user defined AS path.
Using AS Path in Route Map
--------------------------
-.. index:: match as-path WORD
-.. clicmd:: match as-path WORD
+.. index:: [no] match as-path WORD
+.. clicmd:: [no] match as-path WORD
+ For a given as-path, WORD, match it on the BGP as-path given for the prefix
+ and if it matches do normal route-map actions. The no form of the command
+ removes this match from the route-map.
-.. index:: set as-path prepend AS-PATH
-.. clicmd:: set as-path prepend AS-PATH
+.. index:: [no] set as-path prepend AS-PATH
+.. clicmd:: [no] set as-path prepend AS-PATH
- Prepend the given string of AS numbers to the AS_PATH.
+ Prepend the given string of AS numbers to the AS_PATH of the BGP path's NLRI.
+ The no form of this command removes this set operation from the route-map.
-.. index:: set as-path prepend last-as NUM
-.. clicmd:: set as-path prepend last-as NUM
+.. index:: [no] set as-path prepend last-as NUM
+.. clicmd:: [no] set as-path prepend last-as NUM
Prepend the existing last AS number (the leftmost ASN) to the AS_PATH.
+ The no form of this command removes this set operation from the route-map.
.. _bgp-communities-attribute:
@@ -2138,20 +2143,40 @@ Dumping Messages and Routing Tables
Other BGP Commands
------------------
+.. index:: clear bgp \*
+.. clicmd:: clear bgp \*
+
+ Clear all peers.
+
.. index:: clear bgp ipv4|ipv6 \*
.. clicmd:: clear bgp ipv4|ipv6 \*
- Clear all address family peers.
+ Clear all peers with this address-family activated.
+
+.. index:: clear bgp ipv4|ipv6 unicast \*
+.. clicmd:: clear bgp ipv4|ipv6 unicast \*
+
+ Clear all peers with this address-family and sub-address-family activated.
.. index:: clear bgp ipv4|ipv6 PEER
.. clicmd:: clear bgp ipv4|ipv6 PEER
- Clear peers which have addresses of X.X.X.X
+ Clear peers with address of X.X.X.X and this address-family activated.
+
+.. index:: clear bgp ipv4|ipv6 unicast PEER
+.. clicmd:: clear bgp ipv4|ipv6 unicast PEER
-.. index:: clear bgp ipv4|ipv6 PEER soft in
-.. clicmd:: clear bgp ipv4|ipv6 PEER soft in
+ Clear peer with address of X.X.X.X and this address-family and sub-address-family activated.
- Clear peer using soft reconfiguration.
+.. index:: clear bgp ipv4|ipv6 PEER soft|in|out
+.. clicmd:: clear bgp ipv4|ipv6 PEER soft|in|out
+
+ Clear peer using soft reconfiguration in this address-family.
+
+.. index:: clear bgp ipv4|ipv6 unicast PEER soft|in|out
+.. clicmd:: clear bgp ipv4|ipv6 unicast PEER soft|in|out
+
+ Clear peer using soft reconfiguration in this address-family and sub-address-family.
.. _bgp-displaying-bgp-information:
@@ -2226,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/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/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/isisd/isis_bfd.c b/isisd/isis_bfd.c
index fa89c80049..8fc7997d79 100644
--- a/isisd/isis_bfd.c
+++ b/isisd/isis_bfd.c
@@ -19,7 +19,9 @@
#include <zebra.h>
#include "zclient.h"
+#include "nexthop.h"
#include "bfd.h"
+#include "lib_errors.h"
#include "isisd/isis_bfd.h"
#include "isisd/isis_zebra.h"
@@ -33,17 +35,19 @@
DEFINE_MTYPE_STATIC(ISISD, BFD_SESSION, "ISIS BFD Session")
struct bfd_session {
- struct in_addr dst_ip;
- struct in_addr src_ip;
+ int family;
+ union g_addr dst_ip;
+ union g_addr src_ip;
int status;
};
-static struct bfd_session *bfd_session_new(struct in_addr *dst_ip,
- struct in_addr *src_ip)
+static struct bfd_session *bfd_session_new(int family, union g_addr *dst_ip,
+ union g_addr *src_ip)
{
struct bfd_session *rv;
rv = XCALLOC(MTYPE_BFD_SESSION, sizeof(*rv));
+ rv->family = family;
rv->dst_ip = *dst_ip;
rv->src_ip = *src_ip;
return rv;
@@ -58,15 +62,60 @@ static void bfd_session_free(struct bfd_session **session)
*session = NULL;
}
+static bool bfd_session_same(const struct bfd_session *session, int family,
+ const union g_addr *src, const union g_addr *dst)
+{
+ if (session->family != family)
+ return false;
+
+ switch (session->family) {
+ case AF_INET:
+ if (!IPV4_ADDR_SAME(&session->dst_ip.ipv4, &dst->ipv4))
+ return false;
+ if (!IPV4_ADDR_SAME(&session->src_ip.ipv4, &src->ipv4))
+ return false;
+ break;
+ case AF_INET6:
+ if (!IPV6_ADDR_SAME(&session->dst_ip.ipv6, &dst->ipv6))
+ return false;
+ if (!IPV6_ADDR_SAME(&session->src_ip.ipv6, &src->ipv6))
+ return false;
+ break;
+ default:
+ flog_err(EC_LIB_DEVELOPMENT, "%s: unknown address-family: %u",
+ __func__, session->family);
+ exit(1);
+ }
+
+ return true;
+}
+
static void bfd_adj_event(struct isis_adjacency *adj, struct prefix *dst,
int new_status)
{
if (!adj->bfd_session)
return;
- if (adj->bfd_session->dst_ip.s_addr != dst->u.prefix4.s_addr)
+ if (adj->bfd_session->family != dst->family)
return;
+ switch (adj->bfd_session->family) {
+ case AF_INET:
+ if (!IPV4_ADDR_SAME(&adj->bfd_session->dst_ip.ipv4,
+ &dst->u.prefix4))
+ return;
+ break;
+ case AF_INET6:
+ if (!IPV6_ADDR_SAME(&adj->bfd_session->dst_ip.ipv6,
+ &dst->u.prefix6))
+ return;
+ break;
+ default:
+ flog_err(EC_LIB_DEVELOPMENT, "%s: unknown address-family: %u",
+ __func__, adj->bfd_session->family);
+ exit(1);
+ }
+
int old_status = adj->bfd_session->status;
adj->bfd_session->status = new_status;
@@ -76,7 +125,7 @@ static void bfd_adj_event(struct isis_adjacency *adj, struct prefix *dst,
if (isis->debugs & DEBUG_BFD) {
char dst_str[INET6_ADDRSTRLEN];
- inet_ntop(AF_INET, &adj->bfd_session->dst_ip,
+ inet_ntop(adj->bfd_session->family, &adj->bfd_session->dst_ip,
dst_str, sizeof(dst_str));
zlog_debug("ISIS-BFD: Peer %s on %s changed from %s to %s",
dst_str, adj->circuit->interface->name,
@@ -100,14 +149,14 @@ static int isis_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS)
ifp = bfd_get_peer_info(zclient->ibuf, &dst_ip, NULL, &status,
NULL, vrf_id);
- if (!ifp || dst_ip.family != AF_INET)
+ if (!ifp || (dst_ip.family != AF_INET && dst_ip.family != AF_INET6))
return 0;
if (isis->debugs & DEBUG_BFD) {
char dst_buf[INET6_ADDRSTRLEN];
- inet_ntop(AF_INET, &dst_ip.u.prefix4,
- dst_buf, sizeof(dst_buf));
+ inet_ntop(dst_ip.family, &dst_ip.u.prefix, dst_buf,
+ sizeof(dst_buf));
zlog_debug("ISIS-BFD: Received update for %s on %s: Changed state to %s",
dst_buf, ifp->name, bfd_get_status_str(status));
@@ -171,7 +220,7 @@ static void isis_bfd_zebra_connected(struct zclient *zclient)
bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
}
-static void bfd_debug(struct in_addr *dst, struct in_addr *src,
+static void bfd_debug(int family, union g_addr *dst, union g_addr *src,
const char *interface, int command)
{
if (!(isis->debugs & DEBUG_BFD))
@@ -180,8 +229,8 @@ static void bfd_debug(struct in_addr *dst, struct in_addr *src,
char dst_str[INET6_ADDRSTRLEN];
char src_str[INET6_ADDRSTRLEN];
- inet_ntop(AF_INET, dst, dst_str, sizeof(dst_str));
- inet_ntop(AF_INET, src, src_str, sizeof(src_str));
+ inet_ntop(family, dst, dst_str, sizeof(dst_str));
+ inet_ntop(family, src, src_str, sizeof(src_str));
const char *command_str;
@@ -209,10 +258,11 @@ static void bfd_handle_adj_down(struct isis_adjacency *adj)
if (!adj->bfd_session)
return;
- bfd_debug(&adj->bfd_session->dst_ip, &adj->bfd_session->src_ip,
- adj->circuit->interface->name, ZEBRA_BFD_DEST_DEREGISTER);
+ bfd_debug(adj->bfd_session->family, &adj->bfd_session->dst_ip,
+ &adj->bfd_session->src_ip, adj->circuit->interface->name,
+ ZEBRA_BFD_DEST_DEREGISTER);
- bfd_peer_sendmsg(zclient, NULL, AF_INET,
+ bfd_peer_sendmsg(zclient, NULL, adj->bfd_session->family,
&adj->bfd_session->dst_ip,
&adj->bfd_session->src_ip,
adj->circuit->interface->name,
@@ -228,33 +278,50 @@ static void bfd_handle_adj_down(struct isis_adjacency *adj)
static void bfd_handle_adj_up(struct isis_adjacency *adj, int command)
{
struct isis_circuit *circuit = adj->circuit;
+ int family;
+ union g_addr dst_ip;
+ union g_addr src_ip;
+ struct list *local_ips;
+ struct prefix *local_ip;
- if (!circuit->bfd_info
- || !circuit->ip_router
- || !adj->ipv4_address_count)
+ if (!circuit->bfd_info)
goto out;
- struct list *local_ips = fabricd_ip_addrs(adj->circuit);
-
- if (!local_ips)
+ /*
+ * If IS-IS is enabled for both IPv4 and IPv6 on the circuit, prefer
+ * creating a BFD session over IPv6.
+ */
+ if (circuit->ipv6_router && adj->ipv6_address_count) {
+ family = AF_INET6;
+ dst_ip.ipv6 = adj->ipv6_addresses[0];
+ local_ips = circuit->ipv6_link;
+ if (!local_ips || list_isempty(local_ips))
+ goto out;
+ local_ip = listgetdata(listhead(local_ips));
+ src_ip.ipv6 = local_ip->u.prefix6;
+ } else if (circuit->ip_router && adj->ipv4_address_count) {
+ family = AF_INET;
+ dst_ip.ipv4 = adj->ipv4_addresses[0];
+ local_ips = fabricd_ip_addrs(adj->circuit);
+ if (!local_ips || list_isempty(local_ips))
+ goto out;
+ local_ip = listgetdata(listhead(local_ips));
+ src_ip.ipv4 = local_ip->u.prefix4;
+ } else
goto out;
- struct in_addr *dst_ip = &adj->ipv4_addresses[0];
- struct prefix_ipv4 *local_ip = listgetdata(listhead(local_ips));
- struct in_addr *src_ip = &local_ip->prefix;
-
if (adj->bfd_session) {
- if (adj->bfd_session->dst_ip.s_addr != dst_ip->s_addr
- || adj->bfd_session->src_ip.s_addr != src_ip->s_addr)
+ if (bfd_session_same(adj->bfd_session, family, &src_ip,
+ &dst_ip))
bfd_handle_adj_down(adj);
}
if (!adj->bfd_session)
- adj->bfd_session = bfd_session_new(dst_ip, src_ip);
+ adj->bfd_session = bfd_session_new(family, &dst_ip, &src_ip);
- bfd_debug(&adj->bfd_session->dst_ip, &adj->bfd_session->src_ip,
- circuit->interface->name, command);
- bfd_peer_sendmsg(zclient, circuit->bfd_info, AF_INET,
+ bfd_debug(adj->bfd_session->family, &adj->bfd_session->dst_ip,
+ &adj->bfd_session->src_ip, circuit->interface->name, command);
+ bfd_peer_sendmsg(zclient, circuit->bfd_info, adj->bfd_session->family,
&adj->bfd_session->dst_ip,
&adj->bfd_session->src_ip,
circuit->interface->name,
diff --git a/isisd/isis_cli.c b/isisd/isis_cli.c
index 4b43260773..bd06286755 100644
--- a/isisd/isis_cli.c
+++ b/isisd/isis_cli.c
@@ -363,7 +363,7 @@ void cli_show_ip_isis_bfd_monitoring(struct vty *vty, struct lyd_node *dnode,
if (!yang_dnode_get_bool(dnode, NULL))
vty_out(vty, " no");
- vty_out(vty, "isis bfd\n");
+ vty_out(vty, " isis bfd\n");
}
/*
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..25c7058f3f 100644
--- a/isisd/isis_northbound.c
+++ b/isisd/isis_northbound.c
@@ -1561,21 +1561,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);
+ if (circuit->state == C_STATE_UP || circuit->state == C_STATE_CONF)
isis_csm_state_change(ISIS_DISABLE, circuit, circuit->area);
- break;
- case 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_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_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/lib/command.c b/lib/command.c
index 9dabc2af7e..d4c35a80d5 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:
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/ferr.c b/lib/ferr.c
index 8afc926c41..fd5fb50172 100644
--- a/lib/ferr.c
+++ b/lib/ferr.c
@@ -170,7 +170,7 @@ void log_ref_display(struct vty *vty, uint32_t code, bool json)
DEFUN_NOSH(show_error_code,
show_error_code_cmd,
- "show error <(1-4294967296)|all> [json]",
+ "show error <(1-4294967295)|all> [json]",
SHOW_STR
"Information on errors\n"
"Error code to get info about\n"
diff --git a/lib/frr_pthread.c b/lib/frr_pthread.c
index e588571c01..bdb6c2a397 100644
--- a/lib/frr_pthread.c
+++ b/lib/frr_pthread.c
@@ -133,18 +133,29 @@ int frr_pthread_set_name(struct frr_pthread *fpt)
return ret;
}
+static void *frr_pthread_inner(void *arg)
+{
+ struct frr_pthread *fpt = arg;
+
+ rcu_thread_start(fpt->rcu_thread);
+ return fpt->attr.start(fpt);
+}
+
int frr_pthread_run(struct frr_pthread *fpt, const pthread_attr_t *attr)
{
int ret;
- ret = pthread_create(&fpt->thread, attr, fpt->attr.start, fpt);
+ fpt->rcu_thread = rcu_thread_prepare();
+ ret = pthread_create(&fpt->thread, attr, frr_pthread_inner, fpt);
/*
* Per pthread_create(3), the contents of fpt->thread are undefined if
* pthread_create() did not succeed. Reset this value to zero.
*/
- if (ret < 0)
+ if (ret < 0) {
+ rcu_thread_unprepare(fpt->rcu_thread);
memset(&fpt->thread, 0x00, sizeof(fpt->thread));
+ }
return ret;
}
diff --git a/lib/frr_pthread.h b/lib/frr_pthread.h
index 3afe7ba966..6096a50370 100644
--- a/lib/frr_pthread.h
+++ b/lib/frr_pthread.h
@@ -23,6 +23,7 @@
#include <pthread.h>
#include "frratomic.h"
#include "memory.h"
+#include "frrcu.h"
#include "thread.h"
#ifdef __cplusplus
@@ -50,6 +51,8 @@ struct frr_pthread {
/* pthread id */
pthread_t thread;
+ struct rcu_thread *rcu_thread;
+
/* thread master for this pthread's thread.c event loop */
struct thread_master *master;
diff --git a/lib/frrcu.c b/lib/frrcu.c
new file mode 100644
index 0000000000..54626f909d
--- /dev/null
+++ b/lib/frrcu.c
@@ -0,0 +1,526 @@
+/*
+ * Copyright (c) 2017-19 David Lamparter, for NetDEF, Inc.
+ *
+ * Permission to use, copy, modify, and 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+/* implementation notes: this is an epoch-based RCU implementation. rcu_seq
+ * (global variable) counts the current epoch. Threads hold a specific epoch
+ * in rcu_read_lock(). This is the oldest epoch a thread might be accessing
+ * data from.
+ *
+ * The rcu_seq global is only pushed forward on rcu_read_lock() and
+ * rcu_read_unlock() calls. This makes things a tad more efficient since
+ * those are the only places it matters:
+ * - on rcu_read_lock, we don't want to hold an old epoch pointlessly
+ * - on rcu_read_unlock, we want to make sure we're not stuck on an old epoch
+ * when heading into a long idle period where no thread holds RCU
+ *
+ * rcu_thread structures themselves are RCU-free'd.
+ *
+ * rcu_head structures are the most iffy; normally for an ATOMLIST we would
+ * need to make sure we use rcu_free or pthread_rwlock to deallocate old items
+ * to prevent ABA or use-after-free problems. However, our ATOMLIST code
+ * guarantees that if the list remains non-empty in all cases, we only need
+ * the "last" pointer to do an "add_tail()", i.e. we can't run into ABA/UAF
+ * issues - but we do need to keep at least 1 item on the list.
+ *
+ * (Search the atomlist code for all uses of "last")
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <pthread.h>
+#ifdef HAVE_PTHREAD_NP_H
+#include <pthread_np.h>
+#endif
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include "frrcu.h"
+#include "seqlock.h"
+#include "atomlist.h"
+
+DEFINE_MTYPE_STATIC(LIB, RCU_THREAD, "RCU thread")
+
+DECLARE_ATOMLIST(rcu_heads, struct rcu_head, head)
+
+PREDECL_ATOMLIST(rcu_threads)
+struct rcu_thread {
+ struct rcu_threads_item head;
+
+ struct rcu_head rcu_head;
+
+ struct seqlock rcu;
+
+ /* only accessed by thread itself, not atomic */
+ unsigned depth;
+};
+DECLARE_ATOMLIST(rcu_threads, struct rcu_thread, head)
+
+static const struct rcu_action rcua_next = { .type = RCUA_NEXT };
+static const struct rcu_action rcua_end = { .type = RCUA_END };
+static const struct rcu_action rcua_close = { .type = RCUA_CLOSE };
+
+struct rcu_next {
+ struct rcu_head head_free;
+ struct rcu_head head_next;
+};
+
+#define rcu_free_internal(mtype, ptr, field) \
+ do { \
+ typeof(ptr) _ptr = (ptr); \
+ struct rcu_head *_rcu_head = &_ptr->field; \
+ static const struct rcu_action _rcu_action = { \
+ .type = RCUA_FREE, \
+ .u.free = { \
+ .mt = mtype, \
+ .offset = offsetof(typeof(*_ptr), field), \
+ }, \
+ }; \
+ _rcu_head->action = &_rcu_action; \
+ rcu_heads_add_tail(&rcu_heads, _rcu_head); \
+ } while (0)
+
+/* primary global RCU position */
+static struct seqlock rcu_seq;
+/* this is set to rcu_seq whenever something is added on the RCU queue.
+ * rcu_read_lock() and rcu_read_unlock() will then bump rcu_seq up one step.
+ */
+static _Atomic seqlock_val_t rcu_dirty;
+
+static struct rcu_threads_head rcu_threads;
+static struct rcu_heads_head rcu_heads;
+
+/* main thread & RCU sweeper have pre-setup rcu_thread structures. The
+ * reasons are different:
+ *
+ * - rcu_thread_main is there because the main thread isn't started like
+ * other threads, it's implicitly created when the program is started. So
+ * rcu_thread_main matches up implicitly.
+ *
+ * - rcu_thread_rcu isn't actually put on the rcu_threads list (makes no
+ * sense really), it only exists so we can call RCU-using functions from
+ * the RCU thread without special handling in rcu_read_lock/unlock.
+ */
+static struct rcu_thread rcu_thread_main;
+static struct rcu_thread rcu_thread_rcu;
+
+static pthread_t rcu_pthread;
+static pthread_key_t rcu_thread_key;
+static bool rcu_active;
+
+static void rcu_start(void);
+static void rcu_bump(void);
+
+/*
+ * preinitialization for main thread
+ */
+static void rcu_thread_end(void *rcu_thread);
+
+static void rcu_preinit(void) __attribute__((constructor));
+static void rcu_preinit(void)
+{
+ struct rcu_thread *rt;
+
+ rt = &rcu_thread_main;
+ rt->depth = 1;
+ seqlock_init(&rt->rcu);
+ seqlock_acquire_val(&rt->rcu, SEQLOCK_STARTVAL);
+
+ pthread_key_create(&rcu_thread_key, rcu_thread_end);
+ pthread_setspecific(rcu_thread_key, rt);
+
+ rcu_threads_add_tail(&rcu_threads, rt);
+
+ /* RCU sweeper's rcu_thread is a dummy, NOT added to rcu_threads */
+ rt = &rcu_thread_rcu;
+ rt->depth = 1;
+
+ seqlock_init(&rcu_seq);
+ seqlock_acquire_val(&rcu_seq, SEQLOCK_STARTVAL);
+}
+
+static struct rcu_thread *rcu_self(void)
+{
+ return (struct rcu_thread *)pthread_getspecific(rcu_thread_key);
+}
+
+/*
+ * thread management (for the non-main thread)
+ */
+struct rcu_thread *rcu_thread_prepare(void)
+{
+ struct rcu_thread *rt, *cur;
+
+ rcu_assert_read_locked();
+
+ if (!rcu_active)
+ rcu_start();
+
+ cur = rcu_self();
+ assert(cur->depth);
+
+ /* new thread always starts with rcu_read_lock held at depth 1, and
+ * holding the same epoch as the parent (this makes it possible to
+ * use RCU for things passed into the thread through its arg)
+ */
+ rt = XCALLOC(MTYPE_RCU_THREAD, sizeof(*rt));
+ rt->depth = 1;
+
+ seqlock_init(&rt->rcu);
+ seqlock_acquire(&rt->rcu, &cur->rcu);
+
+ rcu_threads_add_tail(&rcu_threads, rt);
+
+ return rt;
+}
+
+void rcu_thread_start(struct rcu_thread *rt)
+{
+ pthread_setspecific(rcu_thread_key, rt);
+}
+
+void rcu_thread_unprepare(struct rcu_thread *rt)
+{
+ if (rt == &rcu_thread_rcu)
+ return;
+
+ rt->depth = 1;
+ seqlock_acquire(&rt->rcu, &rcu_seq);
+
+ rcu_bump();
+ if (rt != &rcu_thread_main)
+ /* this free() happens after seqlock_release() below */
+ rcu_free_internal(MTYPE_RCU_THREAD, rt, rcu_head);
+
+ rcu_threads_del(&rcu_threads, rt);
+ seqlock_release(&rt->rcu);
+}
+
+static void rcu_thread_end(void *rtvoid)
+{
+ struct rcu_thread *rt = rtvoid;
+ rcu_thread_unprepare(rt);
+}
+
+/*
+ * main RCU control aspects
+ */
+
+static void rcu_bump(void)
+{
+ struct rcu_next *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
+ * RCUA_NEXT, since we really don't care.
+ */
+
+ /*
+ * Important race condition: while rcu_heads_add_tail is executing,
+ * there is an intermediate point where the rcu_heads "last" pointer
+ * already points to rn->head_next, but rn->head_next isn't added to
+ * the list yet. That means any other "add_tail" calls append to this
+ * item, which isn't fully on the list yet. Freeze this thread at
+ * that point and look at another thread doing a rcu_bump. It adds
+ * these two items and then does a seqlock_bump. But the rcu_heads
+ * list is still "interrupted" and there's no RCUA_NEXT on the list
+ * yet (from either the frozen thread or the second thread). So
+ * rcu_main() might actually hit the end of the list at the
+ * "interrupt".
+ *
+ * This situation is prevented by requiring that rcu_read_lock is held
+ * for any calls to rcu_bump, since if we're holding the current RCU
+ * epoch, that means rcu_main can't be chewing on rcu_heads and hit
+ * that interruption point. Only by the time the thread has continued
+ * to rcu_read_unlock() - and therefore completed the add_tail - the
+ * RCU sweeper gobbles up the epoch and can be sure to find at least
+ * the RCUA_NEXT and RCUA_FREE items on rcu_heads.
+ */
+ rn->head_next.action = &rcua_next;
+ rcu_heads_add_tail(&rcu_heads, &rn->head_next);
+
+ /* free rn that we allocated above.
+ *
+ * This is INTENTIONALLY not built into the RCUA_NEXT action. This
+ * ensures that after the action above is popped off the queue, there
+ * is still at least 1 item on the RCU queue. This means we never
+ * delete the last item, which is extremely important since it keeps
+ * the atomlist ->last pointer alive and well.
+ *
+ * If we were to "run dry" on the RCU queue, add_tail may run into the
+ * "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_THREAD, rn, head_free);
+
+ /* Only allow the RCU sweeper to run after these 2 items are queued.
+ *
+ * If another thread enqueues some RCU action in the intermediate
+ * window here, nothing bad happens - the queued action is associated
+ * with a larger seq# than strictly necessary. Thus, it might get
+ * executed a bit later, but that's not a problem.
+ *
+ * If another thread acquires the read lock in this window, it holds
+ * the previous epoch, but its RCU queue actions will be in the next
+ * epoch. This isn't a problem either, just a tad inefficient.
+ */
+ seqlock_bump(&rcu_seq);
+}
+
+static void rcu_bump_maybe(void)
+{
+ seqlock_val_t dirty;
+
+ dirty = atomic_load_explicit(&rcu_dirty, memory_order_relaxed);
+ /* no problem if we race here and multiple threads bump rcu_seq;
+ * bumping too much causes no issues while not bumping enough will
+ * result in delayed cleanup
+ */
+ if (dirty == seqlock_cur(&rcu_seq))
+ rcu_bump();
+}
+
+void rcu_read_lock(void)
+{
+ struct rcu_thread *rt = rcu_self();
+
+ assert(rt);
+ if (rt->depth++ > 0)
+ return;
+
+ seqlock_acquire(&rt->rcu, &rcu_seq);
+ /* need to hold RCU for bump ... */
+ rcu_bump_maybe();
+ /* ... but no point in holding the old epoch if we just bumped */
+ seqlock_acquire(&rt->rcu, &rcu_seq);
+}
+
+void rcu_read_unlock(void)
+{
+ struct rcu_thread *rt = rcu_self();
+
+ assert(rt && rt->depth);
+ if (--rt->depth > 0)
+ return;
+ rcu_bump_maybe();
+ seqlock_release(&rt->rcu);
+}
+
+void rcu_assert_read_locked(void)
+{
+ struct rcu_thread *rt = rcu_self();
+ assert(rt && rt->depth && seqlock_held(&rt->rcu));
+}
+
+void rcu_assert_read_unlocked(void)
+{
+ struct rcu_thread *rt = rcu_self();
+ assert(rt && !rt->depth && !seqlock_held(&rt->rcu));
+}
+
+/*
+ * RCU resource-release thread
+ */
+
+static void *rcu_main(void *arg);
+
+static void rcu_start(void)
+{
+ /* ensure we never handle signals on the RCU thread by blocking
+ * everything here (new thread inherits signal mask)
+ */
+ sigset_t oldsigs, blocksigs;
+
+ sigfillset(&blocksigs);
+ pthread_sigmask(SIG_BLOCK, &blocksigs, &oldsigs);
+
+ rcu_active = true;
+
+ assert(!pthread_create(&rcu_pthread, NULL, rcu_main, NULL));
+
+ pthread_sigmask(SIG_SETMASK, &oldsigs, NULL);
+
+#ifdef HAVE_PTHREAD_SETNAME_NP
+# ifdef GNU_LINUX
+ pthread_setname_np(rcu_pthread, "RCU sweeper");
+# elif defined(__NetBSD__)
+ pthread_setname_np(rcu_pthread, "RCU sweeper", NULL);
+# endif
+#elif defined(HAVE_PTHREAD_SET_NAME_NP)
+ pthread_set_name_np(rcu_pthread, "RCU sweeper");
+#endif
+}
+
+static void rcu_do(struct rcu_head *rh)
+{
+ struct rcu_head_close *rhc;
+ void *p;
+
+ switch (rh->action->type) {
+ case RCUA_FREE:
+ p = (char *)rh - rh->action->u.free.offset;
+ if (rh->action->u.free.mt)
+ qfree(rh->action->u.free.mt, p);
+ else
+ free(p);
+ break;
+ case RCUA_CLOSE:
+ rhc = container_of(rh, struct rcu_head_close,
+ rcu_head);
+ close(rhc->fd);
+ break;
+ case RCUA_CALL:
+ p = (char *)rh - rh->action->u.call.offset;
+ rh->action->u.call.fptr(p);
+ break;
+
+ case RCUA_INVALID:
+ case RCUA_NEXT:
+ case RCUA_END:
+ default:
+ assert(0);
+ }
+}
+
+static void rcu_watchdog(struct rcu_thread *rt)
+{
+#if 0
+ /* future work: print a backtrace for the thread that's holding up
+ * RCU. The only (good) way of doing that is to send a signal to the
+ * other thread, save away the backtrace in the signal handler, and
+ * block here until the signal is done processing.
+ *
+ * Just haven't implemented that yet.
+ */
+ fprintf(stderr, "RCU watchdog %p\n", rt);
+#endif
+}
+
+static void *rcu_main(void *arg)
+{
+ struct rcu_thread *rt;
+ struct rcu_head *rh = NULL;
+ bool end = false;
+ struct timespec maxwait;
+
+ seqlock_val_t rcuval = SEQLOCK_STARTVAL;
+
+ pthread_setspecific(rcu_thread_key, &rcu_thread_rcu);
+
+ while (!end) {
+ seqlock_wait(&rcu_seq, rcuval);
+
+ /* RCU watchdog timeout, TODO: configurable value */
+ clock_gettime(CLOCK_MONOTONIC, &maxwait);
+ maxwait.tv_nsec += 100 * 1000 * 1000;
+ if (maxwait.tv_nsec >= 1000000000) {
+ maxwait.tv_sec++;
+ maxwait.tv_nsec -= 1000000000;
+ }
+
+ frr_each (rcu_threads, &rcu_threads, rt)
+ if (!seqlock_timedwait(&rt->rcu, rcuval, &maxwait)) {
+ rcu_watchdog(rt);
+ seqlock_wait(&rt->rcu, rcuval);
+ }
+
+ while ((rh = rcu_heads_pop(&rcu_heads))) {
+ if (rh->action->type == RCUA_NEXT)
+ break;
+ else if (rh->action->type == RCUA_END)
+ end = true;
+ else
+ rcu_do(rh);
+ }
+
+ rcuval += SEQLOCK_INCR;
+ }
+
+ /* rcu_shutdown can only be called singlethreaded, and it does a
+ * pthread_join, so it should be impossible that anything ended up
+ * on the queue after RCUA_END
+ */
+#if 1
+ assert(!rcu_heads_first(&rcu_heads));
+#else
+ while ((rh = rcu_heads_pop(&rcu_heads)))
+ if (rh->action->type >= RCUA_FREE)
+ rcu_do(rh);
+#endif
+ return NULL;
+}
+
+void rcu_shutdown(void)
+{
+ static struct rcu_head rcu_head_end;
+ struct rcu_thread *rt = rcu_self();
+ void *retval;
+
+ if (!rcu_active)
+ return;
+
+ rcu_assert_read_locked();
+ assert(rcu_threads_count(&rcu_threads) == 1);
+
+ rcu_enqueue(&rcu_head_end, &rcua_end);
+
+ rt->depth = 0;
+ seqlock_release(&rt->rcu);
+ seqlock_release(&rcu_seq);
+ rcu_active = false;
+
+ /* clearing rcu_active is before pthread_join in case we hang in
+ * pthread_join & get a SIGTERM or something - in that case, just
+ * ignore the maybe-still-running RCU thread
+ */
+ if (pthread_join(rcu_pthread, &retval) == 0) {
+ seqlock_acquire_val(&rcu_seq, SEQLOCK_STARTVAL);
+ seqlock_acquire_val(&rt->rcu, SEQLOCK_STARTVAL);
+ rt->depth = 1;
+ }
+}
+
+/*
+ * RCU'd free functions
+ */
+
+void rcu_enqueue(struct rcu_head *rh, const struct rcu_action *action)
+{
+ /* refer to rcu_bump() for why we need to hold RCU when adding items
+ * to rcu_heads
+ */
+ rcu_assert_read_locked();
+
+ rh->action = action;
+
+ if (!rcu_active) {
+ rcu_do(rh);
+ return;
+ }
+ rcu_heads_add_tail(&rcu_heads, rh);
+ atomic_store_explicit(&rcu_dirty, seqlock_cur(&rcu_seq),
+ memory_order_relaxed);
+}
+
+void rcu_close(struct rcu_head_close *rhc, int fd)
+{
+ rhc->fd = fd;
+ rcu_enqueue(&rhc->rcu_head, &rcua_close);
+}
diff --git a/lib/frrcu.h b/lib/frrcu.h
new file mode 100644
index 0000000000..8f789303cc
--- /dev/null
+++ b/lib/frrcu.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 2017-19 David Lamparter, for NetDEF, Inc.
+ *
+ * Permission to use, copy, modify, and 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 THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+#ifndef _FRRCU_H
+#define _FRRCU_H
+
+#include "memory.h"
+#include "atomlist.h"
+#include "seqlock.h"
+
+/* quick RCU primer:
+ * There's a global sequence counter. Whenever a thread does a
+ * rcu_read_lock(), it is marked as holding the current sequence counter.
+ * When something is cleaned with RCU, the global sequence counter is
+ * increased and the item is queued for cleanup - *after* all threads are
+ * at a more recent sequence counter (or no sequence counter / unheld).
+ *
+ * So, by delaying resource cleanup, RCU ensures that things don't go away
+ * while another thread may hold a (stale) reference.
+ *
+ * Note that even if a thread is in rcu_read_lock(), it is invalid for that
+ * thread to access bits after rcu_free() & co on them. This is a design
+ * choice to allow no-op'ing out the entire RCU mechanism if we're running
+ * singlethreaded. (Also allows some optimization on the counter bumping.)
+ *
+ * differences from Linux Kernel RCU:
+ * - there's no rcu_synchronize(), if you really need to defer something
+ * use rcu_call() (and double check it's really necessary)
+ * - rcu_dereference() and rcu_assign_pointer() don't exist, use atomic_*
+ * instead (ATOM* list structures do the right thing)
+ */
+
+/* opaque */
+struct rcu_thread;
+
+/* called before new thread creation, sets up rcu thread info for new thread
+ * before it actually exits. This ensures possible RCU references are held
+ * for thread startup.
+ *
+ * return value must be passed into the new thread's call to rcu_thread_start()
+ */
+extern struct rcu_thread *rcu_thread_prepare(void);
+
+/* cleanup in case pthread_create() fails */
+extern void rcu_thread_unprepare(struct rcu_thread *rcu_thread);
+
+/* called early in the new thread, with the return value from the above.
+ * NB: new thread is initially in RCU-held state! (at depth 1)
+ *
+ * TBD: maybe inherit RCU state from rcu_thread_prepare()?
+ */
+extern void rcu_thread_start(struct rcu_thread *rcu_thread);
+
+/* thread exit is handled through pthread_key_create's destructor function */
+
+/* global RCU shutdown - must be called with only 1 active thread left. waits
+ * until remaining RCU actions are done & RCU thread has exited.
+ *
+ * This is mostly here to get a clean exit without memleaks.
+ */
+extern void rcu_shutdown(void);
+
+/* enter / exit RCU-held state. counter-based, so can be called nested. */
+extern void rcu_read_lock(void);
+extern void rcu_read_unlock(void);
+
+/* for debugging / safety checks */
+extern void rcu_assert_read_locked(void);
+extern void rcu_assert_read_unlocked(void);
+
+enum rcu_action_type {
+ RCUA_INVALID = 0,
+ /* used internally by the RCU code, shouldn't ever show up outside */
+ RCUA_NEXT,
+ RCUA_END,
+ /* normal RCU actions, for outside use */
+ RCUA_FREE,
+ RCUA_CLOSE,
+ RCUA_CALL,
+};
+
+/* since rcu_head is intended to be embedded into structs which may exist
+ * with lots of copies, rcu_head is shrunk down to its absolute minimum -
+ * the atomlist pointer + a pointer to this action struct.
+ */
+struct rcu_action {
+ enum rcu_action_type type;
+
+ union {
+ struct {
+ struct memtype *mt;
+ ptrdiff_t offset;
+ } free;
+
+ struct {
+ void (*fptr)(void *arg);
+ ptrdiff_t offset;
+ } call;
+ } u;
+};
+
+/* RCU cleanup function queue item */
+PREDECL_ATOMLIST(rcu_heads)
+struct rcu_head {
+ struct rcu_heads_item head;
+ const struct rcu_action *action;
+};
+
+/* special RCU head for delayed fd-close */
+struct rcu_head_close {
+ struct rcu_head rcu_head;
+ int fd;
+};
+
+/* enqueue RCU action - use the macros below to get the rcu_action set up */
+extern void rcu_enqueue(struct rcu_head *head, const struct rcu_action *action);
+
+/* RCU free() and file close() operations.
+ *
+ * freed memory / closed fds become _immediately_ unavailable to the calling
+ * thread, but will remain available for other threads until they have passed
+ * into RCU-released state.
+ */
+
+/* may be called with NULL mt to do non-MTYPE free() */
+#define rcu_free(mtype, ptr, field) \
+ do { \
+ typeof(ptr) _ptr = (ptr); \
+ struct rcu_head *_rcu_head = &_ptr->field; \
+ static const struct rcu_action _rcu_action = { \
+ .type = RCUA_FREE, \
+ .u.free = { \
+ .mt = mtype, \
+ .offset = offsetof(typeof(*_ptr), field), \
+ }, \
+ }; \
+ rcu_enqueue(_rcu_head, &_rcu_action); \
+ } while (0)
+
+/* use this sparingly, it runs on (and blocks) the RCU thread */
+#define rcu_call(func, ptr, field) \
+ do { \
+ typeof(ptr) _ptr = (ptr); \
+ void (*fptype)(typeof(ptr)); \
+ struct rcu_head *_rcu_head = &_ptr->field; \
+ static const struct rcu_action _rcu_action = { \
+ .type = RCUA_CALL, \
+ .u.call = { \
+ .fptr = (void *)func, \
+ .offset = offsetof(typeof(*_ptr), field), \
+ }, \
+ }; \
+ (void)(_fptype = func); \
+ rcu_enqueue(_rcu_head, &_rcu_action); \
+ } while (0)
+
+extern void rcu_close(struct rcu_head_close *head, int fd);
+
+#endif /* _FRRCU_H */
diff --git a/lib/if.c b/lib/if.c
index f7a167f251..5f92327562 100644
--- a/lib/if.c
+++ b/lib/if.c
@@ -45,6 +45,8 @@ DEFINE_MTYPE_STATIC(LIB, NBR_CONNECTED, "Neighbor Connected")
DEFINE_MTYPE(LIB, CONNECTED_LABEL, "Connected interface label")
DEFINE_MTYPE_STATIC(LIB, IF_LINK_PARAMS, "Informational Link Parameters")
+static struct interface *if_lookup_by_ifindex(ifindex_t ifindex,
+ vrf_id_t vrf_id);
static int if_cmp_func(const struct interface *, const struct interface *);
static int if_cmp_index_func(const struct interface *ifp1,
const struct interface *ifp2);
@@ -132,18 +134,26 @@ static int if_cmp_index_func(const struct interface *ifp1,
}
/* Create new interface structure. */
-struct interface *if_create(const char *name, vrf_id_t vrf_id)
+static struct interface *if_create_backend(const char *name, ifindex_t ifindex,
+ vrf_id_t vrf_id)
{
struct vrf *vrf = vrf_get(vrf_id, NULL);
struct interface *ifp;
ifp = XCALLOC(MTYPE_IF, sizeof(struct interface));
- ifp->ifindex = IFINDEX_INTERNAL;
-
- assert(name);
- strlcpy(ifp->name, name, sizeof(ifp->name));
ifp->vrf_id = vrf_id;
- IFNAME_RB_INSERT(vrf, ifp);
+
+ if (name) {
+ strlcpy(ifp->name, name, sizeof(ifp->name));
+ IFNAME_RB_INSERT(vrf, ifp);
+ } else
+ ifp->name[0] = '\0';
+
+ if (ifindex != IFINDEX_INTERNAL)
+ if_set_index(ifp, ifindex);
+ else
+ ifp->ifindex = ifindex; /* doesn't add it to the list */
+
ifp->connected = list_new();
ifp->connected->del = (void (*)(void *))connected_free;
@@ -158,6 +168,16 @@ struct interface *if_create(const char *name, vrf_id_t vrf_id)
return ifp;
}
+struct interface *if_create(const char *name, vrf_id_t vrf_id)
+{
+ return if_create_backend(name, IFINDEX_INTERNAL, vrf_id);
+}
+
+struct interface *if_create_ifindex(ifindex_t ifindex, vrf_id_t vrf_id)
+{
+ return if_create_backend(NULL, ifindex, vrf_id);
+}
+
/* Create new interface structure. */
void if_update_to_new_vrf(struct interface *ifp, vrf_id_t vrf_id)
{
@@ -239,8 +259,9 @@ void if_delete(struct interface *ifp)
XFREE(MTYPE_IF, ifp);
}
-/* Interface existance check by index. */
-struct interface *if_lookup_by_index(ifindex_t ifindex, vrf_id_t vrf_id)
+/* Used only internally to check within VRF only */
+static struct interface *if_lookup_by_ifindex(ifindex_t ifindex,
+ vrf_id_t vrf_id)
{
struct vrf *vrf;
struct interface if_tmp;
@@ -253,6 +274,19 @@ struct interface *if_lookup_by_index(ifindex_t ifindex, vrf_id_t vrf_id)
return RB_FIND(if_index_head, &vrf->ifaces_by_index, &if_tmp);
}
+/* Interface existance check by index. */
+struct interface *if_lookup_by_index(ifindex_t ifindex, vrf_id_t vrf_id)
+{
+ switch (vrf_get_backend()) {
+ case VRF_BACKEND_UNKNOWN:
+ case VRF_BACKEND_NETNS:
+ return(if_lookup_by_ifindex(ifindex, vrf_id));
+ case VRF_BACKEND_VRF_LITE:
+ return(if_lookup_by_index_all_vrf(ifindex));
+ }
+ return NULL;
+}
+
const char *ifindex2ifname(ifindex_t ifindex, vrf_id_t vrf_id)
{
struct interface *ifp;
@@ -302,7 +336,24 @@ struct interface *if_lookup_by_name_all_vrf(const char *name)
return NULL;
}
-/* Lookup interface by IPv4 address. */
+struct interface *if_lookup_by_index_all_vrf(ifindex_t ifindex)
+{
+ struct vrf *vrf;
+ struct interface *ifp;
+
+ if (ifindex == IFINDEX_INTERNAL)
+ return NULL;
+
+ RB_FOREACH (vrf, vrf_id_head, &vrfs_by_id) {
+ ifp = if_lookup_by_ifindex(ifindex, vrf->vrf_id);
+ if (ifp)
+ return ifp;
+ }
+
+ return NULL;
+}
+
+/* Lookup interface by IP address. */
struct interface *if_lookup_exact_address(void *src, int family,
vrf_id_t vrf_id)
{
@@ -334,7 +385,7 @@ struct interface *if_lookup_exact_address(void *src, int family,
return NULL;
}
-/* Lookup interface by IPv4 address. */
+/* Lookup interface by IP address. */
struct connected *if_lookup_address(void *matchaddr, int family,
vrf_id_t vrf_id)
{
@@ -447,6 +498,34 @@ struct interface *if_get_by_name(const char *name, vrf_id_t vrf_id)
return NULL;
}
+struct interface *if_get_by_ifindex(ifindex_t ifindex, vrf_id_t vrf_id)
+{
+ struct interface *ifp;
+
+ switch (vrf_get_backend()) {
+ case VRF_BACKEND_UNKNOWN:
+ case VRF_BACKEND_NETNS:
+ ifp = if_lookup_by_ifindex(ifindex, vrf_id);
+ if (ifp)
+ return ifp;
+ return if_create_ifindex(ifindex, vrf_id);
+ case VRF_BACKEND_VRF_LITE:
+ ifp = if_lookup_by_index_all_vrf(ifindex);
+ if (ifp) {
+ if (ifp->vrf_id == vrf_id)
+ return ifp;
+ /* If it came from the kernel or by way of zclient,
+ * believe it and update the ifp accordingly.
+ */
+ if_update_to_new_vrf(ifp, vrf_id);
+ return ifp;
+ }
+ return if_create_ifindex(ifindex, vrf_id);
+ }
+
+ return NULL;
+}
+
void if_set_index(struct interface *ifp, ifindex_t ifindex)
{
struct vrf *vrf;
diff --git a/lib/if.h b/lib/if.h
index 603c9c3780..e3ec278f9f 100644
--- a/lib/if.h
+++ b/lib/if.h
@@ -383,16 +383,12 @@ struct connected {
/* N.B. the ZEBRA_IFA_PEER flag should be set if and only if
a peer address has been configured. If this flag is set,
the destination field must contain the peer address.
- Otherwise, if this flag is not set, the destination address
- will either contain a broadcast address or be NULL.
*/
/* Address of connected network. */
struct prefix *address;
- /* Peer or Broadcast address, depending on whether ZEBRA_IFA_PEER is
- set.
- Note: destination may be NULL if ZEBRA_IFA_PEER is not set. */
+ /* Peer address, if ZEBRA_IFA_PEER is set, otherwise NULL */
struct prefix *destination;
/* Label for Linux 2.2.X and upper. */
@@ -478,7 +474,9 @@ extern int if_cmp_name_func(const char *p1, const char *p2);
*/
extern void if_update_to_new_vrf(struct interface *, vrf_id_t vrf_id);
extern struct interface *if_create(const char *name, vrf_id_t vrf_id);
+extern struct interface *if_create_ifindex(ifindex_t ifindex, vrf_id_t vrf_id);
extern struct interface *if_lookup_by_index(ifindex_t, vrf_id_t vrf_id);
+extern struct interface *if_lookup_by_index_all_vrf(ifindex_t);
extern struct interface *if_lookup_exact_address(void *matchaddr, int family,
vrf_id_t vrf_id);
extern struct connected *if_lookup_address(void *matchaddr, int family,
@@ -493,6 +491,7 @@ size_t if_lookup_by_hwaddr(const uint8_t *hw_addr, size_t addrsz,
extern struct interface *if_lookup_by_name_all_vrf(const char *ifname);
extern struct interface *if_lookup_by_name(const char *ifname, vrf_id_t vrf_id);
extern struct interface *if_get_by_name(const char *ifname, vrf_id_t vrf_id);
+extern struct interface *if_get_by_ifindex(ifindex_t ifindex, vrf_id_t vrf_id);
extern void if_set_index(struct interface *ifp, ifindex_t ifindex);
/* Delete the interface, but do not free the structure, and leave it in the
diff --git a/lib/libfrr.c b/lib/libfrr.c
index 0fc321d6e0..4301dc20ad 100644
--- a/lib/libfrr.c
+++ b/lib/libfrr.c
@@ -41,6 +41,7 @@
#include "northbound_cli.h"
#include "northbound_db.h"
#include "debug.h"
+#include "frrcu.h"
DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm))
DEFINE_KOOH(frr_early_fini, (), ())
@@ -409,7 +410,7 @@ static int frr_opt(int opt)
}
if (di->zpathspace)
fprintf(stderr,
- "-N option overriden by -z for zebra named socket path\n");
+ "-N option overridden by -z for zebra named socket path\n");
if (strchr(optarg, '/') || strchr(optarg, '.')) {
fprintf(stderr,
@@ -1081,6 +1082,7 @@ void frr_fini(void)
master = NULL;
closezlog();
/* frrmod_init -> nothing needed / hooks */
+ rcu_shutdown();
if (!debug_memstats_at_exit)
return;
diff --git a/lib/log.c b/lib/log.c
index c577239908..51a0ddd6b7 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -559,9 +559,7 @@ static void crash_write(struct fbuf *fb, char *msgstart)
void zlog_signal(int signo, const char *action, void *siginfo_v,
void *program_counter)
{
-#ifdef SA_SIGINFO
siginfo_t *siginfo = siginfo_v;
-#endif
time_t now;
char buf[sizeof("DEFAULT: Received signal S at T (si_addr 0xP, PC 0xP); aborting...")
+ 100];
@@ -575,7 +573,6 @@ void zlog_signal(int signo, const char *action, void *siginfo_v,
msgstart = fb.pos;
bprintfrr(&fb, "Received signal %d at %lld", signo, (long long)now);
-#ifdef SA_SIGINFO
if (program_counter)
bprintfrr(&fb, " (si_addr 0x%tx, PC 0x%tx)",
(ptrdiff_t)siginfo->si_addr,
@@ -583,7 +580,6 @@ void zlog_signal(int signo, const char *action, void *siginfo_v,
else
bprintfrr(&fb, " (si_addr 0x%tx)",
(ptrdiff_t)siginfo->si_addr);
-#endif /* SA_SIGINFO */
bprintfrr(&fb, "; %s\n", action);
crash_write(&fb, msgstart);
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/northbound.h b/lib/northbound.h
index 69d7c8e0ee..ce79d907f9 100644
--- a/lib/northbound.h
+++ b/lib/northbound.h
@@ -347,15 +347,6 @@ struct nb_callbacks {
* dnode
* libyang data node that should be shown in the form of a CLI
* command.
- *
- * show_defaults
- * Specify whether to display default configuration values or not.
- * This parameter can be ignored most of the time since the
- * northbound doesn't call this callback for default leaves or
- * non-presence containers that contain only default child nodes.
- * The exception are commands associated to multiple configuration
- * nodes, in which case it might be desirable to hide one or more
- * parts of the command when this parameter is set to false.
*/
void (*cli_show_end)(struct vty *vty, struct lyd_node *dnode);
};
diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c
index c691bb27aa..884c01a457 100644
--- a/lib/northbound_cli.c
+++ b/lib/northbound_cli.c
@@ -757,7 +757,7 @@ DEFPY (config_load,
"configuration load\
<\
file [<json$json|xml$xml> [translate WORD$translator_family]] FILENAME$filename\
- |transaction (1-4294967296)$tid\
+ |transaction (1-4294967295)$tid\
>\
[replace$replace]",
"Configuration related settings\n"
@@ -923,12 +923,12 @@ DEFPY (show_config_compare,
<\
candidate$c1_candidate\
|running$c1_running\
- |transaction (1-4294967296)$c1_tid\
+ |transaction (1-4294967295)$c1_tid\
>\
<\
candidate$c2_candidate\
|running$c2_running\
- |transaction (1-4294967296)$c2_tid\
+ |transaction (1-4294967295)$c2_tid\
>\
[<json$json|xml$xml> [translate WORD$translator_family]]",
SHOW_STR
@@ -1029,11 +1029,11 @@ ALIAS (show_config_compare,
"show configuration compare\
<\
running$c1_running\
- |transaction (1-4294967296)$c1_tid\
+ |transaction (1-4294967295)$c1_tid\
>\
<\
running$c2_running\
- |transaction (1-4294967296)$c2_tid\
+ |transaction (1-4294967295)$c2_tid\
>\
[<json$json|xml$xml> [translate WORD$translator_family]]",
SHOW_STR
@@ -1192,7 +1192,7 @@ DEFPY (show_config_transaction,
show_config_transaction_cmd,
"show configuration transaction\
[\
- (1-4294967296)$transaction_id\
+ (1-4294967295)$transaction_id\
[<json$json|xml$xml> [translate WORD$translator_family]]\
[<\
with-defaults$with_defaults\
@@ -1593,7 +1593,7 @@ static int nb_cli_rollback_configuration(struct vty *vty,
DEFPY (rollback_config,
rollback_config_cmd,
- "rollback configuration (1-4294967296)$transaction_id",
+ "rollback configuration (1-4294967295)$transaction_id",
"Rollback to a previous state\n"
"Running configuration\n"
"Transaction ID\n")
diff --git a/lib/pqueue.c b/lib/pqueue.c
deleted file mode 100644
index 87b54a681a..0000000000
--- a/lib/pqueue.c
+++ /dev/null
@@ -1,185 +0,0 @@
-/* Priority queue functions.
- * Copyright (C) 2003 Yasuhiro Ohara
- *
- * This file is part of GNU Zebra.
- *
- * GNU Zebra is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * GNU Zebra is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; see the file COPYING; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#include <zebra.h>
-
-#include "memory.h"
-#include "pqueue.h"
-
-DEFINE_MTYPE_STATIC(LIB, PQUEUE, "Priority queue")
-DEFINE_MTYPE_STATIC(LIB, PQUEUE_DATA, "Priority queue data")
-
-/* priority queue using heap sort */
-
-/* pqueue->cmp() controls the order of sorting (i.e, ascending or
- descending). If you want the left node to move upper of the heap
- binary tree, make cmp() to return less than 0. for example, if cmp
- (10, 20) returns -1, the sorting is ascending order. if cmp (10,
- 20) returns 1, the sorting is descending order. if cmp (10, 20)
- returns 0, this library does not do sorting (which will not be what
- you want). To be brief, if the contents of cmp_func (left, right)
- is left - right, dequeue () returns the smallest node. Otherwise
- (if the contents is right - left), dequeue () returns the largest
- node. */
-
-#define DATA_SIZE (sizeof (void *))
-#define PARENT_OF(x) ((x - 1) / 2)
-#define LEFT_OF(x) (2 * x + 1)
-#define RIGHT_OF(x) (2 * x + 2)
-#define HAVE_CHILD(x,q) (x < (q)->size / 2)
-
-void trickle_up(int index, struct pqueue *queue)
-{
- void *tmp;
-
- /* Save current node as tmp node. */
- tmp = queue->array[index];
-
- /* Continue until the node reaches top or the place where the parent
- node should be upper than the tmp node. */
- while (index > 0
- && (*queue->cmp)(tmp, queue->array[PARENT_OF(index)]) < 0) {
- /* actually trickle up */
- queue->array[index] = queue->array[PARENT_OF(index)];
- if (queue->update != NULL)
- (*queue->update)(queue->array[index], index);
- index = PARENT_OF(index);
- }
-
- /* Restore the tmp node to appropriate place. */
- queue->array[index] = tmp;
- if (queue->update != NULL)
- (*queue->update)(tmp, index);
-}
-
-void trickle_down(int index, struct pqueue *queue)
-{
- void *tmp;
- int which;
-
- /* Save current node as tmp node. */
- tmp = queue->array[index];
-
- /* Continue until the node have at least one (left) child. */
- while (HAVE_CHILD(index, queue)) {
- /* If right child exists, and if the right child is more proper
- to be moved upper. */
- if (RIGHT_OF(index) < queue->size
- && (*queue->cmp)(queue->array[LEFT_OF(index)],
- queue->array[RIGHT_OF(index)])
- > 0)
- which = RIGHT_OF(index);
- else
- which = LEFT_OF(index);
-
- /* If the tmp node should be upper than the child, break. */
- if ((*queue->cmp)(queue->array[which], tmp) > 0)
- break;
-
- /* Actually trickle down the tmp node. */
- queue->array[index] = queue->array[which];
- if (queue->update != NULL)
- (*queue->update)(queue->array[index], index);
- index = which;
- }
-
- /* Restore the tmp node to appropriate place. */
- queue->array[index] = tmp;
- if (queue->update != NULL)
- (*queue->update)(tmp, index);
-}
-
-struct pqueue *pqueue_create(void)
-{
- struct pqueue *queue;
-
- queue = XCALLOC(MTYPE_PQUEUE, sizeof(struct pqueue));
-
- queue->array =
- XCALLOC(MTYPE_PQUEUE_DATA, DATA_SIZE * PQUEUE_INIT_ARRAYSIZE);
- queue->array_size = PQUEUE_INIT_ARRAYSIZE;
-
- /* By default we want nothing to happen when a node changes. */
- queue->update = NULL;
- return queue;
-}
-
-void pqueue_delete(struct pqueue *queue)
-{
- XFREE(MTYPE_PQUEUE_DATA, queue->array);
- XFREE(MTYPE_PQUEUE, queue);
-}
-
-static int pqueue_expand(struct pqueue *queue)
-{
- void **newarray;
-
- newarray =
- XCALLOC(MTYPE_PQUEUE_DATA, queue->array_size * DATA_SIZE * 2);
-
- memcpy(newarray, queue->array, queue->array_size * DATA_SIZE);
-
- XFREE(MTYPE_PQUEUE_DATA, queue->array);
- queue->array = newarray;
- queue->array_size *= 2;
-
- return 1;
-}
-
-void pqueue_enqueue(void *data, struct pqueue *queue)
-{
- if (queue->size + 2 >= queue->array_size && !pqueue_expand(queue))
- return;
-
- queue->array[queue->size] = data;
- if (queue->update != NULL)
- (*queue->update)(data, queue->size);
- trickle_up(queue->size, queue);
- queue->size++;
-}
-
-void *pqueue_dequeue(struct pqueue *queue)
-{
- void *data = queue->array[0];
- queue->array[0] = queue->array[--queue->size];
- trickle_down(0, queue);
- return data;
-}
-
-void pqueue_remove_at(int index, struct pqueue *queue)
-{
- queue->array[index] = queue->array[--queue->size];
-
- if (index > 0
- && (*queue->cmp)(queue->array[index],
- queue->array[PARENT_OF(index)])
- < 0) {
- trickle_up(index, queue);
- } else {
- trickle_down(index, queue);
- }
-}
-
-void pqueue_remove(void *data, struct pqueue *queue)
-{
- for (int i = 0; i < queue->size; i++)
- if (queue->array[i] == data)
- pqueue_remove_at(i, queue);
-}
diff --git a/lib/pqueue.h b/lib/pqueue.h
deleted file mode 100644
index 032ee9db4c..0000000000
--- a/lib/pqueue.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/* Priority queue functions.
- * Copyright (C) 2003 Yasuhiro Ohara
- *
- * This file is part of GNU Zebra.
- *
- * GNU Zebra is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published
- * by the Free Software Foundation; either version 2, or (at your
- * option) any later version.
- *
- * GNU Zebra is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; see the file COPYING; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-#ifndef _ZEBRA_PQUEUE_H
-#define _ZEBRA_PQUEUE_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct pqueue {
- void **array;
- int array_size;
- int size;
-
- int (*cmp)(void *, void *);
- void (*update)(void *node, int actual_position);
-};
-
-#define PQUEUE_INIT_ARRAYSIZE 32
-
-extern struct pqueue *pqueue_create(void);
-extern void pqueue_delete(struct pqueue *queue);
-
-extern void pqueue_enqueue(void *data, struct pqueue *queue);
-extern void *pqueue_dequeue(struct pqueue *queue);
-extern void pqueue_remove_at(int index, struct pqueue *queue);
-extern void pqueue_remove(void *data, struct pqueue *queue);
-
-extern void trickle_down(int index, struct pqueue *queue);
-extern void trickle_up(int index, struct pqueue *queue);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* _ZEBRA_PQUEUE_H */
diff --git a/lib/prefix.c b/lib/prefix.c
index 1a4a914e05..ad8dea273e 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -853,7 +853,7 @@ void prefix_ipv4_free(struct prefix_ipv4 *p)
prefix_free((struct prefix *)p);
}
-/* When string format is invalid return 0. */
+/* If given string is valid return 1 else return 0 */
int str2prefix_ipv4(const char *str, struct prefix_ipv4 *p)
{
int ret;
@@ -881,8 +881,10 @@ int str2prefix_ipv4(const char *str, struct prefix_ipv4 *p)
cp = XMALLOC(MTYPE_TMP, (pnt - str) + 1);
memcpy(cp, str, pnt - str);
*(cp + (pnt - str)) = '\0';
- ret = inet_aton(cp, &p->prefix);
+ ret = inet_pton(AF_INET, cp, &p->prefix);
XFREE(MTYPE_TMP, cp);
+ if (ret == 0)
+ return 0;
/* Get prefix length. */
plen = (uint8_t)atoi(++pnt);
@@ -1023,7 +1025,7 @@ void prefix_ipv6_free(struct prefix_ipv6 *p)
prefix_free((struct prefix *)p);
}
-/* If given string is valid return pin6 else return NULL */
+/* If given string is valid return 1 else return 0 */
int str2prefix_ipv6(const char *str, struct prefix_ipv6 *p)
{
char *pnt;
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/seqlock.c b/lib/seqlock.c
index 223d14952c..c05ec19db4 100644
--- a/lib/seqlock.c
+++ b/lib/seqlock.c
@@ -25,6 +25,7 @@
#include "config.h"
#endif
+#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <errno.h>
@@ -35,44 +36,75 @@
#include "seqlock.h"
+/****************************************
+ * OS specific synchronization wrappers *
+ ****************************************/
+
+/*
+ * Linux: sys_futex()
+ */
#ifdef HAVE_SYNC_LINUX_FUTEX
-/* Linux-specific - sys_futex() */
#include <sys/syscall.h>
#include <linux/futex.h>
-static long sys_futex(void *addr1, int op, int val1, struct timespec *timeout,
- void *addr2, int val3)
+static long sys_futex(void *addr1, int op, int val1,
+ const struct timespec *timeout, void *addr2, int val3)
{
return syscall(SYS_futex, addr1, op, val1, timeout, addr2, val3);
}
#define wait_once(sqlo, val) \
sys_futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0)
+#define wait_time(sqlo, val, time, reltime) \
+ sys_futex((int *)&sqlo->pos, FUTEX_WAIT_BITSET, (int)val, time, \
+ NULL, ~0U)
#define wait_poke(sqlo) \
sys_futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0)
+/*
+ * OpenBSD: sys_futex(), almost the same as on Linux
+ */
#elif defined(HAVE_SYNC_OPENBSD_FUTEX)
-/* OpenBSD variant of the above. untested, not upstream in OpenBSD. */
#include <sys/syscall.h>
#include <sys/futex.h>
+#define TIME_RELATIVE 1
+
#define wait_once(sqlo, val) \
futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, NULL, NULL, 0)
+#define wait_time(sqlo, val, time, reltime) \
+ futex((int *)&sqlo->pos, FUTEX_WAIT, (int)val, reltime, NULL, 0)
#define wait_poke(sqlo) \
futex((int *)&sqlo->pos, FUTEX_WAKE, INT_MAX, NULL, NULL, 0)
+/*
+ * FreeBSD: _umtx_op()
+ */
#elif defined(HAVE_SYNC_UMTX_OP)
-/* FreeBSD-specific: umtx_op() */
#include <sys/umtx.h>
#define wait_once(sqlo, val) \
_umtx_op((void *)&sqlo->pos, UMTX_OP_WAIT_UINT, val, NULL, NULL)
+static int wait_time(struct seqlock *sqlo, uint32_t val,
+ const struct timespec *abstime,
+ const struct timespec *reltime)
+{
+ struct _umtx_time t;
+ t._flags = UMTX_ABSTIME;
+ t._clockid = CLOCK_MONOTONIC;
+ memcpy(&t._timeout, abstime, sizeof(t._timeout));
+ return _umtx_op((void *)&sqlo->pos, UMTX_OP_WAIT_UINT, val,
+ (void *)(uintptr_t) sizeof(t), &t);
+}
#define wait_poke(sqlo) \
_umtx_op((void *)&sqlo->pos, UMTX_OP_WAKE, INT_MAX, NULL, NULL)
-#else
-/* generic version. used on *BSD, Solaris and OSX.
+/*
+ * generic version. used on NetBSD, Solaris and OSX. really shitty.
*/
+#else
+
+#define TIME_ABS_REALTIME 1
#define wait_init(sqlo) do { \
pthread_mutex_init(&sqlo->lock, NULL); \
@@ -80,6 +112,9 @@ static long sys_futex(void *addr1, int op, int val1, struct timespec *timeout,
} while (0)
#define wait_prep(sqlo) pthread_mutex_lock(&sqlo->lock)
#define wait_once(sqlo, val) pthread_cond_wait(&sqlo->wake, &sqlo->lock)
+#define wait_time(sqlo, val, time, reltime) \
+ pthread_cond_timedwait(&sqlo->wake, \
+ &sqlo->lock, time);
#define wait_done(sqlo) pthread_mutex_unlock(&sqlo->lock)
#define wait_poke(sqlo) do { \
pthread_mutex_lock(&sqlo->lock); \
@@ -103,18 +138,112 @@ void seqlock_wait(struct seqlock *sqlo, seqlock_val_t val)
seqlock_assert_valid(val);
wait_prep(sqlo);
- while (1) {
- cur = atomic_load_explicit(&sqlo->pos, memory_order_acquire);
- if (!(cur & 1))
+ cur = atomic_load_explicit(&sqlo->pos, memory_order_relaxed);
+
+ while (cur & SEQLOCK_HELD) {
+ cal = SEQLOCK_VAL(cur) - val - 1;
+ assert(cal < 0x40000000 || cal > 0xc0000000);
+ if (cal < 0x80000000)
break;
- cal = cur - val - 1;
+
+ if ((cur & SEQLOCK_WAITERS)
+ || atomic_compare_exchange_weak_explicit(
+ &sqlo->pos, &cur, cur | SEQLOCK_WAITERS,
+ memory_order_relaxed, memory_order_relaxed)) {
+ wait_once(sqlo, cur | SEQLOCK_WAITERS);
+ cur = atomic_load_explicit(&sqlo->pos,
+ memory_order_relaxed);
+ }
+ /* else: we failed to swap in cur because it just changed */
+ }
+ wait_done(sqlo);
+}
+
+bool seqlock_timedwait(struct seqlock *sqlo, seqlock_val_t val,
+ const struct timespec *abs_monotime_limit)
+{
+/*
+ * ABS_REALTIME - used on NetBSD, Solaris and OSX
+ */
+#if TIME_ABS_REALTIME
+#define time_arg1 &abs_rt
+#define time_arg2 NULL
+#define time_prep
+ struct timespec curmono, abs_rt;
+
+ clock_gettime(CLOCK_MONOTONIC, &curmono);
+ clock_gettime(CLOCK_REALTIME, &abs_rt);
+
+ abs_rt.tv_nsec += abs_monotime_limit->tv_nsec - curmono.tv_nsec;
+ if (abs_rt.tv_nsec < 0) {
+ abs_rt.tv_sec--;
+ abs_rt.tv_nsec += 1000000000;
+ } else if (abs_rt.tv_nsec >= 1000000000) {
+ abs_rt.tv_sec++;
+ abs_rt.tv_nsec -= 1000000000;
+ }
+ abs_rt.tv_sec += abs_monotime_limit->tv_sec - curmono.tv_sec;
+
+/*
+ * RELATIVE - used on OpenBSD (might get a patch to get absolute monotime)
+ */
+#elif TIME_RELATIVE
+ struct timespec reltime;
+
+#define time_arg1 abs_monotime_limit
+#define time_arg2 &reltime
+#define time_prep \
+ clock_gettime(CLOCK_MONOTONIC, &reltime); \
+ reltime.tv_sec = abs_monotime_limit.tv_sec - reltime.tv_sec; \
+ reltime.tv_nsec = abs_monotime_limit.tv_nsec - reltime.tv_nsec; \
+ if (reltime.tv_nsec < 0) { \
+ reltime.tv_sec--; \
+ reltime.tv_nsec += 1000000000; \
+ }
+/*
+ * FreeBSD & Linux: absolute time re. CLOCK_MONOTONIC
+ */
+#else
+#define time_arg1 abs_monotime_limit
+#define time_arg2 NULL
+#define time_prep
+#endif
+
+ bool ret = true;
+ seqlock_val_t cur, cal;
+
+ seqlock_assert_valid(val);
+
+ wait_prep(sqlo);
+ cur = atomic_load_explicit(&sqlo->pos, memory_order_relaxed);
+
+ while (cur & SEQLOCK_HELD) {
+ cal = SEQLOCK_VAL(cur) - val - 1;
assert(cal < 0x40000000 || cal > 0xc0000000);
if (cal < 0x80000000)
break;
- wait_once(sqlo, cur);
+ if ((cur & SEQLOCK_WAITERS)
+ || atomic_compare_exchange_weak_explicit(
+ &sqlo->pos, &cur, cur | SEQLOCK_WAITERS,
+ memory_order_relaxed, memory_order_relaxed)) {
+ int rv;
+
+ time_prep
+
+ rv = wait_time(sqlo, cur | SEQLOCK_WAITERS, time_arg1,
+ time_arg2);
+ if (rv) {
+ ret = false;
+ break;
+ }
+ cur = atomic_load_explicit(&sqlo->pos,
+ memory_order_relaxed);
+ }
}
wait_done(sqlo);
+
+ return ret;
}
bool seqlock_check(struct seqlock *sqlo, seqlock_val_t val)
@@ -123,26 +252,32 @@ bool seqlock_check(struct seqlock *sqlo, seqlock_val_t val)
seqlock_assert_valid(val);
- cur = atomic_load_explicit(&sqlo->pos, memory_order_acquire);
- if (!(cur & 1))
+ cur = atomic_load_explicit(&sqlo->pos, memory_order_relaxed);
+ if (!(cur & SEQLOCK_HELD))
return 1;
- cur -= val;
+ cur = SEQLOCK_VAL(cur) - val - 1;
assert(cur < 0x40000000 || cur > 0xc0000000);
return cur < 0x80000000;
}
void seqlock_acquire_val(struct seqlock *sqlo, seqlock_val_t val)
{
+ seqlock_val_t prev;
+
seqlock_assert_valid(val);
- atomic_store_explicit(&sqlo->pos, val, memory_order_release);
- wait_poke(sqlo);
+ prev = atomic_exchange_explicit(&sqlo->pos, val, memory_order_relaxed);
+ if (prev & SEQLOCK_WAITERS)
+ wait_poke(sqlo);
}
void seqlock_release(struct seqlock *sqlo)
{
- atomic_store_explicit(&sqlo->pos, 0, memory_order_release);
- wait_poke(sqlo);
+ seqlock_val_t prev;
+
+ prev = atomic_exchange_explicit(&sqlo->pos, 0, memory_order_relaxed);
+ if (prev & SEQLOCK_WAITERS)
+ wait_poke(sqlo);
}
void seqlock_init(struct seqlock *sqlo)
@@ -154,14 +289,23 @@ void seqlock_init(struct seqlock *sqlo)
seqlock_val_t seqlock_cur(struct seqlock *sqlo)
{
- return atomic_load_explicit(&sqlo->pos, memory_order_acquire);
+ return SEQLOCK_VAL(atomic_load_explicit(&sqlo->pos,
+ memory_order_relaxed));
}
seqlock_val_t seqlock_bump(struct seqlock *sqlo)
{
- seqlock_val_t val;
+ seqlock_val_t val, cur;
+
+ cur = atomic_load_explicit(&sqlo->pos, memory_order_relaxed);
+ seqlock_assert_valid(cur);
+
+ do {
+ val = SEQLOCK_VAL(cur) + SEQLOCK_INCR;
+ } while (!atomic_compare_exchange_weak_explicit(&sqlo->pos, &cur, val,
+ memory_order_relaxed, memory_order_relaxed));
- val = atomic_fetch_add_explicit(&sqlo->pos, 2, memory_order_release);
- wait_poke(sqlo);
+ if (cur & SEQLOCK_WAITERS)
+ wait_poke(sqlo);
return val;
}
diff --git a/lib/seqlock.h b/lib/seqlock.h
index eef05a4307..b551e3ffc4 100644
--- a/lib/seqlock.h
+++ b/lib/seqlock.h
@@ -54,12 +54,28 @@
*/
/* use sequentially increasing "ticket numbers". lowest bit will always
- * be 1 to have a 'cleared' indication (i.e., counts 1,3,5,7,etc. )
+ * be 1 to have a 'cleared' indication (i.e., counts 1,5,9,13,etc. )
+ * 2nd lowest bit is used to indicate we have waiters.
*/
typedef _Atomic uint32_t seqlock_ctr_t;
typedef uint32_t seqlock_val_t;
-#define seqlock_assert_valid(val) assert(val & 1)
+#define seqlock_assert_valid(val) assert((val) & SEQLOCK_HELD)
+/* NB: SEQLOCK_WAITERS is only allowed if SEQLOCK_HELD is also set; can't
+ * have waiters on an unheld seqlock
+ */
+#define SEQLOCK_HELD (1U << 0)
+#define SEQLOCK_WAITERS (1U << 1)
+#define SEQLOCK_VAL(n) ((n) & ~SEQLOCK_WAITERS)
+#define SEQLOCK_STARTVAL 1U
+#define SEQLOCK_INCR 4U
+
+/* TODO: originally, this was using "atomic_fetch_add", which is the reason
+ * bit 0 is used to indicate held state. With SEQLOCK_WAITERS added, there's
+ * no fetch_add anymore (cmpxchg loop instead), so we don't need to use bit 0
+ * for this anymore & can just special-case the value 0 for it and skip it in
+ * counting.
+ */
struct seqlock {
/* always used */
@@ -74,8 +90,16 @@ struct seqlock {
extern void seqlock_init(struct seqlock *sqlo);
-/* while (sqlo <= val) - wait until seqlock->pos > val, or seqlock unheld */
+/* basically: "while (sqlo <= val) wait();"
+ * returns when sqlo > val || !seqlock_held(sqlo)
+ */
extern void seqlock_wait(struct seqlock *sqlo, seqlock_val_t val);
+
+/* same, but time-limited (limit is an absolute CLOCK_MONOTONIC value) */
+extern bool seqlock_timedwait(struct seqlock *sqlo, seqlock_val_t val,
+ const struct timespec *abs_monotime_limit);
+
+/* one-shot test, returns true if seqlock_wait would return immediately */
extern bool seqlock_check(struct seqlock *sqlo, seqlock_val_t val);
static inline bool seqlock_held(struct seqlock *sqlo)
@@ -85,12 +109,20 @@ static inline bool seqlock_held(struct seqlock *sqlo)
/* sqlo - get seqlock position -- for the "counter" seqlock */
extern seqlock_val_t seqlock_cur(struct seqlock *sqlo);
-/* sqlo++ - note: like x++, returns previous value, before bumping */
+
+/* ++sqlo (but atomic & wakes waiters) - returns value that we bumped to.
+ *
+ * guarantees:
+ * - each seqlock_bump call bumps the position by exactly one SEQLOCK_INCR.
+ * There are no skipped/missed or multiple increments.
+ * - each return value is only returned from one seqlock_bump() call
+ */
extern seqlock_val_t seqlock_bump(struct seqlock *sqlo);
/* sqlo = val - can be used on held seqlock. */
extern void seqlock_acquire_val(struct seqlock *sqlo, seqlock_val_t val);
+
/* sqlo = ref - standard pattern: acquire relative to other seqlock */
static inline void seqlock_acquire(struct seqlock *sqlo, struct seqlock *ref)
{
diff --git a/lib/sigevent.c b/lib/sigevent.c
index d02b074223..fcd85d0d43 100644
--- a/lib/sigevent.c
+++ b/lib/sigevent.c
@@ -24,7 +24,6 @@
#include <memory.h>
#include <lib_errors.h>
-#ifdef SA_SIGINFO
#ifdef HAVE_UCONTEXT_H
#ifdef GNU_LINUX
/* get REG_EIP from ucontext.h */
@@ -34,7 +33,6 @@
#endif /* GNU_LINUX */
#include <ucontext.h>
#endif /* HAVE_UCONTEXT_H */
-#endif /* SA_SIGINFO */
/* master signals descriptor struct */
@@ -158,8 +156,6 @@ static int signal_set(int signo)
return 0;
}
-#ifdef SA_SIGINFO
-
/* XXX This function should be enhanced to support more platforms
(it currently works only on Linux/x86). */
static void *program_counter(void *context)
@@ -199,41 +195,19 @@ static void *program_counter(void *context)
return NULL;
}
-#endif /* SA_SIGINFO */
-
static void __attribute__((noreturn))
-exit_handler(int signo
-#ifdef SA_SIGINFO
- ,
- siginfo_t *siginfo, void *context
-#endif
- )
+exit_handler(int signo, siginfo_t *siginfo, void *context)
{
-#ifndef SA_SIGINFO
- void *siginfo = NULL;
- void *pc = NULL;
-#else
void *pc = program_counter(context);
-#endif
zlog_signal(signo, "exiting...", siginfo, pc);
_exit(128 + signo);
}
static void __attribute__((noreturn))
-core_handler(int signo
-#ifdef SA_SIGINFO
- ,
- siginfo_t *siginfo, void *context
-#endif
- )
+core_handler(int signo, siginfo_t *siginfo, void *context)
{
-#ifndef SA_SIGINFO
- void *siginfo = NULL;
- void *pc = NULL;
-#else
void *pc = program_counter(context);
-#endif
/* make sure we don't hang in here. default for SIGALRM is terminate.
* - if we're in backtrace for more than a second, abort. */
@@ -290,12 +264,7 @@ static void trap_default_signals(void)
static const struct {
const int *sigs;
unsigned int nsigs;
- void (*handler)(int signo
-#ifdef SA_SIGINFO
- ,
- siginfo_t *info, void *context
-#endif
- );
+ void (*handler)(int signo, siginfo_t *info, void *context);
} sigmap[] = {
{core_signals, array_size(core_signals), core_handler},
{exit_signals, array_size(exit_signals), exit_handler},
@@ -316,15 +285,10 @@ static void trap_default_signals(void)
act.sa_handler = SIG_IGN;
act.sa_flags = 0;
} else {
-#ifdef SA_SIGINFO
/* Request extra arguments to signal
* handler. */
act.sa_sigaction = sigmap[i].handler;
act.sa_flags = SA_SIGINFO;
-#else
- act.sa_handler = sigmap[i].handler;
- act.sa_flags = 0;
-#endif
#ifdef SA_RESETHAND
/* don't try to print backtraces
* recursively */
diff --git a/lib/stream.c b/lib/stream.c
index 6c187bd359..dfd13ca186 100644
--- a/lib/stream.c
+++ b/lib/stream.c
@@ -186,14 +186,6 @@ size_t stream_resize_inplace(struct stream **sptr, size_t newsize)
return orig->size;
}
-size_t __attribute__((deprecated))stream_resize_orig(struct stream *s,
- size_t newsize)
-{
- assert("stream_resize: Switch code to use stream_resize_inplace" == NULL);
-
- return stream_resize_inplace(&s, newsize);
-}
-
size_t stream_get_getp(struct stream *s)
{
STREAM_VERIFY_SANE(s);
@@ -904,20 +896,30 @@ int stream_put_prefix(struct stream *s, struct prefix *p)
/* Put NLRI with label */
int stream_put_labeled_prefix(struct stream *s, struct prefix *p,
- mpls_label_t *label)
+ mpls_label_t *label, int addpath_encode,
+ uint32_t addpath_tx_id)
{
size_t psize;
+ size_t psize_with_addpath;
uint8_t *label_pnt = (uint8_t *)label;
STREAM_VERIFY_SANE(s);
psize = PSIZE(p->prefixlen);
+ psize_with_addpath = psize + (addpath_encode ? 4 : 0);
- if (STREAM_WRITEABLE(s) < (psize + 3)) {
+ if (STREAM_WRITEABLE(s) < (psize_with_addpath + 3)) {
STREAM_BOUND_WARN(s, "put");
return 0;
}
+ if (addpath_encode) {
+ s->data[s->endp++] = (uint8_t)(addpath_tx_id >> 24);
+ s->data[s->endp++] = (uint8_t)(addpath_tx_id >> 16);
+ s->data[s->endp++] = (uint8_t)(addpath_tx_id >> 8);
+ s->data[s->endp++] = (uint8_t)addpath_tx_id;
+ }
+
stream_putc(s, (p->prefixlen + 24));
stream_putc(s, label_pnt[0]);
stream_putc(s, label_pnt[1]);
diff --git a/lib/stream.h b/lib/stream.h
index 5341bfa40b..1144e43ef0 100644
--- a/lib/stream.h
+++ b/lib/stream.h
@@ -153,11 +153,6 @@ extern void stream_free(struct stream *);
extern struct stream *stream_copy(struct stream *, struct stream *src);
extern struct stream *stream_dup(struct stream *);
-#if CONFDATE > 20190821
-CPP_NOTICE("lib: time to remove stream_resize_orig")
-#endif
-extern size_t stream_resize_orig(struct stream *s, size_t newsize);
-#define stream_resize stream_resize_orig
extern size_t stream_resize_inplace(struct stream **sptr, size_t newsize);
extern size_t stream_get_getp(struct stream *);
@@ -199,7 +194,8 @@ extern int stream_put_prefix_addpath(struct stream *, struct prefix *,
uint32_t addpath_tx_id);
extern int stream_put_prefix(struct stream *, struct prefix *);
extern int stream_put_labeled_prefix(struct stream *, struct prefix *,
- mpls_label_t *);
+ mpls_label_t *, int addpath_encode,
+ uint32_t addpath_tx_id);
extern void stream_get(void *, struct stream *, size_t);
extern bool stream_get2(void *data, struct stream *s, size_t size);
extern void stream_get_from(void *, struct stream *, size_t, size_t);
diff --git a/lib/subdir.am b/lib/subdir.am
index f4fe369a97..e0f1352380 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -21,6 +21,7 @@ lib_libfrr_la_SOURCES = \
lib/distribute.c \
lib/ferr.c \
lib/filter.c \
+ lib/frrcu.c \
lib/frrlua.c \
lib/frr_pthread.c \
lib/frrstr.c \
@@ -61,10 +62,10 @@ lib_libfrr_la_SOURCES = \
lib/openbsd-tree.c \
lib/pid_output.c \
lib/plist.c \
- lib/pqueue.c \
lib/prefix.c \
lib/privs.c \
lib/ptm_lib.c \
+ lib/pullwr.c \
lib/qobj.c \
lib/ringbuf.c \
lib/routemap.c \
@@ -161,6 +162,7 @@ pkginclude_HEADERS += \
lib/frrlua.h \
lib/frr_pthread.h \
lib/frratomic.h \
+ lib/frrcu.h \
lib/frrstr.h \
lib/getopt.h \
lib/graph.h \
@@ -198,11 +200,11 @@ pkginclude_HEADERS += \
lib/openbsd-queue.h \
lib/openbsd-tree.h \
lib/plist.h \
- lib/pqueue.h \
lib/prefix.h \
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 fc2de09df0..943b849ebf 100644
--- a/lib/thread.c
+++ b/lib/thread.c
@@ -25,9 +25,9 @@
#include "thread.h"
#include "memory.h"
+#include "frrcu.h"
#include "log.h"
#include "hash.h"
-#include "pqueue.h"
#include "command.h"
#include "sigevent.h"
#include "network.h"
@@ -42,6 +42,22 @@ DEFINE_MTYPE_STATIC(LIB, THREAD_STATS, "Thread stats")
DECLARE_LIST(thread_list, struct thread, threaditem)
+static int thread_timer_cmp(const struct thread *a, const struct thread *b)
+{
+ if (a->u.sands.tv_sec < b->u.sands.tv_sec)
+ return -1;
+ if (a->u.sands.tv_sec > b->u.sands.tv_sec)
+ return 1;
+ if (a->u.sands.tv_usec < b->u.sands.tv_usec)
+ return -1;
+ if (a->u.sands.tv_usec > b->u.sands.tv_usec)
+ return 1;
+ return 0;
+}
+
+DECLARE_HEAP(thread_timer_list, struct thread, timeritem,
+ thread_timer_cmp)
+
#if defined(__APPLE__)
#include <mach/mach.h>
#include <mach/mach_time.h>
@@ -401,25 +417,6 @@ void thread_cmd_init(void)
/* CLI end ------------------------------------------------------------------ */
-static int thread_timer_cmp(void *a, void *b)
-{
- struct thread *thread_a = a;
- struct thread *thread_b = b;
-
- if (timercmp(&thread_a->u.sands, &thread_b->u.sands, <))
- return -1;
- if (timercmp(&thread_a->u.sands, &thread_b->u.sands, >))
- return 1;
- return 0;
-}
-
-static void thread_timer_update(void *node, int actual_position)
-{
- struct thread *thread = node;
-
- thread->index = actual_position;
-}
-
static void cancelreq_del(void *cr)
{
XFREE(MTYPE_TMP, cr);
@@ -464,11 +461,7 @@ struct thread_master *thread_master_create(const char *name)
thread_list_init(&rv->event);
thread_list_init(&rv->ready);
thread_list_init(&rv->unuse);
-
- /* Initialize the timer queues */
- rv->timer = pqueue_create();
- rv->timer->cmp = thread_timer_cmp;
- rv->timer->update = thread_timer_update;
+ thread_timer_list_init(&rv->timer);
/* Initialize thread_fetch() settings */
rv->spin = true;
@@ -566,16 +559,6 @@ static void thread_array_free(struct thread_master *m,
XFREE(MTYPE_THREAD_POLL, thread_array);
}
-static void thread_queue_free(struct thread_master *m, struct pqueue *queue)
-{
- int i;
-
- for (i = 0; i < queue->size; i++)
- thread_free(m, queue->array[i]);
-
- pqueue_delete(queue);
-}
-
/*
* thread_master_free_unused
*
@@ -598,6 +581,8 @@ void thread_master_free_unused(struct thread_master *m)
/* Stop thread scheduler. */
void thread_master_free(struct thread_master *m)
{
+ struct thread *t;
+
pthread_mutex_lock(&masters_mtx);
{
listnode_delete(masters, m);
@@ -609,7 +594,8 @@ void thread_master_free(struct thread_master *m)
thread_array_free(m, m->read);
thread_array_free(m, m->write);
- thread_queue_free(m, m->timer);
+ while ((t = thread_timer_list_pop(&m->timer)))
+ thread_free(m, t);
thread_list_free(m, &m->event);
thread_list_free(m, &m->ready);
thread_list_free(m, &m->unuse);
@@ -683,7 +669,6 @@ static struct thread *thread_get(struct thread_master *m, uint8_t type,
thread->add_type = type;
thread->master = m;
thread->arg = arg;
- thread->index = -1;
thread->yield = THREAD_YIELD_TIME_SLOT; /* default */
thread->ref = NULL;
@@ -729,7 +714,7 @@ static int fd_poll(struct thread_master *m, struct pollfd *pfds, nfds_t pfdsize,
{
/* If timer_wait is null here, that means poll() should block
* indefinitely,
- * unless the thread_master has overriden it by setting
+ * unless the thread_master has overridden it by setting
* ->selectpoll_timeout.
* If the value is positive, it specifies the maximum number of
* milliseconds
@@ -753,6 +738,9 @@ static int fd_poll(struct thread_master *m, struct pollfd *pfds, nfds_t pfdsize,
< 0) // effect a poll (return immediately)
timeout = 0;
+ rcu_read_unlock();
+ rcu_assert_read_unlocked();
+
/* add poll pipe poker */
assert(count + 1 < pfdsize);
pfds[count].fd = m->io_pipe[0];
@@ -766,6 +754,8 @@ static int fd_poll(struct thread_master *m, struct pollfd *pfds, nfds_t pfdsize,
while (read(m->io_pipe[0], &trash, sizeof(trash)) > 0)
;
+ rcu_read_lock();
+
return num;
}
@@ -854,7 +844,6 @@ funcname_thread_add_timer_timeval(struct thread_master *m,
struct thread **t_ptr, debugargdef)
{
struct thread *thread;
- struct pqueue *queue;
assert(m != NULL);
@@ -870,7 +859,6 @@ funcname_thread_add_timer_timeval(struct thread_master *m,
return NULL;
}
- queue = m->timer;
thread = thread_get(m, type, func, arg, debugargpass);
pthread_mutex_lock(&thread->mtx);
@@ -878,7 +866,7 @@ funcname_thread_add_timer_timeval(struct thread_master *m,
monotime(&thread->u.sands);
timeradd(&thread->u.sands, time_relative,
&thread->u.sands);
- pqueue_enqueue(thread, queue);
+ thread_timer_list_add(&m->timer, thread);
if (t_ptr) {
*t_ptr = thread;
thread->ref = t_ptr;
@@ -1055,7 +1043,6 @@ static void thread_cancel_rw(struct thread_master *master, int fd, short state)
static void do_thread_cancel(struct thread_master *master)
{
struct thread_list_head *list = NULL;
- struct pqueue *queue = NULL;
struct thread **thread_array = NULL;
struct thread *thread;
@@ -1111,7 +1098,7 @@ static void do_thread_cancel(struct thread_master *master)
thread_array = master->write;
break;
case THREAD_TIMER:
- queue = master->timer;
+ thread_timer_list_del(&master->timer, thread);
break;
case THREAD_EVENT:
list = &master->event;
@@ -1124,16 +1111,10 @@ static void do_thread_cancel(struct thread_master *master)
break;
}
- if (queue) {
- assert(thread->index >= 0);
- assert(thread == queue->array[thread->index]);
- pqueue_remove_at(thread->index, queue);
- } else if (list) {
+ if (list) {
thread_list_del(list, thread);
} else if (thread_array) {
thread_array[thread->u.fd] = NULL;
- } else {
- assert(!"Thread should be either in queue or list or array!");
}
if (thread->ref)
@@ -1251,15 +1232,15 @@ void thread_cancel_async(struct thread_master *master, struct thread **thread,
}
/* ------------------------------------------------------------------------- */
-static struct timeval *thread_timer_wait(struct pqueue *queue,
+static struct timeval *thread_timer_wait(struct thread_timer_list_head *timers,
struct timeval *timer_val)
{
- if (queue->size) {
- struct thread *next_timer = queue->array[0];
- monotime_until(&next_timer->u.sands, timer_val);
- return timer_val;
- }
- return NULL;
+ if (!thread_timer_list_count(timers))
+ return NULL;
+
+ struct thread *next_timer = thread_timer_list_first(timers);
+ monotime_until(&next_timer->u.sands, timer_val);
+ return timer_val;
}
static struct thread *thread_run(struct thread_master *m, struct thread *thread,
@@ -1369,17 +1350,16 @@ static void thread_process_io(struct thread_master *m, unsigned int num)
}
/* Add all timers that have popped to the ready list. */
-static unsigned int thread_process_timers(struct pqueue *queue,
+static unsigned int thread_process_timers(struct thread_timer_list_head *timers,
struct timeval *timenow)
{
struct thread *thread;
unsigned int ready = 0;
- while (queue->size) {
- thread = queue->array[0];
+ while ((thread = thread_timer_list_first(timers))) {
if (timercmp(timenow, &thread->u.sands, <))
return ready;
- pqueue_dequeue(queue);
+ thread_timer_list_pop(timers);
thread->type = THREAD_READY;
thread_list_add_tail(&thread->master->ready, thread);
ready++;
@@ -1461,7 +1441,7 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch)
* once per loop to avoid starvation by events
*/
if (!thread_list_count(&m->ready))
- tw = thread_timer_wait(m->timer, &tv);
+ tw = thread_timer_wait(&m->timer, &tv);
if (thread_list_count(&m->ready) ||
(tw && !timercmp(tw, &zerotime, >)))
@@ -1506,7 +1486,7 @@ struct thread *thread_fetch(struct thread_master *m, struct thread *fetch)
/* Post timers to ready queue. */
monotime(&now);
- thread_process_timers(m->timer, &now);
+ thread_process_timers(&m->timer, &now);
/* Post I/O to ready queue. */
if (num > 0)
diff --git a/lib/thread.h b/lib/thread.h
index 7897265120..412a4d93bf 100644
--- a/lib/thread.h
+++ b/lib/thread.h
@@ -41,8 +41,7 @@ struct rusage_t {
#define GETRUSAGE(X) thread_getrusage(X)
PREDECL_LIST(thread_list)
-
-struct pqueue;
+PREDECL_HEAP(thread_timer_list)
struct fd_handler {
/* number of pfd that fit in the allocated space of pfds. This is a
@@ -73,7 +72,7 @@ struct thread_master {
struct thread **read;
struct thread **write;
- struct pqueue *timer;
+ struct thread_timer_list_head timer;
struct thread_list_head event, ready, unuse;
struct list *cancel_req;
bool canceled;
@@ -95,6 +94,7 @@ struct thread {
uint8_t type; /* thread type */
uint8_t add_type; /* thread type */
struct thread_list_item threaditem;
+ struct thread_timer_list_item timeritem;
struct thread **ref; /* external reference (if given) */
struct thread_master *master; /* pointer to the struct thread_master */
int (*func)(struct thread *); /* event function */
@@ -104,7 +104,6 @@ struct thread {
int fd; /* file descriptor in case of r/w */
struct timeval sands; /* rest of time sands value. */
} u;
- int index; /* queue position for timers */
struct timeval real;
struct cpu_thread_history *hist; /* cache pointer to cpu_history */
unsigned long yield; /* yield time in microseconds */
diff --git a/lib/vty.c b/lib/vty.c
index c1535802cf..deb9391bd5 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -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 6937700199..2d79d9b3c5 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -325,8 +325,9 @@ int zclient_read_header(struct stream *s, int sock, uint16_t *size,
if (*size && stream_read(s, sock, *size) != *size)
return -1;
-stream_failure:
return 0;
+stream_failure:
+ return -1;
}
bool zapi_parse_header(struct stream *zmsg, struct zmsghdr *hdr)
@@ -1056,8 +1057,9 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api)
if (CHECK_FLAG(api->message, ZAPI_MESSAGE_TABLEID))
STREAM_GETL(s, api->tableid);
-stream_failure:
return 0;
+stream_failure:
+ return -1;
}
static void zapi_encode_prefix(struct stream *s, struct prefix *p,
@@ -2254,7 +2256,7 @@ int tm_table_manager_connect(struct zclient *zclient)
return (int)result;
stream_failure:
- return 0;
+ return -1;
}
/**
@@ -2321,8 +2323,9 @@ int tm_get_table_chunk(struct zclient *zclient, uint32_t chunk_size,
if (zclient_debug)
zlog_debug("Table Chunk assign: %u - %u ", *start, *end);
-stream_failure:
return 0;
+stream_failure:
+ return -1;
}
/**
diff --git a/lib/zebra.h b/lib/zebra.h
index 22239f8e60..789a93a3c4 100644
--- a/lib/zebra.h
+++ b/lib/zebra.h
@@ -134,6 +134,10 @@ typedef unsigned char uint8_t;
#endif
#endif
+#ifdef CRYPTO_OPENSSL
+#include <openssl/evp.h>
+#endif
+
#include "openbsd-tree.h"
#include <netinet/in.h>
@@ -356,6 +360,7 @@ typedef enum {
/* Subsequent Address Family Identifier. */
typedef enum {
+ SAFI_UNSPEC = 0,
SAFI_UNICAST = 1,
SAFI_MULTICAST = 2,
SAFI_MPLS_VPN = 3,
diff --git a/m4/ax_python.m4 b/m4/ax_python.m4
index 32043c81ae..66338511a3 100644
--- a/m4/ax_python.m4
+++ b/m4/ax_python.m4
@@ -186,7 +186,11 @@ AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
AC_MSG_RESULT([yes])
PYTHON_CFLAGS="`\"$pycfg\" --includes`"
- PYTHON_LIBS="`\"$pycfg\" --ldflags`"
+ if test x"${py_ver}" == x"3.8" || test x"{py_ver}" == x"3.9"; then
+ PYTHON_LIBS="`\"$pycfg\" --ldflags --embed`"
+ else
+ PYTHON_LIBS="`\"$pycfg\" --ldflags`"
+ fi
AC_MSG_CHECKING([whether ${pycfg} provides a working build environment])
_FRR_PYTHON_DEVENV([$py_hex], [
diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c
index af16c5aa7c..8454016b2e 100644
--- a/ospf6d/ospf6_zebra.c
+++ b/ospf6d/ospf6_zebra.c
@@ -258,7 +258,7 @@ DEFUN (show_zebra,
return CMD_SUCCESS;
}
- vty_out(vty, "Zebra Infomation\n");
+ vty_out(vty, "Zebra Information\n");
vty_out(vty, " fail: %d\n", zclient->fail);
vty_out(vty, " redistribute default: %d\n",
vrf_bitmap_check(zclient->default_information[AFI_IP6],
diff --git a/ospfd/ospf_errors.c b/ospfd/ospf_errors.c
index dd02160195..a985efc668 100644
--- a/ospfd/ospf_errors.c
+++ b/ospfd/ospf_errors.c
@@ -39,7 +39,7 @@ static struct log_ref ferr_ospf_warn[] = {
},
{
.code = EC_OSPF_PACKET,
- .title = "OSPF has detected packet information missmatch",
+ .title = "OSPF has detected packet information mismatch",
.description = "OSPF has detected that packet information received is incorrect",
.suggestion = "Ensure interface configuration is correct, gather log files from here and the peer and open an Issue",
},
diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c
index 50c30a6fa0..62b0444796 100644
--- a/ospfd/ospf_packet.c
+++ b/ospfd/ospf_packet.c
@@ -33,7 +33,9 @@
#include "log.h"
#include "sockopt.h"
#include "checksum.h"
+#ifdef CRYPTO_INTERNAL
#include "md5.h"
+#endif
#include "vrf.h"
#include "lib_errors.h"
@@ -332,7 +334,11 @@ static unsigned int ospf_packet_max(struct ospf_interface *oi)
static int ospf_check_md5_digest(struct ospf_interface *oi,
struct ospf_header *ospfh)
{
+#ifdef CRYPTO_OPENSSL
+ EVP_MD_CTX *ctx;
+#elif CRYPTO_INTERNAL
MD5_CTX ctx;
+#endif
unsigned char digest[OSPF_AUTH_MD5_SIZE];
struct crypt_key *ck;
struct ospf_neighbor *nbr;
@@ -361,11 +367,21 @@ static int ospf_check_md5_digest(struct ospf_interface *oi,
}
/* Generate a digest for the ospf packet - their digest + our digest. */
+#ifdef CRYPTO_OPENSSL
+ unsigned int md5_size = OSPF_AUTH_MD5_SIZE;
+ ctx = EVP_MD_CTX_new();
+ EVP_DigestInit(ctx, EVP_md5());
+ EVP_DigestUpdate(ctx, ospfh, length);
+ EVP_DigestUpdate(ctx, ck->auth_key, OSPF_AUTH_MD5_SIZE);
+ EVP_DigestFinal(ctx, digest, &md5_size);
+ EVP_MD_CTX_free(ctx);
+#elif CRYPTO_INTERNAL
memset(&ctx, 0, sizeof(ctx));
MD5Init(&ctx);
MD5Update(&ctx, ospfh, length);
MD5Update(&ctx, ck->auth_key, OSPF_AUTH_MD5_SIZE);
MD5Final(digest, &ctx);
+#endif
/* compare the two */
if (memcmp((caddr_t)ospfh + length, digest, OSPF_AUTH_MD5_SIZE)) {
@@ -389,7 +405,11 @@ static int ospf_make_md5_digest(struct ospf_interface *oi,
{
struct ospf_header *ospfh;
unsigned char digest[OSPF_AUTH_MD5_SIZE] = {0};
+#ifdef CRYPTO_OPENSSL
+ EVP_MD_CTX *ctx;
+#elif CRYPTO_INTERNAL
MD5_CTX ctx;
+#endif
void *ibuf;
uint32_t t;
struct crypt_key *ck;
@@ -422,11 +442,21 @@ static int ospf_make_md5_digest(struct ospf_interface *oi,
}
/* Generate a digest for the entire packet + our secret key. */
+#ifdef CRYPTO_OPENSSL
+ unsigned int md5_size = OSPF_AUTH_MD5_SIZE;
+ ctx = EVP_MD_CTX_new();
+ EVP_DigestInit(ctx, EVP_md5());
+ EVP_DigestUpdate(ctx, ibuf, ntohs(ospfh->length));
+ EVP_DigestUpdate(ctx, auth_key, OSPF_AUTH_MD5_SIZE);
+ EVP_DigestFinal(ctx, digest, &md5_size);
+ EVP_MD_CTX_free(ctx);
+#elif CRYPTO_INTERNAL
memset(&ctx, 0, sizeof(ctx));
MD5Init(&ctx);
MD5Update(&ctx, ibuf, ntohs(ospfh->length));
MD5Update(&ctx, auth_key, OSPF_AUTH_MD5_SIZE);
MD5Final(digest, &ctx);
+#endif
/* Append md5 digest to the end of the stream. */
stream_put(op->s, digest, OSPF_AUTH_MD5_SIZE);
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index 2564c6f330..8fa91f500c 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -3442,6 +3442,9 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf,
else
vty_out(vty, " This interface is UNNUMBERED,");
} else {
+ struct in_addr dest;
+ const char *dstr;
+
/* Show OSPF interface information. */
if (use_json) {
json_object_string_add(
@@ -3455,46 +3458,40 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf,
inet_ntoa(oi->address->u.prefix4),
oi->address->prefixlen);
- if (oi->connected->destination
- || oi->type == OSPF_IFTYPE_VIRTUALLINK) {
- struct in_addr *dest;
- const char *dstr;
-
- if (CONNECTED_PEER(oi->connected)
- || oi->type == OSPF_IFTYPE_VIRTUALLINK)
- dstr = "Peer";
- else
- dstr = "Broadcast";
+ /* For Vlinks, showing the peer address is
+ * probably more informative than the local
+ * interface that is being used */
+ if (oi->type == OSPF_IFTYPE_VIRTUALLINK) {
+ dstr = "Peer";
+ dest = oi->vl_data->peer_addr;
+ } else if (CONNECTED_PEER(oi->connected)
+ && oi->connected->destination) {
+ dstr = "Peer";
+ dest = oi->connected->destination->u.prefix4;
+ } else {
+ dstr = "Broadcast";
+ dest.s_addr = ipv4_broadcast_addr(
+ oi->connected->address->u.prefix4.s_addr,
+ oi->connected->address->prefixlen);
+ }
- /* For Vlinks, showing the peer address is
- * probably more
- * * * * * informative than the local
- * interface that is being used
- * * * * */
+ if (use_json) {
+ json_object_string_add(
+ json_interface_sub,
+ "ospfIfType", dstr);
if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
- dest = &oi->vl_data->peer_addr;
+ json_object_string_add(
+ json_interface_sub,
+ "vlinkPeer",
+ inet_ntoa(dest));
else
- dest = &oi->connected->destination->u
- .prefix4;
-
- if (use_json) {
json_object_string_add(
json_interface_sub,
- "ospfIfType", dstr);
- if (oi->type == OSPF_IFTYPE_VIRTUALLINK)
- json_object_string_add(
- json_interface_sub,
- "vlinkPeer",
- inet_ntoa(*dest));
- else
- json_object_string_add(
- json_interface_sub,
- "localIfUsed",
- inet_ntoa(*dest));
- } else
- vty_out(vty, " %s %s,", dstr,
- inet_ntoa(*dest));
- }
+ "localIfUsed",
+ inet_ntoa(dest));
+ } else
+ vty_out(vty, " %s %s,", dstr,
+ inet_ntoa(dest));
}
if (use_json) {
json_object_string_add(json_interface_sub, "area",
diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c
index 47438b985e..b478832d84 100644
--- a/ospfd/ospf_zebra.c
+++ b/ospfd/ospf_zebra.c
@@ -983,7 +983,8 @@ static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS)
char buf_prefix[PREFIX_STRLEN];
prefix2str(&api.prefix, buf_prefix, sizeof(buf_prefix));
- zlog_debug("%s: from client %s: vrf_id %d, p %s", __func__,
+ zlog_debug("%s: cmd %s from client %s: vrf_id %d, p %s",
+ __func__, zserv_command_string(cmd),
zebra_route_string(api.type), vrf_id, buf_prefix);
}
diff --git a/pbrd/pbrd.conf.sample b/pbrd/pbrd.conf.sample
index bb1c2edca8..c9e7dce01f 100644
--- a/pbrd/pbrd.conf.sample
+++ b/pbrd/pbrd.conf.sample
@@ -1,3 +1,19 @@
+! Sample pbrd configuration file
+!
+! A quick example of what a pbr configuration might look like
!
!
log stdout
+!
+! nexthop-group TEST
+! nexthop 4.5.6.7
+! nexthop 5.6.7.8
+! !
+! pbr-map BLUE seq 100
+! match dst-ip 9.9.9.0/24
+! match src-ip 10.10.10.0/24
+! set nexthop-group TEST
+! !
+! int swp1
+! pbr-policy BLUE
+!
diff --git a/pimd/pim_bsm.c b/pimd/pim_bsm.c
index 62f13b5b53..675092dbec 100644
--- a/pimd/pim_bsm.c
+++ b/pimd/pim_bsm.c
@@ -711,11 +711,11 @@ static bool pim_bsm_frag_send(uint8_t *buf, uint32_t len, struct interface *ifp,
/* MTU passed here is PIM MTU (IP MTU less IP Hdr) */
if (pim_mtu < (PIM_MIN_BSM_LEN)) {
zlog_warn(
- "%s: mtu(pim mtu: %d) size less than minimum bootsrap len",
+ "%s: mtu(pim mtu: %d) size less than minimum bootstrap len",
__PRETTY_FUNCTION__, pim_mtu);
if (PIM_DEBUG_BSM)
zlog_debug(
- "%s: mtu (pim mtu:%d) less than minimum bootsrap len",
+ "%s: mtu (pim mtu:%d) less than minimum bootstrap len",
__PRETTY_FUNCTION__, pim_mtu);
return false;
}
diff --git a/pimd/pim_neighbor.c b/pimd/pim_neighbor.c
index a63b09fc1f..722ecb2a72 100644
--- a/pimd/pim_neighbor.c
+++ b/pimd/pim_neighbor.c
@@ -424,10 +424,11 @@ struct pim_neighbor *pim_neighbor_find_by_secondary(struct interface *ifp,
struct pim_neighbor *neigh;
struct prefix *p;
- pim_ifp = ifp->info;
- if (!pim_ifp)
+ if (!ifp || !ifp->info)
return NULL;
+ pim_ifp = ifp->info;
+
for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_neighbor_list, node, neigh)) {
for (ALL_LIST_ELEMENTS_RO(neigh->prefix_list, pnode, p)) {
if (prefix_same(p, src))
diff --git a/pimd/pim_nht.c b/pimd/pim_nht.c
index 65ea858cb6..7900e39231 100644
--- a/pimd/pim_nht.c
+++ b/pimd/pim_nht.c
@@ -479,7 +479,7 @@ static int pim_update_upstream_nh_helper(struct hash_bucket *bucket, void *arg)
zlog_debug("%s: NHT upstream %s(%s) old ifp %s new ifp %s",
__PRETTY_FUNCTION__, up->sg_str, pim->vrf->name,
old.source_nexthop.interface
- ? old.source_nexthop.interface->name : "Unknwon",
+ ? old.source_nexthop.interface->name : "Unknown",
up->rpf.source_nexthop.interface->name);
}
@@ -842,6 +842,14 @@ int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS)
}
if (!ifp->info) {
+ /*
+ * Though Multicast is not enabled on this
+ * Interface store it in database otheriwse we
+ * may miss this update and this will not cause
+ * any issue, because while choosing the path we
+ * are ommitting the Interfaces which are not
+ * multicast enabled
+ */
if (PIM_DEBUG_PIM_NHT) {
char buf[NEXTHOP_STRLEN];
@@ -853,8 +861,6 @@ int pim_parse_nexthop_update(ZAPI_CALLBACK_ARGS)
nexthop2str(nexthop, buf,
sizeof(buf)));
}
- nexthop_free(nexthop);
- continue;
}
if (nhlist_tail) {
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 27042e197c..fa0a6d8a0a 100644
--- a/redhat/frr.spec.in
+++ b/redhat/frr.spec.in
@@ -1,6 +1,6 @@
# configure options
#
-# Some can be overriden on rpmbuild commandline with:
+# Some can be overridden on rpmbuild commandline with:
# rpmbuild --define 'variable value'
# (use any value, ie 1 for flag "with_XXXX" definitions)
#
@@ -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 e0ff0430f8..561fbcb52d 100644
--- a/ripd/ripd.c
+++ b/ripd/ripd.c
@@ -37,7 +37,9 @@
#include "if_rmap.h"
#include "plist.h"
#include "distribute.h"
+#ifdef CRYPTO_INTERNAL
#include "md5.h"
+#endif
#include "keychain.h"
#include "privs.h"
#include "lib_errors.h"
@@ -870,7 +872,11 @@ static int rip_auth_md5(struct rip_packet *packet, struct sockaddr_in *from,
struct rip_md5_data *md5data;
struct keychain *keychain;
struct key *key;
+#ifdef CRYPTO_OPENSSL
+ EVP_MD_CTX *ctx;
+#elif CRYPTO_INTERNAL
MD5_CTX ctx;
+#endif
uint8_t digest[RIP_AUTH_MD5_SIZE];
uint16_t packet_len;
char auth_str[RIP_AUTH_MD5_SIZE] = {};
@@ -934,11 +940,21 @@ static int rip_auth_md5(struct rip_packet *packet, struct sockaddr_in *from,
return 0;
/* MD5 digest authentication. */
+#ifdef CRYPTO_OPENSSL
+ unsigned int md5_size = RIP_AUTH_MD5_SIZE;
+ ctx = EVP_MD_CTX_new();
+ EVP_DigestInit(ctx, EVP_md5());
+ EVP_DigestUpdate(ctx, packet, packet_len + RIP_HEADER_SIZE);
+ EVP_DigestUpdate(ctx, auth_str, RIP_AUTH_MD5_SIZE);
+ EVP_DigestFinal(ctx, digest, &md5_size);
+ EVP_MD_CTX_free(ctx);
+#elif CRYPTO_INTERNAL
memset(&ctx, 0, sizeof(ctx));
MD5Init(&ctx);
MD5Update(&ctx, packet, packet_len + RIP_HEADER_SIZE);
MD5Update(&ctx, auth_str, RIP_AUTH_MD5_SIZE);
MD5Final(digest, &ctx);
+#endif
if (memcmp(md5data->digest, digest, RIP_AUTH_MD5_SIZE) == 0)
return packet_len;
@@ -1063,7 +1079,11 @@ static void rip_auth_md5_set(struct stream *s, struct rip_interface *ri,
size_t doff, char *auth_str, int authlen)
{
unsigned long len;
+#ifdef CRYPTO_OPENSSL
+ EVP_MD_CTX *ctx;
+#elif CRYPTO_INTERNAL
MD5_CTX ctx;
+#endif
unsigned char digest[RIP_AUTH_MD5_SIZE];
/* Make it sure this interface is configured as MD5
@@ -1092,11 +1112,21 @@ static void rip_auth_md5_set(struct stream *s, struct rip_interface *ri,
stream_putw(s, RIP_AUTH_DATA);
/* Generate a digest for the RIP packet. */
+#ifdef CRYPTO_OPENSSL
+ unsigned int md5_size = RIP_AUTH_MD5_SIZE;
+ ctx = EVP_MD_CTX_new();
+ EVP_DigestInit(ctx, EVP_md5());
+ EVP_DigestUpdate(ctx, STREAM_DATA(s), stream_get_endp(s));
+ EVP_DigestUpdate(ctx, auth_str, RIP_AUTH_MD5_SIZE);
+ EVP_DigestFinal(ctx, digest, &md5_size);
+ EVP_MD_CTX_free(ctx);
+#elif CRYPTO_INTERNAL
memset(&ctx, 0, sizeof(ctx));
MD5Init(&ctx);
MD5Update(&ctx, STREAM_DATA(s), stream_get_endp(s));
MD5Update(&ctx, auth_str, RIP_AUTH_MD5_SIZE);
MD5Final(digest, &ctx);
+#endif
/* Copy the digest to the packet. */
stream_write(s, digest, RIP_AUTH_MD5_SIZE);
diff --git a/sharpd/sharpd.conf.sample b/sharpd/sharpd.conf.sample
index bb1c2edca8..d1cc19a51f 100644
--- a/sharpd/sharpd.conf.sample
+++ b/sharpd/sharpd.conf.sample
@@ -1,3 +1,6 @@
+! Default sharpd configuration sample
!
+! There are no `default` configuration commands for sharpd
+! all commands are at the view or enable level.
!
log stdout
diff --git a/staticd/static_routes.c b/staticd/static_routes.c
index b2c61bcbab..34f58a98e2 100644
--- a/staticd/static_routes.c
+++ b/staticd/static_routes.c
@@ -73,6 +73,7 @@ int static_add_route(afi_t afi, safi_t safi, uint8_t type, struct prefix *p,
struct static_route *cp;
struct static_route *update = NULL;
struct route_table *stable = svrf->stable[afi][safi];
+ struct interface *ifp;
if (!stable)
return -1;
@@ -182,11 +183,26 @@ int static_add_route(afi_t afi, safi_t safi, uint8_t type, struct prefix *p,
si->next = cp;
/* check whether interface exists in system & install if it does */
- if (!ifname)
+ switch (si->type) {
+ case STATIC_IPV4_GATEWAY:
+ case STATIC_IPV6_GATEWAY:
static_zebra_nht_register(rn, si, true);
- else {
- struct interface *ifp;
+ break;
+ case STATIC_IPV4_GATEWAY_IFNAME:
+ case STATIC_IPV6_GATEWAY_IFNAME:
+ ifp = if_lookup_by_name(ifname, nh_svrf->vrf->vrf_id);
+ if (ifp && ifp->ifindex != IFINDEX_INTERNAL)
+ si->ifindex = ifp->ifindex;
+ else
+ zlog_warn("Static Route using %s interface not installed because the interface does not exist in specified vrf",
+ ifname);
+ static_zebra_nht_register(rn, si, true);
+ break;
+ case STATIC_BLACKHOLE:
+ static_install_route(rn, si, safi);
+ break;
+ case STATIC_IFNAME:
ifp = if_lookup_by_name(ifname, nh_svrf->vrf->vrf_id);
if (ifp && ifp->ifindex != IFINDEX_INTERNAL) {
si->ifindex = ifp->ifindex;
@@ -194,6 +210,8 @@ int static_add_route(afi_t afi, safi_t safi, uint8_t type, struct prefix *p,
} else
zlog_warn("Static Route using %s interface not installed because the interface does not exist in specified vrf",
ifname);
+
+ break;
}
return 1;
diff --git a/staticd/staticd.conf.sample b/staticd/staticd.conf.sample
index bb1c2edca8..3b64eb9c90 100644
--- a/staticd/staticd.conf.sample
+++ b/staticd/staticd.conf.sample
@@ -1,3 +1,5 @@
-!
+! Default staticd configuration sample
!
log stdout
+!
+! ip route 4.5.6.7/32 10.10.10.10
diff --git a/tests/bgpd/test_bgp_table.c b/tests/bgpd/test_bgp_table.c
index 7b38df5f66..819c2d7282 100644
--- a/tests/bgpd/test_bgp_table.c
+++ b/tests/bgpd/test_bgp_table.c
@@ -183,7 +183,7 @@ static void test_range_lookup(void)
do_test(table, "16.0.0.0/8", 16, "16.0.0.0/16", NULL);
- do_test(table, "0.0.0.0/3", 21, "1.16.0.0/16", "1.16.128.0/18",
+ do_test(table, "0.0.0.0/2", 21, "1.16.0.0/16", "1.16.128.0/18",
"1.16.192.0/18", "1.16.64.0/19", "1.16.160.0/19",
"1.16.32.0/20", "1.16.32.0/21", "16.0.0.0/16", NULL);
}
diff --git a/tests/bgpd/test_bgp_table.py b/tests/bgpd/test_bgp_table.py
index 4423530fe0..4deaf08c22 100644
--- a/tests/bgpd/test_bgp_table.py
+++ b/tests/bgpd/test_bgp_table.py
@@ -3,5 +3,5 @@ import frrtest
class TestTable(frrtest.TestMultiOut):
program = './test_bgp_table'
-for i in range(6):
+for i in range(9):
TestTable.onesimple('Checks successfull')
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/cxxcompat.c b/tests/lib/cxxcompat.c
index 517e538abf..88126e84bc 100644
--- a/tests/lib/cxxcompat.c
+++ b/tests/lib/cxxcompat.c
@@ -72,7 +72,6 @@
#include "lib/openbsd-tree.h"
#include "lib/pbr.h"
#include "lib/plist.h"
-#include "lib/pqueue.h"
#include "lib/prefix.h"
#include "lib/privs.h"
#include "lib/ptm_lib.h"
diff --git a/tests/lib/test_atomlist.c b/tests/lib/test_atomlist.c
index 249fff8edb..238ee9539e 100644
--- a/tests/lib/test_atomlist.c
+++ b/tests/lib/test_atomlist.c
@@ -253,7 +253,7 @@ static void *thr1func(void *arg)
struct testrun *tr;
for (tr = runs; tr; tr = tr->next) {
- sv = seqlock_bump(&p->sqlo);
+ sv = seqlock_bump(&p->sqlo) - SEQLOCK_INCR;
seqlock_wait(&sqlo, sv);
tr->func(offset);
@@ -288,14 +288,14 @@ static void run_tr(struct testrun *tr)
size_t c = 0, s = 0, n = 0;
struct item *item, *prev, dummy;
- printf("[%02u] %35s %s\n", seqlock_cur(&sqlo) >> 1, "", desc);
+ printf("[%02u] %35s %s\n", seqlock_cur(&sqlo) >> 2, "", desc);
fflush(stdout);
if (tr->prefill != NOCLEAR)
clear_list(tr->prefill);
monotime(&tv);
- sv = seqlock_bump(&sqlo);
+ sv = seqlock_bump(&sqlo) - SEQLOCK_INCR;
for (size_t i = 0; i < NTHREADS; i++) {
seqlock_wait(&thr[i].sqlo, seqlock_cur(&sqlo));
s += thr[i].counter;
@@ -325,7 +325,7 @@ static void run_tr(struct testrun *tr)
assert(c == alist_count(&ahead));
}
printf("\033[1A[%02u] %9"PRId64"us c=%5zu s=%5zu n=%5zu %s\n",
- sv >> 1, delta, c, s, n, desc);
+ sv >> 2, delta, c, s, n, desc);
}
#ifdef BASIC_TESTS
@@ -381,7 +381,7 @@ int main(int argc, char **argv)
basic_tests();
seqlock_init(&sqlo);
- seqlock_acquire_val(&sqlo, 1);
+ seqlock_acquire_val(&sqlo, SEQLOCK_STARTVAL);
for (i = 0; i < NTHREADS; i++) {
seqlock_init(&thr[i].sqlo);
diff --git a/tests/lib/test_seqlock.c b/tests/lib/test_seqlock.c
index 9cc6f80702..639c2bdc2b 100644
--- a/tests/lib/test_seqlock.c
+++ b/tests/lib/test_seqlock.c
@@ -66,20 +66,20 @@ static void *thr1func(void *arg)
seqlock_wait(&sqlo, 1);
writestr("thr1 @1\n");
- seqlock_wait(&sqlo, 3);
- writestr("thr1 @3\n");
-
seqlock_wait(&sqlo, 5);
writestr("thr1 @5\n");
- seqlock_wait(&sqlo, 7);
- writestr("thr1 @7\n");
-
seqlock_wait(&sqlo, 9);
writestr("thr1 @9\n");
- seqlock_wait(&sqlo, 11);
- writestr("thr1 @11\n");
+ seqlock_wait(&sqlo, 13);
+ writestr("thr1 @13\n");
+
+ seqlock_wait(&sqlo, 17);
+ writestr("thr1 @17\n");
+
+ seqlock_wait(&sqlo, 21);
+ writestr("thr1 @21\n");
return NULL;
}
@@ -95,11 +95,11 @@ int main(int argc, char **argv)
assert(seqlock_cur(&sqlo) == 1);
assert(seqlock_bump(&sqlo) == 1);
- assert(seqlock_cur(&sqlo) == 3);
- assert(seqlock_bump(&sqlo) == 3);
+ assert(seqlock_cur(&sqlo) == 5);
assert(seqlock_bump(&sqlo) == 5);
- assert(seqlock_bump(&sqlo) == 7);
- assert(seqlock_cur(&sqlo) == 9);
+ assert(seqlock_bump(&sqlo) == 9);
+ assert(seqlock_bump(&sqlo) == 13);
+ assert(seqlock_cur(&sqlo) == 17);
assert(seqlock_held(&sqlo));
seqlock_release(&sqlo);
@@ -108,16 +108,16 @@ int main(int argc, char **argv)
pthread_create(&thr1, NULL, thr1func, NULL);
sleep(1);
- writestr("main @3\n");
- seqlock_acquire_val(&sqlo, 3);
+ writestr("main @5\n");
+ seqlock_acquire_val(&sqlo, 5);
sleep(2);
- writestr("main @5\n");
+ writestr("main @9\n");
seqlock_bump(&sqlo);
sleep(1);
- writestr("main @9\n");
- seqlock_acquire_val(&sqlo, 9);
+ writestr("main @17\n");
+ seqlock_acquire_val(&sqlo, 17);
sleep(1);
writestr("main @release\n");
diff --git a/tests/lib/test_timer_correctness.c b/tests/lib/test_timer_correctness.c
index 43e79ba9d0..cbf9b05546 100644
--- a/tests/lib/test_timer_correctness.c
+++ b/tests/lib/test_timer_correctness.c
@@ -28,7 +28,6 @@
#include <unistd.h>
#include "memory.h"
-#include "pqueue.h"
#include "prng.h"
#include "thread.h"
diff --git a/tests/lib/test_timer_performance.c b/tests/lib/test_timer_performance.c
index d5f4badc85..2960e0d81e 100644
--- a/tests/lib/test_timer_performance.c
+++ b/tests/lib/test_timer_performance.c
@@ -28,7 +28,6 @@
#include <unistd.h>
#include "thread.h"
-#include "pqueue.h"
#include "prng.h"
#define SCHEDULE_TIMERS 1000000
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/all-protocol-startup/r1/ipv4_routes.ref b/tests/topotests/all-protocol-startup/r1/ipv4_routes.ref
index e75d896721..0a20231371 100644
--- a/tests/topotests/all-protocol-startup/r1/ipv4_routes.ref
+++ b/tests/topotests/all-protocol-startup/r1/ipv4_routes.ref
@@ -10,3 +10,11 @@ C>* 192.168.8.0/26 is directly connected, r1-eth8, XX:XX:XX
C>* 192.168.9.0/26 is directly connected, r1-eth9, XX:XX:XX
O 192.168.0.0/24 [110/10] is directly connected, r1-eth0, XX:XX:XX
O 192.168.3.0/26 [110/10] is directly connected, r1-eth3, XX:XX:XX
+S>* 4.5.6.10/32 [1/0] via 192.168.0.2, r1-eth0, XX:XX:XX
+S>* 4.5.6.11/32 [1/0] via 192.168.0.2, r1-eth0, XX:XX:XX
+S>* 4.5.6.12/32 [1/0] is directly connected, r1-eth0, XX:XX:XX
+S>* 4.5.6.13/32 [1/0] unreachable (blackhole), XX:XX:XX
+S>* 4.5.6.14/32 [1/0] unreachable (blackhole), XX:XX:XX
+S>* 4.5.6.7/32 [1/0] unreachable (blackhole), XX:XX:XX
+S>* 4.5.6.8/32 [1/0] unreachable (blackhole), XX:XX:XX
+S>* 4.5.6.9/32 [1/0] unreachable (ICMP unreachable), XX:XX:XX
diff --git a/tests/topotests/all-protocol-startup/r1/ipv6_routes.ref b/tests/topotests/all-protocol-startup/r1/ipv6_routes.ref
index 88cee964d6..6e3e9c87c1 100644
--- a/tests/topotests/all-protocol-startup/r1/ipv6_routes.ref
+++ b/tests/topotests/all-protocol-startup/r1/ipv6_routes.ref
@@ -20,3 +20,9 @@ C * fe80::/64 is directly connected, r1-eth7, XX:XX:XX
C * fe80::/64 is directly connected, r1-eth8, XX:XX:XX
C * fe80::/64 is directly connected, r1-eth9, XX:XX:XX
O fc00:0:0:4::/64 [110/10] is directly connected, r1-eth4, XX:XX:XX
+S>* 4:5::/32 [1/0] is directly connected, r1-eth0, XX:XX:XX
+S>* 4:5::6:10/128 [1/0] via fc00::2, r1-eth0, XX:XX:XX
+S>* 4:5::6:11/128 [1/0] via fc00::2, r1-eth0, XX:XX:XX
+S>* 4:5::6:7/128 [1/0] unreachable (blackhole), XX:XX:XX
+S>* 4:5::6:8/128 [1/0] unreachable (blackhole), XX:XX:XX
+S>* 4:5::6:9/128 [1/0] unreachable (ICMP unreachable), XX:XX:XX
diff --git a/tests/topotests/all-protocol-startup/r1/zebra.conf b/tests/topotests/all-protocol-startup/r1/zebra.conf
index 164104da7e..c621593ef7 100644
--- a/tests/topotests/all-protocol-startup/r1/zebra.conf
+++ b/tests/topotests/all-protocol-startup/r1/zebra.conf
@@ -2,6 +2,26 @@ log file zebra.log
!
hostname r1
!
+# Create the various blackhole route types
+ip route 4.5.6.7/32 blackhole
+ipv6 route 4:5::6:7/128 blackhole
+ip route 4.5.6.8/32 Null0
+ipv6 route 4:5::6:8/128 Null0
+ip route 4.5.6.9/32 reject
+ipv6 route 4:5::6:9/128 reject
+# Test various spellings of NULL0 to make sure we accept them
+ip route 4.5.6.13/32 null0
+ip route 4.5.6.14/32 NULL0
+# Create normal gateway routes
+ip route 4.5.6.10/32 192.168.0.2
+ipv6 route 4:5::6:10/128 fc00:0:0:0::2
+# Create normal gateway + interface routes
+ip route 4.5.6.11/32 192.168.0.2 r1-eth0
+ipv6 route 4:5::6:11/128 fc00:0:0:0::2 r1-eth0
+# Create ifname routes
+ip route 4.5.6.12/32 r1-eth0
+ipv6 route 4:5::6:12/32 r1-eth0
+!
interface r1-eth0
description to sw0 - no routing protocol
ip address 192.168.0.1/24
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/vtysh/vtysh.c b/vtysh/vtysh.c
index 1fa1f4eaa2..f23232af75 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -164,9 +164,10 @@ static int vtysh_reconnect(struct vtysh_client *vclient);
static void vclient_close(struct vtysh_client *vclient)
{
if (vclient->fd >= 0) {
- vty_out(vty,
- "Warning: closing connection to %s because of an I/O error!\n",
- vclient->name);
+ if (vty->of)
+ vty_out(vty,
+ "Warning: closing connection to %s because of an I/O error!\n",
+ vclient->name);
close(vclient->fd);
/* indicate as candidate for reconnect */
vclient->fd = VTYSH_WAS_ACTIVE;
@@ -237,8 +238,11 @@ static int vtysh_client_run(struct vtysh_client *vclient, const char *line,
continue;
if (nread <= 0) {
- vty_out(vty, "vtysh: error reading from %s: %s (%d)",
- vclient->name, safe_strerror(errno), errno);
+ if (vty->of)
+ vty_out(vty,
+ "vtysh: error reading from %s: %s (%d)",
+ vclient->name, safe_strerror(errno),
+ errno);
goto out_err;
}
@@ -383,7 +387,7 @@ static int vtysh_client_run_all(struct vtysh_client *head_client,
rc_all = rc;
}
}
- if (wrong_instance && !correct_instance) {
+ if (wrong_instance && !correct_instance && vty->of) {
vty_out(vty,
"%% [%s]: command ignored as it targets an instance that is not running\n",
head_client->name);
@@ -852,11 +856,15 @@ int vtysh_mark_file(const char *filename)
return CMD_ERR_INCOMPLETE;
case CMD_SUCCESS:
vty_out(vty, "%s", vty->buf);
+ if (strmatch(vty_buf_trimmed, "exit-vrf"))
+ vty_out(vty, "end\n");
break;
case CMD_SUCCESS_DAEMON: {
int cmd_stat;
vty_out(vty, "%s", vty->buf);
+ if (strmatch(vty_buf_trimmed, "exit-vrf"))
+ vty_out(vty, "end\n");
cmd_stat = vtysh_client_execute(&vtysh_client[0],
vty->buf);
if (cmd_stat != CMD_SUCCESS)
@@ -1252,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)# "};
@@ -1327,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")
@@ -1470,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"
@@ -1835,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:
@@ -1925,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")
{
@@ -3613,6 +3649,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);
@@ -3846,6 +3883,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/yang/frr-bfdd.yang b/yang/frr-bfdd.yang
index 24ca8f68a8..f9ac2e43b0 100644
--- a/yang/frr-bfdd.yang
+++ b/yang/frr-bfdd.yang
@@ -264,22 +264,22 @@ module frr-bfdd {
leaf session-down-count {
type uint32;
- description "Amount of times the session went down";
+ description "Number of times the session went down";
}
leaf session-up-count {
type uint32;
- description "Amount of times the session went up";
+ description "Number of times the session went up";
}
leaf control-packet-input-count {
type uint64;
- description "Amount of control packets received";
+ description "Number of control packets received";
}
leaf control-packet-output-count {
type uint64;
- description "Amount of control packets sent";
+ description "Number of control packets sent";
}
/*
@@ -296,12 +296,12 @@ module frr-bfdd {
*/
leaf echo-packet-input-count {
type uint64;
- description "Amount of echo packets received";
+ description "Number of echo packets received";
}
leaf echo-packet-output-count {
type uint64;
- description "Amount of echo packets sent";
+ description "Number of echo packets sent";
}
}
diff --git a/yang/frr-isisd.yang b/yang/frr-isisd.yang
index 05a896a1db..9180b0c5f3 100644
--- a/yang/frr-isisd.yang
+++ b/yang/frr-isisd.yang
@@ -1024,7 +1024,7 @@ module frr-isisd {
"This notification is sent when we attempt to propagate
an LSP that is larger than the dataLinkBlockSize for the
circuit. The notification generation must be throttled
- with at least 5 seconds betweeen successive
+ with at least 5 seconds between successive
notifications.";
uses notification-instance-hdr;
@@ -1090,7 +1090,7 @@ module frr-isisd {
"This notification is sent when we receive a PDU
with a different value for the System ID length.
The notification generation must be throttled
- with at least 5 seconds betweeen successive
+ with at least 5 seconds between successive
notifications.";
uses notification-instance-hdr;
@@ -1114,7 +1114,7 @@ module frr-isisd {
"This notification is sent when we receive a PDU
with a different value for the Maximum Area Addresses.
The notification generation must be throttled
- with at least 5 seconds betweeen successive
+ with at least 5 seconds between successive
notifications.";
uses notification-instance-hdr;
@@ -1170,7 +1170,7 @@ module frr-isisd {
"This notification is sent when the system receives a
PDU with the wrong authentication type field.
The notification generation must be throttled
- with at least 5 seconds betweeen successive
+ with at least 5 seconds between successive
notifications.";
uses notification-instance-hdr;
@@ -1188,7 +1188,7 @@ module frr-isisd {
"This notification is sent when the system receives
a PDU with the wrong authentication information.
The notification generation must be throttled with
- with at least 5 seconds betweeen successive
+ with at least 5 seconds between successive
notifications.";
uses notification-instance-hdr;
@@ -1206,7 +1206,7 @@ module frr-isisd {
"This notification is sent when the system receives a
PDU with a different protocol version number.
The notification generation must be throttled
- with at least 5 seconds betweeen successive
+ with at least 5 seconds between successive
notifications.";
uses notification-instance-hdr;
@@ -1230,7 +1230,7 @@ module frr-isisd {
"This notification is sent when the system receives a
Hello PDU from an IS that does not share any area
address. The notification generation must be throttled
- with at least 5 seconds betweeen successive
+ with at least 5 seconds between successive
notifications.";
uses notification-instance-hdr;
@@ -1248,7 +1248,7 @@ module frr-isisd {
"This notification is sent when the system receives a
Hello PDU from an IS but does not establish an adjacency
for some reason. The notification generation must be
- throttled with at least 5 seconds betweeen successive
+ throttled with at least 5 seconds between successive
notifications.";
uses notification-instance-hdr;
@@ -1273,7 +1273,7 @@ module frr-isisd {
description
"This notification is sent when the system receives an
LSP with a parse error. The notification generation must
- be throttled with at least 5 seconds betweeen successive
+ be throttled with at least 5 seconds between successive
notifications.";
uses notification-instance-hdr;
@@ -1350,7 +1350,7 @@ module frr-isisd {
description
"This notification is sent when an LSP is received.
The notification generation must be throttled with at
- least 5 seconds betweeen successive notifications.";
+ least 5 seconds between successive notifications.";
uses notification-instance-hdr;
uses notification-interface-hdr;
@@ -1384,7 +1384,7 @@ module frr-isisd {
description
"This notification is sent when an LSP is regenerated.
The notification generation must be throttled with at
- least 5 seconds betweeen successive notifications.";
+ least 5 seconds between successive notifications.";
uses notification-instance-hdr;
leaf lsp-id {
diff --git a/zebra/connected.c b/zebra/connected.c
index 4101a4bf24..ffc991861c 100644
--- a/zebra/connected.c
+++ b/zebra/connected.c
@@ -120,10 +120,6 @@ struct connected *connected_check_ptp(struct interface *ifp,
struct connected *ifc;
struct listnode *node;
- /* ignore broadcast addresses */
- if (p->prefixlen != IPV4_MAX_PREFIXLEN)
- d = NULL;
-
for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
if (!prefix_same(ifc->address, p))
continue;
@@ -143,6 +139,12 @@ static int connected_same(struct connected *ifc1, struct connected *ifc2)
if (ifc1->ifp != ifc2->ifp)
return 0;
+ if (ifc1->flags != ifc2->flags)
+ return 0;
+
+ if (ifc1->conf != ifc2->conf)
+ return 0;
+
if (ifc1->destination)
if (!ifc2->destination)
return 0;
@@ -154,12 +156,6 @@ static int connected_same(struct connected *ifc1, struct connected *ifc2)
if (!prefix_same(ifc1->destination, ifc2->destination))
return 0;
- if (ifc1->flags != ifc2->flags)
- return 0;
-
- if (ifc1->conf != ifc2->conf)
- return 0;
-
return 1;
}
@@ -276,7 +272,7 @@ void connected_up(struct interface *ifp, struct connected *ifc)
/* Add connected IPv4 route to the interface. */
void connected_add_ipv4(struct interface *ifp, int flags, struct in_addr *addr,
- uint16_t prefixlen, struct in_addr *broad,
+ uint16_t prefixlen, struct in_addr *dest,
const char *label, uint32_t metric)
{
struct prefix_ipv4 *p;
@@ -302,59 +298,39 @@ void connected_add_ipv4(struct interface *ifp, int flags, struct in_addr *addr,
: prefixlen;
ifc->address = (struct prefix *)p;
- /* If there is broadcast or peer address. */
- if (broad) {
- p = prefix_ipv4_new();
- p->family = AF_INET;
- p->prefix = *broad;
- p->prefixlen = prefixlen;
- ifc->destination = (struct prefix *)p;
-
+ /* If there is a peer address. */
+ if (CONNECTED_PEER(ifc)) {
/* validate the destination address */
- if (CONNECTED_PEER(ifc)) {
- if (IPV4_ADDR_SAME(addr, broad))
+ if (dest) {
+ p = prefix_ipv4_new();
+ p->family = AF_INET;
+ p->prefix = *dest;
+ p->prefixlen = prefixlen;
+ ifc->destination = (struct prefix *)p;
+
+ if (IPV4_ADDR_SAME(addr, dest))
flog_warn(
EC_ZEBRA_IFACE_SAME_LOCAL_AS_PEER,
"warning: interface %s has same local and peer "
"address %s, routing protocols may malfunction",
ifp->name, inet_ntoa(*addr));
} else {
- if (broad->s_addr
- != ipv4_broadcast_addr(addr->s_addr, prefixlen)) {
- char buf[2][INET_ADDRSTRLEN];
- struct in_addr bcalc;
- bcalc.s_addr = ipv4_broadcast_addr(addr->s_addr,
- prefixlen);
- flog_warn(
- EC_ZEBRA_BCAST_ADDR_MISMATCH,
- "warning: interface %s broadcast addr %s/%d != "
- "calculated %s, routing protocols may malfunction",
- ifp->name,
- inet_ntop(AF_INET, broad, buf[0],
- sizeof(buf[0])),
- prefixlen,
- inet_ntop(AF_INET, &bcalc, buf[1],
- sizeof(buf[1])));
- }
- }
-
- } else {
- if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_PEER)) {
zlog_debug(
"warning: %s called for interface %s "
"with peer flag set, but no peer address supplied",
__func__, ifp->name);
UNSET_FLAG(ifc->flags, ZEBRA_IFA_PEER);
}
-
- /* no broadcast or destination address was supplied */
- if ((prefixlen == IPV4_MAX_PREFIXLEN) && if_is_pointopoint(ifp))
- zlog_debug(
- "warning: PtP interface %s with addr %s/%d needs a "
- "peer address",
- ifp->name, inet_ntoa(*addr), prefixlen);
}
+ /* no destination address was supplied */
+ if (!dest && (prefixlen == IPV4_MAX_PREFIXLEN)
+ && if_is_pointopoint(ifp))
+ zlog_debug(
+ "warning: PtP interface %s with addr %s/%d needs a "
+ "peer address",
+ ifp->name, inet_ntoa(*addr), prefixlen);
+
/* Label of this address. */
if (label)
ifc->label = XSTRDUP(MTYPE_CONNECTED_LABEL, label);
@@ -464,7 +440,7 @@ static void connected_delete_helper(struct connected *ifc, struct prefix *p)
/* Delete connected IPv4 route to the interface. */
void connected_delete_ipv4(struct interface *ifp, int flags,
struct in_addr *addr, uint16_t prefixlen,
- struct in_addr *broad)
+ struct in_addr *dest)
{
struct prefix p, d;
struct connected *ifc;
@@ -475,10 +451,10 @@ void connected_delete_ipv4(struct interface *ifp, int flags,
p.prefixlen = CHECK_FLAG(flags, ZEBRA_IFA_PEER) ? IPV4_MAX_PREFIXLEN
: prefixlen;
- if (broad) {
+ if (dest) {
memset(&d, 0, sizeof(struct prefix));
d.family = AF_INET;
- d.u.prefix4 = *broad;
+ d.u.prefix4 = *dest;
d.prefixlen = prefixlen;
ifc = connected_check_ptp(ifp, &p, &d);
} else
@@ -489,7 +465,7 @@ void connected_delete_ipv4(struct interface *ifp, int flags,
/* Add connected IPv6 route to the interface. */
void connected_add_ipv6(struct interface *ifp, int flags, struct in6_addr *addr,
- struct in6_addr *broad, uint16_t prefixlen,
+ struct in6_addr *dest, uint16_t prefixlen,
const char *label, uint32_t metric)
{
struct prefix_ipv6 *p;
@@ -514,10 +490,10 @@ void connected_add_ipv6(struct interface *ifp, int flags, struct in6_addr *addr,
p->prefixlen = prefixlen;
ifc->address = (struct prefix *)p;
- if (broad) {
+ if (dest) {
p = prefix_ipv6_new();
p->family = AF_INET6;
- IPV6_ADDR_COPY(&p->prefix, broad);
+ IPV6_ADDR_COPY(&p->prefix, dest);
p->prefixlen = prefixlen;
ifc->destination = (struct prefix *)p;
} else {
@@ -547,7 +523,7 @@ void connected_add_ipv6(struct interface *ifp, int flags, struct in6_addr *addr,
}
void connected_delete_ipv6(struct interface *ifp, struct in6_addr *address,
- struct in6_addr *broad, uint16_t prefixlen)
+ struct in6_addr *dest, uint16_t prefixlen)
{
struct prefix p, d;
struct connected *ifc;
@@ -557,10 +533,10 @@ void connected_delete_ipv6(struct interface *ifp, struct in6_addr *address,
memcpy(&p.u.prefix6, address, sizeof(struct in6_addr));
p.prefixlen = prefixlen;
- if (broad) {
+ if (dest) {
memset(&d, 0, sizeof(struct prefix));
d.family = AF_INET6;
- IPV6_ADDR_COPY(&d.u.prefix6, broad);
+ IPV6_ADDR_COPY(&d.u.prefix6, dest);
d.prefixlen = prefixlen;
ifc = connected_check_ptp(ifp, &p, &d);
} else
diff --git a/zebra/connected.h b/zebra/connected.h
index 7672bec006..14f6cb2db0 100644
--- a/zebra/connected.h
+++ b/zebra/connected.h
@@ -40,12 +40,12 @@ extern struct connected *connected_check_ptp(struct interface *ifp,
extern void connected_add_ipv4(struct interface *ifp, int flags,
struct in_addr *addr, uint16_t prefixlen,
- struct in_addr *broad, const char *label,
+ struct in_addr *dest, const char *label,
uint32_t metric);
extern void connected_delete_ipv4(struct interface *ifp, int flags,
struct in_addr *addr, uint16_t prefixlen,
- struct in_addr *broad);
+ struct in_addr *dest);
extern void connected_delete_ipv4_unnumbered(struct connected *ifc);
@@ -53,12 +53,12 @@ extern void connected_up(struct interface *ifp, struct connected *ifc);
extern void connected_down(struct interface *ifp, struct connected *ifc);
extern void connected_add_ipv6(struct interface *ifp, int flags,
- struct in6_addr *address, struct in6_addr *broad,
+ struct in6_addr *address, struct in6_addr *dest,
uint16_t prefixlen, const char *label,
uint32_t metric);
extern void connected_delete_ipv6(struct interface *ifp,
struct in6_addr *address,
- struct in6_addr *broad, uint16_t prefixlen);
+ struct in6_addr *dest, uint16_t prefixlen);
extern int connected_is_unnumbered(struct interface *);
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c
index 63e72fed00..e157c2d70a 100644
--- a/zebra/if_netlink.c
+++ b/zebra/if_netlink.c
@@ -590,7 +590,7 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup)
char *kind = NULL;
char *desc = NULL;
char *slave_kind = NULL;
- struct zebra_ns *zns;
+ struct zebra_ns *zns = NULL;
vrf_id_t vrf_id = VRF_DEFAULT;
zebra_iftype_t zif_type = ZEBRA_IF_OTHER;
zebra_slave_iftype_t zif_slave_type = ZEBRA_IF_SLAVE_NONE;
@@ -598,6 +598,7 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup)
ifindex_t link_ifindex = IFINDEX_INTERNAL;
ifindex_t bond_ifindex = IFINDEX_INTERNAL;
struct zebra_if *zif;
+ struct vrf *vrf = NULL;
zns = zebra_ns_lookup(ns_id);
ifi = NLMSG_DATA(h);
@@ -681,9 +682,17 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup)
if (tb[IFLA_LINK])
link_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_LINK]);
- /* Add interface. */
- ifp = if_get_by_name(name, vrf_id);
- set_ifindex(ifp, ifi->ifi_index, zns);
+ vrf = vrf_get(vrf_id, NULL);
+ /* Add interface.
+ * We add by index first because in some cases such as the master
+ * interface, we have the index before we have the name. Fixing
+ * back references on the slave interfaces is painful if not done
+ * this way, i.e. by creating by ifindex.
+ */
+ ifp = if_get_by_ifindex(ifi->ifi_index, vrf_id);
+ set_ifindex(ifp, ifi->ifi_index, zns); /* add it to ns struct */
+ strlcpy(ifp->name, name, sizeof(ifp->name));
+ IFNAME_RB_INSERT(vrf, ifp);
ifp->flags = ifi->ifi_flags & 0x0000fffff;
ifp->mtu6 = ifp->mtu = *(uint32_t *)RTA_DATA(tb[IFLA_MTU]);
ifp->metric = 0;
@@ -879,11 +888,13 @@ static int netlink_address_ctx(const struct zebra_dplane_ctx *ctx)
p = dplane_ctx_get_intf_dest(ctx);
addattr_l(&req.n, sizeof(req), IFA_ADDRESS,
&p->u.prefix, bytelen);
- } else if (cmd == RTM_NEWADDR &&
- dplane_ctx_intf_has_dest(ctx)) {
- p = dplane_ctx_get_intf_dest(ctx);
+ } else if (cmd == RTM_NEWADDR) {
+ struct in_addr broad = {
+ .s_addr = ipv4_broadcast_addr(p->u.prefix4.s_addr,
+ p->prefixlen)
+ };
addattr_l(&req.n, sizeof(req), IFA_BROADCAST,
- &p->u.prefix, bytelen);
+ &broad, bytelen);
}
}
@@ -1056,7 +1067,7 @@ int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup)
else
connected_delete_ipv4(
ifp, flags, (struct in_addr *)addr,
- ifa->ifa_prefixlen, (struct in_addr *)broad);
+ ifa->ifa_prefixlen, NULL);
}
if (ifa->ifa_family == AF_INET6) {
if (ifa->ifa_prefixlen > IPV6_MAX_BITLEN) {
@@ -1082,8 +1093,7 @@ int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup)
metric);
} else
connected_delete_ipv6(ifp, (struct in6_addr *)addr,
- (struct in6_addr *)broad,
- ifa->ifa_prefixlen);
+ NULL, ifa->ifa_prefixlen);
}
return 0;
diff --git a/zebra/interface.c b/zebra/interface.c
index 732e900bbd..6486c01430 100644
--- a/zebra/interface.c
+++ b/zebra/interface.c
@@ -1081,12 +1081,10 @@ static void connected_dump_vty(struct vty *vty, struct connected *connected)
vty_out(vty, "/%d", p->prefixlen);
/* If there is destination address, print it. */
- if (connected->destination) {
- vty_out(vty,
- (CONNECTED_PEER(connected) ? " peer " : " broadcast "));
+ if (CONNECTED_PEER(connected) && connected->destination) {
+ vty_out(vty, " peer ");
prefix_vty_out(vty, connected->destination);
- if (CONNECTED_PEER(connected))
- vty_out(vty, "/%d", connected->destination->prefixlen);
+ vty_out(vty, "/%d", connected->destination->prefixlen);
}
if (CHECK_FLAG(connected->flags, ZEBRA_IFA_SECONDARY))
@@ -1382,26 +1380,35 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp)
struct zebra_l2info_brslave *br_slave;
br_slave = &zebra_if->brslave_info;
- if (br_slave->bridge_ifindex != IFINDEX_INTERNAL)
- vty_out(vty, " Master (bridge) ifindex %u\n",
- br_slave->bridge_ifindex);
+ if (br_slave->bridge_ifindex != IFINDEX_INTERNAL) {
+ if (br_slave->br_if)
+ vty_out(vty, " Master interface: %s\n",
+ br_slave->br_if->name);
+ else
+ vty_out(vty, " Master ifindex: %u\n",
+ br_slave->bridge_ifindex);
+ }
}
if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) {
struct zebra_l2info_bondslave *bond_slave;
bond_slave = &zebra_if->bondslave_info;
- if (bond_slave->bond_ifindex != IFINDEX_INTERNAL)
- vty_out(vty, " Master (bond) ifindex %u\n",
- bond_slave->bond_ifindex);
+ if (bond_slave->bond_ifindex != IFINDEX_INTERNAL) {
+ if (bond_slave->bond_if)
+ vty_out(vty, " Master interface: %s\n",
+ bond_slave->bond_if->name);
+ else
+ vty_out(vty, " Master ifindex: %u\n",
+ bond_slave->bond_ifindex);
+ }
}
if (zebra_if->link_ifindex != IFINDEX_INTERNAL) {
- vty_out(vty, " Link ifindex %u", zebra_if->link_ifindex);
if (zebra_if->link)
- vty_out(vty, "(%s)\n", zebra_if->link->name);
+ vty_out(vty, " Parent interface: %s\n", zebra_if->link->name);
else
- vty_out(vty, "(Unknown)\n");
+ vty_out(vty, " Parent ifindex: %d\n", zebra_if->link_ifindex);
}
if (HAS_LINK_PARAMS(ifp)) {
@@ -2675,12 +2682,6 @@ static int ip_address_install(struct vty *vty, struct interface *ifp,
p = prefix_ipv4_new();
*p = pp;
ifc->destination = (struct prefix *)p;
- } else if (p->prefixlen <= IPV4_MAX_PREFIXLEN - 2) {
- p = prefix_ipv4_new();
- *p = lp;
- p->prefix.s_addr = ipv4_broadcast_addr(p->prefix.s_addr,
- p->prefixlen);
- ifc->destination = (struct prefix *)p;
}
/* Label. */
diff --git a/zebra/redistribute.c b/zebra/redistribute.c
index 0280cde238..1ae2ba92b0 100644
--- a/zebra/redistribute.c
+++ b/zebra/redistribute.c
@@ -172,6 +172,13 @@ void redistribute_update(const struct prefix *p, const struct prefix *src_p,
__FUNCTION__);
return;
}
+ if (!zebra_check_addr(p)) {
+ if (IS_ZEBRA_DEBUG_RIB)
+ zlog_debug("Redist update filter prefix %s",
+ prefix2str(p, buf, sizeof(buf)));
+ return;
+ }
+
for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) {
send_redistribute = 0;
@@ -246,6 +253,13 @@ void redistribute_delete(const struct prefix *p, const struct prefix *src_p,
return;
}
+ if (!zebra_check_addr(p)) {
+ if (IS_ZEBRA_DEBUG_RIB)
+ zlog_debug("Redist delete filter prefix %s",
+ prefix2str(p, buf, sizeof(buf)));
+ return;
+ }
+
for (ALL_LIST_ELEMENTS(zrouter.client_list, node, nnode, client)) {
if ((is_default_prefix(p)
&& vrf_bitmap_check(client->redist_default[afi],
diff --git a/zebra/rt.h b/zebra/rt.h
index 04576671fe..727d2d0c55 100644
--- a/zebra/rt.h
+++ b/zebra/rt.h
@@ -41,7 +41,7 @@ extern "C" {
((RKERNEL_ROUTE(type)) || (type) == ZEBRA_ROUTE_CONNECT)
/*
- * Update or delete a route, LSP, or pseudowire from the kernel,
+ * Update or delete a route, LSP, pseudowire, or vxlan MAC from the kernel,
* using info from a dataplane context.
*/
extern enum zebra_dplane_result kernel_route_update(
@@ -55,6 +55,8 @@ enum zebra_dplane_result kernel_pw_update(struct zebra_dplane_ctx *ctx);
enum zebra_dplane_result kernel_address_update_ctx(
struct zebra_dplane_ctx *ctx);
+enum zebra_dplane_result kernel_mac_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,
@@ -68,12 +70,6 @@ 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_mac(struct interface *ifp, vlanid_t vid,
- struct ethaddr *mac, struct in_addr vtep_ip,
- bool sticky);
-extern int kernel_del_mac(struct interface *ifp, vlanid_t vid,
- struct ethaddr *mac, 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);
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index 95ac68fb90..cc73effaf9 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -1019,33 +1019,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 +1205,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) {
@@ -2292,33 +2282,70 @@ int netlink_macfdb_read_specific_mac(struct zebra_ns *zns,
return ret;
}
-static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid,
- struct ethaddr *mac, struct in_addr vtep_ip,
- int cmd, bool sticky)
+
+/*
+ * Netlink-specific handler for MAC updates using dataplane context object.
+ */
+static enum zebra_dplane_result
+netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx)
{
- struct zebra_ns *zns;
struct {
struct nlmsghdr n;
struct ndmsg ndm;
char buf[256];
} req;
+ int ret;
int dst_alen;
struct zebra_if *zif;
struct interface *br_if;
struct zebra_if *br_zif;
- char buf[ETHER_ADDR_STRLEN];
int vid_present = 0;
char vid_buf[20];
- char dst_buf[30];
- struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id);
+ struct zebra_ns *zns;
+ struct interface *ifp;
+ int cmd;
+ struct in_addr vtep_ip;
+ vlanid_t vid;
+
+ if (dplane_ctx_get_op(ctx) == DPLANE_OP_MAC_INSTALL)
+ cmd = RTM_NEWNEIGH;
+ else
+ cmd = RTM_DELNEIGH;
+
+ /* Locate zebra ns and interface objects from context data */
+ zns = zebra_ns_lookup(dplane_ctx_get_ns(ctx)->ns_id);
+ if (zns == NULL) {
+ /* Nothing to be done */
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("MAC %s on IF %s(%u) - zebra ns unknown",
+ (cmd == RTM_NEWNEIGH) ? "add" : "del",
+ dplane_ctx_get_ifname(ctx),
+ dplane_ctx_get_ifindex(ctx));
+
+ return ZEBRA_DPLANE_REQUEST_FAILURE;
+ }
+
+ ifp = if_lookup_by_index_per_ns(zns, dplane_ctx_get_ifindex(ctx));
+ if (ifp == NULL) {
+ /* Nothing to be done */
+ /* Nothing to be done */
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("MAC %s on IF %s(%u) - interface unknown",
+ (cmd == RTM_NEWNEIGH) ? "add" : "del",
+ dplane_ctx_get_ifname(ctx),
+ dplane_ctx_get_ifindex(ctx));
+ return ZEBRA_DPLANE_REQUEST_FAILURE;
+ }
+
+ vid = dplane_ctx_mac_get_vlan(ctx);
- zns = zvrf->zns;
zif = ifp->info;
if ((br_if = zif->brslave_info.br_if) == NULL) {
- zlog_debug("MAC %s on IF %s(%u) - no mapping to bridge",
- (cmd == RTM_NEWNEIGH) ? "add" : "del", ifp->name,
- ifp->ifindex);
- return -1;
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("MAC %s on IF %s(%u) - no mapping to bridge",
+ (cmd == RTM_NEWNEIGH) ? "add" : "del",
+ ifp->name, ifp->ifindex);
+ return ZEBRA_DPLANE_REQUEST_FAILURE;
}
memset(&req, 0, sizeof(req));
@@ -2332,16 +2359,19 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid,
req.ndm.ndm_flags |= NTF_SELF | NTF_MASTER;
req.ndm.ndm_state = NUD_REACHABLE;
- if (sticky)
+ if (dplane_ctx_mac_is_sticky(ctx))
req.ndm.ndm_state |= NUD_NOARP;
else
req.ndm.ndm_flags |= NTF_EXT_LEARNED;
- addattr_l(&req.n, sizeof(req), NDA_LLADDR, mac, 6);
+ addattr_l(&req.n, sizeof(req), NDA_LLADDR,
+ dplane_ctx_mac_get_addr(ctx), 6);
req.ndm.ndm_ifindex = ifp->ifindex;
+
dst_alen = 4; // TODO: hardcoded
+ vtep_ip = *(dplane_ctx_mac_get_vtep_ip(ctx));
addattr_l(&req.n, sizeof(req), NDA_DST, &vtep_ip, dst_alen);
- sprintf(dst_buf, " dst %s", inet_ntoa(vtep_ip));
+
br_zif = (struct zebra_if *)br_if->info;
if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif) && vid > 0) {
addattr16(&req.n, sizeof(req), NDA_VLAN, vid);
@@ -2350,16 +2380,29 @@ static int netlink_macfdb_update(struct interface *ifp, vlanid_t vid,
}
addattr32(&req.n, sizeof(req), NDA_MASTER, br_if->ifindex);
- if (IS_ZEBRA_DEBUG_KERNEL)
+ if (IS_ZEBRA_DEBUG_KERNEL) {
+ char ipbuf[PREFIX_STRLEN];
+ char buf[ETHER_ADDR_STRLEN];
+ char dst_buf[PREFIX_STRLEN + 10];
+
+ inet_ntop(AF_INET, &vtep_ip, ipbuf, sizeof(ipbuf));
+ snprintf(dst_buf, sizeof(dst_buf), " dst %s", ipbuf);
+ prefix_mac2str(dplane_ctx_mac_get_addr(ctx), buf, sizeof(buf));
+
zlog_debug("Tx %s family %s IF %s(%u)%s %sMAC %s%s",
nl_msg_type_to_str(cmd),
nl_family_to_str(req.ndm.ndm_family), ifp->name,
ifp->ifindex, vid_present ? vid_buf : "",
- sticky ? "sticky " : "",
- prefix_mac2str(mac, buf, sizeof(buf)), dst_buf);
+ dplane_ctx_mac_is_sticky(ctx) ? "sticky " : "",
+ buf, dst_buf);
+ }
- return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns,
- 0);
+ ret = netlink_talk_info(netlink_talk_filter, &req.n,
+ dplane_ctx_get_ns(ctx), 0);
+ if (ret == 0)
+ return ZEBRA_DPLANE_REQUEST_SUCCESS;
+ else
+ return ZEBRA_DPLANE_REQUEST_FAILURE;
}
/*
@@ -2772,17 +2815,12 @@ static int netlink_neigh_update2(struct interface *ifp, struct ipaddr *ip,
0);
}
-int kernel_add_mac(struct interface *ifp, vlanid_t vid, struct ethaddr *mac,
- struct in_addr vtep_ip, bool sticky)
-{
- return netlink_macfdb_update(ifp, vid, mac, vtep_ip, RTM_NEWNEIGH,
- sticky);
-}
-
-int kernel_del_mac(struct interface *ifp, vlanid_t vid, struct ethaddr *mac,
- struct in_addr vtep_ip)
+/*
+ * Update MAC, using dataplane context object.
+ */
+enum zebra_dplane_result kernel_mac_update_ctx(struct zebra_dplane_ctx *ctx)
{
- return netlink_macfdb_update(ifp, vid, mac, vtep_ip, RTM_DELNEIGH, 0);
+ return netlink_macfdb_update_ctx(ctx);
}
int kernel_add_neigh(struct interface *ifp, struct ipaddr *ip,
diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c
index 8d8bdd0a6d..7e9a42a617 100644
--- a/zebra/rt_socket.c
+++ b/zebra/rt_socket.c
@@ -386,16 +386,12 @@ int kernel_del_vtep(vni_t vni, struct interface *ifp, struct in_addr *vtep_ip)
return 0;
}
-int kernel_add_mac(struct interface *ifp, vlanid_t vid, struct ethaddr *mac,
- struct in_addr vtep_ip, bool sticky)
-{
- return 0;
-}
-
-int kernel_del_mac(struct interface *ifp, vlanid_t vid, struct ethaddr *mac,
- struct in_addr vtep_ip)
+/*
+ * Update MAC, using dataplane context object. No-op here for now.
+ */
+enum zebra_dplane_result kernel_mac_update_ctx(struct zebra_dplane_ctx *ctx)
{
- return 0;
+ return ZEBRA_DPLANE_REQUEST_SUCCESS;
}
int kernel_add_neigh(struct interface *ifp, struct ipaddr *ip,
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c
index f93562b31b..512a9b4021 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -109,8 +109,6 @@ struct dplane_route_info {
* Pseudowire info for the dataplane
*/
struct dplane_pw_info {
- char ifname[IF_NAMESIZE];
- ifindex_t ifindex;
int type;
int af;
int status;
@@ -130,16 +128,13 @@ struct dplane_pw_info {
*/
struct dplane_intf_info {
- char ifname[INTERFACE_NAMSIZ];
- ifindex_t ifindex;
-
uint32_t metric;
uint32_t flags;
#define DPLANE_INTF_CONNECTED (1 << 0) /* Connected peer, p2p */
#define DPLANE_INTF_SECONDARY (1 << 1)
#define DPLANE_INTF_BROADCAST (1 << 2)
-#define DPLANE_INTF_HAS_DEST (1 << 3)
+#define DPLANE_INTF_HAS_DEST DPLANE_INTF_CONNECTED
#define DPLANE_INTF_HAS_LABEL (1 << 4)
/* Interface address/prefix */
@@ -153,6 +148,17 @@ struct dplane_intf_info {
};
/*
+ * MAC address info for the dataplane.
+ */
+struct dplane_mac_info {
+ vlanid_t vid;
+ struct ethaddr mac;
+ struct in_addr vtep_ip;
+ bool is_sticky;
+
+};
+
+/*
* 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).
@@ -188,12 +194,16 @@ struct zebra_dplane_ctx {
vrf_id_t zd_vrf_id;
uint32_t zd_table_id;
+ char zd_ifname[INTERFACE_NAMSIZ];
+ ifindex_t zd_ifindex;
+
/* Support info for different kinds of updates */
union {
struct dplane_route_info rinfo;
zebra_lsp_t lsp;
struct dplane_pw_info pw;
struct dplane_intf_info intf;
+ struct dplane_mac_info macinfo;
} u;
/* Namespace info, used especially for netlink kernel communication */
@@ -273,8 +283,8 @@ static struct zebra_dplane_globals {
/* Sentinel for end of shutdown */
volatile bool dg_run;
- /* Route-update context queue inbound to the dataplane */
- TAILQ_HEAD(zdg_ctx_q, zebra_dplane_ctx) dg_route_ctx_q;
+ /* Update context queue inbound to the dataplane */
+ TAILQ_HEAD(zdg_ctx_q, zebra_dplane_ctx) dg_update_ctx_q;
/* Ordered list of providers */
TAILQ_HEAD(zdg_prov_q, zebra_dplane_provider) dg_providers_q;
@@ -308,6 +318,9 @@ static struct zebra_dplane_globals {
_Atomic uint32_t dg_intf_addrs_in;
_Atomic uint32_t dg_intf_addr_errors;
+ _Atomic uint32_t dg_macs_in;
+ _Atomic uint32_t dg_mac_errors;
+
_Atomic uint32_t dg_update_yields;
/* Dataplane pthread */
@@ -348,6 +361,10 @@ static enum zebra_dplane_result pw_update_internal(struct zebra_pw *pw,
static enum zebra_dplane_result intf_addr_update_internal(
const struct interface *ifp, const struct connected *ifc,
enum dplane_op_e op);
+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);
/*
* Public APIs
@@ -466,6 +483,8 @@ static void dplane_ctx_free(struct zebra_dplane_ctx **pctx)
}
break;
+ case DPLANE_OP_MAC_INSTALL:
+ case DPLANE_OP_MAC_DELETE:
case DPLANE_OP_NONE:
break;
}
@@ -626,6 +645,12 @@ const char *dplane_op2str(enum dplane_op_e op)
ret = "ADDR_UNINSTALL";
break;
+ case DPLANE_OP_MAC_INSTALL:
+ ret = "MAC_INSTALL";
+ break;
+ case DPLANE_OP_MAC_DELETE:
+ ret = "MAC_DELETE";
+ break;
}
return ret;
@@ -744,6 +769,19 @@ void dplane_ctx_set_notif_provider(struct zebra_dplane_ctx *ctx,
ctx->zd_notif_provider = id;
}
+const char *dplane_ctx_get_ifname(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->zd_ifname;
+}
+
+ifindex_t dplane_ctx_get_ifindex(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+
+ return ctx->zd_ifindex;
+}
void dplane_ctx_set_type(struct zebra_dplane_ctx *ctx, int type)
{
@@ -1030,13 +1068,6 @@ uint32_t dplane_ctx_get_lsp_num_ecmp(const struct zebra_dplane_ctx *ctx)
return ctx->u.lsp.num_ecmp;
}
-const char *dplane_ctx_get_pw_ifname(const struct zebra_dplane_ctx *ctx)
-{
- DPLANE_CTX_VALID(ctx);
-
- return ctx->u.pw.ifname;
-}
-
mpls_label_t dplane_ctx_get_pw_local_label(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
@@ -1104,20 +1135,6 @@ dplane_ctx_get_pw_nhg(const struct zebra_dplane_ctx *ctx)
}
/* Accessors for interface information */
-const char *dplane_ctx_get_ifname(const struct zebra_dplane_ctx *ctx)
-{
- DPLANE_CTX_VALID(ctx);
-
- return ctx->u.intf.ifname;
-}
-
-ifindex_t dplane_ctx_get_ifindex(const struct zebra_dplane_ctx *ctx)
-{
- DPLANE_CTX_VALID(ctx);
-
- return ctx->u.intf.ifindex;
-}
-
uint32_t dplane_ctx_get_intf_metric(const struct zebra_dplane_ctx *ctx)
{
DPLANE_CTX_VALID(ctx);
@@ -1187,6 +1204,33 @@ const char *dplane_ctx_get_intf_label(const struct zebra_dplane_ctx *ctx)
return ctx->u.intf.label;
}
+/* Accessors for MAC information */
+vlanid_t dplane_ctx_mac_get_vlan(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+ return ctx->u.macinfo.vid;
+}
+
+bool dplane_ctx_mac_is_sticky(const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+ return ctx->u.macinfo.is_sticky;
+}
+
+const struct ethaddr *dplane_ctx_mac_get_addr(
+ const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+ return &(ctx->u.macinfo.mac);
+}
+
+const struct in_addr *dplane_ctx_mac_get_vtep_ip(
+ const struct zebra_dplane_ctx *ctx)
+{
+ DPLANE_CTX_VALID(ctx);
+ return &(ctx->u.macinfo.vtep_ip);
+}
+
/*
* End of dplane context accessors
*/
@@ -1422,10 +1466,10 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx,
memset(&ctx->u.pw, 0, sizeof(ctx->u.pw));
/* This name appears to be c-string, so we use string copy. */
- strlcpy(ctx->u.pw.ifname, pw->ifname, sizeof(ctx->u.pw.ifname));
+ strlcpy(ctx->zd_ifname, pw->ifname, sizeof(ctx->zd_ifname));
ctx->zd_vrf_id = pw->vrf_id;
- ctx->u.pw.ifindex = pw->ifindex;
+ ctx->zd_ifindex = pw->ifindex;
ctx->u.pw.type = pw->type;
ctx->u.pw.af = pw->af;
ctx->u.pw.local_label = pw->local_label;
@@ -1467,10 +1511,10 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx,
}
/*
- * Enqueue a new route update,
+ * Enqueue a new update,
* and ensure an event is active for the dataplane pthread.
*/
-static int dplane_route_enqueue(struct zebra_dplane_ctx *ctx)
+static int dplane_update_enqueue(struct zebra_dplane_ctx *ctx)
{
int ret = EINVAL;
uint32_t high, curr;
@@ -1478,7 +1522,7 @@ static int dplane_route_enqueue(struct zebra_dplane_ctx *ctx)
/* Enqueue for processing by the dataplane pthread */
DPLANE_LOCK();
{
- TAILQ_INSERT_TAIL(&zdplane_info.dg_route_ctx_q, ctx,
+ TAILQ_INSERT_TAIL(&zdplane_info.dg_update_ctx_q, ctx,
zd_q_entries);
}
DPLANE_UNLOCK();
@@ -1558,7 +1602,7 @@ dplane_route_update_internal(struct route_node *rn,
}
/* Enqueue context for processing */
- ret = dplane_route_enqueue(ctx);
+ ret = dplane_update_enqueue(ctx);
}
/* Update counter */
@@ -1724,7 +1768,7 @@ dplane_route_notif_update(struct route_node *rn,
dplane_ctx_set_notif_provider(new_ctx,
dplane_ctx_get_notif_provider(ctx));
- dplane_route_enqueue(new_ctx);
+ dplane_update_enqueue(new_ctx);
ret = ZEBRA_DPLANE_REQUEST_QUEUED;
@@ -1791,7 +1835,7 @@ dplane_lsp_notif_update(zebra_lsp_t *lsp,
ctx,
dplane_ctx_get_notif_provider(notif_ctx));
- ret = dplane_route_enqueue(ctx);
+ ret = dplane_update_enqueue(ctx);
done:
/* Update counter */
@@ -1842,7 +1886,7 @@ static enum zebra_dplane_result lsp_update_internal(zebra_lsp_t *lsp,
if (ret != AOK)
goto done;
- ret = dplane_route_enqueue(ctx);
+ ret = dplane_update_enqueue(ctx);
done:
/* Update counter */
@@ -1876,7 +1920,7 @@ static enum zebra_dplane_result pw_update_internal(struct zebra_pw *pw,
if (ret != AOK)
goto done;
- ret = dplane_route_enqueue(ctx);
+ ret = dplane_update_enqueue(ctx);
done:
/* Update counter */
@@ -1967,8 +2011,8 @@ static enum zebra_dplane_result intf_addr_update_internal(
/* Init the interface-addr-specific area */
memset(&ctx->u.intf, 0, sizeof(ctx->u.intf));
- strlcpy(ctx->u.intf.ifname, ifp->name, sizeof(ctx->u.intf.ifname));
- ctx->u.intf.ifindex = ifp->ifindex;
+ strlcpy(ctx->zd_ifname, ifp->name, sizeof(ctx->zd_ifname));
+ ctx->zd_ifindex = ifp->ifindex;
ctx->u.intf.prefix = *(ifc->address);
if (if_is_broadcast(ifp))
@@ -1978,9 +2022,6 @@ static enum zebra_dplane_result intf_addr_update_internal(
ctx->u.intf.dest_prefix = *(ifc->destination);
ctx->u.intf.flags |=
(DPLANE_INTF_CONNECTED | DPLANE_INTF_HAS_DEST);
- } else if (ifc->destination) {
- ctx->u.intf.dest_prefix = *(ifc->destination);
- ctx->u.intf.flags |= DPLANE_INTF_HAS_DEST;
}
if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY))
@@ -2003,7 +2044,7 @@ static enum zebra_dplane_result intf_addr_update_internal(
}
}
- ret = dplane_route_enqueue(ctx);
+ ret = dplane_update_enqueue(ctx);
/* Increment counter */
atomic_fetch_add_explicit(&zdplane_info.dg_intf_addrs_in, 1,
@@ -2022,6 +2063,104 @@ static enum zebra_dplane_result intf_addr_update_internal(
}
/*
+ * Enqueue vxlan/evpn mac add (or update).
+ */
+enum zebra_dplane_result dplane_mac_add(const struct interface *ifp,
+ vlanid_t vid,
+ const struct ethaddr *mac,
+ struct in_addr vtep_ip,
+ bool sticky)
+{
+ enum zebra_dplane_result result;
+
+ /* Use common helper api */
+ result = mac_update_internal(DPLANE_OP_MAC_INSTALL, ifp, vid,
+ mac, vtep_ip, sticky);
+ return result;
+}
+
+/*
+ * Enqueue vxlan/evpn mac delete.
+ */
+enum zebra_dplane_result dplane_mac_del(const struct interface *ifp,
+ vlanid_t vid,
+ const struct ethaddr *mac,
+ struct in_addr vtep_ip)
+{
+ enum zebra_dplane_result result;
+
+ /* Use common helper api */
+ result = mac_update_internal(DPLANE_OP_MAC_DELETE, ifp, vid, mac,
+ vtep_ip, false);
+ return result;
+}
+
+/*
+ * Common helper api for MAC address/vxlan updates
+ */
+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)
+{
+ 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 mac ctx %s: mac %s, ifp %s, vtep %s",
+ dplane_op2str(op),
+ prefix_mac2str(mac, buf1, sizeof(buf1)),
+ ifp->name,
+ inet_ntop(AF_INET, &vtep_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 mac-specific data area */
+ memset(&ctx->u.macinfo, 0, sizeof(ctx->u.macinfo));
+
+ ctx->u.macinfo.vtep_ip = vtep_ip;
+ ctx->u.macinfo.mac = *mac;
+ ctx->u.macinfo.vid = vid;
+ ctx->u.macinfo.is_sticky = sticky;
+
+ /* Enqueue for processing on the dplane pthread */
+ ret = dplane_update_enqueue(ctx);
+
+ /* Increment counter */
+ atomic_fetch_add_explicit(&zdplane_info.dg_macs_in, 1,
+ memory_order_relaxed);
+
+ if (ret == AOK)
+ result = ZEBRA_DPLANE_REQUEST_QUEUED;
+ else {
+ /* Error counter */
+ atomic_fetch_add_explicit(&zdplane_info.dg_mac_errors, 1,
+ memory_order_relaxed);
+ dplane_ctx_free(&ctx);
+ }
+
+ return result;
+}
+
+/*
* Handler for 'show dplane'
*/
int dplane_show_helper(struct vty *vty, bool detailed)
@@ -2054,7 +2193,35 @@ int dplane_show_helper(struct vty *vty, bool detailed)
vty_out(vty, "Route update queue limit: %"PRIu64"\n", limit);
vty_out(vty, "Route update queue depth: %"PRIu64"\n", queued);
vty_out(vty, "Route update queue max: %"PRIu64"\n", queue_max);
- vty_out(vty, "Dplane update yields: %"PRIu64"\n", yields);
+ vty_out(vty, "Dplane update yields: %"PRIu64"\n", yields);
+
+ incoming = atomic_load_explicit(&zdplane_info.dg_lsps_in,
+ memory_order_relaxed);
+ errs = atomic_load_explicit(&zdplane_info.dg_lsp_errors,
+ memory_order_relaxed);
+ vty_out(vty, "LSP updates: %"PRIu64"\n", incoming);
+ vty_out(vty, "LSP update errors: %"PRIu64"\n", errs);
+
+ incoming = atomic_load_explicit(&zdplane_info.dg_pws_in,
+ memory_order_relaxed);
+ errs = atomic_load_explicit(&zdplane_info.dg_pw_errors,
+ memory_order_relaxed);
+ vty_out(vty, "PW updates: %"PRIu64"\n", incoming);
+ vty_out(vty, "PW update errors: %"PRIu64"\n", errs);
+
+ incoming = atomic_load_explicit(&zdplane_info.dg_intf_addrs_in,
+ memory_order_relaxed);
+ errs = atomic_load_explicit(&zdplane_info.dg_intf_addr_errors,
+ memory_order_relaxed);
+ vty_out(vty, "Intf addr updates: %"PRIu64"\n", incoming);
+ vty_out(vty, "Intf addr errors: %"PRIu64"\n", errs);
+
+ incoming = atomic_load_explicit(&zdplane_info.dg_macs_in,
+ memory_order_relaxed);
+ errs = atomic_load_explicit(&zdplane_info.dg_mac_errors,
+ memory_order_relaxed);
+ vty_out(vty, "EVPN MAC updates: %"PRIu64"\n", incoming);
+ vty_out(vty, "EVPN MAC errors: %"PRIu64"\n", errs);
return CMD_SUCCESS;
}
@@ -2374,7 +2541,7 @@ kernel_dplane_pw_update(struct zebra_dplane_ctx *ctx)
if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
zlog_debug("Dplane pw %s: op %s af %d loc: %u rem: %u",
- dplane_ctx_get_pw_ifname(ctx),
+ dplane_ctx_get_ifname(ctx),
dplane_op2str(ctx->zd_op),
dplane_ctx_get_pw_af(ctx),
dplane_ctx_get_pw_local_label(ctx),
@@ -2428,7 +2595,6 @@ kernel_dplane_address_update(struct zebra_dplane_ctx *ctx)
{
enum zebra_dplane_result res;
-
if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
char dest_str[PREFIX_STRLEN];
@@ -2450,6 +2616,34 @@ kernel_dplane_address_update(struct zebra_dplane_ctx *ctx)
}
/*
+ * Handler for kernel-facing MAC address updates
+ */
+static enum zebra_dplane_result
+kernel_dplane_mac_update(struct zebra_dplane_ctx *ctx)
+{
+ enum zebra_dplane_result res;
+
+ if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) {
+ char buf[ETHER_ADDR_STRLEN];
+
+ prefix_mac2str(dplane_ctx_mac_get_addr(ctx), buf,
+ sizeof(buf));
+
+ zlog_debug("Dplane %s, mac %s, ifindex %u",
+ dplane_op2str(dplane_ctx_get_op(ctx)),
+ buf, dplane_ctx_get_ifindex(ctx));
+ }
+
+ res = kernel_mac_update_ctx(ctx);
+
+ if (res != ZEBRA_DPLANE_REQUEST_SUCCESS)
+ atomic_fetch_add_explicit(&zdplane_info.dg_mac_errors,
+ 1, memory_order_relaxed);
+
+ return res;
+}
+
+/*
* Kernel provider callback
*/
static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)
@@ -2503,6 +2697,11 @@ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov)
res = kernel_dplane_address_update(ctx);
break;
+ case DPLANE_OP_MAC_INSTALL:
+ case DPLANE_OP_MAC_DELETE:
+ res = kernel_dplane_mac_update(ctx);
+ break;
+
/* Ignore 'notifications' - no-op */
case DPLANE_OP_SYS_ROUTE_ADD:
case DPLANE_OP_SYS_ROUTE_DELETE:
@@ -2682,7 +2881,7 @@ static bool dplane_work_pending(void)
*/
DPLANE_LOCK();
{
- ctx = TAILQ_FIRST(&zdplane_info.dg_route_ctx_q);
+ ctx = TAILQ_FIRST(&zdplane_info.dg_update_ctx_q);
prov = TAILQ_FIRST(&zdplane_info.dg_providers_q);
}
DPLANE_UNLOCK();
@@ -2810,9 +3009,9 @@ static int dplane_thread_loop(struct thread *event)
/* Move new work from incoming list to temp list */
for (counter = 0; counter < limit; counter++) {
- ctx = TAILQ_FIRST(&zdplane_info.dg_route_ctx_q);
+ ctx = TAILQ_FIRST(&zdplane_info.dg_update_ctx_q);
if (ctx) {
- TAILQ_REMOVE(&zdplane_info.dg_route_ctx_q, ctx,
+ TAILQ_REMOVE(&zdplane_info.dg_update_ctx_q, ctx,
zd_q_entries);
ctx->zd_provider = prov->dp_id;
@@ -2997,7 +3196,7 @@ static void zebra_dplane_init_internal(void)
pthread_mutex_init(&zdplane_info.dg_mutex, NULL);
- TAILQ_INIT(&zdplane_info.dg_route_ctx_q);
+ TAILQ_INIT(&zdplane_info.dg_update_ctx_q);
TAILQ_INIT(&zdplane_info.dg_providers_q);
zdplane_info.dg_updates_per_cycle = DPLANE_DEFAULT_NEW_WORK;
@@ -3023,7 +3222,7 @@ void zebra_dplane_start(void)
/* Start dataplane pthread */
zdplane_info.dg_pthread = frr_pthread_new(&pattr, "Zebra dplane thread",
- "Zebra dplane");
+ "zebra_dplane");
zdplane_info.dg_master = zdplane_info.dg_pthread->master;
diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h
index 6238026bcf..912fda45d3 100644
--- a/zebra/zebra_dplane.h
+++ b/zebra/zebra_dplane.h
@@ -25,6 +25,7 @@
#include "lib/nexthop.h"
#include "lib/nexthop_group.h"
#include "lib/queue.h"
+#include "lib/vlan.h"
#include "zebra/zebra_ns.h"
#include "zebra/rib.h"
#include "zebra/zserv.h"
@@ -124,6 +125,10 @@ enum dplane_op_e {
/* Interface address update */
DPLANE_OP_ADDR_INSTALL,
DPLANE_OP_ADDR_UNINSTALL,
+
+ /* MAC address update */
+ DPLANE_OP_MAC_INSTALL,
+ DPLANE_OP_MAC_DELETE,
};
/* Enable system route notifications */
@@ -180,6 +185,8 @@ const char *dplane_op2str(enum dplane_op_e op);
const struct prefix *dplane_ctx_get_dest(const struct zebra_dplane_ctx *ctx);
void dplane_ctx_set_dest(struct zebra_dplane_ctx *ctx,
const struct prefix *dest);
+const char *dplane_ctx_get_ifname(const struct zebra_dplane_ctx *ctx);
+ifindex_t dplane_ctx_get_ifindex(const struct zebra_dplane_ctx *ctx);
/* Retrieve last/current provider id */
uint32_t dplane_ctx_get_provider(const struct zebra_dplane_ctx *ctx);
@@ -262,7 +269,6 @@ const zebra_nhlfe_t *dplane_ctx_set_best_nhlfe(struct zebra_dplane_ctx *ctx,
uint32_t dplane_ctx_get_lsp_num_ecmp(const struct zebra_dplane_ctx *ctx);
/* Accessors for pseudowire information */
-const char *dplane_ctx_get_pw_ifname(const struct zebra_dplane_ctx *ctx);
mpls_label_t dplane_ctx_get_pw_local_label(const struct zebra_dplane_ctx *ctx);
mpls_label_t dplane_ctx_get_pw_remote_label(const struct zebra_dplane_ctx *ctx);
int dplane_ctx_get_pw_type(const struct zebra_dplane_ctx *ctx);
@@ -277,8 +283,6 @@ const struct nexthop_group *dplane_ctx_get_pw_nhg(
const struct zebra_dplane_ctx *ctx);
/* Accessors for interface information */
-const char *dplane_ctx_get_ifname(const struct zebra_dplane_ctx *ctx);
-ifindex_t dplane_ctx_get_ifindex(const struct zebra_dplane_ctx *ctx);
uint32_t dplane_ctx_get_intf_metric(const struct zebra_dplane_ctx *ctx);
/* Is interface addr p2p? */
bool dplane_ctx_intf_is_connected(const struct zebra_dplane_ctx *ctx);
@@ -292,6 +296,14 @@ const struct prefix *dplane_ctx_get_intf_dest(
bool dplane_ctx_intf_has_label(const struct zebra_dplane_ctx *ctx);
const char *dplane_ctx_get_intf_label(const struct zebra_dplane_ctx *ctx);
+/* Accessors for MAC information */
+vlanid_t dplane_ctx_mac_get_vlan(const struct zebra_dplane_ctx *ctx);
+bool dplane_ctx_mac_is_sticky(const struct zebra_dplane_ctx *ctx);
+const struct ethaddr *dplane_ctx_mac_get_addr(
+ const struct zebra_dplane_ctx *ctx);
+const struct in_addr *dplane_ctx_mac_get_vtep_ip(
+ 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);
@@ -353,6 +365,19 @@ enum zebra_dplane_result dplane_intf_addr_set(const struct interface *ifp,
enum zebra_dplane_result dplane_intf_addr_unset(const struct interface *ifp,
const struct connected *ifc);
+/*
+ * Enqueue evpn mac operations for the dataplane.
+ */
+enum zebra_dplane_result dplane_mac_add(const struct interface *ifp,
+ vlanid_t vid,
+ const struct ethaddr *mac,
+ struct in_addr vtep_ip,
+ bool sticky);
+
+enum zebra_dplane_result dplane_mac_del(const struct interface *ifp,
+ vlanid_t vid,
+ const struct ethaddr *mac,
+ struct in_addr vtep_ip);
/* Retrieve the limit on the number of pending, unprocessed updates. */
uint32_t dplane_get_in_queue_limit(void);
diff --git a/zebra/zebra_errors.c b/zebra/zebra_errors.c
index 5f0a9ec011..a7e5147af3 100644
--- a/zebra/zebra_errors.c
+++ b/zebra/zebra_errors.c
@@ -659,7 +659,7 @@ static struct log_ref ferr_zebra_err[] = {
{
.code = EC_ZEBRA_RTM_VERSION_MISMATCH,
.title =
- "Zebra received kernel message with uknown version",
+ "Zebra received kernel message with unknown version",
.description =
"Zebra received a message from the kernel with a message version that does not match Zebra's internal version. Depending on version compatibility, this may cause issues sending and receiving messages to the kernel.",
.suggestion =
diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c
index 822def318a..f347d3955c 100644
--- a/zebra/zebra_fpm_netlink.c
+++ b/zebra/zebra_fpm_netlink.c
@@ -238,7 +238,7 @@ static int netlink_route_info_add_nh(netlink_route_info_t *ri,
if (re && CHECK_FLAG(re->flags, ZEBRA_FLAG_EVPN_ROUTE)) {
nhi.encap_info.encap_type = FPM_NH_ENCAP_VXLAN;
- zl3vni = zl3vni_from_vrf(ri->rtm_table);
+ zl3vni = zl3vni_from_vrf(nexthop->vrf_id);
if (zl3vni && is_l3vni_oper_up(zl3vni)) {
/* Add VNI to VxLAN encap info */
diff --git a/zebra/zebra_l2.c b/zebra/zebra_l2.c
index ca37dd748e..e549d80a5c 100644
--- a/zebra/zebra_l2.c
+++ b/zebra/zebra_l2.c
@@ -99,15 +99,18 @@ void zebra_l2_unmap_slave_from_bridge(struct zebra_l2info_brslave *br_slave)
br_slave->br_if = NULL;
}
-void zebra_l2_map_slave_to_bond(struct zebra_l2info_bondslave *bond_slave)
+void zebra_l2_map_slave_to_bond(struct zebra_l2info_bondslave *bond_slave,
+ vrf_id_t vrf_id)
{
struct interface *bond_if;
/* TODO: Handle change of master */
- bond_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT),
- bond_slave->bond_ifindex);
+ bond_if = if_lookup_by_index_all_vrf(bond_slave->bond_ifindex);
if (bond_if)
bond_slave->bond_if = bond_if;
+ else
+ bond_slave->bond_if = if_create_ifindex(bond_slave->bond_ifindex,
+ vrf_id);
}
void zebra_l2_unmap_slave_from_bond(struct zebra_l2info_bondslave *bond_slave)
@@ -282,7 +285,7 @@ void zebra_l2if_update_bond_slave(struct interface *ifp, ifindex_t bond_ifindex)
/* Set up or remove link with master */
if (bond_ifindex != IFINDEX_INTERNAL)
- zebra_l2_map_slave_to_bond(&zif->bondslave_info);
+ zebra_l2_map_slave_to_bond(&zif->bondslave_info, ifp->vrf_id);
else if (old_bond_ifindex != IFINDEX_INTERNAL)
zebra_l2_unmap_slave_from_bond(&zif->bondslave_info);
}
diff --git a/zebra/zebra_l2.h b/zebra/zebra_l2.h
index 33aa2e3746..d9f0eec3f8 100644
--- a/zebra/zebra_l2.h
+++ b/zebra/zebra_l2.h
@@ -81,7 +81,7 @@ extern void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave);
extern void
zebra_l2_unmap_slave_from_bridge(struct zebra_l2info_brslave *br_slave);
extern void
-zebra_l2_map_slave_to_bond(struct zebra_l2info_bondslave *bond_slave);
+zebra_l2_map_slave_to_bond(struct zebra_l2info_bondslave *bond_slave, vrf_id_t);
extern void
zebra_l2_unmap_slave_from_bond(struct zebra_l2info_bondslave *bond_slave);
extern void zebra_l2_bridge_add_update(struct interface *ifp,
diff --git a/zebra/zebra_mpls_openbsd.c b/zebra/zebra_mpls_openbsd.c
index e7fdaf127d..9f3ea70c77 100644
--- a/zebra/zebra_mpls_openbsd.c
+++ b/zebra/zebra_mpls_openbsd.c
@@ -369,7 +369,7 @@ static enum zebra_dplane_result kmpw_install(struct zebra_dplane_ctx *ctx)
/* ioctl */
memset(&ifr, 0, sizeof(ifr));
- strlcpy(ifr.ifr_name, dplane_ctx_get_pw_ifname(ctx),
+ strlcpy(ifr.ifr_name, dplane_ctx_get_ifname(ctx),
sizeof(ifr.ifr_name));
ifr.ifr_data = (caddr_t)&imr;
if (ioctl(kr_state.ioctl_fd, SIOCSETMPWCFG, &ifr) == -1) {
@@ -388,7 +388,7 @@ static enum zebra_dplane_result kmpw_uninstall(struct zebra_dplane_ctx *ctx)
memset(&ifr, 0, sizeof(ifr));
memset(&imr, 0, sizeof(imr));
- strlcpy(ifr.ifr_name, dplane_ctx_get_pw_ifname(ctx),
+ strlcpy(ifr.ifr_name, dplane_ctx_get_ifname(ctx),
sizeof(ifr.ifr_name));
ifr.ifr_data = (caddr_t)&imr;
if (ioctl(kr_state.ioctl_fd, SIOCSETMPWCFG, &ifr) == -1) {
diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c
index 977e3bba79..895a545bfe 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);
@@ -87,18 +92,31 @@ static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop,
break;
case NEXTHOP_TYPE_BLACKHOLE:
resolved_hop->type = NEXTHOP_TYPE_BLACKHOLE;
- resolved_hop->bh_type = nexthop->bh_type;
+ resolved_hop->bh_type = newhop->bh_type;
break;
}
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);
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index 3608b887ee..335cc8294c 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -3178,7 +3178,7 @@ static int handle_pw_result(struct zebra_dplane_ctx *ctx)
if (dplane_ctx_get_status(ctx) != ZEBRA_DPLANE_REQUEST_SUCCESS) {
vrf = zebra_vrf_lookup_by_id(dplane_ctx_get_vrf(ctx));
- pw = zebra_pw_find(vrf, dplane_ctx_get_pw_ifname(ctx));
+ pw = zebra_pw_find(vrf, dplane_ctx_get_ifname(ctx));
if (pw)
zebra_pw_install_failure(pw);
}
@@ -3270,6 +3270,11 @@ static int rib_process_dplane_results(struct thread *thread)
dplane_ctx_fini(&ctx);
break;
+ case DPLANE_OP_MAC_INSTALL:
+ case DPLANE_OP_MAC_DELETE:
+ zebra_vxlan_handle_result(ctx);
+ break;
+
default:
/* Don't expect this: just return the struct? */
dplane_ctx_fini(&ctx);
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
index 99431e7e6d..e6487b8334 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);
@@ -1028,7 +1032,7 @@ DEFPY (show_route_all_table_vrf,
DEFPY (show_ip_nht,
show_ip_nht_cmd,
- "show <ip$ipv4|ipv6$ipv6> <nht|import-check>$type [<A.B.C.D|X:X::X:X>$addr|vrf NAME$vrf_name <A.B.C.D|X:X::X:X>$addr|vrf all$vrf_all]",
+ "show <ip$ipv4|ipv6$ipv6> <nht|import-check>$type [<A.B.C.D|X:X::X:X>$addr|vrf NAME$vrf_name [<A.B.C.D|X:X::X:X>$addr]|vrf all$vrf_all]",
SHOW_STR
IP_STR
IP6_STR
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index 2e8c81bddd..bb31247b6a 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -2787,27 +2787,40 @@ static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni,
/* Set "local" forwarding info. */
SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
- SET_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW);
ZEBRA_NEIGH_SET_ACTIVE(n);
- /* Set Router flag (R-bit) */
- if (ip->ipa_type == IPADDR_V6)
- SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
memcpy(&n->emac, macaddr, ETH_ALEN);
n->ifindex = ifp->ifindex;
/* Only advertise in BGP if the knob is enabled */
- if (!advertise_gw_macip_enabled(zvni))
- return 0;
+ if (advertise_gw_macip_enabled(zvni)) {
- if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug(
+ SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW);
+ SET_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW);
+ /* Set Router flag (R-bit) */
+ if (ip->ipa_type == IPADDR_V6)
+ SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG);
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
"SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s add to BGP with flags 0x%x",
ifp->name, ifp->ifindex, zvni->vni,
prefix_mac2str(macaddr, buf, sizeof(buf)),
ipaddr2str(ip, buf2, sizeof(buf2)), n->flags);
- zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr,
- n->flags, n->loc_seq);
+ zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr,
+ n->flags, n->loc_seq);
+ } else if (advertise_svi_macip_enabled(zvni)) {
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "SVI %s(%u) L2-VNI %u, sending SVI MAC %s IP %s add to BGP with flags 0x%x",
+ ifp->name, ifp->ifindex, zvni->vni,
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ ipaddr2str(ip, buf2, sizeof(buf2)), n->flags);
+
+ zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr,
+ n->flags, n->loc_seq);
+ }
return 0;
}
@@ -3720,13 +3733,14 @@ static struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if)
}
/*
- * Install remote MAC into the kernel.
+ * Install remote MAC into the forwarding plane.
*/
static int zvni_mac_install(zebra_vni_t *zvni, zebra_mac_t *mac)
{
struct zebra_if *zif;
struct zebra_l2info_vxlan *vxl;
bool sticky;
+ enum zebra_dplane_result res;
if (!(mac->flags & ZEBRA_MAC_REMOTE))
return 0;
@@ -3739,12 +3753,16 @@ static int zvni_mac_install(zebra_vni_t *zvni, zebra_mac_t *mac)
sticky = !!CHECK_FLAG(mac->flags,
(ZEBRA_MAC_STICKY | ZEBRA_MAC_REMOTE_DEF_GW));
- return kernel_add_mac(zvni->vxlan_if, vxl->access_vlan, &mac->macaddr,
- mac->fwd_info.r_vtep_ip, sticky);
+ res = dplane_mac_add(zvni->vxlan_if, vxl->access_vlan, &mac->macaddr,
+ mac->fwd_info.r_vtep_ip, sticky);
+ if (res != ZEBRA_DPLANE_REQUEST_FAILURE)
+ return 0;
+ else
+ return -1;
}
/*
- * Uninstall remote MAC from the kernel.
+ * Uninstall remote MAC from the forwarding plane.
*/
static int zvni_mac_uninstall(zebra_vni_t *zvni, zebra_mac_t *mac)
{
@@ -3752,6 +3770,7 @@ static int zvni_mac_uninstall(zebra_vni_t *zvni, zebra_mac_t *mac)
struct zebra_l2info_vxlan *vxl;
struct in_addr vtep_ip;
struct interface *ifp;
+ enum zebra_dplane_result res;
if (!(mac->flags & ZEBRA_MAC_REMOTE))
return 0;
@@ -3770,7 +3789,11 @@ static int zvni_mac_uninstall(zebra_vni_t *zvni, zebra_mac_t *mac)
ifp = zvni->vxlan_if;
vtep_ip = mac->fwd_info.r_vtep_ip;
- return kernel_del_mac(ifp, vxl->access_vlan, &mac->macaddr, vtep_ip);
+ res = dplane_mac_del(ifp, vxl->access_vlan, &mac->macaddr, vtep_ip);
+ if (res != ZEBRA_DPLANE_REQUEST_FAILURE)
+ return 0;
+ else
+ return -1;
}
/*
@@ -4457,12 +4480,13 @@ static int zl3vni_rmac_del(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac)
}
/*
- * Install remote RMAC into the kernel.
+ * Install remote RMAC into the forwarding plane.
*/
static int zl3vni_rmac_install(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac)
{
struct zebra_if *zif = NULL;
struct zebra_l2info_vxlan *vxl = NULL;
+ enum zebra_dplane_result res;
if (!(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE))
|| !(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE_RMAC)))
@@ -4474,18 +4498,23 @@ static int zl3vni_rmac_install(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac)
vxl = &zif->l2info.vxl;
- return kernel_add_mac(zl3vni->vxlan_if, vxl->access_vlan,
- &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip, 0);
+ res = dplane_mac_add(zl3vni->vxlan_if, vxl->access_vlan,
+ &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip, 0);
+ if (res != ZEBRA_DPLANE_REQUEST_FAILURE)
+ return 0;
+ else
+ return -1;
}
/*
- * Uninstall remote RMAC from the kernel.
+ * Uninstall remote RMAC from the forwarding plane.
*/
static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac)
{
char buf[ETHER_ADDR_STRLEN];
struct zebra_if *zif = NULL;
struct zebra_l2info_vxlan *vxl = NULL;
+ enum zebra_dplane_result res;
if (!(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE))
|| !(CHECK_FLAG(zrmac->flags, ZEBRA_MAC_REMOTE_RMAC)))
@@ -4505,8 +4534,12 @@ static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac)
vxl = &zif->l2info.vxl;
- return kernel_del_mac(zl3vni->vxlan_if, vxl->access_vlan,
- &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip);
+ res = dplane_mac_del(zl3vni->vxlan_if, vxl->access_vlan,
+ &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip);
+ if (res != ZEBRA_DPLANE_REQUEST_FAILURE)
+ return 0;
+ else
+ return -1;
}
/* handle rmac add */
@@ -6759,7 +6792,7 @@ int zebra_vxlan_clear_dup_detect_vni_ip(struct vty *vty,
if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) {
vty_out(vty,
- "%% Requsted host IP %s is not duplicate detected\n",
+ "%% Requested host IP %s is not duplicate detected\n",
buf);
return CMD_WARNING;
}
@@ -9015,7 +9048,7 @@ void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS)
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug("EVPN SVI-MACIP Adv %s, currently %s",
advertise ? "enabled" : "disabled",
- advertise_gw_macip_enabled(NULL)
+ advertise_svi_macip_enabled(NULL)
? "enabled"
: "disabled");
@@ -9900,6 +9933,15 @@ static int zebra_evpn_cfg_clean_up(struct zserv *client)
return 0;
}
+/*
+ * Handle results for vxlan dataplane operations.
+ */
+extern void zebra_vxlan_handle_result(struct zebra_dplane_ctx *ctx)
+{
+ /* TODO -- anything other than freeing the context? */
+ dplane_ctx_fini(&ctx);
+}
+
/* Cleanup BGP EVPN configuration upon client disconnect */
extern void zebra_evpn_init(void)
{
diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h
index c71953d6bb..bb80ae1c9a 100644
--- a/zebra/zebra_vxlan.h
+++ b/zebra/zebra_vxlan.h
@@ -35,6 +35,7 @@
#include "lib/json.h"
#include "zebra/zebra_vrf.h"
#include "zebra/zserv.h"
+#include "zebra/zebra_dplane.h"
#ifdef __cplusplus
extern "C" {
@@ -213,6 +214,8 @@ extern int zebra_vxlan_clear_dup_detect_vni_all(struct vty *vty,
extern int zebra_vxlan_clear_dup_detect_vni(struct vty *vty,
struct zebra_vrf *zvrf,
vni_t vni);
+extern void zebra_vxlan_handle_result(struct zebra_dplane_ctx *ctx);
+
extern void zebra_evpn_init(void);
#ifdef __cplusplus