summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_community.c36
-rw-r--r--bgpd/bgp_damp.c570
-rw-r--r--bgpd/bgp_damp.h49
-rw-r--r--bgpd/bgp_debug.c18
-rw-r--r--bgpd/bgp_fsm.c25
-rw-r--r--bgpd/bgp_label.c194
-rw-r--r--bgpd/bgp_labelpool.c388
-rw-r--r--bgpd/bgp_labelpool.h2
-rw-r--r--bgpd/bgp_memory.c1
-rw-r--r--bgpd/bgp_memory.h1
-rw-r--r--bgpd/bgp_nb_config.c16
-rw-r--r--bgpd/bgp_open.c16
-rw-r--r--bgpd/bgp_open.h2
-rw-r--r--bgpd/bgp_packet.c228
-rw-r--r--bgpd/bgp_packet.h5
-rw-r--r--bgpd/bgp_route.c137
-rw-r--r--bgpd/bgp_route.h1
-rw-r--r--bgpd/bgp_routemap.c5
-rw-r--r--bgpd/bgp_table.h1
-rw-r--r--bgpd/bgp_vty.c167
-rw-r--r--bgpd/bgp_zebra.c20
-rw-r--r--bgpd/bgpd.c64
-rw-r--r--bgpd/bgpd.h30
-rw-r--r--bgpd/subdir.am1
-rw-r--r--doc/user/bgp.rst82
-rw-r--r--doc/user/overview.rst2
-rw-r--r--doc/user/pim.rst7
-rw-r--r--doc/user/rpki.rst4
-rw-r--r--isisd/isis_nb_config.c9
-rw-r--r--isisd/isis_spf.c77
-rw-r--r--lib/sockunion.c17
-rw-r--r--lib/sockunion.h1
-rw-r--r--nhrpd/README.kernel1
-rw-r--r--nhrpd/nhrp_cache.c42
-rw-r--r--nhrpd/nhrp_interface.c15
-rw-r--r--nhrpd/nhrp_nhs.c26
-rw-r--r--nhrpd/nhrp_peer.c29
-rw-r--r--nhrpd/nhrp_route.c7
-rw-r--r--nhrpd/nhrp_shortcut.c19
-rw-r--r--nhrpd/nhrpd.h5
-rw-r--r--ospfd/ospf_vty.c51
-rw-r--r--pathd/path_pcep_cli.c10
-rw-r--r--pathd/path_pcep_controller.h1
-rw-r--r--pathd/path_pcep_pcc.c1
-rw-r--r--pimd/pim_bsm.c6
-rw-r--r--pimd/pim_bsm.h3
-rw-r--r--pimd/pim_cmd.c147
-rw-r--r--pimd/pim_ifchannel.c28
-rw-r--r--pimd/pim_igmp.c4
-rw-r--r--pimd/pim_igmpv2.c31
-rw-r--r--pimd/pim_igmpv2.h2
-rw-r--r--pimd/pim_mroute.c2
-rw-r--r--pimd/pim_rp.c2
-rw-r--r--pimd/pim_rp.h1
-rw-r--r--pimd/pim_upstream.c7
-rw-r--r--tests/topotests/bgp_features/exabgp.env53
-rw-r--r--tests/topotests/bgp_features/peer1/exa_readpipe.py19
-rw-r--r--tests/topotests/bgp_features/peer1/exabgp.cfg12
-rw-r--r--tests/topotests/bgp_features/peer2/exa_readpipe.py19
-rw-r--r--tests/topotests/bgp_features/peer2/exabgp.cfg12
-rw-r--r--tests/topotests/bgp_features/peer3/exa_readpipe.py19
-rw-r--r--tests/topotests/bgp_features/peer3/exabgp.cfg12
-rw-r--r--tests/topotests/bgp_features/peer4/exa_readpipe.py19
-rw-r--r--tests/topotests/bgp_features/peer4/exabgp.cfg12
-rw-r--r--tests/topotests/bgp_features/r1/bgp_damp_announced.json21
-rw-r--r--tests/topotests/bgp_features/r1/bgp_damp_setup.json10
-rw-r--r--tests/topotests/bgp_features/r2/bgp_damp_announced.json21
-rw-r--r--tests/topotests/bgp_features/r2/bgp_damp_withdrawn.json18
-rw-r--r--tests/topotests/bgp_features/test_bgp_features.py671
-rw-r--r--tests/topotests/bgp_lu_topo1/R1/bgpd.conf21
-rw-r--r--tests/topotests/bgp_lu_topo1/R1/labelpool.summ.json8
-rw-r--r--tests/topotests/bgp_lu_topo1/R1/zebra.conf6
-rw-r--r--tests/topotests/bgp_lu_topo1/R2/bgpd.conf23
-rw-r--r--tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json8
-rw-r--r--tests/topotests/bgp_lu_topo1/R2/zebra.conf11
-rw-r--r--tests/topotests/bgp_lu_topo1/R3/bgpd.conf523
-rw-r--r--tests/topotests/bgp_lu_topo1/R3/zebra.conf9
-rw-r--r--tests/topotests/bgp_lu_topo1/test_bgp_lu.py178
-rw-r--r--tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py3
-rwxr-xr-xtests/topotests/isis-sr-te-topo1/test_isis_sr_te_topo1.py6
-rw-r--r--tests/topotests/ospf_basic_functionality/ospf_p2mp.json198
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py416
-rw-r--r--tools/etc/frr/support_bundle_commands.conf27
-rwxr-xr-xtools/frr-reload.py53
-rw-r--r--vtysh/vtysh.c6
-rw-r--r--zebra/dplane_fpm_nl.c4
-rw-r--r--zebra/label_manager.c5
-rw-r--r--zebra/sample_plugin.c1
-rw-r--r--zebra/subdir.am11
-rw-r--r--zebra/zebra_dplane.c11
-rw-r--r--zebra/zebra_routemap.c11
-rw-r--r--zebra/zebra_routemap.h2
-rw-r--r--zebra/zebra_vxlan.c77
93 files changed, 4651 insertions, 501 deletions
diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c
index b6cc2b839f..f722a8dbc7 100644
--- a/bgpd/bgp_community.c
+++ b/bgpd/bgp_community.c
@@ -24,6 +24,7 @@
#include "hash.h"
#include "memory.h"
#include "jhash.h"
+#include "frrstr.h"
#include "bgpd/bgp_memory.h"
#include "bgpd/bgp_community.h"
@@ -648,6 +649,31 @@ enum community_token {
community_token_unknown
};
+/* Helper to check if a given community is valid */
+static bool community_valid(const char *community)
+{
+ int octets = 0;
+ char **splits;
+ int num;
+ int invalid = 0;
+
+ frrstr_split(community, ":", &splits, &num);
+
+ for (int i = 0; i < num; i++) {
+ if (strtoul(splits[i], NULL, 10) > UINT16_MAX)
+ invalid++;
+
+ if (strlen(splits[i]) == 0)
+ invalid++;
+
+ octets++;
+ XFREE(MTYPE_TMP, splits[i]);
+ }
+ XFREE(MTYPE_TMP, splits);
+
+ return (octets < 2 || invalid) ? false : true;
+}
+
/* Get next community token from string. */
static const char *
community_gettoken(const char *buf, enum community_token *token, uint32_t *val)
@@ -780,6 +806,11 @@ community_gettoken(const char *buf, enum community_token *token, uint32_t *val)
uint32_t community_low = 0;
uint32_t community_high = 0;
+ if (!community_valid(p)) {
+ *token = community_token_unknown;
+ return NULL;
+ }
+
while (isdigit((unsigned char)*p) || *p == ':') {
if (*p == ':') {
if (separator) {
@@ -810,11 +841,6 @@ community_gettoken(const char *buf, enum community_token *token, uint32_t *val)
return NULL;
}
- if (community_low > UINT16_MAX) {
- *token = community_token_unknown;
- return NULL;
- }
-
*val = community_high + community_low;
*token = community_token_val;
return p;
diff --git a/bgpd/bgp_damp.c b/bgpd/bgp_damp.c
index f46d416c3c..b740979b82 100644
--- a/bgpd/bgp_damp.c
+++ b/bgpd/bgp_damp.c
@@ -37,19 +37,115 @@
#include "bgpd/bgp_advertise.h"
#include "bgpd/bgp_vty.h"
-/* Global variable to access damping configuration */
-static struct bgp_damp_config damp[AFI_MAX][SAFI_MAX];
+static void bgp_reuselist_add(struct reuselist *list,
+ struct bgp_damp_info *info)
+{
+ struct reuselist_node *new_node;
+
+ assert(info);
+ new_node = XCALLOC(MTYPE_BGP_DAMP_REUSELIST, sizeof(*new_node));
+ new_node->info = info;
+ SLIST_INSERT_HEAD(list, new_node, entry);
+}
+
+static void bgp_reuselist_del(struct reuselist *list,
+ struct reuselist_node **node)
+{
+ if ((*node) == NULL)
+ return;
+ assert(list && node && *node);
+ SLIST_REMOVE(list, (*node), reuselist_node, entry);
+ XFREE(MTYPE_BGP_DAMP_REUSELIST, (*node));
+ *node = NULL;
+}
-/* Utility macro to add and delete BGP dampening information to no
- used list. */
-#define BGP_DAMP_LIST_ADD(N, A) BGP_PATH_INFO_ADD(N, A, no_reuse_list)
-#define BGP_DAMP_LIST_DEL(N, A) BGP_PATH_INFO_DEL(N, A, no_reuse_list)
+static void bgp_reuselist_switch(struct reuselist *source,
+ struct reuselist_node *node,
+ struct reuselist *target)
+{
+ assert(source && target && node);
+ SLIST_REMOVE(source, node, reuselist_node, entry);
+ SLIST_INSERT_HEAD(target, node, entry);
+}
+
+static void bgp_reuselist_free(struct reuselist *list)
+{
+ struct reuselist_node *rn;
+
+ assert(list);
+ while ((rn = SLIST_FIRST(list)) != NULL)
+ bgp_reuselist_del(list, &rn);
+}
+
+static struct reuselist_node *bgp_reuselist_find(struct reuselist *list,
+ struct bgp_damp_info *info)
+{
+ struct reuselist_node *rn;
+
+ assert(list && info);
+ SLIST_FOREACH (rn, list, entry) {
+ if (rn->info == info)
+ return rn;
+ }
+ return NULL;
+}
+
+static void bgp_damp_info_unclaim(struct bgp_damp_info *bdi)
+{
+ struct reuselist_node *node;
+
+ assert(bdi && bdi->config);
+ if (bdi->index == BGP_DAMP_NO_REUSE_LIST_INDEX) {
+ node = bgp_reuselist_find(&bdi->config->no_reuse_list, bdi);
+ if (node)
+ bgp_reuselist_del(&bdi->config->no_reuse_list, &node);
+ } else {
+ node = bgp_reuselist_find(&bdi->config->reuse_list[bdi->index],
+ bdi);
+ if (node)
+ bgp_reuselist_del(&bdi->config->reuse_list[bdi->index],
+ &node);
+ }
+ bdi->config = NULL;
+}
+
+static void bgp_damp_info_claim(struct bgp_damp_info *bdi,
+ struct bgp_damp_config *bdc)
+{
+ assert(bdc && bdi);
+ if (bdi->config == NULL) {
+ bdi->config = bdc;
+ return;
+ }
+ bgp_damp_info_unclaim(bdi);
+ bdi->config = bdc;
+ bdi->afi = bdc->afi;
+ bdi->safi = bdc->safi;
+}
+
+struct bgp_damp_config *get_active_bdc_from_pi(struct bgp_path_info *pi,
+ afi_t afi, safi_t safi)
+{
+ if (!pi)
+ return NULL;
+ if (CHECK_FLAG(pi->peer->af_flags[afi][safi],
+ PEER_FLAG_CONFIG_DAMPENING))
+ return &pi->peer->damp[afi][safi];
+ if (peer_group_active(pi->peer))
+ if (CHECK_FLAG(pi->peer->group->conf->af_flags[afi][safi],
+ PEER_FLAG_CONFIG_DAMPENING))
+ return &pi->peer->group->conf->damp[afi][safi];
+ if (CHECK_FLAG(pi->peer->bgp->af_flags[afi][safi],
+ BGP_CONFIG_DAMPENING))
+ return &pi->peer->bgp->damp[afi][safi];
+ return NULL;
+}
/* Calculate reuse list index by penalty value. */
static int bgp_reuse_index(int penalty, struct bgp_damp_config *bdc)
{
unsigned int i;
- int index;
+ unsigned int index;
/*
* reuse_limit can't be zero, this is for Coverity
@@ -72,27 +168,45 @@ static int bgp_reuse_index(int penalty, struct bgp_damp_config *bdc)
static void bgp_reuse_list_add(struct bgp_damp_info *bdi,
struct bgp_damp_config *bdc)
{
- int index;
-
- index = bdi->index = bgp_reuse_index(bdi->penalty, bdc);
-
- bdi->prev = NULL;
- bdi->next = bdc->reuse_list[index];
- if (bdc->reuse_list[index])
- bdc->reuse_list[index]->prev = bdi;
- bdc->reuse_list[index] = bdi;
+ bgp_damp_info_claim(bdi, bdc);
+ bdi->index = bgp_reuse_index(bdi->penalty, bdc);
+ bgp_reuselist_add(&bdc->reuse_list[bdi->index], bdi);
}
/* Delete BGP dampening information from reuse list. */
static void bgp_reuse_list_delete(struct bgp_damp_info *bdi,
struct bgp_damp_config *bdc)
{
- if (bdi->next)
- bdi->next->prev = bdi->prev;
- if (bdi->prev)
- bdi->prev->next = bdi->next;
- else
- bdc->reuse_list[bdi->index] = bdi->next;
+ struct reuselist *list;
+ struct reuselist_node *rn;
+
+ list = &bdc->reuse_list[bdi->index];
+ rn = bgp_reuselist_find(list, bdi);
+ bgp_damp_info_unclaim(bdi);
+ bgp_reuselist_del(list, &rn);
+}
+
+static void bgp_no_reuse_list_add(struct bgp_damp_info *bdi,
+ struct bgp_damp_config *bdc)
+{
+ bgp_damp_info_claim(bdi, bdc);
+ bdi->index = BGP_DAMP_NO_REUSE_LIST_INDEX;
+ bgp_reuselist_add(&bdc->no_reuse_list, bdi);
+}
+
+static void bgp_no_reuse_list_delete(struct bgp_damp_info *bdi,
+ struct bgp_damp_config *bdc)
+{
+ struct reuselist_node *rn;
+
+ assert(bdc && bdi);
+ if (bdi->config == NULL) {
+ bgp_damp_info_unclaim(bdi);
+ return;
+ }
+ bdi->config = NULL;
+ rn = bgp_reuselist_find(&bdc->no_reuse_list, bdi);
+ bgp_reuselist_del(&bdc->no_reuse_list, &rn);
}
/* Return decayed penalty value. */
@@ -115,32 +229,34 @@ int bgp_damp_decay(time_t tdiff, int penalty, struct bgp_damp_config *bdc)
is evaluated. RFC2439 Section 4.8.7. */
static int bgp_reuse_timer(struct thread *t)
{
+ struct bgp_damp_config *bdc = THREAD_ARG(t);
struct bgp_damp_info *bdi;
- struct bgp_damp_info *next;
+ struct reuselist plist;
+ struct reuselist_node *node;
+ struct bgp *bgp;
time_t t_now, t_diff;
- struct bgp_damp_config *bdc = THREAD_ARG(t);
-
- bdc->t_reuse = NULL;
thread_add_timer(bm->master, bgp_reuse_timer, bdc, DELTA_REUSE,
&bdc->t_reuse);
t_now = bgp_clock();
- /* 1. save a pointer to the current zeroth queue head and zero the
- list head entry. */
- bdi = bdc->reuse_list[bdc->reuse_offset];
- bdc->reuse_list[bdc->reuse_offset] = NULL;
+ /* 1. save a pointer to the current queue head and zero the list head
+ * list head entry. */
+ assert(bdc->reuse_offset < bdc->reuse_list_size);
+ plist = bdc->reuse_list[bdc->reuse_offset];
+ node = SLIST_FIRST(&plist);
+ SLIST_INIT(&bdc->reuse_list[bdc->reuse_offset]);
/* 2. set offset = modulo reuse-list-size ( offset + 1 ), thereby
rotating the circular queue of list-heads. */
bdc->reuse_offset = (bdc->reuse_offset + 1) % bdc->reuse_list_size;
+ assert(bdc->reuse_offset < bdc->reuse_list_size);
/* 3. if ( the saved list head pointer is non-empty ) */
- for (; bdi; bdi = next) {
- struct bgp *bgp = bdi->path->peer->bgp;
-
- next = bdi->next;
+ while ((node = SLIST_FIRST(&plist)) != NULL) {
+ bdi = node->info;
+ bgp = bdi->path->peer->bgp;
/* Set t-diff = t-now - t-updated. */
t_diff = t_now - bdi->t_updated;
@@ -169,16 +285,27 @@ static int bgp_reuse_timer(struct thread *t)
bdi->safi);
}
- if (bdi->penalty <= bdc->reuse_limit / 2.0)
- bgp_damp_info_free(bdi, 1, bdc->afi, bdc->safi);
- else
- BGP_DAMP_LIST_ADD(bdc, bdi);
- } else
+ if (bdi->penalty <= bdc->reuse_limit / 2.0) {
+ bgp_damp_info_free(&bdi, bdc, 1, bdi->afi,
+ bdi->safi);
+ bgp_reuselist_del(&plist, &node);
+ } else {
+ node->info->index =
+ BGP_DAMP_NO_REUSE_LIST_INDEX;
+ bgp_reuselist_switch(&plist, node,
+ &bdc->no_reuse_list);
+ }
+ } else {
/* Re-insert into another list (See RFC2439 Section
* 4.8.6). */
- bgp_reuse_list_add(bdi, bdc);
+ bdi->index = bgp_reuse_index(bdi->penalty, bdc);
+ bgp_reuselist_switch(&plist, node,
+ &bdc->reuse_list[bdi->index]);
+ }
}
+ assert(SLIST_EMPTY(&plist));
+
return 0;
}
@@ -189,10 +316,13 @@ int bgp_damp_withdraw(struct bgp_path_info *path, struct bgp_dest *dest,
time_t t_now;
struct bgp_damp_info *bdi = NULL;
unsigned int last_penalty = 0;
- struct bgp_damp_config *bdc = &damp[afi][safi];
+ struct bgp_damp_config *bdc;
- t_now = bgp_clock();
+ bdc = get_active_bdc_from_pi(path, afi, safi);
+ if (!bdc)
+ return BGP_DAMP_USED;
+ t_now = bgp_clock();
/* Processing Unreachable Messages. */
if (path->extra)
bdi = path->extra->damp_info;
@@ -214,12 +344,13 @@ int bgp_damp_withdraw(struct bgp_path_info *path, struct bgp_dest *dest,
bdi->flap = 1;
bdi->start_time = t_now;
bdi->suppress_time = 0;
- bdi->index = -1;
+ bdi->index = BGP_DAMP_NO_REUSE_LIST_INDEX;
bdi->afi = afi;
bdi->safi = safi;
(bgp_path_info_extra_get(path))->damp_info = bdi;
- BGP_DAMP_LIST_ADD(bdc, bdi);
+ bgp_no_reuse_list_add(bdi, bdc);
} else {
+ bgp_damp_info_claim(bdi, bdc);
last_penalty = bdi->penalty;
/* 1. Set t-diff = t-now - t-updated. */
@@ -245,7 +376,7 @@ int bgp_damp_withdraw(struct bgp_path_info *path, struct bgp_dest *dest,
/* Remove the route from a reuse list if it is on one. */
if (CHECK_FLAG(bdi->path->flags, BGP_PATH_DAMPED)) {
/* If decay rate isn't equal to 0, reinsert brn. */
- if (bdi->penalty != last_penalty && bdi->index >= 0) {
+ if (bdi->penalty != last_penalty) {
bgp_reuse_list_delete(bdi, bdc);
bgp_reuse_list_add(bdi, bdc);
}
@@ -257,10 +388,9 @@ int bgp_damp_withdraw(struct bgp_path_info *path, struct bgp_dest *dest,
if (bdi->penalty >= bdc->suppress_value) {
bgp_path_info_set_flag(dest, path, BGP_PATH_DAMPED);
bdi->suppress_time = t_now;
- BGP_DAMP_LIST_DEL(bdc, bdi);
+ bgp_no_reuse_list_delete(bdi, bdc);
bgp_reuse_list_add(bdi, bdc);
}
-
return BGP_DAMP_USED;
}
@@ -270,7 +400,10 @@ int bgp_damp_update(struct bgp_path_info *path, struct bgp_dest *dest,
time_t t_now;
struct bgp_damp_info *bdi;
int status;
- struct bgp_damp_config *bdc = &damp[afi][safi];
+ struct bgp_damp_config *bdc;
+
+ bdc = get_active_bdc_from_pi(path, afi, safi);
+ assert(bdc);
if (!path->extra || !((bdi = path->extra->damp_info)))
return BGP_DAMP_USED;
@@ -289,7 +422,7 @@ int bgp_damp_update(struct bgp_path_info *path, struct bgp_dest *dest,
&& (bdi->penalty < bdc->reuse_limit)) {
bgp_path_info_unset_flag(dest, path, BGP_PATH_DAMPED);
bgp_reuse_list_delete(bdi, bdc);
- BGP_DAMP_LIST_ADD(bdc, bdi);
+ bgp_no_reuse_list_add(bdi, bdc);
bdi->suppress_time = 0;
status = BGP_DAMP_USED;
} else
@@ -297,36 +430,29 @@ int bgp_damp_update(struct bgp_path_info *path, struct bgp_dest *dest,
if (bdi->penalty > bdc->reuse_limit / 2.0)
bdi->t_updated = t_now;
- else
- bgp_damp_info_free(bdi, 0, afi, safi);
+ else {
+ bgp_damp_info_unclaim(bdi);
+ bgp_damp_info_free(&bdi, bdc, 0, afi, safi);
+ }
return status;
}
-void bgp_damp_info_free(struct bgp_damp_info *bdi, int withdraw, afi_t afi,
- safi_t safi)
+void bgp_damp_info_free(struct bgp_damp_info **bdi, struct bgp_damp_config *bdc,
+ int withdraw, afi_t afi, safi_t safi)
{
- struct bgp_path_info *path;
- struct bgp_damp_config *bdc = &damp[afi][safi];
+ assert(bdc && bdi && *bdi);
- if (!bdi)
+ if ((*bdi)->path == NULL) {
+ XFREE(MTYPE_BGP_DAMP_INFO, (*bdi));
return;
+ }
- path = bdi->path;
- path->extra->damp_info = NULL;
-
- if (CHECK_FLAG(path->flags, BGP_PATH_DAMPED))
- bgp_reuse_list_delete(bdi, bdc);
- else
- BGP_DAMP_LIST_DEL(bdc, bdi);
-
- bgp_path_info_unset_flag(bdi->dest, path,
+ (*bdi)->path->extra->damp_info = NULL;
+ bgp_path_info_unset_flag((*bdi)->dest, (*bdi)->path,
BGP_PATH_HISTORY | BGP_PATH_DAMPED);
-
- if (bdi->lastrecord == BGP_RECORD_WITHDRAW && withdraw)
- bgp_path_info_delete(bdi->dest, path);
-
- XFREE(MTYPE_BGP_DAMP_INFO, bdi);
+ if ((*bdi)->lastrecord == BGP_RECORD_WITHDRAW && withdraw)
+ bgp_path_info_delete((*bdi)->dest, (*bdi)->path);
}
static void bgp_damp_parameter_set(int hlife, int reuse, int sup, int maxsup,
@@ -369,8 +495,7 @@ static void bgp_damp_parameter_set(int hlife, int reuse, int sup, int maxsup,
bdc->reuse_list =
XCALLOC(MTYPE_BGP_DAMP_ARRAY,
- bdc->reuse_list_size * sizeof(struct bgp_reuse_node *));
-
+ bdc->reuse_list_size * sizeof(struct reuselist));
/* Reuse-array computations */
bdc->reuse_index = XCALLOC(MTYPE_BGP_DAMP_ARRAY,
sizeof(int) * bdc->reuse_index_size);
@@ -397,7 +522,7 @@ static void bgp_damp_parameter_set(int hlife, int reuse, int sup, int maxsup,
int bgp_damp_enable(struct bgp *bgp, afi_t afi, safi_t safi, time_t half,
unsigned int reuse, unsigned int suppress, time_t max)
{
- struct bgp_damp_config *bdc = &damp[afi][safi];
+ struct bgp_damp_config *bdc = &bgp->damp[afi][safi];
if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)) {
if (bdc->half_life == half && bdc->reuse_limit == reuse
@@ -409,6 +534,8 @@ int bgp_damp_enable(struct bgp *bgp, afi_t afi, safi_t safi, time_t half,
SET_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING);
bgp_damp_parameter_set(half, reuse, suppress, max, bdc);
+ bdc->afi = afi;
+ bdc->safi = safi;
/* Register reuse timer. */
thread_add_timer(bm->master, bgp_reuse_timer, bdc, DELTA_REUSE,
@@ -417,8 +544,30 @@ int bgp_damp_enable(struct bgp *bgp, afi_t afi, safi_t safi, time_t half,
return 0;
}
-static void bgp_damp_config_clean(struct bgp_damp_config *bdc)
+/* Clean all the bgp_damp_info stored in reuse_list and no_reuse_list. */
+void bgp_damp_info_clean(struct bgp_damp_config *bdc, afi_t afi, safi_t safi)
{
+ struct bgp_damp_info *bdi;
+ struct reuselist_node *rn;
+ struct reuselist *list;
+ unsigned int i;
+
+ bdc->reuse_offset = 0;
+ for (i = 0; i < bdc->reuse_list_size; ++i) {
+ list = &bdc->reuse_list[i];
+ while ((rn = SLIST_FIRST(list)) != NULL) {
+ bdi = rn->info;
+ bgp_reuselist_del(list, &rn);
+ bgp_damp_info_free(&bdi, bdc, 1, afi, safi);
+ }
+ }
+
+ while ((rn = SLIST_FIRST(&bdc->no_reuse_list)) != NULL) {
+ bdi = rn->info;
+ bgp_reuselist_del(&bdc->no_reuse_list, &rn);
+ bgp_damp_info_free(&bdi, bdc, 1, afi, safi);
+ }
+
/* Free decay array */
XFREE(MTYPE_BGP_DAMP_ARRAY, bdc->decay_array);
bdc->decay_array_size = 0;
@@ -428,96 +577,81 @@ static void bgp_damp_config_clean(struct bgp_damp_config *bdc)
bdc->reuse_index_size = 0;
/* Free reuse list array. */
+ for (i = 0; i < bdc->reuse_list_size; ++i)
+ bgp_reuselist_free(&bdc->reuse_list[i]);
+
XFREE(MTYPE_BGP_DAMP_ARRAY, bdc->reuse_list);
bdc->reuse_list_size = 0;
-}
-
-/* Clean all the bgp_damp_info stored in reuse_list. */
-void bgp_damp_info_clean(afi_t afi, safi_t safi)
-{
- unsigned int i;
- struct bgp_damp_info *bdi, *next;
- struct bgp_damp_config *bdc = &damp[afi][safi];
-
- bdc->reuse_offset = 0;
-
- for (i = 0; i < bdc->reuse_list_size; i++) {
- if (!bdc->reuse_list[i])
- continue;
-
- for (bdi = bdc->reuse_list[i]; bdi; bdi = next) {
- next = bdi->next;
- bgp_damp_info_free(bdi, 1, afi, safi);
- }
- bdc->reuse_list[i] = NULL;
- }
- for (bdi = bdc->no_reuse_list; bdi; bdi = next) {
- next = bdi->next;
- bgp_damp_info_free(bdi, 1, afi, safi);
- }
- bdc->no_reuse_list = NULL;
+ THREAD_OFF(bdc->t_reuse);
}
+/* Disable route flap dampening for a bgp instance.
+ *
+ * Please note that this function also gets used to free memory when deleting a
+ * bgp instance.
+ */
int bgp_damp_disable(struct bgp *bgp, afi_t afi, safi_t safi)
{
- struct bgp_damp_config *bdc = &damp[afi][safi];
+ struct bgp_damp_config *bdc;
+
+ bdc = &bgp->damp[afi][safi];
+ if (!bdc)
+ return 0;
+
/* If it wasn't enabled, there's nothing to do. */
if (!CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING))
return 0;
/* Cancel reuse event. */
- thread_cancel(&(bdc->t_reuse));
+ thread_cancel(&bdc->t_reuse);
/* Clean BGP dampening information. */
- bgp_damp_info_clean(afi, safi);
-
- /* Clear configuration */
- bgp_damp_config_clean(bdc);
+ bgp_damp_info_clean(bdc, afi, safi);
UNSET_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING);
+
return 0;
}
-void bgp_config_write_damp(struct vty *vty, afi_t afi, safi_t safi)
+void bgp_config_write_damp(struct vty *vty, struct bgp *bgp, afi_t afi,
+ safi_t safi)
{
- if (damp[afi][safi].half_life == DEFAULT_HALF_LIFE * 60
- && damp[afi][safi].reuse_limit == DEFAULT_REUSE
- && damp[afi][safi].suppress_value == DEFAULT_SUPPRESS
- && damp[afi][safi].max_suppress_time
- == damp[afi][safi].half_life * 4)
+ struct bgp_damp_config *bdc;
+
+ bdc = &bgp->damp[afi][safi];
+ if (bdc->half_life == DEFAULT_HALF_LIFE * 60
+ && bdc->reuse_limit == DEFAULT_REUSE
+ && bdc->suppress_value == DEFAULT_SUPPRESS
+ && bdc->max_suppress_time == bdc->half_life * 4)
vty_out(vty, " bgp dampening\n");
- else if (damp[afi][safi].half_life != DEFAULT_HALF_LIFE * 60
- && damp[afi][safi].reuse_limit == DEFAULT_REUSE
- && damp[afi][safi].suppress_value == DEFAULT_SUPPRESS
- && damp[afi][safi].max_suppress_time
- == damp[afi][safi].half_life * 4)
- vty_out(vty, " bgp dampening %lld\n",
- damp[afi][safi].half_life / 60LL);
+ else if (bdc->half_life != DEFAULT_HALF_LIFE * 60
+ && bdc->reuse_limit == DEFAULT_REUSE
+ && bdc->suppress_value == DEFAULT_SUPPRESS
+ && bdc->max_suppress_time == bdc->half_life * 4)
+ vty_out(vty, " bgp dampening %lld\n", bdc->half_life / 60LL);
else
vty_out(vty, " bgp dampening %lld %d %d %lld\n",
- damp[afi][safi].half_life / 60LL,
- damp[afi][safi].reuse_limit,
- damp[afi][safi].suppress_value,
- damp[afi][safi].max_suppress_time / 60LL);
+ bdc->half_life / 60LL, bdc->reuse_limit,
+ bdc->suppress_value, bdc->max_suppress_time / 60LL);
}
-static const char *bgp_get_reuse_time(unsigned int penalty, char *buf,
- size_t len, afi_t afi, safi_t safi,
- bool use_json, json_object *json)
+static const char *bgp_get_reuse_time(struct bgp_damp_config *bdc,
+ unsigned int penalty, char *buf,
+ size_t len, bool use_json,
+ json_object *json)
{
time_t reuse_time = 0;
struct tm tm;
int time_store = 0;
- if (penalty > damp[afi][safi].reuse_limit) {
+ if (penalty > bdc->reuse_limit) {
reuse_time = (int)(DELTA_T
- * ((log((double)damp[afi][safi].reuse_limit
- / penalty))
- / (log(damp[afi][safi].decay_array[1]))));
+ * ((log((double)bdc->reuse_limit / penalty))
+ / (log(bdc->decay_array[1]))));
- if (reuse_time > damp[afi][safi].max_suppress_time)
- reuse_time = damp[afi][safi].max_suppress_time;
+ if (reuse_time > bdc->max_suppress_time)
+ reuse_time = bdc->max_suppress_time;
gmtime_r(&reuse_time, &tm);
} else
@@ -569,14 +703,15 @@ static const char *bgp_get_reuse_time(unsigned int penalty, char *buf,
return buf;
}
-void bgp_damp_info_vty(struct vty *vty, struct bgp_path_info *path, afi_t afi,
- safi_t safi, json_object *json_path)
+void bgp_damp_info_vty(struct vty *vty, struct bgp *bgp,
+ struct bgp_path_info *path, afi_t afi, safi_t safi,
+ json_object *json_path)
{
struct bgp_damp_info *bdi;
time_t t_now, t_diff;
char timebuf[BGP_UPTIME_LEN];
int penalty;
- struct bgp_damp_config *bdc = &damp[afi][safi];
+ struct bgp_damp_config *bdc = &bgp->damp[afi][safi];
if (!path->extra)
return;
@@ -602,8 +737,8 @@ void bgp_damp_info_vty(struct vty *vty, struct bgp_path_info *path, afi_t afi,
if (CHECK_FLAG(path->flags, BGP_PATH_DAMPED)
&& !CHECK_FLAG(path->flags, BGP_PATH_HISTORY))
- bgp_get_reuse_time(penalty, timebuf, BGP_UPTIME_LEN,
- afi, safi, 1, json_path);
+ bgp_get_reuse_time(bdc, penalty, timebuf,
+ BGP_UPTIME_LEN, 1, json_path);
} else {
vty_out(vty,
" Dampinfo: penalty %d, flapped %d times in %s",
@@ -614,14 +749,15 @@ void bgp_damp_info_vty(struct vty *vty, struct bgp_path_info *path, afi_t afi,
if (CHECK_FLAG(path->flags, BGP_PATH_DAMPED)
&& !CHECK_FLAG(path->flags, BGP_PATH_HISTORY))
vty_out(vty, ", reuse in %s",
- bgp_get_reuse_time(penalty, timebuf,
- BGP_UPTIME_LEN, afi, safi, 0,
+ bgp_get_reuse_time(bdc, penalty, timebuf,
+ BGP_UPTIME_LEN, 0,
json_path));
vty_out(vty, "\n");
}
}
+
const char *bgp_damp_reuse_time_vty(struct vty *vty, struct bgp_path_info *path,
char *timebuf, size_t len, afi_t afi,
safi_t safi, bool use_json,
@@ -630,7 +766,11 @@ const char *bgp_damp_reuse_time_vty(struct vty *vty, struct bgp_path_info *path,
struct bgp_damp_info *bdi;
time_t t_now, t_diff;
int penalty;
- struct bgp_damp_config *bdc = &damp[afi][safi];
+ struct bgp_damp_config *bdc;
+
+ bdc = get_active_bdc_from_pi(path, afi, safi);
+ if (!bdc)
+ return NULL;
if (!path->extra)
return NULL;
@@ -648,24 +788,23 @@ const char *bgp_damp_reuse_time_vty(struct vty *vty, struct bgp_path_info *path,
t_diff = t_now - bdi->t_updated;
penalty = bgp_damp_decay(t_diff, bdi->penalty, bdc);
- return bgp_get_reuse_time(penalty, timebuf, len, afi, safi, use_json,
- json);
+ return bgp_get_reuse_time(bdc, penalty, timebuf, len, use_json, json);
}
+
static int bgp_print_dampening_parameters(struct bgp *bgp, struct vty *vty,
afi_t afi, safi_t safi)
{
+ struct bgp_damp_config *bdc;
if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)) {
+ bdc = &bgp->damp[afi][safi];
vty_out(vty, "Half-life time: %lld min\n",
- (long long)damp[afi][safi].half_life / 60);
- vty_out(vty, "Reuse penalty: %d\n",
- damp[afi][safi].reuse_limit);
- vty_out(vty, "Suppress penalty: %d\n",
- damp[afi][safi].suppress_value);
+ (long long)bdc->half_life / 60);
+ vty_out(vty, "Reuse penalty: %d\n", bdc->reuse_limit);
+ vty_out(vty, "Suppress penalty: %d\n", bdc->suppress_value);
vty_out(vty, "Max suppress time: %lld min\n",
- (long long)damp[afi][safi].max_suppress_time / 60);
- vty_out(vty, "Max suppress penalty: %u\n",
- damp[afi][safi].ceiling);
+ (long long)bdc->max_suppress_time / 60);
+ vty_out(vty, "Max suppress penalty: %u\n", bdc->ceiling);
vty_out(vty, "\n");
} else
vty_out(vty, "dampening not enabled for %s\n",
@@ -678,8 +817,8 @@ int bgp_show_dampening_parameters(struct vty *vty, afi_t afi, safi_t safi,
uint8_t show_flags)
{
struct bgp *bgp;
- bgp = bgp_get_default();
+ bgp = bgp_get_default();
if (bgp == NULL) {
vty_out(vty, "No BGP process is configured\n");
return CMD_WARNING;
@@ -718,3 +857,132 @@ int bgp_show_dampening_parameters(struct vty *vty, afi_t afi, safi_t safi,
}
return CMD_SUCCESS;
}
+
+void bgp_peer_damp_enable(struct peer *peer, afi_t afi, safi_t safi,
+ time_t half, unsigned int reuse,
+ unsigned int suppress, time_t max)
+{
+ struct bgp_damp_config *bdc;
+
+ if (!peer)
+ return;
+ bdc = &peer->damp[afi][safi];
+ if (peer_af_flag_check(peer, afi, safi, PEER_FLAG_CONFIG_DAMPENING)) {
+ if (bdc->half_life == half && bdc->reuse_limit == reuse
+ && bdc->suppress_value == suppress
+ && bdc->max_suppress_time == max)
+ return;
+ bgp_peer_damp_disable(peer, afi, safi);
+ }
+ SET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_CONFIG_DAMPENING);
+ bgp_damp_parameter_set(half, reuse, suppress, max, bdc);
+ bdc->afi = afi;
+ bdc->safi = safi;
+ thread_add_timer(bm->master, bgp_reuse_timer, bdc, DELTA_REUSE,
+ &bdc->t_reuse);
+}
+
+/* Disable route flap dampening for a peer.
+ *
+ * Please note that this function also gets used to free memory when deleting a
+ * peer or peer group.
+ */
+void bgp_peer_damp_disable(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct bgp_damp_config *bdc;
+
+ if (!peer_af_flag_check(peer, afi, safi, PEER_FLAG_CONFIG_DAMPENING))
+ return;
+ bdc = &peer->damp[afi][safi];
+ if (!bdc)
+ return;
+ bgp_damp_info_clean(bdc, afi, safi);
+ UNSET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_CONFIG_DAMPENING);
+}
+
+void bgp_config_write_peer_damp(struct vty *vty, struct peer *peer, afi_t afi,
+ safi_t safi)
+{
+ struct bgp_damp_config *bdc;
+
+ bdc = &peer->damp[afi][safi];
+ if (bdc->half_life == DEFAULT_HALF_LIFE * 60
+ && bdc->reuse_limit == DEFAULT_REUSE
+ && bdc->suppress_value == DEFAULT_SUPPRESS
+ && bdc->max_suppress_time == bdc->half_life * 4)
+ vty_out(vty, " neighbor %s dampening\n", peer->host);
+ else if (bdc->half_life != DEFAULT_HALF_LIFE * 60
+ && bdc->reuse_limit == DEFAULT_REUSE
+ && bdc->suppress_value == DEFAULT_SUPPRESS
+ && bdc->max_suppress_time == bdc->half_life * 4)
+ vty_out(vty, " neighbor %s dampening %lld\n", peer->host,
+ bdc->half_life / 60LL);
+ else
+ vty_out(vty, " neighbor %s dampening %lld %d %d %lld\n",
+ peer->host, bdc->half_life / 60LL, bdc->reuse_limit,
+ bdc->suppress_value, bdc->max_suppress_time / 60LL);
+}
+
+static void bgp_print_peer_dampening_parameters(struct vty *vty,
+ struct peer *peer, afi_t afi,
+ safi_t safi, bool use_json,
+ json_object *json)
+{
+ struct bgp_damp_config *bdc;
+
+ if (!peer)
+ return;
+ if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_CONFIG_DAMPENING)) {
+ bdc = &peer->damp[afi][safi];
+ if (!bdc)
+ return;
+ if (use_json) {
+ json_object_int_add(json, "halfLifeSecs",
+ bdc->half_life);
+ json_object_int_add(json, "reusePenalty",
+ bdc->reuse_limit);
+ json_object_int_add(json, "suppressPenalty",
+ bdc->suppress_value);
+ json_object_int_add(json, "maxSuppressTimeSecs",
+ bdc->max_suppress_time);
+ json_object_int_add(json, "maxSuppressPenalty",
+ bdc->ceiling);
+ } else {
+ vty_out(vty, "Half-life time: %lld min\n",
+ (long long)bdc->half_life / 60);
+ vty_out(vty, "Reuse penalty: %d\n", bdc->reuse_limit);
+ vty_out(vty, "Suppress penalty: %d\n",
+ bdc->suppress_value);
+ vty_out(vty, "Max suppress time: %lld min\n",
+ (long long)bdc->max_suppress_time / 60);
+ vty_out(vty, "Max suppress penalty: %u\n",
+ bdc->ceiling);
+ vty_out(vty, "\n");
+ }
+ } else if (!use_json)
+ vty_out(vty, "neighbor dampening not enabled for %s\n",
+ get_afi_safi_str(afi, safi, false));
+}
+
+void bgp_show_peer_dampening_parameters(struct vty *vty, struct peer *peer,
+ afi_t afi, safi_t safi, bool use_json)
+{
+ json_object *json;
+
+ if (use_json) {
+ json = json_object_new_object();
+ json_object_string_add(json, "addressFamily",
+ get_afi_safi_str(afi, safi, false));
+ bgp_print_peer_dampening_parameters(vty, peer, afi, safi, true,
+ json);
+ 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, "\nFor address family: %s\n",
+ get_afi_safi_str(afi, safi, false));
+ bgp_print_peer_dampening_parameters(vty, peer, afi, safi, false,
+ NULL);
+ }
+}
diff --git a/bgpd/bgp_damp.h b/bgpd/bgp_damp.h
index 604706300b..521f59b296 100644
--- a/bgpd/bgp_damp.h
+++ b/bgpd/bgp_damp.h
@@ -25,11 +25,6 @@
/* Structure maintained on a per-route basis. */
struct bgp_damp_info {
- /* Doubly linked list. This information must be linked to
- reuse_list or no_reuse_list. */
- struct bgp_damp_info *next;
- struct bgp_damp_info *prev;
-
/* Figure-of-merit. */
unsigned int penalty;
@@ -45,6 +40,9 @@ struct bgp_damp_info {
/* Time of route start to be suppressed. */
time_t suppress_time;
+ /* Back reference to associated dampening configuration. */
+ struct bgp_damp_config *config;
+
/* Back reference to bgp_path_info. */
struct bgp_path_info *path;
@@ -53,6 +51,8 @@ struct bgp_damp_info {
/* Current index in the reuse_list. */
int index;
+#define BGP_DAMP_NO_REUSE_LIST_INDEX \
+ (-1) /* index for elements on no_reuse_list */
/* Last time message type. */
uint8_t lastrecord;
@@ -63,6 +63,13 @@ struct bgp_damp_info {
safi_t safi;
};
+struct reuselist_node {
+ SLIST_ENTRY(reuselist_node) entry;
+ struct bgp_damp_info *info;
+};
+
+SLIST_HEAD(reuselist, reuselist_node);
+
/* Specified parameter set configuration. */
struct bgp_damp_config {
/* Value over which routes suppressed. */
@@ -100,11 +107,11 @@ struct bgp_damp_config {
int *reuse_index;
/* Reuse list array per-set based. */
- struct bgp_damp_info **reuse_list;
- int reuse_offset;
+ struct reuselist *reuse_list;
+ unsigned int reuse_offset;
/* All dampening information which is not on reuse list. */
- struct bgp_damp_info *no_reuse_list;
+ struct reuselist no_reuse_list;
/* Reuse timer thread per-set base. */
struct thread *t_reuse;
@@ -132,6 +139,8 @@ struct bgp_damp_config {
#define REUSE_LIST_SIZE 256
#define REUSE_ARRAY_SIZE 1024
+extern struct bgp_damp_config *get_active_bdc_from_pi(struct bgp_path_info *pi,
+ afi_t afi, safi_t safi);
extern int bgp_damp_enable(struct bgp *, afi_t, safi_t, time_t, unsigned int,
unsigned int, time_t);
extern int bgp_damp_disable(struct bgp *, afi_t, safi_t);
@@ -139,13 +148,18 @@ extern int bgp_damp_withdraw(struct bgp_path_info *path, struct bgp_dest *dest,
afi_t afi, safi_t safi, int attr_change);
extern int bgp_damp_update(struct bgp_path_info *path, struct bgp_dest *dest,
afi_t afi, safi_t saff);
-extern void bgp_damp_info_free(struct bgp_damp_info *path, int withdraw,
+extern void bgp_damp_info_free(struct bgp_damp_info **path,
+ struct bgp_damp_config *bdc, int withdraw,
afi_t afi, safi_t safi);
-extern void bgp_damp_info_clean(afi_t afi, safi_t safi);
+extern void bgp_damp_info_clean(struct bgp_damp_config *bdc, afi_t afi,
+ safi_t safi);
+extern void bgp_damp_config_clean(struct bgp_damp_config *bdc);
extern int bgp_damp_decay(time_t, int, struct bgp_damp_config *damp);
-extern void bgp_config_write_damp(struct vty *, afi_t afi, safi_t safi);
-extern void bgp_damp_info_vty(struct vty *vty, struct bgp_path_info *path,
- afi_t afi, safi_t safi, json_object *json_path);
+extern void bgp_config_write_damp(struct vty *vty, struct bgp *bgp, afi_t afi,
+ safi_t safi);
+extern void bgp_damp_info_vty(struct vty *vty, struct bgp *bgp,
+ struct bgp_path_info *path, afi_t afi,
+ safi_t safi, json_object *json_path);
extern const char *bgp_damp_reuse_time_vty(struct vty *vty,
struct bgp_path_info *path,
char *timebuf, size_t len, afi_t afi,
@@ -153,5 +167,14 @@ extern const char *bgp_damp_reuse_time_vty(struct vty *vty,
json_object *json);
extern int bgp_show_dampening_parameters(struct vty *vty, afi_t, safi_t,
uint8_t);
+extern void bgp_peer_damp_enable(struct peer *peer, afi_t afi, safi_t safi,
+ time_t half, unsigned int reuse,
+ unsigned int suppress, time_t max);
+extern void bgp_peer_damp_disable(struct peer *peer, afi_t afi, safi_t safi);
+extern void bgp_config_write_peer_damp(struct vty *vty, struct peer *peer,
+ afi_t afi, safi_t safi);
+extern void bgp_show_peer_dampening_parameters(struct vty *vty,
+ struct peer *peer, afi_t afi,
+ safi_t safi, bool use_json);
#endif /* _QUAGGA_BGP_DAMP_H */
diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c
index 2c076fb80b..3afa6eaf09 100644
--- a/bgpd/bgp_debug.c
+++ b/bgpd/bgp_debug.c
@@ -38,6 +38,7 @@
#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_debug.h"
#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_lcommunity.h"
#include "bgpd/bgp_updgrp.h"
#include "bgpd/bgp_mplsvpn.h"
#include "bgpd/bgp_ecommunity.h"
@@ -118,7 +119,7 @@ static const struct message bgp_notify_msg[] = {
{BGP_NOTIFY_HOLD_ERR, "Hold Timer Expired"},
{BGP_NOTIFY_FSM_ERR, "Neighbor Events Error"},
{BGP_NOTIFY_CEASE, "Cease"},
- {BGP_NOTIFY_CAPABILITY_ERR, "CAPABILITY Message Error"},
+ {BGP_NOTIFY_ROUTE_REFRESH_ERR, "ROUTE-REFRESH Message Error"},
{0}};
static const struct message bgp_notify_head_msg[] = {
@@ -166,11 +167,9 @@ static const struct message bgp_notify_cease_msg[] = {
{BGP_NOTIFY_CEASE_OUT_OF_RESOURCE, "/Out of Resource"},
{0}};
-static const struct message bgp_notify_capability_msg[] = {
+static const struct message bgp_notify_route_refresh_msg[] = {
{BGP_NOTIFY_SUBCODE_UNSPECIFIC, "/Unspecific"},
- {BGP_NOTIFY_CAPABILITY_INVALID_ACTION, "/Invalid Action Value"},
- {BGP_NOTIFY_CAPABILITY_INVALID_LENGTH, "/Invalid Capability Length"},
- {BGP_NOTIFY_CAPABILITY_MALFORMED_CODE, "/Malformed Capability Value"},
+ {BGP_NOTIFY_ROUTE_REFRESH_INVALID_MSG_LEN, "/Invalid Message Length"},
{0}};
static const struct message bgp_notify_fsm_msg[] = {
@@ -411,6 +410,11 @@ bool bgp_dump_attr(struct attr *attr, char *buf, size_t size)
", community %s",
community_str(attr->community, false));
+ if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES)))
+ snprintf(buf + strlen(buf), size - strlen(buf),
+ ", large-community %s",
+ lcommunity_str(attr->lcommunity, false));
+
if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)))
snprintf(buf + strlen(buf), size - strlen(buf),
", extcommunity %s", ecommunity_str(attr->ecommunity));
@@ -487,8 +491,8 @@ const char *bgp_notify_subcode_str(char code, char subcode)
case BGP_NOTIFY_CEASE:
return lookup_msg(bgp_notify_cease_msg, subcode,
"Unrecognized Error Subcode");
- case BGP_NOTIFY_CAPABILITY_ERR:
- return lookup_msg(bgp_notify_capability_msg, subcode,
+ case BGP_NOTIFY_ROUTE_REFRESH_ERR:
+ return lookup_msg(bgp_notify_route_refresh_msg, subcode,
"Unrecognized Error Subcode");
}
return "";
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
index a4d17cac40..cec4a9339a 100644
--- a/bgpd/bgp_fsm.c
+++ b/bgpd/bgp_fsm.c
@@ -469,6 +469,7 @@ void bgp_timer_set(struct peer *peer)
BGP_TIMER_OFF(peer->t_gr_restart);
BGP_TIMER_OFF(peer->t_gr_stale);
BGP_TIMER_OFF(peer->t_pmax_restart);
+ BGP_TIMER_OFF(peer->t_refresh_stalepath);
/* fallthru */
case Clearing:
BGP_TIMER_OFF(peer->t_start);
@@ -1283,6 +1284,16 @@ int bgp_stop(struct peer *peer)
peer->nsf[afi][safi] = 0;
}
+ /* Stop route-refresh stalepath timer */
+ if (peer->t_refresh_stalepath) {
+ BGP_TIMER_OFF(peer->t_refresh_stalepath);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s: route-refresh restart stalepath timer stopped",
+ peer->host);
+ }
+
/* If peer reset before receiving EOR, decrement EOR count and
* cancel the selection deferral timer if there are no
* pending EOR messages to be received
@@ -2066,14 +2077,16 @@ static int bgp_establish(struct peer *peer)
PEER_CAP_ORF_PREFIX_SM_ADV)) {
if (CHECK_FLAG(peer->af_cap[afi][safi],
PEER_CAP_ORF_PREFIX_RM_RCV))
- bgp_route_refresh_send(peer, afi, safi,
- ORF_TYPE_PREFIX,
- REFRESH_IMMEDIATE, 0);
+ bgp_route_refresh_send(
+ peer, afi, safi, ORF_TYPE_PREFIX,
+ REFRESH_IMMEDIATE, 0,
+ BGP_ROUTE_REFRESH_NORMAL);
else if (CHECK_FLAG(peer->af_cap[afi][safi],
PEER_CAP_ORF_PREFIX_RM_OLD_RCV))
- bgp_route_refresh_send(peer, afi, safi,
- ORF_TYPE_PREFIX_OLD,
- REFRESH_IMMEDIATE, 0);
+ bgp_route_refresh_send(
+ peer, afi, safi, ORF_TYPE_PREFIX_OLD,
+ REFRESH_IMMEDIATE, 0,
+ BGP_ROUTE_REFRESH_NORMAL);
}
}
diff --git a/bgpd/bgp_label.c b/bgpd/bgp_label.c
index 4f440cd1f8..5a31bd0243 100644
--- a/bgpd/bgp_label.c
+++ b/bgpd/bgp_label.c
@@ -120,6 +120,65 @@ mpls_label_t bgp_adv_label(struct bgp_dest *dest, struct bgp_path_info *pi,
return dest->local_label;
}
+static void bgp_send_fec_register_label_msg(struct bgp_dest *dest, bool reg,
+ uint32_t label_index)
+{
+ struct stream *s;
+ int command;
+ const struct prefix *p;
+ uint16_t flags = 0;
+ size_t flags_pos = 0;
+ mpls_label_t *local_label = &(dest->local_label);
+ bool have_label_to_reg =
+ bgp_is_valid_label(local_label)
+ && label_pton(local_label) != MPLS_LABEL_IMPLICIT_NULL;
+
+ p = bgp_dest_get_prefix(dest);
+
+ /* Check socket. */
+ if (!zclient || zclient->sock < 0)
+ return;
+
+ if (BGP_DEBUG(labelpool, LABELPOOL))
+ zlog_debug("%s: FEC %sregister %pRN label_index=%u label=%u",
+ __func__, reg ? "" : "un", bgp_dest_to_rnode(dest),
+ label_index, label_pton(local_label));
+ /* If the route node has a local_label assigned or the
+ * path node has an MPLS SR label index allowing zebra to
+ * derive the label, proceed with registration. */
+ s = zclient->obuf;
+ stream_reset(s);
+ command = (reg) ? ZEBRA_FEC_REGISTER : ZEBRA_FEC_UNREGISTER;
+ zclient_create_header(s, command, VRF_DEFAULT);
+ flags_pos = stream_get_endp(s); /* save position of 'flags' */
+ stream_putw(s, flags); /* initial flags */
+ stream_putw(s, PREFIX_FAMILY(p));
+ stream_put_prefix(s, p);
+ if (reg) {
+ /* label index takes precedence over auto-assigned label. */
+ if (label_index != 0) {
+ flags |= ZEBRA_FEC_REGISTER_LABEL_INDEX;
+ stream_putl(s, label_index);
+ } else if (have_label_to_reg) {
+ flags |= ZEBRA_FEC_REGISTER_LABEL;
+ stream_putl(s, label_pton(local_label));
+ }
+ SET_FLAG(dest->flags, BGP_NODE_REGISTERED_FOR_LABEL);
+ } else
+ UNSET_FLAG(dest->flags, BGP_NODE_REGISTERED_FOR_LABEL);
+
+ /* Set length and flags */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ /*
+ * We only need to write new flags if this is a register
+ */
+ if (reg)
+ stream_putw_at(s, flags_pos, flags);
+
+ zclient_send_message(zclient);
+}
+
/**
* This is passed as the callback function to bgp_labelpool.c:bgp_lp_get()
* by bgp_reg_dereg_for_label() when a label needs to be obtained from
@@ -130,20 +189,21 @@ mpls_label_t bgp_adv_label(struct bgp_dest *dest, struct bgp_path_info *pi,
int bgp_reg_for_label_callback(mpls_label_t new_label, void *labelid,
bool allocated)
{
- struct bgp_path_info *pi;
struct bgp_dest *dest;
- pi = labelid;
- /* Is this path still valid? */
- if (!bgp_path_info_unlock(pi)) {
- if (BGP_DEBUG(labelpool, LABELPOOL))
- zlog_debug(
- "%s: bgp_path_info is no longer valid, ignoring",
- __func__);
+ dest = labelid;
+
+ /*
+ * if the route had been removed or the request has gone then reject
+ * the allocated label. The requesting code will have done what is
+ * required to allocate the correct label
+ */
+ if (!CHECK_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED)) {
+ bgp_dest_unlock_node(dest);
return -1;
}
- dest = pi->net;
+ bgp_dest_unlock_node(dest);
if (BGP_DEBUG(labelpool, LABELPOOL))
zlog_debug("%s: FEC %pRN label=%u, allocated=%d", __func__,
@@ -151,47 +211,15 @@ int bgp_reg_for_label_callback(mpls_label_t new_label, void *labelid,
if (!allocated) {
/*
- * previously-allocated label is now invalid
+ * previously-allocated label is now invalid, set to implicit
+ * null until new label arrives
*/
- if (pi->attr->label_index == MPLS_INVALID_LABEL_INDEX
- && pi->attr->label != MPLS_LABEL_NONE
- && CHECK_FLAG(dest->flags, BGP_NODE_REGISTERED_FOR_LABEL)) {
- bgp_unregister_for_label(dest);
+ if (CHECK_FLAG(dest->flags, BGP_NODE_REGISTERED_FOR_LABEL)) {
+ UNSET_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED);
label_ntop(MPLS_LABEL_IMPLICIT_NULL, 1,
&dest->local_label);
bgp_set_valid_label(&dest->local_label);
}
- return 0;
- }
-
- /*
- * label index is assigned, this should be handled by SR-related code,
- * so retry FEC registration and then reject label allocation for
- * it to be released to label pool
- */
- if (pi->attr->label_index != MPLS_INVALID_LABEL_INDEX) {
- flog_err(
- EC_BGP_LABEL,
- "%s: FEC %pRN Rejecting allocated label %u as Label Index is %u",
- __func__, bgp_dest_to_rnode(dest), new_label,
- pi->attr->label_index);
-
- bgp_register_for_label(pi->net, pi);
-
- return -1;
- }
-
- if (pi->attr->label != MPLS_INVALID_LABEL) {
- if (new_label == pi->attr->label) {
- /* already have same label, accept but do nothing */
- return 0;
- }
- /* Shouldn't happen: different label allocation */
- flog_err(EC_BGP_LABEL,
- "%s: %pRN had label %u but got new assignment %u",
- __func__, bgp_dest_to_rnode(dest), pi->attr->label,
- new_label);
- /* continue means use new one */
}
label_ntop(new_label, 1, &dest->local_label);
@@ -200,7 +228,7 @@ int bgp_reg_for_label_callback(mpls_label_t new_label, void *labelid,
/*
* Get back to registering the FEC
*/
- bgp_register_for_label(pi->net, pi);
+ bgp_send_fec_register_label_msg(dest, true, 0);
return 0;
}
@@ -209,20 +237,12 @@ void bgp_reg_dereg_for_label(struct bgp_dest *dest, struct bgp_path_info *pi,
bool reg)
{
bool with_label_index = false;
- struct stream *s;
const struct prefix *p;
- mpls_label_t *local_label;
- int command;
- uint16_t flags = 0;
- size_t flags_pos = 0;
+ bool have_label_to_reg =
+ bgp_is_valid_label(&dest->local_label)
+ && label_pton(&dest->local_label) != MPLS_LABEL_IMPLICIT_NULL;
p = bgp_dest_get_prefix(dest);
- local_label = &(dest->local_label);
- /* this prevents the loop when we're called by
- * bgp_reg_for_label_callback()
- */
- bool have_label_to_reg = bgp_is_valid_label(local_label)
- && label_pton(local_label) != MPLS_LABEL_IMPLICIT_NULL;
if (reg) {
assert(pi);
@@ -234,67 +254,37 @@ void bgp_reg_dereg_for_label(struct bgp_dest *dest, struct bgp_path_info *pi,
ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID))
&& pi->attr->label_index != BGP_INVALID_LABEL_INDEX) {
with_label_index = true;
+ UNSET_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED);
} else {
/*
- * If no label index was provided -- assume any label
+ * If no label has been registered -- assume any label
* from label pool will do. This means that label index
* always takes precedence over auto-assigned labels.
*/
if (!have_label_to_reg) {
+ SET_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED);
if (BGP_DEBUG(labelpool, LABELPOOL))
zlog_debug(
"%s: Requesting label from LP for %pFX",
__func__, p);
-
- /* bgp_reg_for_label_callback() will call back
- * __func__ when it gets a label from the pool.
- * This means we'll never register FECs without
- * valid labels.
+ /* bgp_reg_for_label_callback() will deal with
+ * fec registration when it gets a label from
+ * the pool. This means we'll never register
+ * FECs withoutvalid labels.
*/
- bgp_lp_get(LP_TYPE_BGP_LU, pi,
- bgp_reg_for_label_callback);
+ bgp_lp_get(LP_TYPE_BGP_LU, dest,
+ bgp_reg_for_label_callback);
return;
}
}
+ } else {
+ UNSET_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED);
+ bgp_lp_release(LP_TYPE_BGP_LU, dest,
+ label_pton(&dest->local_label));
}
- /* Check socket. */
- if (!zclient || zclient->sock < 0)
- return;
-
- /* If the route node has a local_label assigned or the
- * path node has an MPLS SR label index allowing zebra to
- * derive the label, proceed with registration. */
- s = zclient->obuf;
- stream_reset(s);
- command = (reg) ? ZEBRA_FEC_REGISTER : ZEBRA_FEC_UNREGISTER;
- zclient_create_header(s, command, VRF_DEFAULT);
- flags_pos = stream_get_endp(s); /* save position of 'flags' */
- stream_putw(s, flags); /* initial flags */
- stream_putw(s, PREFIX_FAMILY(p));
- stream_put_prefix(s, p);
- if (reg) {
- if (have_label_to_reg) {
- flags |= ZEBRA_FEC_REGISTER_LABEL;
- stream_putl(s, label_pton(local_label));
- } else if (with_label_index) {
- flags |= ZEBRA_FEC_REGISTER_LABEL_INDEX;
- stream_putl(s, pi->attr->label_index);
- }
- SET_FLAG(dest->flags, BGP_NODE_REGISTERED_FOR_LABEL);
- } else
- UNSET_FLAG(dest->flags, BGP_NODE_REGISTERED_FOR_LABEL);
-
- /* Set length and flags */
- stream_putw_at(s, 0, stream_get_endp(s));
-
- /*
- * We only need to write new flags if this is a register
- */
- if (reg)
- stream_putw_at(s, flags_pos, flags);
-
- zclient_send_message(zclient);
+ bgp_send_fec_register_label_msg(
+ dest, reg, with_label_index ? pi->attr->label_index : 0);
}
static int bgp_nlri_get_labels(struct peer *peer, uint8_t *pnt, uint8_t plen,
diff --git a/bgpd/bgp_labelpool.c b/bgpd/bgp_labelpool.c
index e8d8167c35..001340be35 100644
--- a/bgpd/bgp_labelpool.c
+++ b/bgpd/bgp_labelpool.c
@@ -182,18 +182,18 @@ void bgp_lp_init(struct thread_master *master, struct labelpool *pool)
lp->callback_q->spec.max_retries = 0;
}
-/* check if a label callback was for a BGP LU path, and if so, unlock it */
+/* check if a label callback was for a BGP LU node, and if so, unlock it */
static void check_bgp_lu_cb_unlock(struct lp_lcb *lcb)
{
if (lcb->type == LP_TYPE_BGP_LU)
- bgp_path_info_unlock(lcb->labelid);
+ bgp_dest_unlock_node(lcb->labelid);
}
-/* check if a label callback was for a BGP LU path, and if so, lock it */
+/* check if a label callback was for a BGP LU node, and if so, lock it */
static void check_bgp_lu_cb_lock(struct lp_lcb *lcb)
{
if (lcb->type == LP_TYPE_BGP_LU)
- bgp_path_info_lock(lcb->labelid);
+ bgp_dest_lock_node(lcb->labelid);
}
void bgp_lp_finish(void)
@@ -356,7 +356,7 @@ void bgp_lp_get(
q->labelid = lcb->labelid;
q->allocated = true;
- /* if this is a LU request, lock path info before queueing */
+ /* if this is a LU request, lock node before queueing */
check_bgp_lu_cb_lock(lcb);
work_queue_add(lp->callback_q, q);
@@ -384,7 +384,7 @@ void bgp_lp_get(
sizeof(struct lp_fifo));
lf->lcb = *lcb;
- /* if this is a LU request, lock path info before queueing */
+ /* if this is a LU request, lock node before queueing */
check_bgp_lu_cb_lock(lcb);
lp_fifo_add_tail(&lp->requests, lf);
@@ -394,7 +394,7 @@ void bgp_lp_get(
return;
if (zclient_send_get_label_chunk(zclient, 0, LP_CHUNK_SIZE,
MPLS_LABEL_BASE_ANY)
- == ZCLIENT_SEND_FAILURE)
+ != ZCLIENT_SEND_FAILURE)
lp->pending_count += LP_CHUNK_SIZE;
}
}
@@ -461,6 +461,9 @@ void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last)
zlog_debug("%s: labelid %p: request no longer in effect",
__func__, labelid);
}
+ /* if this was a BGP_LU request, unlock node
+ */
+ check_bgp_lu_cb_unlock(lcb);
goto finishedrequest;
}
@@ -472,7 +475,7 @@ void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last)
__func__, labelid,
lcb->label, lcb->label, lcb);
}
- /* if this was a BGP_LU request, unlock path info node
+ /* if this was a BGP_LU request, unlock node
*/
check_bgp_lu_cb_unlock(lcb);
@@ -538,6 +541,7 @@ void bgp_lp_event_zebra_up(void)
struct lp_lcb *lcb;
int lm_init_ok;
+ lp->reconnect_count++;
/*
* Get label chunk allocation request dispatched to zebra
*/
@@ -607,3 +611,371 @@ void bgp_lp_event_zebra_up(void)
skiplist_delete_first(lp->inuse);
}
}
+
+DEFUN(show_bgp_labelpool_summary, show_bgp_labelpool_summary_cmd,
+ "show bgp labelpool summary [json]",
+ SHOW_STR BGP_STR
+ "BGP Labelpool information\n"
+ "BGP Labelpool summary\n" JSON_STR)
+{
+ bool uj = use_json(argc, argv);
+ json_object *json = NULL;
+
+ if (!lp) {
+ if (uj)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "No existing BGP labelpool\n");
+ return (CMD_WARNING);
+ }
+
+ if (uj) {
+ json = json_object_new_object();
+ json_object_int_add(json, "Ledger", skiplist_count(lp->ledger));
+ json_object_int_add(json, "InUse", skiplist_count(lp->inuse));
+ json_object_int_add(json, "Requests",
+ lp_fifo_count(&lp->requests));
+ json_object_int_add(json, "LabelChunks", listcount(lp->chunks));
+ json_object_int_add(json, "Pending", lp->pending_count);
+ json_object_int_add(json, "Reconnects", lp->reconnect_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, "Labelpool Summary\n");
+ vty_out(vty, "-----------------\n");
+ vty_out(vty, "%-13s %d\n",
+ "Ledger:", skiplist_count(lp->ledger));
+ vty_out(vty, "%-13s %d\n", "InUse:", skiplist_count(lp->inuse));
+ vty_out(vty, "%-13s %zu\n",
+ "Requests:", lp_fifo_count(&lp->requests));
+ vty_out(vty, "%-13s %d\n",
+ "LabelChunks:", listcount(lp->chunks));
+ vty_out(vty, "%-13s %d\n", "Pending:", lp->pending_count);
+ vty_out(vty, "%-13s %d\n", "Reconnects:", lp->reconnect_count);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_bgp_labelpool_ledger, show_bgp_labelpool_ledger_cmd,
+ "show bgp labelpool ledger [json]",
+ SHOW_STR BGP_STR
+ "BGP Labelpool information\n"
+ "BGP Labelpool ledger\n" JSON_STR)
+{
+ bool uj = use_json(argc, argv);
+ json_object *json = NULL, *json_elem = NULL;
+ struct lp_lcb *lcb = NULL;
+ struct bgp_dest *dest;
+ void *cursor = NULL;
+ const struct prefix *p;
+ int rc, count;
+
+ if (!lp) {
+ if (uj)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "No existing BGP labelpool\n");
+ return (CMD_WARNING);
+ }
+
+ if (uj) {
+ count = skiplist_count(lp->ledger);
+ if (!count) {
+ vty_out(vty, "{}\n");
+ return CMD_SUCCESS;
+ }
+ json = json_object_new_array();
+ } else {
+ vty_out(vty, "Prefix Label\n");
+ vty_out(vty, "---------------------------\n");
+ }
+
+ for (rc = skiplist_next(lp->ledger, (void **)&dest, (void **)&lcb,
+ &cursor);
+ !rc; rc = skiplist_next(lp->ledger, (void **)&dest, (void **)&lcb,
+ &cursor)) {
+ if (uj) {
+ json_elem = json_object_new_object();
+ json_object_array_add(json, json_elem);
+ }
+ switch (lcb->type) {
+ case LP_TYPE_BGP_LU:
+ if (!CHECK_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED))
+ if (uj) {
+ json_object_string_add(
+ json_elem, "prefix", "INVALID");
+ json_object_int_add(json_elem, "label",
+ lcb->label);
+ } else
+ vty_out(vty, "%-18s %u\n",
+ "INVALID", lcb->label);
+ else {
+ char buf[PREFIX2STR_BUFFER];
+ p = bgp_dest_get_prefix(dest);
+ prefix2str(p, buf, sizeof(buf));
+ if (uj) {
+ json_object_string_add(json_elem,
+ "prefix", buf);
+ json_object_int_add(json_elem, "label",
+ lcb->label);
+ } else
+ vty_out(vty, "%-18s %u\n", buf,
+ lcb->label);
+ }
+ break;
+ case LP_TYPE_VRF:
+ if (uj) {
+ json_object_string_add(json_elem, "prefix",
+ "VRF");
+ json_object_int_add(json_elem, "label",
+ lcb->label);
+ } else
+ vty_out(vty, "%-18s %u\n", "VRF",
+ lcb->label);
+
+ break;
+ }
+ }
+ if (uj) {
+ vty_out(vty, "%s\n",
+ json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_bgp_labelpool_inuse, show_bgp_labelpool_inuse_cmd,
+ "show bgp labelpool inuse [json]",
+ SHOW_STR BGP_STR
+ "BGP Labelpool information\n"
+ "BGP Labelpool inuse\n" JSON_STR)
+{
+ bool uj = use_json(argc, argv);
+ json_object *json = NULL, *json_elem = NULL;
+ struct bgp_dest *dest;
+ mpls_label_t label;
+ struct lp_lcb *lcb;
+ void *cursor = NULL;
+ const struct prefix *p;
+ int rc, count;
+
+ if (!lp) {
+ vty_out(vty, "No existing BGP labelpool\n");
+ return (CMD_WARNING);
+ }
+ if (!lp) {
+ if (uj)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "No existing BGP labelpool\n");
+ return (CMD_WARNING);
+ }
+
+ if (uj) {
+ count = skiplist_count(lp->inuse);
+ if (!count) {
+ vty_out(vty, "{}\n");
+ return CMD_SUCCESS;
+ }
+ json = json_object_new_array();
+ } else {
+ vty_out(vty, "Prefix Label\n");
+ vty_out(vty, "---------------------------\n");
+ }
+ for (rc = skiplist_next(lp->inuse, (void **)&label, (void **)&dest,
+ &cursor);
+ !rc; rc = skiplist_next(lp->ledger, (void **)&label,
+ (void **)&dest, &cursor)) {
+ if (skiplist_search(lp->ledger, dest, (void **)&lcb))
+ continue;
+
+ if (uj) {
+ json_elem = json_object_new_object();
+ json_object_array_add(json, json_elem);
+ }
+
+ switch (lcb->type) {
+ case LP_TYPE_BGP_LU:
+ if (!CHECK_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED))
+ if (uj) {
+ json_object_string_add(
+ json_elem, "prefix", "INVALID");
+ json_object_int_add(json_elem, "label",
+ label);
+ } else
+ vty_out(vty, "INVALID %u\n",
+ label);
+ else {
+ char buf[PREFIX2STR_BUFFER];
+ p = bgp_dest_get_prefix(dest);
+ prefix2str(p, buf, sizeof(buf));
+ if (uj) {
+ json_object_string_add(json_elem,
+ "prefix", buf);
+ json_object_int_add(json_elem, "label",
+ label);
+ } else
+ vty_out(vty, "%-18s %u\n", buf,
+ label);
+ }
+ break;
+ case LP_TYPE_VRF:
+ if (uj) {
+ json_object_string_add(json_elem, "prefix",
+ "VRF");
+ json_object_int_add(json_elem, "label", label);
+ } else
+ vty_out(vty, "%-18s %u\n", "VRF",
+ label);
+ break;
+ }
+ }
+ if (uj) {
+ vty_out(vty, "%s\n",
+ json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_bgp_labelpool_requests, show_bgp_labelpool_requests_cmd,
+ "show bgp labelpool requests [json]",
+ SHOW_STR BGP_STR
+ "BGP Labelpool information\n"
+ "BGP Labelpool requests\n" JSON_STR)
+{
+ bool uj = use_json(argc, argv);
+ json_object *json = NULL, *json_elem = NULL;
+ struct bgp_dest *dest;
+ const struct prefix *p;
+ char buf[PREFIX2STR_BUFFER];
+ struct lp_fifo *item, *next;
+ int count;
+
+ if (!lp) {
+ if (uj)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "No existing BGP labelpool\n");
+ return (CMD_WARNING);
+ }
+
+ if (uj) {
+ count = lp_fifo_count(&lp->requests);
+ if (!count) {
+ vty_out(vty, "{}\n");
+ return CMD_SUCCESS;
+ }
+ json = json_object_new_array();
+ } else {
+ vty_out(vty, "Prefix \n");
+ vty_out(vty, "----------------\n");
+ }
+
+ for (item = lp_fifo_first(&lp->requests); item; item = next) {
+ next = lp_fifo_next_safe(&lp->requests, item);
+ dest = item->lcb.labelid;
+ if (uj) {
+ json_elem = json_object_new_object();
+ json_object_array_add(json, json_elem);
+ }
+ switch (item->lcb.type) {
+ case LP_TYPE_BGP_LU:
+ if (!CHECK_FLAG(dest->flags,
+ BGP_NODE_LABEL_REQUESTED)) {
+ if (uj)
+ json_object_string_add(
+ json_elem, "prefix", "INVALID");
+ else
+ vty_out(vty, "INVALID\n");
+ } else {
+ p = bgp_dest_get_prefix(dest);
+ prefix2str(p, buf, sizeof(buf));
+ if (uj)
+ json_object_string_add(json_elem,
+ "prefix", buf);
+ else
+ vty_out(vty, "%-18s\n", buf);
+ }
+ break;
+ case LP_TYPE_VRF:
+ if (uj)
+ json_object_string_add(json_elem, "prefix",
+ "VRF");
+ else
+ vty_out(vty, "VRF\n");
+ break;
+ }
+ }
+ if (uj) {
+ vty_out(vty, "%s\n",
+ json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_bgp_labelpool_chunks, show_bgp_labelpool_chunks_cmd,
+ "show bgp labelpool chunks [json]",
+ SHOW_STR BGP_STR
+ "BGP Labelpool information\n"
+ "BGP Labelpool chunks\n" JSON_STR)
+{
+ bool uj = use_json(argc, argv);
+ json_object *json = NULL, *json_elem;
+ struct listnode *node;
+ struct lp_chunk *chunk;
+ int count;
+
+ if (!lp) {
+ if (uj)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "No existing BGP labelpool\n");
+ return (CMD_WARNING);
+ }
+
+ if (uj) {
+ count = listcount(lp->chunks);
+ if (!count) {
+ vty_out(vty, "{}\n");
+ return CMD_SUCCESS;
+ }
+ json = json_object_new_array();
+ } else {
+ vty_out(vty, "First Last\n");
+ vty_out(vty, "--------------\n");
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(lp->chunks, node, chunk)) {
+ if (uj) {
+ json_elem = json_object_new_object();
+ json_object_array_add(json, json_elem);
+ json_object_int_add(json_elem, "first", chunk->first);
+ json_object_int_add(json_elem, "last", chunk->last);
+ } else
+ vty_out(vty, "%-10u %-10u\n", chunk->first,
+ chunk->last);
+ }
+ if (uj) {
+ vty_out(vty, "%s\n",
+ json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
+ return CMD_SUCCESS;
+}
+
+void bgp_lp_vty_init(void)
+{
+ install_element(VIEW_NODE, &show_bgp_labelpool_summary_cmd);
+ install_element(VIEW_NODE, &show_bgp_labelpool_ledger_cmd);
+ install_element(VIEW_NODE, &show_bgp_labelpool_inuse_cmd);
+ install_element(VIEW_NODE, &show_bgp_labelpool_requests_cmd);
+ install_element(VIEW_NODE, &show_bgp_labelpool_chunks_cmd);
+}
diff --git a/bgpd/bgp_labelpool.h b/bgpd/bgp_labelpool.h
index eaa3fce20b..d9f64acfe4 100644
--- a/bgpd/bgp_labelpool.h
+++ b/bgpd/bgp_labelpool.h
@@ -40,6 +40,7 @@ struct labelpool {
struct lp_fifo_head requests; /* blocked on zebra */
struct work_queue *callback_q;
uint32_t pending_count; /* requested from zebra */
+ uint32_t reconnect_count; /* zebra reconnections */
};
extern void bgp_lp_init(struct thread_master *master, struct labelpool *pool);
@@ -50,5 +51,6 @@ extern void bgp_lp_release(int type, void *labelid, mpls_label_t label);
extern void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last);
extern void bgp_lp_event_zebra_down(void);
extern void bgp_lp_event_zebra_up(void);
+extern void bgp_lp_vty_init(void);
#endif /* _FRR_BGP_LABELPOOL_H */
diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c
index f9aac35d05..0013eb2df2 100644
--- a/bgpd/bgp_memory.c
+++ b/bgpd/bgp_memory.c
@@ -98,6 +98,7 @@ DEFINE_MTYPE(BGPD, PEER_UPDATE_SOURCE, "BGP peer update interface")
DEFINE_MTYPE(BGPD, PEER_CONF_IF, "BGP peer config interface")
DEFINE_MTYPE(BGPD, BGP_DAMP_INFO, "Dampening info")
DEFINE_MTYPE(BGPD, BGP_DAMP_ARRAY, "BGP Dampening array")
+DEFINE_MTYPE(BGPD, BGP_DAMP_REUSELIST, "BGP Dampening reuse list")
DEFINE_MTYPE(BGPD, BGP_REGEXP, "BGP regexp")
DEFINE_MTYPE(BGPD, BGP_AGGREGATE, "BGP aggregate")
DEFINE_MTYPE(BGPD, BGP_ADDR, "BGP own address")
diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h
index a95d9ef931..63998e95ac 100644
--- a/bgpd/bgp_memory.h
+++ b/bgpd/bgp_memory.h
@@ -94,6 +94,7 @@ DECLARE_MTYPE(PEER_UPDATE_SOURCE)
DECLARE_MTYPE(PEER_CONF_IF)
DECLARE_MTYPE(BGP_DAMP_INFO)
DECLARE_MTYPE(BGP_DAMP_ARRAY)
+DECLARE_MTYPE(BGP_DAMP_REUSELIST)
DECLARE_MTYPE(BGP_REGEXP)
DECLARE_MTYPE(BGP_AGGREGATE)
DECLARE_MTYPE(BGP_ADDR)
diff --git a/bgpd/bgp_nb_config.c b/bgpd/bgp_nb_config.c
index bee9633297..f6a138ba45 100644
--- a/bgpd/bgp_nb_config.c
+++ b/bgpd/bgp_nb_config.c
@@ -894,6 +894,9 @@ int bgp_global_route_selection_options_allow_multiple_as_modify(
"../multi-path-as-set")) {
SET_FLAG(bgp->flags,
BGP_FLAG_MULTIPATH_RELAX_AS_SET);
+ } else {
+ UNSET_FLAG(bgp->flags,
+ BGP_FLAG_MULTIPATH_RELAX_AS_SET);
}
} else {
UNSET_FLAG(bgp->flags, BGP_FLAG_ASPATH_MULTIPATH_RELAX);
@@ -923,15 +926,10 @@ int bgp_global_route_selection_options_multi_path_as_set_modify(
return NB_OK;
case NB_EV_APPLY:
bgp = nb_running_get_entry(args->dnode, NULL, true);
-
- if (!CHECK_FLAG(bgp->flags, BGP_FLAG_MULTIPATH_RELAX_AS_SET)) {
+ if (yang_dnode_get_bool(args->dnode, NULL))
SET_FLAG(bgp->flags, BGP_FLAG_MULTIPATH_RELAX_AS_SET);
-
- } else
- zlog_debug(
- "%s multi-path-as-set as part of allow-multiple-as modify cb.",
- __func__);
-
+ else
+ UNSET_FLAG(bgp->flags, BGP_FLAG_MULTIPATH_RELAX_AS_SET);
break;
}
@@ -9556,7 +9554,7 @@ static int bgp_global_afi_safi_ip_unicast_vpn_config_rd_destroy(
bgp = nb_running_get_entry(af_dnode, NULL, true);
rd_str = yang_dnode_get_string(args->dnode, NULL);
- if (str2prefix_rd(rd_str, &prd)) {
+ if (!str2prefix_rd(rd_str, &prd)) {
snprintf(args->errmsg, args->errmsg_len, "Malformed rd %s \n",
rd_str);
return NB_ERR_INCONSISTENCY;
diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c
index 6cfcb9cc3d..533518cf93 100644
--- a/bgpd/bgp_open.c
+++ b/bgpd/bgp_open.c
@@ -760,6 +760,7 @@ static const struct message capcode_str[] = {
{CAPABILITY_CODE_REFRESH_OLD, "Route Refresh (Old)"},
{CAPABILITY_CODE_ORF_OLD, "ORF (Old)"},
{CAPABILITY_CODE_FQDN, "FQDN"},
+ {CAPABILITY_CODE_ENHANCED_RR, "Enhanced Route Refresh"},
{0}};
/* Minimum sizes for length field of each cap (so not inc. the header) */
@@ -776,6 +777,7 @@ static const size_t cap_minsizes[] = {
[CAPABILITY_CODE_REFRESH_OLD] = CAPABILITY_CODE_REFRESH_LEN,
[CAPABILITY_CODE_ORF_OLD] = CAPABILITY_CODE_ORF_LEN,
[CAPABILITY_CODE_FQDN] = CAPABILITY_CODE_MIN_FQDN_LEN,
+ [CAPABILITY_CODE_ENHANCED_RR] = CAPABILITY_CODE_ENHANCED_LEN,
};
/* value the capability must be a multiple of.
@@ -796,6 +798,7 @@ static const size_t cap_modsizes[] = {
[CAPABILITY_CODE_REFRESH_OLD] = 1,
[CAPABILITY_CODE_ORF_OLD] = 1,
[CAPABILITY_CODE_FQDN] = 1,
+ [CAPABILITY_CODE_ENHANCED_RR] = 1,
};
/**
@@ -863,6 +866,7 @@ static int bgp_capability_parse(struct peer *peer, size_t length,
case CAPABILITY_CODE_DYNAMIC_OLD:
case CAPABILITY_CODE_ENHE:
case CAPABILITY_CODE_FQDN:
+ case CAPABILITY_CODE_ENHANCED_RR:
/* Check length. */
if (caphdr.length < cap_minsizes[caphdr.code]) {
zlog_info(
@@ -913,10 +917,13 @@ static int bgp_capability_parse(struct peer *peer, size_t length,
ret = 0; /* Don't return error for this */
}
} break;
+ case CAPABILITY_CODE_ENHANCED_RR:
case CAPABILITY_CODE_REFRESH:
case CAPABILITY_CODE_REFRESH_OLD: {
/* BGP refresh capability */
- if (caphdr.code == CAPABILITY_CODE_REFRESH_OLD)
+ if (caphdr.code == CAPABILITY_CODE_ENHANCED_RR)
+ SET_FLAG(peer->cap, PEER_CAP_ENHANCED_RR_RCV);
+ else if (caphdr.code == CAPABILITY_CODE_REFRESH_OLD)
SET_FLAG(peer->cap, PEER_CAP_REFRESH_OLD_RCV);
else
SET_FLAG(peer->cap, PEER_CAP_REFRESH_NEW_RCV);
@@ -1450,6 +1457,13 @@ void bgp_open_capability(struct stream *s, struct peer *peer)
stream_putc(s, CAPABILITY_CODE_REFRESH);
stream_putc(s, CAPABILITY_CODE_REFRESH_LEN);
+ /* Enhanced Route Refresh. */
+ SET_FLAG(peer->cap, PEER_CAP_ENHANCED_RR_ADV);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ stream_putc(s, CAPABILITY_CODE_ENHANCED_LEN + 2);
+ stream_putc(s, CAPABILITY_CODE_ENHANCED_RR);
+ stream_putc(s, CAPABILITY_CODE_ENHANCED_LEN);
+
/* AS4 */
SET_FLAG(peer->cap, PEER_CAP_AS4_ADV);
stream_putc(s, BGP_OPEN_OPT_CAP);
diff --git a/bgpd/bgp_open.h b/bgpd/bgp_open.h
index 5250a68581..471ac05c7c 100644
--- a/bgpd/bgp_open.h
+++ b/bgpd/bgp_open.h
@@ -49,6 +49,7 @@ struct graceful_restart_af {
#define CAPABILITY_CODE_DYNAMIC_OLD 66 /* Dynamic Capability, deprecated since 2003 */
#define CAPABILITY_CODE_DYNAMIC 67 /* Dynamic Capability */
#define CAPABILITY_CODE_ADDPATH 69 /* Addpath Capability */
+#define CAPABILITY_CODE_ENHANCED_RR 70 /* Enhanced Route Refresh capability */
#define CAPABILITY_CODE_FQDN 73 /* Advertise hostname capability */
#define CAPABILITY_CODE_ENHE 5 /* Extended Next Hop Encoding */
#define CAPABILITY_CODE_REFRESH_OLD 128 /* Route Refresh Capability(cisco) */
@@ -63,6 +64,7 @@ struct graceful_restart_af {
#define CAPABILITY_CODE_ADDPATH_LEN 4
#define CAPABILITY_CODE_ENHE_LEN 6 /* NRLI AFI = 2, SAFI = 2, Nexthop AFI = 2 */
#define CAPABILITY_CODE_MIN_FQDN_LEN 2
+#define CAPABILITY_CODE_ENHANCED_LEN 0
#define CAPABILITY_CODE_ORF_LEN 5
/* Cooperative Route Filtering Capability. */
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index b2b9e04bc3..b7ecd8a49b 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -444,13 +444,45 @@ int bgp_generate_updgrp_packets(struct thread *thread)
* yet.
*/
if (!next_pkt || !next_pkt->buffer) {
- /* Make sure we supress BGP UPDATES
- * for normal processing later again.
- */
- if (!paf->t_announce_route)
+ if (!paf->t_announce_route) {
+ /* Make sure we supress BGP UPDATES
+ * for normal processing later again.
+ */
UNSET_FLAG(paf->subgroup->sflags,
SUBGRP_STATUS_FORCE_UPDATES);
+ /* If route-refresh BoRR message was
+ * already sent and we are done with
+ * re-announcing tables for a decent
+ * afi/safi, we ready to send
+ * EoRR request.
+ */
+ if (CHECK_FLAG(
+ peer->af_sflags[afi][safi],
+ PEER_STATUS_BORR_SEND)) {
+ bgp_route_refresh_send(
+ peer, afi, safi, 0, 0,
+ 0,
+ BGP_ROUTE_REFRESH_EORR);
+
+ SET_FLAG(peer->af_sflags[afi]
+ [safi],
+ PEER_STATUS_EORR_SEND);
+ UNSET_FLAG(
+ peer->af_sflags[afi]
+ [safi],
+ PEER_STATUS_BORR_SEND);
+
+ if (bgp_debug_neighbor_events(
+ peer))
+ zlog_debug(
+ "%s sending route-refresh (EoRR) for %s/%s",
+ peer->host,
+ afi2str(afi),
+ safi2str(safi));
+ }
+ }
+
if (CHECK_FLAG(peer->cap,
PEER_CAP_RESTART_RCV)) {
if (!(PAF_SUBGRP(paf))->t_coalesce
@@ -816,7 +848,7 @@ void bgp_notify_send(struct peer *peer, uint8_t code, uint8_t sub_code)
*/
void bgp_route_refresh_send(struct peer *peer, afi_t afi, safi_t safi,
uint8_t orf_type, uint8_t when_to_refresh,
- int remove)
+ int remove, uint8_t subtype)
{
struct stream *s;
struct bgp_filter *filter;
@@ -842,7 +874,10 @@ void bgp_route_refresh_send(struct peer *peer, afi_t afi, safi_t safi,
/* Encode Route Refresh message. */
stream_putw(s, pkt_afi);
- stream_putc(s, 0);
+ if (subtype)
+ stream_putc(s, subtype);
+ else
+ stream_putc(s, 0);
stream_putc(s, pkt_safi);
if (orf_type == ORF_TYPE_PREFIX || orf_type == ORF_TYPE_PREFIX_OLD)
@@ -1460,6 +1495,29 @@ static int bgp_keepalive_receive(struct peer *peer, bgp_size_t size)
return Receive_KEEPALIVE_message;
}
+static int bgp_refresh_stalepath_timer_expire(struct thread *thread)
+{
+ struct peer_af *paf;
+
+ paf = THREAD_ARG(thread);
+
+ afi_t afi = paf->afi;
+ safi_t safi = paf->safi;
+ struct peer *peer = paf->peer;
+
+ peer->t_refresh_stalepath = NULL;
+
+ if (peer->nsf[afi][safi])
+ bgp_clear_stale_route(peer, afi, safi);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s: route-refresh (BoRR) timer for %s/%s expired",
+ peer->host, afi2str(afi), safi2str(safi));
+
+ bgp_timer_set(peer);
+
+ return 0;
+}
/**
* Process BGP UPDATE message for peer.
@@ -1888,6 +1946,9 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size)
struct peer_af *paf;
struct update_group *updgrp;
struct peer *updgrp_peer;
+ uint8_t subtype;
+ bgp_size_t msg_length =
+ size - (BGP_MSG_ROUTE_REFRESH_MIN_SIZE - BGP_HEADER_SIZE);
/* If peer does not have the capability, send notification. */
if (!CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_ADV)) {
@@ -1915,14 +1976,9 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size)
/* Parse packet. */
pkt_afi = stream_getw(s);
- (void)stream_getc(s);
+ subtype = stream_getc(s);
pkt_safi = stream_getc(s);
- if (bgp_debug_update(peer, NULL, NULL, 0))
- zlog_debug("%s rcvd REFRESH_REQ for afi/safi: %s/%s",
- peer->host, iana_afi2str(pkt_afi),
- iana_safi2str(pkt_safi));
-
/* Convert AFI, SAFI to internal values and check. */
if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) {
zlog_info(
@@ -1938,8 +1994,34 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size)
uint8_t orf_type;
uint16_t orf_len;
- if (size - (BGP_MSG_ROUTE_REFRESH_MIN_SIZE - BGP_HEADER_SIZE)
- < 5) {
+ if (subtype) {
+ /* If the length, excluding the fixed-size message
+ * header, of the received ROUTE-REFRESH message with
+ * Message Subtype 1 and 2 is not 4, then the BGP
+ * speaker MUST send a NOTIFICATION message with the
+ * Error Code of "ROUTE-REFRESH Message Error" and the
+ * subcode of "Invalid Message Length".
+ */
+ if (msg_length != 4) {
+ zlog_err(
+ "%s Enhanced Route Refresh message length error",
+ peer->host);
+ bgp_notify_send(
+ peer, BGP_NOTIFY_ROUTE_REFRESH_ERR,
+ BGP_NOTIFY_ROUTE_REFRESH_INVALID_MSG_LEN);
+ }
+
+ /* When the BGP speaker receives a ROUTE-REFRESH message
+ * with a "Message Subtype" field other than 0, 1, or 2,
+ * it MUST ignore the received ROUTE-REFRESH message.
+ */
+ if (subtype > 2)
+ zlog_err(
+ "%s Enhanced Route Refresh invalid subtype",
+ peer->host);
+ }
+
+ if (msg_length < 5) {
zlog_info("%s ORF route refresh length error",
peer->host);
bgp_notify_send(peer, BGP_NOTIFY_CEASE,
@@ -2139,6 +2221,124 @@ static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size)
SUBGRP_STATUS_DEFAULT_ORIGINATE);
}
+ if (subtype == BGP_ROUTE_REFRESH_BORR) {
+ /* A BGP speaker that has received the Graceful Restart
+ * Capability from its neighbor MUST ignore any BoRRs for
+ * an <AFI, SAFI> from the neighbor before the speaker
+ * receives the EoR for the given <AFI, SAFI> from the
+ * neighbor.
+ */
+ if (CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)
+ && !CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_EOR_RECEIVED)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s rcvd route-refresh (BoRR) for %s/%s before EoR",
+ peer->host, afi2str(afi),
+ safi2str(safi));
+ return BGP_PACKET_NOOP;
+ }
+
+ if (peer->t_refresh_stalepath) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s rcvd route-refresh (BoRR) for %s/%s, whereas BoRR already received",
+ peer->host, afi2str(afi),
+ safi2str(safi));
+ return BGP_PACKET_NOOP;
+ }
+
+ SET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_BORR_RECEIVED);
+ UNSET_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_EORR_RECEIVED);
+
+ /* When a BGP speaker receives a BoRR message from
+ * a peer, it MUST mark all the routes with the given
+ * Address Family Identifier and Subsequent Address
+ * Family Identifier, <AFI, SAFI> [RFC2918], from
+ * that peer as stale.
+ */
+ if (peer_active_nego(peer)) {
+ SET_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_ENHANCED_REFRESH);
+ bgp_set_stale_route(peer, afi, safi);
+ }
+
+ if (peer->status == Established)
+ thread_add_timer(bm->master,
+ bgp_refresh_stalepath_timer_expire,
+ paf, peer->bgp->stalepath_time,
+ &peer->t_refresh_stalepath);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s rcvd route-refresh (BoRR) for %s/%s, triggering timer for %u seconds",
+ peer->host, afi2str(afi), safi2str(safi),
+ peer->bgp->stalepath_time);
+ } else if (subtype == BGP_ROUTE_REFRESH_EORR) {
+ if (!peer->t_refresh_stalepath) {
+ zlog_err(
+ "%s rcvd route-refresh (EoRR) for %s/%s, whereas no BoRR received",
+ peer->host, afi2str(afi), safi2str(safi));
+ return BGP_PACKET_NOOP;
+ }
+
+ BGP_TIMER_OFF(peer->t_refresh_stalepath);
+
+ SET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_EORR_RECEIVED);
+ UNSET_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_BORR_RECEIVED);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s rcvd route-refresh (EoRR) for %s/%s, stopping BoRR timer",
+ peer->host, afi2str(afi), safi2str(safi));
+
+ if (peer->nsf[afi][safi])
+ bgp_clear_stale_route(peer, afi, safi);
+ } else {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s rcvd route-refresh (REQUEST) for %s/%s",
+ peer->host, afi2str(afi), safi2str(safi));
+
+ /* In response to a "normal route refresh request" from the
+ * peer, the speaker MUST send a BoRR message.
+ */
+ if (CHECK_FLAG(peer->cap, PEER_CAP_ENHANCED_RR_RCV)) {
+ /* For a BGP speaker that supports the BGP Graceful
+ * Restart, it MUST NOT send a BoRR for an <AFI, SAFI>
+ * to a neighbor before it sends the EoR for the
+ * <AFI, SAFI> to the neighbor.
+ */
+ if (!CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_EOR_SEND)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s rcvd route-refresh (REQUEST) for %s/%s before EoR",
+ peer->host, afi2str(afi),
+ safi2str(safi));
+ return BGP_PACKET_NOOP;
+ }
+
+ bgp_route_refresh_send(peer, afi, safi, 0, 0, 0,
+ BGP_ROUTE_REFRESH_BORR);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s sending route-refresh (BoRR) for %s/%s",
+ peer->host, afi2str(afi),
+ safi2str(safi));
+
+ /* Set flag Ready-To-Send to know when we can send EoRR
+ * message.
+ */
+ SET_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_BORR_SEND);
+ UNSET_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_EORR_SEND);
+ }
+ }
+
/* Perform route refreshment to the peer */
bgp_announce_route(peer, afi, safi);
diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h
index e83f7d950c..525859a2da 100644
--- a/bgpd/bgp_packet.h
+++ b/bgpd/bgp_packet.h
@@ -62,8 +62,9 @@ extern void bgp_open_send(struct peer *);
extern void bgp_notify_send(struct peer *, uint8_t, uint8_t);
extern void bgp_notify_send_with_data(struct peer *, uint8_t, uint8_t,
uint8_t *, size_t);
-extern void bgp_route_refresh_send(struct peer *, afi_t, safi_t, uint8_t,
- uint8_t, int);
+extern void bgp_route_refresh_send(struct peer *peer, afi_t afi, safi_t safi,
+ uint8_t orf_type, uint8_t when_to_refresh,
+ int remove, uint8_t subtype);
extern void bgp_capability_send(struct peer *, afi_t, safi_t, int, int);
extern int bgp_capability_receive(struct peer *, bgp_size_t);
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 7e1f7df533..c4ab223b7f 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -208,9 +208,6 @@ void bgp_path_info_extra_free(struct bgp_path_info_extra **extra)
return;
e = *extra;
- if (e->damp_info)
- bgp_damp_info_free(e->damp_info, 0, e->damp_info->afi,
- e->damp_info->safi);
e->damp_info = NULL;
if (e->parent) {
@@ -2755,7 +2752,10 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest,
== BGP_ROUTE_REDISTRIBUTE) {
if (CHECK_FLAG(
dest->flags,
- BGP_NODE_REGISTERED_FOR_LABEL))
+ BGP_NODE_REGISTERED_FOR_LABEL)
+ || CHECK_FLAG(
+ dest->flags,
+ BGP_NODE_LABEL_REQUESTED))
bgp_unregister_for_label(dest);
label_ntop(MPLS_LABEL_IMPLICIT_NULL, 1,
&dest->local_label);
@@ -2765,10 +2765,13 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest,
new_select);
}
} else if (CHECK_FLAG(dest->flags,
- BGP_NODE_REGISTERED_FOR_LABEL)) {
+ BGP_NODE_REGISTERED_FOR_LABEL)
+ || CHECK_FLAG(dest->flags,
+ BGP_NODE_LABEL_REQUESTED)) {
bgp_unregister_for_label(dest);
}
- } else if (CHECK_FLAG(dest->flags, BGP_NODE_REGISTERED_FOR_LABEL)) {
+ } else if (CHECK_FLAG(dest->flags, BGP_NODE_REGISTERED_FOR_LABEL)
+ || CHECK_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED)) {
bgp_unregister_for_label(dest);
}
@@ -3325,14 +3328,16 @@ static void bgp_rib_withdraw(struct bgp_dest *dest, struct bgp_path_info *pi,
/* apply dampening, if result is suppressed, we'll be retaining
* the bgp_path_info in the RIB for historical reference.
*/
- if (CHECK_FLAG(peer->bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)
- && peer->sort == BGP_PEER_EBGP)
- if ((bgp_damp_withdraw(pi, dest, afi, safi, 0))
- == BGP_DAMP_SUPPRESSED) {
- bgp_aggregate_decrement(peer->bgp, p, pi, afi,
- safi);
- return;
+ if (peer->sort == BGP_PEER_EBGP) {
+ if (get_active_bdc_from_pi(pi, afi, safi)) {
+ if (bgp_damp_withdraw(pi, dest, afi, safi, 0)
+ == BGP_DAMP_SUPPRESSED) {
+ bgp_aggregate_decrement(peer->bgp, p, pi, afi,
+ safi);
+ return;
+ }
}
+ }
#ifdef ENABLE_BGP_VNC
if (safi == SAFI_MPLS_VPN) {
@@ -3757,8 +3762,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
&& (overlay_index_equal(
afi, pi,
evpn == NULL ? NULL : &evpn->gw_ip))) {
- if (CHECK_FLAG(bgp->af_flags[afi][safi],
- BGP_CONFIG_DAMPENING)
+ if (get_active_bdc_from_pi(pi, afi, safi)
&& peer->sort == BGP_PEER_EBGP
&& CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) {
if (bgp_debug_update(peer, p, NULL, 1)) {
@@ -3852,11 +3856,11 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
bgp_aggregate_decrement(bgp, p, pi, afi, safi);
/* Update bgp route dampening information. */
- if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)
+ if (get_active_bdc_from_pi(pi, afi, safi)
&& peer->sort == BGP_PEER_EBGP) {
/* This is implicit withdraw so we should update
- dampening
- information. */
+ * dampening information.
+ */
if (!CHECK_FLAG(pi->flags, BGP_PATH_HISTORY))
bgp_damp_withdraw(pi, dest, afi, safi, 1);
}
@@ -3979,7 +3983,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
#endif
/* Update bgp route dampening information. */
- if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)
+ if (get_active_bdc_from_pi(pi, afi, safi)
&& peer->sort == BGP_PEER_EBGP) {
/* Now we do normal update dampening. */
ret = bgp_damp_update(pi, dest, afi, safi);
@@ -4595,8 +4599,10 @@ static wq_item_status bgp_clear_route_node(struct work_queue *wq, void *data)
continue;
/* graceful restart STALE flag set. */
- if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT)
- && peer->nsf[afi][safi]
+ if (((CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT)
+ && peer->nsf[afi][safi])
+ || CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_ENHANCED_REFRESH))
&& !CHECK_FLAG(pi->flags, BGP_PATH_STALE)
&& !CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE))
bgp_path_info_set_flag(dest, pi, BGP_PATH_STALE);
@@ -4847,7 +4853,7 @@ void bgp_clear_stale_route(struct peer *peer, afi_t afi, safi_t safi)
struct bgp_path_info *pi;
struct bgp_table *table;
- if (safi == SAFI_MPLS_VPN) {
+ if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP || safi == SAFI_EVPN) {
for (dest = bgp_table_top(peer->bgp->rib[afi][safi]); dest;
dest = bgp_route_next(dest)) {
struct bgp_dest *rm;
@@ -4886,6 +4892,81 @@ void bgp_clear_stale_route(struct peer *peer, afi_t afi, safi_t safi)
}
}
+void bgp_set_stale_route(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct bgp_dest *dest, *ndest;
+ struct bgp_path_info *pi;
+ struct bgp_table *table;
+
+ if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP || safi == SAFI_EVPN) {
+ for (dest = bgp_table_top(peer->bgp->rib[afi][safi]); dest;
+ dest = bgp_route_next(dest)) {
+ table = bgp_dest_get_bgp_table_info(dest);
+ if (!table)
+ continue;
+
+ for (ndest = bgp_table_top(table); ndest;
+ ndest = bgp_route_next(ndest)) {
+ for (pi = bgp_dest_get_bgp_path_info(ndest); pi;
+ pi = pi->next) {
+ if (pi->peer != peer)
+ continue;
+
+ if ((CHECK_FLAG(
+ peer->af_sflags[afi][safi],
+ PEER_STATUS_ENHANCED_REFRESH))
+ && !CHECK_FLAG(pi->flags,
+ BGP_PATH_STALE)
+ && !CHECK_FLAG(
+ pi->flags,
+ BGP_PATH_UNUSEABLE)) {
+ if (bgp_debug_neighbor_events(
+ peer))
+ zlog_debug(
+ "%s: route-refresh for %s/%s, marking prefix %pFX as stale",
+ peer->host,
+ afi2str(afi),
+ safi2str(safi),
+ bgp_dest_get_prefix(
+ ndest));
+
+ bgp_path_info_set_flag(
+ ndest, pi,
+ BGP_PATH_STALE);
+ }
+ }
+ }
+ }
+ } else {
+ for (dest = bgp_table_top(peer->bgp->rib[afi][safi]); dest;
+ dest = bgp_route_next(dest)) {
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi;
+ pi = pi->next) {
+ if (pi->peer != peer)
+ continue;
+
+ if ((CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_ENHANCED_REFRESH))
+ && !CHECK_FLAG(pi->flags, BGP_PATH_STALE)
+ && !CHECK_FLAG(pi->flags,
+ BGP_PATH_UNUSEABLE)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s: route-refresh for %s/%s, marking prefix %pFX as stale",
+ peer->host,
+ afi2str(afi),
+ safi2str(safi),
+ bgp_dest_get_prefix(
+ dest));
+
+ bgp_path_info_set_flag(dest, pi,
+ BGP_PATH_STALE);
+ }
+ }
+ }
+ }
+}
+
bool bgp_outbound_policy_exists(struct peer *peer, struct bgp_filter *filter)
{
if (peer->sort == BGP_PEER_IBGP)
@@ -10129,7 +10210,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
}
if (path->extra && path->extra->damp_info)
- bgp_damp_info_vty(vty, path, afi, safi, json_path);
+ bgp_damp_info_vty(vty, bgp, path, afi, safi, json_path);
/* Remote Label */
if (path->extra && bgp_is_valid_label(&path->extra->label[0])
@@ -14079,7 +14160,8 @@ static int bgp_clear_damp_route(struct vty *vty, const char *view_name,
if (pi->extra && pi->extra->damp_info) {
pi_temp = pi->next;
bgp_damp_info_free(
- pi->extra->damp_info,
+ &pi->extra->damp_info,
+ &bgp->damp[afi][safi],
1, afi, safi);
pi = pi_temp;
} else
@@ -14101,7 +14183,8 @@ static int bgp_clear_damp_route(struct vty *vty, const char *view_name,
if (pi->extra && pi->extra->damp_info) {
pi_temp = pi->next;
bgp_damp_info_free(
- pi->extra->damp_info,
+ &pi->extra->damp_info,
+ &bgp->damp[afi][safi],
1, afi, safi);
pi = pi_temp;
} else
@@ -14124,7 +14207,9 @@ DEFUN (clear_ip_bgp_dampening,
BGP_STR
"Clear route flap dampening information\n")
{
- bgp_damp_info_clean(AFI_IP, SAFI_UNICAST);
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ bgp_damp_info_clean(&bgp->damp[AFI_IP][SAFI_UNICAST], AFI_IP,
+ SAFI_UNICAST);
return CMD_SUCCESS;
}
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
index ec08eb9c65..bdbf4743ab 100644
--- a/bgpd/bgp_route.h
+++ b/bgpd/bgp_route.h
@@ -575,6 +575,7 @@ extern void bgp_clear_route(struct peer *, afi_t, safi_t);
extern void bgp_clear_route_all(struct peer *);
extern void bgp_clear_adj_in(struct peer *, afi_t, safi_t);
extern void bgp_clear_stale_route(struct peer *, afi_t, safi_t);
+extern void bgp_set_stale_route(struct peer *peer, afi_t afi, safi_t safi);
extern bool bgp_outbound_policy_exists(struct peer *, struct bgp_filter *);
extern bool bgp_inbound_policy_exists(struct peer *, struct bgp_filter *);
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index 637eaca397..0f4f26e3ee 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -3503,8 +3503,9 @@ static void bgp_route_map_process_peer(const char *rmap_name,
zlog_debug(
"Processing route_map %s update on peer %s (inbound, route-refresh)",
rmap_name, peer->host);
- bgp_route_refresh_send(peer, afi, safi, 0, 0,
- 0);
+ bgp_route_refresh_send(
+ peer, afi, safi, 0, 0, 0,
+ BGP_ROUTE_REFRESH_NORMAL);
}
}
}
diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h
index 738d41ee6d..68b460149c 100644
--- a/bgpd/bgp_table.h
+++ b/bgpd/bgp_table.h
@@ -104,6 +104,7 @@ struct bgp_node {
#define BGP_NODE_SELECT_DEFER (1 << 4)
#define BGP_NODE_FIB_INSTALL_PENDING (1 << 5)
#define BGP_NODE_FIB_INSTALLED (1 << 6)
+#define BGP_NODE_LABEL_REQUESTED (1 << 7)
struct bgp_addpath_node_data tx_addpath;
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 4cdd4d2e62..114a00cc36 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -8896,6 +8896,93 @@ DEFPY(
return CMD_SUCCESS;
}
+DEFPY(neighbor_damp,
+ neighbor_damp_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor dampening [(1-45)$half [(1-20000)$reuse (1-20000)$suppress (1-255)$max]]",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Enable neighbor route-flap dampening\n"
+ "Half-life time for the penalty\n"
+ "Value to start reusing a route\n"
+ "Value to start suppressing a route\n"
+ "Maximum duration to suppress a stable route\n")
+{
+ struct peer *peer = peer_and_group_lookup_vty(vty, neighbor);
+
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+ if (!half)
+ half = DEFAULT_HALF_LIFE;
+ if (!reuse) {
+ reuse = DEFAULT_REUSE;
+ suppress = DEFAULT_SUPPRESS;
+ max = half * 4;
+ }
+ if (suppress < reuse) {
+ vty_out(vty,
+ "Suppress value cannot be less than reuse value\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ bgp_peer_damp_enable(peer, bgp_node_afi(vty), bgp_node_safi(vty),
+ half * 60, reuse, suppress, max * 60);
+ return CMD_SUCCESS;
+}
+
+DEFPY(no_neighbor_damp,
+ no_neighbor_damp_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor dampening [HALF [REUSE SUPPRESS MAX]]",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Enable neighbor route-flap dampening\n"
+ "Half-life time for the penalty\n"
+ "Value to start reusing a route\n"
+ "Value to start suppressing a route\n"
+ "Maximum duration to suppress a stable route\n")
+{
+ struct peer *peer = peer_and_group_lookup_vty(vty, neighbor);
+
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+ bgp_peer_damp_disable(peer, bgp_node_afi(vty), bgp_node_safi(vty));
+ return CMD_SUCCESS;
+}
+
+DEFPY (show_ip_bgp_neighbor_damp_param,
+ show_ip_bgp_neighbor_damp_param_cmd,
+ "show [ip] bgp [<ipv4|ipv6> [unicast]] neighbors <A.B.C.D|X:X::X:X|WORD>$neighbor dampening parameters [json]$json",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_AFI_HELP_STR
+ "Address Family modifier\n"
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Neighbor route-flap dampening information\n"
+ "Display detail of configured dampening parameters\n"
+ JSON_STR)
+{
+ bool use_json = false;
+ int idx = 0;
+ afi_t afi = AFI_IP;
+ safi_t safi = SAFI_UNICAST;
+ struct peer *peer;
+
+ if (argv_find(argv, argc, "ip", &idx))
+ afi = AFI_IP;
+ if (argv_find(argv, argc, "ipv4", &idx))
+ afi = AFI_IP;
+ if (argv_find(argv, argc, "ipv6", &idx))
+ afi = AFI_IP6;
+ peer = peer_and_group_lookup_vty(vty, neighbor);
+ if (!peer)
+ return CMD_WARNING;
+ if (json)
+ use_json = true;
+ bgp_show_peer_dampening_parameters(vty, peer, afi, safi, use_json);
+ return CMD_SUCCESS;
+}
+
static int set_ecom_list(struct vty *vty, int argc, struct cmd_token **argv,
struct ecommunity **list, bool is_rt6)
{
@@ -13153,6 +13240,37 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
"received");
}
+ /* Enhanced Route Refresh */
+ if (CHECK_FLAG(p->cap, PEER_CAP_ENHANCED_RR_ADV)
+ || CHECK_FLAG(p->cap,
+ PEER_CAP_ENHANCED_RR_RCV)) {
+ if (CHECK_FLAG(p->cap,
+ PEER_CAP_ENHANCED_RR_ADV)
+ && CHECK_FLAG(
+ p->cap,
+ PEER_CAP_ENHANCED_RR_RCV))
+ json_object_string_add(
+ json_cap,
+ "enhancedRouteRefresh",
+ "advertisedAndReceived");
+ else if (
+ CHECK_FLAG(
+ p->cap,
+ PEER_CAP_ENHANCED_RR_ADV))
+ json_object_string_add(
+ json_cap,
+ "enhancedRouteRefresh",
+ "advertised");
+ else if (
+ CHECK_FLAG(
+ p->cap,
+ PEER_CAP_ENHANCED_RR_RCV))
+ json_object_string_add(
+ json_cap,
+ "enhancedRouteRefresh",
+ "received");
+ }
+
/* Multiprotocol Extensions */
json_object *json_multi = NULL;
json_multi = json_object_new_object();
@@ -13525,6 +13643,28 @@ static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
vty_out(vty, "\n");
}
+ /* Enhanced Route Refresh */
+ if (CHECK_FLAG(p->cap, PEER_CAP_ENHANCED_RR_ADV)
+ || CHECK_FLAG(p->cap,
+ PEER_CAP_ENHANCED_RR_RCV)) {
+ vty_out(vty,
+ " Enhanced Route Refresh:");
+ if (CHECK_FLAG(
+ p->cap,
+ PEER_CAP_ENHANCED_RR_ADV))
+ vty_out(vty, " advertised");
+ if (CHECK_FLAG(
+ p->cap,
+ PEER_CAP_ENHANCED_RR_RCV))
+ vty_out(vty, " %sreceived",
+ CHECK_FLAG(
+ p->cap,
+ PEER_CAP_REFRESH_ADV)
+ ? "and "
+ : "");
+ vty_out(vty, "\n");
+ }
+
/* Multiprotocol Extensions */
FOREACH_AFI_SAFI (afi, safi)
if (p->afc_adv[afi][safi]
@@ -16947,7 +17087,15 @@ static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi,
/* BGP flag dampening. */
if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING))
- bgp_config_write_damp(vty, afi, safi);
+ bgp_config_write_damp(vty, bgp, afi, safi);
+ for (ALL_LIST_ELEMENTS_RO(bgp->group, node, group))
+ if (peer_af_flag_check(group->conf, afi, safi,
+ PEER_FLAG_CONFIG_DAMPENING))
+ bgp_config_write_peer_damp(vty, group->conf, afi, safi);
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer))
+ if (peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_CONFIG_DAMPENING))
+ bgp_config_write_peer_damp(vty, peer, afi, safi);
for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group))
bgp_config_write_peer_af(vty, bgp, group->conf, afi, safi);
@@ -18712,6 +18860,23 @@ void bgp_vty_init(void)
install_element(BGP_EVPN_NODE, &neighbor_allowas_in_cmd);
install_element(BGP_EVPN_NODE, &no_neighbor_allowas_in_cmd);
+ /* "neighbor dampening" commands. */
+ install_element(BGP_NODE, &neighbor_damp_cmd);
+ install_element(BGP_NODE, &no_neighbor_damp_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_damp_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_damp_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_damp_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_damp_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_damp_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_damp_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_damp_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_damp_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_damp_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_damp_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_damp_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_damp_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_neighbor_damp_param_cmd);
+
/* address-family commands. */
install_element(BGP_NODE, &address_family_ipv4_safi_cmd);
install_element(BGP_NODE, &address_family_ipv6_safi_cmd);
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index cd1873054e..f7c4b04adf 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -1347,11 +1347,21 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p,
nexthop = bgp_path_info_to_ipv6_nexthop(mpinfo_cp,
&ifindex);
- nh_updated = update_ipv6nh_for_route_install(
- nh_othervrf, nh_othervrf ?
- info->extra->bgp_orig : bgp,
- nexthop, ifindex,
- mpinfo, info, is_evpn, api_nh);
+
+ if (!nexthop)
+ nh_updated = update_ipv4nh_for_route_install(
+ nh_othervrf,
+ nh_othervrf ? info->extra->bgp_orig
+ : bgp,
+ &mpinfo_cp->attr->nexthop,
+ mpinfo_cp->attr, is_evpn, api_nh);
+ else
+ nh_updated = update_ipv6nh_for_route_install(
+ nh_othervrf,
+ nh_othervrf ? info->extra->bgp_orig
+ : bgp,
+ nexthop, ifindex, mpinfo, info, is_evpn,
+ api_nh);
}
/* Did we get proper nexthop info to update zebra? */
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 2149b14585..2386d1e869 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -2379,6 +2379,14 @@ int peer_delete(struct peer *peer)
bgp_bfd_deregister_peer(peer);
+ /* Delete peer route flap dampening configuration. This needs to happen
+ * before removing the peer from peer groups.
+ */
+ FOREACH_AFI_SAFI (afi, safi)
+ if (peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_CONFIG_DAMPENING))
+ bgp_peer_damp_disable(peer, afi, safi);
+
/* If this peer belongs to peer group, clear up the
relationship. */
if (peer->group) {
@@ -3500,6 +3508,11 @@ int bgp_delete(struct bgp *bgp)
BGP_TIMER_OFF(gr_info->t_route_select);
}
+ /* Delete route flap dampening configuration */
+ FOREACH_AFI_SAFI (afi, safi) {
+ bgp_damp_disable(bgp, afi, safi);
+ }
+
if (BGP_DEBUG(zebra, ZEBRA)) {
if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
zlog_debug("Deleting Default VRF");
@@ -4012,7 +4025,8 @@ void peer_change_action(struct peer *peer, afi_t afi, safi_t safi,
} else if (type == peer_change_reset_in) {
if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_OLD_RCV)
|| CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_NEW_RCV))
- bgp_route_refresh_send(peer, afi, safi, 0, 0, 0);
+ bgp_route_refresh_send(peer, afi, safi, 0, 0, 0,
+ BGP_ROUTE_REFRESH_NORMAL);
else {
if ((peer->doppelganger)
&& (peer->doppelganger->status != Deleted)
@@ -5039,20 +5053,20 @@ int peer_default_originate_unset(struct peer *peer, afi_t afi, safi_t safi)
continue;
/* Remove flag and configuration on peer-group member. */
- UNSET_FLAG(peer->af_flags[afi][safi],
+ UNSET_FLAG(member->af_flags[afi][safi],
PEER_FLAG_DEFAULT_ORIGINATE);
- if (peer->default_rmap[afi][safi].name)
+ if (member->default_rmap[afi][safi].name)
XFREE(MTYPE_ROUTE_MAP_NAME,
- peer->default_rmap[afi][safi].name);
- route_map_counter_decrement(peer->default_rmap[afi][safi].map);
- peer->default_rmap[afi][safi].name = NULL;
- peer->default_rmap[afi][safi].map = NULL;
+ member->default_rmap[afi][safi].name);
+ route_map_counter_decrement(member->default_rmap[afi][safi].map);
+ member->default_rmap[afi][safi].name = NULL;
+ member->default_rmap[afi][safi].map = NULL;
/* Update peer route announcements. */
- if (peer->status == Established && peer->afc_nego[afi][safi]) {
- update_group_adjust_peer(peer_af_find(peer, afi, safi));
- bgp_default_originate(peer, afi, safi, 1);
- bgp_announce_route(peer, afi, safi);
+ if (member->status == Established && member->afc_nego[afi][safi]) {
+ update_group_adjust_peer(peer_af_find(member, afi, safi));
+ bgp_default_originate(member, afi, safi, 1);
+ bgp_announce_route(member, afi, safi);
}
}
@@ -5090,7 +5104,8 @@ static void peer_on_policy_change(struct peer *peer, afi_t afi, safi_t safi,
bgp_soft_reconfig_in(peer, afi, safi);
else if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_OLD_RCV)
|| CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_NEW_RCV))
- bgp_route_refresh_send(peer, afi, safi, 0, 0, 0);
+ bgp_route_refresh_send(peer, afi, safi, 0, 0, 0,
+ BGP_ROUTE_REFRESH_NORMAL);
}
}
@@ -7338,19 +7353,23 @@ int peer_clear_soft(struct peer *peer, afi_t afi, safi_t safi,
PEER_STATUS_ORF_PREFIX_SEND))
bgp_route_refresh_send(
peer, afi, safi, prefix_type,
- REFRESH_DEFER, 1);
- bgp_route_refresh_send(peer, afi, safi,
- prefix_type,
- REFRESH_IMMEDIATE, 0);
+ REFRESH_DEFER, 1,
+ BGP_ROUTE_REFRESH_NORMAL);
+ bgp_route_refresh_send(
+ peer, afi, safi, prefix_type,
+ REFRESH_IMMEDIATE, 0,
+ BGP_ROUTE_REFRESH_NORMAL);
} else {
if (CHECK_FLAG(peer->af_sflags[afi][safi],
PEER_STATUS_ORF_PREFIX_SEND))
bgp_route_refresh_send(
peer, afi, safi, prefix_type,
- REFRESH_IMMEDIATE, 1);
+ REFRESH_IMMEDIATE, 1,
+ BGP_ROUTE_REFRESH_NORMAL);
else
- bgp_route_refresh_send(peer, afi, safi,
- 0, 0, 0);
+ bgp_route_refresh_send(
+ peer, afi, safi, 0, 0, 0,
+ BGP_ROUTE_REFRESH_NORMAL);
}
return 0;
}
@@ -7369,8 +7388,9 @@ int peer_clear_soft(struct peer *peer, afi_t afi, safi_t safi,
message to the peer. */
if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_OLD_RCV)
|| CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_NEW_RCV))
- bgp_route_refresh_send(peer, afi, safi, 0, 0,
- 0);
+ bgp_route_refresh_send(
+ peer, afi, safi, 0, 0, 0,
+ BGP_ROUTE_REFRESH_NORMAL);
else
return BGP_ERR_SOFT_RECONFIG_UNCONFIGURED;
}
@@ -7620,6 +7640,8 @@ void bgp_init(unsigned short instance)
/* BFD init */
bgp_bfd_init();
+ bgp_lp_vty_init();
+
cmd_variable_handler_register(bgp_viewvrf_var_handlers);
}
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 16210bed15..7bab6a0e42 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -43,6 +43,7 @@
#include "bgp_labelpool.h"
#include "bgp_addpath_types.h"
#include "bgp_nexthop.h"
+#include "bgp_damp.h"
#define BGP_MAX_HOSTNAME 64 /* Linux max, is larger than most other sys */
#define BGP_PEER_MAX_HASH_SIZE 16384
@@ -695,6 +696,9 @@ struct bgp {
uint32_t condition_filter_count;
struct thread *t_condition_check;
+ /* BGP route flap dampening configuration */
+ struct bgp_damp_config damp[AFI_MAX][SAFI_MAX];
+
QOBJ_FIELDS
};
DECLARE_QOBJ_TYPE(bgp)
@@ -1063,6 +1067,8 @@ struct peer {
#define PEER_CAP_ENHE_RCV (1U << 14) /* Extended nexthop received */
#define PEER_CAP_HOSTNAME_ADV (1U << 15) /* hostname advertised */
#define PEER_CAP_HOSTNAME_RCV (1U << 16) /* hostname received */
+#define PEER_CAP_ENHANCED_RR_ADV (1U << 17) /* enhanced rr advertised */
+#define PEER_CAP_ENHANCED_RR_RCV (1U << 18) /* enhanced rr received */
/* Capability flags (reset in bgp_stop) */
uint32_t af_cap[AFI_MAX][SAFI_MAX];
@@ -1197,6 +1203,9 @@ struct peer {
/* Last update packet sent time */
time_t pkt_stime[AFI_MAX][SAFI_MAX];
+ /* Peer / peer group route flap dampening configuration */
+ struct bgp_damp_config damp[AFI_MAX][SAFI_MAX];
+
/* Peer Per AF flags */
/*
* Please consult the comments for *flags_override*, *flags_invert* and
@@ -1234,6 +1243,8 @@ struct peer {
#define PEER_FLAG_SEND_LARGE_COMMUNITY (1U << 26) /* Send large Communities */
#define PEER_FLAG_MAX_PREFIX_OUT (1U << 27) /* outgoing maximum prefix */
#define PEER_FLAG_MAX_PREFIX_FORCE (1U << 28) /* maximum-prefix <num> force */
+#define PEER_FLAG_CONFIG_DAMPENING (1U << 29) /* route flap dampening */
+
enum bgp_addpath_strat addpath_type[AFI_MAX][SAFI_MAX];
@@ -1264,6 +1275,11 @@ struct peer {
#define PEER_STATUS_PREFIX_LIMIT (1U << 3) /* exceed prefix-limit */
#define PEER_STATUS_EOR_SEND (1U << 4) /* end-of-rib send to peer */
#define PEER_STATUS_EOR_RECEIVED (1U << 5) /* end-of-rib received from peer */
+#define PEER_STATUS_ENHANCED_REFRESH (1U << 6) /* Enhanced Route Refresh */
+#define PEER_STATUS_BORR_SEND (1U << 7) /* BoRR send to peer */
+#define PEER_STATUS_BORR_RECEIVED (1U << 8) /* BoRR received from peer */
+#define PEER_STATUS_EORR_SEND (1U << 9) /* EoRR send to peer */
+#define PEER_STATUS_EORR_RECEIVED (1U << 10) /* EoRR received from peer */
/* Configured timer values. */
_Atomic uint32_t holdtime;
@@ -1297,6 +1313,7 @@ struct peer {
struct thread *t_gr_stale;
struct thread *t_generate_updgrp_packets;
struct thread *t_process_packet;
+ struct thread *t_refresh_stalepath;
/* Thread flags. */
_Atomic uint32_t thread_flags;
@@ -1621,7 +1638,7 @@ struct bgp_nlri {
#define BGP_NOTIFY_HOLD_ERR 4
#define BGP_NOTIFY_FSM_ERR 5
#define BGP_NOTIFY_CEASE 6
-#define BGP_NOTIFY_CAPABILITY_ERR 7
+#define BGP_NOTIFY_ROUTE_REFRESH_ERR 7
/* Subcodes for BGP Finite State Machine Error */
#define BGP_NOTIFY_FSM_ERR_SUBCODE_UNSPECIFIC 0
@@ -1669,10 +1686,13 @@ struct bgp_nlri {
#define BGP_NOTIFY_CEASE_COLLISION_RESOLUTION 7
#define BGP_NOTIFY_CEASE_OUT_OF_RESOURCE 8
-/* BGP_NOTIFY_CAPABILITY_ERR sub codes (draft-ietf-idr-dynamic-cap-02). */
-#define BGP_NOTIFY_CAPABILITY_INVALID_ACTION 1
-#define BGP_NOTIFY_CAPABILITY_INVALID_LENGTH 2
-#define BGP_NOTIFY_CAPABILITY_MALFORMED_CODE 3
+/* BGP_NOTIFY_ROUTE_REFRESH_ERR sub codes (RFC 7313). */
+#define BGP_NOTIFY_ROUTE_REFRESH_INVALID_MSG_LEN 1
+
+/* BGP route refresh optional subtypes. */
+#define BGP_ROUTE_REFRESH_NORMAL 0
+#define BGP_ROUTE_REFRESH_BORR 1
+#define BGP_ROUTE_REFRESH_EORR 2
/* BGP timers default value. */
#define BGP_INIT_START_TIMER 1
diff --git a/bgpd/subdir.am b/bgpd/subdir.am
index ea60b921d1..ac84f4b9e4 100644
--- a/bgpd/subdir.am
+++ b/bgpd/subdir.am
@@ -18,6 +18,7 @@ vtysh_scan += \
bgpd/bgp_evpn_mh.c \
bgpd/bgp_evpn_vty.c \
bgpd/bgp_filter.c \
+ bgpd/bgp_labelpool.c \
bgpd/bgp_mplsvpn.c \
bgpd/bgp_nexthop.c \
bgpd/bgp_route.c \
diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst
index 7549cec3ea..586dccb3c6 100644
--- a/doc/user/bgp.rst
+++ b/doc/user/bgp.rst
@@ -480,28 +480,57 @@ Disable checking if nexthop is connected on EBGP sessions
Route Flap Dampening
--------------------
-.. clicmd:: bgp dampening (1-45) (1-20000) (1-20000) (1-255)
+.. index:: [no] bgp dampening [(1-45) [(1-20000) (1-20000) (1-255)]]
+.. clicmd:: [no] bgp dampening [(1-45) [(1-20000) (1-20000) (1-255)]]
- This command enables BGP route-flap dampening and specifies dampening parameters.
+ This command enables (with optionally specified dampening parameters) or
+ disables route-flap dampening for all routes of a BGP instance.
+
+.. index:: [no] neighbor PEER dampening [(1-45) [(1-20000) (1-20000) (1-255)]]
+.. clicmd:: [no] neighbor PEER dampening [(1-45) [(1-20000) (1-20000) (1-255)]]
+
+ This command enables (with optionally specified dampening parameters) or
+ disables route-flap dampening for all routes learned from a BGP peer.
+
+.. index:: [no] neighbor GROUP dampening [(1-45) [(1-20000) (1-20000) (1-255)]]
+.. clicmd:: [no] neighbor GROUP dampening [(1-45) [(1-20000) (1-20000) (1-255)]]
+
+ This command enables (with optionally specified dampening parameters) or
+ disables route-flap dampening for all routes learned from peers of a peer
+ group.
half-life
- Half-life time for the penalty
+ Half-life time for the penalty in minutes (default value: 15).
reuse-threshold
- Value to start reusing a route
+ Value to start reusing a route (default value: 750).
suppress-threshold
- Value to start suppressing a route
+ Value to start suppressing a route (default value: 2000).
max-suppress
- Maximum duration to suppress a stable route
+ Maximum duration to suppress a stable route in minutes (default value:
+ 60).
The route-flap damping algorithm is compatible with :rfc:`2439`. The use of
- this command is not recommended nowadays.
+ these commands is not recommended nowadays.
At the moment, route-flap dampening is not working per VRF and is working only
for IPv4 unicast and multicast.
+ With different parameter sets configurable for BGP instances, peer groups and
+ peers, the active dampening profile for a route is chosen on the fly,
+ allowing for various changes in configuration (i.e. peer group memberships)
+ during runtime. The parameter sets are taking precedence in the following
+ order:
+
+ 1. Peer
+ 2. Peer group
+ 3. BGP instance
+
+ The negating commands do not allow to exclude a peer/peer group from a peer
+ group/BGP instances configuration.
+
.. seealso::
https://www.ripe.net/publications/docs/ripe-378
@@ -894,6 +923,19 @@ However, it MUST defer route selection for an address family until it either.
This is command, will set the time for which stale routes are kept in RIB.
+.. index:: bgp graceful-restart stalepath-time (1-4095)
+.. clicmd:: bgp graceful-restart stalepath-time (1-4095)
+
+ This is command, will set the max time (in seconds) to hold onto
+ restarting peer's stale paths.
+
+ It also controls Enhanced Route-Refresh timer.
+
+ If this command is configured and the router does not receive a Route-Refresh EoRR
+ message, the router removes the stale routes from the BGP table after the timer
+ expires. The stale path timer is started when the router receives a Route-Refresh
+ BoRR message.
+
.. _bgp-per-peer-graceful-restart:
BGP Per Peer Graceful Restart
@@ -3384,6 +3426,32 @@ attribute.
If ``json`` option is specified, output is displayed in JSON format.
+.. index:: show bgp labelpool <chunks|inuse|ledger|requests|summary> [json]
+.. clicmd:: show bgp labelpool <chunks|inuse|ledger|requests|summary> [json]
+
+ These commands display information about the BGP labelpool used for
+ the association of MPLS labels with routes for L3VPN and Labeled Unicast
+
+ If ``chunks`` option is specified, output shows the current list of label
+ chunks granted to BGP by Zebra, indicating the start and end label in
+ each chunk
+
+ If ``inuse`` option is specified, output shows the current inuse list of
+ label to prefix mappings
+
+ If ``ledger`` option is specified, output shows ledger list of all
+ label requests made per prefix
+
+ If ``requests`` option is specified, output shows current list of label
+ requests which have not yet been fulfilled by the labelpool
+
+ If ``summary`` option is specified, output is a summary of the counts for
+ the chunks, inuse, ledger and requests list along with the count of
+ outstanding chunk requests to Zebra and the nummber of zebra reconnects
+ that have happened
+
+ If ``json`` option is specified, output is displayed in JSON format.
+
.. _bgp-display-routes-by-lcommunity:
Displaying Routes by Large Community Attribute
diff --git a/doc/user/overview.rst b/doc/user/overview.rst
index a2ce67068f..f67698e404 100644
--- a/doc/user/overview.rst
+++ b/doc/user/overview.rst
@@ -321,6 +321,8 @@ BGP
:t:`The Resource Public Key Infrastructure (RPKI) to Router Protocol. R. Bush, R. Austein. January 2013.`
- :rfc:`6811`
:t:`BGP Prefix Origin Validation. P. Mohapatra, J. Scudder, D. Ward, R. Bush, R. Austein. January 2013.`
+- :rfc:`7313`
+ :t:`Enhanced Route Refresh Capability for BGP-4. K. Patel, E. Chen, B. Venkatachalapathy. July 2014.`
- :rfc:`7606`
:t:`Revised Error Handling for BGP UPDATE Messages. E. Chen, J. Scudder, P. Mohapatra, K. Patel. August 2015.`
- :rfc:`7607`
diff --git a/doc/user/pim.rst b/doc/user/pim.rst
index bacf8637ae..05297a0609 100644
--- a/doc/user/pim.rst
+++ b/doc/user/pim.rst
@@ -726,6 +726,13 @@ Clear commands reset various variables.
Rescan PIM OIL (output interface list).
+.. index:: clear ip pim [vrf NAME] bsr-data
+.. clicmd:: clear ip pim [vrf NAME] bsr-data
+
+ This command will clear the BSM scope data struct. This command also
+ removes the next hop tracking for the bsr and resets the upstreams
+ for the dynamically learnt RPs.
+
PIM EVPN configuration
======================
To use PIM in the underlay for overlay BUM forwarding associate a multicast
diff --git a/doc/user/rpki.rst b/doc/user/rpki.rst
index 2c0e5876fa..451df1aa4e 100644
--- a/doc/user/rpki.rst
+++ b/doc/user/rpki.rst
@@ -271,5 +271,5 @@ RPKI Configuration Example
route-map rpki permit 40
!
-.. [Securing-BGP] Geoff Huston, Randy Bush: Securing BGP, In: The Internet Protocol Journal, Volume 14, No. 2, 2011. <http://www.cisco.com/web/about/ac123/ac147/archived_issues/ipj_14-2/142_bgp.html>
-.. [Resource-Certification] Geoff Huston: Resource Certification, In: The Internet Protocol Journal, Volume 12, No.1, 2009. <http://www.cisco.com/web/about/ac123/ac147/archived_issues/ipj_12-1/121_resource.html>
+.. [Securing-BGP] Geoff Huston, Randy Bush: Securing BGP, In: The Internet Protocol Journal, Volume 14, No. 2, 2011. <https://www.cisco.com/c/dam/en_us/about/ac123/ac147/archived_issues/ipj_14-2/ipj_14-2.pdf>
+.. [Resource-Certification] Geoff Huston: Resource Certification, In: The Internet Protocol Journal, Volume 12, No.1, 2009. <https://www.cisco.com/c/dam/en_us/about/ac123/ac147/archived_issues/ipj_12-1/ipj_12-1.pdf>
diff --git a/isisd/isis_nb_config.c b/isisd/isis_nb_config.c
index 3ec23df43d..ed0fea8824 100644
--- a/isisd/isis_nb_config.c
+++ b/isisd/isis_nb_config.c
@@ -52,6 +52,7 @@
#include "isisd/isis_mt.h"
#include "isisd/isis_redist.h"
#include "isisd/isis_ldp_sync.h"
+#include "isisd/isis_dr.h"
extern struct zclient *zclient;
@@ -214,6 +215,9 @@ int isis_instance_area_address_destroy(struct nb_cb_destroy_args *args)
uint8_t buff[255];
struct isis_area *area;
const char *net_title;
+ struct listnode *cnode;
+ struct isis_circuit *circuit;
+ int lvl;
if (args->event != NB_EV_APPLY)
return NB_OK;
@@ -237,6 +241,11 @@ int isis_instance_area_address_destroy(struct nb_cb_destroy_args *args)
* Last area address - reset the SystemID for this router
*/
if (listcount(area->area_addrs) == 0) {
+ for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, circuit))
+ for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; ++lvl) {
+ if (circuit->u.bc.is_dr[lvl - 1])
+ isis_dr_resign(circuit, lvl);
+ }
memset(area->isis->sysid, 0, ISIS_SYS_ID_LEN);
area->isis->sysid_set = 0;
if (IS_DEBUG_EVENTS)
diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c
index 30a94c1890..dee082fce1 100644
--- a/isisd/isis_spf.c
+++ b/isisd/isis_spf.c
@@ -828,7 +828,8 @@ lspfragloop:
#endif /* EXTREME_DEBUG */
if (no_overload) {
- if (pseudo_lsp || spftree->mtid == ISIS_MT_IPV4_UNICAST) {
+ if ((pseudo_lsp || spftree->mtid == ISIS_MT_IPV4_UNICAST)
+ && spftree->area->oldmetric) {
struct isis_oldstyle_reach *r;
for (r = (struct isis_oldstyle_reach *)
lsp->tlvs->oldstyle_reach.head;
@@ -856,42 +857,47 @@ lspfragloop:
}
}
- struct isis_item_list *te_neighs = NULL;
- if (pseudo_lsp || spftree->mtid == ISIS_MT_IPV4_UNICAST)
- te_neighs = &lsp->tlvs->extended_reach;
- else
- te_neighs = isis_lookup_mt_items(&lsp->tlvs->mt_reach,
- spftree->mtid);
-
- struct isis_extended_reach *er;
- for (er = te_neighs
- ? (struct isis_extended_reach *)
- te_neighs->head
- : NULL;
- er; er = er->next) {
- /* C.2.6 a) */
- /* Two way connectivity */
- if (!LSP_PSEUDO_ID(er->id)
- && !memcmp(er->id, root_sysid, ISIS_SYS_ID_LEN))
- continue;
- if (!pseudo_lsp
- && !memcmp(er->id, null_sysid, ISIS_SYS_ID_LEN))
- continue;
- dist = cost
- + (CHECK_FLAG(spftree->flags,
- F_SPFTREE_HOPCOUNT_METRIC)
- ? 1
- : er->metric);
- process_N(spftree,
- LSP_PSEUDO_ID(er->id) ? VTYPE_PSEUDO_TE_IS
- : VTYPE_NONPSEUDO_TE_IS,
- (void *)er->id, dist, depth + 1, NULL,
- parent);
+ if (spftree->area->newmetric) {
+ struct isis_item_list *te_neighs = NULL;
+ if (pseudo_lsp || spftree->mtid == ISIS_MT_IPV4_UNICAST)
+ te_neighs = &lsp->tlvs->extended_reach;
+ else
+ te_neighs = isis_lookup_mt_items(
+ &lsp->tlvs->mt_reach, spftree->mtid);
+
+ struct isis_extended_reach *er;
+ for (er = te_neighs ? (struct isis_extended_reach *)
+ te_neighs->head
+ : NULL;
+ er; er = er->next) {
+ /* C.2.6 a) */
+ /* Two way connectivity */
+ if (!LSP_PSEUDO_ID(er->id)
+ && !memcmp(er->id, root_sysid,
+ ISIS_SYS_ID_LEN))
+ continue;
+ if (!pseudo_lsp
+ && !memcmp(er->id, null_sysid,
+ ISIS_SYS_ID_LEN))
+ continue;
+ dist = cost
+ + (CHECK_FLAG(spftree->flags,
+ F_SPFTREE_HOPCOUNT_METRIC)
+ ? 1
+ : er->metric);
+ process_N(spftree,
+ LSP_PSEUDO_ID(er->id)
+ ? VTYPE_PSEUDO_TE_IS
+ : VTYPE_NONPSEUDO_TE_IS,
+ (void *)er->id, dist, depth + 1, NULL,
+ parent);
+ }
}
}
if (!fabricd && !pseudo_lsp && spftree->family == AF_INET
- && spftree->mtid == ISIS_MT_IPV4_UNICAST) {
+ && spftree->mtid == ISIS_MT_IPV4_UNICAST
+ && spftree->area->oldmetric) {
struct isis_item_list *reachs[] = {
&lsp->tlvs->oldstyle_ip_reach,
&lsp->tlvs->oldstyle_ip_reach_ext};
@@ -916,6 +922,10 @@ lspfragloop:
}
}
+ /* we can skip all the rest if we're using metric style narrow */
+ if (!spftree->area->newmetric)
+ goto end;
+
if (!pseudo_lsp && spftree->family == AF_INET) {
struct isis_item_list *ipv4_reachs;
if (spftree->mtid == ISIS_MT_IPV4_UNICAST)
@@ -1035,6 +1045,7 @@ lspfragloop:
}
}
+end:
if (fragnode == NULL)
fragnode = listhead(lsp->lspu.frags);
else
diff --git a/lib/sockunion.c b/lib/sockunion.c
index c999845659..1dbf77efa4 100644
--- a/lib/sockunion.c
+++ b/lib/sockunion.c
@@ -708,3 +708,20 @@ static ssize_t printfrr_psu(char *buf, size_t bsz, const char *fmt,
fb.pos[0] = '\0';
return consumed;
}
+
+int sockunion_is_null(const union sockunion *su)
+{
+ unsigned char null_s6_addr[16] = {0};
+
+ switch (sockunion_family(su)) {
+ case AF_UNSPEC:
+ return 1;
+ case AF_INET:
+ return (su->sin.sin_addr.s_addr == 0);
+ case AF_INET6:
+ return !memcmp(su->sin6.sin6_addr.s6_addr, null_s6_addr,
+ sizeof(null_s6_addr));
+ default:
+ return 0;
+ }
+}
diff --git a/lib/sockunion.h b/lib/sockunion.h
index 72f12b77ca..5e80ba1090 100644
--- a/lib/sockunion.h
+++ b/lib/sockunion.h
@@ -102,6 +102,7 @@ extern union sockunion *sockunion_getpeername(int);
extern union sockunion *sockunion_dup(const union sockunion *);
extern void sockunion_free(union sockunion *);
extern void sockunion_init(union sockunion *);
+extern int sockunion_is_null(const union sockunion *su);
#ifdef _FRR_ATTRIBUTE_PRINTFRR
#pragma FRR printfrr_ext "%pSU" (union sockunion *)
diff --git a/nhrpd/README.kernel b/nhrpd/README.kernel
index 5831316f1f..067ff9838c 100644
--- a/nhrpd/README.kernel
+++ b/nhrpd/README.kernel
@@ -32,6 +32,7 @@ This list tries to collect them to one source of information:
commit "ipv4: introduce ip_dst_mtu_maybe_forward and protect forwarding path against pmtu spoofing"
Workaround:
Set sysctl net.ipv4.ip_forward_use_pmtu=1
+ See: https://marc.info/?t=143636239500003&r=1&w=2 for details
(Should fix kernel to have this by default on for tunnel devices)
- subtle path mtu mishandling issues
diff --git a/nhrpd/nhrp_cache.c b/nhrpd/nhrp_cache.c
index 1c8fee8b07..0b5a0427e6 100644
--- a/nhrpd/nhrp_cache.c
+++ b/nhrpd/nhrp_cache.c
@@ -69,12 +69,13 @@ static void nhrp_cache_free(struct nhrp_cache *c)
{
struct nhrp_interface *nifp = c->ifp->info;
- zassert(c->cur.type == NHRP_CACHE_INVALID && c->cur.peer == NULL);
- zassert(c->new.type == NHRP_CACHE_INVALID && c->new.peer == NULL);
+ debugf(NHRP_DEBUG_COMMON, "Deleting cache entry");
nhrp_cache_counts[c->cur.type]--;
notifier_call(&c->notifier_list, NOTIFY_CACHE_DELETE);
zassert(!notifier_active(&c->notifier_list));
hash_release(nifp->cache_hash, c);
+ THREAD_OFF(c->t_timeout);
+ THREAD_OFF(c->t_auth);
XFREE(MTYPE_NHRP_CACHE, c);
}
@@ -140,6 +141,41 @@ struct nhrp_cache_config *nhrp_cache_config_get(struct interface *ifp,
create ? nhrp_cache_config_alloc : NULL);
}
+static void do_nhrp_cache_free(struct hash_bucket *hb,
+ void *arg __attribute__((__unused__)))
+{
+ struct nhrp_cache *c = hb->data;
+
+ nhrp_cache_free(c);
+}
+
+static void do_nhrp_cache_config_free(struct hash_bucket *hb,
+ void *arg __attribute__((__unused__)))
+{
+ struct nhrp_cache_config *cc = hb->data;
+
+ nhrp_cache_config_free(cc);
+}
+
+void nhrp_cache_interface_del(struct interface *ifp)
+{
+ struct nhrp_interface *nifp = ifp->info;
+
+ debugf(NHRP_DEBUG_COMMON, "Cleaning up undeleted cache entries (%lu)",
+ nifp->cache_hash ? nifp->cache_hash->count : 0);
+
+ if (nifp->cache_hash) {
+ hash_iterate(nifp->cache_hash, do_nhrp_cache_free, NULL);
+ hash_free(nifp->cache_hash);
+ }
+
+ if (nifp->cache_config_hash) {
+ hash_iterate(nifp->cache_config_hash, do_nhrp_cache_config_free,
+ NULL);
+ hash_free(nifp->cache_config_hash);
+ }
+}
+
struct nhrp_cache *nhrp_cache_get(struct interface *ifp,
union sockunion *remote_addr, int create)
{
@@ -164,6 +200,7 @@ struct nhrp_cache *nhrp_cache_get(struct interface *ifp,
static int nhrp_cache_do_free(struct thread *t)
{
struct nhrp_cache *c = THREAD_ARG(t);
+
c->t_timeout = NULL;
nhrp_cache_free(c);
return 0;
@@ -172,6 +209,7 @@ static int nhrp_cache_do_free(struct thread *t)
static int nhrp_cache_do_timeout(struct thread *t)
{
struct nhrp_cache *c = THREAD_ARG(t);
+
c->t_timeout = NULL;
if (c->cur.type != NHRP_CACHE_INVALID)
nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL);
diff --git a/nhrpd/nhrp_interface.c b/nhrpd/nhrp_interface.c
index 7768383e6b..269499cc59 100644
--- a/nhrpd/nhrp_interface.c
+++ b/nhrpd/nhrp_interface.c
@@ -49,6 +49,21 @@ static int nhrp_if_new_hook(struct interface *ifp)
static int nhrp_if_delete_hook(struct interface *ifp)
{
+ struct nhrp_interface *nifp = ifp->info;
+
+ debugf(NHRP_DEBUG_IF, "Deleted interface (%s)", ifp->name);
+
+ nhrp_cache_interface_del(ifp);
+ nhrp_nhs_interface_del(ifp);
+ nhrp_peer_interface_del(ifp);
+
+ if (nifp->ipsec_profile)
+ free(nifp->ipsec_profile);
+ if (nifp->ipsec_fallback_profile)
+ free(nifp->ipsec_fallback_profile);
+ if (nifp->source)
+ free(nifp->source);
+
XFREE(MTYPE_NHRP_IF, ifp->info);
return 0;
}
diff --git a/nhrpd/nhrp_nhs.c b/nhrpd/nhrp_nhs.c
index 085cab347f..540708f1ae 100644
--- a/nhrpd/nhrp_nhs.c
+++ b/nhrpd/nhrp_nhs.c
@@ -35,6 +35,7 @@ static void nhrp_reg_reply(struct nhrp_reqid *reqid, void *arg)
union sockunion cie_nbma, cie_proto, *proto;
char buf[64];
int ok = 0, holdtime;
+ unsigned short mtu = 0;
nhrp_reqid_free(&nhrp_packet_reqid, &r->reqid);
@@ -57,6 +58,8 @@ static void nhrp_reg_reply(struct nhrp_reqid *reqid, void *arg)
|| (cie->code == NHRP_CODE_ADMINISTRATIVELY_PROHIBITED
&& nhs->hub)))
ok = 0;
+ mtu = ntohs(cie->mtu);
+ debugf(NHRP_DEBUG_COMMON, "NHS: CIE MTU: %d", mtu);
}
if (!ok)
@@ -96,7 +99,7 @@ static void nhrp_reg_reply(struct nhrp_reqid *reqid, void *arg)
c = nhrp_cache_get(ifp, &p->dst_proto, 1);
if (c)
nhrp_cache_update_binding(c, NHRP_CACHE_NHS, holdtime,
- nhrp_peer_ref(r->peer), 0, NULL);
+ nhrp_peer_ref(r->peer), mtu, NULL);
}
static int nhrp_reg_timeout(struct thread *t)
@@ -197,7 +200,8 @@ static int nhrp_reg_send_req(struct thread *t)
/* FIXME: push CIE for each local protocol address */
cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, NULL, NULL);
- cie->prefix_length = 0xff;
+ /* RFC2332 5.2.1 if unique is set then prefix length must be 0xff */
+ cie->prefix_length = (if_ad->flags & NHRP_IFF_REG_NO_UNIQUE) ? 8 * sockunion_get_addrlen(dst_proto) : 0xff;
cie->holding_time = htons(if_ad->holdtime);
cie->mtu = htons(if_ad->mtu);
@@ -378,6 +382,24 @@ int nhrp_nhs_free(struct nhrp_nhs *nhs)
return 0;
}
+void nhrp_nhs_interface_del(struct interface *ifp)
+{
+ struct nhrp_interface *nifp = ifp->info;
+ struct nhrp_nhs *nhs, *tmp;
+ afi_t afi;
+
+ for (afi = 0; afi < AFI_MAX; afi++) {
+ debugf(NHRP_DEBUG_COMMON, "Cleaning up nhs entries (%d)",
+ !list_empty(&nifp->afi[afi].nhslist_head));
+
+ list_for_each_entry_safe(nhs, tmp, &nifp->afi[afi].nhslist_head,
+ nhslist_entry)
+ {
+ nhrp_nhs_free(nhs);
+ }
+ }
+}
+
void nhrp_nhs_terminate(void)
{
struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT);
diff --git a/nhrpd/nhrp_peer.c b/nhrpd/nhrp_peer.c
index 2dc019ce65..9aaa9dec1e 100644
--- a/nhrpd/nhrp_peer.c
+++ b/nhrpd/nhrp_peer.c
@@ -38,11 +38,17 @@ static void nhrp_packet_debug(struct zbuf *zb, const char *dir);
static void nhrp_peer_check_delete(struct nhrp_peer *p)
{
+ char buf[2][256];
struct nhrp_interface *nifp = p->ifp->info;
if (p->ref || notifier_active(&p->notifier_list))
return;
+ debugf(NHRP_DEBUG_COMMON, "Deleting peer ref:%d remote:%s local:%s",
+ p->ref,
+ sockunion2str(&p->vc->remote.nbma, buf[0], sizeof(buf[0])),
+ sockunion2str(&p->vc->local.nbma, buf[1], sizeof(buf[1])));
+
THREAD_OFF(p->t_fallback);
hash_release(nifp->peer_hash, p);
nhrp_interface_notify_del(p->ifp, &p->ifp_notifier);
@@ -185,6 +191,27 @@ static void *nhrp_peer_create(void *data)
return p;
}
+static void do_peer_hash_free(struct hash_bucket *hb,
+ void *arg __attribute__((__unused__)))
+{
+ struct nhrp_peer *p = hb->data;
+ nhrp_peer_check_delete(p);
+}
+
+void nhrp_peer_interface_del(struct interface *ifp)
+{
+ struct nhrp_interface *nifp = ifp->info;
+
+ debugf(NHRP_DEBUG_COMMON, "Cleaning up undeleted peer entries (%lu)",
+ nifp->peer_hash ? nifp->peer_hash->count : 0);
+
+ if (nifp->peer_hash) {
+ hash_iterate(nifp->peer_hash, do_peer_hash_free, NULL);
+ assert(nifp->peer_hash->count == 0);
+ hash_free(nifp->peer_hash);
+ }
+}
+
struct nhrp_peer *nhrp_peer_get(struct interface *ifp,
const union sockunion *remote_nbma)
{
@@ -271,6 +298,8 @@ int nhrp_peer_check(struct nhrp_peer *p, int establish)
return 0;
if (sockunion_family(&vc->local.nbma) == AF_UNSPEC)
return 0;
+ if (vc->ipsec)
+ return 1;
p->prio = establish > 1;
p->requested = 1;
diff --git a/nhrpd/nhrp_route.c b/nhrpd/nhrp_route.c
index e7d35b90ff..ce2b1fe2ff 100644
--- a/nhrpd/nhrp_route.c
+++ b/nhrpd/nhrp_route.c
@@ -56,7 +56,7 @@ static void nhrp_route_update_put(struct route_node *rn)
struct route_info *ri = rn->info;
if (!ri->ifp && !ri->nhrp_ifp
- && sockunion_family(&ri->via) == AF_UNSPEC) {
+ && sockunion_is_null(&ri->via)) {
XFREE(MTYPE_NHRP_ROUTE, rn->info);
route_unlock_node(rn);
}
@@ -70,8 +70,7 @@ static void nhrp_route_update_zebra(const struct prefix *p,
struct route_node *rn;
struct route_info *ri;
- rn = nhrp_route_update_get(
- p, (sockunion_family(nexthop) != AF_UNSPEC) || ifp);
+ rn = nhrp_route_update_get(p, !sockunion_is_null(nexthop) || ifp);
if (rn) {
ri = rn->info;
ri->via = *nexthop;
@@ -225,7 +224,7 @@ int nhrp_route_read(ZAPI_CALLBACK_ARGS)
sockunion2str(&nexthop_addr, buf, sizeof(buf)),
ifp ? ifp->name : "(none)");
- nhrp_route_update_zebra(&api.prefix, &nexthop_addr, ifp);
+ nhrp_route_update_zebra(&api.prefix, &nexthop_addr, added ? ifp : NULL);
nhrp_shortcut_prefix_change(&api.prefix, !added);
return 0;
diff --git a/nhrpd/nhrp_shortcut.c b/nhrpd/nhrp_shortcut.c
index 2359cfa4ac..6ad0c9ea03 100644
--- a/nhrpd/nhrp_shortcut.c
+++ b/nhrpd/nhrp_shortcut.c
@@ -62,7 +62,7 @@ static void nhrp_shortcut_cache_notify(struct notifier_block *n,
s->p, s->cache->ifp->name);
nhrp_route_announce(1, s->type, s->p, s->cache->ifp,
- NULL, 0);
+ &s->cache->remote_addr, 0);
s->route_installed = 1;
}
break;
@@ -207,6 +207,7 @@ static void nhrp_shortcut_recv_resolution_rep(struct nhrp_reqid *reqid,
struct nhrp_extension_header *ext;
struct nhrp_cie_header *cie;
struct nhrp_cache *c = NULL;
+ struct nhrp_cache *c_dst_proto = NULL;
union sockunion *proto, cie_proto, *nbma, cie_nbma, nat_nbma;
struct prefix prefix, route_prefix;
struct zbuf extpl;
@@ -304,6 +305,22 @@ static void nhrp_shortcut_recv_resolution_rep(struct nhrp_reqid *reqid,
debugf(NHRP_DEBUG_COMMON,
"Shortcut: no cache for nbma %s", buf[2]);
}
+
+ /* Update cache binding for dst_proto as well */
+ if (proto != &pp->dst_proto) {
+ c_dst_proto = nhrp_cache_get(pp->ifp, &pp->dst_proto, 1);
+ if (c_dst_proto) {
+ debugf(NHRP_DEBUG_COMMON,
+ "Shortcut: cache found, update binding");
+ nhrp_cache_update_binding(c_dst_proto, NHRP_CACHE_DYNAMIC,
+ holding_time,
+ nhrp_peer_get(pp->ifp, nbma),
+ htons(cie->mtu), nbma);
+ } else {
+ debugf(NHRP_DEBUG_COMMON,
+ "Shortcut: no cache for nbma %s", buf[2]);
+ }
+ }
}
/* Update shortcut entry for subnet to protocol gw binding */
diff --git a/nhrpd/nhrpd.h b/nhrpd/nhrpd.h
index 80a365a3c3..a36d0c445d 100644
--- a/nhrpd/nhrpd.h
+++ b/nhrpd/nhrpd.h
@@ -124,7 +124,7 @@ enum nhrp_notify_type {
struct nhrp_vc {
struct notifier_list notifier_list;
- uint8_t ipsec;
+ uint32_t ipsec;
uint8_t updating;
uint8_t abort_migration;
@@ -343,6 +343,7 @@ void nhrp_nhs_foreach(struct interface *ifp, afi_t afi,
void (*cb)(struct nhrp_nhs *, struct nhrp_registration *,
void *),
void *ctx);
+void nhrp_nhs_interface_del(struct interface *ifp);
void nhrp_route_update_nhrp(const struct prefix *p, struct interface *ifp);
void nhrp_route_announce(int add, enum nhrp_cache_type type,
@@ -366,6 +367,7 @@ void nhrp_shortcut_foreach(afi_t afi,
void nhrp_shortcut_purge(struct nhrp_shortcut *s, int force);
void nhrp_shortcut_prefix_change(const struct prefix *p, int deleted);
+void nhrp_cache_interface_del(struct interface *ifp);
void nhrp_cache_config_free(struct nhrp_cache_config *c);
struct nhrp_cache_config *nhrp_cache_config_get(struct interface *ifp,
union sockunion *remote_addr,
@@ -446,6 +448,7 @@ struct nhrp_reqid *nhrp_reqid_lookup(struct nhrp_reqid_pool *, uint32_t reqid);
int nhrp_packet_init(void);
+void nhrp_peer_interface_del(struct interface *ifp);
struct nhrp_peer *nhrp_peer_get(struct interface *ifp,
const union sockunion *remote_nbma);
struct nhrp_peer *nhrp_peer_ref(struct nhrp_peer *p);
diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c
index 6052d48e83..c58073c521 100644
--- a/ospfd/ospf_vty.c
+++ b/ospfd/ospf_vty.c
@@ -3365,6 +3365,54 @@ DEFUN (show_ip_ospf_instance,
return ret;
}
+static void ospf_interface_auth_show(struct vty *vty, struct ospf_interface *oi,
+ json_object *json, bool use_json)
+{
+ int auth_type;
+
+ auth_type = OSPF_IF_PARAM(oi, auth_type);
+
+ switch (auth_type) {
+ case OSPF_AUTH_NULL:
+ if (use_json)
+ json_object_string_add(json, "authentication",
+ "authenticationNone");
+ else
+ vty_out(vty, " Authentication NULL is enabled\n");
+ break;
+ case OSPF_AUTH_SIMPLE: {
+ if (use_json)
+ json_object_string_add(json, "authentication",
+ "authenticationSimplePassword");
+ else
+ vty_out(vty,
+ " Simple password authentication enabled\n");
+ break;
+ }
+ case OSPF_AUTH_CRYPTOGRAPHIC: {
+ struct crypt_key *ckey;
+
+ if (list_isempty(OSPF_IF_PARAM(oi, auth_crypt)))
+ return;
+
+ ckey = listgetdata(listtail(OSPF_IF_PARAM(oi, auth_crypt)));
+ if (ckey) {
+ if (use_json) {
+ json_object_string_add(json, "authentication",
+ "authenticationMessageDigest");
+ } else {
+ vty_out(vty,
+ " Cryptographic authentication enabled\n");
+ vty_out(vty, " Algorithm:MD5\n");
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
+
static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf,
struct interface *ifp,
json_object *json_interface_sub,
@@ -3686,6 +3734,9 @@ static void show_ip_ospf_interface_sub(struct vty *vty, struct ospf *ospf,
ospf_nbr_count(oi, 0),
ospf_nbr_count(oi, NSM_Full));
ospf_bfd_interface_show(vty, ifp, json_interface_sub, use_json);
+
+ /* OSPF Authentication information */
+ ospf_interface_auth_show(vty, oi, json_interface_sub, use_json);
}
}
diff --git a/pathd/path_pcep_cli.c b/pathd/path_pcep_cli.c
index f80814f17c..add3391f22 100644
--- a/pathd/path_pcep_cli.c
+++ b/pathd/path_pcep_cli.c
@@ -1156,14 +1156,17 @@ static void print_pcep_session(struct vty *vty, struct pce_opts *pce_opts,
}
if (pcc_info->is_best_multi_pce) {
- vty_out(vty, " MultiPCE precedence %d, best candidate\n",
+ vty_out(vty, " Precedence %d, best candidate\n",
((pcc_info->precedence > 0) ? pcc_info->precedence
: DEFAULT_PCE_PRECEDENCE));
} else {
- vty_out(vty, " MultiPCE precedence %d\n",
+ vty_out(vty, " Precedence %d\n",
((pcc_info->precedence > 0) ? pcc_info->precedence
: DEFAULT_PCE_PRECEDENCE));
}
+ vty_out(vty, " Confidence %s\n",
+ ((pcc_info->previous_best) ? "low"
+ : "normal"));
/* PCEPlib pcep session values, get a thread safe copy of the counters
*/
@@ -1479,7 +1482,8 @@ int pcep_cli_pcc_config_write(struct vty *vty)
csnprintfrr(buf, sizeof(buf), " peer %s",
pce_opts->pce_name);
- if (pce_opts->precedence > 0) {
+ if (pce_opts->precedence > 0
+ && pce_opts->precedence != DEFAULT_PCE_PRECEDENCE) {
csnprintfrr(buf, sizeof(buf), " %s %d",
PCEP_VTYSH_ARG_PRECEDENCE,
pce_opts->precedence);
diff --git a/pathd/path_pcep_controller.h b/pathd/path_pcep_controller.h
index 8f25ccc1eb..f6eaa0ca2a 100644
--- a/pathd/path_pcep_controller.h
+++ b/pathd/path_pcep_controller.h
@@ -99,6 +99,7 @@ struct pcep_pcc_info {
uint32_t next_reqid;
uint32_t next_plspid;
bool is_best_multi_pce;
+ bool previous_best;
uint8_t precedence;
};
diff --git a/pathd/path_pcep_pcc.c b/pathd/path_pcep_pcc.c
index 6bb5ce4bd1..c1f60edd22 100644
--- a/pathd/path_pcep_pcc.c
+++ b/pathd/path_pcep_pcc.c
@@ -1101,6 +1101,7 @@ void pcep_pcc_copy_pcc_info(struct pcc_state **pcc,
pcc_info->status = pcc_state->status;
pcc_info->pcc_id = pcc_state->id;
pcc_info->is_best_multi_pce = pcc_state->is_best;
+ pcc_info->previous_best = pcc_state->previous_best;
pcc_info->precedence =
pcc_state->pce_opts ? pcc_state->pce_opts->precedence : 0;
memcpy(&pcc_info->pcc_addr, &pcc_state->pcc_addr_tr,
diff --git a/pimd/pim_bsm.c b/pimd/pim_bsm.c
index 1acfece895..e873af5759 100644
--- a/pimd/pim_bsm.c
+++ b/pimd/pim_bsm.c
@@ -63,7 +63,7 @@ void pim_bsm_write_config(struct vty *vty, struct interface *ifp)
}
}
-static void pim_free_bsgrp_data(struct bsgrp_node *bsgrp_node)
+void pim_free_bsgrp_data(struct bsgrp_node *bsgrp_node)
{
if (bsgrp_node->bsrp_list)
list_delete(&bsgrp_node->bsrp_list);
@@ -72,7 +72,7 @@ static void pim_free_bsgrp_data(struct bsgrp_node *bsgrp_node)
XFREE(MTYPE_PIM_BSGRP_NODE, bsgrp_node);
}
-static void pim_free_bsgrp_node(struct route_table *rt, struct prefix *grp)
+void pim_free_bsgrp_node(struct route_table *rt, struct prefix *grp)
{
struct route_node *rn;
@@ -222,7 +222,7 @@ static int pim_on_bs_timer(struct thread *t)
return 0;
}
-static void pim_bs_timer_stop(struct bsm_scope *scope)
+void pim_bs_timer_stop(struct bsm_scope *scope)
{
if (PIM_DEBUG_BSM)
zlog_debug("%s : BS timer being stopped of sz: %d", __func__,
diff --git a/pimd/pim_bsm.h b/pimd/pim_bsm.h
index 0758c94f19..2829c1e05a 100644
--- a/pimd/pim_bsm.h
+++ b/pimd/pim_bsm.h
@@ -195,4 +195,7 @@ int pim_bsm_process(struct interface *ifp,
bool pim_bsm_new_nbr_fwd(struct pim_neighbor *neigh, struct interface *ifp);
struct bsgrp_node *pim_bsm_get_bsgrp_node(struct bsm_scope *scope,
struct prefix *grp);
+void pim_bs_timer_stop(struct bsm_scope *scope);
+void pim_free_bsgrp_data(struct bsgrp_node *bsgrp_node);
+void pim_free_bsgrp_node(struct route_table *rt, struct prefix *grp);
#endif
diff --git a/pimd/pim_cmd.c b/pimd/pim_cmd.c
index 8e7b13cc17..ff85151839 100644
--- a/pimd/pim_cmd.c
+++ b/pimd/pim_cmd.c
@@ -4001,6 +4001,152 @@ DEFUN (clear_ip_pim_oil,
return CMD_SUCCESS;
}
+static void clear_pim_bsr_db(struct pim_instance *pim)
+{
+ struct route_node *rn;
+ struct route_node *rpnode;
+ struct bsgrp_node *bsgrp;
+ struct prefix nht_p;
+ struct prefix g_all;
+ struct rp_info *rp_all;
+ struct pim_upstream *up;
+ struct rp_info *rp_info;
+ bool is_bsr_tracking = true;
+
+ /* Remove next hop tracking for the bsr */
+ nht_p.family = AF_INET;
+ nht_p.prefixlen = IPV4_MAX_BITLEN;
+ nht_p.u.prefix4 = pim->global_scope.current_bsr;
+ if (PIM_DEBUG_BSM) {
+ zlog_debug("%s: Deregister BSR addr %pFX with Zebra NHT",
+ __func__, &nht_p);
+ }
+ pim_delete_tracked_nexthop(pim, &nht_p, NULL, NULL, is_bsr_tracking);
+
+ /* Reset scope zone data */
+ pim->global_scope.accept_nofwd_bsm = false;
+ pim->global_scope.state = ACCEPT_ANY;
+ pim->global_scope.current_bsr.s_addr = INADDR_ANY;
+ pim->global_scope.current_bsr_prio = 0;
+ pim->global_scope.current_bsr_first_ts = 0;
+ pim->global_scope.current_bsr_last_ts = 0;
+ pim->global_scope.bsm_frag_tag = 0;
+ list_delete_all_node(pim->global_scope.bsm_list);
+
+ pim_bs_timer_stop(&pim->global_scope);
+
+ for (rn = route_top(pim->global_scope.bsrp_table); rn;
+ rn = route_next(rn)) {
+ bsgrp = rn->info;
+ if (!bsgrp)
+ continue;
+
+ rpnode = route_node_lookup(pim->rp_table, &bsgrp->group);
+
+ if (!rpnode) {
+ pim_free_bsgrp_node(bsgrp->scope->bsrp_table,
+ &bsgrp->group);
+ pim_free_bsgrp_data(bsgrp);
+ continue;
+ }
+
+ rp_info = (struct rp_info *)rpnode->info;
+
+ if ((!rp_info) || (rp_info->rp_src != RP_SRC_BSR)) {
+ pim_free_bsgrp_node(bsgrp->scope->bsrp_table,
+ &bsgrp->group);
+ pim_free_bsgrp_data(bsgrp);
+ continue;
+ }
+
+ /* Deregister addr with Zebra NHT */
+ nht_p.family = AF_INET;
+ nht_p.prefixlen = IPV4_MAX_BITLEN;
+ nht_p.u.prefix4 = rp_info->rp.rpf_addr.u.prefix4;
+
+ if (PIM_DEBUG_PIM_NHT_RP) {
+ zlog_debug("%s: Deregister RP addr %pFX with Zebra ",
+ __func__, &nht_p);
+ }
+
+ pim_delete_tracked_nexthop(pim, &nht_p, NULL, rp_info, false);
+
+ if (!str2prefix("224.0.0.0/4", &g_all))
+ return;
+
+ rp_all = pim_rp_find_match_group(pim, &g_all);
+
+ if (rp_all == rp_info) {
+ rp_all->rp.rpf_addr.family = AF_INET;
+ rp_all->rp.rpf_addr.u.prefix4.s_addr = INADDR_NONE;
+ rp_all->i_am_rp = 0;
+ } else {
+ /* Delete the rp_info from rp-list */
+ listnode_delete(pim->rp_list, rp_info);
+
+ /* Delete the rp node from rp_table */
+ rpnode->info = NULL;
+ route_unlock_node(rpnode);
+ route_unlock_node(rpnode);
+ }
+
+ XFREE(MTYPE_PIM_RP, rp_info);
+
+ pim_free_bsgrp_node(bsgrp->scope->bsrp_table, &bsgrp->group);
+ pim_free_bsgrp_data(bsgrp);
+ }
+ pim_rp_refresh_group_to_rp_mapping(pim);
+
+
+ frr_each (rb_pim_upstream, &pim->upstream_head, up) {
+ /* Find the upstream (*, G) whose upstream address is same as
+ * the RP
+ */
+ if (up->sg.src.s_addr != INADDR_ANY)
+ continue;
+
+ struct prefix grp;
+ struct rp_info *trp_info;
+
+ grp.family = AF_INET;
+ grp.prefixlen = IPV4_MAX_BITLEN;
+ grp.u.prefix4 = up->sg.grp;
+
+ trp_info = pim_rp_find_match_group(pim, &grp);
+
+ /* RP not found for the group grp */
+ if (pim_rpf_addr_is_inaddr_none(&trp_info->rp)) {
+ pim_upstream_rpf_clear(pim, up);
+ pim_rp_set_upstream_addr(pim, &up->upstream_addr,
+ up->sg.src, up->sg.grp);
+ } else {
+ /* RP found for the group grp */
+ pim_upstream_update(pim, up);
+ }
+ }
+}
+
+
+DEFUN (clear_ip_pim_bsr_db,
+ clear_ip_pim_bsr_db_cmd,
+ "clear ip pim [vrf NAME] bsr-data",
+ CLEAR_STR
+ IP_STR
+ CLEAR_IP_PIM_STR
+ VRF_CMD_HELP_STR
+ "Reset pim bsr data\n")
+{
+ int idx = 2;
+ struct vrf *vrf = pim_cmd_lookup_vrf(vty, argv, argc, &idx);
+
+ if (!vrf)
+ return CMD_WARNING;
+
+ clear_pim_bsr_db(vrf->info);
+
+ return CMD_SUCCESS;
+}
+
DEFUN (show_ip_igmp_interface,
show_ip_igmp_interface_cmd,
"show ip igmp [vrf NAME] interface [detail|WORD] [json]",
@@ -11396,6 +11542,7 @@ void pim_cmd_init(void)
install_element(ENABLE_NODE, &clear_ip_pim_interface_traffic_cmd);
install_element(ENABLE_NODE, &clear_ip_pim_oil_cmd);
install_element(ENABLE_NODE, &clear_ip_pim_statistics_cmd);
+ install_element(ENABLE_NODE, &clear_ip_pim_bsr_db_cmd);
install_element(ENABLE_NODE, &show_debugging_pim_cmd);
diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c
index e7ff434f4b..fc0f514a49 100644
--- a/pimd/pim_ifchannel.c
+++ b/pimd/pim_ifchannel.c
@@ -550,8 +550,21 @@ struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp,
struct pim_upstream *up;
ch = pim_ifchannel_find(ifp, sg);
- if (ch)
+ if (ch) {
+ if (up_flags == PIM_UPSTREAM_FLAG_MASK_SRC_PIM)
+ PIM_IF_FLAG_SET_PROTO_PIM(ch->flags);
+
+ if (up_flags == PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
+ PIM_IF_FLAG_SET_PROTO_IGMP(ch->flags);
+
+ if (ch->upstream)
+ ch->upstream->flags |= up_flags;
+ else if (PIM_DEBUG_EVENTS)
+ zlog_debug("%s:%s No Upstream found", __func__,
+ pim_str_sg_dump(sg));
+
return ch;
+ }
pim_ifp = ifp->info;
@@ -642,6 +655,12 @@ static void ifjoin_to_noinfo(struct pim_ifchannel *ch, bool ch_del)
{
pim_forward_stop(ch, !ch_del);
pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_NOINFO);
+
+ if (ch->upstream)
+ PIM_UPSTREAM_FLAG_UNSET_SRC_PIM(ch->upstream->flags);
+
+ PIM_IF_FLAG_UNSET_PROTO_PIM(ch->flags);
+
if (ch_del)
delete_on_noinfo(ch);
}
@@ -1272,6 +1291,13 @@ void pim_ifchannel_local_membership_del(struct interface *ifp,
* parent' delete_no_info */
}
}
+
+ /* Resettng the IGMP flags here */
+ if (orig->upstream)
+ PIM_UPSTREAM_FLAG_UNSET_SRC_IGMP(orig->upstream->flags);
+
+ PIM_IF_FLAG_UNSET_PROTO_IGMP(orig->flags);
+
delete_on_noinfo(orig);
}
diff --git a/pimd/pim_igmp.c b/pimd/pim_igmp.c
index 9924e335b0..73e42e9d83 100644
--- a/pimd/pim_igmp.c
+++ b/pimd/pim_igmp.c
@@ -558,8 +558,8 @@ int pim_igmp_packet(struct igmp_sock *igmp, char *buf, size_t len)
igmp_msg, igmp_msg_len);
case PIM_IGMP_V2_LEAVE_GROUP:
- return igmp_v2_recv_leave(igmp, ip_hdr->ip_src, from_str,
- igmp_msg, igmp_msg_len);
+ return igmp_v2_recv_leave(igmp, ip_hdr, from_str, igmp_msg,
+ igmp_msg_len);
case PIM_IGMP_MTRACE_RESPONSE:
return igmp_mtrace_recv_response(igmp, ip_hdr, ip_hdr->ip_src,
diff --git a/pimd/pim_igmpv2.c b/pimd/pim_igmpv2.c
index d836c66cbb..7f3c7a0f8c 100644
--- a/pimd/pim_igmpv2.c
+++ b/pimd/pim_igmpv2.c
@@ -158,12 +158,13 @@ int igmp_v2_recv_report(struct igmp_sock *igmp, struct in_addr from,
return 0;
}
-int igmp_v2_recv_leave(struct igmp_sock *igmp, struct in_addr from,
+int igmp_v2_recv_leave(struct igmp_sock *igmp, struct ip *ip_hdr,
const char *from_str, char *igmp_msg, int igmp_msg_len)
{
struct interface *ifp = igmp->interface;
struct in_addr group_addr;
char group_str[INET_ADDRSTRLEN];
+ struct in_addr from = ip_hdr->ip_src;
on_trace(__func__, igmp->interface, from);
@@ -184,8 +185,6 @@ int igmp_v2_recv_leave(struct igmp_sock *igmp, struct in_addr from,
return -1;
}
- /* Collecting IGMP Rx stats */
- igmp->rx_stats.leave_v2++;
memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
@@ -195,6 +194,32 @@ int igmp_v2_recv_leave(struct igmp_sock *igmp, struct in_addr from,
zlog_debug("Recv IGMPv2 LEAVE from %s on %s for %s", from_str,
ifp->name, group_str);
}
+ /*
+ * As per RFC 2236, section 9:
+ Message Type Destination Group
+ ------------ -----------------
+ General Query ALL-SYSTEMS (224.0.0.1)
+ Group-Specific Query The group being queried
+ Membership Report The group being reported
+ Leave Message ALL-ROUTERS (224.0.0.2)
+
+ Note: in older (i.e., non-standard and now obsolete) versions of
+ IGMPv2, hosts send Leave Messages to the group being left. A
+ router SHOULD accept Leave Messages addressed to the group being
+ left in the interests of backwards compatibility with such hosts.
+ In all cases, however, hosts MUST send to the ALL-ROUTERS address
+ to be compliant with this specification.
+ */
+ if ((ntohl(ip_hdr->ip_dst.s_addr) != INADDR_ALLRTRS_GROUP)
+ && (ip_hdr->ip_dst.s_addr != group_addr.s_addr)) {
+ if (PIM_DEBUG_IGMP_EVENTS)
+ zlog_debug(
+ "IGMPv2 Leave message is ignored since received on address other than ALL-ROUTERS or Group-address");
+ return -1;
+ }
+
+ /* Collecting IGMP Rx stats */
+ igmp->rx_stats.leave_v2++;
/*
* RFC 3376
diff --git a/pimd/pim_igmpv2.h b/pimd/pim_igmpv2.h
index f0a6fdc5fb..29591ff16c 100644
--- a/pimd/pim_igmpv2.h
+++ b/pimd/pim_igmpv2.h
@@ -29,7 +29,7 @@ void igmp_v2_send_query(struct igmp_group *group, int fd, const char *ifname,
int igmp_v2_recv_report(struct igmp_sock *igmp, struct in_addr from,
const char *from_str, char *igmp_msg, int igmp_msg_len);
-int igmp_v2_recv_leave(struct igmp_sock *igmp, struct in_addr from,
+int igmp_v2_recv_leave(struct igmp_sock *igmp, struct ip *ip_hdr,
const char *from_str, char *igmp_msg, int igmp_msg_len);
#endif /* PIM_IGMPV2_H */
diff --git a/pimd/pim_mroute.c b/pimd/pim_mroute.c
index 0bccba397b..23259900b7 100644
--- a/pimd/pim_mroute.c
+++ b/pimd/pim_mroute.c
@@ -628,7 +628,7 @@ static int pim_mroute_msg(struct pim_instance *pim, const char *buf,
ifaddr = connected_src->u.prefix4;
igmp = pim_igmp_sock_lookup_ifaddr(pim_ifp->igmp_socket_list, ifaddr);
- if (PIM_DEBUG_MROUTE) {
+ if (PIM_DEBUG_IGMP_PACKETS) {
zlog_debug(
"%s(%s): igmp kernel upcall on %s(%p) for %pI4 -> %pI4",
__func__, pim->vrf->name, ifp->name, igmp,
diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c
index fa5d6f37bf..301a27001f 100644
--- a/pimd/pim_rp.c
+++ b/pimd/pim_rp.c
@@ -271,7 +271,7 @@ struct rp_info *pim_rp_find_match_group(struct pim_instance *pim,
*
* This is a placeholder function for now.
*/
-static void pim_rp_refresh_group_to_rp_mapping(struct pim_instance *pim)
+void pim_rp_refresh_group_to_rp_mapping(struct pim_instance *pim)
{
pim_msdp_i_am_rp_changed(pim);
pim_upstream_reeval_use_rpt(pim);
diff --git a/pimd/pim_rp.h b/pimd/pim_rp.h
index 8a12cb076c..dd7cd5d75e 100644
--- a/pimd/pim_rp.h
+++ b/pimd/pim_rp.h
@@ -86,4 +86,5 @@ int pim_rp_list_cmp(void *v1, void *v2);
struct rp_info *pim_rp_find_match_group(struct pim_instance *pim,
const struct prefix *group);
void pim_upstream_update(struct pim_instance *pim, struct pim_upstream *up);
+void pim_rp_refresh_group_to_rp_mapping(struct pim_instance *pim);
#endif
diff --git a/pimd/pim_upstream.c b/pimd/pim_upstream.c
index d95b092d94..9899172e6c 100644
--- a/pimd/pim_upstream.c
+++ b/pimd/pim_upstream.c
@@ -749,6 +749,13 @@ void pim_upstream_switch(struct pim_instance *pim, struct pim_upstream *up,
bool send_xg_jp = false;
forward_off(up);
+ /*
+ * RFC 4601 Sec 4.5.7:
+ * JoinDesired(S,G) -> False, set SPTbit to false.
+ */
+ if (up->sg.src.s_addr != INADDR_ANY)
+ up->sptbit = PIM_UPSTREAM_SPTBIT_FALSE;
+
if (old_state == PIM_UPSTREAM_JOINED)
pim_msdp_up_join_state_changed(pim, up);
diff --git a/tests/topotests/bgp_features/exabgp.env b/tests/topotests/bgp_features/exabgp.env
new file mode 100644
index 0000000000..6c554f5fa8
--- /dev/null
+++ b/tests/topotests/bgp_features/exabgp.env
@@ -0,0 +1,53 @@
+
+[exabgp.api]
+encoder = text
+highres = false
+respawn = false
+socket = ''
+
+[exabgp.bgp]
+openwait = 60
+
+[exabgp.cache]
+attributes = true
+nexthops = true
+
+[exabgp.daemon]
+daemonize = true
+pid = '/var/run/exabgp/exabgp.pid'
+user = 'exabgp'
+
+[exabgp.log]
+all = false
+configuration = true
+daemon = true
+destination = '/var/log/exabgp.log'
+enable = true
+level = INFO
+message = false
+network = true
+packets = false
+parser = false
+processes = true
+reactor = true
+rib = false
+routes = false
+short = false
+timers = false
+
+[exabgp.pdb]
+enable = false
+
+[exabgp.profile]
+enable = false
+file = ''
+
+[exabgp.reactor]
+speed = 1.0
+
+[exabgp.tcp]
+acl = false
+bind = ''
+delay = 0
+once = false
+port = 179
diff --git a/tests/topotests/bgp_features/peer1/exa_readpipe.py b/tests/topotests/bgp_features/peer1/exa_readpipe.py
new file mode 100644
index 0000000000..dba1536388
--- /dev/null
+++ b/tests/topotests/bgp_features/peer1/exa_readpipe.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+"Helper script to read api commands from a pipe and feed them to ExaBGP"
+
+import sys
+
+if len(sys.argv) != 2:
+ sys.exit(1)
+fifo = sys.argv[1]
+
+while True:
+ pipe = open(fifo, 'r')
+ with pipe:
+ line = pipe.readline().strip()
+ if line != "":
+ sys.stdout.write("{}\n".format(line))
+ sys.stdout.flush()
+ pipe.close()
+
+sys.exit(0)
diff --git a/tests/topotests/bgp_features/peer1/exabgp.cfg b/tests/topotests/bgp_features/peer1/exabgp.cfg
new file mode 100644
index 0000000000..2e95252cf6
--- /dev/null
+++ b/tests/topotests/bgp_features/peer1/exabgp.cfg
@@ -0,0 +1,12 @@
+group exabgp {
+ process announce-routes {
+ run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer1.in";
+ encoder text;
+ }
+ neighbor 192.168.101.1 {
+ router-id 192.168.101.3;
+ local-address 192.168.101.3;
+ local-as 65403;
+ peer-as 65000;
+ }
+}
diff --git a/tests/topotests/bgp_features/peer2/exa_readpipe.py b/tests/topotests/bgp_features/peer2/exa_readpipe.py
new file mode 100644
index 0000000000..dba1536388
--- /dev/null
+++ b/tests/topotests/bgp_features/peer2/exa_readpipe.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+"Helper script to read api commands from a pipe and feed them to ExaBGP"
+
+import sys
+
+if len(sys.argv) != 2:
+ sys.exit(1)
+fifo = sys.argv[1]
+
+while True:
+ pipe = open(fifo, 'r')
+ with pipe:
+ line = pipe.readline().strip()
+ if line != "":
+ sys.stdout.write("{}\n".format(line))
+ sys.stdout.flush()
+ pipe.close()
+
+sys.exit(0)
diff --git a/tests/topotests/bgp_features/peer2/exabgp.cfg b/tests/topotests/bgp_features/peer2/exabgp.cfg
new file mode 100644
index 0000000000..1f65547bc5
--- /dev/null
+++ b/tests/topotests/bgp_features/peer2/exabgp.cfg
@@ -0,0 +1,12 @@
+group exabgp {
+ process announce-routes {
+ run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer2.in";
+ encoder text;
+ }
+ neighbor 192.168.101.1 {
+ router-id 192.168.101.4;
+ local-address 192.168.101.4;
+ local-as 65404;
+ peer-as 65000;
+ }
+}
diff --git a/tests/topotests/bgp_features/peer3/exa_readpipe.py b/tests/topotests/bgp_features/peer3/exa_readpipe.py
new file mode 100644
index 0000000000..dba1536388
--- /dev/null
+++ b/tests/topotests/bgp_features/peer3/exa_readpipe.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+"Helper script to read api commands from a pipe and feed them to ExaBGP"
+
+import sys
+
+if len(sys.argv) != 2:
+ sys.exit(1)
+fifo = sys.argv[1]
+
+while True:
+ pipe = open(fifo, 'r')
+ with pipe:
+ line = pipe.readline().strip()
+ if line != "":
+ sys.stdout.write("{}\n".format(line))
+ sys.stdout.flush()
+ pipe.close()
+
+sys.exit(0)
diff --git a/tests/topotests/bgp_features/peer3/exabgp.cfg b/tests/topotests/bgp_features/peer3/exabgp.cfg
new file mode 100644
index 0000000000..8632cc86c5
--- /dev/null
+++ b/tests/topotests/bgp_features/peer3/exabgp.cfg
@@ -0,0 +1,12 @@
+group exabgp {
+ process announce-routes {
+ run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer3.in";
+ encoder text;
+ }
+ neighbor 192.168.101.1 {
+ router-id 192.168.101.5;
+ local-address 192.168.101.5;
+ local-as 65405;
+ peer-as 65000;
+ }
+}
diff --git a/tests/topotests/bgp_features/peer4/exa_readpipe.py b/tests/topotests/bgp_features/peer4/exa_readpipe.py
new file mode 100644
index 0000000000..dba1536388
--- /dev/null
+++ b/tests/topotests/bgp_features/peer4/exa_readpipe.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+"Helper script to read api commands from a pipe and feed them to ExaBGP"
+
+import sys
+
+if len(sys.argv) != 2:
+ sys.exit(1)
+fifo = sys.argv[1]
+
+while True:
+ pipe = open(fifo, 'r')
+ with pipe:
+ line = pipe.readline().strip()
+ if line != "":
+ sys.stdout.write("{}\n".format(line))
+ sys.stdout.flush()
+ pipe.close()
+
+sys.exit(0)
diff --git a/tests/topotests/bgp_features/peer4/exabgp.cfg b/tests/topotests/bgp_features/peer4/exabgp.cfg
new file mode 100644
index 0000000000..06bc0d6e64
--- /dev/null
+++ b/tests/topotests/bgp_features/peer4/exabgp.cfg
@@ -0,0 +1,12 @@
+group exabgp {
+ process announce-routes {
+ run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer4.in";
+ encoder text;
+ }
+ neighbor 192.168.101.1 {
+ router-id 192.168.101.6;
+ local-address 192.168.101.6;
+ local-as 65406;
+ peer-as 65000;
+ }
+}
diff --git a/tests/topotests/bgp_features/r1/bgp_damp_announced.json b/tests/topotests/bgp_features/r1/bgp_damp_announced.json
new file mode 100644
index 0000000000..cb4a2c9b2f
--- /dev/null
+++ b/tests/topotests/bgp_features/r1/bgp_damp_announced.json
@@ -0,0 +1,21 @@
+{
+ "localAS":65000,
+ "routes":{
+ "192.168.31.0/24": [ { "valid":true, "network":"192.168.31.0\/24", "peerId":"192.168.101.3" } ],
+ "192.168.32.0/24": [ { "valid":true, "network":"192.168.32.0\/24", "peerId":"192.168.101.3" } ],
+ "192.168.33.0/24": [ { "valid":true, "network":"192.168.33.0\/24", "peerId":"192.168.101.3" } ],
+ "192.168.34.0/24": [ { "valid":true, "network":"192.168.34.0\/24", "peerId":"192.168.101.3" } ],
+ "192.168.41.0/24": [ { "valid":true, "network":"192.168.41.0\/24", "peerId":"192.168.101.4" } ],
+ "192.168.42.0/24": [ { "valid":true, "network":"192.168.42.0\/24", "peerId":"192.168.101.4" } ],
+ "192.168.43.0/24": [ { "valid":true, "network":"192.168.43.0\/24", "peerId":"192.168.101.4" } ],
+ "192.168.44.0/24": [ { "valid":true, "network":"192.168.44.0\/24", "peerId":"192.168.101.4" } ],
+ "192.168.51.0/24": [ { "valid":true, "network":"192.168.51.0\/24", "peerId":"192.168.101.5" } ],
+ "192.168.52.0/24": [ { "valid":true, "network":"192.168.52.0\/24", "peerId":"192.168.101.5" } ],
+ "192.168.53.0/24": [ { "valid":true, "network":"192.168.53.0\/24", "peerId":"192.168.101.5" } ],
+ "192.168.54.0/24": [ { "valid":true, "network":"192.168.54.0\/24", "peerId":"192.168.101.5" } ],
+ "192.168.61.0/24": [ { "valid":true, "network":"192.168.61.0\/24", "peerId":"192.168.101.6" } ],
+ "192.168.62.0/24": [ { "valid":true, "network":"192.168.62.0\/24", "peerId":"192.168.101.6" } ],
+ "192.168.63.0/24": [ { "valid":true, "network":"192.168.63.0\/24", "peerId":"192.168.101.6" } ],
+ "192.168.64.0/24": [ { "valid":true, "network":"192.168.64.0\/24", "peerId":"192.168.101.6" } ]
+ }
+}
diff --git a/tests/topotests/bgp_features/r1/bgp_damp_setup.json b/tests/topotests/bgp_features/r1/bgp_damp_setup.json
new file mode 100644
index 0000000000..f9f89db894
--- /dev/null
+++ b/tests/topotests/bgp_features/r1/bgp_damp_setup.json
@@ -0,0 +1,10 @@
+{
+ "ipv4Unicast":{
+ "peers":{
+ "192.168.101.3":{"remoteAs":65403, "state":"Established"},
+ "192.168.101.4":{"remoteAs":65404, "state":"Established"},
+ "192.168.101.5":{"remoteAs":65405, "state":"Established"},
+ "192.168.101.6":{"remoteAs":65406, "state":"Established"}
+ }
+ }
+}
diff --git a/tests/topotests/bgp_features/r2/bgp_damp_announced.json b/tests/topotests/bgp_features/r2/bgp_damp_announced.json
new file mode 100644
index 0000000000..9394358f82
--- /dev/null
+++ b/tests/topotests/bgp_features/r2/bgp_damp_announced.json
@@ -0,0 +1,21 @@
+{
+ "localAS":65000,
+ "routes":{
+ "192.168.31.0/24": [ { "network":"192.168.31.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.32.0/24": [ { "network":"192.168.32.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.33.0/24": [ { "network":"192.168.33.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.34.0/24": [ { "network":"192.168.34.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.41.0/24": [ { "network":"192.168.41.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.42.0/24": [ { "network":"192.168.42.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.43.0/24": [ { "network":"192.168.43.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.44.0/24": [ { "network":"192.168.44.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.51.0/24": [ { "network":"192.168.51.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.52.0/24": [ { "network":"192.168.52.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.53.0/24": [ { "network":"192.168.53.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.54.0/24": [ { "network":"192.168.54.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.61.0/24": [ { "network":"192.168.61.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.62.0/24": [ { "network":"192.168.62.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.63.0/24": [ { "network":"192.168.63.0\/24", "peerId":"192.168.0.1" } ],
+ "192.168.64.0/24": [ { "network":"192.168.64.0\/24", "peerId":"192.168.0.1" } ]
+ }
+}
diff --git a/tests/topotests/bgp_features/r2/bgp_damp_withdrawn.json b/tests/topotests/bgp_features/r2/bgp_damp_withdrawn.json
new file mode 100644
index 0000000000..f3c54a70a1
--- /dev/null
+++ b/tests/topotests/bgp_features/r2/bgp_damp_withdrawn.json
@@ -0,0 +1,18 @@
+{
+ "192.168.31.0/24": null,
+ "192.168.32.0/24": null,
+ "192.168.33.0/24": null,
+ "192.168.34.0/24": null,
+ "192.168.41.0/24": null,
+ "192.168.42.0/24": null,
+ "192.168.43.0/24": null,
+ "192.168.44.0/24": null,
+ "192.168.51.0/24": null,
+ "192.168.52.0/24": null,
+ "192.168.53.0/24": null,
+ "192.168.54.0/24": null,
+ "192.168.61.0/24": null,
+ "192.168.62.0/24": null,
+ "192.168.63.0/24": null,
+ "192.168.64.0/24": null
+}
diff --git a/tests/topotests/bgp_features/test_bgp_features.py b/tests/topotests/bgp_features/test_bgp_features.py
index 5f3809c2b3..3d963b4cf6 100644
--- a/tests/topotests/bgp_features/test_bgp_features.py
+++ b/tests/topotests/bgp_features/test_bgp_features.py
@@ -33,6 +33,7 @@ import sys
import pytest
import re
import time
+from time import sleep
# Save the Current Working Directory to find configuration files.
CWD = os.path.dirname(os.path.realpath(__file__))
@@ -64,6 +65,14 @@ class BGPFeaturesTopo1(Topo):
for rtrNum in range(1, 6):
tgen.add_router("r{}".format(rtrNum))
+ # create ExaBGP peers
+ for peer_num in range(1, 5):
+ tgen.add_exabgp_peer(
+ "peer{}".format(peer_num),
+ ip="192.168.101.{}".format(peer_num + 2),
+ defaultRoute="via 192.168.101.1",
+ )
+
# Setup Switches and connections
for swNum in range(1, 11):
tgen.add_switch("sw{}".format(swNum))
@@ -89,6 +98,12 @@ class BGPFeaturesTopo1(Topo):
tgen.gears["r2"].add_link(tgen.gears["sw5"])
tgen.gears["r5"].add_link(tgen.gears["sw5"])
+ # Add ExaBGP peers to sw4
+ tgen.gears["peer1"].add_link(tgen.gears["sw4"])
+ tgen.gears["peer2"].add_link(tgen.gears["sw4"])
+ tgen.gears["peer3"].add_link(tgen.gears["sw4"])
+ tgen.gears["peer4"].add_link(tgen.gears["sw4"])
+
#####################################################
#
@@ -1093,6 +1108,662 @@ def test_bgp_delayopen_dual():
# end test_bgp_delayopen_dual
+def test_bgp_dampening_setup():
+ "BGP route-flap dampening test setup"
+
+ # This test starts four ExaBGP peers, adds them as neighbors to the
+ # configuration of router r1 and checks if connections get established.
+
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Starting BGP route-flap dampening test setup")
+
+ # Start ExaBGP peers connected to r1 via switch 4
+ logger.info("Starting ExaBGP peers")
+ for peer_num in range(1, 5):
+ logger.info("Creating named pipe for ExaBGP peer peer{}".format(peer_num))
+ fifo_in = "/var/run/exabgp_peer{}.in".format(peer_num)
+ if os.path.exists(fifo_in):
+ os.remove(fifo_in)
+ os.mkfifo(fifo_in, 0o777)
+ logger.info("Starting ExaBGP on peer peer{}".format(peer_num))
+ peer = tgen.gears["peer{}".format(peer_num)]
+ peer_dir = os.path.join(CWD, "peer{}".format(peer_num))
+ env_file = os.path.join(CWD, "exabgp.env")
+ peer.start(peer_dir, env_file)
+
+ # Add ExaBGP peers to configuration of router r2
+ logger.info("Adding ExaBGP peers as neighbors to configuration of router r2")
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.3 remote-as 65403"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.3 route-map testmap-in"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.3 route-map testmap-out"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.4 remote-as 65404"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.4 route-map testmap-in"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.4 route-map testmap-out"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.5 remote-as 65405"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.5 route-map testmap-in"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.5 route-map testmap-out"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.6 remote-as 65406"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.6 route-map testmap-in"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.6 route-map testmap-out"'
+ )
+
+ # Check if exabgp peers are up and running
+ logger.info("Checking for established connections to ExaBGP peers on router r1")
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/bgp_damp_setup.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp summary json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertmsg = (
+ "BGP session on r1 did not establish connections with one ore more ExaBGP peers"
+ )
+ assert res is None, assertmsg
+
+ # end test_bgp_dampening_setup
+
+
+def test_bgp_dampening_route_announce():
+ "Test of BGP route-flap dampening route announcement"
+
+ # This test checks if the four ExaBGP peers can announce routes to router
+ # r1 and if these routes get forwarded to router r2.
+
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Starting test of BGP route-flap dampening route announcement")
+
+ # Announce routes on exabgp peers to r2
+ logger.info("Announcing routes on ExaBGP peers to r1")
+ for prefix_iter in range(1, 5):
+ for peer_num in range(1, 5):
+ pipe = open("/run/exabgp_peer{}.in".format(peer_num), "w")
+ with pipe:
+ pipe.write(
+ "announce route 192.168.{}{}.0/24 next-hop 192.168.101.{}\n".format(
+ (peer_num + 2), prefix_iter, (peer_num + 2)
+ )
+ )
+ pipe.close()
+ sleep(0.1) # ExaBGP API command processing delay
+
+ # Check if routes announced by ExaBGP peers are present in RIB of router r1
+ logger.info(
+ "Checking if routes announced by ExaBGP peers are present in RIB of router r1"
+ )
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/bgp_damp_announced.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertmsg = (
+ "BGP session on router r1 did not receive routes announced by ExaBGP peers"
+ )
+ assert res is None, assertmsg
+
+ # Check if routes announced by ExaBGP peers to router r1 have been forwarded
+ # and are now present in RIB of router r2
+ logger.info(
+ "Checking if forwarded routes announced by ExaBGP peers are present in RIB of router r2"
+ )
+ router = tgen.gears["r2"]
+ reffile = os.path.join(CWD, "r2/bgp_damp_announced.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertmsg = "BGP session on router r2 did not receive routes announced by ExaBGP peers forwarded by router r1"
+ assert res is None, assertmsg
+
+ # end test_bgp_dampening_route_announce
+
+
+def test_bgp_dampening_disabled():
+ "Test of BGP route-flapping with dampening disabled"
+
+ # This test verifies that flapped routes do not get withdrawn from the RIB
+ # of router r1 if dampening is disabled.
+
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Starting test of BGP route-flapping with dampening disabled")
+
+ # Flapping routes on ExaBGP peer peer1
+ logger.info(
+ "Flapping routes on ExaBGP peer peer1 with route-flap dampening disabled"
+ )
+ for _ in range(1, 5):
+ for prefix_iter in range(1, 5):
+ pipe = open("/run/exabgp_peer1.in", "w")
+ with pipe:
+ pipe.write(
+ "withdraw route 192.168.3{}.0/24 next-hop 192.168.101.3\n".format(
+ prefix_iter
+ )
+ )
+ pipe.close()
+ sleep(0.1) # ExaBGP API command processing delay
+ sleep(1) # Give the BGP session on router r1 time to process routes
+ for prefix_iter in range(1, 5):
+ pipe = open("/run/exabgp_peer1.in", "w")
+ with pipe:
+ pipe.write(
+ "announce route 192.168.3{}.0/24 next-hop 192.168.101.3\n".format(
+ prefix_iter
+ )
+ )
+ pipe.close()
+ sleep(0.1) # ExaBGP API command processing delay
+
+ # Verify flapped routes are still present in RIB of router r1
+ logger.info(
+ "Verifying that the flapped routes are still present in RIB of router r1"
+ )
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/bgp_damp_announced.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertmsg = "BGP session on router r1 removed flapped routes despite route-flap dampening being disabled"
+ assert res is None, assertmsg
+
+ # end test_bgp_dampening_disabled
+
+
+def test_bgp_dampening_config():
+ "Test of BGP route-flap dampening configuration"
+
+ # This test adds peer-group group1 with peers peer1 and peer2 to the
+ # configuration of router r1, sets up dampening configurations with
+ # different profiles and verifies the configured dampening parameters.
+
+ tgen = get_topogen()
+ r_1 = tgen.net["r1"]
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Starting test of BGP route-flap dampening configuration")
+
+ # Add peer-group group1 with peers peer1 and peer2
+ logger.info(
+ "Creating peer-group group1 and adding ExaBGP peers peer1 and peer2 to it"
+ )
+ r_1.cmd('vtysh -c "conf t" -c "router bgp 65000" -c "neighbor group1 peer-group"')
+ r_1.cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.3 peer-group group1"'
+ ) # peer1
+ r_1.cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.4 peer-group group1"'
+ ) # peer2
+
+ # Enable different dampening profiles for peer1, peer3, group1 and global
+ # configuration
+ logger.info(
+ "Enabling different dampening profiles for peer1, peer3, group1 and global configuration"
+ )
+ r_1.cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "bgp dampening 30 300 900 90"'
+ )
+ r_1.cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor group1 dampening 20 200 600 60"'
+ )
+ r_1.cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.3 dampening 10 100 300 30"'
+ ) # peer1
+ r_1.cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.5 dampening 10 100 300 30"'
+ ) # peer3
+
+ # Verify route-flap dampening configuration
+ logger.info("Verifying route-flap dampening configuration on router r1")
+ vtyout = r_1.cmd('vtysh -c "show running-config"')
+ assertmsg = "BGP Session on r1 does not show enabled global route-flap dampening in running configuration"
+ assert re.search("bgp dampening 30 300 900 90", vtyout), assertmsg
+ assertmsg = "BGP Session on r1 does not show route-flap dampening enabled for peer-group group1 in running configuration"
+ assert re.search("neighbor group1 dampening 20 200 600 60", vtyout), assertmsg
+ assertmsg = "BGP Session on r1 does not show route-flap dampening enabled for peer peer1 in running configuration"
+ assert re.search(
+ "neighbor 192.168.101.3 dampening 10 100 300 30", vtyout
+ ), assertmsg
+ assertmsg = "BGP Session on r1 does not show route-flap dampening enabled for peer peer3 in running configuration"
+ assert re.search(
+ "neighbor 192.168.101.5 dampening 10 100 300 30", vtyout
+ ), assertmsg
+
+ # end test_bgp_dampening_config
+
+
+def test_bgp_dampening_profile_peer_over_group():
+ "Test of BGP route-flap dampening profile preferences: peer over group"
+
+ # This test verifies that the dampening profile of a peer takes precedence
+ # over the dampening profile of its peer-group by flapping the peers routes
+ # until dampened and comparing the reuse times to the one specified in the
+ # dampening configuration.
+
+ tgen = get_topogen()
+ r_1 = tgen.net["r1"]
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info(
+ "Starting test of BGP route-flap dampening profile preferences: peer over group"
+ )
+
+ # Flapping routes on ExaBGP peer peer1
+ logger.info(
+ "Flapping routes on ExaBGP peer peer1 with route-flap dampening enabled"
+ )
+ for _ in range(1, 5):
+ for prefix_iter in range(1, 5):
+ pipe = open("/run/exabgp_peer1.in", "w")
+ with pipe:
+ pipe.write(
+ "withdraw route 192.168.3{}.0/24 next-hop 192.168.101.3\n".format(
+ prefix_iter
+ )
+ )
+ pipe.close()
+ sleep(0.1) # ExaBGP API command processing delay
+ sleep(1) # Give the BGP session on router r1 time to process routes
+ for prefix_iter in range(1, 5):
+ pipe = open("/run/exabgp_peer1.in", "w")
+ with pipe:
+ pipe.write(
+ "announce route 192.168.3{}.0/24 next-hop 192.168.101.3\n".format(
+ prefix_iter
+ )
+ )
+ pipe.close()
+ sleep(0.1) # ExaBGP API command processing delay
+
+ # Check damped paths on r1 for routes of peer1 witn peer profile
+ logger.info(
+ "Checking if router r1 used the correct dampening profile on routes flapped by ExaBGP peer peer1"
+ )
+ sleep(5) # Wait 5 seconds for paths to show up in dampened-paths list
+ vtyout = r_1.cmd('vtysh -c "show ip bgp dampening dampened-paths"')
+ routes = re.findall(r"\*d 192\.168\.3\d\.0\/24.*", vtyout)
+ assertmsg = (
+ "BGP session on router r1 did not dampen routes flapped by ExaBGP peer peer1"
+ )
+ assert len(routes) == 4, assertmsg
+ assertmsg = "BGP session on router r1 used wrong dampening profile for a route flapped by ExaBGP peer peer1"
+ for route in routes:
+ assert (int(route.split()[3].split(":")[0]) == 0) and ( # hours of reuse time
+ 35 > int(route.split()[3].split(":")[1]) > 25
+ ), assertmsg # minutes of reuse time
+
+ # end test_bgp_dampening_profile_peer_over_group
+
+
+def test_bgp_dampening_profile_group_over_global():
+ "Test of BGP route-flap dampening profile preferences: group over global"
+
+ # This test verifies that the dampening profile of a peer-group takes
+ # precedence over the global dampening profile by flapping the routes of a
+ # peer-group member until dampened and comparing the reuse times to the one
+ # specified in the dampening configuration.
+
+ tgen = get_topogen()
+ r_1 = tgen.net["r1"]
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info(
+ "Starting test of BGP route-flap dampening profile preferences: group over global"
+ )
+
+ # Flapping routes on ExaBGP peer peer2
+ logger.info(
+ "Flapping routes on ExaBGP peer peer2 with route-flap dampening enabled"
+ )
+ for _ in range(1, 5):
+ for prefix_iter in range(1, 5):
+ pipe = open("/run/exabgp_peer2.in", "w")
+ with pipe:
+ pipe.write(
+ "withdraw route 192.168.4{}.0/24 next-hop 192.168.101.4\n".format(
+ prefix_iter
+ )
+ )
+ pipe.close()
+ sleep(0.1) # ExaBGP API command processing delay
+ sleep(1) # Give the BGP session on router r1 time to process routes
+ for prefix_iter in range(1, 5):
+ pipe = open("/run/exabgp_peer2.in", "w")
+ with pipe:
+ pipe.write(
+ "announce route 192.168.4{}.0/24 next-hop 192.168.101.4\n".format(
+ prefix_iter
+ )
+ )
+ pipe.close()
+ sleep(0.1) # ExaBGP API command processing delay
+
+ # Check damped paths on r1 for routes of peer2 witn group profile
+ logger.info(
+ "Checking if router r1 used the correct dampening profile on routes flapped by ExaBGP peer peer2"
+ )
+ sleep(5) # wait 5 seconds for paths to shop up in damp list
+ vtyout = r_1.cmd('vtysh -c "show ip bgp dampening dampened-paths"')
+ routes = re.findall(r"\*d 192\.168\.4\d\.0\/24.*", vtyout)
+ assertmsg = (
+ "BGP session on router r1 did not dampen routes flapped by ExaBGP peer peer2"
+ )
+ assert len(routes) == 4, assertmsg
+ assertmsg = "BGP session on router r1 used wrong dampening profile for a route flapped by ExaBGP peer peer2"
+ for route in routes:
+ assert (int(route.split()[3].split(":")[0]) == 0) and ( # hours of reuse time
+ 65 > int(route.split()[3].split(":")[1]) > 55
+ ), assertmsg # minutes of reuse time
+
+ # end test_bgp_dampening_profile_group_over_global
+
+
+def test_bgp_dampening_profile_peer_over_global():
+ "Test of BGP route-flap dampening profile preferences: peer over global"
+
+ # This test verifies that the dampening profile of a peer takes precedence
+ # over the global dampening profile by flapping the routes of the peer until
+ # dampened and comparing the reuse times to the one specified in the
+ # dampening configuration.
+
+ tgen = get_topogen()
+ r_1 = tgen.net["r1"]
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info(
+ "Starting test of BGP route-flap dampening profile preferences: peer over global"
+ )
+
+ # Flapping routes on ExaBGP peer peer3
+ logger.info(
+ "Flapping routes on ExaBGP peer peer3 with route-flap dampening enabled"
+ )
+ for _ in range(1, 5):
+ for prefix_iter in range(1, 5):
+ pipe = open("/run/exabgp_peer3.in", "w")
+ with pipe:
+ pipe.write(
+ "withdraw route 192.168.5{}.0/24 next-hop 192.168.101.5\n".format(
+ prefix_iter
+ )
+ )
+ pipe.close()
+ sleep(0.1) # ExaBGP API command processing delay
+ sleep(1) # Give the BGP session on router r1 time to process routes
+ for prefix_iter in range(1, 5):
+ pipe = open("/run/exabgp_peer3.in", "w")
+ with pipe:
+ pipe.write(
+ "announce route 192.168.5{}.0/24 next-hop 192.168.101.5\n".format(
+ prefix_iter
+ )
+ )
+ pipe.close()
+ sleep(0.1) # ExaBGP API command processing delay
+
+ # Check damped paths on r1 for routes of peer3 witn peer profile
+ logger.info(
+ "Checking if router r1 used the correct dampening profile on routes flapped by ExaBGP peer peer3"
+ )
+ sleep(5) # wait 5 seconds for paths to shop up in damp list
+ vtyout = r_1.cmd('vtysh -c "show ip bgp dampening dampened-paths"')
+ routes = re.findall(r"\*d 192\.168\.5\d\.0\/24.*", vtyout)
+ assertmsg = (
+ "BGP session on router r1 did not dampen routes flapped by ExaBGP peer peer3"
+ )
+ assert len(routes) == 4, assertmsg
+ assertmsg = "BGP session on router r1 used wrong dampening profile for a route flapped by ExaBGP peer peer3"
+ for route in routes:
+ assert (int(route.split()[3].split(":")[0]) == 0) and ( # hours of reuse time
+ 35 > int(route.split()[3].split(":")[1]) > 25
+ ), assertmsg # minutes of reuse time
+
+ # end test_bgp_dampening_profile_peer_over_global
+
+
+def test_bgp_dampening_profile_global():
+ "Test of BGP route-flap dampening global profile"
+
+ # This test verifies the application of the global dampening profile by
+ # flapping the routes of a peer until dampened and comparing the reuse times
+ # to the one specified in the dampening configuration.
+
+ tgen = get_topogen()
+ r_1 = tgen.net["r1"]
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Starting test of BGP route-flap dampening global profile")
+
+ # Flapping routes on ExaBGP peer peer4
+ logger.info(
+ "Flapping routes on ExaBGP peer peer4 with route-flap dampening enabled"
+ )
+ for _ in range(1, 5):
+ for prefix_iter in range(1, 5):
+ pipe = open("/run/exabgp_peer4.in", "w")
+ with pipe:
+ pipe.write(
+ "withdraw route 192.168.6{}.0/24 next-hop 192.168.101.6\n".format(
+ prefix_iter
+ )
+ )
+ pipe.close()
+ sleep(0.1) # ExaBGP API command processing delay
+ sleep(1) # Give the BGP session on router r1 time to process routes
+ for prefix_iter in range(1, 5):
+ pipe = open("/run/exabgp_peer4.in", "w")
+ with pipe:
+ pipe.write(
+ "announce route 192.168.6{}.0/24 next-hop 192.168.101.6\n".format(
+ prefix_iter
+ )
+ )
+ pipe.close()
+ sleep(0.1) # ExaBGP API command processing delay
+
+ # Check damped paths on r1 for routes of peer4 witn global profile
+ logger.info(
+ "Checking if router r1 used the global dampening profile on routes flapped by ExaBGP peer peer4"
+ )
+ sleep(5) # wait 5 seconds for paths to shop up in damp list
+ vtyout = r_1.cmd('vtysh -c "show ip bgp dampening dampened-paths"')
+ routes = re.findall(r"\*d 192\.168\.6\d\.0\/24.*", vtyout)
+ assertmsg = (
+ "BGP session on router r1 did not dampen routes flapped by ExaBGP peer peer4"
+ )
+ assert len(routes) == 4, assertmsg
+ assertmsg = "BGP session on router r1 did not use the global dampening profile for a route flapped by ExaBGP peer peer4"
+ for route in routes:
+ assert (int(route.split()[3].split(":")[0]) == 1) and ( # hours of reuse time
+ 35 > int(route.split()[3].split(":")[1]) > 25
+ ), assertmsg # minutes of reuse time
+
+ # end test_bgp_dampening_profile_global
+
+
+def test_bgp_dampening_withdaw():
+ "Test BGP route-flap dampening route withdraw"
+
+ # This test verifies that the withrawl of dampened routes from the RIB of
+ # router r1 was propagated to router r2.
+
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Starting test of BGP route-flap dampening route withdraw")
+
+ # Check if routes dampened on router r1 have been withdrawn from the RIB on
+ # router r2
+ logger.info(
+ "Checking if routes dampened on router r1 have been withdrawn of RIB on router r2"
+ )
+ reffile = os.path.join(CWD, "r2/bgp_damp_withdrawn.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp, tgen.gears["r2"], "show ip bgp json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=5, wait=1)
+ assertmsg = "BGP session on router r2 did not receive withdraw of routes dampened on router r1"
+ assert res is None, assertmsg
+
+ # end test_bgp_dampening_withdaw
+
+
+def test_bgp_dampening_cleanup():
+ "BGP route-flap dampening test cleanup"
+
+ # This test cleans up after other tests associated with route-flap dampening
+ # by disabling all dampening configurations, removing added peers and
+ # peer-groups from the configuration on router r1, and shutting down ExaBGP
+ # peers peer1, peer2 and peer3.
+
+ tgen = get_topogen()
+ r_1 = tgen.net["r1"]
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Starting BGP route-flap dampening test cleanup")
+
+ # Disable all dampening configurations
+ logger.info("Disabling all dampening configurations")
+ r_1.cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "no bgp dampening"'
+ )
+ r_1.cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "no neighbor group1 dampening"'
+ )
+ r_1.cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "no neighbor 192.168.101.3 dampening"'
+ )
+ r_1.cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "no neighbor 192.168.101.5 dampening"'
+ )
+
+ # Remove ExaBGP peers from configuration of router r1
+ logger.info("Removing ExaBGP peers from configuration of router r1")
+ for router_num in range(3, 7):
+ r_1.cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.101.{}"'.format(
+ router_num
+ )
+ )
+
+ # Remove peer-group group1 from configuration of router r1
+ logger.info("Removing peer-group group1 peers from configuration of router r1")
+ r_1.cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor group1 peer-group"'
+ )
+
+ # Stop ExaBGP peers and remove associated named pipes
+ logger.info("Stopping ExaBGP peers and removing associated named pipes")
+ for peer_num in range(1, 5):
+ logger.info("Terminating ExaBGP on peer peer{}".format(peer_num))
+ peer = tgen.gears["peer{}".format(peer_num)]
+ logger.info("Removing named pipe of ExaBGP peer peer{}".format(peer_num))
+ fifo_in = "/var/run/exabgp_peer{}.in".format(peer_num)
+ peer.stop()
+ if os.path.exists(fifo_in):
+ os.remove(fifo_in)
+
+ # end test_bgp_dampening_cleanup
+
+
+def test_bgp_dampening_aftermath():
+ "BGP route-flap dampening aftermath test"
+
+ # This test verifies routers r1 and r2 not being affected by the route-flap
+ # dampening test series.
+
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Check BGP Summary on routers r1 and r2
+ for rtr_num in [1, 2]:
+ logger.info(
+ "Checking if BGP router on r{} remains unaffected by route-flap dampening tests".format(
+ rtr_num
+ )
+ )
+ router = tgen.gears["r{}".format(rtr_num)]
+ reffile = os.path.join(CWD, "r{}/show_bgp.json".format(rtr_num))
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=10, wait=2)
+ assertmsg = "BGP routes on router r{} are wrong after route-flap dampening tests".format(
+ rtr_num
+ )
+ assert res is None, assertmsg
+
+ # end test_bgp_dampening_aftermath
+
+
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_lu_topo1/R1/bgpd.conf b/tests/topotests/bgp_lu_topo1/R1/bgpd.conf
new file mode 100644
index 0000000000..1bdb4c7a3e
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo1/R1/bgpd.conf
@@ -0,0 +1,21 @@
+!
+debug bgp labelpool
+debug bgp zebra
+!
+router bgp 1
+ bgp router-id 10.0.0.1
+ timers bgp 3 9
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 10.0.0.2 remote-as 2
+ neighbor 10.0.0.2 solo
+ neighbor 10.0.0.2 timers connect 10
+!
+ address-family ipv4 unicast
+ no neighbor 10.0.0.2 activate
+ exit-address-family
+ !
+ address-family ipv4 labeled-unicast
+ neighbor 10.0.0.2 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_lu_topo1/R1/labelpool.summ.json b/tests/topotests/bgp_lu_topo1/R1/labelpool.summ.json
new file mode 100644
index 0000000000..29e6c2cbf7
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo1/R1/labelpool.summ.json
@@ -0,0 +1,8 @@
+{
+ "Ledger":506,
+ "InUse":506,
+ "Requests":0,
+ "LabelChunks":11,
+ "Pending":0,
+ "Reconnects":0
+}
diff --git a/tests/topotests/bgp_lu_topo1/R1/zebra.conf b/tests/topotests/bgp_lu_topo1/R1/zebra.conf
new file mode 100644
index 0000000000..4f6fee579f
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo1/R1/zebra.conf
@@ -0,0 +1,6 @@
+debug zebra events
+debug zebra dplane
+debug zebra mpls
+!
+interface R1-eth0
+ ip address 10.0.0.1/24
diff --git a/tests/topotests/bgp_lu_topo1/R2/bgpd.conf b/tests/topotests/bgp_lu_topo1/R2/bgpd.conf
new file mode 100644
index 0000000000..bac608e1c3
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo1/R2/bgpd.conf
@@ -0,0 +1,23 @@
+debug bgp labelpool
+debug bgp zebra
+!
+router bgp 2
+ bgp router-id 10.0.0.2
+ timers bgp 3 9
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 10.0.1.3 remote-as 2
+ neighbor 10.0.1.3 update-source 10.0.1.2
+ neighbor 10.0.1.3 timers connect 10
+ neighbor 10.0.0.1 remote-as 1
+ neighbor 10.0.0.1 timers connect 10
+!
+ address-family ipv4 unicast
+ neighbor 10.0.1.3 activate
+ no neighbor 10.0.0.1 activate
+ exit-address-family
+ !
+ address-family ipv4 labeled-unicast
+ neighbor 10.0.0.1 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json b/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json
new file mode 100644
index 0000000000..29e6c2cbf7
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json
@@ -0,0 +1,8 @@
+{
+ "Ledger":506,
+ "InUse":506,
+ "Requests":0,
+ "LabelChunks":11,
+ "Pending":0,
+ "Reconnects":0
+}
diff --git a/tests/topotests/bgp_lu_topo1/R2/zebra.conf b/tests/topotests/bgp_lu_topo1/R2/zebra.conf
new file mode 100644
index 0000000000..33ee53efe7
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo1/R2/zebra.conf
@@ -0,0 +1,11 @@
+!
+debug zebra events
+debug zebra dplane
+debug zebra mpls
+!
+interface R2-eth0
+ ip address 10.0.0.2/24
+!
+interface R2-eth1
+ ip address 10.0.1.2/24
+! \ No newline at end of file
diff --git a/tests/topotests/bgp_lu_topo1/R3/bgpd.conf b/tests/topotests/bgp_lu_topo1/R3/bgpd.conf
new file mode 100644
index 0000000000..b42df022e0
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo1/R3/bgpd.conf
@@ -0,0 +1,523 @@
+log file /tmp/bgpd.log
+!
+debug bgp updates
+!
+router bgp 2
+ bgp router-id 10.0.1.3
+ timers bgp 3 9
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 10.0.1.2 remote-as 2
+ neighbor 10.0.1.2 timers connect 10
+ !
+ address-family ipv4 unicast
+ neighbor 10.0.1.2 activate
+ network 11.0.0.1/32
+ network 11.0.0.2/32
+ network 11.0.0.3/32
+ network 11.0.0.4/32
+ network 11.0.0.5/32
+ network 11.0.0.6/32
+ network 11.0.0.7/32
+ network 11.0.0.8/32
+ network 11.0.0.9/32
+ network 11.0.0.10/32
+ network 11.0.0.11/32
+ network 11.0.0.12/32
+ network 11.0.0.13/32
+ network 11.0.0.14/32
+ network 11.0.0.15/32
+ network 11.0.0.16/32
+ network 11.0.0.17/32
+ network 11.0.0.18/32
+ network 11.0.0.19/32
+ network 11.0.0.20/32
+ network 11.0.0.21/32
+ network 11.0.0.22/32
+ network 11.0.0.23/32
+ network 11.0.0.24/32
+ network 11.0.0.25/32
+ network 11.0.0.26/32
+ network 11.0.0.27/32
+ network 11.0.0.28/32
+ network 11.0.0.29/32
+ network 11.0.0.30/32
+ network 11.0.0.31/32
+ network 11.0.0.32/32
+ network 11.0.0.33/32
+ network 11.0.0.34/32
+ network 11.0.0.35/32
+ network 11.0.0.36/32
+ network 11.0.0.37/32
+ network 11.0.0.38/32
+ network 11.0.0.39/32
+ network 11.0.0.40/32
+ network 11.0.0.41/32
+ network 11.0.0.42/32
+ network 11.0.0.43/32
+ network 11.0.0.44/32
+ network 11.0.0.45/32
+ network 11.0.0.46/32
+ network 11.0.0.47/32
+ network 11.0.0.48/32
+ network 11.0.0.49/32
+ network 11.0.0.50/32
+ network 11.0.0.51/32
+ network 11.0.0.52/32
+ network 11.0.0.53/32
+ network 11.0.0.54/32
+ network 11.0.0.55/32
+ network 11.0.0.56/32
+ network 11.0.0.57/32
+ network 11.0.0.58/32
+ network 11.0.0.59/32
+ network 11.0.0.60/32
+ network 11.0.0.61/32
+ network 11.0.0.62/32
+ network 11.0.0.63/32
+ network 11.0.0.64/32
+ network 11.0.0.65/32
+ network 11.0.0.66/32
+ network 11.0.0.67/32
+ network 11.0.0.68/32
+ network 11.0.0.69/32
+ network 11.0.0.70/32
+ network 11.0.0.71/32
+ network 11.0.0.72/32
+ network 11.0.0.73/32
+ network 11.0.0.74/32
+ network 11.0.0.75/32
+ network 11.0.0.76/32
+ network 11.0.0.77/32
+ network 11.0.0.78/32
+ network 11.0.0.79/32
+ network 11.0.0.80/32
+ network 11.0.0.81/32
+ network 11.0.0.82/32
+ network 11.0.0.83/32
+ network 11.0.0.84/32
+ network 11.0.0.85/32
+ network 11.0.0.86/32
+ network 11.0.0.87/32
+ network 11.0.0.88/32
+ network 11.0.0.89/32
+ network 11.0.0.90/32
+ network 11.0.0.91/32
+ network 11.0.0.92/32
+ network 11.0.0.93/32
+ network 11.0.0.94/32
+ network 11.0.0.95/32
+ network 11.0.0.96/32
+ network 11.0.0.97/32
+ network 11.0.0.98/32
+ network 11.0.0.99/32
+ network 11.0.0.100/32
+ network 11.0.0.101/32
+ network 11.0.0.102/32
+ network 11.0.0.103/32
+ network 11.0.0.104/32
+ network 11.0.0.105/32
+ network 11.0.0.106/32
+ network 11.0.0.107/32
+ network 11.0.0.108/32
+ network 11.0.0.109/32
+ network 11.0.0.110/32
+ network 11.0.0.111/32
+ network 11.0.0.112/32
+ network 11.0.0.113/32
+ network 11.0.0.114/32
+ network 11.0.0.115/32
+ network 11.0.0.116/32
+ network 11.0.0.117/32
+ network 11.0.0.118/32
+ network 11.0.0.119/32
+ network 11.0.0.120/32
+ network 11.0.0.121/32
+ network 11.0.0.122/32
+ network 11.0.0.123/32
+ network 11.0.0.124/32
+ network 11.0.0.125/32
+ network 11.0.0.126/32
+ network 11.0.0.127/32
+ network 11.0.0.128/32
+ network 11.0.0.129/32
+ network 11.0.0.130/32
+ network 11.0.0.131/32
+ network 11.0.0.132/32
+ network 11.0.0.133/32
+ network 11.0.0.134/32
+ network 11.0.0.135/32
+ network 11.0.0.136/32
+ network 11.0.0.137/32
+ network 11.0.0.138/32
+ network 11.0.0.139/32
+ network 11.0.0.140/32
+ network 11.0.0.141/32
+ network 11.0.0.142/32
+ network 11.0.0.143/32
+ network 11.0.0.144/32
+ network 11.0.0.145/32
+ network 11.0.0.146/32
+ network 11.0.0.147/32
+ network 11.0.0.148/32
+ network 11.0.0.149/32
+ network 11.0.0.150/32
+ network 11.0.0.151/32
+ network 11.0.0.152/32
+ network 11.0.0.153/32
+ network 11.0.0.154/32
+ network 11.0.0.155/32
+ network 11.0.0.156/32
+ network 11.0.0.157/32
+ network 11.0.0.158/32
+ network 11.0.0.159/32
+ network 11.0.0.160/32
+ network 11.0.0.161/32
+ network 11.0.0.162/32
+ network 11.0.0.163/32
+ network 11.0.0.164/32
+ network 11.0.0.165/32
+ network 11.0.0.166/32
+ network 11.0.0.167/32
+ network 11.0.0.168/32
+ network 11.0.0.169/32
+ network 11.0.0.170/32
+ network 11.0.0.171/32
+ network 11.0.0.172/32
+ network 11.0.0.173/32
+ network 11.0.0.174/32
+ network 11.0.0.175/32
+ network 11.0.0.176/32
+ network 11.0.0.177/32
+ network 11.0.0.178/32
+ network 11.0.0.179/32
+ network 11.0.0.180/32
+ network 11.0.0.181/32
+ network 11.0.0.182/32
+ network 11.0.0.183/32
+ network 11.0.0.184/32
+ network 11.0.0.185/32
+ network 11.0.0.186/32
+ network 11.0.0.187/32
+ network 11.0.0.188/32
+ network 11.0.0.189/32
+ network 11.0.0.190/32
+ network 11.0.0.191/32
+ network 11.0.0.192/32
+ network 11.0.0.193/32
+ network 11.0.0.194/32
+ network 11.0.0.195/32
+ network 11.0.0.196/32
+ network 11.0.0.197/32
+ network 11.0.0.198/32
+ network 11.0.0.199/32
+ network 11.0.0.200/32
+ network 11.0.0.201/32
+ network 11.0.0.202/32
+ network 11.0.0.203/32
+ network 11.0.0.204/32
+ network 11.0.0.205/32
+ network 11.0.0.206/32
+ network 11.0.0.207/32
+ network 11.0.0.208/32
+ network 11.0.0.209/32
+ network 11.0.0.210/32
+ network 11.0.0.211/32
+ network 11.0.0.212/32
+ network 11.0.0.213/32
+ network 11.0.0.214/32
+ network 11.0.0.215/32
+ network 11.0.0.216/32
+ network 11.0.0.217/32
+ network 11.0.0.218/32
+ network 11.0.0.219/32
+ network 11.0.0.220/32
+ network 11.0.0.221/32
+ network 11.0.0.222/32
+ network 11.0.0.223/32
+ network 11.0.0.224/32
+ network 11.0.0.225/32
+ network 11.0.0.226/32
+ network 11.0.0.227/32
+ network 11.0.0.228/32
+ network 11.0.0.229/32
+ network 11.0.0.230/32
+ network 11.0.0.231/32
+ network 11.0.0.232/32
+ network 11.0.0.233/32
+ network 11.0.0.234/32
+ network 11.0.0.235/32
+ network 11.0.0.236/32
+ network 11.0.0.237/32
+ network 11.0.0.238/32
+ network 11.0.0.239/32
+ network 11.0.0.240/32
+ network 11.0.0.241/32
+ network 11.0.0.242/32
+ network 11.0.0.243/32
+ network 11.0.0.244/32
+ network 11.0.0.245/32
+ network 11.0.0.246/32
+ network 11.0.0.247/32
+ network 11.0.0.248/32
+ network 11.0.0.249/32
+ network 11.0.0.250/32
+ network 11.0.0.251/32
+ network 11.0.0.252/32
+ network 11.0.0.253/32
+ network 11.0.1.1/32
+ network 11.0.1.2/32
+ network 11.0.1.3/32
+ network 11.0.1.4/32
+ network 11.0.1.5/32
+ network 11.0.1.6/32
+ network 11.0.1.7/32
+ network 11.0.1.8/32
+ network 11.0.1.9/32
+ network 11.0.1.10/32
+ network 11.0.1.11/32
+ network 11.0.1.12/32
+ network 11.0.1.13/32
+ network 11.0.1.14/32
+ network 11.0.1.15/32
+ network 11.0.1.16/32
+ network 11.0.1.17/32
+ network 11.0.1.18/32
+ network 11.0.1.19/32
+ network 11.0.1.20/32
+ network 11.0.1.21/32
+ network 11.0.1.22/32
+ network 11.0.1.23/32
+ network 11.0.1.24/32
+ network 11.0.1.25/32
+ network 11.0.1.26/32
+ network 11.0.1.27/32
+ network 11.0.1.28/32
+ network 11.0.1.29/32
+ network 11.0.1.30/32
+ network 11.0.1.31/32
+ network 11.0.1.32/32
+ network 11.0.1.33/32
+ network 11.0.1.34/32
+ network 11.0.1.35/32
+ network 11.0.1.36/32
+ network 11.0.1.37/32
+ network 11.0.1.38/32
+ network 11.0.1.39/32
+ network 11.0.1.40/32
+ network 11.0.1.41/32
+ network 11.0.1.42/32
+ network 11.0.1.43/32
+ network 11.0.1.44/32
+ network 11.0.1.45/32
+ network 11.0.1.46/32
+ network 11.0.1.47/32
+ network 11.0.1.48/32
+ network 11.0.1.49/32
+ network 11.0.1.50/32
+ network 11.0.1.51/32
+ network 11.0.1.52/32
+ network 11.0.1.53/32
+ network 11.0.1.54/32
+ network 11.0.1.55/32
+ network 11.0.1.56/32
+ network 11.0.1.57/32
+ network 11.0.1.58/32
+ network 11.0.1.59/32
+ network 11.0.1.60/32
+ network 11.0.1.61/32
+ network 11.0.1.62/32
+ network 11.0.1.63/32
+ network 11.0.1.64/32
+ network 11.0.1.65/32
+ network 11.0.1.66/32
+ network 11.0.1.67/32
+ network 11.0.1.68/32
+ network 11.0.1.69/32
+ network 11.0.1.70/32
+ network 11.0.1.71/32
+ network 11.0.1.72/32
+ network 11.0.1.73/32
+ network 11.0.1.74/32
+ network 11.0.1.75/32
+ network 11.0.1.76/32
+ network 11.0.1.77/32
+ network 11.0.1.78/32
+ network 11.0.1.79/32
+ network 11.0.1.80/32
+ network 11.0.1.81/32
+ network 11.0.1.82/32
+ network 11.0.1.83/32
+ network 11.0.1.84/32
+ network 11.0.1.85/32
+ network 11.0.1.86/32
+ network 11.0.1.87/32
+ network 11.0.1.88/32
+ network 11.0.1.89/32
+ network 11.0.1.90/32
+ network 11.0.1.91/32
+ network 11.0.1.92/32
+ network 11.0.1.93/32
+ network 11.0.1.94/32
+ network 11.0.1.95/32
+ network 11.0.1.96/32
+ network 11.0.1.97/32
+ network 11.0.1.98/32
+ network 11.0.1.99/32
+ network 11.0.1.100/32
+ network 11.0.1.101/32
+ network 11.0.1.102/32
+ network 11.0.1.103/32
+ network 11.0.1.104/32
+ network 11.0.1.105/32
+ network 11.0.1.106/32
+ network 11.0.1.107/32
+ network 11.0.1.108/32
+ network 11.0.1.109/32
+ network 11.0.1.110/32
+ network 11.0.1.111/32
+ network 11.0.1.112/32
+ network 11.0.1.113/32
+ network 11.0.1.114/32
+ network 11.0.1.115/32
+ network 11.0.1.116/32
+ network 11.0.1.117/32
+ network 11.0.1.118/32
+ network 11.0.1.119/32
+ network 11.0.1.120/32
+ network 11.0.1.121/32
+ network 11.0.1.122/32
+ network 11.0.1.123/32
+ network 11.0.1.124/32
+ network 11.0.1.125/32
+ network 11.0.1.126/32
+ network 11.0.1.127/32
+ network 11.0.1.128/32
+ network 11.0.1.129/32
+ network 11.0.1.130/32
+ network 11.0.1.131/32
+ network 11.0.1.132/32
+ network 11.0.1.133/32
+ network 11.0.1.134/32
+ network 11.0.1.135/32
+ network 11.0.1.136/32
+ network 11.0.1.137/32
+ network 11.0.1.138/32
+ network 11.0.1.139/32
+ network 11.0.1.140/32
+ network 11.0.1.141/32
+ network 11.0.1.142/32
+ network 11.0.1.143/32
+ network 11.0.1.144/32
+ network 11.0.1.145/32
+ network 11.0.1.146/32
+ network 11.0.1.147/32
+ network 11.0.1.148/32
+ network 11.0.1.149/32
+ network 11.0.1.150/32
+ network 11.0.1.151/32
+ network 11.0.1.152/32
+ network 11.0.1.153/32
+ network 11.0.1.154/32
+ network 11.0.1.155/32
+ network 11.0.1.156/32
+ network 11.0.1.157/32
+ network 11.0.1.158/32
+ network 11.0.1.159/32
+ network 11.0.1.160/32
+ network 11.0.1.161/32
+ network 11.0.1.162/32
+ network 11.0.1.163/32
+ network 11.0.1.164/32
+ network 11.0.1.165/32
+ network 11.0.1.166/32
+ network 11.0.1.167/32
+ network 11.0.1.168/32
+ network 11.0.1.169/32
+ network 11.0.1.170/32
+ network 11.0.1.171/32
+ network 11.0.1.172/32
+ network 11.0.1.173/32
+ network 11.0.1.174/32
+ network 11.0.1.175/32
+ network 11.0.1.176/32
+ network 11.0.1.177/32
+ network 11.0.1.178/32
+ network 11.0.1.179/32
+ network 11.0.1.180/32
+ network 11.0.1.181/32
+ network 11.0.1.182/32
+ network 11.0.1.183/32
+ network 11.0.1.184/32
+ network 11.0.1.185/32
+ network 11.0.1.186/32
+ network 11.0.1.187/32
+ network 11.0.1.188/32
+ network 11.0.1.189/32
+ network 11.0.1.190/32
+ network 11.0.1.191/32
+ network 11.0.1.192/32
+ network 11.0.1.193/32
+ network 11.0.1.194/32
+ network 11.0.1.195/32
+ network 11.0.1.196/32
+ network 11.0.1.197/32
+ network 11.0.1.198/32
+ network 11.0.1.199/32
+ network 11.0.1.200/32
+ network 11.0.1.201/32
+ network 11.0.1.202/32
+ network 11.0.1.203/32
+ network 11.0.1.204/32
+ network 11.0.1.205/32
+ network 11.0.1.206/32
+ network 11.0.1.207/32
+ network 11.0.1.208/32
+ network 11.0.1.209/32
+ network 11.0.1.210/32
+ network 11.0.1.211/32
+ network 11.0.1.212/32
+ network 11.0.1.213/32
+ network 11.0.1.214/32
+ network 11.0.1.215/32
+ network 11.0.1.216/32
+ network 11.0.1.217/32
+ network 11.0.1.218/32
+ network 11.0.1.219/32
+ network 11.0.1.220/32
+ network 11.0.1.221/32
+ network 11.0.1.222/32
+ network 11.0.1.223/32
+ network 11.0.1.224/32
+ network 11.0.1.225/32
+ network 11.0.1.226/32
+ network 11.0.1.227/32
+ network 11.0.1.228/32
+ network 11.0.1.229/32
+ network 11.0.1.230/32
+ network 11.0.1.231/32
+ network 11.0.1.232/32
+ network 11.0.1.233/32
+ network 11.0.1.234/32
+ network 11.0.1.235/32
+ network 11.0.1.236/32
+ network 11.0.1.237/32
+ network 11.0.1.238/32
+ network 11.0.1.239/32
+ network 11.0.1.240/32
+ network 11.0.1.241/32
+ network 11.0.1.242/32
+ network 11.0.1.243/32
+ network 11.0.1.244/32
+ network 11.0.1.245/32
+ network 11.0.1.246/32
+ network 11.0.1.247/32
+ network 11.0.1.248/32
+ network 11.0.1.249/32
+ network 11.0.1.250/32
+ network 11.0.1.251/32
+ network 11.0.1.252/32
+ network 11.0.1.253/32
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_lu_topo1/R3/zebra.conf b/tests/topotests/bgp_lu_topo1/R3/zebra.conf
new file mode 100644
index 0000000000..524978bff6
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo1/R3/zebra.conf
@@ -0,0 +1,9 @@
+log file /tmp/zebra.log
+!
+debug zebra events
+debug zebra packet detail
+debug zebra mpls
+!
+interface R3-eth0
+ ip address 10.0.1.3/24
+!
diff --git a/tests/topotests/bgp_lu_topo1/test_bgp_lu.py b/tests/topotests/bgp_lu_topo1/test_bgp_lu.py
new file mode 100644
index 0000000000..61418d7a79
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo1/test_bgp_lu.py
@@ -0,0 +1,178 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_lu.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# 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 NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF 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.
+#
+
+"""
+test_bgp_lu.py: Test BGP LU label allocation
+"""
+
+import os
+import sys
+import json
+from functools import partial
+from time import sleep
+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, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+from mininet.topo import Topo
+
+#Basic scenario for BGP-LU. Nodes are directly connected.
+#Node 3 is advertising many routes to 2, which advertises them
+#as BGP-LU to 1; this way we get routes with actual labels, as
+#opposed to implicit-null routes in the 2-node case.
+#
+# AS1 BGP-LU AS2 iBGP AS2
+#+-----+ +-----+ +-----+
+#| |.1 .2| |.2 .3| |
+#| 1 +----------------+ 2 +-----------------+ 3 |
+#| | 10.0.0.0/24 | | 10.0.1.0/24 | |
+#+-----+ +-----+ +-----+
+
+class TemplateTopo(Topo):
+ "Test topology builder"
+
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ # This function only purpose is to define allocation and relationship
+ # between routers, switches and hosts.
+ #
+ #
+ # Create routers
+ tgen.add_router("R1")
+ tgen.add_router("R2")
+ tgen.add_router("R3")
+
+ # R1-R2
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["R1"])
+ switch.add_link(tgen.gears["R2"])
+
+ # R2-R3
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["R2"])
+ switch.add_link(tgen.gears["R3"])
+
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(TemplateTopo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+ tgen.start_topology()
+
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+
+ # For all registred routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ # After loading the configurations, this function loads configured daemons.
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+def check_labelpool(router):
+ json_file = "{}/{}/labelpool.summ.json".format(CWD, router.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(topotest.router_json_cmp, router, "show bgp labelpool summary json", expected)
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ assertmsg = '"{}" JSON output mismatches - Did not converge'.format(router.name)
+ assert result is None, assertmsg
+
+def test_converge_bgplu():
+ "Wait for protocol convergence"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ #tgen.mininet_cli();
+ r1 = tgen.gears["R1"]
+ r2 = tgen.gears["R2"]
+
+ check_labelpool(r1)
+ check_labelpool(r2)
+
+def test_clear_bgplu():
+ "Wait for protocol convergence"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ #tgen.mininet_cli();
+ r1 = tgen.gears["R1"]
+ r2 = tgen.gears["R2"]
+
+ r1.vtysh_cmd("clear bgp 10.0.0.2")
+ check_labelpool(r1)
+ check_labelpool(r2)
+
+ r2.vtysh_cmd("clear bgp 10.0.1.3")
+ check_labelpool(r1)
+ check_labelpool(r2)
+
+ r1.vtysh_cmd("clear bgp 10.0.0.2")
+ r2.vtysh_cmd("clear bgp 10.0.1.3")
+ check_labelpool(r1)
+ check_labelpool(r2)
+
+def test_memory_leak():
+ "Run the memory leak test and report results."
+ tgen = get_topogen()
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py b/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py
index d34446e2ee..19a9140c13 100644
--- a/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py
+++ b/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py
@@ -2687,6 +2687,9 @@ def test_delete_and_re_add_vrf_p1(request):
}
}
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {}: Failed\n Error {}".format(tc_name, result)
+
result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2)
assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
diff --git a/tests/topotests/isis-sr-te-topo1/test_isis_sr_te_topo1.py b/tests/topotests/isis-sr-te-topo1/test_isis_sr_te_topo1.py
index 5e5c0f9fee..b1071310cf 100755
--- a/tests/topotests/isis-sr-te-topo1/test_isis_sr_te_topo1.py
+++ b/tests/topotests/isis-sr-te-topo1/test_isis_sr_te_topo1.py
@@ -150,7 +150,13 @@ class TemplateTopo(Topo):
def setup_module(mod):
"Sets up the pytest environment"
+
tgen = Topogen(TemplateTopo, mod.__name__)
+
+ frrdir = tgen.config.get(tgen.CONFIG_SECTION, "frrdir")
+ if not os.path.isfile(os.path.join(frrdir, "pathd")):
+ pytest.skip("pathd daemon wasn't built")
+
tgen.start_topology()
router_list = tgen.routers()
diff --git a/tests/topotests/ospf_basic_functionality/ospf_p2mp.json b/tests/topotests/ospf_basic_functionality/ospf_p2mp.json
new file mode 100644
index 0000000000..40815f36f3
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/ospf_p2mp.json
@@ -0,0 +1,198 @@
+{
+
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-multipoint"
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-multipoint"
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-multipoint"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-multipoint"
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-multipoint"
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-multipoint"
+ }
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-multipoint"
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-multipoint"
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-multipoint"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-multipoint"
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-multipoint"
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-multipoint"
+ }
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR1",
+ "ospf": {
+ "area": "0.0.0.0"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py b/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py
new file mode 100644
index 0000000000..c90275ecb0
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py
@@ -0,0 +1,416 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2020 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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+import json
+from copy import deepcopy
+from ipaddress import IPv4Address
+
+# 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, "../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from mininet.topo import Topo
+from lib.topogen import Topogen, get_topogen
+import ipaddress
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ step,
+ create_route_maps,
+ shutdown_bringup_interface,
+ create_interfaces_cfg,
+ topo_daemons,
+)
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+from lib.ospf import (
+ verify_ospf_neighbor,
+ config_ospf_interface,
+ clear_ospf,
+ verify_ospf_rib,
+ create_router_ospf,
+ verify_ospf_interface,
+ verify_ospf_database,
+)
+
+# Global variables
+topo = None
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/ospf_p2mp.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+"""
+TOPOOLOGY =
+ Please view in a fixed-width font such as Courier.
+ +---+ A1 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A2
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A3 +---+
+
+TESTCASES =
+1. OSPF P2MP -Verify state change events on p2mp network.
+ """
+
+
+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 topo
+ 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__)
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen, daemons)
+
+ # 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)
+
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ 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()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospf_p2mp_tc1_p0(request):
+ """OSPF IFSM -Verify state change events on p2mp network."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+ step(
+ "Verify that OSPF is subscribed to multi cast services "
+ "(All SPF, all DR Routers)."
+ )
+ step("Verify that interface is enabled in ospf.")
+ step("Verify that config is successful.")
+ dut = "r0"
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {"ospf": {"mcastMemberOspfAllRouters": True, "ospfEnabled": True}}
+ }
+ }
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the ip address")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv4": topo["routers"]["r0"]["links"]["r3"]["ipv4"],
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Change the ip on the R0 interface")
+
+ topo_modify_change_ip = deepcopy(topo)
+ intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"]
+ topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] = str(
+ IPv4Address(unicode(intf_ip.split("/")[0])) + 3
+ ) + "/{}".format(intf_ip.split("/")[1])
+
+ build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False)
+ step("Verify that interface is enabled in ospf.")
+ dut = "r0"
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ospf": {
+ "ipAddress": topo_modify_change_ip["routers"]["r0"]["links"][
+ "r3"
+ ]["ipv4"].split("/")[0],
+ "ipAddressPrefixlen": int(
+ topo_modify_change_ip["routers"]["r0"]["links"]["r3"][
+ "ipv4"
+ ].split("/")[1]
+ ),
+ }
+ }
+ }
+ }
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Modify the mask on the R0 interface")
+ ip_addr = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"]
+ mask = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"]
+ step("Delete the ip address")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv4": ip_addr,
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Change the ip on the R0 interface")
+
+ topo_modify_change_ip = deepcopy(topo)
+ intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"]
+ topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] = str(
+ IPv4Address(unicode(intf_ip.split("/")[0])) + 3
+ ) + "/{}".format(int(intf_ip.split("/")[1]) + 1)
+
+ build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False)
+ step("Verify that interface is enabled in ospf.")
+ dut = "r0"
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ospf": {
+ "ipAddress": topo_modify_change_ip["routers"]["r0"]["links"][
+ "r3"
+ ]["ipv4"].split("/")[0],
+ "ipAddressPrefixlen": int(
+ topo_modify_change_ip["routers"]["r0"]["links"]["r3"][
+ "ipv4"
+ ].split("/")[1]
+ ),
+ }
+ }
+ }
+ }
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv4": topo_modify_change_ip["routers"]["r0"]["links"]["r3"][
+ "ipv4"
+ ],
+ "interface": topo_modify_change_ip["routers"]["r0"]["links"]["r3"][
+ "interface"
+ ],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ build_config_from_json(tgen, topo, save_bkup=False)
+
+ step("Change the area id on the interface")
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "ospf": {"area": "0.0.0.0"},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "ospf": {"area": "0.0.0.1"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ step("Verify that interface is enabled in ospf.")
+ dut = "r0"
+ input_dict = {
+ "r0": {"links": {"r3": {"ospf": {"area": "0.0.0.1", "ospfEnabled": True}}}}
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "ospf": {"area": "0.0.0.1"},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "ospf": {"area": "0.0.0.0"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify if interface is enabled with network type P2MP")
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "ospf": {
+ "area": "0.0.0.0",
+ "networkType":"POINTOMULTIPOINT"
+ },
+ }
+ }
+ }
+ }
+ result = create_interfaces_cfg(tgen, input_dict)
+ 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/tools/etc/frr/support_bundle_commands.conf b/tools/etc/frr/support_bundle_commands.conf
index 11f88e7101..087c35981d 100644
--- a/tools/etc/frr/support_bundle_commands.conf
+++ b/tools/etc/frr/support_bundle_commands.conf
@@ -8,17 +8,17 @@
PROC_NAME:bgp
CMD_LIST_START
show bgp summary
-show ip bgp
-show ip bgp neighbors
-show ip bgp summary
-show ip bgp statistics
+show bgp ipv4 uni
+show bgp ipv4 neighbors
+show bgp ipv4 summary
+show bgp ipv4 statistics
-show ip bgp update-groups advertise-queue
-show ip bgp update-groups advertised-routes
-show ip bgp update-groups packet-queue
-show ip bgp update-groups statistics
-show ip bgp peer-group
-show ip bgp memory
+show bgp ipv4 update-groups advertise-queue
+show bgp ipv4 update-groups advertised-routes
+show bgp ipv4 update-groups packet-queue
+show bgp ipv4 update-groups statistics
+show bgp peer-group
+show bgp memory
show bgp ipv6
show bgp ipv6 neighbors
@@ -27,8 +27,9 @@ show bgp ipv6 update-groups advertise-queue
show bgp ipv6 update-groups advertised-routes
show bgp ipv6 update-groups packet-queue
show bgp ipv6 update-groups statistics
-show ip bgp statistics
+show bgp ipv6 statistics
show bgp martian next-hop
+show bgp nexthop
show bgp evpn route
CMD_LIST_END
@@ -38,13 +39,15 @@ PROC_NAME:zebra
CMD_LIST_START
show zebra
show zebra client summary
+show zebra router table summary
show ip nht vrf all
+show ipv6 nht vrf all
+show nexthop-group rib
show route-map
show memory
show interface vrf all
show vrf
show zebra fpm stats
-show error all
show work-queues
show debugging hashtable
show running-config
diff --git a/tools/frr-reload.py b/tools/frr-reload.py
index c18a314a0a..d5fa8ab6a3 100755
--- a/tools/frr-reload.py
+++ b/tools/frr-reload.py
@@ -684,26 +684,6 @@ end
log.debug("LINE %-50s: entering new context, %-50s", line, ctx_keys)
elif (
- line.startswith("peer ")
- and len(ctx_keys) == 4
- and ctx_keys[0].startswith("segment-routing")
- and ctx_keys[1].startswith("traffic-eng")
- and ctx_keys[2].startswith("pcep")
- and ctx_keys[3].startswith("pcc")
- ):
- # If there is no precedence, we add the default one (255) so
- # the line is not removed and added back
- m = re.search('peer ([^ ]*)', line)
- if (m != None):
- (name,) = m.groups()
- line = "peer %s precedence 255" % (name,)
-
- current_context_lines.append(line)
- log.debug(
- "LINE %-50s: append to current_context_lines, %-50s", line, ctx_keys
- )
-
- elif (
line.startswith("address-family ")
or line.startswith("vnc defaults")
or line.startswith("vnc l2-group")
@@ -1413,6 +1393,8 @@ def compare_context_objects(newconf, running):
lines_to_del = []
pollist_to_del = []
seglist_to_del = []
+ pceconf_to_del = []
+ pcclist_to_del = []
candidates_to_add = []
delete_bgpd = False
@@ -1498,9 +1480,8 @@ def compare_context_objects(newconf, running):
# Segment routing and traffic engineering never need to be deleted
elif (
- len(running_ctx_keys) > 1
+ running_ctx_keys[0].startswith('segment-routing')
and len(running_ctx_keys) < 3
- and running_ctx_keys[0].startswith('segment-routing')
):
continue
@@ -1508,7 +1489,7 @@ def compare_context_objects(newconf, running):
elif (
len(running_ctx_keys) == 3
and running_ctx_keys[0].startswith('segment-routing')
- and running_ctx_keys[2].startswith('pcep4')
+ and running_ctx_keys[2].startswith('pcep')
):
continue
@@ -1530,6 +1511,23 @@ def compare_context_objects(newconf, running):
):
pollist_to_del.append((running_ctx_keys, None))
+ # pce-config must be deleted after the pce, to be sure we add them
+ # to a separate array that is going to be appended at the end
+ elif (
+ len(running_ctx_keys) >= 4
+ and running_ctx_keys[0].startswith('segment-routing')
+ and running_ctx_keys[3].startswith('pce-config')
+ ):
+ pceconf_to_del.append((running_ctx_keys, None))
+
+ # pcc must be deleted after the pce and pce-config too
+ elif (
+ len(running_ctx_keys) >= 4
+ and running_ctx_keys[0].startswith('segment-routing')
+ and running_ctx_keys[3].startswith('pcc')
+ ):
+ pcclist_to_del.append((running_ctx_keys, None))
+
# Non-global context
elif running_ctx_keys and not any(
"address-family" in key for key in running_ctx_keys
@@ -1552,6 +1550,14 @@ def compare_context_objects(newconf, running):
if len(seglist_to_del) > 0:
lines_to_del.extend(seglist_to_del)
+ # if we have some pce list commands to delete, append them to lines_to_del
+ if len(pceconf_to_del) > 0:
+ lines_to_del.extend(pceconf_to_del)
+
+ # if we have some pcc list commands to delete, append them to lines_to_del
+ if len(pcclist_to_del) > 0:
+ lines_to_del.extend(pcclist_to_del)
+
# Find the lines within each context to add
# Find the lines within each context to del
for (newconf_ctx_keys, newconf_ctx) in iteritems(newconf.contexts):
@@ -1765,6 +1771,7 @@ if __name__ == "__main__":
"vrrpd",
"ldpd",
"pathd",
+ "bfdd",
]:
msg = "Daemon %s is not a valid option for 'show running-config'" % args.daemon
print(msg)
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index f3e28b70e8..a6f9f39a4c 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -1261,6 +1261,7 @@ static struct cmd_node pw_node = {
.prompt = "%s(config-pw)# ",
};
+#if defined(HAVE_PATHD)
static struct cmd_node segment_routing_node = {
.name = "segment-routing",
.node = SEGMENT_ROUTING_NODE,
@@ -1296,6 +1297,7 @@ static struct cmd_node srte_candidate_dyn_node = {
.prompt = "%s(config-sr-te-candidate)# ",
};
+#if defined(HAVE_PATHD_PCEP)
static struct cmd_node pcep_node = {
.name = "srte pcep",
.node = PCEP_NODE,
@@ -1323,6 +1325,8 @@ static struct cmd_node pcep_pce_config_node = {
.parent_node = PCEP_NODE,
.prompt = "%s(pcep-sr-te-pcep-pce-config)# ",
};
+#endif /* HAVE_PATHD_PCEP */
+#endif /* HAVE_PATHD */
static struct cmd_node vrf_node = {
.name = "vrf",
@@ -2548,6 +2552,7 @@ DEFUNSH(VTYSH_KEYS, vtysh_quit_keys, vtysh_quit_keys_cmd, "quit",
return vtysh_exit_keys(self, vty, argc, argv);
}
+#if defined(HAVE_PATHD)
DEFUNSH(VTYSH_PATHD, vtysh_exit_pathd, vtysh_exit_pathd_cmd, "exit",
"Exit current mode and down to previous mode\n")
{
@@ -2559,6 +2564,7 @@ DEFUNSH(VTYSH_PATHD, vtysh_quit_pathd, vtysh_quit_pathd_cmd, "quit",
{
return vtysh_exit_pathd(self, vty, argc, argv);
}
+#endif /* HAVE_PATHD */
DEFUNSH(VTYSH_ALL, vtysh_exit_line_vty, vtysh_exit_line_vty_cmd, "exit",
"Exit current mode and down to previous mode\n")
diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c
index b6ea6745e8..51ce59c477 100644
--- a/zebra/dplane_fpm_nl.c
+++ b/zebra/dplane_fpm_nl.c
@@ -1467,6 +1467,10 @@ static int fpm_nl_process(struct zebra_dplane_provider *prov)
thread_add_timer(fnc->fthread->master, fpm_process_queue,
fnc, 0, &fnc->t_dequeue);
+ /* Ensure dataplane thread is rescheduled if we hit the work limit */
+ if (counter >= limit)
+ dplane_provider_work_ready();
+
return 0;
}
diff --git a/zebra/label_manager.c b/zebra/label_manager.c
index ef51669022..2634a333ee 100644
--- a/zebra/label_manager.c
+++ b/zebra/label_manager.c
@@ -306,7 +306,7 @@ assign_label_chunk(uint8_t proto, unsigned short instance, uint32_t session_id,
{
struct label_manager_chunk *lmc;
struct listnode *node;
- uint32_t prev_end = 0;
+ uint32_t prev_end = MPLS_LABEL_UNRESERVED_MIN;
/* handle chunks request with a specific base label */
if (base != MPLS_LABEL_BASE_ANY)
@@ -328,8 +328,7 @@ assign_label_chunk(uint8_t proto, unsigned short instance, uint32_t session_id,
}
/* check if we hadve a "hole" behind us that we can squeeze into
*/
- if ((lmc->start > prev_end)
- && (lmc->start - prev_end >= size)) {
+ if ((lmc->start > prev_end) && (lmc->start - prev_end > size)) {
lmc = create_label_chunk(proto, instance, session_id,
keep, prev_end + 1,
prev_end + size);
diff --git a/zebra/sample_plugin.c b/zebra/sample_plugin.c
index c96a86cc73..464205f2f3 100644
--- a/zebra/sample_plugin.c
+++ b/zebra/sample_plugin.c
@@ -92,7 +92,6 @@ static int sample_process(struct zebra_dplane_provider *prov)
static int init_sample_plugin(struct thread_master *tm)
{
int ret;
- struct zebra_dplane_provider *prov = NULL;
/* Note that we don't use or store the thread_master 'tm'. We
* don't use the zebra main pthread: our plugin code will run in
diff --git a/zebra/subdir.am b/zebra/subdir.am
index cdacabc102..f842a8c0f3 100644
--- a/zebra/subdir.am
+++ b/zebra/subdir.am
@@ -40,6 +40,11 @@ if LINUX
module_LTLIBRARIES += zebra/zebra_cumulus_mlag.la
endif
+# Dataplane sample plugin
+if DEV_BUILD
+module_LTLIBRARIES += zebra/dplane_sample_plugin.la
+endif
+
man8 += $(MANBUILD)/frr-zebra.8
## endif ZEBRA
endif
@@ -206,6 +211,12 @@ zebra/zebra_fpm_dt.lo: fpm/fpm.pb-c.h qpb/qpb.pb-c.h
endif
endif
+# Sample dataplane plugin
+if DEV_BUILD
+zebra_dplane_sample_plugin_la_SOURCES = zebra/sample_plugin.c
+zebra_dplane_sample_plugin_la_LDFLAGS = -module -shared -avoid-version -export-dynamic
+endif
+
nodist_zebra_zebra_SOURCES = \
yang/frr-zebra.yang.c \
# end
diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c
index ddab2b8742..da7f4cf64e 100644
--- a/zebra/zebra_dplane.c
+++ b/zebra/zebra_dplane.c
@@ -4537,6 +4537,7 @@ static int dplane_thread_loop(struct thread *event)
struct zebra_dplane_ctx *ctx, *tctx;
int limit, counter, error_counter;
uint64_t curr, high;
+ bool reschedule = false;
/* Capture work limit per cycle */
limit = zdplane_info.dg_updates_per_cycle;
@@ -4673,6 +4674,9 @@ static int dplane_thread_loop(struct thread *event)
dplane_provider_unlock(prov);
+ if (counter >= limit)
+ reschedule = true;
+
if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)
zlog_debug("dplane dequeues %d completed work from provider %s",
counter, dplane_provider_get_name(prov));
@@ -4683,6 +4687,13 @@ static int dplane_thread_loop(struct thread *event)
DPLANE_UNLOCK();
}
+ /*
+ * We hit the work limit while processing at least one provider's
+ * output queue - ensure we come back and finish it.
+ */
+ if (reschedule)
+ dplane_provider_work_ready();
+
/* After all providers have been serviced, enqueue any completed
* work and any errors back to zebra so it can process the results.
*/
diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c
index 0ce724f608..bbc8b6f19d 100644
--- a/zebra/zebra_routemap.c
+++ b/zebra/zebra_routemap.c
@@ -1668,14 +1668,6 @@ void zebra_routemap_finish(void)
route_map_finish();
}
-void zebra_route_map_write_delay_timer(struct vty *vty)
-{
- if (vty && (zebra_rmap_update_timer != ZEBRA_RMAP_DEFAULT_UPDATE_TIMER))
- vty_out(vty, "zebra route-map delay-timer %d\n",
- zebra_rmap_update_timer);
- return;
-}
-
route_map_result_t
zebra_route_map_check(int family, int rib_type, uint8_t instance,
const struct prefix *p, struct nexthop *nexthop,
@@ -1870,7 +1862,8 @@ void zebra_routemap_config_write_protocol(struct vty *vty,
vty_out(vty, "%sipv6 nht %s route-map %s\n", space, "any",
NHT_RM_NAME(zvrf, AFI_IP6, ZEBRA_ROUTE_MAX));
- if (zebra_rmap_update_timer != ZEBRA_RMAP_DEFAULT_UPDATE_TIMER)
+ if (zvrf_id(zvrf) == VRF_DEFAULT
+ && zebra_rmap_update_timer != ZEBRA_RMAP_DEFAULT_UPDATE_TIMER)
vty_out(vty, "zebra route-map delay-timer %d\n",
zebra_rmap_update_timer);
}
diff --git a/zebra/zebra_routemap.h b/zebra/zebra_routemap.h
index 56e805ea03..251e07af72 100644
--- a/zebra/zebra_routemap.h
+++ b/zebra/zebra_routemap.h
@@ -36,8 +36,6 @@ extern void zebra_add_import_table_route_map(afi_t afi, const char *rmap_name,
uint32_t table);
extern void zebra_del_import_table_route_map(afi_t afi, uint32_t table);
-extern void zebra_route_map_write_delay_timer(struct vty *);
-
extern route_map_result_t
zebra_import_table_route_map_check(int family, int rib_type, uint8_t instance,
const struct prefix *p,
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index c22766cad5..697a6eecf1 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -1992,7 +1992,10 @@ static void zevpn_add_to_l3vni_list(struct hash_bucket *bucket, void *ctxt)
}
/*
- * handle transition of vni from l2 to l3 and vice versa
+ * Handle transition of vni from l2 to l3 and vice versa.
+ * This function handles only the L2VNI add/delete part of
+ * the above transition.
+ * L3VNI add/delete is handled by the calling functions.
*/
static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni,
int add)
@@ -2033,11 +2036,71 @@ static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni,
return -1;
}
} else {
- /* TODO_MITESH: This needs to be thought through. We don't have
- * enough information at this point to reprogram the vni as
- * l2-vni. One way is to store the required info in l3-vni and
- * used it solely for this purpose
- */
+ struct zebra_ns *zns;
+ struct route_node *rn;
+ struct interface *ifp;
+ struct zebra_if *zif;
+ struct zebra_l2info_vxlan *vxl;
+ struct interface *vlan_if;
+ bool found = false;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("Adding L2-VNI %u - transition from L3-VNI",
+ vni);
+
+ /* Find VxLAN interface for this VNI. */
+ zns = zebra_ns_lookup(NS_DEFAULT);
+ for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) {
+ ifp = (struct interface *)rn->info;
+ if (!ifp)
+ continue;
+ zif = ifp->info;
+ if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
+ continue;
+
+ vxl = &zif->l2info.vxl;
+ if (vxl->vni == vni) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_err(
+ "Adding L2-VNI - Failed to find VxLAN interface for VNI %u",
+ vni);
+ return -1;
+ }
+
+ /* Create VNI hash entry for L2VNI */
+ zevpn = zebra_evpn_lookup(vni);
+ if (zevpn)
+ return 0;
+
+ zevpn = zebra_evpn_add(vni);
+ if (!zevpn) {
+ flog_err(EC_ZEBRA_VNI_ADD_FAILED,
+ "Adding L2-VNI - Failed to add VNI hash, VNI %u",
+ vni);
+
+ return -1;
+ }
+
+ /* Find bridge interface for the VNI */
+ vlan_if = zvni_map_to_svi(vxl->access_vlan,
+ zif->brslave_info.br_if);
+ if (vlan_if)
+ zevpn->vrf_id = vlan_if->vrf_id;
+
+ zevpn->vxlan_if = ifp;
+ zevpn->local_vtep_ip = vxl->vtep_ip;
+
+ /* Inform BGP if the VNI is up and mapped to a bridge. */
+ if (if_is_operative(ifp) && zif->brslave_info.br_if) {
+ zebra_evpn_send_add_to_client(zevpn);
+ zebra_evpn_read_mac_neigh(zevpn, ifp);
+ }
}
return 0;
@@ -5201,6 +5264,7 @@ int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni,
if (add) {
+ /* Remove L2VNI if present */
zebra_vxlan_handle_vni_transition(zvrf, vni, add);
/* check if the vni is already present under zvrf */
@@ -5295,6 +5359,7 @@ int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni,
zvrf->l3vni = 0;
zl3vni_del(zl3vni);
+ /* Add L2VNI for this VNI */
zebra_vxlan_handle_vni_transition(zvrf, vni, add);
}
return 0;