#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
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. */
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;
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;
}
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;
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. */
/* 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);
}
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;
}
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;
&& (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
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,
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);
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
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,
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;
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
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;
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",
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,
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;
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",
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;
}
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);
+ }
+}
/* 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;
/* 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;
/* 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;
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. */
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;
#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);
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,
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 */
{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[] = {
{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[] = {
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 "";
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);
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
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);
}
}
bf_free(bm->rd_idspace);
list_delete(&bm->bgp);
+ list_delete(&bm->addresses);
bgp_lp_finish();
int tmp_port;
int bgp_port = BGP_PORT_DEFAULT;
- char *bgp_address = NULL;
+ struct list *addresses = list_new();
int no_fib_flag = 0;
int no_zebra_flag = 0;
int skip_runas = 0;
int instance = 0;
int buffer_size = BGP_SOCKET_SNDBUF_SIZE;
+ char *address;
+ struct listnode *node;
+
+ addresses->cmp = (int (*)(void *, void *))strcmp;
frr_preinit(&bgpd_di, argc, argv);
frr_opt_add(
break;
}
case 'l':
- bgp_address = optarg;
+ listnode_add_sort_nodup(addresses, optarg);
/* listenon implies -n */
/* fallthru */
case 'n':
memset(&bgpd_privs, 0, sizeof(bgpd_privs));
/* BGP master init. */
- bgp_master_init(frr_init(), buffer_size);
+ bgp_master_init(frr_init(), buffer_size, addresses);
bm->port = bgp_port;
if (bgp_port == 0)
bgp_option_set(BGP_OPT_NO_LISTEN);
- bm->address = bgp_address;
if (no_fib_flag || no_zebra_flag)
bgp_option_set(BGP_OPT_NO_FIB);
if (no_zebra_flag)
/* BGP related initialization. */
bgp_init((unsigned short)instance);
- snprintf(bgpd_di.startinfo, sizeof(bgpd_di.startinfo), ", bgp@%s:%d",
- (bm->address ? bm->address : "<all>"), bm->port);
+ if (list_isempty(bm->addresses)) {
+ snprintf(bgpd_di.startinfo, sizeof(bgpd_di.startinfo),
+ ", bgp@<all>:%d", bm->port);
+ } else {
+ for (ALL_LIST_ELEMENTS_RO(bm->addresses, node, address))
+ snprintf(bgpd_di.startinfo + strlen(bgpd_di.startinfo),
+ sizeof(bgpd_di.startinfo)
+ - strlen(bgpd_di.startinfo),
+ ", bgp@%s:%d", address, bm->port);
+ }
frr_config_fork();
/* must be called after fork() */
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")
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)
{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) */
[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.
[CAPABILITY_CODE_REFRESH_OLD] = 1,
[CAPABILITY_CODE_ORF_OLD] = 1,
[CAPABILITY_CODE_FQDN] = 1,
+ [CAPABILITY_CODE_ENHANCED_RR] = 1,
};
/**
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(
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);
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);
#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) */
#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. */
* 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
*/
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;
/* 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)
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.
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)) {
/* 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(
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,
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);
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);
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) {
/* 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) {
&& (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)) {
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);
}
#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);
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);
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;
}
}
+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)
}
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])
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
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
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;
}
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 *);
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);
}
}
}
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)
{
"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();
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]
/* 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);
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);
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? */
static int bgp_check_main_socket(bool create, struct bgp *bgp)
{
static int bgp_server_main_created;
+ struct listnode *node;
+ char *address;
if (create) {
if (bgp_server_main_created)
return 0;
- if (bgp_socket(bgp, bm->port, bm->address) < 0)
- return BGP_ERR_INVALID_VALUE;
+ if (list_isempty(bm->addresses)) {
+ if (bgp_socket(bgp, bm->port, NULL) < 0)
+ return BGP_ERR_INVALID_VALUE;
+ } else {
+ for (ALL_LIST_ELEMENTS_RO(bm->addresses, node, address))
+ if (bgp_socket(bgp, bm->port, address) < 0)
+ return BGP_ERR_INVALID_VALUE;
+ }
bgp_server_main_created = 1;
return 0;
}
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) {
int bgp_handle_socket(struct bgp *bgp, struct vrf *vrf, vrf_id_t old_vrf_id,
bool create)
{
- int ret = 0;
+ struct listnode *node;
+ char *address;
/* Create BGP server socket, if listen mode not disabled */
if (!bgp || bgp_option_check(BGP_OPT_NO_LISTEN))
*/
if (vrf->vrf_id == VRF_UNKNOWN)
return 0;
- ret = bgp_socket(bgp, bm->port, bm->address);
- if (ret < 0)
- return BGP_ERR_INVALID_VALUE;
+ if (list_isempty(bm->addresses)) {
+ if (bgp_socket(bgp, bm->port, NULL) < 0)
+ return BGP_ERR_INVALID_VALUE;
+ } else {
+ for (ALL_LIST_ELEMENTS_RO(bm->addresses, node, address))
+ if (bgp_socket(bgp, bm->port, address) < 0)
+ return BGP_ERR_INVALID_VALUE;
+ }
return 0;
} else
return bgp_check_main_socket(create, 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");
} 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)
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);
}
}
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;
}
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;
}
return buf;
}
-void bgp_master_init(struct thread_master *master, const int buffer_size)
+void bgp_master_init(struct thread_master *master, const int buffer_size,
+ struct list *addresses)
{
qobj_init();
bm->bgp = list_new();
bm->listen_sockets = list_new();
bm->port = BGP_PORT_DEFAULT;
+ bm->addresses = addresses;
bm->master = master;
bm->start_time = bgp_clock();
bm->t_rmap_update = NULL;
#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
/* BGP port number. */
uint16_t port;
- /* Listener address */
- char *address;
+ /* Listener addresses */
+ struct list *addresses;
/* The Mac table */
struct hash *self_mac_hash;
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)
#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];
/* 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
#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];
#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;
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;
#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
#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
extern int bgp_config_write(struct vty *);
-extern void bgp_master_init(struct thread_master *master,
- const int buffer_size);
+extern void bgp_master_init(struct thread_master *master, const int buffer_size,
+ struct list *addresses);
extern void bgp_init(unsigned short instance);
extern void bgp_pthreads_run(void);
ospf
zebra
vtysh
- pathd
+ path
+ link-state
+-----------+--------------------------+----------------------------------------------+
| ``%pNHs`` | ``struct nexthop *`` | ``1.2.3.4 if 15`` |
+-----------+--------------------------+----------------------------------------------+
-| ``%pFX`` + ``struct bgp_dest *`` | ``fe80::1234/64`` available in BGP only |
+| ``%pFX`` | ``struct bgp_dest *`` | ``fe80::1234/64`` (available in BGP only) |
+-----------+--------------------------+----------------------------------------------+
Printf features like field lengths can be used normally with these extensions,
doc/developer/include-compile.rst \
doc/developer/index.rst \
doc/developer/library.rst \
+ doc/developer/link-state.rst \
doc/developer/lists.rst \
doc/developer/locking.rst \
doc/developer/logging.rst \
doc/developer/path.rst \
doc/developer/rcu.rst \
doc/developer/static-linking.rst \
+ doc/developer/tracing.rst \
doc/developer/testing.rst \
doc/developer/topotests-snippets.rst \
doc/developer/topotests.rst \
LTTng user manual). When using USDT probes with LTTng, follow the example in
`this article
<https://lttng.org/blog/2019/10/15/new-dynamic-user-space-tracing-in-lttng/>`_.
-To trace with dtrace or SystemTap, compile with :option:`--enable-usdt=yes` and
+To trace with dtrace or SystemTap, compile with `--enable-usdt=yes` and
use your tracer as usual.
To see available USDT probes::
This is very helpful for users who want to quickly remind themselves what a
particular command does.
+When documenting a cli that has a ``no`` form, please do not include
+the ``no`` in the ``.. index::`` line.
+
Configuration Snippets
^^^^^^^^^^^^^^^^^^^^^^
.. option:: -l, --listenon
- Specify a specific IP address for bgpd to listen on, rather than its default
+ Specify specific IP addresses for bgpd to listen on, rather than its default
of ``0.0.0.0`` / ``::``. This can be useful to constrain bgpd to an internal
- address, or to run multiple bgpd processes on one host.
+ address, or to run multiple bgpd processes on one host. Multiple addresses
+ can be specified.
+
+ In the following example, bgpd is started listening for connections on the
+ addresses 100.0.1.2 and fd00::2:2. The options -d (runs in daemon mode) and
+ -f (uses specific configuration file) are also used in this example as we
+ are likely to run multiple bgpd instances, each one with different
+ configurations, when using -l option.
Note that this option implies the --no_kernel option, and no learned routes will be installed into the linux kernel.
+.. code-block:: shell
+
+ # /usr/lib/frr/bgpd -d -f /some-folder/bgpd.conf -l 100.0.1.2 -l fd00::2:2
+
.. option:: -n, --no_kernel
Do not install learned routes into the linux kernel. This option is useful
Suppress duplicate updates
--------------------------
-.. index:: [no] bgp suppress-duplicates
+.. index:: bgp suppress-duplicates
.. clicmd:: [no] bgp suppress-duplicates
For example, BGP routers can generate multiple identical announcements with
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 (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 BGP route-flap dampening and specifies dampening parameters.
+ 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
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
peer in question. This number is between 0 and 600 seconds,
with the default advertisement interval being 0.
-.. index:: [no] neighbor PEER timers delayopen (1-240)
+.. index:: neighbor PEER timers delayopen (1-240)
.. clicmd:: [no] neighbor PEER timers delayopen (1-240)
This command allows the user enable the
unless removed from the configuration with the negating command prior to the
configuration write operation.
-.. index:: [no] bgp send-extra-data zebra
+.. index:: bgp send-extra-data zebra
.. clicmd:: [no] bgp send-extra-data zebra
This Command turns off the ability of BGP to send extra data to zebra.
Disable load sharing across multiple LFA backups.
+.. index:: fast-reroute remote-lfa prefix-list WORD [level-1 | level-2]
+.. clicmd:: [no] fast-reroute remote-lfa prefix-list [WORD] [level-1 | level-2]
+
+ Configure a prefix-list to select eligible PQ nodes (valid for all protected
+ interfaces).
+
.. _isis-region:
ISIS region
Enable per-prefix TI-LFA fast reroute link or node protection.
+.. index:: isis fast-reroute remote-lfa tunnel mpls-ldp [level-1 | level-2]
+.. clicmd:: [no] isis fast-reroute remote-lfa tunnel mpls-ldp [level-1 | level-2]
+
+ Enable per-prefix Remote LFA fast reroute link protection. Note that other
+ routers in the network need to be configured to accept LDP targeted hello
+ messages in order for RLFA to work.
+
+.. index:: isis fast-reroute remote-lfa maximum-metric (1-16777215) [level-1 | level-2]
+.. clicmd:: [no] isis fast-reroute remote-lfa maximum-metric (1-16777215) [level-1 | level-2]
+
+ Limit Remote LFA PQ node selection within the specified metric.
+
.. _showing-isis-information:
Showing ISIS information
: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`
Configure segment routing traffic engineering.
-.. index:: [no] segment-list NAME
+.. index:: segment-list NAME
.. clicmd:: [no] segment-list NAME
Delete or start a segment list definition.
-.. index:: [no] index INDEX mpls label LABEL [nai node ADDRESS]
+.. index:: index INDEX mpls label LABEL [nai node ADDRESS]
.. clicmd:: [no] index INDEX mpls label LABEL [nai node ADDRESS]
Delete or specify a segment in a segment list definition.
-.. index:: [no] policy color COLOR endpoint ENDPOINT
+.. index:: policy color COLOR endpoint ENDPOINT
.. clicmd:: [no] policy color COLOR endpoint ENDPOINT
Delete or start a policy definition.
Specify the policy SID.
-.. index:: [no] candidate-path preference PREFERENCE name NAME explicit segment-list SEGMENT-LIST-NAME
+.. index:: candidate-path preference PREFERENCE name NAME explicit segment-list SEGMENT-LIST-NAME
.. clicmd:: [no] candidate-path preference PREFERENCE name NAME explicit segment-list SEGMENT-LIST-NAME
Delete or define an explicit candidate path.
-.. index:: [no] candidate-path preference PREFERENCE name NAME dynamic
+.. index:: candidate-path preference PREFERENCE name NAME dynamic
.. clicmd:: [no] candidate-path preference PREFERENCE name NAME dynamic
Delete or start a dynamic candidate path definition.
-.. index:: [no] affinity {exclude-any|include-any|include-all} BITPATTERN
+.. index:: affinity {exclude-any|include-any|include-all} BITPATTERN
.. clicmd:: [no] affinity {exclude-any|include-any|include-all} BITPATTERN
Delete or specify an affinity constraint for a dynamic candidate path.
-.. index:: [no] bandwidth BANDWIDTH [required]
+.. index:: bandwidth BANDWIDTH [required]
.. clicmd:: [no] bandwidth BANDWIDTH [required]
Delete or specify a bandwidth constraint for a dynamic candidate path.
-.. index:: [no] metric [bound] METRIC VALUE [required]
+.. index:: metric [bound] METRIC VALUE [required]
.. clicmd:: [no] metric [bound] METRIC VALUE [required]
Delete or specify a metric constraint for a dynamic candidate path.
- bnc: Border Node Count metric
-.. index:: [no] objective-function OBJFUN1 [required]
+.. index:: objective-function OBJFUN1 [required]
.. clicmd:: [no] objective-function OBJFUN1 [required]
Delete or specify a PCEP objective function constraint for a dynamic
- msn: Minimize the number of Shared Nodes [RFC8800]
-.. index:: [no] debug pathd pcep [basic|path|message|pceplib]
+.. index:: debug pathd pcep [basic|path|message|pceplib]
.. clicmd:: [no] debug pathd pcep [basic|path|message|pceplib]
Enable or disable debugging for the pcep module:
Configure PCEP support.
-.. index:: [no] cep-config NAME
+.. index:: cep-config NAME
.. clicmd:: [no] pce-config NAME
Define a shared PCE configuration that can be used in multiple PCE
declarations.
-.. index:: [no] pce NAME
+.. index:: pce NAME
.. clicmd:: [no] pce NAME
Define or delete a PCE definition.
configuration group.
-.. index:: [no] pcc
+.. index:: pcc
.. clicmd:: [no] pcc
Disable or start the definition of a PCC.
Specify the maximum SID depth in a PCC definition.
-.. index:: [no] peer WORD [precedence (1-255)]
+.. index:: peer WORD [precedence (1-255)]
.. clicmd:: [no] peer WORD [precedence (1-255)]
Specify a peer and its precedence in a PCC definition.
#include "eigrp_structs.h"
#include "eigrpd.h"
#include "eigrp_zebra.h"
+#include "eigrp_cli.h"
#ifndef VTYSH_EXTRACT_PL
#include "eigrpd/eigrp_cli_clippy.c"
--- /dev/null
+/*
+ * EIGRP CLI Functions.
+ * Copyright (C) 2019
+ * Authors:
+ * Donnie Savage
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _EIGRP_CLI_H_
+#define _EIGRP_CLI_H_
+
+/*Prototypes*/
+extern void eigrp_cli_show_header(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_end_header(struct vty *vty, struct lyd_node *dnode);
+extern void eigrp_cli_show_router_id(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_passive_interface(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_active_time(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_variance(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_maximum_paths(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_metrics(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_network(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_neighbor(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_redistribute(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_delay(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_bandwidth(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_hello_interval(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_hold_time(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_summarize_address(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_authentication(struct vty *vty,
+ struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_show_keychain(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
+extern void eigrp_cli_init(void);
+
+#endif /*EIGRP_CLI_H_ */
#define EIGRP_TOPOLOGY_TYPE_REMOTE_EXTERNAL 2 // Remote external network
/*EIGRP TT entry flags*/
-#define EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG (1 << 0)
-#define EIGRP_NEXTHOP_ENTRY_FSUCCESSOR_FLAG (1 << 1)
-#define EIGRP_NEXTHOP_ENTRY_INTABLE_FLAG (1 << 2)
-#define EIGRP_NEXTHOP_ENTRY_EXTERNAL_FLAG (1 << 3)
+#define EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG (1 << 0)
+#define EIGRP_ROUTE_DESCRIPTOR_FSUCCESSOR_FLAG (1 << 1)
+#define EIGRP_ROUTE_DESCRIPTOR_INTABLE_FLAG (1 << 2)
+#define EIGRP_ROUTE_DESCRIPTOR_EXTERNAL_FLAG (1 << 3)
/*EIGRP FSM state count, event count*/
#define EIGRP_FSM_STATE_MAX 5
"Codes: P - Passive, A - Active, U - Update, Q - Query, R - Reply\n r - reply Status, s - sia Status\n\n");
}
-void show_ip_eigrp_prefix_entry(struct vty *vty, struct eigrp_prefix_entry *tn)
+void show_ip_eigrp_prefix_descriptor(struct vty *vty,
+ struct eigrp_prefix_descriptor *tn)
{
struct list *successors = eigrp_topology_get_successor(tn);
list_delete(&successors);
}
-void show_ip_eigrp_nexthop_entry(struct vty *vty, struct eigrp *eigrp,
- struct eigrp_nexthop_entry *te, bool *first)
+void show_ip_eigrp_route_descriptor(struct vty *vty, struct eigrp *eigrp,
+ struct eigrp_route_descriptor *te,
+ bool *first)
{
if (te->reported_distance == EIGRP_MAX_METRIC)
return;
if (*first) {
- show_ip_eigrp_prefix_entry(vty, te->prefix);
+ show_ip_eigrp_prefix_descriptor(vty, te->prefix);
*first = false;
}
struct eigrp_interface *);
extern void show_ip_eigrp_neighbor_sub(struct vty *, struct eigrp_neighbor *,
int);
-extern void show_ip_eigrp_prefix_entry(struct vty *,
- struct eigrp_prefix_entry *);
-extern void show_ip_eigrp_nexthop_entry(struct vty *vty, struct eigrp *eigrp,
- struct eigrp_nexthop_entry *ne,
- bool *first);
+extern void show_ip_eigrp_prefix_descriptor(struct vty *vty,
+ struct eigrp_prefix_descriptor *tn);
+extern void show_ip_eigrp_route_descriptor(struct vty *vty, struct eigrp *eigrp,
+ struct eigrp_route_descriptor *ne,
+ bool *first);
extern void eigrp_debug_init(void);
#include "linklist.h"
#include "vty.h"
+#include "eigrpd/eigrp_types.h"
#include "eigrpd/eigrp_structs.h"
#include "eigrpd/eigrpd.h"
#include "eigrpd/eigrp_interface.h"
#include "eigrpd/eigrp_dump.h"
#include "eigrpd/eigrp_topology.h"
#include "eigrpd/eigrp_fsm.h"
+#include "eigrpd/eigrp_metric.h"
/*
* Prototypes
{
// Loading base information from message
// struct eigrp *eigrp = msg->eigrp;
- struct eigrp_prefix_entry *prefix = msg->prefix;
- struct eigrp_nexthop_entry *entry = msg->entry;
+ struct eigrp_prefix_descriptor *prefix = msg->prefix;
+ struct eigrp_route_descriptor *entry = msg->entry;
uint8_t actual_state = prefix->state;
enum metric_change change;
if (entry == NULL) {
- entry = eigrp_nexthop_entry_new();
+ entry = eigrp_route_descriptor_new();
entry->adv_router = msg->adv_router;
entry->ei = msg->adv_router->ei;
entry->prefix = prefix;
switch (actual_state) {
case EIGRP_FSM_STATE_PASSIVE: {
- struct eigrp_nexthop_entry *head =
+ struct eigrp_route_descriptor *head =
listnode_head(prefix->entries);
if (head->reported_distance < prefix->fdistance) {
}
case EIGRP_FSM_STATE_ACTIVE_0: {
if (msg->packet_type == EIGRP_OPC_REPLY) {
- struct eigrp_nexthop_entry *head =
+ struct eigrp_route_descriptor *head =
listnode_head(prefix->entries);
listnode_delete(prefix->rij, entry->adv_router);
return EIGRP_FSM_EVENT_LR_FCN;
} else if (msg->packet_type == EIGRP_OPC_QUERY
&& (entry->flags
- & EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG)) {
+ & EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG)) {
return EIGRP_FSM_EVENT_QACT;
}
}
case EIGRP_FSM_STATE_ACTIVE_1: {
if (msg->packet_type == EIGRP_OPC_QUERY
- && (entry->flags & EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG)) {
+ && (entry->flags & EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG)) {
return EIGRP_FSM_EVENT_QACT;
} else if (msg->packet_type == EIGRP_OPC_REPLY) {
listnode_delete(prefix->rij, entry->adv_router);
if (change == METRIC_INCREASE
&& (entry->flags
- & EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG)) {
+ & EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG)) {
return EIGRP_FSM_EVENT_DINC;
} else if (prefix->rij->count) {
return EIGRP_FSM_KEEP_STATE;
} else if (msg->packet_type == EIGRP_OPC_UPDATE
&& change == METRIC_INCREASE
&& (entry->flags
- & EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG)) {
+ & EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG)) {
return EIGRP_FSM_EVENT_DINC;
}
return EIGRP_FSM_KEEP_STATE;
}
case EIGRP_FSM_STATE_ACTIVE_2: {
if (msg->packet_type == EIGRP_OPC_REPLY) {
- struct eigrp_nexthop_entry *head =
+ struct eigrp_route_descriptor *head =
listnode_head(prefix->entries);
listnode_delete(prefix->rij, entry->adv_router);
if (change == METRIC_INCREASE
&& (entry->flags
- & EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG)) {
+ & EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG)) {
return EIGRP_FSM_EVENT_DINC;
} else if (prefix->rij->count) {
return EIGRP_FSM_KEEP_STATE;
} else if (msg->packet_type == EIGRP_OPC_UPDATE
&& change == METRIC_INCREASE
&& (entry->flags
- & EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG)) {
+ & EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG)) {
return EIGRP_FSM_EVENT_DINC;
}
return EIGRP_FSM_KEEP_STATE;
int eigrp_fsm_event_nq_fcn(struct eigrp_fsm_action_message *msg)
{
struct eigrp *eigrp = msg->eigrp;
- struct eigrp_prefix_entry *prefix = msg->prefix;
+ struct eigrp_prefix_descriptor *prefix = msg->prefix;
struct list *successors = eigrp_topology_get_successor(prefix);
- struct eigrp_nexthop_entry *ne;
+ struct eigrp_route_descriptor *ne;
assert(successors); // If this is NULL we have shit the bed, fun huh?
int eigrp_fsm_event_q_fcn(struct eigrp_fsm_action_message *msg)
{
struct eigrp *eigrp = msg->eigrp;
- struct eigrp_prefix_entry *prefix = msg->prefix;
+ struct eigrp_prefix_descriptor *prefix = msg->prefix;
struct list *successors = eigrp_topology_get_successor(prefix);
- struct eigrp_nexthop_entry *ne;
+ struct eigrp_route_descriptor *ne;
assert(successors); // If this is NULL somebody poked us in the eye.
int eigrp_fsm_event_keep_state(struct eigrp_fsm_action_message *msg)
{
struct eigrp *eigrp = msg->eigrp;
- struct eigrp_prefix_entry *prefix = msg->prefix;
- struct eigrp_nexthop_entry *ne = listnode_head(prefix->entries);
+ struct eigrp_prefix_descriptor *prefix = msg->prefix;
+ struct eigrp_route_descriptor *ne = listnode_head(prefix->entries);
if (prefix->state == EIGRP_FSM_STATE_PASSIVE) {
if (!eigrp_metrics_is_same(prefix->reported_metric,
int eigrp_fsm_event_lr(struct eigrp_fsm_action_message *msg)
{
struct eigrp *eigrp = msg->eigrp;
- struct eigrp_prefix_entry *prefix = msg->prefix;
- struct eigrp_nexthop_entry *ne = listnode_head(prefix->entries);
+ struct eigrp_prefix_descriptor *prefix = msg->prefix;
+ struct eigrp_route_descriptor *ne = listnode_head(prefix->entries);
prefix->fdistance = prefix->distance = prefix->rdistance = ne->distance;
prefix->reported_metric = ne->total_metric;
int eigrp_fsm_event_dinc(struct eigrp_fsm_action_message *msg)
{
struct list *successors = eigrp_topology_get_successor(msg->prefix);
- struct eigrp_nexthop_entry *ne;
+ struct eigrp_route_descriptor *ne;
assert(successors); // Trump and his big hands
int eigrp_fsm_event_lr_fcs(struct eigrp_fsm_action_message *msg)
{
struct eigrp *eigrp = msg->eigrp;
- struct eigrp_prefix_entry *prefix = msg->prefix;
- struct eigrp_nexthop_entry *ne = listnode_head(prefix->entries);
+ struct eigrp_prefix_descriptor *prefix = msg->prefix;
+ struct eigrp_route_descriptor *ne = listnode_head(prefix->entries);
prefix->state = EIGRP_FSM_STATE_PASSIVE;
prefix->distance = prefix->rdistance = ne->distance;
int eigrp_fsm_event_lr_fcn(struct eigrp_fsm_action_message *msg)
{
struct eigrp *eigrp = msg->eigrp;
- struct eigrp_prefix_entry *prefix = msg->prefix;
- struct eigrp_nexthop_entry *best_successor;
+ struct eigrp_prefix_descriptor *prefix = msg->prefix;
+ struct eigrp_route_descriptor *best_successor;
struct list *successors = eigrp_topology_get_successor(prefix);
assert(successors); // Routing without a stack
int eigrp_fsm_event_qact(struct eigrp_fsm_action_message *msg)
{
struct list *successors = eigrp_topology_get_successor(msg->prefix);
- struct eigrp_nexthop_entry *ne;
+ struct eigrp_route_descriptor *ne;
assert(successors); // Cats and no Dogs
#include "eigrpd/eigrp_memory.h"
#include "eigrpd/eigrp_fsm.h"
#include "eigrpd/eigrp_dump.h"
+#include "eigrpd/eigrp_types.h"
+#include "eigrpd/eigrp_metric.h"
struct eigrp_interface *eigrp_if_new(struct eigrp *eigrp, struct interface *ifp,
struct prefix *p)
int eigrp_if_up(struct eigrp_interface *ei)
{
- struct eigrp_prefix_entry *pe;
- struct eigrp_nexthop_entry *ne;
+ struct eigrp_prefix_descriptor *pe;
+ struct eigrp_route_descriptor *ne;
struct eigrp_metrics metric;
struct eigrp_interface *ei2;
struct listnode *node, *nnode;
/*Add connected entry to topology table*/
- ne = eigrp_nexthop_entry_new();
+ ne = eigrp_route_descriptor_new();
ne->ei = ei;
ne->reported_metric = metric;
ne->total_metric = metric;
ne->distance = eigrp_calculate_metrics(eigrp, metric);
ne->reported_distance = 0;
ne->adv_router = eigrp->neighbor_self;
- ne->flags = EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG;
+ ne->flags = EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG;
struct prefix dest_addr;
&dest_addr);
if (pe == NULL) {
- pe = eigrp_prefix_entry_new();
+ pe = eigrp_prefix_descriptor_new();
pe->serno = eigrp->serno;
pe->destination = (struct prefix *)prefix_ipv4_new();
prefix_copy(pe->destination, &dest_addr);
pe->state = EIGRP_FSM_STATE_PASSIVE;
pe->fdistance = eigrp_calculate_metrics(eigrp, metric);
pe->req_action |= EIGRP_FSM_NEED_UPDATE;
- eigrp_prefix_entry_add(eigrp->topology_table, pe);
+ eigrp_prefix_descriptor_add(eigrp->topology_table, pe);
listnode_add(eigrp->topology_changes_internalIPV4, pe);
- eigrp_nexthop_entry_add(eigrp, pe, ne);
+ eigrp_route_descriptor_add(eigrp, pe, ne);
for (ALL_LIST_ELEMENTS(eigrp->eiflist, node, nnode, ei2)) {
eigrp_update_send(ei2);
struct eigrp_fsm_action_message msg;
ne->prefix = pe;
- eigrp_nexthop_entry_add(eigrp, pe, ne);
+ eigrp_route_descriptor_add(eigrp, pe, ne);
msg.packet_type = EIGRP_OPC_UPDATE;
msg.eigrp = eigrp;
void eigrp_if_free(struct eigrp_interface *ei, int source)
{
struct prefix dest_addr;
- struct eigrp_prefix_entry *pe;
+ struct eigrp_prefix_descriptor *pe;
struct eigrp *eigrp = ei->eigrp;
if (source == INTERFACE_DOWN_BY_VTY) {
pe = eigrp_topology_table_lookup_ipv4(eigrp->topology_table,
&dest_addr);
if (pe)
- eigrp_prefix_entry_delete(eigrp, eigrp->topology_table, pe);
+ eigrp_prefix_descriptor_delete(eigrp, eigrp->topology_table,
+ pe);
eigrp_if_down(ei);
return NULL;
}
-
-uint32_t eigrp_bandwidth_to_scaled(uint32_t bandwidth)
-{
- uint64_t temp_bandwidth = (256ull * 10000000) / bandwidth;
-
- temp_bandwidth = temp_bandwidth < EIGRP_MAX_METRIC ? temp_bandwidth
- : EIGRP_MAX_METRIC;
-
- return (uint32_t)temp_bandwidth;
-}
-
-uint32_t eigrp_scaled_to_bandwidth(uint32_t scaled)
-{
- uint64_t temp_scaled = scaled * (256ull * 10000000);
-
- temp_scaled =
- temp_scaled < EIGRP_MAX_METRIC ? temp_scaled : EIGRP_MAX_METRIC;
-
- return (uint32_t)temp_scaled;
-}
-
-uint32_t eigrp_delay_to_scaled(uint32_t delay)
-{
- return delay * 256;
-}
-
-uint32_t eigrp_scaled_to_delay(uint32_t scaled)
-{
- return scaled / 256;
-}
/* Simulate down/up on the interface. */
extern void eigrp_if_reset(struct interface *);
-extern uint32_t eigrp_bandwidth_to_scaled(uint32_t);
-extern uint32_t eigrp_scaled_to_bandwidth(uint32_t);
-extern uint32_t eigrp_delay_to_scaled(uint32_t);
-extern uint32_t eigrp_scaled_to_delay(uint32_t);
-
#endif /* ZEBRA_EIGRP_INTERFACE_H_ */
#include "eigrpd/eigrp_filter.h"
#include "eigrpd/eigrp_errors.h"
#include "eigrpd/eigrp_vrf.h"
+#include "eigrpd/eigrp_cli.h"
+#include "eigrpd/eigrp_yang.h"
//#include "eigrpd/eigrp_routemap.h"
/* eigprd privileges */
DEFINE_MTYPE(EIGRPD, EIGRP_SEQ_TLV, "EIGRP SEQ TLV")
DEFINE_MTYPE(EIGRPD, EIGRP_AUTH_TLV, "EIGRP AUTH TLV")
DEFINE_MTYPE(EIGRPD, EIGRP_AUTH_SHA256_TLV, "EIGRP SHA TLV")
-DEFINE_MTYPE(EIGRPD, EIGRP_PREFIX_ENTRY, "EIGRP Prefix")
-DEFINE_MTYPE(EIGRPD, EIGRP_NEXTHOP_ENTRY, "EIGRP Nexthop Entry")
+DEFINE_MTYPE(EIGRPD, EIGRP_PREFIX_DESCRIPTOR, "EIGRP Prefix")
+DEFINE_MTYPE(EIGRPD, EIGRP_ROUTE_DESCRIPTOR, "EIGRP Nexthop Entry")
DEFINE_MTYPE(EIGRPD, EIGRP_FSM_MSG, "EIGRP FSM Message")
DECLARE_MTYPE(EIGRP_SEQ_TLV)
DECLARE_MTYPE(EIGRP_AUTH_TLV)
DECLARE_MTYPE(EIGRP_AUTH_SHA256_TLV)
-DECLARE_MTYPE(EIGRP_PREFIX_ENTRY)
-DECLARE_MTYPE(EIGRP_NEXTHOP_ENTRY)
+DECLARE_MTYPE(EIGRP_PREFIX_DESCRIPTOR)
+DECLARE_MTYPE(EIGRP_ROUTE_DESCRIPTOR)
DECLARE_MTYPE(EIGRP_FSM_MSG)
#endif /* _FRR_EIGRP_MEMORY_H */
--- /dev/null
+/*
+ * EIGRP Metric Math Functions.
+ * Copyright (C) 2013-2016
+ * Authors:
+ * Donnie Savage
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "eigrpd/eigrp_structs.h"
+#include "eigrpd/eigrpd.h"
+#include "eigrpd/eigrp_types.h"
+#include "eigrpd/eigrp_metric.h"
+
+eigrp_scaled_t eigrp_bandwidth_to_scaled(eigrp_bandwidth_t bandwidth)
+{
+ eigrp_bandwidth_t scaled = EIGRP_BANDWIDTH_MAX;
+
+ if (bandwidth != EIGRP_BANDWIDTH_MAX) {
+ scaled = (EIGRP_CLASSIC_SCALER * EIGRP_BANDWIDTH_SCALER);
+ scaled = scaled / bandwidth;
+
+ scaled = scaled ? scaled : EIGRP_BANDWIDTH_MIN;
+ }
+
+ scaled = (scaled < EIGRP_METRIC_MAX) ? scaled : EIGRP_METRIC_MAX;
+ return (eigrp_scaled_t)scaled;
+}
+
+eigrp_bandwidth_t eigrp_scaled_to_bandwidth(eigrp_scaled_t scaled)
+{
+ eigrp_bandwidth_t bandwidth = EIGRP_BANDWIDTH_MAX;
+
+ if (scaled != EIGRP_CLASSIC_MAX) {
+ bandwidth = (EIGRP_CLASSIC_SCALER * EIGRP_BANDWIDTH_SCALER);
+ bandwidth = scaled * bandwidth;
+ bandwidth = (bandwidth < EIGRP_METRIC_MAX)
+ ? bandwidth
+ : EIGRP_BANDWIDTH_MAX;
+ }
+
+ return bandwidth;
+}
+
+eigrp_scaled_t eigrp_delay_to_scaled(eigrp_delay_t delay)
+{
+ delay = delay ? delay : EIGRP_DELAY_MIN;
+ return delay * EIGRP_CLASSIC_SCALER;
+}
+
+eigrp_delay_t eigrp_scaled_to_delay(eigrp_scaled_t scaled)
+{
+ scaled = scaled / EIGRP_CLASSIC_SCALER;
+ scaled = scaled ? scaled : EIGRP_DELAY_MIN;
+
+ return scaled;
+}
+
+eigrp_metric_t eigrp_calculate_metrics(struct eigrp *eigrp,
+ struct eigrp_metrics metric)
+{
+ eigrp_metric_t composite = 0;
+
+ if (metric.delay == EIGRP_MAX_METRIC)
+ return EIGRP_METRIC_MAX;
+
+ /*
+ * EIGRP Composite =
+ * {K1*BW+[(K2*BW)/(256-load)]+(K3*delay)}*{K5/(reliability+K4)}
+ */
+
+ if (eigrp->k_values[0])
+ composite += (eigrp->k_values[0] * metric.bandwidth);
+ if (eigrp->k_values[1])
+ composite += ((eigrp->k_values[1] * metric.bandwidth)
+ / (256 - metric.load));
+ if (eigrp->k_values[2])
+ composite += (eigrp->k_values[2] * metric.delay);
+ if (eigrp->k_values[3] && !eigrp->k_values[4])
+ composite *= eigrp->k_values[3];
+ if (!eigrp->k_values[3] && eigrp->k_values[4])
+ composite *= (eigrp->k_values[4] / metric.reliability);
+ if (eigrp->k_values[3] && eigrp->k_values[4])
+ composite *= ((eigrp->k_values[4] / metric.reliability)
+ + eigrp->k_values[3]);
+
+ composite =
+ (composite <= EIGRP_METRIC_MAX) ? composite : EIGRP_METRIC_MAX;
+
+ return composite;
+}
+
+eigrp_metric_t
+eigrp_calculate_total_metrics(struct eigrp *eigrp,
+ struct eigrp_route_descriptor *entry)
+{
+ struct eigrp_interface *ei = entry->ei;
+ eigrp_delay_t temp_delay;
+ eigrp_bandwidth_t bw;
+
+ entry->total_metric = entry->reported_metric;
+ temp_delay = entry->total_metric.delay
+ + eigrp_delay_to_scaled(ei->params.delay);
+
+ entry->total_metric.delay = temp_delay > EIGRP_METRIC_MAX_CLASSIC
+ ? EIGRP_METRIC_MAX_CLASSIC
+ : temp_delay;
+
+ bw = eigrp_bandwidth_to_scaled(ei->params.bandwidth);
+ entry->total_metric.bandwidth = entry->total_metric.bandwidth > bw
+ ? bw
+ : entry->total_metric.bandwidth;
+
+ return eigrp_calculate_metrics(eigrp, entry->total_metric);
+}
+
+bool eigrp_metrics_is_same(struct eigrp_metrics metric1,
+ struct eigrp_metrics metric2)
+{
+ if ((metric1.bandwidth == metric2.bandwidth)
+ && (metric1.delay == metric2.delay)
+ && (metric1.hop_count == metric2.hop_count)
+ && (metric1.load == metric2.load)
+ && (metric1.reliability == metric2.reliability)
+ && (metric1.mtu[0] == metric2.mtu[0])
+ && (metric1.mtu[1] == metric2.mtu[1])
+ && (metric1.mtu[2] == metric2.mtu[2])) {
+ return true;
+ }
+
+ return false; /* if different */
+}
--- /dev/null
+/*
+ * EIGRP Metric Math Functions.
+ * Copyright (C) 2013-2016
+ * Authors:
+ * Donnie Savage
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _ZEBRA_EIGRP_METRIC_H_
+#define _ZEBRA_EIGRP_METRIC_H_
+
+/* Constants */
+#define EIGRP_BANDWIDTH_MIN 0x1ull /* 1 */
+#define EIGRP_BANDWIDTH_SCALER 10000000ull /* Inversion value */
+#define EIGRP_BANDWIDTH_MAX 0xffffffffffffffffull /* 1.84467441x10^19 */
+
+#define EIGRP_DELAY_MIN 0x1ull /* 1 */
+#define EIGRP_DELAY_PICO 1000000ull
+#define EIGRP_DELAY_MAX 0xffffffffffffffffull /* 1.84467441x10^19 */
+
+#define EIGRP_MAX_LOAD 256
+#define EIGRP_MAX_HOPS 100
+
+#define EIGRP_INACCESSIBLE 0xFFFFFFFFFFFFFFFFull
+
+#define EIGRP_METRIC_MAX 0xffffffffffffffffull /* 1.84467441x10^19 */
+#define EIGRP_METRIC_MAX_CLASSIC 0xffffffff
+#define EIGRP_METRIC_SCALER 65536 /* CLASSIC to WIDE conversion */
+
+#define EIGRP_CLASSIC_MAX 0xffffffff /* 4294967295 */
+#define EIGRP_CLASSIC_SCALER 256 /* IGRP to EIGRP conversion */
+
+
+/* Prototypes */
+extern eigrp_scaled_t eigrp_bandwidth_to_scaled(eigrp_bandwidth_t bw);
+extern eigrp_bandwidth_t eigrp_scaled_to_bandwidth(eigrp_scaled_t scale);
+extern eigrp_scaled_t eigrp_delay_to_scaled(eigrp_delay_t delay);
+extern eigrp_delay_t eigrp_scaled_to_delay(eigrp_scaled_t scale);
+
+extern eigrp_metric_t eigrp_calculate_metrics(struct eigrp *eigrp,
+ struct eigrp_metrics metric);
+extern eigrp_metric_t
+eigrp_calculate_total_metrics(struct eigrp *eigrp,
+ struct eigrp_route_descriptor *rd);
+extern bool eigrp_metrics_is_same(struct eigrp_metrics m1,
+ struct eigrp_metrics m2);
+
+#endif /* _ZEBRA_EIGRP_METRIC_H_ */
eigrp_nbr_delete(nbr);
}
-int eigrp_nbr_split_horizon_check(struct eigrp_nexthop_entry *ne,
+int eigrp_nbr_split_horizon_check(struct eigrp_route_descriptor *ne,
struct eigrp_interface *ei)
{
if (ne->distance == EIGRP_MAX_METRIC)
eigrp_nbr_lookup_by_addr_process(struct eigrp *eigrp, struct in_addr addr);
extern void eigrp_nbr_hard_restart(struct eigrp_neighbor *nbr, struct vty *vty);
-extern int eigrp_nbr_split_horizon_check(struct eigrp_nexthop_entry *ne,
+extern int eigrp_nbr_split_horizon_check(struct eigrp_route_descriptor *ne,
struct eigrp_interface *ei);
#endif /* _ZEBRA_EIGRP_NEIGHBOR_H */
return 1;
}
-uint32_t eigrp_calculate_metrics(struct eigrp *eigrp,
- struct eigrp_metrics metric)
-{
- uint64_t temp_metric;
- temp_metric = 0;
-
- if (metric.delay == EIGRP_MAX_METRIC)
- return EIGRP_MAX_METRIC;
-
- // EIGRP Metric =
- // {K1*BW+[(K2*BW)/(256-load)]+(K3*delay)}*{K5/(reliability+K4)}
-
- if (eigrp->k_values[0])
- temp_metric += (eigrp->k_values[0] * metric.bandwidth);
- if (eigrp->k_values[1])
- temp_metric += ((eigrp->k_values[1] * metric.bandwidth)
- / (256 - metric.load));
- if (eigrp->k_values[2])
- temp_metric += (eigrp->k_values[2] * metric.delay);
- if (eigrp->k_values[3] && !eigrp->k_values[4])
- temp_metric *= eigrp->k_values[3];
- if (!eigrp->k_values[3] && eigrp->k_values[4])
- temp_metric *= (eigrp->k_values[4] / metric.reliability);
- if (eigrp->k_values[3] && eigrp->k_values[4])
- temp_metric *= ((eigrp->k_values[4] / metric.reliability)
- + eigrp->k_values[3]);
-
- if (temp_metric <= EIGRP_MAX_METRIC)
- return (uint32_t)temp_metric;
- else
- return EIGRP_MAX_METRIC;
-}
-
-uint32_t eigrp_calculate_total_metrics(struct eigrp *eigrp,
- struct eigrp_nexthop_entry *entry)
-{
- struct eigrp_interface *ei = entry->ei;
-
- entry->total_metric = entry->reported_metric;
- uint64_t temp_delay =
- (uint64_t)entry->total_metric.delay
- + (uint64_t)eigrp_delay_to_scaled(ei->params.delay);
- entry->total_metric.delay = temp_delay > EIGRP_MAX_METRIC
- ? EIGRP_MAX_METRIC
- : (uint32_t)temp_delay;
-
- uint32_t bw = eigrp_bandwidth_to_scaled(ei->params.bandwidth);
- entry->total_metric.bandwidth = entry->total_metric.bandwidth > bw
- ? bw
- : entry->total_metric.bandwidth;
-
- return eigrp_calculate_metrics(eigrp, entry->total_metric);
-}
-
-uint8_t eigrp_metrics_is_same(struct eigrp_metrics metric1,
- struct eigrp_metrics metric2)
-{
- if ((metric1.bandwidth == metric2.bandwidth)
- && (metric1.delay == metric2.delay)
- && (metric1.hop_count == metric2.hop_count)
- && (metric1.load == metric2.load)
- && (metric1.reliability == metric2.reliability)
- && (metric1.mtu[0] == metric2.mtu[0])
- && (metric1.mtu[1] == metric2.mtu[1])
- && (metric1.mtu[2] == metric2.mtu[2]))
- return 1;
-
- return 0; // if different
-}
-
void eigrp_external_routes_refresh(struct eigrp *eigrp, int type)
{
}
unsigned int ifindex);
extern void eigrp_adjust_sndbuflen(struct eigrp *, unsigned int);
-extern uint32_t eigrp_calculate_metrics(struct eigrp *, struct eigrp_metrics);
-extern uint32_t eigrp_calculate_total_metrics(struct eigrp *,
- struct eigrp_nexthop_entry *);
-extern uint8_t eigrp_metrics_is_same(struct eigrp_metrics,
- struct eigrp_metrics);
extern void eigrp_external_routes_refresh(struct eigrp *, int);
#endif /* EIGRP_NETWORK_H_ */
#include "eigrp_interface.h"
#include "eigrp_network.h"
#include "eigrp_zebra.h"
+#include "eigrp_cli.h"
/* Helper functions. */
static void redistribute_get_metrics(const struct lyd_node *dnode,
}
uint16_t eigrp_add_internalTLV_to_stream(struct stream *s,
- struct eigrp_prefix_entry *pe)
+ struct eigrp_prefix_descriptor *pe)
{
uint16_t length;
extern int eigrp_read(struct thread *);
extern int eigrp_write(struct thread *);
-extern struct eigrp_packet *eigrp_packet_new(size_t, struct eigrp_neighbor *);
-extern struct eigrp_packet *eigrp_packet_duplicate(struct eigrp_packet *,
- struct eigrp_neighbor *);
-extern void eigrp_packet_free(struct eigrp_packet *);
-extern void eigrp_packet_delete(struct eigrp_interface *);
-extern void eigrp_packet_header_init(int, struct eigrp *, struct stream *,
- uint32_t, uint32_t, uint32_t);
-extern void eigrp_packet_checksum(struct eigrp_interface *, struct stream *,
- uint16_t);
+extern struct eigrp_packet *eigrp_packet_new(size_t size,
+ struct eigrp_neighbor *nbr);
+extern struct eigrp_packet *eigrp_packet_duplicate(struct eigrp_packet *old,
+ struct eigrp_neighbor *nbr);
+extern void eigrp_packet_free(struct eigrp_packet *ep);
+extern void eigrp_packet_delete(struct eigrp_interface *ei);
+extern void eigrp_packet_header_init(int type, struct eigrp *eigrp,
+ struct stream *s, uint32_t flags,
+ uint32_t sequence, uint32_t ack);
+extern void eigrp_packet_checksum(struct eigrp_interface *ei, struct stream *s,
+ uint16_t length);
extern struct eigrp_fifo *eigrp_fifo_new(void);
-extern struct eigrp_packet *eigrp_fifo_next(struct eigrp_fifo *);
-extern struct eigrp_packet *eigrp_fifo_pop(struct eigrp_fifo *);
-extern void eigrp_fifo_push(struct eigrp_fifo *, struct eigrp_packet *);
-extern void eigrp_fifo_free(struct eigrp_fifo *);
-extern void eigrp_fifo_reset(struct eigrp_fifo *);
-
-extern void eigrp_send_packet_reliably(struct eigrp_neighbor *);
-
-extern struct TLV_IPv4_Internal_type *eigrp_read_ipv4_tlv(struct stream *);
-extern uint16_t eigrp_add_internalTLV_to_stream(struct stream *,
- struct eigrp_prefix_entry *);
-extern uint16_t eigrp_add_authTLV_MD5_to_stream(struct stream *,
- struct eigrp_interface *);
-extern uint16_t eigrp_add_authTLV_SHA256_to_stream(struct stream *,
- struct eigrp_interface *);
-
-extern int eigrp_unack_packet_retrans(struct thread *);
-extern int eigrp_unack_multicast_packet_retrans(struct thread *);
+extern struct eigrp_packet *eigrp_fifo_next(struct eigrp_fifo *fifo);
+extern struct eigrp_packet *eigrp_fifo_pop(struct eigrp_fifo *fifo);
+extern void eigrp_fifo_push(struct eigrp_fifo *fifo, struct eigrp_packet *ep);
+extern void eigrp_fifo_free(struct eigrp_fifo *fifo);
+extern void eigrp_fifo_reset(struct eigrp_fifo *fifo);
+
+extern void eigrp_send_packet_reliably(struct eigrp_neighbor *nbr);
+
+extern struct TLV_IPv4_Internal_type *eigrp_read_ipv4_tlv(struct stream *s);
+extern uint16_t
+eigrp_add_internalTLV_to_stream(struct stream *s,
+ struct eigrp_prefix_descriptor *pe);
+extern uint16_t eigrp_add_authTLV_MD5_to_stream(struct stream *s,
+ struct eigrp_interface *ei);
+extern uint16_t eigrp_add_authTLV_SHA256_to_stream(struct stream *s,
+ struct eigrp_interface *ei);
+
+extern int eigrp_unack_packet_retrans(struct thread *thread);
+extern int eigrp_unack_multicast_packet_retrans(struct thread *thread);
/*
* untill there is reason to have their own header, these externs are found in
* eigrp_hello.c
*/
extern void eigrp_sw_version_initialize(void);
-extern void eigrp_hello_send(struct eigrp_interface *, uint8_t,
- struct in_addr *);
-extern void eigrp_hello_send_ack(struct eigrp_neighbor *);
-extern void eigrp_hello_receive(struct eigrp *, struct ip *,
- struct eigrp_header *, struct stream *,
- struct eigrp_interface *, int);
-extern int eigrp_hello_timer(struct thread *);
+extern void eigrp_hello_send(struct eigrp_interface *ei, uint8_t flags,
+ struct in_addr *nbr_addr);
+extern void eigrp_hello_send_ack(struct eigrp_neighbor *nbr);
+extern void eigrp_hello_receive(struct eigrp *eigrp, struct ip *iph,
+ struct eigrp_header *eigrph, struct stream *s,
+ struct eigrp_interface *ei, int size);
+extern int eigrp_hello_timer(struct thread *thread);
/*
* These externs are found in eigrp_update.c
extern bool eigrp_update_prefix_apply(struct eigrp *eigrp,
struct eigrp_interface *ei, int in,
struct prefix *prefix);
-extern void eigrp_update_send(struct eigrp_interface *);
-extern void eigrp_update_receive(struct eigrp *, struct ip *,
- struct eigrp_header *, struct stream *,
- struct eigrp_interface *, int);
-extern void eigrp_update_send_all(struct eigrp *, struct eigrp_interface *);
-extern void eigrp_update_send_init(struct eigrp_neighbor *);
-extern void eigrp_update_send_EOT(struct eigrp_neighbor *);
-extern int eigrp_update_send_GR_thread(struct thread *);
-extern void eigrp_update_send_GR(struct eigrp_neighbor *, enum GR_type,
- struct vty *);
-extern void eigrp_update_send_interface_GR(struct eigrp_interface *,
- enum GR_type, struct vty *);
-extern void eigrp_update_send_process_GR(struct eigrp *, enum GR_type,
- struct vty *);
+extern void eigrp_update_send(struct eigrp_interface *ei);
+extern void eigrp_update_receive(struct eigrp *eigrp, struct ip *iph,
+ struct eigrp_header *eigrph, struct stream *s,
+ struct eigrp_interface *ei, int size);
+extern void eigrp_update_send_all(struct eigrp *eigrp,
+ struct eigrp_interface *exception);
+extern void eigrp_update_send_init(struct eigrp_neighbor *nbr);
+extern void eigrp_update_send_EOT(struct eigrp_neighbor *nbr);
+extern int eigrp_update_send_GR_thread(struct thread *thread);
+extern void eigrp_update_send_GR(struct eigrp_neighbor *nbr,
+ enum GR_type gr_type, struct vty *vty);
+extern void eigrp_update_send_interface_GR(struct eigrp_interface *ei,
+ enum GR_type gr_type,
+ struct vty *vty);
+extern void eigrp_update_send_process_GR(struct eigrp *eigrp,
+ enum GR_type gr_type, struct vty *vty);
/*
* These externs are found in eigrp_query.c
*/
-extern void eigrp_send_query(struct eigrp_interface *);
-extern void eigrp_query_receive(struct eigrp *, struct ip *,
- struct eigrp_header *, struct stream *,
- struct eigrp_interface *, int);
-extern uint32_t eigrp_query_send_all(struct eigrp *);
+extern void eigrp_send_query(struct eigrp_interface *ei);
+extern void eigrp_query_receive(struct eigrp *eigrp, struct ip *iph,
+ struct eigrp_header *eigrph, struct stream *s,
+ struct eigrp_interface *ei, int size);
+extern uint32_t eigrp_query_send_all(struct eigrp *eigrp);
/*
* These externs are found in eigrp_reply.c
*/
-extern void eigrp_send_reply(struct eigrp_neighbor *,
- struct eigrp_prefix_entry *);
-extern void eigrp_reply_receive(struct eigrp *, struct ip *,
- struct eigrp_header *, struct stream *,
- struct eigrp_interface *, int);
+extern void eigrp_send_reply(struct eigrp_neighbor *nbr,
+ struct eigrp_prefix_descriptor *pe);
+extern void eigrp_reply_receive(struct eigrp *eigrp, struct ip *iph,
+ struct eigrp_header *eigrph, struct stream *s,
+ struct eigrp_interface *ei, int size);
/*
* These externs are found in eigrp_siaquery.c
*/
-extern void eigrp_send_siaquery(struct eigrp_neighbor *,
- struct eigrp_prefix_entry *);
-extern void eigrp_siaquery_receive(struct eigrp *, struct ip *,
- struct eigrp_header *, struct stream *,
- struct eigrp_interface *, int);
+extern void eigrp_send_siaquery(struct eigrp_neighbor *nbr,
+ struct eigrp_prefix_descriptor *pe);
+extern void eigrp_siaquery_receive(struct eigrp *eigrp, struct ip *iph,
+ struct eigrp_header *eigrph,
+ struct stream *s, struct eigrp_interface *ei,
+ int size);
/*
* These externs are found in eigrp_siareply.c
*/
-extern void eigrp_send_siareply(struct eigrp_neighbor *,
- struct eigrp_prefix_entry *);
-extern void eigrp_siareply_receive(struct eigrp *, struct ip *,
- struct eigrp_header *, struct stream *,
- struct eigrp_interface *, int);
+extern void eigrp_send_siareply(struct eigrp_neighbor *nbr,
+ struct eigrp_prefix_descriptor *pe);
+extern void eigrp_siareply_receive(struct eigrp *eigrp, struct ip *iph,
+ struct eigrp_header *eigrph,
+ struct stream *s, struct eigrp_interface *ei,
+ int size);
extern struct TLV_MD5_Authentication_Type *eigrp_authTLV_MD5_new(void);
-extern void eigrp_authTLV_MD5_free(struct TLV_MD5_Authentication_Type *);
+extern void eigrp_authTLV_MD5_free(struct TLV_MD5_Authentication_Type *authTLV);
extern struct TLV_SHA256_Authentication_Type *eigrp_authTLV_SHA256_new(void);
-extern void eigrp_authTLV_SHA256_free(struct TLV_SHA256_Authentication_Type *);
-
-extern int eigrp_make_md5_digest(struct eigrp_interface *, struct stream *,
- uint8_t);
-extern int eigrp_check_md5_digest(struct stream *,
- struct TLV_MD5_Authentication_Type *,
- struct eigrp_neighbor *, uint8_t);
-extern int eigrp_make_sha256_digest(struct eigrp_interface *, struct stream *,
- uint8_t);
-extern int eigrp_check_sha256_digest(struct stream *,
- struct TLV_SHA256_Authentication_Type *,
- struct eigrp_neighbor *, uint8_t);
-
-
-extern void eigrp_IPv4_InternalTLV_free(struct TLV_IPv4_Internal_type *);
+extern void
+eigrp_authTLV_SHA256_free(struct TLV_SHA256_Authentication_Type *authTLV);
+
+extern int eigrp_make_md5_digest(struct eigrp_interface *ei, struct stream *s,
+ uint8_t flags);
+extern int eigrp_check_md5_digest(struct stream *s,
+ struct TLV_MD5_Authentication_Type *authTLV,
+ struct eigrp_neighbor *nbr, uint8_t flags);
+extern int eigrp_make_sha256_digest(struct eigrp_interface *ei,
+ struct stream *s, uint8_t flags);
+extern int
+eigrp_check_sha256_digest(struct stream *s,
+ struct TLV_SHA256_Authentication_Type *authTLV,
+ struct eigrp_neighbor *nbr, uint8_t flags);
+
+
+extern void
+eigrp_IPv4_InternalTLV_free(struct TLV_IPv4_Internal_type *IPv4_InternalTLV);
extern struct TLV_Sequence_Type *eigrp_SequenceTLV_new(void);
{
struct eigrp_interface *iface;
struct listnode *node, *node2, *nnode2;
- struct eigrp_prefix_entry *pe;
+ struct eigrp_prefix_descriptor *pe;
uint32_t counter;
if (eigrp == NULL) {
dest_addr.family = AF_INET;
dest_addr.u.prefix4 = tlv->destination;
dest_addr.prefixlen = tlv->prefix_length;
- struct eigrp_prefix_entry *dest =
+ struct eigrp_prefix_descriptor *dest =
eigrp_topology_table_lookup_ipv4(
eigrp->topology_table, &dest_addr);
* know)*/
if (dest != NULL) {
struct eigrp_fsm_action_message msg;
- struct eigrp_nexthop_entry *entry =
- eigrp_prefix_entry_lookup(dest->entries,
- nbr);
+ struct eigrp_route_descriptor *entry =
+ eigrp_route_descriptor_lookup(
+ dest->entries, nbr);
msg.packet_type = EIGRP_OPC_QUERY;
msg.eigrp = eigrp;
msg.data_type = EIGRP_INT;
uint16_t length = EIGRP_HEADER_LEN;
struct listnode *node, *nnode, *node2, *nnode2;
struct eigrp_neighbor *nbr;
- struct eigrp_prefix_entry *pe;
+ struct eigrp_prefix_descriptor *pe;
bool has_tlv = false;
bool new_packet = true;
uint16_t eigrp_mtu = EIGRP_PACKET_MTU(ei->ifp->mtu);
#include "eigrpd/eigrp_memory.h"
#include "eigrpd/eigrp_errors.h"
-void eigrp_send_reply(struct eigrp_neighbor *nbr, struct eigrp_prefix_entry *pe)
+void eigrp_send_reply(struct eigrp_neighbor *nbr,
+ struct eigrp_prefix_descriptor *pe)
{
struct eigrp_packet *ep;
uint16_t length = EIGRP_HEADER_LEN;
struct eigrp_interface *ei = nbr->ei;
struct eigrp *eigrp = ei->eigrp;
- struct eigrp_prefix_entry *pe2;
+ struct eigrp_prefix_descriptor *pe2;
// TODO: Work in progress
/* Filtering */
/* get list from eigrp process */
- pe2 = XCALLOC(MTYPE_EIGRP_PREFIX_ENTRY,
- sizeof(struct eigrp_prefix_entry));
- memcpy(pe2, pe, sizeof(struct eigrp_prefix_entry));
+ pe2 = XCALLOC(MTYPE_EIGRP_PREFIX_DESCRIPTOR,
+ sizeof(struct eigrp_prefix_descriptor));
+ memcpy(pe2, pe, sizeof(struct eigrp_prefix_descriptor));
if (eigrp_update_prefix_apply(eigrp, ei, EIGRP_FILTER_OUT,
pe2->destination)) {
eigrp_send_packet_reliably(nbr);
}
- XFREE(MTYPE_EIGRP_PREFIX_ENTRY, pe2);
+ XFREE(MTYPE_EIGRP_PREFIX_DESCRIPTOR, pe2);
}
/*EIGRP REPLY read function*/
dest_addr.family = AF_INET;
dest_addr.u.prefix4 = tlv->destination;
dest_addr.prefixlen = tlv->prefix_length;
- struct eigrp_prefix_entry *dest =
+ struct eigrp_prefix_descriptor *dest =
eigrp_topology_table_lookup_ipv4(eigrp->topology_table,
&dest_addr);
/*
}
struct eigrp_fsm_action_message msg;
- struct eigrp_nexthop_entry *entry =
- eigrp_prefix_entry_lookup(dest->entries, nbr);
+ struct eigrp_route_descriptor *entry =
+ eigrp_route_descriptor_lookup(dest->entries, nbr);
if (eigrp_update_prefix_apply(eigrp, ei, EIGRP_FILTER_IN,
&dest_addr)) {
// uint32_t *metric;
// uint32_t check;
// struct rip_info *rinfo;
- // struct eigrp_nexthop_entry *te;
- // struct eigrp_prefix_entry *pe;
+ // struct eigrp_route_descriptor *te;
+ // struct eigrp_prefix_descriptor *pe;
// struct listnode *node, *node2, *nnode, *nnode2;
// struct eigrp *e;
//
dest_addr.family = AFI_IP;
dest_addr.u.prefix4 = tlv->destination;
dest_addr.prefixlen = tlv->prefix_length;
- struct eigrp_prefix_entry *dest =
+ struct eigrp_prefix_descriptor *dest =
eigrp_topology_table_lookup_ipv4(
eigrp->topology_table, &dest_addr);
* know)*/
if (dest != NULL) {
struct eigrp_fsm_action_message msg;
- struct eigrp_nexthop_entry *entry =
- eigrp_prefix_entry_lookup(dest->entries,
- nbr);
+ struct eigrp_route_descriptor *entry =
+ eigrp_route_descriptor_lookup(
+ dest->entries, nbr);
msg.packet_type = EIGRP_OPC_SIAQUERY;
msg.eigrp = eigrp;
msg.data_type = EIGRP_INT;
}
void eigrp_send_siaquery(struct eigrp_neighbor *nbr,
- struct eigrp_prefix_entry *pe)
+ struct eigrp_prefix_descriptor *pe)
{
struct eigrp_packet *ep;
uint16_t length = EIGRP_HEADER_LEN;
dest_addr.family = AFI_IP;
dest_addr.u.prefix4 = tlv->destination;
dest_addr.prefixlen = tlv->prefix_length;
- struct eigrp_prefix_entry *dest =
+ struct eigrp_prefix_descriptor *dest =
eigrp_topology_table_lookup_ipv4(
eigrp->topology_table, &dest_addr);
* know)*/
if (dest != NULL) {
struct eigrp_fsm_action_message msg;
- struct eigrp_nexthop_entry *entry =
- eigrp_prefix_entry_lookup(dest->entries,
- nbr);
+ struct eigrp_route_descriptor *entry =
+ eigrp_route_descriptor_lookup(
+ dest->entries, nbr);
msg.packet_type = EIGRP_OPC_SIAQUERY;
msg.eigrp = eigrp;
msg.data_type = EIGRP_INT;
}
void eigrp_send_siareply(struct eigrp_neighbor *nbr,
- struct eigrp_prefix_entry *pe)
+ struct eigrp_prefix_descriptor *pe)
{
struct eigrp_packet *ep;
uint16_t length = EIGRP_HEADER_LEN;
#include "eigrpd/eigrp_const.h"
#include "eigrpd/eigrp_macros.h"
-/* EIGRP master for system wide configuration and variables. */
-struct eigrp_master {
- /* EIGRP instance. */
- struct list *eigrp;
-
- /* EIGRP thread master. */
- struct thread_master *master;
-
- /* Zebra interface list. */
- struct list *iflist;
-
- /* EIGRP start time. */
- time_t start_time;
-
- /* Various EIGRP global configuration. */
- uint8_t options;
-
-#define EIGRP_MASTER_SHUTDOWN (1 << 0) /* deferred-shutdown */
-};
-
struct eigrp_metrics {
uint32_t delay;
uint32_t bandwidth;
uint8_t flags;
};
+struct eigrp_extdata {
+ uint32_t orig;
+ uint32_t as;
+ uint32_t tag;
+ uint32_t metric;
+ uint16_t reserved;
+ uint8_t protocol;
+ uint8_t flags;
+};
+
struct eigrp {
vrf_id_t vrf_id;
//---------------------------------------------------------------------------------------------------------------------------------------------
/* EIGRP Topology table node structure */
-struct eigrp_prefix_entry {
+struct eigrp_prefix_descriptor {
struct list *entries, *rij;
uint32_t fdistance; // FD
uint32_t rdistance; // RD
};
/* EIGRP Topology table record structure */
-struct eigrp_nexthop_entry {
- struct eigrp_prefix_entry *prefix;
+struct eigrp_route_descriptor {
+ uint16_t type;
+ uint16_t afi;
+
+ struct eigrp_prefix_descriptor *prefix;
+ struct eigrp_neighbor *adv_router;
+ struct in_addr nexthop;
+
uint32_t reported_distance; // distance reported by neighbor
uint32_t distance; // sum of reported distance and link cost to
// advertised neighbor
struct eigrp_metrics reported_metric;
struct eigrp_metrics total_metric;
- struct eigrp_neighbor *adv_router; // ip address of advertising neighbor
+ struct eigrp_metrics metric;
+ struct eigrp_extdata extdata;
+
uint8_t flags; // used for marking successor and FS
struct eigrp_interface *ei; // pointer for case of connected entry
uint8_t packet_type; // UPDATE, QUERY, SIAQUERY, SIAREPLY
struct eigrp *eigrp; // which thread sent mesg
struct eigrp_neighbor *adv_router; // advertising neighbor
- struct eigrp_nexthop_entry *entry;
- struct eigrp_prefix_entry *prefix;
+ struct eigrp_route_descriptor *entry;
+ struct eigrp_prefix_descriptor *prefix;
msg_data_t data_type; // internal or external tlv type
struct eigrp_metrics metrics;
enum metric_change change;
#include "vty.h"
#include "lib_errors.h"
+#include "eigrpd/eigrp_types.h"
#include "eigrpd/eigrp_structs.h"
#include "eigrpd/eigrpd.h"
#include "eigrpd/eigrp_interface.h"
#include "eigrpd/eigrp_topology.h"
#include "eigrpd/eigrp_fsm.h"
#include "eigrpd/eigrp_memory.h"
+#include "eigrpd/eigrp_metric.h"
-static int eigrp_nexthop_entry_cmp(struct eigrp_nexthop_entry *,
- struct eigrp_nexthop_entry *);
+static int eigrp_route_descriptor_cmp(struct eigrp_route_descriptor *rd1,
+ struct eigrp_route_descriptor *rd2);
/*
* Returns linkedlist used as topology table
* Returns new created toplogy node
* cmp - assigned function for comparing topology entry
*/
-struct eigrp_prefix_entry *eigrp_prefix_entry_new(void)
+struct eigrp_prefix_descriptor *eigrp_prefix_descriptor_new(void)
{
- struct eigrp_prefix_entry *new;
- new = XCALLOC(MTYPE_EIGRP_PREFIX_ENTRY,
- sizeof(struct eigrp_prefix_entry));
+ struct eigrp_prefix_descriptor *new;
+ new = XCALLOC(MTYPE_EIGRP_PREFIX_DESCRIPTOR,
+ sizeof(struct eigrp_prefix_descriptor));
new->entries = list_new();
new->rij = list_new();
- new->entries->cmp = (int (*)(void *, void *))eigrp_nexthop_entry_cmp;
+ new->entries->cmp = (int (*)(void *, void *))eigrp_route_descriptor_cmp;
new->distance = new->fdistance = new->rdistance = EIGRP_MAX_METRIC;
new->destination = NULL;
/*
* Topology entry comparison
*/
-static int eigrp_nexthop_entry_cmp(struct eigrp_nexthop_entry *entry1,
- struct eigrp_nexthop_entry *entry2)
+static int eigrp_route_descriptor_cmp(struct eigrp_route_descriptor *entry1,
+ struct eigrp_route_descriptor *entry2)
{
if (entry1->distance < entry2->distance)
return -1;
* Returns new topology entry
*/
-struct eigrp_nexthop_entry *eigrp_nexthop_entry_new(void)
+struct eigrp_route_descriptor *eigrp_route_descriptor_new(void)
{
- struct eigrp_nexthop_entry *new;
+ struct eigrp_route_descriptor *new;
- new = XCALLOC(MTYPE_EIGRP_NEXTHOP_ENTRY,
- sizeof(struct eigrp_nexthop_entry));
+ new = XCALLOC(MTYPE_EIGRP_ROUTE_DESCRIPTOR,
+ sizeof(struct eigrp_route_descriptor));
new->reported_distance = EIGRP_MAX_METRIC;
new->distance = EIGRP_MAX_METRIC;
/*
* Adding topology node to topology table
*/
-void eigrp_prefix_entry_add(struct route_table *topology,
- struct eigrp_prefix_entry *pe)
+void eigrp_prefix_descriptor_add(struct route_table *topology,
+ struct eigrp_prefix_descriptor *pe)
{
struct route_node *rn;
/*
* Adding topology entry to topology node
*/
-void eigrp_nexthop_entry_add(struct eigrp *eigrp,
- struct eigrp_prefix_entry *node,
- struct eigrp_nexthop_entry *entry)
+void eigrp_route_descriptor_add(struct eigrp *eigrp,
+ struct eigrp_prefix_descriptor *node,
+ struct eigrp_route_descriptor *entry)
{
struct list *l = list_new();
/*
* Deleting topology node from topology table
*/
-void eigrp_prefix_entry_delete(struct eigrp *eigrp, struct route_table *table,
- struct eigrp_prefix_entry *pe)
+void eigrp_prefix_descriptor_delete(struct eigrp *eigrp,
+ struct route_table *table,
+ struct eigrp_prefix_descriptor *pe)
{
- struct eigrp_nexthop_entry *ne;
+ struct eigrp_route_descriptor *ne;
struct listnode *node, *nnode;
struct route_node *rn;
listnode_delete(eigrp->topology_changes_internalIPV4, pe);
for (ALL_LIST_ELEMENTS(pe->entries, node, nnode, ne))
- eigrp_nexthop_entry_delete(eigrp, pe, ne);
+ eigrp_route_descriptor_delete(eigrp, pe, ne);
list_delete(&pe->entries);
list_delete(&pe->rij);
eigrp_zebra_route_delete(eigrp, pe->destination);
rn->info = NULL;
route_unlock_node(rn); // Lookup above
route_unlock_node(rn); // Initial creation
- XFREE(MTYPE_EIGRP_PREFIX_ENTRY, pe);
+ XFREE(MTYPE_EIGRP_PREFIX_DESCRIPTOR, pe);
}
/*
* Deleting topology entry from topology node
*/
-void eigrp_nexthop_entry_delete(struct eigrp *eigrp,
- struct eigrp_prefix_entry *node,
- struct eigrp_nexthop_entry *entry)
+void eigrp_route_descriptor_delete(struct eigrp *eigrp,
+ struct eigrp_prefix_descriptor *node,
+ struct eigrp_route_descriptor *entry)
{
if (listnode_lookup(node->entries, entry) != NULL) {
listnode_delete(node->entries, entry);
eigrp_zebra_route_delete(eigrp, node->destination);
- XFREE(MTYPE_EIGRP_NEXTHOP_ENTRY, entry);
+ XFREE(MTYPE_EIGRP_ROUTE_DESCRIPTOR, entry);
}
}
struct route_table *topology)
{
struct route_node *rn;
- struct eigrp_prefix_entry *pe;
+ struct eigrp_prefix_descriptor *pe;
for (rn = route_top(topology); rn; rn = route_next(rn)) {
pe = rn->info;
if (!pe)
continue;
- eigrp_prefix_entry_delete(eigrp, topology, pe);
+ eigrp_prefix_descriptor_delete(eigrp, topology, pe);
}
}
-struct eigrp_prefix_entry *
+struct eigrp_prefix_descriptor *
eigrp_topology_table_lookup_ipv4(struct route_table *table,
struct prefix *address)
{
- struct eigrp_prefix_entry *pe;
+ struct eigrp_prefix_descriptor *pe;
struct route_node *rn;
rn = route_node_lookup(table, address);
* That way we can clean up all the list_new and list_delete's
* that we are doing. DBS
*/
-struct list *eigrp_topology_get_successor(struct eigrp_prefix_entry *table_node)
+struct list *
+eigrp_topology_get_successor(struct eigrp_prefix_descriptor *table_node)
{
struct list *successors = list_new();
- struct eigrp_nexthop_entry *data;
+ struct eigrp_route_descriptor *data;
struct listnode *node1, *node2;
for (ALL_LIST_ELEMENTS(table_node->entries, node1, node2, data)) {
- if (data->flags & EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG) {
+ if (data->flags & EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG) {
listnode_add(successors, data);
}
}
}
struct list *
-eigrp_topology_get_successor_max(struct eigrp_prefix_entry *table_node,
+eigrp_topology_get_successor_max(struct eigrp_prefix_descriptor *table_node,
unsigned int maxpaths)
{
struct list *successors = eigrp_topology_get_successor(table_node);
return successors;
}
-struct eigrp_nexthop_entry *
-eigrp_prefix_entry_lookup(struct list *entries, struct eigrp_neighbor *nbr)
+struct eigrp_route_descriptor *
+eigrp_route_descriptor_lookup(struct list *entries, struct eigrp_neighbor *nbr)
{
- struct eigrp_nexthop_entry *data;
+ struct eigrp_route_descriptor *data;
struct listnode *node, *nnode;
for (ALL_LIST_ELEMENTS(entries, node, nnode, data)) {
if (data->adv_router == nbr) {
struct eigrp_neighbor *nbr)
{
struct listnode *node2, *node22;
- struct eigrp_nexthop_entry *entry;
- struct eigrp_prefix_entry *pe;
+ struct eigrp_route_descriptor *entry;
+ struct eigrp_prefix_descriptor *pe;
struct route_node *rn;
/* create new empty list for prefixes storage */
eigrp_topology_update_distance(struct eigrp_fsm_action_message *msg)
{
struct eigrp *eigrp = msg->eigrp;
- struct eigrp_prefix_entry *prefix = msg->prefix;
- struct eigrp_nexthop_entry *entry = msg->entry;
+ struct eigrp_prefix_descriptor *prefix = msg->prefix;
+ struct eigrp_route_descriptor *entry = msg->entry;
enum metric_change change = METRIC_SAME;
uint32_t new_reported_distance;
void eigrp_topology_update_all_node_flags(struct eigrp *eigrp)
{
- struct eigrp_prefix_entry *pe;
+ struct eigrp_prefix_descriptor *pe;
struct route_node *rn;
if (!eigrp)
}
void eigrp_topology_update_node_flags(struct eigrp *eigrp,
- struct eigrp_prefix_entry *dest)
+ struct eigrp_prefix_descriptor *dest)
{
struct listnode *node;
- struct eigrp_nexthop_entry *entry;
+ struct eigrp_route_descriptor *entry;
for (ALL_LIST_ELEMENTS_RO(dest->entries, node, entry)) {
if (entry->reported_distance < dest->fdistance) {
&& entry->distance != EIGRP_MAX_METRIC) {
// is successor
entry->flags |=
- EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG;
+ EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG;
entry->flags &=
- ~EIGRP_NEXTHOP_ENTRY_FSUCCESSOR_FLAG;
+ ~EIGRP_ROUTE_DESCRIPTOR_FSUCCESSOR_FLAG;
} else {
// is feasible successor only
entry->flags |=
- EIGRP_NEXTHOP_ENTRY_FSUCCESSOR_FLAG;
+ EIGRP_ROUTE_DESCRIPTOR_FSUCCESSOR_FLAG;
entry->flags &=
- ~EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG;
+ ~EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG;
}
} else {
- entry->flags &= ~EIGRP_NEXTHOP_ENTRY_FSUCCESSOR_FLAG;
- entry->flags &= ~EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG;
+ entry->flags &= ~EIGRP_ROUTE_DESCRIPTOR_FSUCCESSOR_FLAG;
+ entry->flags &= ~EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG;
}
}
}
void eigrp_update_routing_table(struct eigrp *eigrp,
- struct eigrp_prefix_entry *prefix)
+ struct eigrp_prefix_descriptor *prefix)
{
struct list *successors;
struct listnode *node;
- struct eigrp_nexthop_entry *entry;
+ struct eigrp_route_descriptor *entry;
successors = eigrp_topology_get_successor_max(prefix, eigrp->max_paths);
eigrp_zebra_route_add(eigrp, prefix->destination, successors,
prefix->fdistance);
for (ALL_LIST_ELEMENTS_RO(successors, node, entry))
- entry->flags |= EIGRP_NEXTHOP_ENTRY_INTABLE_FLAG;
+ entry->flags |= EIGRP_ROUTE_DESCRIPTOR_INTABLE_FLAG;
list_delete(&successors);
} else {
eigrp_zebra_route_delete(eigrp, prefix->destination);
for (ALL_LIST_ELEMENTS_RO(prefix->entries, node, entry))
- entry->flags &= ~EIGRP_NEXTHOP_ENTRY_INTABLE_FLAG;
+ entry->flags &= ~EIGRP_ROUTE_DESCRIPTOR_INTABLE_FLAG;
}
}
struct eigrp_neighbor *nbr)
{
struct listnode *node2, *node22;
- struct eigrp_prefix_entry *pe;
- struct eigrp_nexthop_entry *entry;
+ struct eigrp_prefix_descriptor *pe;
+ struct eigrp_route_descriptor *entry;
struct route_node *rn;
for (rn = route_top(eigrp->topology_table); rn; rn = route_next(rn)) {
void eigrp_update_topology_table_prefix(struct eigrp *eigrp,
struct route_table *table,
- struct eigrp_prefix_entry *prefix)
+ struct eigrp_prefix_descriptor *prefix)
{
struct listnode *node1, *node2;
- struct eigrp_nexthop_entry *entry;
+ struct eigrp_route_descriptor *entry;
for (ALL_LIST_ELEMENTS(prefix->entries, node1, node2, entry)) {
if (entry->distance == EIGRP_MAX_METRIC) {
- eigrp_nexthop_entry_delete(eigrp, prefix, entry);
+ eigrp_route_descriptor_delete(eigrp, prefix, entry);
}
}
if (prefix->distance == EIGRP_MAX_METRIC
&& prefix->nt != EIGRP_TOPOLOGY_TYPE_CONNECTED) {
- eigrp_prefix_entry_delete(eigrp, table, prefix);
+ eigrp_prefix_descriptor_delete(eigrp, table, prefix);
}
}
/* EIGRP Topology table related functions. */
extern struct route_table *eigrp_topology_new(void);
extern void eigrp_topology_init(struct route_table *table);
-extern struct eigrp_prefix_entry *eigrp_prefix_entry_new(void);
-extern struct eigrp_nexthop_entry *eigrp_nexthop_entry_new(void);
+extern struct eigrp_prefix_descriptor *eigrp_prefix_descriptor_new(void);
+extern struct eigrp_route_descriptor *eigrp_route_descriptor_new(void);
extern void eigrp_topology_free(struct eigrp *eigrp, struct route_table *table);
-extern void eigrp_prefix_entry_add(struct route_table *table,
- struct eigrp_prefix_entry *pe);
-extern void eigrp_nexthop_entry_add(struct eigrp *eigrp,
- struct eigrp_prefix_entry *pe,
- struct eigrp_nexthop_entry *ne);
-extern void eigrp_prefix_entry_delete(struct eigrp *eigrp,
- struct route_table *table,
- struct eigrp_prefix_entry *pe);
-extern void eigrp_nexthop_entry_delete(struct eigrp *eigrp,
- struct eigrp_prefix_entry *pe,
- struct eigrp_nexthop_entry *ne);
+extern void eigrp_prefix_descriptor_add(struct route_table *table,
+ struct eigrp_prefix_descriptor *pe);
+extern void eigrp_route_descriptor_add(struct eigrp *eigrp,
+ struct eigrp_prefix_descriptor *pe,
+ struct eigrp_route_descriptor *ne);
+extern void eigrp_prefix_descriptor_delete(struct eigrp *eigrp,
+ struct route_table *table,
+ struct eigrp_prefix_descriptor *pe);
+extern void eigrp_route_descriptor_delete(struct eigrp *eigrp,
+ struct eigrp_prefix_descriptor *pe,
+ struct eigrp_route_descriptor *ne);
extern void eigrp_topology_delete_all(struct eigrp *eigrp,
struct route_table *table);
-extern struct eigrp_prefix_entry *
+extern struct eigrp_prefix_descriptor *
eigrp_topology_table_lookup_ipv4(struct route_table *table, struct prefix *p);
-extern struct list *eigrp_topology_get_successor(struct eigrp_prefix_entry *pe);
extern struct list *
-eigrp_topology_get_successor_max(struct eigrp_prefix_entry *pe,
+eigrp_topology_get_successor(struct eigrp_prefix_descriptor *pe);
+extern struct list *
+eigrp_topology_get_successor_max(struct eigrp_prefix_descriptor *pe,
unsigned int maxpaths);
-extern struct eigrp_nexthop_entry *
-eigrp_prefix_entry_lookup(struct list *entries, struct eigrp_neighbor *neigh);
+extern struct eigrp_route_descriptor *
+eigrp_route_descriptor_lookup(struct list *entries,
+ struct eigrp_neighbor *neigh);
extern struct list *eigrp_neighbor_prefixes_lookup(struct eigrp *eigrp,
struct eigrp_neighbor *n);
extern void eigrp_topology_update_all_node_flags(struct eigrp *eigrp);
-extern void eigrp_topology_update_node_flags(struct eigrp *eigrp,
- struct eigrp_prefix_entry *pe);
+extern void
+eigrp_topology_update_node_flags(struct eigrp *eigrp,
+ struct eigrp_prefix_descriptor *pe);
extern enum metric_change
eigrp_topology_update_distance(struct eigrp_fsm_action_message *msg);
extern void eigrp_update_routing_table(struct eigrp *eigrp,
- struct eigrp_prefix_entry *pe);
+ struct eigrp_prefix_descriptor *pe);
extern void eigrp_topology_neighbor_down(struct eigrp *eigrp,
struct eigrp_neighbor *neigh);
-extern void eigrp_update_topology_table_prefix(struct eigrp *eigrp,
- struct route_table *table,
- struct eigrp_prefix_entry *pe);
+extern void
+eigrp_update_topology_table_prefix(struct eigrp *eigrp,
+ struct route_table *table,
+ struct eigrp_prefix_descriptor *pe);
#endif
--- /dev/null
+/*
+ * EIGRP Definition of Data Types
+ * Copyright (C) 2018
+ * Authors:
+ * Donnie Savage
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _ZEBRA_EIGRP_TYPES_H_
+#define _ZEBRA_EIGRP_TYPES_H_
+
+typedef uint64_t eigrp_bandwidth_t;
+typedef uint64_t eigrp_delay_t;
+typedef uint64_t eigrp_metric_t;
+typedef uint32_t eigrp_scaled_t;
+
+typedef uint32_t eigrp_system_metric_t;
+typedef uint32_t eigrp_system_delay_t;
+typedef uint32_t eigrp_system_bandwidth_t;
+
+#endif /* _ZEBRA_EIGRP_TYPES_H_ */
#include "routemap.h"
#include "vty.h"
+#include "eigrpd/eigrp_types.h"
#include "eigrpd/eigrp_structs.h"
#include "eigrpd/eigrpd.h"
#include "eigrpd/eigrp_interface.h"
#include "eigrpd/eigrp_fsm.h"
#include "eigrpd/eigrp_network.h"
#include "eigrpd/eigrp_memory.h"
+#include "eigrpd/eigrp_metric.h"
bool eigrp_update_prefix_apply(struct eigrp *eigrp, struct eigrp_interface *ei,
int in, struct prefix *prefix)
* Function is used for removing received prefix
* from list of neighbor prefixes
*/
-static void remove_received_prefix_gr(struct list *nbr_prefixes,
- struct eigrp_prefix_entry *recv_prefix)
+static void
+remove_received_prefix_gr(struct list *nbr_prefixes,
+ struct eigrp_prefix_descriptor *recv_prefix)
{
struct listnode *node1, *node11;
- struct eigrp_prefix_entry *prefix = NULL;
+ struct eigrp_prefix_descriptor *prefix = NULL;
/* iterate over all prefixes in list */
for (ALL_LIST_ELEMENTS(nbr_prefixes, node1, node11, prefix)) {
struct list *nbr_prefixes)
{
struct listnode *node1;
- struct eigrp_prefix_entry *prefix;
+ struct eigrp_prefix_descriptor *prefix;
struct eigrp_fsm_action_message fsm_msg;
/* iterate over all prefixes which weren't advertised by neighbor */
/* set delay to MAX */
fsm_msg.metrics.delay = EIGRP_MAX_METRIC;
- struct eigrp_nexthop_entry *entry =
- eigrp_prefix_entry_lookup(prefix->entries, nbr);
+ struct eigrp_route_descriptor *entry =
+ eigrp_route_descriptor_lookup(prefix->entries, nbr);
fsm_msg.packet_type = EIGRP_OPC_UPDATE;
fsm_msg.eigrp = eigrp;
{
struct eigrp_neighbor *nbr;
struct TLV_IPv4_Internal_type *tlv;
- struct eigrp_prefix_entry *pe;
- struct eigrp_nexthop_entry *ne;
+ struct eigrp_prefix_descriptor *pe;
+ struct eigrp_route_descriptor *ne;
uint32_t flags;
uint16_t type;
uint16_t length;
dest_addr.family = AF_INET;
dest_addr.u.prefix4 = tlv->destination;
dest_addr.prefixlen = tlv->prefix_length;
- struct eigrp_prefix_entry *dest =
+ struct eigrp_prefix_descriptor *dest =
eigrp_topology_table_lookup_ipv4(
eigrp->topology_table, &dest_addr);
dest);
struct eigrp_fsm_action_message msg;
- struct eigrp_nexthop_entry *entry =
- eigrp_prefix_entry_lookup(dest->entries,
- nbr);
+ struct eigrp_route_descriptor *entry =
+ eigrp_route_descriptor_lookup(
+ dest->entries, nbr);
msg.packet_type = EIGRP_OPC_UPDATE;
msg.eigrp = eigrp;
eigrp_fsm_event(&msg);
} else {
/*Here comes topology information save*/
- pe = eigrp_prefix_entry_new();
+ pe = eigrp_prefix_descriptor_new();
pe->serno = eigrp->serno;
pe->destination =
(struct prefix *)prefix_ipv4_new();
pe->state = EIGRP_FSM_STATE_PASSIVE;
pe->nt = EIGRP_TOPOLOGY_TYPE_REMOTE;
- ne = eigrp_nexthop_entry_new();
+ ne = eigrp_route_descriptor_new();
ne->ei = ei;
ne->adv_router = nbr;
ne->reported_metric = tlv->metric;
pe->fdistance = pe->distance = pe->rdistance =
ne->distance;
ne->prefix = pe;
- ne->flags = EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG;
+ ne->flags =
+ EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG;
- eigrp_prefix_entry_add(eigrp->topology_table,
- pe);
- eigrp_nexthop_entry_add(eigrp, pe, ne);
+ eigrp_prefix_descriptor_add(
+ eigrp->topology_table, pe);
+ eigrp_route_descriptor_add(eigrp, pe, ne);
pe->distance = pe->fdistance = pe->rdistance =
ne->distance;
pe->reported_metric = ne->total_metric;
{
struct eigrp_packet *ep;
uint16_t length = EIGRP_HEADER_LEN;
- struct eigrp_nexthop_entry *te;
- struct eigrp_prefix_entry *pe;
+ struct eigrp_route_descriptor *te;
+ struct eigrp_prefix_descriptor *pe;
struct listnode *node2, *nnode2;
struct eigrp_interface *ei = nbr->ei;
struct eigrp *eigrp = ei->eigrp;
{
struct eigrp_packet *ep;
struct listnode *node, *nnode;
- struct eigrp_prefix_entry *pe;
+ struct eigrp_prefix_descriptor *pe;
uint8_t has_tlv;
struct eigrp *eigrp = ei->eigrp;
struct prefix *dest_addr;
has_tlv = 0;
for (ALL_LIST_ELEMENTS(ei->eigrp->topology_changes_internalIPV4, node,
nnode, pe)) {
- struct eigrp_nexthop_entry *ne;
+ struct eigrp_route_descriptor *ne;
if (!(pe->req_action & EIGRP_FSM_NEED_UPDATE))
continue;
{
struct eigrp_interface *iface;
struct listnode *node, *node2, *nnode2;
- struct eigrp_prefix_entry *pe;
+ struct eigrp_prefix_descriptor *pe;
for (ALL_LIST_ELEMENTS_RO(eigrp->eiflist, node, iface)) {
if (iface != exception) {
{
struct eigrp_packet *ep;
uint16_t length = EIGRP_HEADER_LEN;
- struct eigrp_prefix_entry *pe;
+ struct eigrp_prefix_descriptor *pe;
struct prefix *dest_addr;
struct eigrp_interface *ei = nbr->ei;
struct eigrp *eigrp = ei->eigrp;
/* prepare message for FSM */
struct eigrp_fsm_action_message fsm_msg;
- struct eigrp_nexthop_entry *entry =
- eigrp_prefix_entry_lookup(pe->entries, nbr);
+ struct eigrp_route_descriptor *entry =
+ eigrp_route_descriptor_lookup(pe->entries, nbr);
fsm_msg.packet_type = EIGRP_OPC_UPDATE;
fsm_msg.eigrp = eigrp;
void eigrp_update_send_GR(struct eigrp_neighbor *nbr, enum GR_type gr_type,
struct vty *vty)
{
- struct eigrp_prefix_entry *pe2;
+ struct eigrp_prefix_descriptor *pe2;
struct list *prefixes;
struct route_node *rn;
struct eigrp_interface *ei = nbr->ei;
#include "eigrpd/eigrp_vty_clippy.c"
#endif
-static void eigrp_vty_display_prefix_entry(struct vty *vty,
- struct eigrp *eigrp,
- struct eigrp_prefix_entry *pe,
+static void eigrp_vty_display_prefix_entry(struct vty *vty, struct eigrp *eigrp,
+ struct eigrp_prefix_descriptor *pe,
bool all)
{
bool first = true;
- struct eigrp_nexthop_entry *te;
+ struct eigrp_route_descriptor *te;
struct listnode *node;
for (ALL_LIST_ELEMENTS_RO(pe->entries, node, te)) {
if (all
- || (((te->flags
- & EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG)
- == EIGRP_NEXTHOP_ENTRY_SUCCESSOR_FLAG)
- || ((te->flags
- & EIGRP_NEXTHOP_ENTRY_FSUCCESSOR_FLAG)
- == EIGRP_NEXTHOP_ENTRY_FSUCCESSOR_FLAG))) {
- show_ip_eigrp_nexthop_entry(vty, eigrp, te,
- &first);
+ || (((te->flags & EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG)
+ == EIGRP_ROUTE_DESCRIPTOR_SUCCESSOR_FLAG)
+ || ((te->flags & EIGRP_ROUTE_DESCRIPTOR_FSUCCESSOR_FLAG)
+ == EIGRP_ROUTE_DESCRIPTOR_FSUCCESSOR_FLAG))) {
+ show_ip_eigrp_route_descriptor(vty, eigrp, te, &first);
first = false;
}
}
static void eigrp_topology_helper(struct vty *vty, struct eigrp *eigrp,
const char *all)
{
- struct eigrp_prefix_entry *tn;
+ struct eigrp_prefix_descriptor *tn;
struct route_node *rn;
show_ip_eigrp_topology_header(vty, eigrp);
"For a specific prefix\n")
{
struct eigrp *eigrp;
- struct eigrp_prefix_entry *tn;
+ struct eigrp_prefix_descriptor *tn;
struct route_node *rn;
struct prefix cmp;
--- /dev/null
+/*
+ * EIGRP YANG Functions.
+ * Copyright (C) 2019
+ * Authors:
+ * Donnie Savage
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _EIGRP_YANG_H_
+#define _EIGRP_YANG_H_
+
+/*Prototypes*/
+
+/* eigrp_northbound.c */
+extern const struct frr_yang_module_info frr_eigrpd_info;
+
+#endif /*EIGRP_YANG_H_ */
#include "log.h"
#include "nexthop.h"
+#include "eigrpd/eigrp_types.h"
#include "eigrpd/eigrp_structs.h"
#include "eigrpd/eigrpd.h"
#include "eigrpd/eigrp_interface.h"
#include "eigrpd/eigrp_network.h"
#include "eigrpd/eigrp_topology.h"
#include "eigrpd/eigrp_fsm.h"
+#include "eigrpd/eigrp_metric.h"
static int eigrp_interface_address_add(ZAPI_CALLBACK_ARGS);
static int eigrp_interface_address_delete(ZAPI_CALLBACK_ARGS);
{
struct zapi_route api;
struct zapi_nexthop *api_nh;
- struct eigrp_nexthop_entry *te;
+ struct eigrp_route_descriptor *te;
struct listnode *node;
int count = 0;
#define EIGRP_MAJOR_VERSION 1
#define EIGRP_MINOR_VERSION 2
+#define EIGRP_TLV_32B_VERSION 1 /* Original 32bit scaled metrics */
+#define EIGRP_TLV_64B_VERSION 2 /* Current 64bit 'wide' metrics */
+#define EIGRP_TLV_MTR_VERSION 3 /* MTR TLVs with 32bit metric *Not Supported */
+#define EIGRP_TLV_SAF_VERSION 4 /* SAF TLVs with 64bit metric *Not Supported */
+
+struct eigrp_master {
+ /* EIGRP instance. */
+ struct list *eigrp;
+
+ /* EIGRP thread master. */
+ struct thread_master *master;
+
+ /* Zebra interface list. */
+ struct list *iflist;
+
+ /* EIGRP start time. */
+ time_t start_time;
+
+ /* Various EIGRP global configuration. */
+ uint8_t options;
+
+#define EIGRP_MASTER_SHUTDOWN (1 << 0) /* deferred-shutdown */
+};
+
/* Extern variables. */
extern struct zclient *zclient;
extern struct thread_master *master;
/* Prototypes */
extern void eigrp_master_init(void);
extern void eigrp_terminate(void);
-extern void eigrp_finish_final(struct eigrp *);
-extern void eigrp_finish(struct eigrp *);
+extern void eigrp_finish_final(struct eigrp *eigrp);
+extern void eigrp_finish(struct eigrp *eigrp);
extern struct eigrp *eigrp_get(uint16_t as, vrf_id_t vrf_id);
extern struct eigrp *eigrp_lookup(vrf_id_t vrf_id);
-extern void eigrp_router_id_update(struct eigrp *);
-
-/* eigrp_cli.c */
-extern void eigrp_cli_show_header(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void eigrp_cli_show_end_header(struct vty *vty, struct lyd_node *dnode);
-extern void eigrp_cli_show_router_id(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void eigrp_cli_show_passive_interface(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-extern void eigrp_cli_show_active_time(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void eigrp_cli_show_variance(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void eigrp_cli_show_maximum_paths(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-extern void eigrp_cli_show_metrics(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void eigrp_cli_show_network(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void eigrp_cli_show_neighbor(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void eigrp_cli_show_redistribute(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-extern void eigrp_cli_show_delay(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void eigrp_cli_show_bandwidth(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void eigrp_cli_show_hello_interval(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-extern void eigrp_cli_show_hold_time(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void eigrp_cli_show_summarize_address(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-extern void eigrp_cli_show_authentication(struct vty *vty,
- struct lyd_node *dnode,
- bool show_defaults);
-extern void eigrp_cli_show_keychain(struct vty *vty, struct lyd_node *dnode,
- bool show_defaults);
-extern void eigrp_cli_init(void);
-
-/* eigrp_northbound.c */
-extern const struct frr_yang_module_info frr_eigrpd_info;
+extern void eigrp_router_id_update(struct eigrp *eigrp);
#endif /* _ZEBRA_EIGRPD_H */
eigrpd/eigrp_hello.c \
eigrpd/eigrp_interface.c \
eigrpd/eigrp_memory.c \
+ eigrpd/eigrp_metric.c \
eigrpd/eigrp_neighbor.c \
eigrpd/eigrp_network.c \
eigrpd/eigrp_northbound.c \
# end
noinst_HEADERS += \
+ eigrpd/eigrp_cli.h \
eigrpd/eigrp_const.h \
eigrpd/eigrp_errors.h \
eigrpd/eigrp_filter.h \
eigrpd/eigrp_interface.h \
eigrpd/eigrp_macros.h \
eigrpd/eigrp_memory.h \
+ eigrpd/eigrp_metric.h \
eigrpd/eigrp_neighbor.h \
eigrpd/eigrp_network.h \
eigrpd/eigrp_packet.h \
eigrpd/eigrp_snmp.h \
eigrpd/eigrp_structs.h \
+ eigrpd/eigrp_types.h \
eigrpd/eigrp_vrf.h \
eigrpd/eigrp_vty.h \
+ eigrpd/eigrp_yang.h \
eigrpd/eigrp_zebra.h \
# end
struct bfd_info *bfd_info;
struct ldp_sync_info *ldp_sync_info;
bool lfa_protection[ISIS_LEVELS];
+ bool rlfa_protection[ISIS_LEVELS];
+ uint32_t rlfa_max_metric[ISIS_LEVELS];
struct hash *lfa_excluded_ifaces[ISIS_LEVELS];
bool tilfa_protection[ISIS_LEVELS];
bool tilfa_node_protection[ISIS_LEVELS];
if (no) {
nb_cli_enqueue_change(
vty, "./fast-reroute/level-1/lfa/load-sharing",
- NB_OP_DESTROY, "true");
+ NB_OP_MODIFY, "true");
} else {
nb_cli_enqueue_change(
vty, "./fast-reroute/level-1/lfa/load-sharing",
- NB_OP_CREATE, "false");
+ NB_OP_MODIFY, "false");
}
}
if (!level || strmatch(level, "level-2")) {
if (no) {
nb_cli_enqueue_change(
vty, "./fast-reroute/level-2/lfa/load-sharing",
- NB_OP_DESTROY, "true");
+ NB_OP_MODIFY, "true");
} else {
nb_cli_enqueue_change(
vty, "./fast-reroute/level-2/lfa/load-sharing",
- NB_OP_CREATE, "false");
+ NB_OP_MODIFY, "false");
}
}
dnode->parent->parent->schema->name);
}
+/*
+ * XPath: /frr-isisd:isis/instance/fast-reroute/level-{1,2}/remote-lfa/prefix-list
+ */
+DEFPY_YANG (isis_frr_remote_lfa_plist,
+ isis_frr_remote_lfa_plist_cmd,
+ "fast-reroute remote-lfa prefix-list WORD$plist [<level-1|level-2>$level]",
+ "Configure Fast ReRoute\n"
+ "Enable remote LFA related configuration\n"
+ "Filter PQ node router ID based on prefix list\n"
+ "Prefix-list name\n"
+ "Enable router ID filtering for level-1 only\n"
+ "Enable router ID filtering for level-2 only\n")
+{
+ if (!level || strmatch(level, "level-1"))
+ nb_cli_enqueue_change(
+ vty, "./fast-reroute/level-1/remote-lfa/prefix-list",
+ NB_OP_MODIFY, plist);
+ if (!level || strmatch(level, "level-2"))
+ nb_cli_enqueue_change(
+ vty, "./fast-reroute/level-2/remote-lfa/prefix-list",
+ NB_OP_MODIFY, plist);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG (no_isis_frr_remote_lfa_plist,
+ no_isis_frr_remote_lfa_plist_cmd,
+ "no fast-reroute remote-lfa prefix-list [WORD] [<level-1|level-2>$level]",
+ NO_STR
+ "Configure Fast ReRoute\n"
+ "Enable remote LFA related configuration\n"
+ "Filter PQ node router ID based on prefix list\n"
+ "Prefix-list name\n"
+ "Enable router ID filtering for level-1 only\n"
+ "Enable router ID filtering for level-2 only\n")
+{
+ if (!level || strmatch(level, "level-1"))
+ nb_cli_enqueue_change(
+ vty, "./fast-reroute/level-1/remote-lfa/prefix-list",
+ NB_OP_DESTROY, NULL);
+ if (!level || strmatch(level, "level-2"))
+ nb_cli_enqueue_change(
+ vty, "./fast-reroute/level-2/remote-lfa/prefix-list",
+ NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_isis_frr_remote_lfa_plist(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " fast-reroute remote-lfa prefix-list %s %s\n",
+ yang_dnode_get_string(dnode, NULL),
+ dnode->parent->parent->schema->name);
+}
+
/*
* XPath: /frr-interface:lib/interface/frr-isisd:isis/passive
*/
}
}
+ /* Remote LFA */
+ l1_enabled = yang_dnode_get_bool(dnode, "./level-1/remote-lfa/enable");
+ l2_enabled = yang_dnode_get_bool(dnode, "./level-2/remote-lfa/enable");
+
+ if (l1_enabled || l2_enabled) {
+ if (l1_enabled == l2_enabled) {
+ vty_out(vty,
+ " isis fast-reroute remote-lfa tunnel mpls-ldp\n");
+ vty_out(vty, "\n");
+ } else {
+ if (l1_enabled)
+ vty_out(vty,
+ " isis fast-reroute remote-lfa tunnel mpls-ldp level-1\n");
+ if (l2_enabled)
+ vty_out(vty,
+ " isis fast-reroute remote-lfa tunnel mpls-ldp level-2\n");
+ }
+ }
+
/* TI-LFA */
l1_enabled = yang_dnode_get_bool(dnode, "./level-1/ti-lfa/enable");
l2_enabled = yang_dnode_get_bool(dnode, "./level-2/ti-lfa/enable");
yang_dnode_get_string(dnode, NULL));
}
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-{1,2}/remote-lfa/enable
+ */
+DEFPY(isis_remote_lfa, isis_remote_lfa_cmd,
+ "[no] isis fast-reroute remote-lfa tunnel mpls-ldp [level-1|level-2]$level",
+ NO_STR
+ "IS-IS routing protocol\n"
+ "Interface IP Fast-reroute configuration\n"
+ "Enable remote LFA computation\n"
+ "Enable remote LFA computation using tunnels\n"
+ "Use MPLS LDP tunnel to reach the remote LFA node\n"
+ "Enable LFA computation for Level 1 only\n"
+ "Enable LFA computation for Level 2 only\n")
+{
+ if (!level || strmatch(level, "level-1")) {
+ if (no) {
+ nb_cli_enqueue_change(
+ vty,
+ "./frr-isisd:isis/fast-reroute/level-1/remote-lfa/enable",
+ NB_OP_MODIFY, "false");
+ } else {
+ nb_cli_enqueue_change(
+ vty,
+ "./frr-isisd:isis/fast-reroute/level-1/remote-lfa/enable",
+ NB_OP_MODIFY, "true");
+ }
+ }
+ if (!level || strmatch(level, "level-2")) {
+ if (no) {
+ nb_cli_enqueue_change(
+ vty,
+ "./frr-isisd:isis/fast-reroute/level-2/remote-lfa/enable",
+ NB_OP_MODIFY, "false");
+ } else {
+ nb_cli_enqueue_change(
+ vty,
+ "./frr-isisd:isis/fast-reroute/level-2/remote-lfa/enable",
+ NB_OP_MODIFY, "true");
+ }
+ }
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-{1,2}/remote-lfa/maximum-metric
+ */
+DEFPY(isis_remote_lfa_max_metric, isis_remote_lfa_max_metric_cmd,
+ "[no] isis fast-reroute remote-lfa maximum-metric (1-16777215)$metric [level-1|level-2]$level",
+ NO_STR
+ "IS-IS routing protocol\n"
+ "Interface IP Fast-reroute configuration\n"
+ "Enable remote LFA computation\n"
+ "Limit remote LFA node selection within the metric\n"
+ "Value of the metric\n"
+ "Enable LFA computation for Level 1 only\n"
+ "Enable LFA computation for Level 2 only\n")
+{
+ if (!level || strmatch(level, "level-1")) {
+ if (no) {
+ nb_cli_enqueue_change(
+ vty,
+ "./frr-isisd:isis/fast-reroute/level-1/remote-lfa/maximum-metric",
+ NB_OP_DESTROY, NULL);
+ } else {
+ nb_cli_enqueue_change(
+ vty,
+ "./frr-isisd:isis/fast-reroute/level-1/remote-lfa/maximum-metric",
+ NB_OP_MODIFY, metric_str);
+ }
+ }
+ if (!level || strmatch(level, "level-2")) {
+ if (no) {
+ nb_cli_enqueue_change(
+ vty,
+ "./frr-isisd:isis/fast-reroute/level-2/remote-lfa/maximum-metric",
+ NB_OP_DESTROY, NULL);
+ } else {
+ nb_cli_enqueue_change(
+ vty,
+ "./frr-isisd:isis/fast-reroute/level-2/remote-lfa/maximum-metric",
+ NB_OP_MODIFY, metric_str);
+ }
+ }
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+void cli_show_frr_remote_lfa_max_metric(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults)
+{
+ vty_out(vty, " isis fast-reroute remote-lfa maximum-metric %s %s\n",
+ yang_dnode_get_string(dnode, NULL),
+ dnode->parent->parent->schema->name);
+}
+
/*
* XPath: /frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-{1,2}/ti-lfa/enable
*/
install_element(ISIS_NODE, &isis_frr_lfa_priority_limit_cmd);
install_element(ISIS_NODE, &isis_frr_lfa_tiebreaker_cmd);
install_element(ISIS_NODE, &isis_frr_lfa_load_sharing_cmd);
+ install_element(ISIS_NODE, &isis_frr_remote_lfa_plist_cmd);
+ install_element(ISIS_NODE, &no_isis_frr_remote_lfa_plist_cmd);
install_element(INTERFACE_NODE, &isis_passive_cmd);
install_element(INTERFACE_NODE, &isis_lfa_cmd);
install_element(INTERFACE_NODE, &isis_lfa_exclude_interface_cmd);
+ install_element(INTERFACE_NODE, &isis_remote_lfa_cmd);
+ install_element(INTERFACE_NODE, &isis_remote_lfa_max_metric_cmd);
install_element(INTERFACE_NODE, &isis_ti_lfa_cmd);
install_element(ISIS_NODE, &log_adj_changes_cmd);
#include "vrf.h"
#include "table.h"
#include "srcdest_table.h"
+#include "plist.h"
+#include "zclient.h"
#include "isis_common.h"
#include "isisd.h"
#include "isis_mt.h"
#include "isis_tlvs.h"
#include "isis_spf_private.h"
-#include "isisd/isis_errors.h"
+#include "isis_zebra.h"
+#include "isis_errors.h"
DEFINE_MTYPE_STATIC(ISISD, ISIS_SPF_NODE, "ISIS SPF Node");
DEFINE_MTYPE_STATIC(ISISD, ISIS_LFA_TIEBREAKER, "ISIS LFA Tiebreaker");
DEFINE_MTYPE_STATIC(ISISD, ISIS_LFA_EXCL_IFACE, "ISIS LFA Excluded Interface");
+DEFINE_MTYPE_STATIC(ISISD, ISIS_RLFA, "ISIS Remote LFA");
static inline int isis_spf_node_compare(const struct isis_spf_node *a,
const struct isis_spf_node *b)
{
const struct lfa_protected_resource *resource;
- if (spftree->type != SPF_TYPE_TI_LFA)
+ if (spftree->type != SPF_TYPE_RLFA && spftree->type != SPF_TYPE_TI_LFA)
return false;
/*
return false;
}
-/* Check if a given TI-LFA post-convergence SPF vertex needs protection. */
-static bool tilfa_check_needs_protection(const struct isis_spftree *spftree_pc,
- const struct isis_vertex *vertex)
+/* Check if a given RLFA/TI-LFA post-convergence SPF vertex needs protection. */
+static bool lfa_check_needs_protection(const struct isis_spftree *spftree_pc,
+ const struct isis_vertex *vertex)
{
struct isis_vertex *vertex_old;
- /* Only local adjacencies need Adj-SID protection. */
- if (VTYPE_IS(vertex->type)
+ /* Only local adjacencies need TI-LFA Adj-SID protection. */
+ if (spftree_pc->type == SPF_TYPE_TI_LFA && VTYPE_IS(vertex->type)
&& !isis_adj_find(spftree_pc->area, spftree_pc->level,
vertex->N.id))
return false;
if (!vertex_old)
return false;
+ /* Skip vertex if it's already protected by local LFA. */
+ if (CHECK_FLAG(vertex_old->flags, F_ISIS_VERTEX_LFA_PROTECTED))
+ return false;
+
return spf_vertex_check_is_affected(
vertex_old, spftree_pc->sysid,
&spftree_pc->lfa.protected_resource);
if (!spftree_pc->area->srdb.enabled)
return -1;
- if (IS_DEBUG_LFA)
- vid2string(vertex, buf, sizeof(buf));
-
- if (!tilfa_check_needs_protection(spftree_pc, vertex)) {
+ if (!lfa_check_needs_protection(spftree_pc, vertex)) {
if (IS_DEBUG_LFA)
zlog_debug(
"ISIS-LFA: %s %s unaffected by %s",
- vtype2string(vertex->type), buf,
+ vtype2string(vertex->type),
+ vid2string(vertex, buf, sizeof(buf)),
lfa_protected_resource2str(
&spftree_pc->lfa.protected_resource));
if (IS_DEBUG_LFA)
zlog_debug(
"ISIS-LFA: %s %s already covered by node protection",
- vtype2string(vertex->type), buf);
+ vtype2string(vertex->type),
+ vid2string(vertex, buf, sizeof(buf)));
return -1;
}
if (IS_DEBUG_LFA)
zlog_debug(
"ISIS-LFA: %s %s already covered by node protection",
- vtype2string(vertex->type), buf);
+ vtype2string(vertex->type),
+ vid2string(vertex, buf, sizeof(buf)));
return -1;
}
if (IS_DEBUG_LFA)
zlog_debug(
"ISIS-LFA: computing repair path(s) of %s %s w.r.t %s",
- vtype2string(vertex->type), buf,
+ vtype2string(vertex->type),
+ vid2string(vertex, buf, sizeof(buf)),
lfa_protected_resource2str(
&spftree_pc->lfa.protected_resource));
if (ret != 0)
zlog_warn(
"ISIS-LFA: failed to compute repair path(s) of %s %s w.r.t %s",
- vtype2string(vertex->type), buf,
+ vtype2string(vertex->type),
+ vid2string(vertex, buf, sizeof(buf)),
lfa_protected_resource2str(
&spftree_pc->lfa.protected_resource));
struct isis_vertex *vertex_child;
struct isis_vertex_adj *vadj;
bool reverse = false;
- char buf1[VID2STR_BUFFER];
- char buf2[VID2STR_BUFFER];
-
- if (IS_DEBUG_LFA)
- zlog_debug("ISIS-LFA: vertex %s parent %s",
- vid2string(vertex, buf1, sizeof(buf1)),
- vid2string(pvertex, buf2, sizeof(buf2)));
if (p_space && resource->type == LFA_NODE_PROTECTION) {
if (isis_spf_node_find(&resource->nodes, vertex->N.id))
if (isis_spf_node_find(nodes, vertex->N.id))
continue;
- if (IS_DEBUG_LFA)
- zlog_debug("ISIS-LFA: checking %s",
- vid2string(vertex, buf, sizeof(buf)));
-
if (!vertex_is_affected(spftree_root, adj_nodes, p_space,
vertex, resource)) {
if (IS_DEBUG_LFA)
struct isis_spf_node *adj_node;
if (IS_DEBUG_LFA)
- zlog_debug("ISIS-LFA: computing the P/Q spaces w.r.t. %s",
+ zlog_debug("ISIS-LFA: computing TI-LFAs for %s",
lfa_protected_resource2str(resource));
/* Populate list of nodes affected by link failure. */
return 0;
}
+/* Find Router ID of PQ node. */
+static struct in_addr *rlfa_pq_node_rtr_id(struct isis_spftree *spftree,
+ const struct isis_vertex *vertex_pq)
+{
+ struct isis_lsp *lsp;
+
+ lsp = isis_root_system_lsp(spftree->lspdb, vertex_pq->N.id);
+ if (!lsp)
+ return NULL;
+
+ if (lsp->tlvs->router_cap->router_id.s_addr == INADDR_ANY)
+ return NULL;
+
+ return &lsp->tlvs->router_cap->router_id;
+}
+
+/* Find PQ node by intersecting the P/Q spaces. This is a recursive function. */
+static const struct in_addr *
+rlfa_find_pq_node(struct isis_spftree *spftree_pc,
+ struct isis_vertex *vertex_dest,
+ const struct isis_vertex *vertex,
+ const struct isis_vertex *vertex_child)
+{
+ struct isis_area *area = spftree_pc->area;
+ int level = spftree_pc->level;
+ struct isis_vertex *pvertex;
+ struct listnode *node;
+ bool is_pnode, is_qnode;
+
+ if (!vertex_child)
+ goto parents;
+ if (vertex->type != VTYPE_NONPSEUDO_IS
+ && vertex->type != VTYPE_NONPSEUDO_TE_IS)
+ goto parents;
+ if (!VTYPE_IS(vertex_child->type))
+ vertex_child = NULL;
+
+ /* Check if node is part of the extended P-space and/or Q-space. */
+ is_pnode = lfa_ext_p_space_check(spftree_pc, vertex_dest, vertex);
+ is_qnode = lfa_q_space_check(spftree_pc, vertex);
+
+ if (is_pnode && is_qnode) {
+ const struct in_addr *rtr_id_pq;
+ uint32_t max_metric;
+ struct prefix_list *plist = NULL;
+
+ rtr_id_pq = rlfa_pq_node_rtr_id(spftree_pc, vertex);
+ if (!rtr_id_pq) {
+ if (IS_DEBUG_LFA) {
+ char buf[VID2STR_BUFFER];
+
+ vid2string(vertex, buf, sizeof(buf));
+ zlog_debug(
+ "ISIS-LFA: tentative PQ node (%s %s) doesn't have a router-ID",
+ vtype2string(vertex->type), buf);
+ }
+ goto parents;
+ }
+
+ max_metric = spftree_pc->lfa.remote.max_metric;
+ if (max_metric && vertex->d_N > max_metric) {
+ if (IS_DEBUG_LFA)
+ zlog_debug(
+ "ISIS-LFA: skipping PQ node %pI4 (maximum metric)",
+ rtr_id_pq);
+ goto parents;
+ }
+
+ plist = area->rlfa_plist[level - 1];
+ if (plist) {
+ struct prefix p;
+
+ p.family = AF_INET;
+ p.prefixlen = IPV4_MAX_BITLEN;
+ p.u.prefix4 = *rtr_id_pq;
+ if (prefix_list_apply(plist, &p) == PREFIX_DENY) {
+ if (IS_DEBUG_LFA)
+ zlog_debug(
+ "ISIS-LFA: PQ node %pI4 filtered by prefix-list",
+ rtr_id_pq);
+ goto parents;
+ }
+ }
+
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: found PQ node: %pI4", rtr_id_pq);
+
+ return rtr_id_pq;
+ }
+
+parents:
+ for (ALL_LIST_ELEMENTS_RO(vertex->parents, node, pvertex)) {
+ const struct in_addr *rtr_id_pq;
+
+ rtr_id_pq = rlfa_find_pq_node(spftree_pc, vertex_dest, pvertex,
+ vertex);
+ if (rtr_id_pq)
+ return rtr_id_pq;
+ }
+
+ return NULL;
+}
+
+int rlfa_cmp(const struct rlfa *a, const struct rlfa *b)
+{
+ return prefix_cmp(&a->prefix, &b->prefix);
+}
+
+static struct rlfa *rlfa_add(struct isis_spftree *spftree,
+ struct isis_vertex *vertex,
+ struct in_addr pq_address)
+{
+ struct rlfa *rlfa;
+
+ assert(VTYPE_IP(vertex->type));
+ rlfa = XCALLOC(MTYPE_ISIS_RLFA, sizeof(*rlfa));
+ rlfa->prefix = vertex->N.ip.p.dest;
+ rlfa->vertex = vertex;
+ rlfa->pq_address = pq_address;
+ rlfa_tree_add(&spftree->lfa.remote.rlfas, rlfa);
+
+ return rlfa;
+}
+
+static void rlfa_delete(struct isis_spftree *spftree, struct rlfa *rlfa)
+{
+ rlfa_tree_del(&spftree->lfa.remote.rlfas, rlfa);
+ XFREE(MTYPE_ISIS_RLFA, rlfa);
+}
+
+static struct rlfa *rlfa_lookup(struct isis_spftree *spftree,
+ union prefixconstptr pu)
+{
+ struct rlfa s = {};
+
+ s.prefix = *pu.p;
+ return rlfa_tree_find(&spftree->lfa.remote.rlfas, &s);
+}
+
+static int isis_area_verify_routes_cb(struct thread *thread)
+{
+ struct isis_area *area = THREAD_ARG(thread);
+
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: updating RLFAs in the RIB");
+
+ isis_area_verify_routes(area);
+
+ return 0;
+}
+
+static mpls_label_t rlfa_nexthop_label(struct isis_spftree *spftree,
+ struct isis_vertex_adj *vadj,
+ struct zapi_rlfa_response *response)
+{
+ struct isis_spf_adj *sadj = vadj->sadj;
+ struct isis_adjacency *adj = sadj->adj;
+
+ /*
+ * Special case to make unit tests work (use implicit-null labels
+ * instead of artifical ones).
+ */
+ if (CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES))
+ return MPLS_LABEL_IMPLICIT_NULL;
+
+ for (unsigned int i = 0; i < response->nexthop_num; i++) {
+ switch (response->nexthops[i].family) {
+ case AF_INET:
+ for (unsigned int j = 0; j < adj->ipv4_address_count;
+ j++) {
+ struct in_addr addr = adj->ipv4_addresses[j];
+
+ if (!IPV4_ADDR_SAME(
+ &addr,
+ &response->nexthops[i].gate.ipv4))
+ continue;
+
+ return response->nexthops[i].label;
+ }
+ break;
+ case AF_INET6:
+ for (unsigned int j = 0; j < adj->ipv6_address_count;
+ j++) {
+ struct in6_addr addr = adj->ipv6_addresses[j];
+
+ if (!IPV6_ADDR_SAME(
+ &addr,
+ &response->nexthops[i].gate.ipv6))
+ continue;
+
+ return response->nexthops[i].label;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return MPLS_INVALID_LABEL;
+}
+
+int isis_rlfa_activate(struct isis_spftree *spftree, struct rlfa *rlfa,
+ struct zapi_rlfa_response *response)
+{
+ struct isis_area *area = spftree->area;
+ struct isis_vertex *vertex = rlfa->vertex;
+ struct isis_vertex_adj *vadj;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(vertex->Adj_N, node, vadj)) {
+ mpls_label_t ldp_label;
+ struct mpls_label_stack *label_stack;
+ size_t num_labels = 0;
+ size_t i = 0;
+
+ ldp_label = rlfa_nexthop_label(spftree, vadj, response);
+ if (ldp_label == MPLS_INVALID_LABEL) {
+ if (IS_DEBUG_LFA)
+ zlog_debug(
+ "ISIS-LFA: failed to activate RLFA: missing LDP label to reach PQ node through %s",
+ sysid_print(vadj->sadj->id));
+ return -1;
+ }
+
+ if (ldp_label != MPLS_LABEL_IMPLICIT_NULL)
+ num_labels++;
+ if (response->pq_label != MPLS_LABEL_IMPLICIT_NULL)
+ num_labels++;
+ if (vadj->sr.present
+ && vadj->sr.label != MPLS_LABEL_IMPLICIT_NULL)
+ num_labels++;
+
+ /* Allocate label stack. */
+ label_stack =
+ XCALLOC(MTYPE_ISIS_NEXTHOP_LABELS,
+ sizeof(struct mpls_label_stack)
+ + num_labels * sizeof(mpls_label_t));
+ label_stack->num_labels = num_labels;
+
+ /* Push label allocated by the nexthop (outer label). */
+ if (ldp_label != MPLS_LABEL_IMPLICIT_NULL)
+ label_stack->label[i++] = ldp_label;
+ /* Push label allocated by the PQ node (inner label). */
+ if (response->pq_label != MPLS_LABEL_IMPLICIT_NULL)
+ label_stack->label[i++] = response->pq_label;
+ /* Preserve the original Prefix-SID label when it's present. */
+ if (vadj->sr.present
+ && vadj->sr.label != MPLS_LABEL_IMPLICIT_NULL)
+ label_stack->label[i++] = vadj->sr.label;
+
+ vadj->label_stack = label_stack;
+ }
+
+ isis_route_create(&vertex->N.ip.p.dest, &vertex->N.ip.p.src,
+ vertex->d_N, vertex->depth, &vertex->N.ip.sr,
+ vertex->Adj_N, true, area,
+ spftree->route_table_backup);
+ spftree->lfa.protection_counters.rlfa[vertex->N.ip.priority] += 1;
+
+ thread_cancel(&area->t_rlfa_rib_update);
+ thread_add_timer(master, isis_area_verify_routes_cb, area, 2,
+ &area->t_rlfa_rib_update);
+
+ return 0;
+}
+
+void isis_rlfa_deactivate(struct isis_spftree *spftree, struct rlfa *rlfa)
+{
+ struct isis_area *area = spftree->area;
+ struct isis_vertex *vertex = rlfa->vertex;
+ struct route_node *rn;
+
+ rn = route_node_lookup(spftree->route_table_backup, &rlfa->prefix);
+ if (!rn)
+ return;
+ isis_route_delete(area, rn, spftree->route_table_backup);
+ spftree->lfa.protection_counters.rlfa[vertex->N.ip.priority] -= 1;
+
+ thread_cancel(&area->t_rlfa_rib_update);
+ thread_add_timer(master, isis_area_verify_routes_cb, area, 2,
+ &area->t_rlfa_rib_update);
+}
+
+void isis_rlfa_list_init(struct isis_spftree *spftree)
+{
+ rlfa_tree_init(&spftree->lfa.remote.rlfas);
+}
+
+void isis_rlfa_list_clear(struct isis_spftree *spftree)
+{
+ while (rlfa_tree_count(&spftree->lfa.remote.rlfas) > 0) {
+ struct rlfa *rlfa;
+
+ rlfa = rlfa_tree_first(&spftree->lfa.remote.rlfas);
+ isis_rlfa_deactivate(spftree, rlfa);
+ rlfa_delete(spftree, rlfa);
+ }
+}
+
+void isis_rlfa_process_ldp_response(struct zapi_rlfa_response *response)
+{
+ struct isis *isis;
+ struct isis_area *area;
+ struct isis_spftree *spftree;
+ struct rlfa *rlfa;
+ enum spf_tree_id tree_id;
+ uint32_t spf_run_id;
+ int level;
+
+ if (response->igp.protocol != ZEBRA_ROUTE_ISIS)
+ return;
+
+ isis = isis_lookup_by_vrfid(response->igp.vrf_id);
+ if (!isis)
+ return;
+
+ area = isis_area_lookup(response->igp.isis.area_tag,
+ response->igp.vrf_id);
+ if (!area)
+ return;
+
+ tree_id = response->igp.isis.spf.tree_id;
+ if (tree_id != SPFTREE_IPV4 && tree_id != SPFTREE_IPV6) {
+ zlog_warn("ISIS-LFA: invalid SPF tree ID received from LDP");
+ return;
+ }
+
+ level = response->igp.isis.spf.level;
+ if (level != ISIS_LEVEL1 && level != ISIS_LEVEL2) {
+ zlog_warn("ISIS-LFA: invalid IS-IS level received from LDP");
+ return;
+ }
+
+ spf_run_id = response->igp.isis.spf.run_id;
+ spftree = area->spftree[tree_id][level - 1];
+ if (spftree->runcount != spf_run_id)
+ /* Outdated RLFA, ignore... */
+ return;
+
+ rlfa = rlfa_lookup(spftree, &response->destination);
+ if (!rlfa) {
+ zlog_warn(
+ "ISIS-LFA: couldn't find Remote-LFA %pFX received from LDP",
+ &response->destination);
+ return;
+ }
+
+ if (response->pq_label != MPLS_INVALID_LABEL) {
+ if (IS_DEBUG_LFA)
+ zlog_debug(
+ "ISIS-LFA: activating/updating RLFA for %pFX",
+ &rlfa->prefix);
+
+ if (isis_rlfa_activate(spftree, rlfa, response) != 0)
+ isis_rlfa_deactivate(spftree, rlfa);
+ } else {
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: deactivating RLFA for %pFX",
+ &rlfa->prefix);
+
+ isis_rlfa_deactivate(spftree, rlfa);
+ }
+}
+
+void isis_ldp_rlfa_handle_client_close(struct zapi_client_close_info *info)
+{
+ struct isis *isis = isis_lookup_by_vrfid(VRF_DEFAULT);
+ struct isis_area *area;
+ struct listnode *node;
+
+ if (!isis)
+ return;
+
+ /* Check if the LDP main client session closed */
+ if (info->proto != ZEBRA_ROUTE_LDP || info->session_id == 0)
+ return;
+
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: LDP is down, deactivating all RLFAs");
+
+ for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
+ for (int tree = SPFTREE_IPV4; tree < SPFTREE_COUNT; tree++) {
+ for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS;
+ level++) {
+ struct isis_spftree *spftree;
+
+ spftree = area->spftree[tree][level - 1];
+ isis_rlfa_list_clear(spftree);
+ }
+ }
+ }
+}
+
+/**
+ * Check if the given SPF vertex needs protection and, if so, attempt to
+ * compute a Remote LFA for it.
+ *
+ * @param spftree_pc The post-convergence SPF tree
+ * @param vertex IS-IS SPF vertex to check
+ */
+void isis_rlfa_check(struct isis_spftree *spftree_pc,
+ struct isis_vertex *vertex)
+{
+ struct isis_spftree *spftree_old = spftree_pc->lfa.old.spftree;
+ struct rlfa *rlfa;
+ const struct in_addr *rtr_id_pq;
+ char buf[VID2STR_BUFFER];
+
+ if (!lfa_check_needs_protection(spftree_pc, vertex)) {
+ if (IS_DEBUG_LFA)
+ zlog_debug(
+ "ISIS-LFA: %s %s unaffected by %s",
+ vtype2string(vertex->type),
+ vid2string(vertex, buf, sizeof(buf)),
+ lfa_protected_resource2str(
+ &spftree_pc->lfa.protected_resource));
+
+ return;
+ }
+
+ if (IS_DEBUG_LFA)
+ zlog_debug(
+ "ISIS-LFA: computing repair path(s) of %s %s w.r.t %s",
+ vtype2string(vertex->type),
+ vid2string(vertex, buf, sizeof(buf)),
+ lfa_protected_resource2str(
+ &spftree_pc->lfa.protected_resource));
+
+ /* Find PQ node. */
+ rtr_id_pq = rlfa_find_pq_node(spftree_pc, vertex, vertex, NULL);
+ if (!rtr_id_pq) {
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: no acceptable PQ node found");
+ return;
+ }
+
+ /* Store valid RLFA and store LDP label for the PQ node. */
+ rlfa = rlfa_add(spftree_old, vertex, *rtr_id_pq);
+
+ /* Register RLFA with LDP. */
+ if (isis_zebra_rlfa_register(spftree_old, rlfa) != 0)
+ rlfa_delete(spftree_old, rlfa);
+}
+
+/**
+ * Compute the Remote LFA backup paths for a given protected interface.
+ *
+ * @param area IS-IS area
+ * @param spftree IS-IS SPF tree
+ * @param spftree_reverse IS-IS Reverse SPF tree
+ * @param max_metric Remote LFA maximum metric
+ * @param resource Protected resource
+ *
+ * @return Pointer to the post-convergence SPF tree
+ */
+struct isis_spftree *isis_rlfa_compute(struct isis_area *area,
+ struct isis_spftree *spftree,
+ struct isis_spftree *spftree_reverse,
+ uint32_t max_metric,
+ struct lfa_protected_resource *resource)
+{
+ struct isis_spftree *spftree_pc;
+
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: computing remote LFAs for %s",
+ lfa_protected_resource2str(resource));
+
+ /* Create post-convergence SPF tree. */
+ spftree_pc = isis_spftree_new(area, spftree->lspdb, spftree->sysid,
+ spftree->level, spftree->tree_id,
+ SPF_TYPE_RLFA, spftree->flags);
+ spftree_pc->lfa.old.spftree = spftree;
+ spftree_pc->lfa.old.spftree_reverse = spftree_reverse;
+ spftree_pc->lfa.remote.max_metric = max_metric;
+ spftree_pc->lfa.protected_resource = *resource;
+
+ /* Compute the extended P-space and Q-space. */
+ lfa_calc_pq_spaces(spftree_pc, resource);
+
+ if (IS_DEBUG_LFA)
+ zlog_debug(
+ "ISIS-LFA: computing the post convergence SPT w.r.t. %s",
+ lfa_protected_resource2str(resource));
+
+ /* Re-run SPF in the local node to find the post-convergence paths. */
+ isis_run_spf(spftree_pc);
+
+ return spftree_pc;
+}
+
/* Calculate the distance from the root node to the given IP destination. */
static int lfa_calc_dist_destination(struct isis_spftree *spftree,
const struct isis_vertex *vertex_N,
}
static struct list *
-isis_lfa_tiebreakers(struct isis_area *area, struct isis_circuit *circuit,
- struct isis_spftree *spftree,
+isis_lfa_tiebreakers(struct isis_area *area, struct isis_spftree *spftree,
struct lfa_protected_resource *resource,
struct isis_vertex *vertex,
struct isis_spf_adj *sadj_primary, struct list *lfa_list)
resource->type = LFA_LINK_PROTECTION;
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: computing local LFAs for %s",
+ lfa_protected_resource2str(resource));
+
for (ALL_QUEUE_ELEMENTS_RO(&spftree->paths, vnode, vertex)) {
struct list *lfa_list;
struct list *filtered_lfa_list;
resource)) {
if (IS_DEBUG_LFA)
zlog_debug(
- "ISIS-LFA: route unaffected by %s",
+ "ISIS-LFA: %s %s unaffected by %s",
+ vtype2string(vertex->type), buf,
lfa_protected_resource2str(resource));
continue;
}
if (list_isempty(lfa_list)) {
if (IS_DEBUG_LFA)
- zlog_debug("ISIS-LFA: no valid LFAs found");
+ zlog_debug(
+ "ISIS-LFA: no valid local LFAs found");
list_delete(&lfa_list);
continue;
}
+ SET_FLAG(vertex->flags, F_ISIS_VERTEX_LFA_PROTECTED);
+
/* Check tie-breakers. */
filtered_lfa_list =
- isis_lfa_tiebreakers(area, circuit, spftree, resource,
- vertex, sadj_primary, lfa_list);
+ isis_lfa_tiebreakers(area, spftree, resource, vertex,
+ sadj_primary, lfa_list);
/* Create backup route using the best LFAs. */
allow_ecmp = area->lfa_load_sharing[level - 1];
}
/**
- * Run the LFA/TI-LFA algorithms for all protected interfaces.
+ * Run the LFA/RLFA/TI-LFA algorithms for all protected interfaces.
*
* @param area IS-IS area
* @param spftree IS-IS SPF tree
struct isis_spftree *spftree_reverse = NULL;
struct isis_circuit *circuit;
struct listnode *node;
- bool tilfa_configured;
int level = spftree->level;
- tilfa_configured = (area->tilfa_protected_links[level - 1] > 0);
-
/* Run reverse SPF locally. */
- if (tilfa_configured)
+ if (area->rlfa_protected_links[level - 1] > 0
+ || area->tilfa_protected_links[level - 1] > 0)
spftree_reverse = isis_spf_reverse_run(spftree);
/* Run forward SPF on all adjacent routers. */
continue;
}
- if (circuit->lfa_protection[level - 1])
+ if (circuit->lfa_protection[level - 1]) {
+ /* Run local LFA. */
isis_lfa_compute(area, circuit, spftree, &resource);
- else if (circuit->tilfa_protection[level - 1]) {
+
+ if (circuit->rlfa_protection[level - 1]) {
+ struct isis_spftree *spftree_pc;
+ uint32_t max_metric;
+
+ /* Run remote LFA. */
+ assert(spftree_reverse);
+ max_metric =
+ circuit->rlfa_max_metric[level - 1];
+ spftree_pc = isis_rlfa_compute(
+ area, spftree, spftree_reverse,
+ max_metric, &resource);
+ listnode_add(spftree->lfa.remote.pc_spftrees,
+ spftree_pc);
+ }
+ } else if (circuit->tilfa_protection[level - 1]) {
+ /* Run TI-LFA. */
assert(spftree_reverse);
isis_spf_run_tilfa(area, circuit, spftree,
spftree_reverse, &resource);
}
}
- if (tilfa_configured)
+ if (spftree_reverse)
isis_spftree_del(spftree_reverse);
}
#define _FRR_ISIS_LFA_H
#include "lib/typesafe.h"
+#include "lib/zclient.h"
PREDECL_RBTREE_UNIQ(lfa_tiebreaker_tree)
+PREDECL_RBTREE_UNIQ(rlfa_tree)
enum lfa_tiebreaker_type {
LFA_TIEBREAKER_DOWNSTREAM = 0,
DECLARE_RBTREE_UNIQ(lfa_tiebreaker_tree, struct lfa_tiebreaker, entry,
lfa_tiebreaker_cmp)
+struct rlfa {
+ struct rlfa_tree_item entry;
+ struct prefix prefix;
+ struct isis_vertex *vertex;
+ struct in_addr pq_address;
+};
+int rlfa_cmp(const struct rlfa *a, const struct rlfa *b);
+DECLARE_RBTREE_UNIQ(rlfa_tree, struct rlfa, entry, rlfa_cmp)
+
enum isis_tilfa_sid_type {
TILFA_SID_PREFIX = 1,
TILFA_SID_ADJ,
const uint8_t *id);
struct isis_spftree *isis_spf_reverse_run(const struct isis_spftree *spftree);
int isis_spf_run_neighbors(struct isis_spftree *spftree);
+int isis_rlfa_activate(struct isis_spftree *spftree, struct rlfa *rlfa,
+ struct zapi_rlfa_response *response);
+void isis_rlfa_deactivate(struct isis_spftree *spftree, struct rlfa *rlfa);
+void isis_rlfa_list_init(struct isis_spftree *spftree);
+void isis_rlfa_list_clear(struct isis_spftree *spftree);
+void isis_rlfa_process_ldp_response(struct zapi_rlfa_response *response);
+void isis_ldp_rlfa_handle_client_close(struct zapi_client_close_info *info);
+void isis_rlfa_check(struct isis_spftree *spftree, struct isis_vertex *vertex);
+struct isis_spftree *isis_rlfa_compute(struct isis_area *area,
+ struct isis_spftree *spftree,
+ struct isis_spftree *spftree_reverse,
+ uint32_t max_metric,
+ struct lfa_protected_resource *resource);
void isis_lfa_compute(struct isis_area *area, struct isis_circuit *circuit,
struct isis_spftree *spftree,
struct lfa_protected_resource *resource);
access_list_delete_hook(isis_filter_update);
isis_vrf_init();
prefix_list_init();
+ prefix_list_add_hook(isis_prefix_list_update);
+ prefix_list_delete_hook(isis_prefix_list_update);
isis_init();
isis_circuit_init();
#ifdef FABRICD
DEFINE_MTYPE(ISISD, ISIS_EXT_INFO, "ISIS redistributed route info")
DEFINE_MTYPE(ISISD, ISIS_MPLS_TE, "ISIS MPLS_TE parameters")
DEFINE_MTYPE(ISISD, ISIS_ACL_NAME, "ISIS access-list name")
+DEFINE_MTYPE(ISISD, ISIS_PLIST_NAME, "ISIS prefix-list name")
DECLARE_MTYPE(ISIS_EXT_INFO)
DECLARE_MTYPE(ISIS_MPLS_TE)
DECLARE_MTYPE(ISIS_ACL_NAME)
+DECLARE_MTYPE(ISIS_PLIST_NAME)
#endif /* _QUAGGA_ISIS_MEMORY_H */
.modify = isis_instance_fast_reroute_level_1_lfa_tiebreaker_type_modify,
}
},
+ {
+ .xpath = "/frr-isisd:isis/instance/fast-reroute/level-1/remote-lfa/prefix-list",
+ .cbs = {
+ .cli_show = cli_show_isis_frr_remote_lfa_plist,
+ .modify = isis_instance_fast_reroute_level_1_remote_lfa_prefix_list_modify,
+ .destroy = isis_instance_fast_reroute_level_1_remote_lfa_prefix_list_destroy,
+ }
+ },
{
.xpath = "/frr-isisd:isis/instance/fast-reroute/level-2/lfa/load-sharing",
.cbs = {
.modify = isis_instance_fast_reroute_level_2_lfa_tiebreaker_type_modify,
}
},
+ {
+ .xpath = "/frr-isisd:isis/instance/fast-reroute/level-2/remote-lfa/prefix-list",
+ .cbs = {
+ .cli_show = cli_show_isis_frr_remote_lfa_plist,
+ .modify = isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_modify,
+ .destroy = isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_destroy,
+ }
+ },
{
.xpath = "/frr-isisd:isis/instance/log-adjacency-changes",
.cbs = {
.destroy = lib_interface_isis_fast_reroute_level_1_lfa_exclude_interface_destroy,
}
},
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-1/remote-lfa/enable",
+ .cbs = {
+ .modify = lib_interface_isis_fast_reroute_level_1_remote_lfa_enable_modify,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-1/remote-lfa/maximum-metric",
+ .cbs = {
+ .cli_show = cli_show_frr_remote_lfa_max_metric,
+ .modify = lib_interface_isis_fast_reroute_level_1_remote_lfa_maximum_metric_modify,
+ .destroy = lib_interface_isis_fast_reroute_level_1_remote_lfa_maximum_metric_destroy,
+ }
+ },
{
.xpath = "/frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-1/ti-lfa/enable",
.cbs = {
.destroy = lib_interface_isis_fast_reroute_level_2_lfa_exclude_interface_destroy,
}
},
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-2/remote-lfa/enable",
+ .cbs = {
+ .modify = lib_interface_isis_fast_reroute_level_2_remote_lfa_enable_modify,
+ }
+ },
+ {
+ .xpath = "/frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-2/remote-lfa/maximum-metric",
+ .cbs = {
+ .cli_show = cli_show_frr_remote_lfa_max_metric,
+ .modify = lib_interface_isis_fast_reroute_level_2_remote_lfa_maximum_metric_modify,
+ .destroy = lib_interface_isis_fast_reroute_level_2_remote_lfa_maximum_metric_destroy,
+ }
+ },
{
.xpath = "/frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-2/ti-lfa/enable",
.cbs = {
struct nb_cb_destroy_args *args);
int isis_instance_fast_reroute_level_1_lfa_tiebreaker_type_modify(
struct nb_cb_modify_args *args);
+int isis_instance_fast_reroute_level_1_remote_lfa_prefix_list_modify(
+ struct nb_cb_modify_args *args);
+int isis_instance_fast_reroute_level_1_remote_lfa_prefix_list_destroy(
+ struct nb_cb_destroy_args *args);
int isis_instance_fast_reroute_level_2_lfa_load_sharing_modify(
struct nb_cb_modify_args *args);
int isis_instance_fast_reroute_level_2_lfa_priority_limit_modify(
struct nb_cb_destroy_args *args);
int isis_instance_fast_reroute_level_2_lfa_tiebreaker_type_modify(
struct nb_cb_modify_args *args);
+int isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_modify(
+ struct nb_cb_modify_args *args);
+int isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_destroy(
+ struct nb_cb_destroy_args *args);
int isis_instance_log_adjacency_changes_modify(struct nb_cb_modify_args *args);
int isis_instance_mpls_te_create(struct nb_cb_create_args *args);
int isis_instance_mpls_te_destroy(struct nb_cb_destroy_args *args);
struct nb_cb_create_args *args);
int lib_interface_isis_fast_reroute_level_1_lfa_exclude_interface_destroy(
struct nb_cb_destroy_args *args);
+int lib_interface_isis_fast_reroute_level_1_remote_lfa_enable_modify(
+ struct nb_cb_modify_args *args);
+int lib_interface_isis_fast_reroute_level_1_remote_lfa_maximum_metric_modify(
+ struct nb_cb_modify_args *args);
+int lib_interface_isis_fast_reroute_level_1_remote_lfa_maximum_metric_destroy(
+ struct nb_cb_destroy_args *args);
int lib_interface_isis_fast_reroute_level_1_ti_lfa_enable_modify(
struct nb_cb_modify_args *args);
int lib_interface_isis_fast_reroute_level_1_ti_lfa_node_protection_modify(
struct nb_cb_create_args *args);
int lib_interface_isis_fast_reroute_level_2_lfa_exclude_interface_destroy(
struct nb_cb_destroy_args *args);
+int lib_interface_isis_fast_reroute_level_2_remote_lfa_enable_modify(
+ struct nb_cb_modify_args *args);
+int lib_interface_isis_fast_reroute_level_2_remote_lfa_maximum_metric_modify(
+ struct nb_cb_modify_args *args);
+int lib_interface_isis_fast_reroute_level_2_remote_lfa_maximum_metric_destroy(
+ struct nb_cb_destroy_args *args);
int lib_interface_isis_fast_reroute_level_2_ti_lfa_enable_modify(
struct nb_cb_modify_args *args);
int lib_interface_isis_fast_reroute_level_2_ti_lfa_node_protection_modify(
bool show_defaults);
void cli_show_isis_frr_lfa_load_sharing(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
+void cli_show_isis_frr_remote_lfa_plist(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
void cli_show_ip_isis_passive(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
void cli_show_ip_isis_password(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
void cli_show_frr_lfa_exclude_interface(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
+void cli_show_frr_remote_lfa_max_metric(struct vty *vty, struct lyd_node *dnode,
+ bool show_defaults);
void cli_show_ip_isis_circ_type(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
void cli_show_ip_isis_network_type(struct vty *vty, struct lyd_node *dnode,
#include "log.h"
#include "bfd.h"
#include "filter.h"
+#include "plist.h"
#include "spf_backoff.h"
#include "lib_errors.h"
#include "vrf.h"
return NB_OK;
}
+/*
+ * XPath: /frr-isisd:isis/instance/fast-reroute/level-1/remote-lfa/prefix-list
+ */
+int isis_instance_fast_reroute_level_1_remote_lfa_prefix_list_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct isis_area *area;
+ const char *plist_name;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+ plist_name = yang_dnode_get_string(args->dnode, NULL);
+
+ area->rlfa_plist_name[0] = XSTRDUP(MTYPE_ISIS_PLIST_NAME, plist_name);
+ area->rlfa_plist[0] = prefix_list_lookup(AFI_IP, plist_name);
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+int isis_instance_fast_reroute_level_1_remote_lfa_prefix_list_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct isis_area *area;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+
+ XFREE(MTYPE_ISIS_PLIST_NAME, area->rlfa_plist_name[0]);
+ area->rlfa_plist[0] = NULL;
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
/*
* XPath: /frr-isisd:isis/instance/fast-reroute/level-2/lfa/load-sharing
*/
return NB_OK;
}
+/*
+ * XPath: /frr-isisd:isis/instance/fast-reroute/level-2/remote-lfa/prefix-list
+ */
+int isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct isis_area *area;
+ const char *plist_name;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+ plist_name = yang_dnode_get_string(args->dnode, NULL);
+
+ area->rlfa_plist_name[1] = XSTRDUP(MTYPE_ISIS_PLIST_NAME, plist_name);
+ area->rlfa_plist[1] = prefix_list_lookup(AFI_IP, plist_name);
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+int isis_instance_fast_reroute_level_2_remote_lfa_prefix_list_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct isis_area *area;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ area = nb_running_get_entry(args->dnode, NULL, true);
+
+ XFREE(MTYPE_ISIS_PLIST_NAME, area->rlfa_plist_name[1]);
+ area->rlfa_plist[1] = NULL;
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
/*
* XPath: /frr-isisd:isis/instance/log-adjacency-changes
*/
return NB_OK;
}
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-1/remote-lfa/enable
+ */
+int lib_interface_isis_fast_reroute_level_1_remote_lfa_enable_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct isis_area *area;
+ struct isis_circuit *circuit;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(args->dnode, NULL, true);
+ circuit->rlfa_protection[0] = yang_dnode_get_bool(args->dnode, NULL);
+ if (circuit->rlfa_protection[0])
+ circuit->area->rlfa_protected_links[0]++;
+ else {
+ assert(circuit->area->rlfa_protected_links[0] > 0);
+ circuit->area->rlfa_protected_links[0]--;
+ }
+
+ area = circuit->area;
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-1/remote-lfa/maximum-metric
+ */
+int lib_interface_isis_fast_reroute_level_1_remote_lfa_maximum_metric_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct isis_area *area;
+ struct isis_circuit *circuit;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(args->dnode, NULL, true);
+ circuit->rlfa_max_metric[0] = yang_dnode_get_uint32(args->dnode, NULL);
+
+ area = circuit->area;
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+int lib_interface_isis_fast_reroute_level_1_remote_lfa_maximum_metric_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct isis_area *area;
+ struct isis_circuit *circuit;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(args->dnode, NULL, true);
+ circuit->rlfa_max_metric[0] = 0;
+
+ area = circuit->area;
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
/*
* XPath:
* /frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-1/ti-lfa/enable
return NB_OK;
}
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-2/remote-lfa/enable
+ */
+int lib_interface_isis_fast_reroute_level_2_remote_lfa_enable_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct isis_area *area;
+ struct isis_circuit *circuit;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(args->dnode, NULL, true);
+ circuit->rlfa_protection[1] = yang_dnode_get_bool(args->dnode, NULL);
+ if (circuit->rlfa_protection[1])
+ circuit->area->rlfa_protected_links[1]++;
+ else {
+ assert(circuit->area->rlfa_protected_links[1] > 0);
+ circuit->area->rlfa_protected_links[1]--;
+ }
+
+ area = circuit->area;
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-2/remote-lfa/maximum-metric
+ */
+int lib_interface_isis_fast_reroute_level_2_remote_lfa_maximum_metric_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct isis_area *area;
+ struct isis_circuit *circuit;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(args->dnode, NULL, true);
+ circuit->rlfa_max_metric[1] = yang_dnode_get_uint32(args->dnode, NULL);
+
+ area = circuit->area;
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
+int lib_interface_isis_fast_reroute_level_2_remote_lfa_maximum_metric_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ struct isis_area *area;
+ struct isis_circuit *circuit;
+
+ if (args->event != NB_EV_APPLY)
+ return NB_OK;
+
+ circuit = nb_running_get_entry(args->dnode, NULL, true);
+ circuit->rlfa_max_metric[1] = 0;
+
+ area = circuit->area;
+ lsp_regenerate_schedule(area, area->is_type, 0);
+
+ return NB_OK;
+}
+
/*
* XPath:
* /frr-interface:lib/interface/frr-isisd:isis/fast-reroute/level-2/ti-lfa/enable
return true;
}
+static bool isis_label_stack_same(struct mpls_label_stack *new,
+ struct mpls_label_stack *old)
+{
+ if (!new && !old)
+ return true;
+ if (!new || !old)
+ return false;
+ if (new->num_labels != old->num_labels)
+ return false;
+ if (memcmp(&new->label, &old->label,
+ sizeof(mpls_label_t) * new->num_labels))
+ return false;
+
+ return true;
+}
+
static int isis_route_info_same(struct isis_route_info *new,
struct isis_route_info *old, char *buf,
size_t buf_size)
snprintf(buf, buf_size, "nhop SR label");
return 0;
}
+ if (!isis_label_stack_same(new_nh->label_stack,
+ old_nh->label_stack)) {
+ if (buf)
+ snprintf(buf, buf_size, "nhop label stack");
+ return 0;
+ }
}
/* only the resync flag needs to be checked */
return route_info;
}
-static void isis_route_delete(struct isis_area *area, struct route_node *rode,
- struct route_table *table)
+void isis_route_delete(struct isis_area *area, struct route_node *rode,
+ struct route_table *table)
{
struct isis_route_info *rinfo;
char buff[SRCDEST2STR_BUFFER];
SET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
UNSET_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC);
} else {
- if (!CHECK_FLAG(route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED))
- return;
-
/* Uninstall Prefix-SID label. */
if (route_info->sr.present)
isis_zebra_prefix_sid_uninstall(
rinfo->backup = rnode_bck->info;
UNSET_FLAG(rinfo->flag,
ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
+ } else if (rinfo->backup) {
+ rinfo->backup = NULL;
+ UNSET_FLAG(rinfo->flag,
+ ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
}
}
rinfo->backup = rnode_bck->info;
UNSET_FLAG(rinfo->flag,
ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
+ } else if (rinfo->backup) {
+ rinfo->backup = NULL;
+ UNSET_FLAG(rinfo->flag,
+ ISIS_ROUTE_FLAG_ZEBRA_SYNCED);
}
mrnode = srcdest_rnode_get(merge, prefix, src_p);
uint32_t cost, uint32_t depth, struct isis_sr_psid_info *sr,
struct list *adjacencies, bool allow_ecmp,
struct isis_area *area, struct route_table *table);
+void isis_route_delete(struct isis_area *area, struct route_node *rode,
+ struct route_table *table);
/* Walk the given table and install new routes to zebra and remove old ones.
* route status is tracked using ISIS_ROUTE_FLAG_ACTIVE */
#include "isis_csm.h"
#include "isis_mt.h"
#include "isis_tlvs.h"
+#include "isis_zebra.h"
#include "fabricd.h"
#include "isis_spf_private.h"
tree->tree_id = tree_id;
tree->family = (tree->tree_id == SPFTREE_IPV4) ? AF_INET : AF_INET6;
tree->flags = flags;
- if (tree->type == SPF_TYPE_TI_LFA) {
+ isis_rlfa_list_init(tree);
+ tree->lfa.remote.pc_spftrees = list_new();
+ tree->lfa.remote.pc_spftrees->del = (void (*)(void *))isis_spftree_del;
+ if (tree->type == SPF_TYPE_RLFA || tree->type == SPF_TYPE_TI_LFA) {
isis_spf_node_list_init(&tree->lfa.p_space);
isis_spf_node_list_init(&tree->lfa.q_space);
}
{
hash_clean(spftree->prefix_sids, NULL);
hash_free(spftree->prefix_sids);
- if (spftree->type == SPF_TYPE_TI_LFA) {
+ isis_zebra_rlfa_unregister_all(spftree);
+ isis_rlfa_list_clear(spftree);
+ list_delete(&spftree->lfa.remote.pc_spftrees);
+ if (spftree->type == SPF_TYPE_RLFA
+ || spftree->type == SPF_TYPE_TI_LFA) {
isis_spf_node_list_clear(&spftree->lfa.q_space);
isis_spf_node_list_clear(&spftree->lfa.p_space);
}
#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;
}
}
- 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};
}
}
+ /* 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)
}
}
+end:
if (fragnode == NULL)
fragnode = listhead(lsp->lspu.frags);
else
list_delete_all_node(spftree->sadj_list);
isis_vertex_queue_clear(&spftree->tents);
isis_vertex_queue_clear(&spftree->paths);
+ isis_zebra_rlfa_unregister_all(spftree);
+ isis_rlfa_list_clear(spftree);
+ list_delete_all_node(spftree->lfa.remote.pc_spftrees);
memset(&spftree->lfa.protection_counters, 0,
sizeof(spftree->lfa.protection_counters));
priority = spf_prefix_priority(spftree, vertex);
vertex->N.ip.priority = priority;
if (vertex->depth == 1 || listcount(vertex->Adj_N) > 0) {
+ struct isis_spftree *pre_spftree;
struct route_table *route_table;
bool allow_ecmp;
- if (spftree->type == SPF_TYPE_TI_LFA) {
- struct isis_spftree *pre_spftree;
-
+ switch (spftree->type) {
+ case SPF_TYPE_RLFA:
+ case SPF_TYPE_TI_LFA:
if (priority
> area->lfa_priority_limit[level - 1]) {
if (IS_DEBUG_LFA)
sizeof(buff)));
return;
}
+ break;
+ default:
+ break;
+ }
+ switch (spftree->type) {
+ case SPF_TYPE_RLFA:
+ isis_rlfa_check(spftree, vertex);
+ return;
+ case SPF_TYPE_TI_LFA:
if (isis_tilfa_check(spftree, vertex) != 0)
return;
allow_ecmp = area->lfa_load_sharing[level - 1];
pre_spftree->lfa.protection_counters
.tilfa[vertex->N.ip.priority] += 1;
- } else {
+ break;
+ default:
route_table = spftree->route_table;
allow_ecmp = true;
spftree->lfa.protection_counters
.ecmp[priority] += 1;
}
+ break;
}
isis_route_create(
area->area_tag, level, diff, func, file, line);
}
+ thread_cancel(&area->t_rlfa_rib_update);
if (area->spf_delay_ietf[level - 1]) {
/* Need to call schedule function also if spf delay is running
* to
enum spf_type {
SPF_TYPE_FORWARD = 1,
SPF_TYPE_REVERSE,
+ SPF_TYPE_RLFA,
SPF_TYPE_TI_LFA,
};
struct list *parents; /* list of parents for ECMP */
struct hash *firsthops; /* first two hops to neighbor */
uint64_t insert_counter;
+ uint8_t flags;
};
+#define F_ISIS_VERTEX_LFA_PROTECTED 0x01
/* Vertex Queue and associated functions */
struct isis_spf_nodes p_space;
struct isis_spf_nodes q_space;
+ /* Remote LFA related information. */
+ struct {
+ /* List of RLFAs eligible to be installed. */
+ struct rlfa_tree_head rlfas;
+
+ /*
+ * RLFA post-convergence SPTs (needed to activate RLFAs
+ * once label information is received from LDP).
+ */
+ struct list *pc_spftrees;
+
+ /* RLFA maximum metric (or zero if absent). */
+ uint32_t max_metric;
+ } remote;
+
/* Protection counters. */
struct {
uint32_t lfa[SPF_PREFIX_PRIO_MAX];
#include "isisd/isis_circuit.h"
#include "isisd/isis_csm.h"
#include "isisd/isis_lsp.h"
+#include "isisd/isis_spf.h"
+#include "isisd/isis_spf_private.h"
#include "isisd/isis_route.h"
#include "isisd/isis_zebra.h"
#include "isisd/isis_adjacency.h"
type, 0, VRF_DEFAULT);
}
+/**
+ * Register RLFA with LDP.
+ */
+int isis_zebra_rlfa_register(struct isis_spftree *spftree, struct rlfa *rlfa)
+{
+ struct isis_area *area = spftree->area;
+ struct zapi_rlfa_request zr = {};
+ int ret;
+
+ if (!zclient)
+ return 0;
+
+ zr.igp.vrf_id = area->isis->vrf_id;
+ zr.igp.protocol = ZEBRA_ROUTE_ISIS;
+ strlcpy(zr.igp.isis.area_tag, area->area_tag,
+ sizeof(zr.igp.isis.area_tag));
+ zr.igp.isis.spf.tree_id = spftree->tree_id;
+ zr.igp.isis.spf.level = spftree->level;
+ zr.igp.isis.spf.run_id = spftree->runcount;
+ zr.destination = rlfa->prefix;
+ zr.pq_address = rlfa->pq_address;
+
+ zlog_debug("ISIS-LFA: registering RLFA %pFX@%pI4 with LDP",
+ &rlfa->prefix, &rlfa->pq_address);
+
+ ret = zclient_send_opaque_unicast(zclient, LDP_RLFA_REGISTER,
+ ZEBRA_ROUTE_LDP, 0, 0,
+ (const uint8_t *)&zr, sizeof(zr));
+ if (ret == ZCLIENT_SEND_FAILURE) {
+ zlog_warn("ISIS-LFA: failed to register RLFA with LDP");
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Unregister all RLFAs from the given SPF tree with LDP.
+ */
+void isis_zebra_rlfa_unregister_all(struct isis_spftree *spftree)
+{
+ struct isis_area *area = spftree->area;
+ struct zapi_rlfa_igp igp = {};
+ int ret;
+
+ if (!zclient || spftree->type != SPF_TYPE_FORWARD
+ || CHECK_FLAG(spftree->flags, F_SPFTREE_NO_ADJACENCIES))
+ return;
+
+ if (IS_DEBUG_LFA)
+ zlog_debug("ISIS-LFA: unregistering all RLFAs with LDP");
+
+ igp.vrf_id = area->isis->vrf_id;
+ igp.protocol = ZEBRA_ROUTE_ISIS;
+ strlcpy(igp.isis.area_tag, area->area_tag, sizeof(igp.isis.area_tag));
+ igp.isis.spf.tree_id = spftree->tree_id;
+ igp.isis.spf.level = spftree->level;
+ igp.isis.spf.run_id = spftree->runcount;
+
+ ret = zclient_send_opaque_unicast(zclient, LDP_RLFA_UNREGISTER_ALL,
+ ZEBRA_ROUTE_LDP, 0, 0,
+ (const uint8_t *)&igp, sizeof(igp));
+ if (ret == ZCLIENT_SEND_FAILURE)
+ zlog_warn("ISIS-LFA: failed to unregister RLFA with LDP");
+}
+
/* Label Manager Functions */
/**
static void isis_zebra_connected(struct zclient *zclient)
{
zclient_send_reg_requests(zclient, VRF_DEFAULT);
+ zclient_register_opaque(zclient, LDP_RLFA_LABELS);
}
/*
struct zapi_opaque_msg info;
struct ldp_igp_sync_if_state state;
struct ldp_igp_sync_announce announce;
+ struct zapi_rlfa_response rlfa;
int ret = 0;
s = zclient->ibuf;
STREAM_GET(&announce, s, sizeof(announce));
ret = isis_ldp_sync_announce_update(announce);
break;
+ case LDP_RLFA_LABELS:
+ STREAM_GET(&rlfa, s, sizeof(rlfa));
+ isis_rlfa_process_ldp_response(&rlfa);
+ break;
default:
break;
}
return -1;
isis_ldp_sync_handle_client_close(&info);
+ isis_ldp_rlfa_handle_client_close(&info);
return ret;
}
void isis_zebra_stop(void)
{
+ zclient_unregister_opaque(zclient, LDP_RLFA_LABELS);
zclient_stop(zclient_sync);
zclient_free(zclient_sync);
zclient_stop(zclient);
int isis_distribute_list_update(int routetype);
void isis_zebra_redistribute_set(afi_t afi, int type);
void isis_zebra_redistribute_unset(afi_t afi, int type);
+int isis_zebra_rlfa_register(struct isis_spftree *spftree, struct rlfa *rlfa);
+void isis_zebra_rlfa_unregister_all(struct isis_spftree *spftree);
bool isis_zebra_label_manager_ready(void);
int isis_zebra_label_manager_connect(void);
int isis_zebra_request_label_range(uint32_t base, uint32_t chunk_size);
#include "if.h"
#include "hash.h"
#include "filter.h"
+#include "plist.h"
#include "stream.h"
#include "prefix.h"
#include "table.h"
thread_cancel(&area->t_tick);
thread_cancel(&area->t_lsp_refresh[0]);
thread_cancel(&area->t_lsp_refresh[1]);
+ thread_cancel(&area->t_rlfa_rib_update);
thread_cancel_event(master, area);
}
}
+void isis_prefix_list_update(struct prefix_list *plist)
+{
+ struct isis *isis;
+ struct isis_area *area;
+ struct listnode *node, *anode;
+
+ for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) {
+ for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) {
+ for (int level = ISIS_LEVEL1; level <= ISIS_LEVELS;
+ level++) {
+ const char *plist_name =
+ prefix_list_name(plist);
+
+ if (!area->rlfa_plist_name[level - 1])
+ continue;
+
+ if (!strmatch(area->rlfa_plist_name[level - 1],
+ plist_name))
+ continue;
+
+ area->rlfa_plist[level - 1] =
+ prefix_list_lookup(AFI_IP, plist_name);
+ lsp_regenerate_schedule(area, area->is_type, 0);
+ }
+ }
+ }
+}
+
#ifdef FABRICD
static void area_set_mt_enabled(struct isis_area *area, uint16_t mtid,
bool enabled)
struct thread *t_tick; /* LSP walker */
struct thread *t_lsp_refresh[ISIS_LEVELS];
struct timeval last_lsp_refresh_event[ISIS_LEVELS];
+ struct thread *t_rlfa_rib_update;
/* t_lsp_refresh is used in two ways:
* a) regular refresh of LSPs
* b) (possibly throttled) updates to LSPs
size_t lfa_load_sharing[ISIS_LEVELS];
enum spf_prefix_priority lfa_priority_limit[ISIS_LEVELS];
struct lfa_tiebreaker_tree_head lfa_tiebreakers[ISIS_LEVELS];
+ char *rlfa_plist_name[ISIS_LEVELS];
+ struct prefix_list *rlfa_plist[ISIS_LEVELS];
+ size_t rlfa_protected_links[ISIS_LEVELS];
size_t tilfa_protected_links[ISIS_LEVELS];
/* Counters */
uint32_t circuit_state_changes;
int isis_area_get(struct vty *vty, const char *area_tag);
void isis_area_destroy(struct isis_area *area);
void isis_filter_update(struct access_list *access);
+void isis_prefix_list_update(struct prefix_list *plist);
void print_debug(struct vty *, int, int);
struct isis_lsp *lsp_for_arg(struct lspdb_head *head, const char *argv,
struct isis *isis);
if (adj->source.type == HELLO_TARGETED) {
if (!(adj->source.target->flags & F_TNBR_CONFIGURED) &&
- adj->source.target->pw_count == 0) {
+ adj->source.target->pw_count == 0 &&
+ adj->source.target->rlfa_count == 0) {
/* remove dynamic targeted neighbor */
tnbr_del(leconf, adj->source.target);
return (0);
tnbr_check(struct ldpd_conf *xconf, struct tnbr *tnbr)
{
if (!(tnbr->flags & (F_TNBR_CONFIGURED|F_TNBR_DYNAMIC)) &&
- tnbr->pw_count == 0) {
+ tnbr->pw_count == 0 && tnbr->rlfa_count == 0) {
tnbr_del(xconf, tnbr);
return (NULL);
}
af = tnbr->af;
holdtime = tnbr_get_hello_holdtime(tnbr);
flags = F_HELLO_TARGETED;
- if ((tnbr->flags & F_TNBR_CONFIGURED) || tnbr->pw_count)
+ if ((tnbr->flags & F_TNBR_CONFIGURED) || tnbr->pw_count
+ || tnbr->rlfa_count)
flags |= F_HELLO_REQ_TARG;
fd = (ldp_af_global_get(&global, af))->ldp_edisc_socket;
#include "log.h"
#include "lde.h"
#include "ldp_debug.h"
+#include "rlfa.h"
#include <lib/log.h>
#include "memory.h"
int shut = 0;
struct fec fec;
struct ldp_access *laccess;
+ struct ldp_rlfa_node *rnode, *rntmp;
+ struct ldp_rlfa_client *rclient;
+ struct zapi_rlfa_request *rlfa_req;
+ struct zapi_rlfa_igp *rlfa_igp;
iev->ev_read = NULL;
lde_check_filter_af(AF_INET6, &ldeconf->ipv6,
laccess->name);
break;
+ case IMSG_RLFA_REG:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct zapi_rlfa_request)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ rlfa_req = imsg.data;
+ rnode = rlfa_node_find(&rlfa_req->destination,
+ rlfa_req->pq_address);
+ if (!rnode)
+ rnode = rlfa_node_new(&rlfa_req->destination,
+ rlfa_req->pq_address);
+ rclient = rlfa_client_find(rnode, &rlfa_req->igp);
+ if (rclient)
+ /* RLFA already registered - do nothing */
+ break;
+ rclient = rlfa_client_new(rnode, &rlfa_req->igp);
+ lde_rlfa_check(rclient);
+ break;
+ case IMSG_RLFA_UNREG_ALL:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct zapi_rlfa_igp)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ rlfa_igp = imsg.data;
+
+ RB_FOREACH_SAFE (rnode, ldp_rlfa_node_head,
+ &rlfa_node_tree, rntmp) {
+ rclient = rlfa_client_find(rnode, rlfa_igp);
+ if (!rclient)
+ continue;
+
+ rlfa_client_del(rclient);
+ }
+ break;
default:
log_debug("%s: unexpected imsg %d", __func__,
imsg.hdr.type);
}
}
+void
+lde_fec2prefix(const struct fec *fec, struct prefix *prefix)
+{
+ memset(prefix, 0, sizeof(*prefix));
+ switch (fec->type) {
+ case FEC_TYPE_IPV4:
+ prefix->family = AF_INET;
+ prefix->u.prefix4 = fec->u.ipv4.prefix;
+ prefix->prefixlen = fec->u.ipv4.prefixlen;
+ break;
+ case FEC_TYPE_IPV6:
+ prefix->family = AF_INET6;
+ prefix->u.prefix6 = fec->u.ipv6.prefix;
+ prefix->prefixlen = fec->u.ipv6.prefixlen;
+ break;
+ default:
+ prefix->family = AF_UNSPEC;
+ break;
+ }
+}
+
+void
+lde_prefix2fec(const struct prefix *prefix, struct fec *fec)
+{
+ memset(fec, 0, sizeof(*fec));
+ switch (prefix->family) {
+ case AF_INET:
+ fec->type = FEC_TYPE_IPV4;
+ fec->u.ipv4.prefix = prefix->u.prefix4;
+ fec->u.ipv4.prefixlen = prefix->prefixlen;
+ break;
+ case AF_INET6:
+ fec->type = FEC_TYPE_IPV6;
+ fec->u.ipv6.prefix = prefix->u.prefix6;
+ fec->u.ipv6.prefixlen = prefix->prefixlen;
+ break;
+ default:
+ fatalx("lde_prefix2fec: unknown af");
+ break;
+ }
+}
+
void
lde_fec2map(struct fec *fec, struct map *map)
{
RB_FOREACH(f, fec_tree, &ft) {
fn = (struct fec_node *)f;
+ /* Update RLFA clients. */
+ lde_rlfa_update_clients(f, ln, MPLS_INVALID_LABEL);
+
LIST_FOREACH(fnh, &fn->nexthops, entry) {
switch (f->type) {
case FEC_TYPE_IPV4:
uint32_t pw_remote_status;
void *data; /* fec specific data */
+ uint8_t flags;
};
+#define F_FEC_NHS_CHANGED 0x01
#define CHUNK_SIZE 64
struct label_chunk {
void lde_free_label(uint32_t label);
void lde_send_change_klabel(struct fec_node *, struct fec_nh *);
void lde_send_delete_klabel(struct fec_node *, struct fec_nh *);
+void lde_fec2prefix(const struct fec *fec, struct prefix *prefix);
+void lde_prefix2fec(const struct prefix *prefix, struct fec *fec);
void lde_fec2map(struct fec *, struct map *);
void lde_map2fec(struct map *, struct in_addr, struct fec *);
void lde_send_labelmapping(struct lde_nbr *, struct fec_node *,
#include "ldpe.h"
#include "lde.h"
#include "log.h"
+#include "rlfa.h"
#include "mpls.h"
fnh = fec_nh_find(fn, af, nexthop, ifindex, route_type, route_instance);
if (fnh == NULL) {
+ fn->flags |= F_FEC_NHS_CHANGED;
+
fnh = fec_nh_add(fn, af, nexthop, ifindex, route_type,
route_instance);
/*
} else
fnh->flags |= F_FEC_NH_NO_LDP;
} else {
+ fn->flags |= F_FEC_NHS_CHANGED;
lde_send_delete_klabel(fn, fnh);
fec_nh_del(fnh);
}
}
+ if (!(fn->flags & F_FEC_NHS_CHANGED))
+ /* return earlier if nothing has changed */
+ return;
+ fn->flags &= ~F_FEC_NHS_CHANGED;
+
if (LIST_EMPTY(&fn->nexthops)) {
RB_FOREACH(ln, nbr_tree, &lde_nbrs)
lde_send_labelwithdraw(ln, fn, NULL, NULL);
break;
}
}
+
+ /* Update RLFA clients. */
+ lde_rlfa_update_clients(&fec, ln, map->label);
+
/* LMp.13 & LMp.16: Record the mapping from this peer */
if (me == NULL)
me = lde_map_add(ln, fn, 0);
fnh->remote_label = NO_LABEL;
}
+ /* Update RLFA clients. */
+ lde_rlfa_update_clients(&fec, ln, MPLS_INVALID_LABEL);
+
/* LWd.2: send label release */
lde_send_labelrelease(ln, fn, NULL, map->label);
fnh->remote_label = NO_LABEL;
}
+ /* Update RLFA clients. */
+ lde_rlfa_update_clients(f, ln, MPLS_INVALID_LABEL);
+
/* LWd.3: check previously received label mapping */
if (me && (map->label == NO_LABEL ||
map->label == me->map.label))
ldp_zebra_opaque_register(void)
{
zclient_register_opaque(zclient, LDP_IGP_SYNC_IF_STATE_REQUEST);
+ zclient_register_opaque(zclient, LDP_RLFA_REGISTER);
+ zclient_register_opaque(zclient, LDP_RLFA_UNREGISTER_ALL);
}
static void
ldp_zebra_opaque_unregister(void)
{
zclient_unregister_opaque(zclient, LDP_IGP_SYNC_IF_STATE_REQUEST);
+ zclient_unregister_opaque(zclient, LDP_RLFA_REGISTER);
+ zclient_unregister_opaque(zclient, LDP_RLFA_UNREGISTER_ALL);
}
int
return 0;
}
+int ldp_zebra_send_rlfa_labels(struct zapi_rlfa_response *rlfa_labels)
+{
+ int ret;
+
+ ret = zclient_send_opaque(zclient, LDP_RLFA_LABELS,
+ (const uint8_t *)rlfa_labels,
+ sizeof(*rlfa_labels));
+ if (ret == ZCLIENT_SEND_FAILURE) {
+ log_warn("failed to send RLFA labels to IGP");
+ return -1;
+ }
+
+ return 0;
+}
+
static int
ldp_zebra_opaque_msg_handler(ZAPI_CALLBACK_ARGS)
{
struct stream *s;
struct zapi_opaque_msg info;
struct ldp_igp_sync_if_state_req state_req;
+ struct zapi_rlfa_igp igp;
+ struct zapi_rlfa_request rlfa;
s = zclient->ibuf;
main_imsg_compose_ldpe(IMSG_LDP_SYNC_IF_STATE_REQUEST, 0, &state_req,
sizeof(state_req));
break;
+ case LDP_RLFA_REGISTER:
+ STREAM_GET(&rlfa, s, sizeof(rlfa));
+ main_imsg_compose_both(IMSG_RLFA_REG, &rlfa, sizeof(rlfa));
+ break;
+ case LDP_RLFA_UNREGISTER_ALL:
+ STREAM_GET(&igp, s, sizeof(igp));
+ main_imsg_compose_both(IMSG_RLFA_UNREG_ALL, &igp, sizeof(igp));
+ break;
default:
break;
}
struct imsg imsg;
ssize_t n;
int shut = 0;
+ struct zapi_rlfa_response *rlfa_labels;
iev->ev_read = NULL;
fatalx("IMSG_ACL_CHECK imsg with wrong len");
ldp_acl_reply(iev, (struct acl_check *)imsg.data);
break;
+ case IMSG_RLFA_LABELS:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct zapi_rlfa_response)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ rlfa_labels = imsg.data;
+ ldp_zebra_send_rlfa_labels(rlfa_labels);
+ break;
default:
log_debug("%s: error handling imsg %d", __func__,
imsg.hdr.type);
IMSG_FILTER_UPDATE,
IMSG_NBR_SHUTDOWN,
IMSG_LDP_SYNC_IF_STATE_REQUEST,
- IMSG_LDP_SYNC_IF_STATE_UPDATE
+ IMSG_LDP_SYNC_IF_STATE_UPDATE,
+ IMSG_RLFA_REG,
+ IMSG_RLFA_UNREG_ALL,
+ IMSG_RLFA_LABELS,
};
struct ldpd_init {
union ldpd_addr addr;
int state;
uint16_t pw_count;
+ uint32_t rlfa_count;
uint8_t flags;
QOBJ_FIELDS
};
void ldp_zebra_init(struct thread_master *);
void ldp_zebra_destroy(void);
int ldp_sync_zebra_send_state_update(struct ldp_igp_sync_if_state *);
+int ldp_zebra_send_rlfa_labels(struct zapi_rlfa_response *
+ rlfa_labels);
/* compatibility */
#ifndef __OpenBSD__
#include "control.h"
#include "log.h"
#include "ldp_debug.h"
+#include "rlfa.h"
#include <lib/log.h>
#include "memory.h"
int n, shut = 0;
struct ldp_access *laccess;
struct ldp_igp_sync_if_state_req *ldp_sync_if_state_req;
-
+ struct ldp_rlfa_node *rnode, *rntmp;
+ struct ldp_rlfa_client *rclient;
+ struct zapi_rlfa_request *rlfa_req;
+ struct zapi_rlfa_igp *rlfa_igp;
+
iev->ev_read = NULL;
if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
ldp_sync_if_state_req = imsg.data;
ldp_sync_fsm_state_req(ldp_sync_if_state_req);
break;
+ case IMSG_RLFA_REG:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct zapi_rlfa_request)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ rlfa_req = imsg.data;
+
+ rnode = rlfa_node_find(&rlfa_req->destination,
+ rlfa_req->pq_address);
+ if (!rnode)
+ rnode = rlfa_node_new(&rlfa_req->destination,
+ rlfa_req->pq_address);
+ rclient = rlfa_client_find(rnode, &rlfa_req->igp);
+ if (rclient)
+ /* RLFA already registered - do nothing */
+ break;
+ rclient = rlfa_client_new(rnode, &rlfa_req->igp);
+ ldpe_rlfa_init(rclient);
+ break;
+ case IMSG_RLFA_UNREG_ALL:
+ if (imsg.hdr.len != IMSG_HEADER_SIZE +
+ sizeof(struct zapi_rlfa_igp)) {
+ log_warnx("%s: wrong imsg len", __func__);
+ break;
+ }
+ rlfa_igp = imsg.data;
+
+ RB_FOREACH_SAFE (rnode, ldp_rlfa_node_head,
+ &rlfa_node_tree, rntmp) {
+ rclient = rlfa_client_find(rnode, rlfa_igp);
+ if (!rclient)
+ continue;
+
+ ldpe_rlfa_exit(rclient);
+ rlfa_client_del(rclient);
+ }
+ break;
default:
log_debug("ldpe_dispatch_main: error handling imsg %d",
imsg.hdr.type);
--- /dev/null
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ * Renato Westphal
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "ldpd.h"
+#include "lde.h"
+#include "ldpe.h"
+#include "log.h"
+#include "ldp_debug.h"
+#include "rlfa.h"
+
+#include <lib/log.h>
+
+struct ldp_rlfa_node_head rlfa_node_tree;
+
+static int ldp_rlfa_client_compare(const struct ldp_rlfa_client *a,
+ const struct ldp_rlfa_client *b)
+{
+ if (a->igp.vrf_id < b->igp.vrf_id)
+ return -1;
+ if (a->igp.vrf_id > b->igp.vrf_id)
+ return 1;
+
+ if (a->igp.protocol < b->igp.protocol)
+ return -1;
+ if (a->igp.protocol > b->igp.protocol)
+ return 1;
+
+ if (a->igp.isis.spf.tree_id < b->igp.isis.spf.tree_id)
+ return -1;
+ if (a->igp.isis.spf.tree_id > b->igp.isis.spf.tree_id)
+ return 1;
+
+ if (a->igp.isis.spf.level < b->igp.isis.spf.level)
+ return -1;
+ if (a->igp.isis.spf.level > b->igp.isis.spf.level)
+ return 1;
+
+ return 0;
+}
+RB_GENERATE(ldp_rlfa_client_head, ldp_rlfa_client, entry,
+ ldp_rlfa_client_compare)
+
+static int ldp_rlfa_node_compare(const struct ldp_rlfa_node *a,
+ const struct ldp_rlfa_node *b)
+{
+ if (ntohl(a->pq_address.s_addr) < ntohl(b->pq_address.s_addr))
+ return -1;
+ if (ntohl(a->pq_address.s_addr) > ntohl(b->pq_address.s_addr))
+ return 1;
+
+ return prefix_cmp(&a->destination, &b->destination);
+}
+RB_GENERATE(ldp_rlfa_node_head, ldp_rlfa_node, entry, ldp_rlfa_node_compare)
+
+struct ldp_rlfa_client *rlfa_client_new(struct ldp_rlfa_node *rnode,
+ struct zapi_rlfa_igp *igp)
+{
+ struct ldp_rlfa_client *rclient;
+
+ if ((rclient = calloc(1, sizeof(*rclient))) == NULL)
+ fatal(__func__);
+
+ rclient->igp = *igp;
+ rclient->node = rnode;
+ RB_INSERT(ldp_rlfa_client_head, &rnode->clients, rclient);
+
+ return rclient;
+}
+
+void rlfa_client_del(struct ldp_rlfa_client *rclient)
+{
+ struct ldp_rlfa_node *rnode = rclient->node;
+
+ RB_REMOVE(ldp_rlfa_client_head, &rnode->clients, rclient);
+ free(rclient);
+
+ /* Delete RLFA node if it's empty. */
+ if (RB_EMPTY(ldp_rlfa_client_head, &rnode->clients))
+ rlfa_node_del(rnode);
+}
+
+struct ldp_rlfa_client *rlfa_client_find(struct ldp_rlfa_node *rnode,
+ struct zapi_rlfa_igp *igp)
+{
+ struct ldp_rlfa_client rclient;
+
+ rclient.igp = *igp;
+ return RB_FIND(ldp_rlfa_client_head, &rnode->clients, &rclient);
+}
+
+struct ldp_rlfa_node *rlfa_node_new(const struct prefix *destination,
+ struct in_addr pq_address)
+{
+ struct ldp_rlfa_node *rnode;
+
+ if ((rnode = calloc(1, sizeof(*rnode))) == NULL)
+ fatal(__func__);
+
+ rnode->destination = *destination;
+ rnode->pq_address = pq_address;
+ rnode->pq_label = MPLS_INVALID_LABEL;
+ RB_INIT(ldp_rlfa_client_head, &rnode->clients);
+ RB_INSERT(ldp_rlfa_node_head, &rlfa_node_tree, rnode);
+
+ return rnode;
+}
+
+void rlfa_node_del(struct ldp_rlfa_node *rnode)
+{
+ /* Delete RLFA clients. */
+ while (!RB_EMPTY(ldp_rlfa_client_head, &rnode->clients)) {
+ struct ldp_rlfa_client *rclient;
+
+ rclient = RB_ROOT(ldp_rlfa_client_head, &rnode->clients);
+ rlfa_client_del(rclient);
+ }
+
+ RB_REMOVE(ldp_rlfa_node_head, &rlfa_node_tree, rnode);
+ free(rnode);
+}
+
+struct ldp_rlfa_node *rlfa_node_find(const struct prefix *destination,
+ struct in_addr pq_address)
+{
+ struct ldp_rlfa_node rnode = {};
+
+ rnode.destination = *destination;
+ rnode.pq_address = pq_address;
+ return RB_FIND(ldp_rlfa_node_head, &rlfa_node_tree, &rnode);
+}
+
+void lde_rlfa_client_send(struct ldp_rlfa_client *rclient)
+{
+ struct ldp_rlfa_node *rnode = rclient->node;
+ struct zapi_rlfa_response rlfa_labels = {};
+ struct fec fec;
+ struct fec_node *fn;
+ struct fec_nh *fnh;
+ int i = 0;
+
+ /* Fill in inner label (allocated by PQ node). */
+ rlfa_labels.igp = rclient->igp;
+ rlfa_labels.destination = rnode->destination;
+ rlfa_labels.pq_label = rnode->pq_label;
+
+ /* Fill in outer label(s) (allocated by the nexthop routers). */
+ fec.type = FEC_TYPE_IPV4;
+ fec.u.ipv4.prefix = rnode->pq_address;
+ fec.u.ipv4.prefixlen = IPV4_MAX_BITLEN;
+ fn = (struct fec_node *)fec_find(&ft, &fec);
+ if (!fn)
+ return;
+ LIST_FOREACH(fnh, &fn->nexthops, entry) {
+ if (fnh->remote_label == NO_LABEL)
+ continue;
+
+ rlfa_labels.nexthops[i].family = fnh->af;
+ switch (fnh->af) {
+ case AF_INET:
+ rlfa_labels.nexthops[i].gate.ipv4 = fnh->nexthop.v4;
+ break;
+ case AF_INET6:
+ rlfa_labels.nexthops[i].gate.ipv6 = fnh->nexthop.v6;
+ break;
+ default:
+ continue;
+ }
+ rlfa_labels.nexthops[i].label = fnh->remote_label;
+ i++;
+ }
+ rlfa_labels.nexthop_num = i;
+
+ lde_imsg_compose_parent(IMSG_RLFA_LABELS, 0, &rlfa_labels,
+ sizeof(rlfa_labels));
+}
+
+void lde_rlfa_label_update(const struct fec *fec)
+{
+ struct ldp_rlfa_node *rnode;
+
+ if (fec->type != FEC_TYPE_IPV4
+ || fec->u.ipv4.prefixlen != IPV4_MAX_BITLEN)
+ return;
+
+ /*
+ * TODO: use an rb-tree lookup to restrict the iteration to the RLFAs
+ * that were effectivelly affected by the label update.
+ */
+ RB_FOREACH (rnode, ldp_rlfa_node_head, &rlfa_node_tree) {
+ struct ldp_rlfa_client *rclient;
+
+ if (!IPV4_ADDR_SAME(&rnode->pq_address, &fec->u.ipv4.prefix))
+ continue;
+
+ RB_FOREACH (rclient, ldp_rlfa_client_head, &rnode->clients)
+ lde_rlfa_client_send(rclient);
+ }
+}
+
+void lde_rlfa_check(struct ldp_rlfa_client *rclient)
+{
+ struct lde_nbr *ln;
+ struct lde_map *me;
+ struct fec fec;
+ union ldpd_addr pq_address = {};
+
+ pq_address.v4 = rclient->node->pq_address;
+ ln = lde_nbr_find_by_addr(AF_INET, &pq_address);
+ if (!ln)
+ return;
+
+ lde_prefix2fec(&rclient->node->destination, &fec);
+ me = (struct lde_map *)fec_find(&ln->recv_map, &fec);
+ if (!me)
+ return;
+
+ rclient->node->pq_label = me->map.label;
+ lde_rlfa_client_send(rclient);
+}
+
+/*
+ * Check if there's any registered RLFA client for this prefix/neighbor (PQ
+ * node) and notify about the updated label.
+ */
+void lde_rlfa_update_clients(struct fec *fec, struct lde_nbr *ln,
+ uint32_t label)
+{
+ struct prefix rlfa_dest;
+ struct ldp_rlfa_node *rnode;
+
+ lde_fec2prefix(fec, &rlfa_dest);
+ rnode = rlfa_node_find(&rlfa_dest, ln->id);
+ if (rnode) {
+ struct ldp_rlfa_client *rclient;
+
+ rnode->pq_label = label;
+ RB_FOREACH (rclient, ldp_rlfa_client_head, &rnode->clients)
+ lde_rlfa_client_send(rclient);
+ } else
+ lde_rlfa_label_update(fec);
+}
+
+void ldpe_rlfa_init(struct ldp_rlfa_client *rclient)
+{
+ struct tnbr *tnbr;
+ union ldpd_addr pq_address = {};
+
+ pq_address.v4 = rclient->node->pq_address;
+ tnbr = tnbr_find(leconf, AF_INET, &pq_address);
+ if (tnbr == NULL) {
+ tnbr = tnbr_new(AF_INET, &pq_address);
+ tnbr_update(tnbr);
+ RB_INSERT(tnbr_head, &leconf->tnbr_tree, tnbr);
+ }
+
+ tnbr->rlfa_count++;
+}
+
+void ldpe_rlfa_exit(struct ldp_rlfa_client *rclient)
+{
+ struct tnbr *tnbr;
+ union ldpd_addr pq_address = {};
+
+ pq_address.v4 = rclient->node->pq_address;
+ tnbr = tnbr_find(leconf, AF_INET, &pq_address);
+ if (tnbr) {
+ tnbr->rlfa_count--;
+ tnbr_check(leconf, tnbr);
+ }
+}
--- /dev/null
+/*
+ * Copyright (C) 2020 NetDEF, Inc.
+ * Renato Westphal
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _LDPD_RLFA_H_
+#define _LDPD_RLFA_H_
+
+#include "openbsd-tree.h"
+#include "zclient.h"
+
+struct ldp_rlfa_client {
+ RB_ENTRY(ldp_rlfa_client) entry;
+
+ /* IGP instance data. */
+ struct zapi_rlfa_igp igp;
+
+ /* Backpointer to RLFA node. */
+ struct ldp_rlfa_node *node;
+};
+RB_HEAD(ldp_rlfa_client_head, ldp_rlfa_client);
+RB_PROTOTYPE(ldp_rlfa_client_head, ldp_rlfa_client, entry,
+ ldp_rlfa_client_compare);
+
+struct ldp_rlfa_node {
+ RB_ENTRY(ldp_rlfa_node) entry;
+
+ /* Destination prefix. */
+ struct prefix destination;
+
+ /* PQ node address. */
+ struct in_addr pq_address;
+
+ /* RLFA clients. */
+ struct ldp_rlfa_client_head clients;
+
+ /* Label allocated by the PQ node to the RLFA destination. */
+ mpls_label_t pq_label;
+};
+RB_HEAD(ldp_rlfa_node_head, ldp_rlfa_node);
+RB_PROTOTYPE(ldp_rlfa_node_head, ldp_rlfa_node, entry, ldp_rlfa_node_compare);
+
+extern struct ldp_rlfa_node_head rlfa_node_tree;
+
+/* prototypes */
+struct ldp_rlfa_client *rlfa_client_new(struct ldp_rlfa_node *rnode,
+ struct zapi_rlfa_igp *igp);
+void rlfa_client_del(struct ldp_rlfa_client *rclient);
+struct ldp_rlfa_client *rlfa_client_find(struct ldp_rlfa_node *rnode,
+ struct zapi_rlfa_igp *igp);
+struct ldp_rlfa_node *rlfa_node_new(const struct prefix *destination,
+ struct in_addr pq_address);
+void rlfa_node_del(struct ldp_rlfa_node *rnode);
+struct ldp_rlfa_node *rlfa_node_find(const struct prefix *destination,
+ struct in_addr pq_address);
+void lde_rlfa_check(struct ldp_rlfa_client *rclient);
+void lde_rlfa_client_send(struct ldp_rlfa_client *rclient);
+void lde_rlfa_label_update(const struct fec *fec);
+void lde_rlfa_update_clients(struct fec *fec, struct lde_nbr *ln,
+ uint32_t label);
+void ldpe_rlfa_init(struct ldp_rlfa_client *rclient);
+void ldpe_rlfa_exit(struct ldp_rlfa_client *rclient);
+
+#endif /* _LDPD_RLFA_H_ */
ldpd/notification.c \
ldpd/packet.c \
ldpd/pfkey.c \
+ ldpd/rlfa.c \
ldpd/socket.c \
ldpd/util.c \
# end
ldpd/ldpd.h \
ldpd/ldpe.h \
ldpd/log.h \
+ ldpd/rlfa.h \
# end
ldpd_ldpd_SOURCES = ldpd/ldpd.c
#include "nexthop_group.h"
#include "lib_errors.h"
#include "srte.h"
+#include "printfrr.h"
DEFINE_MTYPE_STATIC(LIB, ZCLIENT, "Zclient")
DEFINE_MTYPE_STATIC(LIB, REDIST_INST, "Redistribution instance IDs")
return ZEBRA_NHG_PROTO_SPACING * proto;
}
+
+char *zclient_dump_route_flags(uint32_t flags, char *buf, size_t len)
+{
+ if (flags == 0) {
+ snprintfrr(buf, len, "None ");
+ return buf;
+ }
+
+ snprintfrr(
+ buf, len, "%s%s%s%s%s%s%s%s%s%s",
+ CHECK_FLAG(flags, ZEBRA_FLAG_ALLOW_RECURSION) ? "Recursion "
+ : "",
+ CHECK_FLAG(flags, ZEBRA_FLAG_SELFROUTE) ? "Self " : "",
+ CHECK_FLAG(flags, ZEBRA_FLAG_IBGP) ? "iBGP " : "",
+ CHECK_FLAG(flags, ZEBRA_FLAG_SELECTED) ? "Selected " : "",
+ CHECK_FLAG(flags, ZEBRA_FLAG_FIB_OVERRIDE) ? "Override " : "",
+ CHECK_FLAG(flags, ZEBRA_FLAG_EVPN_ROUTE) ? "Evpn " : "",
+ CHECK_FLAG(flags, ZEBRA_FLAG_RR_USE_DISTANCE) ? "RR Distance "
+ : "",
+ CHECK_FLAG(flags, ZEBRA_FLAG_TRAPPED) ? "Trapped " : "",
+ CHECK_FLAG(flags, ZEBRA_FLAG_OFFLOADED) ? "Offloaded " : "",
+ CHECK_FLAG(flags, ZEBRA_FLAG_OFFLOAD_FAILED) ? "Offload Failed "
+ : "");
+ return buf;
+}
+
+char *zclient_evpn_dump_macip_flags(uint8_t flags, char *buf, size_t len)
+{
+ if (flags == 0) {
+ snprintfrr(buf, len, "None ");
+ return buf;
+ }
+
+ snprintfrr(
+ buf, len, "%s%s%s%s%s%s%s",
+ CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY) ? "Sticky MAC " : "",
+ CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW) ? "Gateway MAC " : "",
+ CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG) ? "Router "
+ : "",
+ CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_OVERRIDE_FLAG) ? "Override "
+ : "",
+ CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_SVI_IP) ? "SVI MAC " : "",
+ CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT) ? "Proxy "
+ : "",
+ CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_SYNC_PATH) ? "Sync " : "");
+
+ return buf;
+}
uint8_t type;
unsigned short instance;
+ /* If you add flags, update zclient_dump_route_flags */
uint32_t flags;
/*
* Cause Zebra to consider this routes nexthops recursively
} opaque;
};
+extern char *zclient_dump_route_flags(uint32_t flags, char *buf, size_t len);
+
struct zapi_labels {
uint8_t message;
#define ZAPI_LABELS_FTN 0x01
uint32_t status;
};
+/* IGP instance data associated to a RLFA. */
+struct zapi_rlfa_igp {
+ vrf_id_t vrf_id;
+ int protocol;
+ union {
+ struct {
+ char area_tag[32];
+ struct {
+ int tree_id;
+ int level;
+ unsigned int run_id;
+ } spf;
+ } isis;
+ };
+};
+
+/* IGP -> LDP RLFA (un)registration message. */
+struct zapi_rlfa_request {
+ /* IGP instance data. */
+ struct zapi_rlfa_igp igp;
+
+ /* Destination prefix. */
+ struct prefix destination;
+
+ /* PQ node address. */
+ struct in_addr pq_address;
+};
+
+/* LDP -> IGP RLFA label update. */
+struct zapi_rlfa_response {
+ /* IGP instance data. */
+ struct zapi_rlfa_igp igp;
+
+ /* Destination prefix. */
+ struct prefix destination;
+
+ /* Resolved LDP labels. */
+ mpls_label_t pq_label;
+ uint16_t nexthop_num;
+ struct {
+ int family;
+ union g_addr gate;
+ mpls_label_t label;
+ } nexthops[MULTIPATH_NUM];
+};
+
enum zapi_route_notify_owner {
ZAPI_ROUTE_FAIL_INSTALL,
ZAPI_ROUTE_BETTER_ADMIN_WON,
#define ZEBRA_MACIP_TYPE_PROXY_ADVERT 0x20 /* Not locally active */
#define ZEBRA_MACIP_TYPE_SYNC_PATH 0x40 /* sync path */
/* XXX - flags is an u8; that needs to be changed to u32 if you need
- * to allocate past 0x80
+ * to allocate past 0x80. Additionally touch zclient_evpn_dump_macip_flags
*/
+#define MACIP_BUF_SIZE 128
+extern char *zclient_evpn_dump_macip_flags(uint8_t flags, char *buf,
+ size_t len);
/* Zebra ES VTEP flags (ZEBRA_REMOTE_ES_VTEP_ADD) */
/* ESR has been rxed from the VTEP. Only VTEPs that have advertised the
LDP_IGP_SYNC_IF_STATE_UPDATE = 4,
/* Announce that LDP is up */
LDP_IGP_SYNC_ANNOUNCE_UPDATE = 5,
+ /* Register RLFA with LDP */
+ LDP_RLFA_REGISTER = 7,
+ /* Unregister all RLFAs with LDP */
+ LDP_RLFA_UNREGISTER_ALL = 8,
+ /* Announce LDP labels associated to a previously registered RLFA */
+ LDP_RLFA_LABELS = 9,
};
/* Send the hello message.
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,
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);
}
}
{
int i = 0;
qobj_init();
- bgp_master_init(thread_master_create(NULL), BGP_SOCKET_SNDBUF_SIZE);
+ bgp_master_init(thread_master_create(NULL), BGP_SOCKET_SNDBUF_SIZE,
+ list_new());
master = bm->master;
bgp_option_set(BGP_OPT_NO_LISTEN);
bgp_attr_init();
qobj_init();
master = thread_master_create(NULL);
- bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE);
+ bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
vrf_init(NULL, NULL, NULL, NULL, NULL);
bgp_option_set(BGP_OPT_NO_LISTEN);
cmd_init(0);
bgp_vty_init();
master = thread_master_create("test mp attr");
- bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE);
+ bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
vrf_init(NULL, NULL, NULL, NULL, NULL);
bgp_option_set(BGP_OPT_NO_LISTEN);
bgp_attr_init();
qobj_init();
master = thread_master_create(NULL);
zclient = zclient_new(master, &zclient_options_default);
- bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE);
+ bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
vrf_init(NULL, NULL, NULL, NULL, NULL);
bgp_option_set(BGP_OPT_NO_LISTEN);
qobj_init();
bgp_attr_init();
master = thread_master_create(NULL);
- bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE);
+ bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
vrf_init(NULL, NULL, NULL, NULL, NULL);
bgp_option_set(BGP_OPT_NO_LISTEN);
master = thread_master_create(NULL);
yang_init(true);
nb_init(master, bgpd_yang_modules, array_size(bgpd_yang_modules), false);
- bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE);
+ bgp_master_init(master, BGP_SOCKET_SNDBUF_SIZE, list_new());
bgp_option_set(BGP_OPT_NO_LISTEN);
vrf_init(NULL, NULL, NULL, NULL, NULL);
frr_pthread_init();
return NULL;
}
+mpls_label_t test_topology_node_ldp_label(const struct isis_topology *topology,
+ struct in_addr router_id)
+{
+ for (size_t i = 0; topology->nodes[i].hostname[0]; i++) {
+ const struct isis_test_node *tnode = &topology->nodes[i];
+ struct in_addr node_router_id;
+
+ if (!tnode->router_id)
+ continue;
+
+ (void)inet_pton(AF_INET, tnode->router_id, &node_router_id);
+ if (IPV4_ADDR_SAME(&router_id, &node_router_id))
+ return (50000 + (i + 1) * 100);
+ }
+
+ return MPLS_INVALID_LABEL;
+}
+
static struct isis_lsp *lsp_add(struct lspdb_head *lspdb,
struct isis_area *area, int level,
const uint8_t *sysid, uint8_t pseudonode_id)
const char *hostname, uint8_t pseudonode_id);
extern const struct isis_topology *
test_topology_find(struct isis_topology *test_topologies, uint16_t number);
+extern mpls_label_t
+test_topology_node_ldp_label(const struct isis_topology *topology,
+ struct in_addr router_id);
extern int test_topology_load(const struct isis_topology *topology,
struct isis_area *area,
struct lspdb_head lspdb[]);
#include "isisd/isisd.h"
#include "isisd/isis_dynhn.h"
#include "isisd/isis_misc.h"
+#include "isisd/isis_route.h"
#include "isisd/isis_spf.h"
#include "isisd/isis_spf_private.h"
TEST_SPF = 1,
TEST_REVERSE_SPF,
TEST_LFA,
+ TEST_RLFA,
TEST_TI_LFA,
};
isis_spftree_del(spftree_self);
}
+static void test_run_rlfa(struct vty *vty, const struct isis_topology *topology,
+ const struct isis_test_node *root,
+ struct isis_area *area, struct lspdb_head *lspdb,
+ int level, int tree,
+ struct lfa_protected_resource *protected_resource)
+{
+ struct isis_spftree *spftree_self;
+ struct isis_spftree *spftree_reverse;
+ struct isis_spftree *spftree_pc;
+ struct isis_spf_node *spf_node, *node;
+ struct rlfa *rlfa;
+ uint8_t flags;
+
+ /* Run forward SPF in the root node. */
+ flags = F_SPFTREE_NO_ADJACENCIES;
+ spftree_self = isis_spftree_new(area, lspdb, root->sysid, level, tree,
+ SPF_TYPE_FORWARD, flags);
+ isis_run_spf(spftree_self);
+
+ /* Run reverse SPF in the root node. */
+ spftree_reverse = isis_spf_reverse_run(spftree_self);
+
+ /* Run forward SPF on all adjacent routers. */
+ isis_spf_run_neighbors(spftree_self);
+
+ /* Compute the local LFA repair paths. */
+ isis_lfa_compute(area, NULL, spftree_self, protected_resource);
+
+ /* Compute the remote LFA repair paths. */
+ spftree_pc = isis_rlfa_compute(area, spftree_self, spftree_reverse, 0,
+ protected_resource);
+
+ /* Print the extended P-space and Q-space. */
+ vty_out(vty, "P-space (self):\n");
+ RB_FOREACH (node, isis_spf_nodes, &spftree_pc->lfa.p_space)
+ vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
+ vty_out(vty, "\n");
+ RB_FOREACH (spf_node, isis_spf_nodes, &spftree_self->adj_nodes) {
+ if (RB_EMPTY(isis_spf_nodes, &spf_node->lfa.p_space))
+ continue;
+ vty_out(vty, "P-space (%s):\n",
+ print_sys_hostname(spf_node->sysid));
+ RB_FOREACH (node, isis_spf_nodes, &spf_node->lfa.p_space)
+ vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
+ vty_out(vty, "\n");
+ }
+ vty_out(vty, "Q-space:\n");
+ RB_FOREACH (node, isis_spf_nodes, &spftree_pc->lfa.q_space)
+ vty_out(vty, " %s\n", print_sys_hostname(node->sysid));
+ vty_out(vty, "\n");
+
+ /* Print the post-convergence SPT. */
+ isis_print_spftree(vty, spftree_pc);
+
+ /*
+ * Activate the computed RLFAs (if any) using artificial LDP labels for
+ * the PQ nodes.
+ */
+ frr_each_safe (rlfa_tree, &spftree_self->lfa.remote.rlfas, rlfa) {
+ struct zapi_rlfa_response response = {};
+
+ response.pq_label = test_topology_node_ldp_label(
+ topology, rlfa->pq_address);
+ assert(response.pq_label != MPLS_INVALID_LABEL);
+ isis_rlfa_activate(spftree_self, rlfa, &response);
+ }
+
+ /* Print the SPT and the corresponding main/backup routing tables. */
+ isis_print_spftree(vty, spftree_self);
+ vty_out(vty, "Main:\n");
+ isis_print_routes(vty, spftree_self, false, false);
+ vty_out(vty, "Backup:\n");
+ isis_print_routes(vty, spftree_self, false, true);
+
+ /* Cleanup everything. */
+ isis_spftree_del(spftree_self);
+ isis_spftree_del(spftree_reverse);
+ isis_spftree_del(spftree_pc);
+}
+
static void test_run_ti_lfa(struct vty *vty,
const struct isis_topology *topology,
const struct isis_test_node *root,
&area->lspdb[level - 1], level,
tree, &protected_resource);
break;
+ case TEST_RLFA:
+ test_run_rlfa(vty, topology, root, area,
+ &area->lspdb[level - 1], level,
+ tree, &protected_resource);
+ break;
case TEST_TI_LFA:
test_run_ti_lfa(vty, topology, root, area,
&area->lspdb[level - 1], level,
spf\
|reverse-spf\
|lfa system-id WORD [pseudonode-id <1-255>]\
+ |remote-lfa system-id WORD [pseudonode-id <1-255>]\
|ti-lfa system-id WORD [pseudonode-id <1-255>] [node-protection]\
>\
[display-lspdb] [<ipv4-only|ipv6-only>] [<level-1-only|level-2-only>]",
"System ID\n"
"Pseudonode-ID\n"
"Pseudonode-ID\n"
+ "Remote LFA\n"
+ "System ID\n"
+ "System ID\n"
+ "Pseudonode-ID\n"
+ "Pseudonode-ID\n"
"Topology Independent LFA\n"
"System ID\n"
"System ID\n"
else if (argv_find(argv, argc, "lfa", &idx)) {
test_type = TEST_LFA;
+ fail_sysid_str = argv[idx + 2]->arg;
+ if (argv_find(argv, argc, "pseudonode-id", &idx))
+ fail_pseudonode_id =
+ strtoul(argv[idx + 1]->arg, NULL, 10);
+ protection_type = LFA_LINK_PROTECTION;
+ } else if (argv_find(argv, argc, "remote-lfa", &idx)) {
+ test_type = TEST_RLFA;
+
fail_sysid_str = argv[idx + 2]->arg;
if (argv_find(argv, argc, "pseudonode-id", &idx))
fail_pseudonode_id =
test isis topology 14 root rt1 lfa system-id rt2
test isis topology 14 root rt5 lfa system-id rt4
+test isis topology 1 root rt1 remote-lfa system-id rt2
+test isis topology 2 root rt5 remote-lfa system-id rt1 pseudonode-id 1
+test isis topology 3 root rt5 remote-lfa system-id rt4 ipv4-only
+test isis topology 3 root rt5 remote-lfa system-id rt3 ipv4-only
+test isis topology 5 root rt1 remote-lfa system-id rt2 ipv4-only
+test isis topology 6 root rt4 remote-lfa system-id rt3 ipv4-only
+test isis topology 7 root rt11 remote-lfa system-id rt8 ipv4-only
+test isis topology 7 root rt6 remote-lfa system-id rt5 ipv4-only
+test isis topology 8 root rt2 remote-lfa system-id rt5 ipv4-only
+test isis topology 11 root rt2 remote-lfa system-id rt4
+test isis topology 13 root rt1 remote-lfa system-id rt3 ipv4-only
+
test isis topology 1 root rt1 ti-lfa system-id rt2
test isis topology 2 root rt1 ti-lfa system-id rt3
test isis topology 2 root rt1 ti-lfa system-id rt1 pseudonode-id 1
2001:db8::3/128 50 - rt3 - \r
2001:db8::4/128 60 - rt3 - \r
\r
+test# \r
+test# test isis topology 1 root rt1 remote-lfa system-id rt2\r
+P-space (self):\r
+ rt3\r
+ rt5\r
+\r
+P-space (rt3):\r
+ rt3\r
+ rt5\r
+ rt6\r
+\r
+Q-space:\r
+ rt2\r
+ rt4\r
+ rt6\r
+\r
+IS-IS paths to level-1 routers that speak IP\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt1 \r
+10.0.255.1/32 IP internal 0 rt1(4)\r
+rt3 TE-IS 10 rt3 - rt1(4)\r
+rt5 TE-IS 20 rt3 - rt3(4)\r
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)\r
+rt6 TE-IS 30 rt3 - rt5(4)\r
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)\r
+rt4 TE-IS 40 rt3 - rt6(4)\r
+10.0.255.6/32 IP TE 40 rt3 - rt6(4)\r
+rt2 TE-IS 50 rt3 - rt4(4)\r
+10.0.255.4/32 IP TE 50 rt3 - rt4(4)\r
+10.0.255.2/32 IP TE 60 rt3 - rt2(4)\r
+\r
+IS-IS paths to level-1 routers that speak IP\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt1 \r
+10.0.255.1/32 IP internal 0 rt1(4)\r
+rt2 TE-IS 10 rt2 - rt1(4)\r
+rt3 TE-IS 10 rt3 - rt1(4)\r
+rt4 TE-IS 20 rt2 - rt2(4)\r
+rt5 TE-IS 20 rt3 - rt3(4)\r
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)\r
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)\r
+rt6 TE-IS 30 rt2 - rt4(4)\r
+ rt3 - rt5(4)\r
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)\r
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)\r
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)\r
+ rt3 - \r
+\r
+Main:\r
+IS-IS L1 IPv4 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ ----------------------------------------------------------\r
+ 10.0.255.1/32 0 - - - \r
+ 10.0.255.2/32 20 - rt2 implicit-null \r
+ 10.0.255.3/32 20 - rt3 implicit-null \r
+ 10.0.255.4/32 30 - rt2 16040 \r
+ 10.0.255.5/32 30 - rt3 16050 \r
+ 10.0.255.6/32 40 - rt2 16060 \r
+ - rt3 16060 \r
+\r
+Backup:\r
+IS-IS L1 IPv4 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ --------------------------------------------------------\r
+ 10.0.255.2/32 60 - rt3 50600/16020 \r
+ 10.0.255.4/32 50 - rt3 50600/16040 \r
+\r
+P-space (self):\r
+ rt3\r
+ rt5\r
+\r
+P-space (rt3):\r
+ rt3\r
+ rt5\r
+ rt6\r
+\r
+Q-space:\r
+ rt2\r
+ rt4\r
+ rt6\r
+\r
+IS-IS paths to level-1 routers that speak IPv6\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt1 \r
+2001:db8::1/128 IP6 internal 0 rt1(4)\r
+rt3 TE-IS 10 rt3 - rt1(4)\r
+rt5 TE-IS 20 rt3 - rt3(4)\r
+2001:db8::3/128 IP6 internal 20 rt3 - rt3(4)\r
+rt6 TE-IS 30 rt3 - rt5(4)\r
+2001:db8::5/128 IP6 internal 30 rt3 - rt5(4)\r
+rt4 TE-IS 40 rt3 - rt6(4)\r
+2001:db8::6/128 IP6 internal 40 rt3 - rt6(4)\r
+rt2 TE-IS 50 rt3 - rt4(4)\r
+2001:db8::4/128 IP6 internal 50 rt3 - rt4(4)\r
+2001:db8::2/128 IP6 internal 60 rt3 - rt2(4)\r
+\r
+IS-IS paths to level-1 routers that speak IPv6\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt1 \r
+2001:db8::1/128 IP6 internal 0 rt1(4)\r
+rt2 TE-IS 10 rt2 - rt1(4)\r
+rt3 TE-IS 10 rt3 - rt1(4)\r
+rt4 TE-IS 20 rt2 - rt2(4)\r
+rt5 TE-IS 20 rt3 - rt3(4)\r
+2001:db8::2/128 IP6 internal 20 rt2 - rt2(4)\r
+2001:db8::3/128 IP6 internal 20 rt3 - rt3(4)\r
+rt6 TE-IS 30 rt2 - rt4(4)\r
+ rt3 - rt5(4)\r
+2001:db8::4/128 IP6 internal 30 rt2 - rt4(4)\r
+2001:db8::5/128 IP6 internal 30 rt3 - rt5(4)\r
+2001:db8::6/128 IP6 internal 40 rt2 - rt6(4)\r
+ rt3 - \r
+\r
+Main:\r
+IS-IS L1 IPv6 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ ------------------------------------------------------------\r
+ 2001:db8::1/128 0 - - - \r
+ 2001:db8::2/128 20 - rt2 implicit-null \r
+ 2001:db8::3/128 20 - rt3 implicit-null \r
+ 2001:db8::4/128 30 - rt2 16041 \r
+ 2001:db8::5/128 30 - rt3 16051 \r
+ 2001:db8::6/128 40 - rt2 16061 \r
+ - rt3 16061 \r
+\r
+Backup:\r
+IS-IS L1 IPv6 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ ----------------------------------------------------------\r
+ 2001:db8::2/128 60 - rt3 50600/16021 \r
+ 2001:db8::4/128 50 - rt3 50600/16041 \r
+\r
+test# test isis topology 2 root rt5 remote-lfa system-id rt1 pseudonode-id 1\r
+P-space (self):\r
+ rt6\r
+\r
+P-space (rt3):\r
+ rt1\r
+ rt2\r
+ rt3\r
+ rt4\r
+\r
+P-space (rt6):\r
+ rt4\r
+ rt6\r
+\r
+Q-space:\r
+ rt1\r
+ rt2\r
+ rt3\r
+ rt4\r
+ rt6\r
+\r
+IS-IS paths to level-1 routers that speak IP\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt5 \r
+10.0.255.5/32 IP internal 0 rt5(4)\r
+rt6 TE-IS 10 rt6 - rt5(4)\r
+rt4 TE-IS 20 rt6 - rt6(4)\r
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)\r
+rt1 pseudo_TE-IS 30 rt6 - rt4(4)\r
+rt1 TE-IS 30 rt6 - rt1(2)\r
+10.0.255.4/32 IP TE 30 rt6 - rt4(4)\r
+rt3 TE-IS 40 rt3 - rt5(4)\r
+10.0.255.1/32 IP TE 40 rt6 - rt1(4)\r
+rt2 TE-IS 45 rt6 - rt1(4)\r
+10.0.255.3/32 IP TE 50 rt3 - rt3(4)\r
+10.0.255.2/32 IP TE 55 rt6 - rt2(4)\r
+\r
+IS-IS paths to level-1 routers that speak IP\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt5 \r
+10.0.255.5/32 IP internal 0 rt5(4)\r
+rt1 TE-IS 10 rt1 - rt5(4)\r
+rt4 TE-IS 10 rt4 - rt5(4)\r
+rt6 TE-IS 10 rt6 - rt5(4)\r
+rt1 pseudo_TE-IS 20 rt1 - rt1(4)\r
+ rt4 - rt4(4)\r
+10.0.255.1/32 IP TE 20 rt1 - rt1(4)\r
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)\r
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)\r
+rt2 TE-IS 25 rt1 - rt1(4)\r
+10.0.255.2/32 IP TE 35 rt1 - rt2(4)\r
+rt3 TE-IS 40 rt3 - rt5(4)\r
+ rt1 - rt1(4)\r
+10.0.255.3/32 IP TE 50 rt3 - rt3(4)\r
+ rt1 - \r
+\r
+Main:\r
+IS-IS L1 IPv4 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ ----------------------------------------------------------\r
+ 10.0.255.1/32 20 - rt1 implicit-null \r
+ 10.0.255.2/32 35 - rt1 16020 \r
+ 10.0.255.3/32 50 - rt3 implicit-null \r
+ - rt1 implicit-null \r
+ 10.0.255.4/32 20 - rt4 implicit-null \r
+ 10.0.255.5/32 0 - - - \r
+ 10.0.255.6/32 20 - rt6 implicit-null \r
+\r
+Backup:\r
+IS-IS L1 IPv4 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ --------------------------------------------------------\r
+ 10.0.255.1/32 40 - rt6 50400/16010 \r
+ 10.0.255.2/32 55 - rt6 50400/16020 \r
+ 10.0.255.4/32 30 - rt6 50400/16040 \r
+\r
+P-space (self):\r
+ rt6\r
+\r
+P-space (rt3):\r
+ rt1\r
+ rt2\r
+ rt3\r
+ rt4\r
+\r
+P-space (rt6):\r
+ rt4\r
+ rt6\r
+\r
+Q-space:\r
+ rt1\r
+ rt2\r
+ rt3\r
+ rt4\r
+ rt6\r
+\r
+IS-IS paths to level-1 routers that speak IPv6\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt5 \r
+2001:db8::5/128 IP6 internal 0 rt5(4)\r
+rt6 TE-IS 10 rt6 - rt5(4)\r
+rt4 TE-IS 20 rt6 - rt6(4)\r
+2001:db8::6/128 IP6 internal 20 rt6 - rt6(4)\r
+rt1 pseudo_TE-IS 30 rt6 - rt4(4)\r
+rt1 TE-IS 30 rt6 - rt1(2)\r
+2001:db8::4/128 IP6 internal 30 rt6 - rt4(4)\r
+rt3 TE-IS 40 rt3 - rt5(4)\r
+2001:db8::1/128 IP6 internal 40 rt6 - rt1(4)\r
+rt2 TE-IS 45 rt6 - rt1(4)\r
+2001:db8::3/128 IP6 internal 50 rt3 - rt3(4)\r
+2001:db8::2/128 IP6 internal 55 rt6 - rt2(4)\r
+\r
+IS-IS paths to level-1 routers that speak IPv6\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt5 \r
+2001:db8::5/128 IP6 internal 0 rt5(4)\r
+rt1 TE-IS 10 rt1 - rt5(4)\r
+rt4 TE-IS 10 rt4 - rt5(4)\r
+rt6 TE-IS 10 rt6 - rt5(4)\r
+rt1 pseudo_TE-IS 20 rt1 - rt1(4)\r
+ rt4 - rt4(4)\r
+2001:db8::1/128 IP6 internal 20 rt1 - rt1(4)\r
+2001:db8::4/128 IP6 internal 20 rt4 - rt4(4)\r
+2001:db8::6/128 IP6 internal 20 rt6 - rt6(4)\r
+rt2 TE-IS 25 rt1 - rt1(4)\r
+2001:db8::2/128 IP6 internal 35 rt1 - rt2(4)\r
+rt3 TE-IS 40 rt3 - rt5(4)\r
+ rt1 - rt1(4)\r
+2001:db8::3/128 IP6 internal 50 rt3 - rt3(4)\r
+ rt1 - \r
+\r
+Main:\r
+IS-IS L1 IPv6 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ ------------------------------------------------------------\r
+ 2001:db8::1/128 20 - rt1 implicit-null \r
+ 2001:db8::2/128 35 - rt1 16021 \r
+ 2001:db8::3/128 50 - rt3 implicit-null \r
+ - rt1 implicit-null \r
+ 2001:db8::4/128 20 - rt4 implicit-null \r
+ 2001:db8::5/128 0 - - - \r
+ 2001:db8::6/128 20 - rt6 implicit-null \r
+\r
+Backup:\r
+IS-IS L1 IPv6 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ ----------------------------------------------------------\r
+ 2001:db8::1/128 40 - rt6 50400/16011 \r
+ 2001:db8::2/128 55 - rt6 50400/16021 \r
+ 2001:db8::4/128 30 - rt6 50400/16041 \r
+\r
+test# test isis topology 3 root rt5 remote-lfa system-id rt4 ipv4-only\r
+P-space (self):\r
+ rt6\r
+\r
+P-space (rt3):\r
+ rt1\r
+ rt2\r
+ rt3\r
+ rt4\r
+ rt6\r
+\r
+P-space (rt6):\r
+ rt1\r
+ rt2\r
+ rt3\r
+ rt4\r
+ rt6\r
+\r
+Q-space:\r
+ rt1\r
+ rt2\r
+ rt3\r
+ rt4\r
+ rt6\r
+\r
+IS-IS paths to level-1 routers that speak IP\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt5 \r
+10.0.255.5/32 IP internal 0 rt5(4)\r
+rt6 TE-IS 10 rt6 - rt5(4)\r
+rt4 TE-IS 20 rt6 - rt6(4)\r
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)\r
+rt3 TE-IS 30 rt3 - rt5(4)\r
+rt2 TE-IS 30 rt6 - rt4(4)\r
+10.0.255.4/32 IP TE 30 rt6 - rt4(4)\r
+rt1 TE-IS 40 rt3 - rt3(4)\r
+ rt6 - rt2(4)\r
+10.0.255.3/32 IP TE 40 rt3 - rt3(4)\r
+10.0.255.2/32 IP TE 40 rt6 - rt2(4)\r
+10.0.255.1/32 IP TE 50 rt3 - rt1(4)\r
+ rt6 - \r
+\r
+IS-IS paths to level-1 routers that speak IP\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt5 \r
+10.0.255.5/32 IP internal 0 rt5(4)\r
+rt4 TE-IS 10 rt4 - rt5(4)\r
+rt6 TE-IS 10 rt6 - rt5(4)\r
+rt2 TE-IS 20 rt4 - rt4(4)\r
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)\r
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)\r
+rt3 TE-IS 30 rt3 - rt5(4)\r
+ rt4 - rt2(4)\r
+rt1 TE-IS 30 rt4 - rt2(4)\r
+10.0.255.2/32 IP TE 30 rt4 - rt2(4)\r
+10.0.255.3/32 IP TE 40 rt3 - rt3(4)\r
+ rt4 - \r
+10.0.255.1/32 IP TE 40 rt4 - rt1(4)\r
+\r
+Main:\r
+IS-IS L1 IPv4 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ ----------------------------------------------------------\r
+ 10.0.255.1/32 40 - rt4 16010 \r
+ 10.0.255.2/32 30 - rt4 16020 \r
+ 10.0.255.3/32 40 - rt3 implicit-null \r
+ - rt4 implicit-null \r
+ 10.0.255.4/32 20 - rt4 implicit-null \r
+ 10.0.255.5/32 0 - - - \r
+ 10.0.255.6/32 20 - rt6 implicit-null \r
+\r
+Backup:\r
+IS-IS L1 IPv4 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ -----------------------------------------------------\r
+ 10.0.255.1/32 40 - rt3 16010 \r
+ - rt6 16010 \r
+ 10.0.255.2/32 30 - rt3 16020 \r
+ - rt6 16020 \r
+ 10.0.255.4/32 20 - rt3 16040 \r
+ - rt6 16040 \r
+\r
+test# test isis topology 3 root rt5 remote-lfa system-id rt3 ipv4-only\r
+P-space (self):\r
+ rt1\r
+ rt2\r
+ rt4\r
+ rt6\r
+\r
+P-space (rt4):\r
+ rt1\r
+ rt2\r
+ rt3\r
+ rt4\r
+ rt6\r
+\r
+P-space (rt6):\r
+ rt1\r
+ rt2\r
+ rt3\r
+ rt4\r
+ rt6\r
+\r
+Q-space:\r
+ rt1\r
+ rt2\r
+ rt3\r
+ rt4\r
+ rt6\r
+\r
+IS-IS paths to level-1 routers that speak IP\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt5 \r
+10.0.255.5/32 IP internal 0 rt5(4)\r
+rt4 TE-IS 10 rt4 - rt5(4)\r
+rt6 TE-IS 10 rt6 - rt5(4)\r
+rt2 TE-IS 20 rt4 - rt4(4)\r
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)\r
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)\r
+rt1 TE-IS 30 rt4 - rt2(4)\r
+rt3 TE-IS 30 rt4 - rt2(4)\r
+10.0.255.2/32 IP TE 30 rt4 - rt2(4)\r
+10.0.255.1/32 IP TE 40 rt4 - rt1(4)\r
+10.0.255.3/32 IP TE 40 rt4 - rt3(4)\r
+\r
+IS-IS paths to level-1 routers that speak IP\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt5 \r
+10.0.255.5/32 IP internal 0 rt5(4)\r
+rt4 TE-IS 10 rt4 - rt5(4)\r
+rt6 TE-IS 10 rt6 - rt5(4)\r
+rt2 TE-IS 20 rt4 - rt4(4)\r
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)\r
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)\r
+rt3 TE-IS 30 rt3 - rt5(4)\r
+ rt4 - rt2(4)\r
+rt1 TE-IS 30 rt4 - rt2(4)\r
+10.0.255.2/32 IP TE 30 rt4 - rt2(4)\r
+10.0.255.3/32 IP TE 40 rt3 - rt3(4)\r
+ rt4 - \r
+10.0.255.1/32 IP TE 40 rt4 - rt1(4)\r
+\r
+Main:\r
+IS-IS L1 IPv4 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ ----------------------------------------------------------\r
+ 10.0.255.1/32 40 - rt4 16010 \r
+ 10.0.255.2/32 30 - rt4 16020 \r
+ 10.0.255.3/32 40 - rt3 implicit-null \r
+ - rt4 implicit-null \r
+ 10.0.255.4/32 20 - rt4 implicit-null \r
+ 10.0.255.5/32 0 - - - \r
+ 10.0.255.6/32 20 - rt6 implicit-null \r
+\r
+Backup:\r
+IS-IS L1 IPv4 routing table:\r
+\r
+test# test isis topology 5 root rt1 remote-lfa system-id rt2 ipv4-only\r
+P-space (self):\r
+ rt3\r
+ rt5\r
+ rt7\r
+\r
+P-space (rt3):\r
+ rt3\r
+ rt5\r
+ rt7\r
+ rt8\r
+\r
+Q-space:\r
+ rt2\r
+ rt4\r
+ rt6\r
+ rt8\r
+\r
+IS-IS paths to level-1 routers that speak IP\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt1 \r
+10.0.255.1/32 IP internal 0 rt1(4)\r
+rt3 TE-IS 10 rt3 - rt1(4)\r
+rt5 TE-IS 20 rt3 - rt3(4)\r
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)\r
+rt7 TE-IS 30 rt3 - rt5(4)\r
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)\r
+rt8 TE-IS 40 rt3 - rt7(4)\r
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)\r
+rt6 TE-IS 50 rt3 - rt8(4)\r
+10.0.255.8/32 IP TE 50 rt3 - rt8(4)\r
+rt4 TE-IS 60 rt3 - rt6(4)\r
+10.0.255.6/32 IP TE 60 rt3 - rt6(4)\r
+rt2 TE-IS 70 rt3 - rt4(4)\r
+10.0.255.4/32 IP TE 70 rt3 - rt4(4)\r
+10.0.255.2/32 IP TE 80 rt3 - rt2(4)\r
+\r
+IS-IS paths to level-1 routers that speak IP\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt1 \r
+10.0.255.1/32 IP internal 0 rt1(4)\r
+rt2 TE-IS 10 rt2 - rt1(4)\r
+rt3 TE-IS 10 rt3 - rt1(4)\r
+rt4 TE-IS 20 rt2 - rt2(4)\r
+rt5 TE-IS 20 rt3 - rt3(4)\r
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)\r
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)\r
+rt6 TE-IS 30 rt2 - rt4(4)\r
+rt7 TE-IS 30 rt3 - rt5(4)\r
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)\r
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)\r
+rt8 TE-IS 40 rt2 - rt6(4)\r
+ rt3 - rt7(4)\r
+10.0.255.6/32 IP TE 40 rt2 - rt6(4)\r
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)\r
+10.0.255.8/32 IP TE 50 rt2 - rt8(4)\r
+ rt3 - \r
+\r
+Main:\r
+IS-IS L1 IPv4 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ ----------------------------------------------------------\r
+ 10.0.255.1/32 0 - - - \r
+ 10.0.255.2/32 20 - rt2 implicit-null \r
+ 10.0.255.3/32 20 - rt3 implicit-null \r
+ 10.0.255.4/32 30 - rt2 16040 \r
+ 10.0.255.5/32 30 - rt3 16050 \r
+ 10.0.255.6/32 40 - rt2 16060 \r
+ 10.0.255.7/32 40 - rt3 16070 \r
+ 10.0.255.8/32 50 - rt2 16080 \r
+ - rt3 16080 \r
+\r
+Backup:\r
+IS-IS L1 IPv4 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ --------------------------------------------------------\r
+ 10.0.255.2/32 80 - rt3 50800/16020 \r
+ 10.0.255.4/32 70 - rt3 50800/16040 \r
+ 10.0.255.6/32 60 - rt3 50800/16060 \r
+\r
+test# test isis topology 6 root rt4 remote-lfa system-id rt3 ipv4-only\r
+P-space (self):\r
+ rt2\r
+ rt5\r
+ rt6\r
+ rt7\r
+ rt8\r
+\r
+P-space (rt2):\r
+ rt1\r
+ rt2\r
+\r
+P-space (rt6):\r
+ rt5\r
+ rt6\r
+ rt7\r
+ rt8\r
+\r
+Q-space:\r
+ rt1\r
+ rt3\r
+\r
+IS-IS paths to level-1 routers that speak IP\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt4 \r
+10.0.255.4/32 IP internal 0 rt4(4)\r
+rt2 TE-IS 10 rt2 - rt4(4)\r
+rt6 TE-IS 10 rt6 - rt4(4)\r
+rt1 TE-IS 20 rt2 - rt2(4)\r
+rt5 TE-IS 20 rt6 - rt6(4)\r
+rt8 TE-IS 20 rt6 - rt6(4)\r
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)\r
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)\r
+rt3 TE-IS 30 rt2 - rt1(4)\r
+rt7 TE-IS 30 rt6 - rt5(4)\r
+ rt8(4)\r
+10.0.255.1/32 IP TE 30 rt2 - rt1(4)\r
+10.0.255.5/32 IP TE 30 rt6 - rt5(4)\r
+10.0.255.8/32 IP TE 30 rt6 - rt8(4)\r
+10.0.255.3/32 IP TE 40 rt2 - rt3(4)\r
+10.0.255.7/32 IP TE 40 rt6 - rt7(4)\r
+\r
+IS-IS paths to level-1 routers that speak IP\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt4 \r
+10.0.255.4/32 IP internal 0 rt4(4)\r
+rt2 TE-IS 10 rt2 - rt4(4)\r
+rt3 TE-IS 10 rt3 - rt4(4)\r
+rt6 TE-IS 10 rt6 - rt4(4)\r
+rt1 TE-IS 20 rt2 - rt2(4)\r
+ rt3 - rt3(4)\r
+rt5 TE-IS 20 rt6 - rt6(4)\r
+rt8 TE-IS 20 rt6 - rt6(4)\r
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)\r
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)\r
+10.0.255.6/32 IP TE 20 rt6 - rt6(4)\r
+rt7 TE-IS 30 rt6 - rt5(4)\r
+ rt8(4)\r
+10.0.255.1/32 IP TE 30 rt2 - rt1(4)\r
+ rt3 - \r
+10.0.255.5/32 IP TE 30 rt6 - rt5(4)\r
+10.0.255.8/32 IP TE 30 rt6 - rt8(4)\r
+10.0.255.7/32 IP TE 40 rt6 - rt7(4)\r
+\r
+Main:\r
+IS-IS L1 IPv4 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ ----------------------------------------------------------\r
+ 10.0.255.1/32 30 - rt2 16010 \r
+ - rt3 16010 \r
+ 10.0.255.2/32 20 - rt2 implicit-null \r
+ 10.0.255.3/32 20 - rt3 implicit-null \r
+ 10.0.255.4/32 0 - - - \r
+ 10.0.255.5/32 30 - rt6 16050 \r
+ 10.0.255.6/32 20 - rt6 implicit-null \r
+ 10.0.255.7/32 40 - rt6 16070 \r
+ 10.0.255.8/32 30 - rt6 16080 \r
+\r
+Backup:\r
+IS-IS L1 IPv4 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ --------------------------------------------------------\r
+ 10.0.255.3/32 40 - rt2 50100/16030 \r
+\r
+test# test isis topology 7 root rt11 remote-lfa system-id rt8 ipv4-only\r
+P-space (self):\r
+ rt10\r
+ rt12\r
+\r
+P-space (rt10):\r
+ rt1\r
+ rt4\r
+ rt7\r
+ rt10\r
+\r
+P-space (rt12):\r
+ rt9\r
+ rt12\r
+\r
+Q-space:\r
+ rt1\r
+ rt2\r
+ rt3\r
+ rt4\r
+ rt5\r
+ rt6\r
+ rt7\r
+ rt8\r
+ rt9\r
+\r
+IS-IS paths to level-1 routers that speak IP\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt11 \r
+10.0.255.11/32 IP internal 0 rt11(4)\r
+rt10 TE-IS 10 rt10 - rt11(4)\r
+rt12 TE-IS 10 rt12 - rt11(4)\r
+rt9 TE-IS 20 rt12 - rt12(4)\r
+10.0.255.10/32 IP TE 20 rt10 - rt10(4)\r
+10.0.255.12/32 IP TE 20 rt12 - rt12(4)\r
+rt7 TE-IS 30 rt10 - rt10(4)\r
+rt8 TE-IS 30 rt12 - rt9(4)\r
+10.0.255.9/32 IP TE 30 rt12 - rt9(4)\r
+rt4 TE-IS 40 rt10 - rt7(4)\r
+rt5 TE-IS 40 rt12 - rt8(4)\r
+10.0.255.7/32 IP TE 40 rt10 - rt7(4)\r
+10.0.255.8/32 IP TE 40 rt12 - rt8(4)\r
+rt6 TE-IS 50 rt12 - rt9(4)\r
+ rt5(4)\r
+rt1 TE-IS 50 rt10 - rt4(4)\r
+rt2 TE-IS 50 rt12 - rt5(4)\r
+10.0.255.4/32 IP TE 50 rt10 - rt4(4)\r
+10.0.255.5/32 IP TE 50 rt12 - rt5(4)\r
+rt3 TE-IS 60 rt12 - rt6(4)\r
+ rt2(4)\r
+10.0.255.6/32 IP TE 60 rt12 - rt6(4)\r
+10.0.255.1/32 IP TE 60 rt10 - rt1(4)\r
+10.0.255.2/32 IP TE 60 rt12 - rt2(4)\r
+10.0.255.3/32 IP TE 70 rt12 - rt3(4)\r
+\r
+IS-IS paths to level-1 routers that speak IP\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt11 \r
+10.0.255.11/32 IP internal 0 rt11(4)\r
+rt8 TE-IS 10 rt8 - rt11(4)\r
+rt10 TE-IS 10 rt10 - rt11(4)\r
+rt12 TE-IS 10 rt12 - rt11(4)\r
+rt5 TE-IS 20 rt8 - rt8(4)\r
+rt7 TE-IS 20 rt8 - rt8(4)\r
+rt9 TE-IS 20 rt8 - rt8(4)\r
+ rt12 - rt12(4)\r
+10.0.255.8/32 IP TE 20 rt8 - rt8(4)\r
+10.0.255.10/32 IP TE 20 rt10 - rt10(4)\r
+10.0.255.12/32 IP TE 20 rt12 - rt12(4)\r
+rt2 TE-IS 30 rt8 - rt5(4)\r
+rt4 TE-IS 30 rt8 - rt5(4)\r
+ rt7(4)\r
+rt6 TE-IS 30 rt8 - rt5(4)\r
+10.0.255.5/32 IP TE 30 rt8 - rt5(4)\r
+10.0.255.7/32 IP TE 30 rt8 - rt7(4)\r
+10.0.255.9/32 IP TE 30 rt8 - rt9(4)\r
+ rt12 - \r
+rt3 TE-IS 40 rt8 - rt2(4)\r
+ rt6(4)\r
+rt1 TE-IS 40 rt8 - rt4(4)\r
+10.0.255.2/32 IP TE 40 rt8 - rt2(4)\r
+10.0.255.4/32 IP TE 40 rt8 - rt4(4)\r
+10.0.255.6/32 IP TE 40 rt8 - rt6(4)\r
+10.0.255.3/32 IP TE 50 rt8 - rt3(4)\r
+10.0.255.1/32 IP TE 50 rt8 - rt1(4)\r
+\r
+Main:\r
+IS-IS L1 IPv4 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ -----------------------------------------------------------\r
+ 10.0.255.1/32 50 - rt8 16010 \r
+ 10.0.255.2/32 40 - rt8 16020 \r
+ 10.0.255.3/32 50 - rt8 16030 \r
+ 10.0.255.4/32 40 - rt8 16040 \r
+ 10.0.255.5/32 30 - rt8 16050 \r
+ 10.0.255.6/32 40 - rt8 16060 \r
+ 10.0.255.7/32 30 - rt8 16070 \r
+ 10.0.255.8/32 20 - rt8 implicit-null \r
+ 10.0.255.9/32 30 - rt8 16090 \r
+ - rt12 16090 \r
+ 10.0.255.10/32 20 - rt10 implicit-null \r
+ 10.0.255.11/32 0 - - - \r
+ 10.0.255.12/32 20 - rt12 implicit-null \r
+\r
+Backup:\r
+IS-IS L1 IPv4 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ --------------------------------------------------------\r
+ 10.0.255.1/32 50 - rt10 16010 \r
+ 10.0.255.2/32 60 - rt12 50900/16020 \r
+ 10.0.255.3/32 70 - rt12 50900/16030 \r
+ 10.0.255.4/32 40 - rt10 16040 \r
+ 10.0.255.5/32 50 - rt12 50900/16050 \r
+ 10.0.255.6/32 60 - rt12 50900/16060 \r
+ 10.0.255.7/32 30 - rt10 16070 \r
+ 10.0.255.8/32 40 - rt12 50900/16080 \r
+\r
+test# test isis topology 7 root rt6 remote-lfa system-id rt5 ipv4-only\r
+P-space (self):\r
+ rt3\r
+\r
+P-space (rt3):\r
+ rt2\r
+ rt3\r
+\r
+P-space (rt9):\r
+ rt1\r
+ rt2\r
+ rt4\r
+ rt5\r
+ rt7\r
+ rt8\r
+ rt9\r
+ rt10\r
+ rt11\r
+ rt12\r
+\r
+Q-space:\r
+ rt1\r
+ rt2\r
+ rt4\r
+ rt5\r
+ rt7\r
+ rt8\r
+ rt9\r
+ rt10\r
+ rt11\r
+ rt12\r
+\r
+IS-IS paths to level-1 routers that speak IP\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt6 \r
+10.0.255.6/32 IP internal 0 rt6(4)\r
+rt3 TE-IS 10 rt3 - rt6(4)\r
+rt2 TE-IS 20 rt3 - rt3(4)\r
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)\r
+rt9 TE-IS 30 rt9 - rt6(4)\r
+rt5 TE-IS 30 rt3 - rt2(4)\r
+10.0.255.2/32 IP TE 30 rt3 - rt2(4)\r
+rt8 TE-IS 40 rt9 - rt9(4)\r
+ rt3 - rt5(4)\r
+rt12 TE-IS 40 rt9 - rt9(4)\r
+rt4 TE-IS 40 rt3 - rt5(4)\r
+10.0.255.9/32 IP TE 40 rt9 - rt9(4)\r
+10.0.255.5/32 IP TE 40 rt3 - rt5(4)\r
+rt7 TE-IS 50 rt9 - rt8(4)\r
+ rt3 - rt4(4)\r
+rt11 TE-IS 50 rt9 - rt8(4)\r
+ rt3 - rt12(4)\r
+rt1 TE-IS 50 rt3 - rt4(4)\r
+10.0.255.8/32 IP TE 50 rt9 - rt8(4)\r
+ rt3 - \r
+10.0.255.12/32 IP TE 50 rt9 - rt12(4)\r
+10.0.255.4/32 IP TE 50 rt3 - rt4(4)\r
+rt10 TE-IS 60 rt9 - rt11(4)\r
+ rt3 - \r
+10.0.255.7/32 IP TE 60 rt9 - rt7(4)\r
+ rt3 - \r
+10.0.255.11/32 IP TE 60 rt9 - rt11(4)\r
+ rt3 - \r
+10.0.255.1/32 IP TE 60 rt3 - rt1(4)\r
+10.0.255.10/32 IP TE 70 rt9 - rt10(4)\r
+ rt3 - \r
+\r
+IS-IS paths to level-1 routers that speak IP\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt6 \r
+10.0.255.6/32 IP internal 0 rt6(4)\r
+rt3 TE-IS 10 rt3 - rt6(4)\r
+rt5 TE-IS 10 rt5 - rt6(4)\r
+rt2 TE-IS 20 rt3 - rt3(4)\r
+ rt5 - rt5(4)\r
+rt4 TE-IS 20 rt5 - rt5(4)\r
+rt8 TE-IS 20 rt5 - rt5(4)\r
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)\r
+10.0.255.5/32 IP TE 20 rt5 - rt5(4)\r
+rt9 TE-IS 30 rt9 - rt6(4)\r
+ rt5 - rt8(4)\r
+rt1 TE-IS 30 rt5 - rt4(4)\r
+rt7 TE-IS 30 rt5 - rt4(4)\r
+ rt8(4)\r
+rt11 TE-IS 30 rt5 - rt8(4)\r
+10.0.255.2/32 IP TE 30 rt3 - rt2(4)\r
+ rt5 - \r
+10.0.255.4/32 IP TE 30 rt5 - rt4(4)\r
+10.0.255.8/32 IP TE 30 rt5 - rt8(4)\r
+rt12 TE-IS 40 rt9 - rt9(4)\r
+ rt5 - rt11(4)\r
+rt10 TE-IS 40 rt5 - rt11(4)\r
+10.0.255.9/32 IP TE 40 rt9 - rt9(4)\r
+ rt5 - \r
+10.0.255.1/32 IP TE 40 rt5 - rt1(4)\r
+10.0.255.7/32 IP TE 40 rt5 - rt7(4)\r
+10.0.255.11/32 IP TE 40 rt5 - rt11(4)\r
+10.0.255.12/32 IP TE 50 rt9 - rt12(4)\r
+ rt5 - \r
+10.0.255.10/32 IP TE 50 rt5 - rt10(4)\r
+\r
+Main:\r
+IS-IS L1 IPv4 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ -----------------------------------------------------------\r
+ 10.0.255.1/32 40 - rt5 16010 \r
+ 10.0.255.2/32 30 - rt3 16020 \r
+ - rt5 16020 \r
+ 10.0.255.3/32 20 - rt3 implicit-null \r
+ 10.0.255.4/32 30 - rt5 16040 \r
+ 10.0.255.5/32 20 - rt5 implicit-null \r
+ 10.0.255.6/32 0 - - - \r
+ 10.0.255.7/32 40 - rt5 16070 \r
+ 10.0.255.8/32 30 - rt5 16080 \r
+ 10.0.255.9/32 40 - rt9 implicit-null \r
+ - rt5 implicit-null \r
+ 10.0.255.10/32 50 - rt5 16100 \r
+ 10.0.255.11/32 40 - rt5 16110 \r
+ 10.0.255.12/32 50 - rt9 16120 \r
+ - rt5 16120 \r
+\r
+Backup:\r
+IS-IS L1 IPv4 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ ------------------------------------------------------\r
+ 10.0.255.1/32 70 - rt9 16010 \r
+ 10.0.255.4/32 60 - rt9 16040 \r
+ 10.0.255.5/32 50 - rt9 16050 \r
+ 10.0.255.7/32 50 - rt9 16070 \r
+ 10.0.255.8/32 40 - rt9 16080 \r
+ 10.0.255.10/32 60 - rt9 16100 \r
+ 10.0.255.11/32 50 - rt9 16110 \r
+\r
+test# test isis topology 8 root rt2 remote-lfa system-id rt5 ipv4-only\r
+P-space (self):\r
+ rt1\r
+ rt3\r
+ rt4\r
+ rt7\r
+ rt10\r
+\r
+P-space (rt1):\r
+ rt1\r
+ rt4\r
+ rt7\r
+ rt10\r
+\r
+P-space (rt3):\r
+ rt3\r
+ rt6\r
+\r
+Q-space:\r
+ rt5\r
+ rt6\r
+ rt8\r
+ rt9\r
+ rt11\r
+ rt12\r
+\r
+IS-IS paths to level-1 routers that speak IP\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt2 \r
+10.0.255.2/32 IP internal 0 rt2(4)\r
+rt1 TE-IS 10 rt1 - rt2(4)\r
+rt3 TE-IS 10 rt3 - rt2(4)\r
+rt4 TE-IS 20 rt1 - rt1(4)\r
+rt6 TE-IS 20 rt3 - rt3(4)\r
+10.0.255.1/32 IP TE 20 rt1 - rt1(4)\r
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)\r
+rt7 TE-IS 30 rt1 - rt4(4)\r
+rt5 TE-IS 30 rt3 - rt6(4)\r
+10.0.255.4/32 IP TE 30 rt1 - rt4(4)\r
+10.0.255.6/32 IP TE 30 rt3 - rt6(4)\r
+rt10 TE-IS 40 rt1 - rt7(4)\r
+rt8 TE-IS 40 rt3 - rt5(4)\r
+10.0.255.7/32 IP TE 40 rt1 - rt7(4)\r
+10.0.255.5/32 IP TE 40 rt3 - rt5(4)\r
+rt9 TE-IS 50 rt3 - rt8(4)\r
+rt11 TE-IS 50 rt3 - rt8(4)\r
+10.0.255.10/32 IP TE 50 rt1 - rt10(4)\r
+10.0.255.8/32 IP TE 50 rt3 - rt8(4)\r
+rt12 TE-IS 60 rt3 - rt9(4)\r
+ rt11(4)\r
+10.0.255.9/32 IP TE 60 rt3 - rt9(4)\r
+10.0.255.11/32 IP TE 60 rt3 - rt11(4)\r
+10.0.255.12/32 IP TE 70 rt3 - rt12(4)\r
+\r
+IS-IS paths to level-1 routers that speak IP\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt2 \r
+10.0.255.2/32 IP internal 0 rt2(4)\r
+rt1 TE-IS 10 rt1 - rt2(4)\r
+rt3 TE-IS 10 rt3 - rt2(4)\r
+rt5 TE-IS 10 rt5 - rt2(4)\r
+rt4 TE-IS 20 rt1 - rt1(4)\r
+rt6 TE-IS 20 rt3 - rt3(4)\r
+ rt5 - rt5(4)\r
+rt8 TE-IS 20 rt5 - rt5(4)\r
+10.0.255.1/32 IP TE 20 rt1 - rt1(4)\r
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)\r
+10.0.255.5/32 IP TE 20 rt5 - rt5(4)\r
+rt7 TE-IS 30 rt1 - rt4(4)\r
+rt9 TE-IS 30 rt5 - rt8(4)\r
+rt11 TE-IS 30 rt5 - rt8(4)\r
+10.0.255.4/32 IP TE 30 rt1 - rt4(4)\r
+10.0.255.6/32 IP TE 30 rt3 - rt6(4)\r
+ rt5 - \r
+10.0.255.8/32 IP TE 30 rt5 - rt8(4)\r
+rt10 TE-IS 40 rt1 - rt7(4)\r
+rt12 TE-IS 40 rt5 - rt9(4)\r
+ rt11(4)\r
+10.0.255.7/32 IP TE 40 rt1 - rt7(4)\r
+10.0.255.9/32 IP TE 40 rt5 - rt9(4)\r
+10.0.255.11/32 IP TE 40 rt5 - rt11(4)\r
+10.0.255.10/32 IP TE 50 rt1 - rt10(4)\r
+10.0.255.12/32 IP TE 50 rt5 - rt12(4)\r
+\r
+Main:\r
+IS-IS L1 IPv4 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ -----------------------------------------------------------\r
+ 10.0.255.1/32 20 - rt1 implicit-null \r
+ 10.0.255.2/32 0 - - - \r
+ 10.0.255.3/32 20 - rt3 implicit-null \r
+ 10.0.255.4/32 30 - rt1 16040 \r
+ 10.0.255.5/32 20 - rt5 implicit-null \r
+ 10.0.255.6/32 30 - rt3 16060 \r
+ - rt5 16060 \r
+ 10.0.255.7/32 40 - rt1 16070 \r
+ 10.0.255.8/32 30 - rt5 16080 \r
+ 10.0.255.9/32 40 - rt5 16090 \r
+ 10.0.255.10/32 50 - rt1 16100 \r
+ 10.0.255.11/32 40 - rt5 16110 \r
+ 10.0.255.12/32 50 - rt5 16120 \r
+\r
+Backup:\r
+IS-IS L1 IPv4 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ ---------------------------------------------------------\r
+ 10.0.255.5/32 40 - rt3 50600/16050 \r
+ 10.0.255.8/32 50 - rt3 50600/16080 \r
+ 10.0.255.9/32 60 - rt3 50600/16090 \r
+ 10.0.255.11/32 60 - rt3 50600/16110 \r
+ 10.0.255.12/32 70 - rt3 50600/16120 \r
+\r
+test# test isis topology 11 root rt2 remote-lfa system-id rt4\r
+P-space (self):\r
+\r
+P-space (rt1):\r
+ rt1\r
+ rt3\r
+ rt5\r
+\r
+P-space (rt3):\r
+ rt1\r
+ rt3\r
+ rt5\r
+ rt6\r
+\r
+Q-space:\r
+ rt1\r
+ rt3\r
+ rt4\r
+ rt5\r
+ rt6\r
+\r
+IS-IS paths to level-1 routers that speak IP\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt2 \r
+10.0.255.2/32 IP internal 0 rt2(4)\r
+rt1 TE-IS 50 rt1 - rt2(4)\r
+rt3 TE-IS 50 rt3 - rt2(4)\r
+rt2 \r
+rt5 TE-IS 60 rt3 - rt3(4)\r
+10.0.255.1/32 IP TE 60 rt1 - rt1(4)\r
+10.0.255.3/32 IP TE 60 rt3 - rt3(4)\r
+rt4 TE-IS 70 rt3 - rt5(4)\r
+rt6 TE-IS 70 rt3 - rt5(4)\r
+10.0.255.5/32 IP TE 70 rt3 - rt5(4)\r
+10.0.255.4/32 IP TE 80 rt3 - rt4(4)\r
+10.0.255.6/32 IP TE 80 rt3 - rt6(4)\r
+\r
+IS-IS paths to level-1 routers that speak IP\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt2 \r
+10.0.255.2/32 IP internal 0 rt2(4)\r
+rt4 TE-IS 10 rt4 - rt2(4)\r
+rt5 TE-IS 20 rt4 - rt4(4)\r
+rt6 TE-IS 20 rt4 - rt4(4)\r
+10.0.255.4/32 IP TE 20 rt4 - rt4(4)\r
+rt3 TE-IS 30 rt4 - rt5(4)\r
+10.0.255.5/32 IP TE 30 rt4 - rt5(4)\r
+10.0.255.6/32 IP TE 30 rt4 - rt6(4)\r
+rt2 \r
+rt1 TE-IS 40 rt4 - rt2(2)\r
+10.0.255.3/32 IP TE 40 rt4 - rt3(4)\r
+10.0.255.1/32 IP TE 50 rt4 - rt1(4)\r
+\r
+Main:\r
+IS-IS L1 IPv4 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ ----------------------------------------------------------\r
+ 10.0.255.1/32 50 - rt4 16010 \r
+ 10.0.255.2/32 0 - - - \r
+ 10.0.255.3/32 40 - rt4 16030 \r
+ 10.0.255.4/32 20 - rt4 implicit-null \r
+ 10.0.255.5/32 30 - rt4 16050 \r
+ 10.0.255.6/32 30 - rt4 16060 \r
+\r
+Backup:\r
+IS-IS L1 IPv4 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ ----------------------------------------------------------\r
+ 10.0.255.1/32 50 - rt1 implicit-null \r
+ - rt3 16010 \r
+ 10.0.255.3/32 50 - rt1 16030 \r
+ - rt3 implicit-null \r
+ 10.0.255.4/32 80 - rt3 50500/16040 \r
+ 10.0.255.5/32 60 - rt1 16050 \r
+ - rt3 16050 \r
+ 10.0.255.6/32 70 - rt3 16060 \r
+\r
+P-space (self):\r
+\r
+P-space (rt1):\r
+ rt1\r
+ rt3\r
+ rt5\r
+\r
+P-space (rt3):\r
+ rt1\r
+ rt3\r
+ rt5\r
+ rt6\r
+\r
+Q-space:\r
+ rt1\r
+ rt3\r
+ rt4\r
+ rt5\r
+ rt6\r
+\r
+IS-IS paths to level-1 routers that speak IPv6\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt2 \r
+2001:db8::2/128 IP6 internal 0 rt2(4)\r
+rt1 TE-IS 50 rt1 - rt2(4)\r
+rt3 TE-IS 50 rt3 - rt2(4)\r
+rt2 \r
+rt5 TE-IS 60 rt3 - rt3(4)\r
+2001:db8::1/128 IP6 internal 60 rt1 - rt1(4)\r
+2001:db8::3/128 IP6 internal 60 rt3 - rt3(4)\r
+rt4 TE-IS 70 rt3 - rt5(4)\r
+rt6 TE-IS 70 rt3 - rt5(4)\r
+2001:db8::5/128 IP6 internal 70 rt3 - rt5(4)\r
+2001:db8::4/128 IP6 internal 80 rt3 - rt4(4)\r
+2001:db8::6/128 IP6 internal 80 rt3 - rt6(4)\r
+\r
+IS-IS paths to level-1 routers that speak IPv6\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt2 \r
+2001:db8::2/128 IP6 internal 0 rt2(4)\r
+rt4 TE-IS 10 rt4 - rt2(4)\r
+rt5 TE-IS 20 rt4 - rt4(4)\r
+rt6 TE-IS 20 rt4 - rt4(4)\r
+2001:db8::4/128 IP6 internal 20 rt4 - rt4(4)\r
+rt3 TE-IS 30 rt4 - rt5(4)\r
+2001:db8::5/128 IP6 internal 30 rt4 - rt5(4)\r
+2001:db8::6/128 IP6 internal 30 rt4 - rt6(4)\r
+rt2 \r
+rt1 TE-IS 40 rt4 - rt2(2)\r
+2001:db8::3/128 IP6 internal 40 rt4 - rt3(4)\r
+2001:db8::1/128 IP6 internal 50 rt4 - rt1(4)\r
+\r
+Main:\r
+IS-IS L1 IPv6 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ ------------------------------------------------------------\r
+ 2001:db8::1/128 50 - rt4 16011 \r
+ 2001:db8::2/128 0 - - - \r
+ 2001:db8::3/128 40 - rt4 16031 \r
+ 2001:db8::4/128 20 - rt4 implicit-null \r
+ 2001:db8::5/128 30 - rt4 16051 \r
+ 2001:db8::6/128 30 - rt4 16061 \r
+\r
+Backup:\r
+IS-IS L1 IPv6 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ ------------------------------------------------------------\r
+ 2001:db8::1/128 50 - rt1 implicit-null \r
+ - rt3 16011 \r
+ 2001:db8::3/128 50 - rt1 16031 \r
+ - rt3 implicit-null \r
+ 2001:db8::4/128 80 - rt3 50500/16041 \r
+ 2001:db8::5/128 60 - rt1 16051 \r
+ - rt3 16051 \r
+ 2001:db8::6/128 70 - rt3 16061 \r
+\r
+test# test isis topology 13 root rt1 remote-lfa system-id rt3 ipv4-only\r
+P-space (self):\r
+ rt2\r
+\r
+P-space (rt2):\r
+ rt2\r
+ rt4\r
+\r
+Q-space:\r
+ rt3\r
+ rt4\r
+ rt5\r
+ rt6\r
+ rt7\r
+\r
+IS-IS paths to level-1 routers that speak IP\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt1 \r
+10.0.255.1/32 IP internal 0 rt1(4)\r
+rt2 TE-IS 10 rt2 - rt1(4)\r
+rt4 TE-IS 20 rt2 - rt2(4)\r
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)\r
+rt3 TE-IS 30 rt2 - rt4(4)\r
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)\r
+rt5 TE-IS 40 rt2 - rt3(4)\r
+rt6 TE-IS 40 rt2 - rt3(4)\r
+10.0.255.3/32 IP TE 40 rt2 - rt3(4)\r
+rt7 TE-IS 50 rt2 - rt5(4)\r
+ rt6(4)\r
+10.0.255.5/32 IP TE 50 rt2 - rt5(4)\r
+10.0.255.6/32 IP TE 50 rt2 - rt6(4)\r
+10.0.255.7/32 IP TE 60 rt2 - rt7(4)\r
+\r
+IS-IS paths to level-1 routers that speak IP\r
+Vertex Type Metric Next-Hop Interface Parent\r
+rt1 \r
+10.0.255.1/32 IP internal 0 rt1(4)\r
+rt2 TE-IS 10 rt2 - rt1(4)\r
+rt3 TE-IS 10 rt3 - rt1(4)\r
+rt4 TE-IS 20 rt2 - rt2(4)\r
+ rt3 - rt3(4)\r
+rt5 TE-IS 20 rt3 - rt3(4)\r
+rt6 TE-IS 20 rt3 - rt3(4)\r
+10.0.255.2/32 IP TE 20 rt2 - rt2(4)\r
+10.0.255.3/32 IP TE 20 rt3 - rt3(4)\r
+rt7 TE-IS 30 rt3 - rt5(4)\r
+ rt6(4)\r
+10.0.255.4/32 IP TE 30 rt2 - rt4(4)\r
+ rt3 - \r
+10.0.255.5/32 IP TE 30 rt3 - rt5(4)\r
+10.0.255.6/32 IP TE 30 rt3 - rt6(4)\r
+10.0.255.7/32 IP TE 40 rt3 - rt7(4)\r
+\r
+Main:\r
+IS-IS L1 IPv4 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ ----------------------------------------------------------\r
+ 10.0.255.1/32 0 - - - \r
+ 10.0.255.2/32 20 - rt2 implicit-null \r
+ 10.0.255.3/32 20 - rt3 implicit-null \r
+ 10.0.255.4/32 30 - rt2 16040 \r
+ - rt3 16040 \r
+ 10.0.255.5/32 30 - rt3 16050 \r
+ 10.0.255.6/32 30 - rt3 16060 \r
+ 10.0.255.7/32 40 - rt3 16070 \r
+\r
+Backup:\r
+IS-IS L1 IPv4 routing table:\r
+\r
+ Prefix Metric Interface Nexthop Label(s) \r
+ --------------------------------------------------------\r
+ 10.0.255.3/32 40 - rt2 50400/16030 \r
+ 10.0.255.5/32 50 - rt2 50400/16050 \r
+ 10.0.255.6/32 50 - rt2 50400/16060 \r
+ 10.0.255.7/32 60 - rt2 50400/16070 \r
+\r
test# \r
test# test isis topology 1 root rt1 ti-lfa system-id rt2\r
P-space (self):\r
--- /dev/null
+
+[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
--- /dev/null
+#!/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)
--- /dev/null
+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;
+ }
+}
--- /dev/null
+#!/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)
--- /dev/null
+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;
+ }
+}
--- /dev/null
+#!/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)
--- /dev/null
+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;
+ }
+}
--- /dev/null
+#!/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)
--- /dev/null
+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;
+ }
+}
--- /dev/null
+{
+ "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" } ]
+ }
+}
--- /dev/null
+{
+ "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"}
+ }
+ }
+}
--- /dev/null
+{
+ "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" } ]
+ }
+}
--- /dev/null
+{
+ "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
+}
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__))
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))
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"])
+
#####################################################
#
# 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))
--- /dev/null
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:DB8:F::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "1000",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "2000",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "2000",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r4": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "3000",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
--- /dev/null
+#!/usr/bin/env python
+
+#
+# test_bgp_listen_on_multiple_addresses.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by Boeing Defence Australia
+# Adriano Marto Reis
+#
+# 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_listen_on_multiple_addresses.py: Test BGP daemon listening for
+connections on multiple addresses.
+
+ +------+ +------+ +------+ +------+
+ | | | | | | | |
+ | r1 |--------| r2 |--------| r3 |--------| r4 |
+ | | | | | | | |
+ +------+ +------+ +------+ +------+
+
+ | | | |
+ | AS 1000 | AS 2000 | AS 3000 |
+ | | | |
+ +------------+--------------------------------+-------------+
+"""
+
+import os
+import sys
+import json
+import pytest
+
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+from lib.topogen import Topogen, get_topogen
+from lib.topojson import build_topo_from_json, build_config_from_json
+from lib.common_config import start_topology
+from lib.topotest import router_json_cmp, run_and_expect
+from mininet.topo import Topo
+from functools import partial
+
+
+LISTEN_ADDRESSES = {
+ "r1": ["10.0.0.1"],
+ "r2": ["10.0.0.2", "10.0.1.1"],
+ "r3": ["10.0.1.2", "10.0.2.1"],
+ "r4": ["10.0.2.2"],
+}
+
+
+# Reads data from JSON File for topology and configuration creation.
+jsonFile = "{}/bgp_listen_on_multiple_addresses.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+
+class TemplateTopo(Topo):
+ "Topology builder."
+
+ def build(self, *_args, **_opts):
+ "Defines the allocation and relationship between routers and switches."
+ tgen = get_topogen(self)
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ "Sets up the test environment."
+ tgen = Topogen(TemplateTopo, mod.__name__)
+
+ # Adds extra parameters to bgpd so they listen for connections on specific
+ # multiple addresses.
+ for router_name in tgen.routers().keys():
+ tgen.net[router_name].daemons_options["bgpd"] = "-l " + " -l ".join(
+ LISTEN_ADDRESSES[router_name]
+ )
+
+ start_topology(tgen)
+ build_config_from_json(tgen, topo)
+
+
+def teardown_module(_mod):
+ "Tears-down the test environment."
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_peering():
+ "Checks if the routers peer-up."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ _bgp_converge_initial("r1", "10.0.0.2")
+ _bgp_converge_initial("r2", "10.0.0.1")
+ _bgp_converge_initial("r2", "10.0.1.2")
+ _bgp_converge_initial("r3", "10.0.1.1")
+ _bgp_converge_initial("r3", "10.0.2.2")
+ _bgp_converge_initial("r4", "10.0.2.1")
+
+
+def test_listening_address():
+ """
+ Checks if bgpd is only listening on the specified IP addresses.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for router in tgen.routers().values():
+ # bgpd must not be listening on the default address.
+ output = router.run("netstat -nlt4 | grep 0.0.0.0:179")
+ assert output == "", "{}: bpgd is listening on 0.0.0.0:179".format(router.name)
+
+ # bgpd must be listening on the specified addresses.
+ for address in LISTEN_ADDRESSES[router.name]:
+ output = router.run("netstat -nlt4 | grep {}:179".format(address))
+ assert output != "", "{}: bpgd is not listening on {}:179".format(
+ router.name, address
+ )
+
+
+def _bgp_converge_initial(router_name, peer_address, timeout=180):
+ """
+ Waits for the BGP connection between a given router and a given peer
+ (specified by its IP address) to be established. If the connection is
+ not established within a given timeout, then an exception is raised.
+ """
+ tgen = get_topogen()
+ router = tgen.routers()[router_name]
+ expected = {"ipv4Unicast": {"peers": {peer_address: {"state": "Established"}}}}
+
+ test_func = partial(router_json_cmp, router, "show ip bgp summary json", expected)
+ _, result = run_and_expect(test_func, None, count=timeout, wait=1)
+ assert result is None, "{}: Failed to establish connection with {}".format(
+ router_name, peer_address
+ )
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
--- /dev/null
+password 1
+hostname rt1
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt2
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis fast-reroute lfa
+ isis fast-reroute remote-lfa tunnel mpls-ldp
+!
+interface eth-rt3
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis fast-reroute lfa
+ isis fast-reroute remote-lfa tunnel mpls-ldp
+!
+ip prefix-list PLIST seq 5 permit 10.0.255.8/32
+!
+router isis 1
+ net 49.0000.0000.0000.0001.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+ fast-reroute remote-lfa prefix-list PLIST
+!
--- /dev/null
+log file ldpd.log
+!
+hostname rt1
+!
+debug mpls ldp messages recv
+debug mpls ldp messages sent
+debug mpls ldp zebra
+!
+mpls ldp
+ router-id 10.0.255.1
+ dual-stack transport-connection prefer ipv4
+ !
+ address-family ipv4
+ label local allocate host-routes
+ discovery targeted-hello accept
+ discovery transport-address 10.0.255.1
+ !
+ interface eth-rt2
+ interface eth-rt3
+ !
+ !
+ address-family ipv6
+ label local allocate host-routes
+ discovery transport-address 2001:db8::1
+ !
+ interface eth-rt2
+ interface eth-rt3
+ !
+ !
+!
--- /dev/null
+{
+ "10.0.255.2\/32":[
+ {
+ "prefix":"10.0.255.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.255.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "onLink":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.255.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "onLink":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "10.0.255.3\/32":[
+ {
+ "prefix":"10.0.255.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.255.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "onLink":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.255.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "onLink":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "10.0.255.4\/32":[
+ {
+ "prefix":"10.0.255.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.255.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "onLink":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.255.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "onLink":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "10.0.255.5\/32":[
+ {
+ "prefix":"10.0.255.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.255.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "onLink":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.255.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "onLink":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "10.0.255.6\/32":[
+ {
+ "prefix":"10.0.255.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.255.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "onLink":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.255.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "onLink":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "10.0.255.7\/32":[
+ {
+ "prefix":"10.0.255.7\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.255.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "onLink":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.255.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "onLink":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "10.0.255.8\/32":[
+ {
+ "prefix":"10.0.255.8\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":50,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.255.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "onLink":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.255.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "onLink":true
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "2001:db8::2\/128":[
+ {
+ "prefix":"2001:db8::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "2001:db8::3\/128":[
+ {
+ "prefix":"2001:db8::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "2001:db8::4\/128":[
+ {
+ "prefix":"2001:db8::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "2001:db8::5\/128":[
+ {
+ "prefix":"2001:db8::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "2001:db8::6\/128":[
+ {
+ "prefix":"2001:db8::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "2001:db8::7\/128":[
+ {
+ "prefix":"2001:db8::7\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "2001:db8::8\/128":[
+ {
+ "prefix":"2001:db8::8\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":50,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
--- /dev/null
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1-2",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt3",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1-2",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
--- /dev/null
+--- a/rt1/step9/show_ip_route.ref
++++ b/rt1/step10/show_ip_route.ref
+@@ -15,7 +15,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -70,7 +83,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -125,7 +151,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
--- /dev/null
+--- a/rt1/step9/show_ipv6_route.ref
++++ b/rt1/step10/show_ipv6_route.ref
+@@ -13,7 +13,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -62,7 +73,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -111,7 +133,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
--- /dev/null
+--- a/rt1/step1/show_ip_route.ref
++++ b/rt1/step2/show_ip_route.ref
+@@ -15,20 +15,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -49,20 +36,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -83,20 +57,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -117,20 +78,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -151,20 +99,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -185,20 +120,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
--- /dev/null
+--- a/rt1/step1/show_ipv6_route.ref
++++ b/rt1/step2/show_ipv6_route.ref
+@@ -13,18 +13,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -43,18 +32,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -73,18 +51,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -103,18 +70,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -133,18 +89,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -163,18 +108,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
--- /dev/null
+--- a/rt1/step2/show_ip_route.ref
++++ b/rt1/step3/show_ip_route.ref
+@@ -15,7 +15,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -36,7 +49,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -57,7 +83,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -78,7 +117,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -99,7 +151,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -120,7 +185,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
--- /dev/null
+--- a/rt1/step2/show_ipv6_route.ref
++++ b/rt1/step3/show_ipv6_route.ref
+@@ -13,7 +13,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -32,7 +43,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -51,7 +73,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -70,7 +103,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -89,7 +133,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -108,7 +163,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
--- /dev/null
+--- a/rt1/step3/show_ip_route.ref
++++ b/rt1/step4/show_ip_route.ref
+@@ -15,20 +15,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -83,20 +70,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -151,20 +125,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
--- /dev/null
+--- a/rt1/step3/show_ipv6_route.ref
++++ b/rt1/step4/show_ipv6_route.ref
+@@ -13,18 +13,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -73,18 +62,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -133,18 +111,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
--- /dev/null
+--- a/rt1/step4/show_ip_route.ref
++++ b/rt1/step5/show_ip_route.ref
+@@ -36,20 +36,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -91,20 +78,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -146,20 +120,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
--- /dev/null
+--- a/rt1/step4/show_ipv6_route.ref
++++ b/rt1/step5/show_ipv6_route.ref
+@@ -32,18 +32,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -81,18 +70,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -130,18 +108,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
--- /dev/null
+--- a/rt1/step5/show_ip_route.ref
++++ b/rt1/step6/show_ip_route.ref
+@@ -15,7 +15,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -36,7 +49,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -57,7 +83,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -78,7 +117,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -99,7 +151,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -120,7 +185,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
--- /dev/null
+--- a/rt1/step5/show_ipv6_route.ref
++++ b/rt1/step6/show_ipv6_route.ref
+@@ -13,7 +13,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -32,7 +43,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -51,7 +73,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -70,7 +103,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -89,7 +133,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -108,7 +163,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
--- /dev/null
+--- a/rt1/step8/show_ip_route.ref
++++ b/rt1/step9/show_ip_route.ref
+@@ -15,20 +15,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -83,20 +70,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -151,20 +125,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
--- /dev/null
+--- a/rt1/step8/show_ipv6_route.ref
++++ b/rt1/step9/show_ipv6_route.ref
+@@ -13,18 +13,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -73,18 +62,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -133,18 +111,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
--- /dev/null
+log file zebra.log
+!
+hostname rt1
+!
+debug zebra kernel
+debug zebra packet
+debug zebra mpls
+!
+interface lo
+ ip address 10.0.255.1/32
+ ipv6 address 2001:db8::1/128
+!
+interface eth-rt2
+ ip address 10.0.255.1/32
+!
+interface eth-rt3
+ ip address 10.0.255.1/32
+!
+ip forwarding
+!
+line vty
+!
--- /dev/null
+password 1
+hostname rt2
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0002.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
--- /dev/null
+log file ldpd.log
+!
+hostname rt2
+!
+debug mpls ldp messages recv
+debug mpls ldp messages sent
+debug mpls ldp zebra
+!
+mpls ldp
+ router-id 10.0.255.2
+ dual-stack transport-connection prefer ipv4
+ !
+ address-family ipv4
+ label local allocate host-routes
+ discovery targeted-hello accept
+ discovery transport-address 10.0.255.2
+ !
+ interface eth-rt1
+ interface eth-rt4
+ !
+ !
+ address-family ipv6
+ label local allocate host-routes
+ discovery transport-address 2001:db8::2
+ !
+ interface eth-rt1
+ interface eth-rt4
+ !
+ !
+!
--- /dev/null
+log file zebra.log
+!
+hostname rt2
+!
+debug zebra kernel
+debug zebra packet
+debug zebra mpls
+!
+interface lo
+ ip address 10.0.255.2/32
+ ipv6 address 2001:db8::2/128
+!
+interface eth-rt1
+ ip address 10.0.255.2/32
+!
+interface eth-rt4
+ ip address 10.0.255.2/32
+!
+ip forwarding
+!
+line vty
+!
--- /dev/null
+password 1
+hostname rt3
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0003.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
--- /dev/null
+log file ldpd.log
+!
+hostname rt3
+!
+debug mpls ldp messages recv
+debug mpls ldp messages sent
+debug mpls ldp zebra
+!
+mpls ldp
+ router-id 10.0.255.3
+ dual-stack transport-connection prefer ipv4
+ !
+ address-family ipv4
+ label local allocate host-routes
+ discovery targeted-hello accept
+ discovery transport-address 10.0.255.3
+ !
+ interface eth-rt1
+ interface eth-rt5
+ !
+ !
+ address-family ipv6
+ label local allocate host-routes
+ discovery transport-address 2001:db8::3
+ !
+ interface eth-rt1
+ interface eth-rt5
+ !
+ !
+!
--- /dev/null
+log file zebra.log
+!
+hostname rt3
+!
+debug zebra kernel
+debug zebra packet
+debug zebra mpls
+!
+interface lo
+ ip address 10.0.255.3/32
+ ipv6 address 2001:db8::3/128
+!
+interface eth-rt1
+ ip address 10.0.255.3/32
+!
+interface eth-rt5
+ ip address 10.0.255.3/32
+!
+ip forwarding
+!
+line vty
+!
--- /dev/null
+password 1
+hostname rt4
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt2
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0004.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
--- /dev/null
+log file ldpd.log
+!
+hostname rt4
+!
+debug mpls ldp messages recv
+debug mpls ldp messages sent
+debug mpls ldp zebra
+!
+mpls ldp
+ router-id 10.0.255.4
+ dual-stack transport-connection prefer ipv4
+ !
+ address-family ipv4
+ label local allocate host-routes
+ discovery targeted-hello accept
+ discovery transport-address 10.0.255.4
+ !
+ interface eth-rt2
+ interface eth-rt6
+ !
+ !
+ address-family ipv6
+ label local allocate host-routes
+ discovery transport-address 2001:db8::4
+ !
+ interface eth-rt2
+ interface eth-rt6
+ !
+ !
+!
--- /dev/null
+log file zebra.log
+!
+hostname rt4
+!
+debug zebra kernel
+debug zebra packet
+debug zebra mpls
+!
+interface lo
+ ip address 10.0.255.4/32
+ ipv6 address 2001:db8::4/128
+!
+interface eth-rt2
+ ip address 10.0.255.4/32
+!
+interface eth-rt6
+ ip address 10.0.255.4/32
+!
+ip forwarding
+!
+line vty
+!
--- /dev/null
+password 1
+hostname rt5
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt3
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt7
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0005.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
--- /dev/null
+log file ldpd.log
+!
+hostname rt5
+!
+debug mpls ldp messages recv
+debug mpls ldp messages sent
+debug mpls ldp zebra
+!
+mpls ldp
+ router-id 10.0.255.5
+ dual-stack transport-connection prefer ipv4
+ !
+ address-family ipv4
+ label local allocate host-routes
+ discovery targeted-hello accept
+ discovery transport-address 10.0.255.5
+ !
+ interface eth-rt3
+ interface eth-rt7
+ !
+ !
+ address-family ipv6
+ label local allocate host-routes
+ discovery transport-address 2001:db8::5
+ !
+ interface eth-rt3
+ interface eth-rt7
+ !
+ !
+!
--- /dev/null
+log file zebra.log
+!
+hostname rt5
+!
+debug zebra kernel
+debug zebra packet
+debug zebra mpls
+!
+interface lo
+ ip address 10.0.255.5/32
+ ipv6 address 2001:db8::5/128
+!
+interface eth-rt3
+ ip address 10.0.255.5/32
+!
+interface eth-rt7
+ ip address 10.0.255.5/32
+!
+ip forwarding
+!
+line vty
+!
--- /dev/null
+password 1
+hostname rt6
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt8
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0006.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
--- /dev/null
+log file ldpd.log
+!
+hostname rt6
+!
+debug mpls ldp messages recv
+debug mpls ldp messages sent
+debug mpls ldp zebra
+!
+mpls ldp
+ router-id 10.0.255.6
+ dual-stack transport-connection prefer ipv4
+ !
+ address-family ipv4
+ label local allocate host-routes
+ discovery targeted-hello accept
+ discovery transport-address 10.0.255.6
+ !
+ interface eth-rt4
+ interface eth-rt8
+ !
+ !
+ address-family ipv6
+ label local allocate host-routes
+ discovery transport-address 2001:db8::6
+ !
+ interface eth-rt4
+ interface eth-rt8
+ !
+ !
+!
--- /dev/null
+log file zebra.log
+!
+hostname rt6
+!
+debug zebra kernel
+debug zebra packet
+debug zebra mpls
+!
+interface lo
+ ip address 10.0.255.6/32
+ ipv6 address 2001:db8::6/128
+!
+interface eth-rt4
+ ip address 10.0.255.6/32
+!
+interface eth-rt8
+ ip address 10.0.255.6/32
+!
+ip forwarding
+!
+line vty
+!
--- /dev/null
+password 1
+hostname rt7
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt8
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0007.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
--- /dev/null
+log file ldpd.log
+!
+hostname rt7
+!
+debug mpls ldp messages recv
+debug mpls ldp messages sent
+debug mpls ldp zebra
+!
+mpls ldp
+ router-id 10.0.255.7
+ dual-stack transport-connection prefer ipv4
+ !
+ address-family ipv4
+ label local allocate host-routes
+ discovery targeted-hello accept
+ discovery transport-address 10.0.255.7
+ !
+ interface eth-rt5
+ interface eth-rt8
+ !
+ !
+ address-family ipv6
+ label local allocate host-routes
+ discovery transport-address 2001:db8::7
+ !
+ interface eth-rt5
+ interface eth-rt8
+ !
+ !
+!
--- /dev/null
+log file zebra.log
+!
+hostname rt7
+!
+debug zebra kernel
+debug zebra packet
+debug zebra mpls
+!
+interface lo
+ ip address 10.0.255.7/32
+ ipv6 address 2001:db8::7/128
+!
+interface eth-rt5
+ ip address 10.0.255.7/32
+!
+interface eth-rt8
+ ip address 10.0.255.7/32
+!
+ip forwarding
+!
+line vty
+!
--- /dev/null
+password 1
+hostname rt8
+log file isisd.log
+!
+debug isis events
+debug isis route-events
+debug isis spf-events
+debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt7
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0008.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
--- /dev/null
+log file ldpd.log
+!
+hostname rt8
+!
+debug mpls ldp messages recv
+debug mpls ldp messages sent
+debug mpls ldp zebra
+!
+mpls ldp
+ router-id 10.0.255.8
+ dual-stack transport-connection prefer ipv4
+ !
+ address-family ipv4
+ label local allocate host-routes
+ discovery targeted-hello accept
+ discovery transport-address 10.0.255.8
+ !
+ interface eth-rt6
+ interface eth-rt7
+ !
+ !
+ address-family ipv6
+ label local allocate host-routes
+ discovery transport-address 2001:db8::8
+ !
+ interface eth-rt6
+ interface eth-rt7
+ !
+ !
+!
--- /dev/null
+log file zebra.log
+!
+hostname rt8
+!
+debug zebra kernel
+debug zebra packet
+debug zebra mpls
+!
+interface lo
+ ip address 10.0.255.8/32
+ ipv6 address 2001:db8::8/128
+!
+interface eth-rt6
+ ip address 10.0.255.8/32
+!
+interface eth-rt7
+ ip address 10.0.255.8/32
+!
+ip forwarding
+!
+line vty
+!
--- /dev/null
+#!/usr/bin/env python
+
+#
+# test_isis_rlfa_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+#
+# 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_isis_rlfa_topo1.py:
+
+ +---------+ +---------+
+ | | | |
+ | RT1 | | RT2 |
+ | +---------------------+ |
+ | | | |
+ +---+-----+ +------+--+
+ | |
+ | |
+ | |
+ +---+-----+ +------+--+
+ | | | |
+ | RT3 | | RT4 |
+ | | | |
+ | | | |
+ +---+-----+ +------+--+
+ | |
+ | |
+ | |
+ +---+-----+ +------+--+
+ | | | |
+ | RT5 | | RT6 |
+ | | | |
+ | | | |
+ +---+-----+ +------+--+
+ | |
+ | |
+ | |
+ +---+-----+ +------+--+
+ | | | |
+ | RT7 | | RT8 |
+ | +---------------------+ |
+ | | | |
+ +---------+ +---------+
+"""
+
+import os
+import sys
+import pytest
+import json
+import re
+import tempfile
+from time import sleep
+from functools import partial
+
+# 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
+
+# Global multi-dimensional dictionary containing all expected outputs
+outputs = {}
+
+
+class TemplateTopo(Topo):
+ "Test topology builder"
+
+ def build(self, *_args, **_opts):
+ "Build function"
+ tgen = get_topogen(self)
+
+ #
+ # Define FRR Routers
+ #
+ for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7", "rt8"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-rt2")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt1")
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-rt3")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt1")
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2")
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3")
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt6")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt4")
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt7")
+ switch.add_link(tgen.gears["rt7"], nodeif="eth-rt5")
+ switch = tgen.add_switch("s7")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt8")
+ switch.add_link(tgen.gears["rt8"], nodeif="eth-rt6")
+ switch = tgen.add_switch("s8")
+ switch.add_link(tgen.gears["rt7"], nodeif="eth-rt8")
+ switch.add_link(tgen.gears["rt8"], nodeif="eth-rt7")
+
+ #
+ # Populate multi-dimensional dictionary containing all expected outputs
+ #
+ files = [
+ "show_ip_route.ref",
+ "show_ipv6_route.ref",
+ "show_yang_interface_isis_adjacencies.ref",
+ ]
+ for rname in ["rt1"]:
+ outputs[rname] = {}
+ for step in range(1, 10 + 1):
+ outputs[rname][step] = {}
+ for file in files:
+ if step == 1:
+ # Get snapshots relative to the expected initial network convergence
+ filename = "{}/{}/step{}/{}".format(CWD, rname, step, file)
+ outputs[rname][step][file] = open(filename).read()
+ else:
+ if file == "show_yang_interface_isis_adjacencies.ref":
+ continue
+
+ # Get diff relative to the previous step
+ filename = "{}/{}/step{}/{}.diff".format(CWD, rname, step, file)
+
+ # Create temporary files in order to apply the diff
+ f_in = tempfile.NamedTemporaryFile()
+ f_in.write(outputs[rname][step - 1][file])
+ f_in.flush()
+ f_out = tempfile.NamedTemporaryFile()
+ os.system(
+ "patch -s -o %s %s %s" % (f_out.name, f_in.name, filename)
+ )
+
+ # Store the updated snapshot and remove the temporary files
+ outputs[rname][step][file] = open(f_out.name).read()
+ f_in.close()
+ f_out.close()
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(TemplateTopo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.iteritems():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname))
+ )
+
+ 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 router_compare_json_output(rname, command, reference):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ expected = json.loads(reference)
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
+ _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+#
+# Step 1
+#
+# Test initial network convergence
+#
+def test_isis_adjacencies_step1():
+ logger.info("Test (step 1): check IS-IS adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname,
+ "show yang operational-data /frr-interface:lib isisd",
+ outputs[rname][1]["show_yang_interface_isis_adjacencies.ref"],
+ )
+
+
+def test_rib_ipv4_step1():
+ logger.info("Test (step 1): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][1]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step1():
+ logger.info("Test (step 1): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][1]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 2
+#
+# Action(s):
+# -Configure rt8 (rt1's PQ router) to not accept targeted hello messages
+#
+# Expected changes:
+# -All rt1 backup routes should be uninstalled
+#
+def test_rib_ipv4_step2():
+ logger.info("Test (step 2): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Configuring rt8 to not accept targeted hello messages")
+ tgen.net["rt8"].cmd(
+ 'vtysh -c "conf t" -c "mpls ldp" -c "address-family ipv4" -c "no discovery targeted-hello accept"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][2]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step2():
+ logger.info("Test (step 2): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][2]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 3
+#
+# Action(s):
+# -Configure rt8 (rt1's PQ router) to accept targeted hello messages
+#
+# Expected changes:
+# -All rt1 previously uninstalled backup routes should be reinstalled
+#
+def test_rib_ipv4_step3():
+ logger.info("Test (step 3): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Configuring rt8 to accept targeted hello messages")
+ tgen.net["rt8"].cmd(
+ 'vtysh -c "conf t" -c "mpls ldp" -c "address-family ipv4" -c "discovery targeted-hello accept"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][3]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step3():
+ logger.info("Test (step 3): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][3]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 4
+#
+# Action(s):
+# -Disable RLFA on rt1's eth-rt2 interface
+#
+# Expected changes:
+# -All non-ECMP routes whose primary nexthop is eth-rt2 should lose their backup nexthops
+#
+def test_rib_ipv4_step4():
+ logger.info("Test (step 4): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Disabling RLFA on rt1's eth-rt2 interface")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt2" -c "no isis fast-reroute remote-lfa tunnel mpls-ldp"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][4]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step4():
+ logger.info("Test (step 4): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][4]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 5
+#
+# Action(s):
+# -Disable RLFA on rt1's eth-rt3 interface
+#
+# Expected changes:
+# -All non-ECMP routes whose primary nexthop is eth-rt3 should lose their backup nexthops
+#
+def test_rib_ipv4_step5():
+ logger.info("Test (step 5): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Disabling RLFA on rt1's eth-rt3 interface")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt3" -c "no isis fast-reroute remote-lfa tunnel mpls-ldp"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][5]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step5():
+ logger.info("Test (step 5): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][5]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 6
+#
+# Action(s):
+# -Re-enable RLFA on rt1's eth-rt2 and eth-rt3 interfaces
+#
+# Expected changes:
+# -Revert changes from the previous two steps (reinstall all backup routes)
+#
+def test_rib_ipv4_step6():
+ logger.info("Test (step 6): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Re-enabling RLFA on rt1's eth-rt2 and eth-rt3 interfaces")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute remote-lfa tunnel mpls-ldp"'
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt3" -c "isis fast-reroute remote-lfa tunnel mpls-ldp"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][6]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step6():
+ logger.info("Test (step 6): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][6]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 7
+#
+# Action(s):
+# -Configure a PQ node prefix-list filter
+#
+# Expected changes:
+# -All backup routes should be uninstalled
+#
+def test_rib_ipv4_step7():
+ logger.info("Test (step 7): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Configuring a PQ node prefix-list filter")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "fast-reroute remote-lfa prefix-list PLIST"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][7]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step7():
+ logger.info("Test (step 7): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][7]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 8
+#
+# Action(s):
+# -Configure a prefix-list allowing rt8 as a PQ node
+#
+# Expected changes:
+# -All backup routes should be installed again
+#
+def test_rib_ipv4_step8():
+ logger.info("Test (step 8): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Configuring a prefix-list allowing rt8 as a PQ node")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "ip prefix-list PLIST seq 5 permit 10.0.255.8/32"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][8]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step8():
+ logger.info("Test (step 8): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][8]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 9
+#
+# Action(s):
+# -Change the maximum metric up to the PQ node to 30 on the eth-rt2 interface
+#
+# Expected changes:
+# -All non-ECMP routes whose primary nexthop is eth-rt2 should lose their backup nexthops
+#
+def test_rib_ipv4_step9():
+ logger.info("Test (step 9): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info(
+ "Changing the maximum metric up to the PQ node to 30 on the eth-rt2 interface"
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute remote-lfa maximum-metric 30"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][9]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step9():
+ logger.info("Test (step 9): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][9]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 10
+#
+# Action(s):
+# -Change the maximum metric up to the PQ node to 40 on the eth-rt2 interface
+#
+# Expected changes:
+# -All non-ECMP routes whose primary nexthop is eth-rt2 should recover their backup nexthops
+#
+def test_rib_ipv4_step10():
+ logger.info("Test (step 10): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info(
+ "Changing the maximum metric up to the PQ node to 40 on the eth-rt2 interface"
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute remote-lfa maximum-metric 40"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][10]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step10():
+ logger.info("Test (step 10): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname,
+ "show ipv6 route isis json",
+ outputs[rname][10]["show_ipv6_route.ref"],
+ )
+
+
+# Memory leak test template
+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))
interface_data.append("no ip ospf " " hello-interval")
else:
interface_data.append(
- "ip ospf " " hello-interval {}".format(intf_ospf_hello)
+ "ip ospf" " hello-interval {}".format(intf_ospf_hello)
)
if "dead_interval" in ospf_data:
interface_data.append("no ip ospf" " dead-interval")
else:
interface_data.append(
- "ip ospf " " dead-interval {}".format(intf_ospf_dead)
+ "ip ospf" " dead-interval {}".format(intf_ospf_dead)
)
if "network" in ospf_data:
errormsg = (
"[DUT: {}]: tag value {}"
" is not matched for"
- " route {} in RIB \n".format(dut, _tag, st_rt,)
+ " route {} in RIB \n".format(
+ dut,
+ _tag,
+ st_rt,
+ )
)
return errormsg
errormsg = (
"[DUT: {}]: metric value "
"{} is not matched for "
- "route {} in RIB \n".format(dut, metric, st_rt,)
+ "route {} in RIB \n".format(
+ dut,
+ metric,
+ st_rt,
+ )
)
return errormsg
rnode.run(cmd)
logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+
+def verify_ip_nht(tgen, input_dict):
+ """
+ Running "show ip nht" command and verifying given nexthop resolution
+ Parameters
+ ----------
+ * `tgen` : topogen object
+ * `input_dict`: data to verify nexthop
+ Usage
+ -----
+ input_dict_4 = {
+ "r1": {
+ nh: {
+ "Address": nh,
+ "resolvedVia": "connected",
+ "nexthops": {
+ "nexthop1": {
+ "Interface": intf
+ }
+ }
+ }
+ }
+ }
+ result = verify_ip_nht(tgen, input_dict_4)
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: verify_ip_nht()")
+
+ for router in input_dict.keys():
+ if router not in tgen.routers():
+ continue
+
+ rnode = tgen.routers()[router]
+ nh_list = input_dict[router]
+
+ if validate_ip_address(nh_list.keys()[0]) is "ipv6":
+ show_ip_nht = run_frr_cmd(rnode, "show ipv6 nht")
+ else:
+ show_ip_nht = run_frr_cmd(rnode, "show ip nht")
+
+ for nh in nh_list:
+ if nh in show_ip_nht:
+ logger.info("Nexthop %s is resolved on %s", nh, router)
+ return True
+ else:
+ errormsg = "Nexthop {} is resolved on {}".format(nh, router)
+ return errormsg
+
+ logger.debug("Exiting lib API: verify_ip_nht()")
+ return False
+
"r1": {
"ospf": {
"router_id": "22.22.22.22",
- "area": [{ "id":0.0.0.0, "type": "nssa"}]
+ "area": [{ "id": "0.0.0.0", "type": "nssa"}]
}
}
"links": {
"r2": {
"ospf": {
- "authentication": 'message-digest',
+ "authentication": "message-digest",
"authentication-key": "ospf",
"message-digest-key": "10"
}
if data_ospf_area:
cmd = "ip ospf area {}".format(data_ospf_area)
config_data.append(cmd)
+
# interface ospf auth
if data_ospf_auth:
if data_ospf_auth == "null":
logger.debug("Exiting lib API: clear_ospf()")
+def redistribute_ospf(tgen, topo, dut, route_type, **kwargs):
+ """
+ Redstribution of routes inside ospf.
+
+ Parameters
+ ----------
+ * `tgen`: Topogen object
+ * `topo` : json file data
+ * `dut`: device under test
+ * `route_type`: "static" or "connected" or ....
+ * `kwargs`: pass extra information (see below)
+
+ Usage
+ -----
+ redistribute_ospf(tgen, topo, "r0", "static", delete=True)
+ redistribute_ospf(tgen, topo, "r0", "static", route_map="rmap_ipv4")
+ """
+
+ ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": route_type}]}}}
+ for k, v in kwargs.items():
+ ospf_red[dut]["ospf"]["redistribute"][0][k] = v
+
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+
################################
# Verification procs
################################
logger.info("Verifying OSPF neighborship on router %s:", router)
show_ospf_json = run_frr_cmd(
- rnode, "show ip ospf neighbor all json", isjson=True
+ rnode, "show ip ospf neighbor all json", isjson=True
)
# Verifying output dictionary show_ospf_json is empty or not
if "routeType" not in ospf_rib_json[st_rt]:
errormsg = (
"[DUT: {}]: routeType missing"
- "for route {} in OSPF RIB \n".format(dut, st_rt)
+ " for route {} in OSPF RIB \n".format(
+ dut, st_rt
+ )
)
return errormsg
elif _rtype != ospf_rib_json[st_rt]["routeType"]:
errormsg = (
"[DUT: {}]: routeType mismatch"
- "for route {} in OSPF RIB \n".format(dut, st_rt)
+ " for route {} in OSPF RIB \n".format(
+ dut, st_rt
+ )
)
return errormsg
else:
logger.info(
- "DUT: {}]: Found routeType {}"
- "for route {}".format(dut, _rtype, st_rt)
+ "[DUT: {}]: Found routeType {}"
+ " for route {}".format(dut, _rtype, st_rt)
)
if tag:
if "tag" not in ospf_rib_json[st_rt]:
--- /dev/null
+{
+
+ "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
verify_ospf_rib,
create_router_ospf,
verify_ospf_interface,
+ redistribute_ospf,
)
topo = None
logger.info("=" * 40)
-def red_static(dut, config=True):
- """Local def for Redstribute static routes inside ospf."""
- global topo
- tgen = get_topogen()
- if config:
- ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}}
- else:
- ospf_red = {
- dut: {"ospf": {"redistribute": [{"redist_type": "static", "delete": True}]}}
- }
- result = create_router_ospf(tgen, topo, ospf_red)
- assert result is True, "Testcase : Failed \n Error: {}".format(result)
-
-
-def red_connected(dut, config=True):
- """Local def for Redstribute connected routes inside ospf."""
- global topo
- tgen = get_topogen()
- if config:
- ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}}
- else:
- ospf_red = {
- dut: {
- "ospf": {
- "redistribute": [{"redist_type": "connected", "del_action": True}]
- }
- }
- }
- result = create_router_ospf(tgen, topo, ospf_red)
- assert result is True, "Testcase: Failed \n Error: {}".format(result)
-
-
# ##################################
# Test cases start here.
# ##################################
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
dut = "r0"
- red_static(dut)
+ redistribute_ospf(tgen, topo, dut, "static")
step("Verify that route in R2 in stalled with 8 next hops.")
nh = []
step(" Un configure static route on R0")
dut = "r0"
- red_static(dut, config=False)
+ redistribute_ospf(tgen, topo, dut, "static", delete=True)
# Wait for R0 to flush external LSAs.
sleep(10)
step("Re configure the static route in R0.")
dut = "r0"
- red_static(dut)
+ redistribute_ospf(tgen, topo, dut, "static")
dut = "r1"
result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
dut = "r0"
- red_static(dut)
+ redistribute_ospf(tgen, topo, dut, "static")
step("Verify that route in R2 in stalled with 2 next hops.")
step(" Un configure static route on R0")
dut = "r0"
- red_static(dut, config=False)
+ redistribute_ospf(tgen, topo, dut, "static", delete=True)
# sleep till the route gets withdrawn
sleep(10)
step("Reconfigure the static route in R0.Change ECMP value to 2.")
dut = "r0"
- red_static(dut)
+ redistribute_ospf(tgen, topo, dut, "static")
step("Configure cost on R0 as 100")
r0_ospf_cost = {"r0": {"links": {"r1": {"ospf": {"cost": 100}}}}}
verify_ospf_rib,
create_router_ospf,
verify_ospf_interface,
+ redistribute_ospf,
)
from ipaddress import IPv4Address
pass
-def red_static(dut, config=True):
- """Local def for Redstribute static routes inside ospf."""
- global topo
- tgen = get_topogen()
- if config:
- ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}}
- else:
- ospf_red = {
- dut: {
- "ospf": {
- "redistribute": [{"redist_type": "static", "del_action": True}]
- }
- }
- }
- result = create_router_ospf(tgen, topo, ospf_red)
- assert result is True, "Testcase : Failed \n Error: {}".format(result)
-
-
-def red_connected(dut, config=True):
- """Local def for Redstribute connected routes inside ospf."""
- global topo
- tgen = get_topogen()
- if config:
- ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}}
- else:
- ospf_red = {
- dut: {
- "ospf": {
- "redistribute": [{"redist_type": "connected", "del_action": True}]
- }
- }
- }
- result = create_router_ospf(tgen, topo, ospf_red)
- assert result is True, "Testcase: Failed \n Error: {}".format(result)
-
-
# ##################################
# Test cases start here.
# ##################################
)
dut = rtr
- red_static(dut)
+ redistribute_ospf(tgen, topo, dut, "static")
step(
"Verify that route in R0 in stalled with 8 hops. "
pass
-def red_static(dut, config=True):
- """Local def for Redstribute static routes inside ospf."""
- global topo
- tgen = get_topogen()
- if config:
- ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}}
- else:
- ospf_red = {
- dut: {
- "ospf": {
- "redistribute": [{"redist_type": "static", "del_action": True}]
- }
- }
- }
- result = create_router_ospf(tgen, topo, ospf_red)
- assert result is True, "Testcase : Failed \n Error: {}".format(result)
-
-
-def red_connected(dut, config=True):
- """Local def for Redstribute connected routes inside ospf."""
- global topo
- tgen = get_topogen()
- if config:
- ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}}
- else:
- ospf_red = {
- dut: {
- "ospf": {
- "redistribute": [{"redist_type": "connected", "del_action": True}]
- }
- }
- }
- result = create_router_ospf(tgen, topo, ospf_red)
- assert result is True, "Testcase: Failed \n Error: {}".format(result)
-
-
# ##################################
# Test cases start here.
# ##################################
verify_ospf_rib,
create_router_ospf,
verify_ospf_interface,
+ redistribute_ospf,
)
from lib.topojson import build_topo_from_json, build_config_from_json
from lib.topolog import logger
logger.info("=" * 40)
-def red_static(dut, config=True):
- """Local def for Redstribute static routes inside ospf."""
- global topo
- tgen = get_topogen()
- if config:
- ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}}
- else:
- ospf_red = {
- dut: {"ospf": {"redistribute": [{"redist_type": "static", "delete": True}]}}
- }
- result = create_router_ospf(tgen, topo, ospf_red)
- assert result is True, "Testcase : Failed \n Error: {}".format(result)
-
-
-def red_connected(dut, config=True):
- """Local def for Redstribute connected routes inside ospf."""
- global topo
- tgen = get_topogen()
- if config:
- ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}}
- else:
- ospf_red = {
- dut: {
- "ospf": {
- "redistribute": [{"redist_type": "connected", "del_action": True}]
- }
- }
- }
- result = create_router_ospf(tgen, topo, ospf_red)
- assert result is True, "Testcase: Failed \n Error: {}".format(result)
-
-
# ##################################
# Test cases start here.
# ##################################
step("Redistribute static route in R2 ospf.")
dut = "r2"
- red_static(dut)
+ redistribute_ospf(tgen, topo, dut, "static")
step("Verify that Type 5 LSA is originated by R2.")
dut = "r0"
--- /dev/null
+#!/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))
verify_ospf_rib,
create_router_ospf,
verify_ospf_database,
+ redistribute_ospf,
)
# Global variables
result = create_static_routes(tgen, input_dict)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
- ospf_red_r1 = {"r0": {"ospf": {"redistribute": [{"redist_type": "static"}]}}}
- result = create_router_ospf(tgen, topo, ospf_red_r1)
- assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ redistribute_ospf(tgen, topo, "r0", "static")
dut = "r1"
lsid = NETWORK["ipv4"][0].split("/")[0]
result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
- ospf_red_r1 = {
- "r0": {
- "ospf": {"redistribute": [{"redist_type": "static", "del_action": True}]}
- }
- }
- result = create_router_ospf(tgen, topo, ospf_red_r1)
- assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ redistribute_ospf(tgen, topo, "r0", "static", delete=True)
step(
"Create prefix-list in R0 to permit 10.0.20.1/32 prefix &" " deny 10.0.20.2/32"
" ospf using route map rmap1"
)
- ospf_red_r1 = {
- "r0": {
- "ospf": {
- "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv4"}]
- }
- }
- }
- result = create_router_ospf(tgen, topo, ospf_red_r1)
- assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ redistribute_ospf(tgen, topo, "r0", "static", route_map="rmap_ipv4")
step("Change prefix rules to permit 10.0.20.2 and deny 10.0.20.1")
# Create ip prefix list
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
step("Redistribute to ospf using route map ( non existent route map)")
- ospf_red_r1 = {
- "r0": {
- "ospf": {
- "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv4"}]
- }
- }
- }
- result = create_router_ospf(tgen, topo, ospf_red_r1)
- assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ redistribute_ospf(tgen, topo, "r0", "static", route_map="rmap_ipv4")
step(
"Verify that routes are not allowed in OSPF even tough no "
result = create_static_routes(tgen, input_dict)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
- ospf_red_r0 = {
- "r0": {
- "ospf": {
- "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv4"}]
- }
- }
- }
- result = create_router_ospf(tgen, topo, ospf_red_r0)
- assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ redistribute_ospf(tgen, topo, "r0", "static", route_map="rmap_ipv4")
# Create route map
routemaps = {
result = create_static_routes(tgen, input_dict)
assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
- ospf_red_r0 = {
- "r0": {
- "ospf": {
- "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv4"}]
- }
- }
- }
- result = create_router_ospf(tgen, topo, ospf_red_r0)
- assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ redistribute_ospf(tgen, topo, "r0", "static", route_map="rmap_ipv4")
# Create ip prefix list
pfx_list = {
clear_ospf,
verify_ospf_rib,
create_router_ospf,
+ redistribute_ospf,
)
# Global variables
logger.info("=" * 40)
-def red_static(dut, config=True):
- """Local def for Redstribute static routes inside ospf."""
- global topo
- tgen = get_topogen()
- if config:
- ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}}
- else:
- ospf_red = {
- dut: {
- "ospf": {
- "redistribute": [{"redist_type": "static", "del_action": True}]
- }
- }
- }
- result = create_router_ospf(tgen, topo, ospf_red)
- assert result is True, "Testcase : Failed \n Error: {}".format(result)
-
-
-def red_connected(dut, config=True):
- """Local def for Redstribute connected routes inside ospf."""
- global topo
- tgen = get_topogen()
- if config:
- ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}}
- else:
- ospf_red = {
- dut: {
- "ospf": {
- "redistribute": [{"redist_type": "connected", "del_action": True}]
- }
- }
- }
- result = create_router_ospf(tgen, topo, ospf_red)
- assert result is True, "Testcase: Failed \n Error: {}".format(result)
-
-
# ##################################
# Test cases start here.
# ##################################
"advertised/exchaged via ospf"
)
for rtr in topo["routers"]:
- red_static(rtr)
- red_connected(rtr)
+ redistribute_ospf(tgen, topo, rtr, "static")
+ redistribute_ospf(tgen, topo, rtr, "connected")
for node in topo["routers"]:
input_dict = {
"r0": {
)
for rtr in topo["routers"]:
- ospf_red = {
- rtr: {"ospf": {"redistribute": [{"redist_type": "static", "delete": True}]}}
- }
- result = create_router_ospf(tgen, topo, ospf_red)
- assert result is True, "Testcase {} : Failed \n Error: {}".format(
- tc_name, result
- )
+ redistribute_ospf(tgen, topo, rtr, "static", delete=True)
input_dict = {
"r0": {
--- /dev/null
+{
+ "address_types": [
+ "ipv4",
+ "ipv6"
+ ],
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 30,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 29,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link0": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link0": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link0": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link0": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+#!/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.
+#
+"""
+ -Verify static route ECMP functionality with 8 next hop
+
+ -Verify static route functionality with 8 next hop different AD value
+
+ -Verify static route with tag option
+
+ -Verify BGP did not install the static route when it receive route
+ with local next hop
+
+"""
+import sys
+import json
+import time
+import os
+import pytest
+import random
+import platform
+from lib.topotest import version_cmp
+
+# 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
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ check_address_types,
+ step,
+ create_interfaces_cfg,
+ shutdown_bringup_interface,
+ stop_router,
+ start_router,
+ create_route_maps,
+ verify_ip_nht,
+)
+from lib.topolog import logger
+from lib.bgp import verify_bgp_convergence, create_router_bgp, verify_bgp_rib
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/static_routes_topo3_ebgp.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+
+# Global variables
+BGP_CONVERGENCE = False
+ADDR_TYPES = check_address_types()
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ "11.0.20.6/32",
+ "11.0.20.7/32",
+ "11.0.20.8/32",
+ ],
+ "ipv6": [
+ "2::1/128",
+ "2::2/128",
+ "2::3/128",
+ "2::4/128",
+ "2::5/128",
+ "2::6/128",
+ "2::7/128",
+ "2::8/128",
+ ],
+}
+PREFIX1 = {"ipv4": "110.0.20.1/32", "ipv6": "20::1/128"}
+NETWORK2 = {"ipv4": ["11.0.20.1/32"], "ipv6": ["2::1/128"]}
+NEXT_HOP_IP = []
+
+
+class CreateTopo(Topo):
+ """
+ Test CreateTopo - topology 1.
+
+ * `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):
+ """
+
+ Set 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.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ if version_cmp(platform.release(), "4.19") < 0:
+ error_msg = (
+ 'These tests will not run. (have kernel "{}", '
+ "requires kernel >= 4.19)".format(platform.release())
+ )
+ pytest.skip(error_msg)
+
+ # Checking BGP convergence
+ global BGP_CONVERGENCE
+ global ADDR_TYPES
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ # Api call verify whether BGP is converged
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ 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)
+
+
+def populate_nh():
+ NEXT_HOP_IP = {
+ "nh1": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link0"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0],
+ },
+ "nh2": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link1"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link1"]["ipv6"].split("/")[0],
+ },
+ "nh3": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link2"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link2"]["ipv6"].split("/")[0],
+ },
+ "nh4": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link3"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link3"]["ipv6"].split("/")[0],
+ },
+ "nh5": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link4"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link4"]["ipv6"].split("/")[0],
+ },
+ "nh6": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link5"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link5"]["ipv6"].split("/")[0],
+ },
+ "nh7": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link6"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link6"]["ipv6"].split("/")[0],
+ },
+ "nh8": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link7"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link7"]["ipv6"].split("/")[0],
+ },
+ }
+ return NEXT_HOP_IP
+
+
+#####################################################
+#
+# Tests starting
+#
+#####################################################
+
+
+def test_staticroute_with_ecmp_p0_tc3_ebgp(request):
+ """
+ Verify static route ECMP functionality with 8 next hop'
+
+ """
+ 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)
+
+ reset_config_on_routers(tgen)
+ NEXT_HOP_IP = populate_nh()
+
+ step("Configure 8 interfaces / links between R1 and R2,")
+ step(
+ "Configure IPv4 static route in R2 with 8 next hop"
+ "N1(21.1.1.2), N2(22.1.1.2), N3(23.1.1.2), N4(24.1.1.2),"
+ "N5(25.1.1.2), N6(26.1.1.2), N7(27.1.1.2),N8(28.1.1.2), Static"
+ "route next-hop present on R1"
+ )
+
+ step("Configure IBGP IPv4 peering between R2 and R3 router.")
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Verifying %s routes on r2", addr_type)
+ nh = [
+ NEXT_HOP_IP["nh1"][addr_type],
+ NEXT_HOP_IP["nh2"][addr_type],
+ NEXT_HOP_IP["nh3"][addr_type],
+ NEXT_HOP_IP["nh4"][addr_type],
+ NEXT_HOP_IP["nh5"][addr_type],
+ NEXT_HOP_IP["nh6"][addr_type],
+ NEXT_HOP_IP["nh7"][addr_type],
+ NEXT_HOP_IP["nh8"][addr_type],
+ ]
+
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes are"
+ " missing in RIB".format(tc_name)
+ step("Configure redistribute static in BGP on R2 router")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Remove the static route configured with nexthop N1 to N8, one"
+ "by one from running config"
+ )
+ for addr_type in ADDR_TYPES:
+ # delete static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes are"
+ " still present in RIB".format(tc_name)
+
+ step("Configure the static route with nexthop N1 to N8, one by" "one")
+
+ for addr_type in ADDR_TYPES:
+ # add static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes are"
+ " missing in RIB".format(tc_name)
+
+ step("Random shut of the nexthop interfaces")
+ randnum = random.randint(0, 7)
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ input_dict_5 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_5,
+ next_hop=nhip,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \n"
+ "Error: Routes are still present in RIB".format(tc_name)
+
+ step("Random no shut of the nexthop interfaces")
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_5, next_hop=nhip, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n"
+ "Error: Routes are missing in RIB".format(tc_name)
+
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+ start_router(tgen, "r2")
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes are"
+ " missing in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_staticroute_with_ecmp_with_diff_AD_p0_tc4_ebgp(request):
+ """
+ Verify static route ECMP functionality with 8 next hop
+
+ """
+ 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)
+
+ reset_config_on_routers(tgen)
+ NEXT_HOP_IP = populate_nh()
+
+ step("Configure 8 interfaces / links between R1 and R2,")
+ step("Configure IBGP IPv4 peering between R2 and R3 router.")
+ reset_config_on_routers(tgen)
+ NEXT_HOP_IP = populate_nh()
+ nh_all = {}
+ for addr_type in ADDR_TYPES:
+ nh_all[addr_type] = []
+ for nhp in range(1, 9):
+ nh_all[addr_type].append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ step(
+ "Configure IPv4 static route in R2 with 8 next hop"
+ "N1(21.1.1.2) AD 10, N2(22.1.1.2) AD 20, N3(23.1.1.2) AD 30,"
+ "N4(24.1.1.2) AD 40, N5(25.1.1.2) AD 50, N6(26.1.1.2) AD 60,"
+ "N7(27.1.1.2) AD 70, N8(28.1.1.2) AD 80, Static route next-hop"
+ "present on R1"
+ )
+ for addr_type in ADDR_TYPES:
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Verifying %s routes on r2", addr_type)
+
+ step(
+ "On R2, static route installed in RIB using "
+ "show ip route with 8 next hop, lowest AD nexthop is active"
+ )
+ step("On R2, static route with lowest AD nexthop installed in FIB")
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, "Testcase {} : Failed \nError: Route with "
+ " lowest AD is missing in RIB".format(tc_name)
+
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes "
+ " with high AD are active in RIB".format(tc_name)
+
+ step("Configure redistribute static in BGP on R2 router")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+
+ logger.info("Configuring redistribute static")
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After configuring them, route is always active with lowest AD"
+ "value and all the nexthop populated in RIB and FIB again "
+ )
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, "Testcase {} : Failed \nError: Route with "
+ " lowest AD is missing in RIB".format(tc_name)
+
+ step(
+ "Remove the static route configured with nexthop N1 to N8, one"
+ "by one from running config"
+ )
+
+ for addr_type in ADDR_TYPES:
+ # delete static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After removing the static route with N1 to N8 one by one, "
+ "route become active with next preferred nexthop and nexthop which "
+ "got removed is not shown in RIB and FIB"
+ )
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh_all[addr_type],
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes are"
+ " still present in RIB".format(tc_name)
+
+ step("Configure the static route with nexthop N1 to N8, one by" "one")
+ for addr_type in ADDR_TYPES:
+ # add static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("On R2, static route with lowest AD nexthop installed in FIB")
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, "Testcase {} : Failed \nError: Route with "
+ " lowest AD is missing in RIB".format(tc_name)
+
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes "
+ " with high AD are active in RIB".format(tc_name)
+
+ step("Random shut of the nexthop interfaces")
+ randnum = random.randint(0, 7)
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ input_dict_5 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_5,
+ next_hop=nhip,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \n"
+ "Error: Routes are still present in RIB".format(tc_name)
+
+ step("Random no shut of the nexthop interfaces")
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_5, next_hop=nhip, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n"
+ "Error: Routes are missing in RIB".format(tc_name)
+
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+ start_router(tgen, "r2")
+
+ step(
+ "After reload of FRR router, static route installed "
+ "in RIB and FIB properly ."
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, "Testcase {} : Failed \nError: Route with "
+ " lowest AD is missing in RIB".format(tc_name)
+
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes "
+ " with high AD are active in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_bgp_local_nexthop_p1_tc14_ebgp(request):
+ """
+ Verify BGP did not install the static route when it receive route
+ with local next hop
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ step("Configure BGP IPv4 session between R2 and R3")
+ step("Configure IPv4 static route on R2")
+ reset_config_on_routers(tgen)
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": topo["routers"]["r3"]["links"]["r2-link0"][
+ addr_type
+ ].split("/")[0],
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure redistribute static in the BGP")
+
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify R2 BGP table has IPv4 route")
+ dut = "r2"
+ result = verify_rib(tgen, addr_type, dut, input_dict_4)
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB of R2".format(tc_name)
+
+ step(" Verify route did not install in the R3 BGP table, RIB/FIB")
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4, expected=False)
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ " still present in BGP RIB of R2".format(tc_name)
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, expected=False)
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ " still present in RIB of R2".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_frr_intf_name_as_gw_gap_tc4_ebgp_p0(request):
+ """
+ Verify static route configure with interface name as gateway'
+ 'address'
+ """
+ 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)
+
+ reset_config_on_routers(tgen)
+
+ dut = "r1"
+ intf = topo["routers"]["r1"]["links"]["r2-link0"]["interface"]
+ nh = topo["routers"]["r1"]["links"]["r2-link0"]
+ ip_list = {
+ "ipv4": [(dut, intf, ["1.1.1.1/32"], nh["ipv4"].split("/")[0])],
+ "ipv6": [(dut, intf, ["4001::32/128"], nh["ipv6"].split("/")[0])],
+ }
+
+ step(
+ "Configure IPv4 and IPv6 static route in FRR with different next"
+ "hop (ens224 as nexthop))"
+ )
+ step("ip route 2.2.2.0/24 20.1.1.1 ens224 ----from FRR cli")
+ step("ipv6 route 2000::1/120 5000::1 ens224 ----from FRR cli")
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ nh = topo["routers"]["r2"]["links"]["r1-link0"][addr_type].split("/")[0]
+ input_dict_4 = {
+ "r1": {
+ "static_routes": [
+ {"network": ip_list[addr_type][0][2][0], "next_hop": nh}
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "IPv4 and IPv6 Static route added in FRR verify using "
+ "show ip route , nexthop is resolved using show nht"
+ )
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, next_hop=nh
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ input_dict_nh = {
+ "r1": {
+ nh: {
+ "Address": nh,
+ "resolvedVia": "connected",
+ "nexthops": {"nexthop1": {"Interfcae": intf}},
+ }
+ }
+ }
+ result = verify_ip_nht(tgen, input_dict_nh)
+ assert result is True, "Testcase {} : Failed \nError: Nexthop is"
+ " missing in RIB".format(tc_name)
+
+ step(
+ "Shut / no shut IPv4 and IPv6 static next hop interface from"
+ "kernel and FRR CLI"
+ )
+
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step(
+ "After shut of nexthop interface, IPv4 and IPv6 route got removed "
+ "from RIB verify using show ip route show nht"
+ )
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ protocol=protocol,
+ next_hop=nh,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes is"
+ " missing in RIB".format(tc_name)
+
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "After no shut route got added again in RIB /FIB using "
+ "show ip route nexthop is resolved using show nht"
+ )
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+ assert result is True, "Testcase {} : Failed".format(tc_name)
+
+ for addr_type in ADDR_TYPES:
+ nh = topo["routers"]["r2"]["links"]["r1-link0"][addr_type].split("/")[0]
+ input_dict_4 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": ip_list[addr_type][0][2][0],
+ "next_hop": nh,
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Removing FRR configured static route verify FRR route also "
+ "removed from FRR"
+ )
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ protocol=protocol,
+ next_hop=nh,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Routes"
+ " still present in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_static_route_with_tag_p0_tc_13_ebgp(request):
+ """
+ Verify static route with tag option
+
+ """
+ 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)
+
+ step("Configure 8 links between R1 and R2")
+ step("Configure 1 links between R2 and R3")
+ NEXT_HOP_IP = populate_nh()
+
+ step(
+ "Configure 2 IPv4 static route (S1 and S2) in R2 with same"
+ "next hop N1 28.1.1.2"
+ )
+ step("Configure static route S1 with tag 1 and static route S2 with" "tag2")
+ step("S1= ip route 10.1.1.1/24 28.1.1.2 tag 1")
+ step("S2= ip route 20.1.1.1/24 28.1.1.2 tag 2")
+ step("Enable redistribute static in BGP with route-map")
+ reset_config_on_routers(tgen)
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "tag": 4001,
+ },
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "tag": 4002,
+ },
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step("verify routes are present in RIB")
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes are"
+ " missing in RIB".format(tc_name)
+
+ step("Configure route-map on R2 with allow tag1 and deny tag2")
+
+ # Create route map
+ input_dict_3 = {
+ "r2": {
+ "route_maps": {
+ "rmap_match_tag_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": 10,
+ "match": {addr_type: {"tag": "4001"}},
+ },
+ {
+ "action": "deny",
+ "seq_id": 20,
+ "match": {addr_type: {"tag": "4002"}},
+ },
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link0": {
+ "route_maps": [
+ {
+ "name": "rmap_match_tag_1_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ },
+ "redistribute": [{"redist_type": "static"}],
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify static route S1 advetised in BGP table when tag1 permit"
+ "in route-map else it is denied"
+ )
+ dut = "r3"
+ input_dict_0 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "tag": 4002,
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_0, protocol=protocol, expected=False
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Route with "
+ "tag 4002 is still present in RIB".format(tc_name)
+
+ dut = "r2"
+ input_dict_1 = {
+ "r2": {"static_routes": [{"network": NETWORK[addr_type], "tag": 4001}]}
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_0, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \nError: Route with "
+ "tag 4001 is missing in RIB".format(tc_name)
+
+ step("Modify the route-map to allow tag2 and deny tag1")
+ # Create route map
+ input_dict_3 = {
+ "r2": {
+ "route_maps": {
+ "rmap_match_tag_1_{}".format(addr_type): [
+ {
+ "action": "deny",
+ "seq_id": 10,
+ "match": {addr_type: {"tag": "4001"}},
+ },
+ {
+ "action": "permit",
+ "seq_id": 20,
+ "match": {addr_type: {"tag": "4002"}},
+ },
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r3"
+ step(
+ "Verify static route S2 advertised in BGP table when tag2"
+ "permit in route-map else it is denied"
+ )
+ protocol = "bgp"
+ input_dict_0 = {
+ "r2": {"static_routes": [{"network": NETWORK2[addr_type], "tag": 4002}]}
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_0, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \nError: Route with "
+ "tag 4002 is missing in RIB".format(tc_name)
+
+ input_dict_1 = {
+ "r2": {"static_routes": [{"network": NETWORK[addr_type], "tag": 4001}]}
+ }
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_1, protocol=protocol, expected=False
+ )
+ assert result is not True, "Testcase {} : Failed \nError: Route with "
+ "tag 4001 is still present in RIB".format(tc_name, result)
+
+ step("Configure one static route with 2 ECMP nexthop N1 and N2")
+ step("For N1 configure tag 1 and for N2 configure tag 2")
+ step("S1= ip route 10.1.1.1/24 28.1.1.2 tag 1")
+ step("S1= ip route 10.1.1.1/24 29.1.1.2 tag 2")
+ step("configure the route-map to allow tag1 and deny tag 2")
+ step("Modify the route-map to allow tag2 and deny tag1")
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "tag": 4001,
+ },
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "tag": 4002,
+ },
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, fib=True
+ )
+ assert result is True, "Testcase {} : Failed \nError: Routes are"
+ " missing in RIB".format(tc_name)
+
+ step("shut/no shut of tag1 and tag2 nexthop")
+
+ intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step("configure one static route with 3 next-hop")
+ step("N1-tag1, N2-tag2, N3-tag3")
+ step("S1= ip route 10.1.1.1/24 28.1.1.2 tag 1")
+ step("S1= ip route 10.1.1.1/24 29.1.1.2 tag 2")
+ step("S1= ip route 10.1.1.1/24 28.1.1.2 tag 3")
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "tag": 4001,
+ },
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "tag": 4002,
+ },
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh3"][addr_type],
+ "tag": 4003,
+ },
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+
+ step("configure the route-map to allow tag2 & tag3 and deny tag1")
+ # Create route map
+ input_dict_3 = {
+ "r2": {
+ "route_maps": {
+ "rmap_match_tag_1_{}".format(addr_type): [
+ {
+ "action": "deny",
+ "seq_id": 10,
+ "match": {addr_type: {"tag": "4001"}},
+ },
+ {
+ "action": "permit",
+ "seq_id": 20,
+ "match": {addr_type: {"tag": "4002"}},
+ },
+ {
+ "action": "permit",
+ "seq_id": 30,
+ "match": {addr_type: {"tag": "4003"}},
+ },
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify static route advertised in BGP table with tag3"
+ " nexthop if tag2 is down"
+ )
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("shut / no shut of tag2 and tag3 next-hop")
+
+ intf = topo["routers"]["r2"]["links"]["r1-link1"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ intf = topo["routers"]["r2"]["links"]["r1-link2"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("shut/no shut of tag2 and tag3 nexthop")
+ intf = topo["routers"]["r2"]["links"]["r1-link1"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ intf = topo["routers"]["r2"]["links"]["r1-link2"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step("Verify after shut/noshut of nexthop BGP table updated correctly")
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ 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))
for ligne in lines:
self.dlines[ligne] = True
+
def get_normalized_es_id(line):
"""
The es-id or es-sys-mac need to be converted to lower case
break
return line
+
def get_normalized_mac_ip_line(line):
if line.startswith("evpn mh es"):
return get_normalized_es_id(line)
return line
+
class Config(object):
"""
log.debug(
"LINE %-50s: popping segment routing sub-context to ctx%-50s",
line,
- ctx_keys
+ ctx_keys,
)
elif line in ["exit-address-family", "exit", "exit-vnc"]:
log.debug(
"LINE %-50s: popping from subcontext to ctx%-50s",
line,
- ctx_keys
+ ctx_keys,
)
elif line in ["exit-vni", "exit-ldp-if"]:
self.save_contexts(ctx_keys, current_context_lines)
current_context_lines = []
log.debug(
- "LINE %-50s: entering segment routing sub-context, append to ctx_keys", line
+ "LINE %-50s: entering segment routing sub-context, append to ctx_keys",
+ line,
)
ctx_keys.append(line)
self.save_contexts(ctx_keys, current_context_lines)
current_context_lines = []
log.debug(
- "LINE %-50s: entering segment routing sub-context, append to ctx_keys", line
+ "LINE %-50s: entering segment routing sub-context, append to ctx_keys",
+ line,
)
ctx_keys.append(line)
self.save_contexts(ctx_keys, current_context_lines)
current_context_lines = []
log.debug(
- "LINE %-50s: entering segment routing sub-context, append to ctx_keys", line
+ "LINE %-50s: entering segment routing sub-context, append to ctx_keys",
+ line,
)
ctx_keys.append(line)
current_context_lines = []
main_ctx_key = copy.deepcopy(ctx_keys)
log.debug(
- "LINE %-50s: entering candidate-path sub-context, append to ctx_keys", line
+ "LINE %-50s: entering candidate-path sub-context, append to ctx_keys",
+ line,
)
ctx_keys.append(line)
current_context_lines = []
main_ctx_key = copy.deepcopy(ctx_keys)
log.debug(
- "LINE %-50s: entering pce-config sub-context, append to ctx_keys", line
+ "LINE %-50s: entering pce-config sub-context, append to ctx_keys",
+ line,
)
ctx_keys.append(line)
lines_to_add_to_del.append((ctx[0], None))
"""
- ip/ipv6 prefix-list can be specified without a seq number. However,
- the running config always adds 'seq x', where x is a number incremented
- by 5 for every element, to the prefix list. So, ignore such lines as
- well. Sample prefix-list lines:
+ ip/ipv6 prefix-lists and access-lists can be specified without a seq number.
+ However, the running config always adds 'seq x', where x is a number
+ incremented by 5 for every element of the prefix/access list.
+ So, ignore such lines as well. Sample prefix-list and acces-list lines:
ip prefix-list PR-TABLE-2 seq 5 permit 20.8.2.0/24 le 32
ip prefix-list PR-TABLE-2 seq 10 permit 20.8.2.0/24 le 32
ipv6 prefix-list vrfdev6-12 permit 2000:9:2::/64 gt 64
+ access-list FOO seq 5 permit 2.2.2.2/32
+ ipv6 access-list BAR seq 5 permit 2:2:2::2/128
"""
- re_ip_pfxlst = re.search(
- "^(ip|ipv6)(\s+prefix-list\s+)(\S+\s+)(seq \d+\s+)(permit|deny)(.*)$",
+ re_acl_pfxlst = re.search(
+ "^(ip |ipv6 |)(prefix-list|access-list)(\s+\S+\s+)(seq \d+\s+)(permit|deny)(.*)$",
ctx_keys[0],
)
- if re_ip_pfxlst:
+ if re_acl_pfxlst:
+ found = False
tmpline = (
- re_ip_pfxlst.group(1)
- + re_ip_pfxlst.group(2)
- + re_ip_pfxlst.group(3)
- + re_ip_pfxlst.group(5)
- + re_ip_pfxlst.group(6)
+ re_acl_pfxlst.group(1)
+ + re_acl_pfxlst.group(2)
+ + re_acl_pfxlst.group(3)
+ + re_acl_pfxlst.group(5)
+ + re_acl_pfxlst.group(6)
)
for ctx in lines_to_add:
if ctx[0][0] == tmpline:
lines_to_del_to_del.append((ctx_keys, None))
lines_to_add_to_del.append(((tmpline,), None))
+ found = True
+ """
+ If prefix-lists or access-lists are being deleted and
+ not added (see comment above), add command with 'no' to
+ lines_to_add and remove from lines_to_del to improve
+ scaling performance.
+ """
+ if found is False:
+ add_cmd = ("no " + ctx_keys[0],)
+ lines_to_add.append((add_cmd, None))
+ lines_to_del_to_del.append((ctx_keys, None))
if (
len(ctx_keys) == 3
# doing vtysh -c inefficient (and can time out.) For
# these commands, instead of adding them to lines_to_del,
# add the "no " version to lines_to_add.
- elif (
- running_ctx_keys[0].startswith("ip route")
- or running_ctx_keys[0].startswith("ipv6 route")
- or running_ctx_keys[0].startswith("access-list")
- or running_ctx_keys[0].startswith("ipv6 access-list")
- or running_ctx_keys[0].startswith("ip prefix-list")
- or running_ctx_keys[0].startswith("ipv6 prefix-list")
- ):
+ elif running_ctx_keys[0].startswith("ip route") or running_ctx_keys[
+ 0
+ ].startswith("ipv6 route"):
add_cmd = ("no " + running_ctx_keys[0],)
lines_to_add.append((add_cmd, None))
continue
# same thing for a pseudowire sub-context inside an l2vpn context
- elif (len(running_ctx_keys) > 1 and running_ctx_keys[0].startswith('l2vpn') and
- running_ctx_keys[1].startswith('member pseudowire') and
- (running_ctx_keys[:1], None) in lines_to_del):
+ elif (
+ len(running_ctx_keys) > 1
+ and running_ctx_keys[0].startswith("l2vpn")
+ and running_ctx_keys[1].startswith("member pseudowire")
+ and (running_ctx_keys[:1], None) in lines_to_del
+ ):
continue
# Segment routing and traffic engineering never need to be deleted
elif (
- running_ctx_keys[0].startswith('segment-routing')
+ running_ctx_keys[0].startswith("segment-routing")
and len(running_ctx_keys) < 3
):
continue
# Neither the pcep command
elif (
len(running_ctx_keys) == 3
- and running_ctx_keys[0].startswith('segment-routing')
- and running_ctx_keys[2].startswith('pcep')
+ and running_ctx_keys[0].startswith("segment-routing")
+ and running_ctx_keys[2].startswith("pcep")
):
continue
# use them, so add them to a separate array that is going to be appended at the end
elif (
len(running_ctx_keys) == 3
- and running_ctx_keys[0].startswith('segment-routing')
- and running_ctx_keys[2].startswith('segment-list')
+ and running_ctx_keys[0].startswith("segment-routing")
+ and running_ctx_keys[2].startswith("segment-list")
):
seglist_to_del.append((running_ctx_keys, None))
# we add them to a separate array that is going to be appended at the end
elif (
len(running_ctx_keys) == 3
- and running_ctx_keys[0].startswith('segment-routing')
- and running_ctx_keys[2].startswith('policy')
+ and running_ctx_keys[0].startswith("segment-routing")
+ and running_ctx_keys[2].startswith("policy")
):
pollist_to_del.append((running_ctx_keys, None))
# 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')
+ 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')
+ and running_ctx_keys[0].startswith("segment-routing")
+ and running_ctx_keys[3].startswith("pcc")
):
pcclist_to_del.append((running_ctx_keys, None))
# so add them to a separate array that is going to be appended at the end
if (
len(newconf_ctx_keys) == 3
- and newconf_ctx_keys[0].startswith('segment-routing')
- and newconf_ctx_keys[2].startswith('policy ')
- and line.startswith('candidate-path ')
+ and newconf_ctx_keys[0].startswith("segment-routing")
+ and newconf_ctx_keys[2].startswith("policy ")
+ and line.startswith("candidate-path ")
):
candidates_to_add.append((newconf_ctx_keys, line))
# so add them to a separate array that is going to be appended at the end
if (
len(newconf_ctx_keys) == 4
- and newconf_ctx_keys[0].startswith('segment-routing')
- and newconf_ctx_keys[3].startswith('candidate-path')
+ and newconf_ctx_keys[0].startswith("segment-routing")
+ and newconf_ctx_keys[3].startswith("candidate-path")
):
candidates_to_add.append((newconf_ctx_keys, None))
for line in newconf_ctx.lines:
else:
running.load_from_show_running(args.daemon)
-
-
(lines_to_add, lines_to_del) = compare_context_objects(newconf, running)
lines_to_configure = []
type string;
}
+ typedef prefix-list-ref {
+ type string;
+ }
+
grouping redistribute-attributes {
description
"Common optional attributes of any redistribute entry.";
}
}
+ grouping global-config-remote-lfa {
+ container remote-lfa {
+ description
+ "Remote LFA configuration.";
+
+ leaf prefix-list {
+ type prefix-list-ref;
+ description
+ "Filter PQ node router ID based on prefix list.";
+ }
+ }
+ }
+
grouping interface-config-lfa {
container lfa {
description
}
}
+ grouping interface-config-remote-lfa {
+ container remote-lfa {
+ description
+ "Remote LFA configuration.";
+
+ leaf enable {
+ type boolean;
+ default false;
+ description
+ "Enables remote LFA computation using LDP tunnels.";
+ must ". = 'false' or ../../lfa/enable = 'true'" {
+ error-message
+ "Remote LFA depends on classic LFA being configured in the interface.";
+ }
+
+ }
+ leaf maximum-metric {
+ type uint32 {
+ range "1..16777215";
+ }
+ description
+ "Limit remote LFA node selection within the metric.";
+ }
+ }
+ }
+
grouping interface-config-ti-lfa {
container ti-lfa {
description
"Can't enable both classic LFA and TI-LFA in the same interface.";
}
uses interface-config-lfa;
+ uses interface-config-remote-lfa;
uses interface-config-ti-lfa;
}
container level-2 {
"Can't enable both classic LFA and TI-LFA in the same interface.";
}
uses interface-config-lfa;
+ uses interface-config-remote-lfa;
uses interface-config-ti-lfa;
}
}
description
"Level-1 IP Fast-reroute configuration.";
uses global-config-lfa;
+ uses global-config-remote-lfa;
}
container level-2 {
description
"Level-2 IP Fast-reroute configuration.";
uses global-config-lfa;
+ uses global-config-remote-lfa;
}
}
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
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
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
int dplane_show_provs_helper(struct vty *vty, bool detailed)
{
struct zebra_dplane_provider *prov;
- uint64_t in, in_max, out, out_max;
+ uint64_t in, in_q, in_max, out, out_q, out_max;
vty_out(vty, "Zebra dataplane providers:\n");
in = atomic_load_explicit(&prov->dp_in_counter,
memory_order_relaxed);
+ in_q = atomic_load_explicit(&prov->dp_in_queued,
+ memory_order_relaxed);
in_max = atomic_load_explicit(&prov->dp_in_max,
memory_order_relaxed);
out = atomic_load_explicit(&prov->dp_out_counter,
memory_order_relaxed);
+ out_q = atomic_load_explicit(&prov->dp_out_queued,
+ memory_order_relaxed);
out_max = atomic_load_explicit(&prov->dp_out_max,
memory_order_relaxed);
- vty_out(vty,
- "%s (%u): in: %" PRIu64 ", q_max: %" PRIu64
- ", out: %" PRIu64 ", q_max: %" PRIu64 "\n",
- prov->dp_name, prov->dp_id, in, in_max, out, out_max);
+ vty_out(vty, "%s (%u): in: %"PRIu64", q: %"PRIu64", q_max: %"PRIu64", out: %"PRIu64", q: %"PRIu64", q_max: %"PRIu64"\n",
+ prov->dp_name, prov->dp_id, in, in_q, in_max,
+ out, out_q, out_max);
DPLANE_LOCK();
prov = TAILQ_NEXT(prov, dp_prov_link);
void dplane_provider_enqueue_out_ctx(struct zebra_dplane_provider *prov,
struct zebra_dplane_ctx *ctx)
{
+ uint64_t curr, high;
+
dplane_provider_lock(prov);
TAILQ_INSERT_TAIL(&(prov->dp_ctx_out_q), ctx,
zd_q_entries);
+ /* Maintain out-queue counters */
+ atomic_fetch_add_explicit(&(prov->dp_out_queued), 1,
+ memory_order_relaxed);
+ curr = atomic_load_explicit(&prov->dp_out_queued,
+ memory_order_relaxed);
+ high = atomic_load_explicit(&prov->dp_out_max,
+ memory_order_relaxed);
+ if (curr > high)
+ atomic_store_explicit(&prov->dp_out_max, curr,
+ memory_order_relaxed);
+
dplane_provider_unlock(prov);
atomic_fetch_add_explicit(&(prov->dp_out_counter), 1,
#include "prefix.h"
#include "vlan.h"
#include "json.h"
+#include "printfrr.h"
#include "zebra/zserv.h"
#include "zebra/debug.h"
}
}
+#define MAC_BUF_SIZE 256
+static char *zebra_evpn_zebra_mac_flag_dump(struct zebra_mac_t_ *mac, char *buf,
+ size_t len)
+{
+ if (mac->flags == 0) {
+ snprintfrr(buf, len, "None ");
+ return buf;
+ }
+
+ snprintfrr(
+ buf, len, "%s%s%s%s%s%s%s%s%s%s%s%s",
+ CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) ? "LOC " : "",
+ CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) ? "REM " : "",
+ CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO) ? "AUTO " : "",
+ CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY) ? "STICKY " : "",
+ CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_RMAC) ? "REM Router "
+ : "",
+ CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW) ? "Default GW " : "",
+ CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW) ? "REM DEF GW "
+ : "",
+ CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE) ? "DUP " : "",
+ CHECK_FLAG(mac->flags, ZEBRA_MAC_FPM_SENT) ? "FPM " : "",
+ CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE) ? "LOC Active "
+ : "",
+ CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_PROXY) ? "PROXY " : "",
+ CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE)
+ ? "LOC Inactive "
+ : "");
+ return buf;
+}
+
static int zebra_evpn_dad_mac_auto_recovery_exp(struct thread *t)
{
struct zebra_vrf *zvrf = NULL;
if (!mac)
return 0;
- if (IS_ZEBRA_DEBUG_VXLAN)
+ if (IS_ZEBRA_DEBUG_VXLAN) {
+ char mac_buf[MAC_BUF_SIZE];
+
zlog_debug(
- "%s: duplicate addr mac %s flags 0x%x learn count %u host count %u auto recovery expired",
+ "%s: duplicate addr mac %s flags %slearn count %u host count %u auto recovery expired",
__func__,
prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
- mac->flags, mac->dad_count, listcount(mac->neigh_list));
+ zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
+ sizeof(mac_buf)),
+ mac->dad_count, listcount(mac->neigh_list));
+ }
/* Remove all IPs as duplicate associcated with this MAC */
for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) {
* Remote MAC event -> hold on installing it.
*/
if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) {
- if (IS_ZEBRA_DEBUG_VXLAN)
+ if (IS_ZEBRA_DEBUG_VXLAN) {
+ char mac_buf[MAC_BUF_SIZE];
+
zlog_debug(
- "%s: duplicate addr MAC %s flags 0x%x skip update to client, learn count %u recover time %u",
+ "%s: duplicate addr MAC %s flags %sskip update to client, learn count %u recover time %u",
__func__,
prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
- mac->flags, mac->dad_count,
- zvrf->dad_freeze_time);
-
+ zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
+ sizeof(mac_buf)),
+ mac->dad_count, zvrf->dad_freeze_time);
+ }
/* For duplicate MAC do not update
* client but update neigh due to
* this MAC update.
}
if (reset_params) {
- if (IS_ZEBRA_DEBUG_VXLAN)
+ if (IS_ZEBRA_DEBUG_VXLAN) {
+ char mac_buf[MAC_BUF_SIZE];
+
zlog_debug(
- "%s: duplicate addr MAC %s flags 0x%x detection time passed, reset learn count %u",
+ "%s: duplicate addr MAC %s flags %sdetection time passed, reset learn count %u",
__func__,
prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
- mac->flags, mac->dad_count);
+ zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
+ sizeof(mac_buf)),
+ mac->dad_count);
+ }
mac->dad_count = 0;
/* Start dup. addr detection (DAD) start time,
/* Start auto recovery timer for this MAC */
THREAD_OFF(mac->dad_mac_auto_recovery_timer);
if (zvrf->dad_freeze && zvrf->dad_freeze_time) {
- if (IS_ZEBRA_DEBUG_VXLAN)
+ if (IS_ZEBRA_DEBUG_VXLAN) {
+ char mac_buf[MAC_BUF_SIZE];
+
zlog_debug(
- "%s: duplicate addr MAC %s flags 0x%x auto recovery time %u start",
+ "%s: duplicate addr MAC %s flags %sauto recovery time %u start",
__func__,
prefix_mac2str(&mac->macaddr, buf,
sizeof(buf)),
- mac->flags, zvrf->dad_freeze_time);
+ zebra_evpn_zebra_mac_flag_dump(
+ mac, mac_buf, sizeof(mac_buf)),
+ zvrf->dad_freeze_time);
+ }
thread_add_timer(zrouter.master,
zebra_evpn_dad_mac_auto_recovery_exp,
}
static char *zebra_evpn_print_mac_flags(zebra_mac_t *mac, char *flags_buf,
- uint32_t flags_buf_sz)
+ size_t flags_buf_sz)
{
snprintf(flags_buf, flags_buf_sz, "%s%s%s%s",
mac->sync_neigh_cnt ?
/* Write packet size. */
stream_putw_at(s, 0, stream_get_endp(s));
- if (IS_ZEBRA_DEBUG_VXLAN)
+ if (IS_ZEBRA_DEBUG_VXLAN) {
+ char flag_buf[MACIP_BUF_SIZE];
+
zlog_debug(
- "Send MACIP %s f 0x%x MAC %s IP %s seq %u L2-VNI %u ESI %s to %s",
- (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", flags,
+ "Send MACIP %s f %s MAC %s IP %s seq %u L2-VNI %u ESI %s to %s",
+ (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del",
+ zclient_evpn_dump_macip_flags(flags, flag_buf,
+ sizeof(flag_buf)),
prefix_mac2str(macaddr, buf, sizeof(buf)),
ipaddr2str(ip, buf2, sizeof(buf2)), seq, vni,
es ? es->esi_str : "-",
zebra_route_string(client->proto));
+ }
if (cmd == ZEBRA_MACIP_ADD)
client->macipadd_cnt++;
mac->uptime = monotime(NULL);
if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) {
char buf[ETHER_ADDR_STRLEN];
+ char mac_buf[MAC_BUF_SIZE];
- zlog_debug("%s: MAC %s flags 0x%x", __func__,
+ zlog_debug("%s: MAC %s flags %s", __func__,
prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
- mac->flags);
+ zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
+ sizeof(mac_buf)));
}
return mac;
}
if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) {
char buf[ETHER_ADDR_STRLEN];
+ char mac_buf[MAC_BUF_SIZE];
- zlog_debug("%s: MAC %s flags 0x%x", __func__,
+ zlog_debug("%s: MAC %s flags %s", __func__,
prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
- mac->flags);
+ zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
+ sizeof(mac_buf)));
}
/* If the MAC is freed before the neigh we will end up
&& !listcount(mac->neigh_list)) {
if (IS_ZEBRA_DEBUG_VXLAN) {
char buf[ETHER_ADDR_STRLEN];
+ char mac_buf[MAC_BUF_SIZE];
zlog_debug(
- "%s: Del MAC %s flags 0x%x", __func__,
+ "%s: Del MAC %s flags %s", __func__,
prefix_mac2str(&mac->macaddr, buf, sizeof(buf)),
- mac->flags);
+ zebra_evpn_zebra_mac_flag_dump(
+ mac, mac_buf, sizeof(mac_buf)));
}
wctx->uninstall = 0;
zebra_evpn_mac_get_access_info(mac, &ifp, &vid);
if (!ifp) {
- if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) {
+ char mac_buf[MAC_BUF_SIZE];
+
zlog_debug(
- "%s: dp-install sync-mac vni %u mac %pEA es %s 0x%x %sskipped, no access-port",
+ "%s: dp-install sync-mac vni %u mac %pEA es %s %s%sskipped, no access-port",
caller, zevpn->vni, &mac->macaddr,
- mac->es ? mac->es->esi_str : "-", mac->flags,
+ mac->es ? mac->es->esi_str : "-",
+ zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
+ sizeof(mac_buf)),
set_inactive ? "inactive " : "");
+ }
return -1;
}
zif = ifp->info;
br_ifp = zif->brslave_info.br_if;
if (!br_ifp) {
- if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) {
+ char mac_buf[MAC_BUF_SIZE];
+
zlog_debug(
- "%s: dp-install sync-mac vni %u mac %pEA es %s 0x%x %sskipped, no br",
+ "%s: dp-install sync-mac vni %u mac %pEA es %s %s%sskipped, no br",
caller, zevpn->vni, &mac->macaddr,
- mac->es ? mac->es->esi_str : "-", mac->flags,
+ mac->es ? mac->es->esi_str : "-",
+ zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
+ sizeof(mac_buf)),
set_inactive ? "inactive " : "");
+ }
return -1;
}
* supported and if the local ES is oper-down.
*/
if (mac->es && zebra_evpn_es_local_mac_via_network_port(mac->es)) {
- if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) {
+ char mac_buf[MAC_BUF_SIZE];
+
zlog_debug(
- "dp-%s sync-nw-mac vni %u mac %pEA es %s 0x%x %s",
+ "dp-%s sync-nw-mac vni %u mac %pEA es %s %s%s",
set_static ? "install" : "uninstall",
zevpn->vni, &mac->macaddr,
- mac->es ? mac->es->esi_str : "-", mac->flags,
+ mac->es ? mac->es->esi_str : "-",
+ zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
+ sizeof(mac_buf)),
set_inactive ? "inactive " : "");
+ }
if (set_static)
/* XXX - old_static needs to be computed more
* accurately
return 0;
}
- if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
- zlog_debug(
- "dp-install sync-mac vni %u mac %pEA es %s 0x%x %s%s",
- zevpn->vni, &mac->macaddr,
- mac->es ? mac->es->esi_str : "-", mac->flags,
- set_static ? "static " : "",
- set_inactive ? "inactive " : "");
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) {
+ char mac_buf[MAC_BUF_SIZE];
+
+ zlog_debug("dp-install sync-mac vni %u mac %pEA es %s %s%s%s",
+ zevpn->vni, &mac->macaddr,
+ mac->es ? mac->es->esi_str : "-",
+ zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
+ sizeof(mac_buf)),
+ set_static ? "static " : "",
+ set_inactive ? "inactive " : "");
+ }
dplane_local_mac_add(ifp, br_ifp, vid, &mac->macaddr, sticky,
set_static, set_inactive);
new_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags);
new_static = zebra_evpn_mac_is_static(mac);
- if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) {
+ char mac_buf[MAC_BUF_SIZE];
+
zlog_debug(
- "sync-mac vni %u mac %s es %s 0x%x hold expired",
+ "sync-mac vni %u mac %s es %s %shold expired",
mac->zevpn->vni,
prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)),
- mac->es ? mac->es->esi_str : "-", mac->flags);
+ mac->es ? mac->es->esi_str : "-",
+ zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
+ sizeof(mac_buf)));
+ }
/* re-program the local mac in the dataplane if the mac is no
* longer static
if (mac->hold_timer)
return;
- if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) {
+ char mac_buf[MAC_BUF_SIZE];
+
zlog_debug(
- "sync-mac vni %u mac %s es %s 0x%x hold started",
+ "sync-mac vni %u mac %s es %s %shold started",
mac->zevpn->vni,
prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)),
- mac->es ? mac->es->esi_str : "-", mac->flags);
+ mac->es ? mac->es->esi_str : "-",
+ zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
+ sizeof(mac_buf)));
+ }
thread_add_timer(zrouter.master, zebra_evpn_mac_hold_exp_cb, mac,
zmh_info->mac_hold_time, &mac->hold_timer);
}
if (!mac->hold_timer)
return;
- if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) {
+ char mac_buf[MAC_BUF_SIZE];
+
zlog_debug(
- "sync-mac vni %u mac %s es %s 0x%x hold stopped",
+ "sync-mac vni %u mac %s es %s %shold stopped",
mac->zevpn->vni,
prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)),
- mac->es ? mac->es->esi_str : "-", mac->flags);
+ mac->es ? mac->es->esi_str : "-",
+ zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
+ sizeof(mac_buf)));
+ }
+
THREAD_OFF(mac->hold_timer);
}
bool old_static;
bool new_static;
- if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) {
+ char mac_buf[MAC_BUF_SIZE];
+
zlog_debug(
- "sync-mac del vni %u mac %s es %s seq %d f 0x%x",
+ "sync-mac del vni %u mac %s es %s seq %d f %s",
mac->zevpn->vni,
prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)),
mac->es ? mac->es->esi_str : "-", mac->loc_seq,
- mac->flags);
+ zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
+ sizeof(mac_buf)));
+ }
+
old_static = zebra_evpn_mac_is_static(mac);
UNSET_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_PROXY);
if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE))
*/
if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)
&& !zebra_evpn_mac_is_ready_for_bgp(mac->flags)) {
- if (IS_ZEBRA_DEBUG_EVPN_MH_MAC || IS_ZEBRA_DEBUG_VXLAN)
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC
+ || IS_ZEBRA_DEBUG_VXLAN) {
+ char mac_buf[MAC_BUF_SIZE];
+
zlog_debug(
- "%s-macip accept vni %u %s-mac %s%s%s lower seq %u f 0x%x",
+ "%s-macip accept vni %u %s-mac %s%s%s lower seq %u f %s",
sync ? "sync" : "rem", zevpn->vni,
n_type,
prefix_mac2str(&mac->macaddr, macbuf,
ipa_len ? ipaddr2str(ipaddr, ipbuf,
sizeof(ipbuf))
: "",
- tmp_seq, mac->flags);
+ tmp_seq,
+ zebra_evpn_zebra_mac_flag_dump(
+ mac, mac_buf, sizeof(mac_buf)));
+ }
+
return true;
}
- if (IS_ZEBRA_DEBUG_EVPN_MH_MAC || IS_ZEBRA_DEBUG_VXLAN)
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC || IS_ZEBRA_DEBUG_VXLAN) {
+ char mac_buf[MAC_BUF_SIZE];
+
zlog_debug(
- "%s-macip ignore vni %u %s-mac %s%s%s as existing has higher seq %u f 0x%x",
+ "%s-macip ignore vni %u %s-mac %s%s%s as existing has higher seq %u f %s",
sync ? "sync" : "rem", zevpn->vni, n_type,
prefix_mac2str(&mac->macaddr, macbuf,
sizeof(macbuf)),
ipa_len ? ipaddr2str(ipaddr, ipbuf,
sizeof(ipbuf))
: "",
- tmp_seq, mac->flags);
+ tmp_seq,
+ zebra_evpn_zebra_mac_flag_dump(
+ mac, mac_buf, sizeof(mac_buf)));
+ }
return false;
}
memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
mac->flags = new_flags;
- if (IS_ZEBRA_DEBUG_EVPN_MH_MAC && (old_flags != new_flags))
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC && (old_flags != new_flags)) {
+ char mac_buf[MAC_BUF_SIZE], omac_buf[MAC_BUF_SIZE];
+ struct zebra_mac_t_ omac;
+
+ omac.flags = old_flags;
zlog_debug(
- "sync-mac vni %u mac %s old_f 0x%x new_f 0x%x",
+ "sync-mac vni %u mac %s old_f %snew_f %s",
zevpn->vni,
prefix_mac2str(macaddr, macbuf, sizeof(macbuf)),
- old_flags, mac->flags);
+ zebra_evpn_zebra_mac_flag_dump(
+ &omac, omac_buf, sizeof(omac_buf)),
+ zebra_evpn_zebra_mac_flag_dump(
+ mac, mac_buf, sizeof(mac_buf)));
+ }
/* update es */
es_change = zebra_evpn_es_mac_ref(mac, esi);
inform_bgp = true;
}
- if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
- zlog_debug("sync-mac %s vni %u mac %s es %s seq %d f 0x%x%s%s",
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) {
+ char mac_buf[MAC_BUF_SIZE];
+
+ zlog_debug("sync-mac %s vni %u mac %s es %s seq %d f %s%s%s",
ctx->mac_created ? "created" : "updated", zevpn->vni,
prefix_mac2str(macaddr, macbuf, sizeof(macbuf)),
mac->es ? mac->es->esi_str : "-", mac->loc_seq,
- mac->flags, inform_bgp ? " inform_bgp" : "",
+ zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
+ sizeof(mac_buf)),
+ inform_bgp ? "inform_bgp" : "",
inform_dataplane ? " inform_dp" : "");
+ }
if (inform_bgp)
zebra_evpn_mac_send_add_del_to_client(mac, old_bgp_ready,
if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
/* force drop the sync flags */
old_static = zebra_evpn_mac_is_static(mac);
- if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) {
+ char mac_buf[MAC_BUF_SIZE];
+
zlog_debug(
- "sync-mac->remote vni %u mac %s es %s seq %d f 0x%x",
+ "sync-mac->remote vni %u mac %s es %s seq %d f %s",
zevpn->vni,
prefix_mac2str(macaddr, buf,
sizeof(buf)),
mac->es ? mac->es->esi_str : "-",
- mac->loc_seq, mac->flags);
+ mac->loc_seq,
+ zebra_evpn_zebra_mac_flag_dump(
+ mac, mac_buf, sizeof(mac_buf)));
+ }
+
zebra_evpn_mac_clear_sync_info(mac);
zebra_evpn_mac_send_del_to_client(zevpn->vni, macaddr,
mac->flags,
SET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
inform_client = true;
} else {
- if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) {
+ char mac_buf[MAC_BUF_SIZE];
+
zlog_debug(
- "UPD %sMAC %s intf %s(%u) VID %u -> VNI %u %scurFlags 0x%x",
+ "UPD %sMAC %s intf %s(%u) VID %u -> VNI %u %scurFlags %s",
sticky ? "sticky " : "",
prefix_mac2str(macaddr, buf, sizeof(buf)),
ifp->name, ifp->ifindex, vid, zevpn->vni,
local_inactive ? "local-inactive " : "",
- mac->flags);
+ zebra_evpn_zebra_mac_flag_dump(
+ mac, mac_buf, sizeof(mac_buf)));
+ }
if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
struct interface *old_ifp;
*/
if ((old_local_inactive != local_inactive)
|| (new_bgp_ready != old_bgp_ready)) {
- if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) {
+ char mac_buf[MAC_BUF_SIZE];
+
zlog_debug(
- "local mac vni %u mac %s es %s seq %d f 0x%x%s",
+ "local mac vni %u mac %s es %s seq %d f %s%s",
zevpn->vni,
prefix_mac2str(macaddr, buf, sizeof(buf)),
mac->es ? mac->es->esi_str : "", mac->loc_seq,
- mac->flags,
- local_inactive ? " local-inactive" : "");
+ zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
+ sizeof(mac_buf)),
+ local_inactive ? "local-inactive" : "");
+ }
+
if (!is_dup_detect)
inform_client = true;
}
if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL))
return 0;
- if (IS_ZEBRA_DEBUG_VXLAN)
+ if (IS_ZEBRA_DEBUG_VXLAN) {
+ char mac_buf[MAC_BUF_SIZE];
+
zlog_debug(
- "DEL MAC %s intf %s(%u) VID %u -> VNI %u seq %u flags 0x%x nbr count %u",
+ "DEL MAC %s intf %s(%u) VID %u -> VNI %u seq %u flags %snbr count %u",
prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name,
ifp->ifindex, mac->fwd_info.local.vid, zevpn->vni,
- mac->loc_seq, mac->flags, listcount(mac->neigh_list));
+ mac->loc_seq,
+ zebra_evpn_zebra_mac_flag_dump(mac, mac_buf,
+ sizeof(mac_buf)),
+ listcount(mac->neigh_list));
+ }
old_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags);
if (zebra_evpn_mac_is_static(mac)) {
*/
memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
- if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) {
+ char mac_buf[MAC_BUF_SIZE];
+
zlog_debug(
- "re-add sync-mac vni %u mac %s es %s seq %d f 0x%x",
+ "re-add sync-mac vni %u mac %s es %s seq %d f %s",
zevpn->vni,
prefix_mac2str(macaddr, buf, sizeof(buf)),
mac->es ? mac->es->esi_str : "-", mac->loc_seq,
- mac->flags);
+ zebra_evpn_zebra_mac_flag_dump(
+ mac, mac_buf, sizeof(mac_buf)));
+ }
/* inform-bgp about change in local-activity if any */
if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE)) {
/* MAC address. */
struct ethaddr macaddr;
+ /* When modifying flags please fixup zebra_evpn_zebra_mac_flag_dump */
uint32_t flags;
#define ZEBRA_MAC_LOCAL 0x01
#define ZEBRA_MAC_REMOTE 0x02
struct interface *ifp;
route_map_result_t ret = RMAP_PERMITMATCH;
int family;
- char buf[SRCDEST2STR_BUFFER];
const struct prefix *p, *src_p;
struct zebra_vrf *zvrf;
zvrf, re->tag);
if (ret == RMAP_DENYMATCH) {
if (IS_ZEBRA_DEBUG_RIB) {
- srcdest_rnode2str(rn, buf, sizeof(buf));
zlog_debug(
- "%u:%s: Filtering out with NH out %s due to route map",
- re->vrf_id, buf,
+ "%u:%pRN: Filtering out with NH out %s due to route map",
+ re->vrf_id, rn,
ifindex2ifname(nexthop->ifindex,
nexthop->vrf_id));
}
#include "workqueue.h"
#include "nexthop_group_private.h"
#include "frr_pthread.h"
+#include "printfrr.h"
#include "zebra/zebra_router.h"
#include "zebra/connected.h"
zlog(priority, "%s: (%u:%u):%s: %s", _func, vrf_id, table, buf, msgbuf);
}
+static char *_dump_re_status(const struct route_entry *re, char *buf,
+ size_t len)
+{
+ if (re->status == 0) {
+ snprintfrr(buf, len, "None ");
+ return buf;
+ }
+
+ snprintfrr(
+ buf, len, "%s%s%s%s%s%s%s",
+ CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED) ? "Removed " : "",
+ CHECK_FLAG(re->status, ROUTE_ENTRY_CHANGED) ? "Changed " : "",
+ CHECK_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED)
+ ? "Label Changed "
+ : "",
+ CHECK_FLAG(re->status, ROUTE_ENTRY_QUEUED) ? "Queued " : "",
+ CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED) ? "Installed "
+ : "",
+ CHECK_FLAG(re->status, ROUTE_ENTRY_FAILED) ? "Failed " : "",
+ CHECK_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG) ? "Fib NHG "
+ : "");
+ return buf;
+}
+
#define rnode_debug(node, vrf_id, ...) \
_rnode_zlog(__func__, vrf_id, node, LOG_DEBUG, __VA_ARGS__)
#define rnode_info(node, ...) \
}
RNODE_FOREACH_RE_SAFE (rn, re, next) {
- if (IS_ZEBRA_DEBUG_RIB_DETAILED)
+ if (IS_ZEBRA_DEBUG_RIB_DETAILED) {
+ char flags_buf[128];
+ char status_buf[128];
+
zlog_debug(
- "%s(%u:%u):%s: Examine re %p (%s) status %x flags %x dist %d metric %d",
+ "%s(%u:%u):%s: Examine re %p (%s) status: %sflags: %sdist %d metric %d",
VRF_LOGNAME(vrf), vrf_id, re->table, buf, re,
- zebra_route_string(re->type), re->status,
- re->flags, re->distance, re->metric);
+ zebra_route_string(re->type),
+ _dump_re_status(re, status_buf,
+ sizeof(status_buf)),
+ zclient_dump_route_flags(re->flags, flags_buf,
+ sizeof(flags_buf)),
+ re->distance, re->metric);
+ }
/* Currently selected re. */
if (CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED)) {
*/
if (CHECK_FLAG(re->status, ROUTE_ENTRY_CHANGED)) {
if (!nexthop_active_update(rn, re)) {
+ const struct prefix *p;
+ struct rib_table_info *info;
+
if (re->type == ZEBRA_ROUTE_TABLE) {
/* XXX: HERE BE DRAGONS!!!!!
* In all honesty, I have not yet
ROUTE_ENTRY_REMOVED);
}
+ info = srcdest_rnode_table_info(rn);
+ srcdest_rnode_prefixes(rn, &p, NULL);
+ zsend_route_notify_owner(re, p,
+ ZAPI_ROUTE_FAIL_INSTALL,
+ info->afi, info->safi);
continue;
}
} else {
bool is_srcdst = src_p && src_p->prefixlen;
char straddr[PREFIX_STRLEN];
char srcaddr[PREFIX_STRLEN];
+ char flags_buf[128];
+ char status_buf[128];
struct nexthop *nexthop;
struct vrf *vrf = vrf_lookup_by_id(re->vrf_id);
struct nexthop_group *nhg;
zlog_debug("%s: uptime == %lu, type == %u, instance == %d, table == %d",
straddr, (unsigned long)re->uptime, re->type, re->instance,
re->table);
- zlog_debug("%s: metric == %u, mtu == %u, distance == %u, flags == %u, status == %u",
- straddr, re->metric, re->mtu, re->distance, re->flags,
- re->status);
+ zlog_debug(
+ "%s: metric == %u, mtu == %u, distance == %u, flags == %sstatus == %s",
+ straddr, re->metric, re->mtu, re->distance,
+ zclient_dump_route_flags(re->flags, flags_buf,
+ sizeof(flags_buf)),
+ _dump_re_status(re, status_buf, sizeof(status_buf)));
zlog_debug("%s: nexthop_num == %u, nexthop_active_num == %u", straddr,
nexthop_group_nexthop_num(&(re->nhe->nhg)),
nexthop_group_active_nexthop_num(&(re->nhe->nhg)));
struct nhg_hash_entry *nhe = NULL;
struct route_table *table;
struct route_node *rn;
- struct route_entry *same = NULL;
+ struct route_entry *same = NULL, *first_same = NULL;
int ret = 0;
+ int same_count = 0;
+ rib_dest_t *dest;
if (!re || !re_nhe)
return -1;
* for the install don't do a route replace.
*/
RNODE_FOREACH_RE (rn, same) {
- if (CHECK_FLAG(same->status, ROUTE_ENTRY_REMOVED))
+ if (CHECK_FLAG(same->status, ROUTE_ENTRY_REMOVED)) {
+ same_count++;
continue;
+ }
/* Compare various route_entry properties */
- if (rib_compare_routes(re, same))
- break;
+ if (rib_compare_routes(re, same)) {
+ same_count++;
+
+ if (first_same == NULL)
+ first_same = same;
+ }
}
+ same = first_same;
+
/* If this route is kernel/connected route, notify the dataplane. */
if (RIB_SYSTEM_ROUTE(re)) {
/* Notify dataplane */
/* Link new re to node.*/
if (IS_ZEBRA_DEBUG_RIB) {
rnode_debug(rn, re->vrf_id,
- "Inserting route rn %p, re %p (%s) existing %p",
- rn, re, zebra_route_string(re->type), same);
+ "Inserting route rn %p, re %p (%s) existing %p, same_count %d",
+ rn, re, zebra_route_string(re->type), same,
+ same_count);
if (IS_ZEBRA_DEBUG_RIB_DETAILED)
route_entry_dump(p, src_p, re);
if (same)
rib_delnode(rn, same);
+ /* See if we can remove some RE entries that are queued for
+ * removal, but won't be considered in rib processing.
+ */
+ dest = rib_dest_from_rnode(rn);
+ RNODE_FOREACH_RE_SAFE (rn, re, same) {
+ if (CHECK_FLAG(re->status, ROUTE_ENTRY_REMOVED)) {
+ /* If the route was used earlier, must retain it. */
+ if (dest && re == dest->selected_fib)
+ continue;
+
+ if (IS_ZEBRA_DEBUG_RIB)
+ rnode_debug(rn, re->vrf_id, "rn %p, removing unneeded re %p",
+ rn, re);
+
+ rib_unlink(rn, re);
+ }
+ }
+
route_unlock_node(rn);
return ret;
}