diff options
23 files changed, 1568 insertions, 192 deletions
diff --git a/bgpd/bgp_damp.c b/bgpd/bgp_damp.c index f46d416c3..b740979b8 100644 --- a/bgpd/bgp_damp.c +++ b/bgpd/bgp_damp.c @@ -37,19 +37,115 @@ #include "bgpd/bgp_advertise.h" #include "bgpd/bgp_vty.h" -/* Global variable to access damping configuration */ -static struct bgp_damp_config damp[AFI_MAX][SAFI_MAX]; +static void bgp_reuselist_add(struct reuselist *list, + struct bgp_damp_info *info) +{ + struct reuselist_node *new_node; + + assert(info); + new_node = XCALLOC(MTYPE_BGP_DAMP_REUSELIST, sizeof(*new_node)); + new_node->info = info; + SLIST_INSERT_HEAD(list, new_node, entry); +} + +static void bgp_reuselist_del(struct reuselist *list, + struct reuselist_node **node) +{ + if ((*node) == NULL) + return; + assert(list && node && *node); + SLIST_REMOVE(list, (*node), reuselist_node, entry); + XFREE(MTYPE_BGP_DAMP_REUSELIST, (*node)); + *node = NULL; +} -/* Utility macro to add and delete BGP dampening information to no - used list. */ -#define BGP_DAMP_LIST_ADD(N, A) BGP_PATH_INFO_ADD(N, A, no_reuse_list) -#define BGP_DAMP_LIST_DEL(N, A) BGP_PATH_INFO_DEL(N, A, no_reuse_list) +static void bgp_reuselist_switch(struct reuselist *source, + struct reuselist_node *node, + struct reuselist *target) +{ + assert(source && target && node); + SLIST_REMOVE(source, node, reuselist_node, entry); + SLIST_INSERT_HEAD(target, node, entry); +} + +static void bgp_reuselist_free(struct reuselist *list) +{ + struct reuselist_node *rn; + + assert(list); + while ((rn = SLIST_FIRST(list)) != NULL) + bgp_reuselist_del(list, &rn); +} + +static struct reuselist_node *bgp_reuselist_find(struct reuselist *list, + struct bgp_damp_info *info) +{ + struct reuselist_node *rn; + + assert(list && info); + SLIST_FOREACH (rn, list, entry) { + if (rn->info == info) + return rn; + } + return NULL; +} + +static void bgp_damp_info_unclaim(struct bgp_damp_info *bdi) +{ + struct reuselist_node *node; + + assert(bdi && bdi->config); + if (bdi->index == BGP_DAMP_NO_REUSE_LIST_INDEX) { + node = bgp_reuselist_find(&bdi->config->no_reuse_list, bdi); + if (node) + bgp_reuselist_del(&bdi->config->no_reuse_list, &node); + } else { + node = bgp_reuselist_find(&bdi->config->reuse_list[bdi->index], + bdi); + if (node) + bgp_reuselist_del(&bdi->config->reuse_list[bdi->index], + &node); + } + bdi->config = NULL; +} + +static void bgp_damp_info_claim(struct bgp_damp_info *bdi, + struct bgp_damp_config *bdc) +{ + assert(bdc && bdi); + if (bdi->config == NULL) { + bdi->config = bdc; + return; + } + bgp_damp_info_unclaim(bdi); + bdi->config = bdc; + bdi->afi = bdc->afi; + bdi->safi = bdc->safi; +} + +struct bgp_damp_config *get_active_bdc_from_pi(struct bgp_path_info *pi, + afi_t afi, safi_t safi) +{ + if (!pi) + return NULL; + if (CHECK_FLAG(pi->peer->af_flags[afi][safi], + PEER_FLAG_CONFIG_DAMPENING)) + return &pi->peer->damp[afi][safi]; + if (peer_group_active(pi->peer)) + if (CHECK_FLAG(pi->peer->group->conf->af_flags[afi][safi], + PEER_FLAG_CONFIG_DAMPENING)) + return &pi->peer->group->conf->damp[afi][safi]; + if (CHECK_FLAG(pi->peer->bgp->af_flags[afi][safi], + BGP_CONFIG_DAMPENING)) + return &pi->peer->bgp->damp[afi][safi]; + return NULL; +} /* Calculate reuse list index by penalty value. */ static int bgp_reuse_index(int penalty, struct bgp_damp_config *bdc) { unsigned int i; - int index; + unsigned int index; /* * reuse_limit can't be zero, this is for Coverity @@ -72,27 +168,45 @@ static int bgp_reuse_index(int penalty, struct bgp_damp_config *bdc) static void bgp_reuse_list_add(struct bgp_damp_info *bdi, struct bgp_damp_config *bdc) { - int index; - - index = bdi->index = bgp_reuse_index(bdi->penalty, bdc); - - bdi->prev = NULL; - bdi->next = bdc->reuse_list[index]; - if (bdc->reuse_list[index]) - bdc->reuse_list[index]->prev = bdi; - bdc->reuse_list[index] = bdi; + bgp_damp_info_claim(bdi, bdc); + bdi->index = bgp_reuse_index(bdi->penalty, bdc); + bgp_reuselist_add(&bdc->reuse_list[bdi->index], bdi); } /* Delete BGP dampening information from reuse list. */ static void bgp_reuse_list_delete(struct bgp_damp_info *bdi, struct bgp_damp_config *bdc) { - if (bdi->next) - bdi->next->prev = bdi->prev; - if (bdi->prev) - bdi->prev->next = bdi->next; - else - bdc->reuse_list[bdi->index] = bdi->next; + struct reuselist *list; + struct reuselist_node *rn; + + list = &bdc->reuse_list[bdi->index]; + rn = bgp_reuselist_find(list, bdi); + bgp_damp_info_unclaim(bdi); + bgp_reuselist_del(list, &rn); +} + +static void bgp_no_reuse_list_add(struct bgp_damp_info *bdi, + struct bgp_damp_config *bdc) +{ + bgp_damp_info_claim(bdi, bdc); + bdi->index = BGP_DAMP_NO_REUSE_LIST_INDEX; + bgp_reuselist_add(&bdc->no_reuse_list, bdi); +} + +static void bgp_no_reuse_list_delete(struct bgp_damp_info *bdi, + struct bgp_damp_config *bdc) +{ + struct reuselist_node *rn; + + assert(bdc && bdi); + if (bdi->config == NULL) { + bgp_damp_info_unclaim(bdi); + return; + } + bdi->config = NULL; + rn = bgp_reuselist_find(&bdc->no_reuse_list, bdi); + bgp_reuselist_del(&bdc->no_reuse_list, &rn); } /* Return decayed penalty value. */ @@ -115,32 +229,34 @@ int bgp_damp_decay(time_t tdiff, int penalty, struct bgp_damp_config *bdc) is evaluated. RFC2439 Section 4.8.7. */ static int bgp_reuse_timer(struct thread *t) { + struct bgp_damp_config *bdc = THREAD_ARG(t); struct bgp_damp_info *bdi; - struct bgp_damp_info *next; + struct reuselist plist; + struct reuselist_node *node; + struct bgp *bgp; time_t t_now, t_diff; - struct bgp_damp_config *bdc = THREAD_ARG(t); - - bdc->t_reuse = NULL; thread_add_timer(bm->master, bgp_reuse_timer, bdc, DELTA_REUSE, &bdc->t_reuse); t_now = bgp_clock(); - /* 1. save a pointer to the current zeroth queue head and zero the - list head entry. */ - bdi = bdc->reuse_list[bdc->reuse_offset]; - bdc->reuse_list[bdc->reuse_offset] = NULL; + /* 1. save a pointer to the current queue head and zero the list head + * list head entry. */ + assert(bdc->reuse_offset < bdc->reuse_list_size); + plist = bdc->reuse_list[bdc->reuse_offset]; + node = SLIST_FIRST(&plist); + SLIST_INIT(&bdc->reuse_list[bdc->reuse_offset]); /* 2. set offset = modulo reuse-list-size ( offset + 1 ), thereby rotating the circular queue of list-heads. */ bdc->reuse_offset = (bdc->reuse_offset + 1) % bdc->reuse_list_size; + assert(bdc->reuse_offset < bdc->reuse_list_size); /* 3. if ( the saved list head pointer is non-empty ) */ - for (; bdi; bdi = next) { - struct bgp *bgp = bdi->path->peer->bgp; - - next = bdi->next; + while ((node = SLIST_FIRST(&plist)) != NULL) { + bdi = node->info; + bgp = bdi->path->peer->bgp; /* Set t-diff = t-now - t-updated. */ t_diff = t_now - bdi->t_updated; @@ -169,16 +285,27 @@ static int bgp_reuse_timer(struct thread *t) bdi->safi); } - if (bdi->penalty <= bdc->reuse_limit / 2.0) - bgp_damp_info_free(bdi, 1, bdc->afi, bdc->safi); - else - BGP_DAMP_LIST_ADD(bdc, bdi); - } else + if (bdi->penalty <= bdc->reuse_limit / 2.0) { + bgp_damp_info_free(&bdi, bdc, 1, bdi->afi, + bdi->safi); + bgp_reuselist_del(&plist, &node); + } else { + node->info->index = + BGP_DAMP_NO_REUSE_LIST_INDEX; + bgp_reuselist_switch(&plist, node, + &bdc->no_reuse_list); + } + } else { /* Re-insert into another list (See RFC2439 Section * 4.8.6). */ - bgp_reuse_list_add(bdi, bdc); + bdi->index = bgp_reuse_index(bdi->penalty, bdc); + bgp_reuselist_switch(&plist, node, + &bdc->reuse_list[bdi->index]); + } } + assert(SLIST_EMPTY(&plist)); + return 0; } @@ -189,10 +316,13 @@ int bgp_damp_withdraw(struct bgp_path_info *path, struct bgp_dest *dest, time_t t_now; struct bgp_damp_info *bdi = NULL; unsigned int last_penalty = 0; - struct bgp_damp_config *bdc = &damp[afi][safi]; + struct bgp_damp_config *bdc; - t_now = bgp_clock(); + bdc = get_active_bdc_from_pi(path, afi, safi); + if (!bdc) + return BGP_DAMP_USED; + t_now = bgp_clock(); /* Processing Unreachable Messages. */ if (path->extra) bdi = path->extra->damp_info; @@ -214,12 +344,13 @@ int bgp_damp_withdraw(struct bgp_path_info *path, struct bgp_dest *dest, bdi->flap = 1; bdi->start_time = t_now; bdi->suppress_time = 0; - bdi->index = -1; + bdi->index = BGP_DAMP_NO_REUSE_LIST_INDEX; bdi->afi = afi; bdi->safi = safi; (bgp_path_info_extra_get(path))->damp_info = bdi; - BGP_DAMP_LIST_ADD(bdc, bdi); + bgp_no_reuse_list_add(bdi, bdc); } else { + bgp_damp_info_claim(bdi, bdc); last_penalty = bdi->penalty; /* 1. Set t-diff = t-now - t-updated. */ @@ -245,7 +376,7 @@ int bgp_damp_withdraw(struct bgp_path_info *path, struct bgp_dest *dest, /* Remove the route from a reuse list if it is on one. */ if (CHECK_FLAG(bdi->path->flags, BGP_PATH_DAMPED)) { /* If decay rate isn't equal to 0, reinsert brn. */ - if (bdi->penalty != last_penalty && bdi->index >= 0) { + if (bdi->penalty != last_penalty) { bgp_reuse_list_delete(bdi, bdc); bgp_reuse_list_add(bdi, bdc); } @@ -257,10 +388,9 @@ int bgp_damp_withdraw(struct bgp_path_info *path, struct bgp_dest *dest, if (bdi->penalty >= bdc->suppress_value) { bgp_path_info_set_flag(dest, path, BGP_PATH_DAMPED); bdi->suppress_time = t_now; - BGP_DAMP_LIST_DEL(bdc, bdi); + bgp_no_reuse_list_delete(bdi, bdc); bgp_reuse_list_add(bdi, bdc); } - return BGP_DAMP_USED; } @@ -270,7 +400,10 @@ int bgp_damp_update(struct bgp_path_info *path, struct bgp_dest *dest, time_t t_now; struct bgp_damp_info *bdi; int status; - struct bgp_damp_config *bdc = &damp[afi][safi]; + struct bgp_damp_config *bdc; + + bdc = get_active_bdc_from_pi(path, afi, safi); + assert(bdc); if (!path->extra || !((bdi = path->extra->damp_info))) return BGP_DAMP_USED; @@ -289,7 +422,7 @@ int bgp_damp_update(struct bgp_path_info *path, struct bgp_dest *dest, && (bdi->penalty < bdc->reuse_limit)) { bgp_path_info_unset_flag(dest, path, BGP_PATH_DAMPED); bgp_reuse_list_delete(bdi, bdc); - BGP_DAMP_LIST_ADD(bdc, bdi); + bgp_no_reuse_list_add(bdi, bdc); bdi->suppress_time = 0; status = BGP_DAMP_USED; } else @@ -297,36 +430,29 @@ int bgp_damp_update(struct bgp_path_info *path, struct bgp_dest *dest, if (bdi->penalty > bdc->reuse_limit / 2.0) bdi->t_updated = t_now; - else - bgp_damp_info_free(bdi, 0, afi, safi); + else { + bgp_damp_info_unclaim(bdi); + bgp_damp_info_free(&bdi, bdc, 0, afi, safi); + } return status; } -void bgp_damp_info_free(struct bgp_damp_info *bdi, int withdraw, afi_t afi, - safi_t safi) +void bgp_damp_info_free(struct bgp_damp_info **bdi, struct bgp_damp_config *bdc, + int withdraw, afi_t afi, safi_t safi) { - struct bgp_path_info *path; - struct bgp_damp_config *bdc = &damp[afi][safi]; + assert(bdc && bdi && *bdi); - if (!bdi) + if ((*bdi)->path == NULL) { + XFREE(MTYPE_BGP_DAMP_INFO, (*bdi)); return; + } - path = bdi->path; - path->extra->damp_info = NULL; - - if (CHECK_FLAG(path->flags, BGP_PATH_DAMPED)) - bgp_reuse_list_delete(bdi, bdc); - else - BGP_DAMP_LIST_DEL(bdc, bdi); - - bgp_path_info_unset_flag(bdi->dest, path, + (*bdi)->path->extra->damp_info = NULL; + bgp_path_info_unset_flag((*bdi)->dest, (*bdi)->path, BGP_PATH_HISTORY | BGP_PATH_DAMPED); - - if (bdi->lastrecord == BGP_RECORD_WITHDRAW && withdraw) - bgp_path_info_delete(bdi->dest, path); - - XFREE(MTYPE_BGP_DAMP_INFO, bdi); + if ((*bdi)->lastrecord == BGP_RECORD_WITHDRAW && withdraw) + bgp_path_info_delete((*bdi)->dest, (*bdi)->path); } static void bgp_damp_parameter_set(int hlife, int reuse, int sup, int maxsup, @@ -369,8 +495,7 @@ static void bgp_damp_parameter_set(int hlife, int reuse, int sup, int maxsup, bdc->reuse_list = XCALLOC(MTYPE_BGP_DAMP_ARRAY, - bdc->reuse_list_size * sizeof(struct bgp_reuse_node *)); - + bdc->reuse_list_size * sizeof(struct reuselist)); /* Reuse-array computations */ bdc->reuse_index = XCALLOC(MTYPE_BGP_DAMP_ARRAY, sizeof(int) * bdc->reuse_index_size); @@ -397,7 +522,7 @@ static void bgp_damp_parameter_set(int hlife, int reuse, int sup, int maxsup, int bgp_damp_enable(struct bgp *bgp, afi_t afi, safi_t safi, time_t half, unsigned int reuse, unsigned int suppress, time_t max) { - struct bgp_damp_config *bdc = &damp[afi][safi]; + struct bgp_damp_config *bdc = &bgp->damp[afi][safi]; if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)) { if (bdc->half_life == half && bdc->reuse_limit == reuse @@ -409,6 +534,8 @@ int bgp_damp_enable(struct bgp *bgp, afi_t afi, safi_t safi, time_t half, SET_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING); bgp_damp_parameter_set(half, reuse, suppress, max, bdc); + bdc->afi = afi; + bdc->safi = safi; /* Register reuse timer. */ thread_add_timer(bm->master, bgp_reuse_timer, bdc, DELTA_REUSE, @@ -417,8 +544,30 @@ int bgp_damp_enable(struct bgp *bgp, afi_t afi, safi_t safi, time_t half, return 0; } -static void bgp_damp_config_clean(struct bgp_damp_config *bdc) +/* Clean all the bgp_damp_info stored in reuse_list and no_reuse_list. */ +void bgp_damp_info_clean(struct bgp_damp_config *bdc, afi_t afi, safi_t safi) { + struct bgp_damp_info *bdi; + struct reuselist_node *rn; + struct reuselist *list; + unsigned int i; + + bdc->reuse_offset = 0; + for (i = 0; i < bdc->reuse_list_size; ++i) { + list = &bdc->reuse_list[i]; + while ((rn = SLIST_FIRST(list)) != NULL) { + bdi = rn->info; + bgp_reuselist_del(list, &rn); + bgp_damp_info_free(&bdi, bdc, 1, afi, safi); + } + } + + while ((rn = SLIST_FIRST(&bdc->no_reuse_list)) != NULL) { + bdi = rn->info; + bgp_reuselist_del(&bdc->no_reuse_list, &rn); + bgp_damp_info_free(&bdi, bdc, 1, afi, safi); + } + /* Free decay array */ XFREE(MTYPE_BGP_DAMP_ARRAY, bdc->decay_array); bdc->decay_array_size = 0; @@ -428,96 +577,81 @@ static void bgp_damp_config_clean(struct bgp_damp_config *bdc) bdc->reuse_index_size = 0; /* Free reuse list array. */ + for (i = 0; i < bdc->reuse_list_size; ++i) + bgp_reuselist_free(&bdc->reuse_list[i]); + XFREE(MTYPE_BGP_DAMP_ARRAY, bdc->reuse_list); bdc->reuse_list_size = 0; -} - -/* Clean all the bgp_damp_info stored in reuse_list. */ -void bgp_damp_info_clean(afi_t afi, safi_t safi) -{ - unsigned int i; - struct bgp_damp_info *bdi, *next; - struct bgp_damp_config *bdc = &damp[afi][safi]; - - bdc->reuse_offset = 0; - - for (i = 0; i < bdc->reuse_list_size; i++) { - if (!bdc->reuse_list[i]) - continue; - - for (bdi = bdc->reuse_list[i]; bdi; bdi = next) { - next = bdi->next; - bgp_damp_info_free(bdi, 1, afi, safi); - } - bdc->reuse_list[i] = NULL; - } - for (bdi = bdc->no_reuse_list; bdi; bdi = next) { - next = bdi->next; - bgp_damp_info_free(bdi, 1, afi, safi); - } - bdc->no_reuse_list = NULL; + THREAD_OFF(bdc->t_reuse); } +/* Disable route flap dampening for a bgp instance. + * + * Please note that this function also gets used to free memory when deleting a + * bgp instance. + */ int bgp_damp_disable(struct bgp *bgp, afi_t afi, safi_t safi) { - struct bgp_damp_config *bdc = &damp[afi][safi]; + struct bgp_damp_config *bdc; + + bdc = &bgp->damp[afi][safi]; + if (!bdc) + return 0; + /* If it wasn't enabled, there's nothing to do. */ if (!CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)) return 0; /* Cancel reuse event. */ - thread_cancel(&(bdc->t_reuse)); + thread_cancel(&bdc->t_reuse); /* Clean BGP dampening information. */ - bgp_damp_info_clean(afi, safi); - - /* Clear configuration */ - bgp_damp_config_clean(bdc); + bgp_damp_info_clean(bdc, afi, safi); UNSET_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING); + return 0; } -void bgp_config_write_damp(struct vty *vty, afi_t afi, safi_t safi) +void bgp_config_write_damp(struct vty *vty, struct bgp *bgp, afi_t afi, + safi_t safi) { - if (damp[afi][safi].half_life == DEFAULT_HALF_LIFE * 60 - && damp[afi][safi].reuse_limit == DEFAULT_REUSE - && damp[afi][safi].suppress_value == DEFAULT_SUPPRESS - && damp[afi][safi].max_suppress_time - == damp[afi][safi].half_life * 4) + struct bgp_damp_config *bdc; + + bdc = &bgp->damp[afi][safi]; + if (bdc->half_life == DEFAULT_HALF_LIFE * 60 + && bdc->reuse_limit == DEFAULT_REUSE + && bdc->suppress_value == DEFAULT_SUPPRESS + && bdc->max_suppress_time == bdc->half_life * 4) vty_out(vty, " bgp dampening\n"); - else if (damp[afi][safi].half_life != DEFAULT_HALF_LIFE * 60 - && damp[afi][safi].reuse_limit == DEFAULT_REUSE - && damp[afi][safi].suppress_value == DEFAULT_SUPPRESS - && damp[afi][safi].max_suppress_time - == damp[afi][safi].half_life * 4) - vty_out(vty, " bgp dampening %lld\n", - damp[afi][safi].half_life / 60LL); + else if (bdc->half_life != DEFAULT_HALF_LIFE * 60 + && bdc->reuse_limit == DEFAULT_REUSE + && bdc->suppress_value == DEFAULT_SUPPRESS + && bdc->max_suppress_time == bdc->half_life * 4) + vty_out(vty, " bgp dampening %lld\n", bdc->half_life / 60LL); else vty_out(vty, " bgp dampening %lld %d %d %lld\n", - damp[afi][safi].half_life / 60LL, - damp[afi][safi].reuse_limit, - damp[afi][safi].suppress_value, - damp[afi][safi].max_suppress_time / 60LL); + bdc->half_life / 60LL, bdc->reuse_limit, + bdc->suppress_value, bdc->max_suppress_time / 60LL); } -static const char *bgp_get_reuse_time(unsigned int penalty, char *buf, - size_t len, afi_t afi, safi_t safi, - bool use_json, json_object *json) +static const char *bgp_get_reuse_time(struct bgp_damp_config *bdc, + unsigned int penalty, char *buf, + size_t len, bool use_json, + json_object *json) { time_t reuse_time = 0; struct tm tm; int time_store = 0; - if (penalty > damp[afi][safi].reuse_limit) { + if (penalty > bdc->reuse_limit) { reuse_time = (int)(DELTA_T - * ((log((double)damp[afi][safi].reuse_limit - / penalty)) - / (log(damp[afi][safi].decay_array[1])))); + * ((log((double)bdc->reuse_limit / penalty)) + / (log(bdc->decay_array[1])))); - if (reuse_time > damp[afi][safi].max_suppress_time) - reuse_time = damp[afi][safi].max_suppress_time; + if (reuse_time > bdc->max_suppress_time) + reuse_time = bdc->max_suppress_time; gmtime_r(&reuse_time, &tm); } else @@ -569,14 +703,15 @@ static const char *bgp_get_reuse_time(unsigned int penalty, char *buf, return buf; } -void bgp_damp_info_vty(struct vty *vty, struct bgp_path_info *path, afi_t afi, - safi_t safi, json_object *json_path) +void bgp_damp_info_vty(struct vty *vty, struct bgp *bgp, + struct bgp_path_info *path, afi_t afi, safi_t safi, + json_object *json_path) { struct bgp_damp_info *bdi; time_t t_now, t_diff; char timebuf[BGP_UPTIME_LEN]; int penalty; - struct bgp_damp_config *bdc = &damp[afi][safi]; + struct bgp_damp_config *bdc = &bgp->damp[afi][safi]; if (!path->extra) return; @@ -602,8 +737,8 @@ void bgp_damp_info_vty(struct vty *vty, struct bgp_path_info *path, afi_t afi, if (CHECK_FLAG(path->flags, BGP_PATH_DAMPED) && !CHECK_FLAG(path->flags, BGP_PATH_HISTORY)) - bgp_get_reuse_time(penalty, timebuf, BGP_UPTIME_LEN, - afi, safi, 1, json_path); + bgp_get_reuse_time(bdc, penalty, timebuf, + BGP_UPTIME_LEN, 1, json_path); } else { vty_out(vty, " Dampinfo: penalty %d, flapped %d times in %s", @@ -614,14 +749,15 @@ void bgp_damp_info_vty(struct vty *vty, struct bgp_path_info *path, afi_t afi, if (CHECK_FLAG(path->flags, BGP_PATH_DAMPED) && !CHECK_FLAG(path->flags, BGP_PATH_HISTORY)) vty_out(vty, ", reuse in %s", - bgp_get_reuse_time(penalty, timebuf, - BGP_UPTIME_LEN, afi, safi, 0, + bgp_get_reuse_time(bdc, penalty, timebuf, + BGP_UPTIME_LEN, 0, json_path)); vty_out(vty, "\n"); } } + const char *bgp_damp_reuse_time_vty(struct vty *vty, struct bgp_path_info *path, char *timebuf, size_t len, afi_t afi, safi_t safi, bool use_json, @@ -630,7 +766,11 @@ const char *bgp_damp_reuse_time_vty(struct vty *vty, struct bgp_path_info *path, struct bgp_damp_info *bdi; time_t t_now, t_diff; int penalty; - struct bgp_damp_config *bdc = &damp[afi][safi]; + struct bgp_damp_config *bdc; + + bdc = get_active_bdc_from_pi(path, afi, safi); + if (!bdc) + return NULL; if (!path->extra) return NULL; @@ -648,24 +788,23 @@ const char *bgp_damp_reuse_time_vty(struct vty *vty, struct bgp_path_info *path, t_diff = t_now - bdi->t_updated; penalty = bgp_damp_decay(t_diff, bdi->penalty, bdc); - return bgp_get_reuse_time(penalty, timebuf, len, afi, safi, use_json, - json); + return bgp_get_reuse_time(bdc, penalty, timebuf, len, use_json, json); } + static int bgp_print_dampening_parameters(struct bgp *bgp, struct vty *vty, afi_t afi, safi_t safi) { + struct bgp_damp_config *bdc; if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)) { + bdc = &bgp->damp[afi][safi]; vty_out(vty, "Half-life time: %lld min\n", - (long long)damp[afi][safi].half_life / 60); - vty_out(vty, "Reuse penalty: %d\n", - damp[afi][safi].reuse_limit); - vty_out(vty, "Suppress penalty: %d\n", - damp[afi][safi].suppress_value); + (long long)bdc->half_life / 60); + vty_out(vty, "Reuse penalty: %d\n", bdc->reuse_limit); + vty_out(vty, "Suppress penalty: %d\n", bdc->suppress_value); vty_out(vty, "Max suppress time: %lld min\n", - (long long)damp[afi][safi].max_suppress_time / 60); - vty_out(vty, "Max suppress penalty: %u\n", - damp[afi][safi].ceiling); + (long long)bdc->max_suppress_time / 60); + vty_out(vty, "Max suppress penalty: %u\n", bdc->ceiling); vty_out(vty, "\n"); } else vty_out(vty, "dampening not enabled for %s\n", @@ -678,8 +817,8 @@ int bgp_show_dampening_parameters(struct vty *vty, afi_t afi, safi_t safi, uint8_t show_flags) { struct bgp *bgp; - bgp = bgp_get_default(); + bgp = bgp_get_default(); if (bgp == NULL) { vty_out(vty, "No BGP process is configured\n"); return CMD_WARNING; @@ -718,3 +857,132 @@ int bgp_show_dampening_parameters(struct vty *vty, afi_t afi, safi_t safi, } return CMD_SUCCESS; } + +void bgp_peer_damp_enable(struct peer *peer, afi_t afi, safi_t safi, + time_t half, unsigned int reuse, + unsigned int suppress, time_t max) +{ + struct bgp_damp_config *bdc; + + if (!peer) + return; + bdc = &peer->damp[afi][safi]; + if (peer_af_flag_check(peer, afi, safi, PEER_FLAG_CONFIG_DAMPENING)) { + if (bdc->half_life == half && bdc->reuse_limit == reuse + && bdc->suppress_value == suppress + && bdc->max_suppress_time == max) + return; + bgp_peer_damp_disable(peer, afi, safi); + } + SET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_CONFIG_DAMPENING); + bgp_damp_parameter_set(half, reuse, suppress, max, bdc); + bdc->afi = afi; + bdc->safi = safi; + thread_add_timer(bm->master, bgp_reuse_timer, bdc, DELTA_REUSE, + &bdc->t_reuse); +} + +/* Disable route flap dampening for a peer. + * + * Please note that this function also gets used to free memory when deleting a + * peer or peer group. + */ +void bgp_peer_damp_disable(struct peer *peer, afi_t afi, safi_t safi) +{ + struct bgp_damp_config *bdc; + + if (!peer_af_flag_check(peer, afi, safi, PEER_FLAG_CONFIG_DAMPENING)) + return; + bdc = &peer->damp[afi][safi]; + if (!bdc) + return; + bgp_damp_info_clean(bdc, afi, safi); + UNSET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_CONFIG_DAMPENING); +} + +void bgp_config_write_peer_damp(struct vty *vty, struct peer *peer, afi_t afi, + safi_t safi) +{ + struct bgp_damp_config *bdc; + + bdc = &peer->damp[afi][safi]; + if (bdc->half_life == DEFAULT_HALF_LIFE * 60 + && bdc->reuse_limit == DEFAULT_REUSE + && bdc->suppress_value == DEFAULT_SUPPRESS + && bdc->max_suppress_time == bdc->half_life * 4) + vty_out(vty, " neighbor %s dampening\n", peer->host); + else if (bdc->half_life != DEFAULT_HALF_LIFE * 60 + && bdc->reuse_limit == DEFAULT_REUSE + && bdc->suppress_value == DEFAULT_SUPPRESS + && bdc->max_suppress_time == bdc->half_life * 4) + vty_out(vty, " neighbor %s dampening %lld\n", peer->host, + bdc->half_life / 60LL); + else + vty_out(vty, " neighbor %s dampening %lld %d %d %lld\n", + peer->host, bdc->half_life / 60LL, bdc->reuse_limit, + bdc->suppress_value, bdc->max_suppress_time / 60LL); +} + +static void bgp_print_peer_dampening_parameters(struct vty *vty, + struct peer *peer, afi_t afi, + safi_t safi, bool use_json, + json_object *json) +{ + struct bgp_damp_config *bdc; + + if (!peer) + return; + if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_CONFIG_DAMPENING)) { + bdc = &peer->damp[afi][safi]; + if (!bdc) + return; + if (use_json) { + json_object_int_add(json, "halfLifeSecs", + bdc->half_life); + json_object_int_add(json, "reusePenalty", + bdc->reuse_limit); + json_object_int_add(json, "suppressPenalty", + bdc->suppress_value); + json_object_int_add(json, "maxSuppressTimeSecs", + bdc->max_suppress_time); + json_object_int_add(json, "maxSuppressPenalty", + bdc->ceiling); + } else { + vty_out(vty, "Half-life time: %lld min\n", + (long long)bdc->half_life / 60); + vty_out(vty, "Reuse penalty: %d\n", bdc->reuse_limit); + vty_out(vty, "Suppress penalty: %d\n", + bdc->suppress_value); + vty_out(vty, "Max suppress time: %lld min\n", + (long long)bdc->max_suppress_time / 60); + vty_out(vty, "Max suppress penalty: %u\n", + bdc->ceiling); + vty_out(vty, "\n"); + } + } else if (!use_json) + vty_out(vty, "neighbor dampening not enabled for %s\n", + get_afi_safi_str(afi, safi, false)); +} + +void bgp_show_peer_dampening_parameters(struct vty *vty, struct peer *peer, + afi_t afi, safi_t safi, bool use_json) +{ + json_object *json; + + if (use_json) { + json = json_object_new_object(); + json_object_string_add(json, "addressFamily", + get_afi_safi_str(afi, safi, false)); + bgp_print_peer_dampening_parameters(vty, peer, afi, safi, true, + json); + vty_out(vty, "%s\n", + json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } else { + vty_out(vty, "\nFor address family: %s\n", + get_afi_safi_str(afi, safi, false)); + bgp_print_peer_dampening_parameters(vty, peer, afi, safi, false, + NULL); + } +} diff --git a/bgpd/bgp_damp.h b/bgpd/bgp_damp.h index 604706300..521f59b29 100644 --- a/bgpd/bgp_damp.h +++ b/bgpd/bgp_damp.h @@ -25,11 +25,6 @@ /* Structure maintained on a per-route basis. */ struct bgp_damp_info { - /* Doubly linked list. This information must be linked to - reuse_list or no_reuse_list. */ - struct bgp_damp_info *next; - struct bgp_damp_info *prev; - /* Figure-of-merit. */ unsigned int penalty; @@ -45,6 +40,9 @@ struct bgp_damp_info { /* Time of route start to be suppressed. */ time_t suppress_time; + /* Back reference to associated dampening configuration. */ + struct bgp_damp_config *config; + /* Back reference to bgp_path_info. */ struct bgp_path_info *path; @@ -53,6 +51,8 @@ struct bgp_damp_info { /* Current index in the reuse_list. */ int index; +#define BGP_DAMP_NO_REUSE_LIST_INDEX \ + (-1) /* index for elements on no_reuse_list */ /* Last time message type. */ uint8_t lastrecord; @@ -63,6 +63,13 @@ struct bgp_damp_info { safi_t safi; }; +struct reuselist_node { + SLIST_ENTRY(reuselist_node) entry; + struct bgp_damp_info *info; +}; + +SLIST_HEAD(reuselist, reuselist_node); + /* Specified parameter set configuration. */ struct bgp_damp_config { /* Value over which routes suppressed. */ @@ -100,11 +107,11 @@ struct bgp_damp_config { int *reuse_index; /* Reuse list array per-set based. */ - struct bgp_damp_info **reuse_list; - int reuse_offset; + struct reuselist *reuse_list; + unsigned int reuse_offset; /* All dampening information which is not on reuse list. */ - struct bgp_damp_info *no_reuse_list; + struct reuselist no_reuse_list; /* Reuse timer thread per-set base. */ struct thread *t_reuse; @@ -132,6 +139,8 @@ struct bgp_damp_config { #define REUSE_LIST_SIZE 256 #define REUSE_ARRAY_SIZE 1024 +extern struct bgp_damp_config *get_active_bdc_from_pi(struct bgp_path_info *pi, + afi_t afi, safi_t safi); extern int bgp_damp_enable(struct bgp *, afi_t, safi_t, time_t, unsigned int, unsigned int, time_t); extern int bgp_damp_disable(struct bgp *, afi_t, safi_t); @@ -139,13 +148,18 @@ extern int bgp_damp_withdraw(struct bgp_path_info *path, struct bgp_dest *dest, afi_t afi, safi_t safi, int attr_change); extern int bgp_damp_update(struct bgp_path_info *path, struct bgp_dest *dest, afi_t afi, safi_t saff); -extern void bgp_damp_info_free(struct bgp_damp_info *path, int withdraw, +extern void bgp_damp_info_free(struct bgp_damp_info **path, + struct bgp_damp_config *bdc, int withdraw, afi_t afi, safi_t safi); -extern void bgp_damp_info_clean(afi_t afi, safi_t safi); +extern void bgp_damp_info_clean(struct bgp_damp_config *bdc, afi_t afi, + safi_t safi); +extern void bgp_damp_config_clean(struct bgp_damp_config *bdc); extern int bgp_damp_decay(time_t, int, struct bgp_damp_config *damp); -extern void bgp_config_write_damp(struct vty *, afi_t afi, safi_t safi); -extern void bgp_damp_info_vty(struct vty *vty, struct bgp_path_info *path, - afi_t afi, safi_t safi, json_object *json_path); +extern void bgp_config_write_damp(struct vty *vty, struct bgp *bgp, afi_t afi, + safi_t safi); +extern void bgp_damp_info_vty(struct vty *vty, struct bgp *bgp, + struct bgp_path_info *path, afi_t afi, + safi_t safi, json_object *json_path); extern const char *bgp_damp_reuse_time_vty(struct vty *vty, struct bgp_path_info *path, char *timebuf, size_t len, afi_t afi, @@ -153,5 +167,14 @@ extern const char *bgp_damp_reuse_time_vty(struct vty *vty, json_object *json); extern int bgp_show_dampening_parameters(struct vty *vty, afi_t, safi_t, uint8_t); +extern void bgp_peer_damp_enable(struct peer *peer, afi_t afi, safi_t safi, + time_t half, unsigned int reuse, + unsigned int suppress, time_t max); +extern void bgp_peer_damp_disable(struct peer *peer, afi_t afi, safi_t safi); +extern void bgp_config_write_peer_damp(struct vty *vty, struct peer *peer, + afi_t afi, safi_t safi); +extern void bgp_show_peer_dampening_parameters(struct vty *vty, + struct peer *peer, afi_t afi, + safi_t safi, bool use_json); #endif /* _QUAGGA_BGP_DAMP_H */ diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c index f9aac35d0..0013eb2df 100644 --- a/bgpd/bgp_memory.c +++ b/bgpd/bgp_memory.c @@ -98,6 +98,7 @@ DEFINE_MTYPE(BGPD, PEER_UPDATE_SOURCE, "BGP peer update interface") DEFINE_MTYPE(BGPD, PEER_CONF_IF, "BGP peer config interface") DEFINE_MTYPE(BGPD, BGP_DAMP_INFO, "Dampening info") DEFINE_MTYPE(BGPD, BGP_DAMP_ARRAY, "BGP Dampening array") +DEFINE_MTYPE(BGPD, BGP_DAMP_REUSELIST, "BGP Dampening reuse list") DEFINE_MTYPE(BGPD, BGP_REGEXP, "BGP regexp") DEFINE_MTYPE(BGPD, BGP_AGGREGATE, "BGP aggregate") DEFINE_MTYPE(BGPD, BGP_ADDR, "BGP own address") diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h index a95d9ef93..63998e95a 100644 --- a/bgpd/bgp_memory.h +++ b/bgpd/bgp_memory.h @@ -94,6 +94,7 @@ DECLARE_MTYPE(PEER_UPDATE_SOURCE) DECLARE_MTYPE(PEER_CONF_IF) DECLARE_MTYPE(BGP_DAMP_INFO) DECLARE_MTYPE(BGP_DAMP_ARRAY) +DECLARE_MTYPE(BGP_DAMP_REUSELIST) DECLARE_MTYPE(BGP_REGEXP) DECLARE_MTYPE(BGP_AGGREGATE) DECLARE_MTYPE(BGP_ADDR) diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 8dec3dde8..c4ab223b7 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -208,9 +208,6 @@ void bgp_path_info_extra_free(struct bgp_path_info_extra **extra) return; e = *extra; - if (e->damp_info) - bgp_damp_info_free(e->damp_info, 0, e->damp_info->afi, - e->damp_info->safi); e->damp_info = NULL; if (e->parent) { @@ -3331,14 +3328,16 @@ static void bgp_rib_withdraw(struct bgp_dest *dest, struct bgp_path_info *pi, /* apply dampening, if result is suppressed, we'll be retaining * the bgp_path_info in the RIB for historical reference. */ - if (CHECK_FLAG(peer->bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING) - && peer->sort == BGP_PEER_EBGP) - if ((bgp_damp_withdraw(pi, dest, afi, safi, 0)) - == BGP_DAMP_SUPPRESSED) { - bgp_aggregate_decrement(peer->bgp, p, pi, afi, - safi); - return; + if (peer->sort == BGP_PEER_EBGP) { + if (get_active_bdc_from_pi(pi, afi, safi)) { + if (bgp_damp_withdraw(pi, dest, afi, safi, 0) + == BGP_DAMP_SUPPRESSED) { + bgp_aggregate_decrement(peer->bgp, p, pi, afi, + safi); + return; + } } + } #ifdef ENABLE_BGP_VNC if (safi == SAFI_MPLS_VPN) { @@ -3763,8 +3762,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, && (overlay_index_equal( afi, pi, evpn == NULL ? NULL : &evpn->gw_ip))) { - if (CHECK_FLAG(bgp->af_flags[afi][safi], - BGP_CONFIG_DAMPENING) + if (get_active_bdc_from_pi(pi, afi, safi) && peer->sort == BGP_PEER_EBGP && CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) { if (bgp_debug_update(peer, p, NULL, 1)) { @@ -3858,11 +3856,11 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, bgp_aggregate_decrement(bgp, p, pi, afi, safi); /* Update bgp route dampening information. */ - if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING) + if (get_active_bdc_from_pi(pi, afi, safi) && peer->sort == BGP_PEER_EBGP) { /* This is implicit withdraw so we should update - dampening - information. */ + * dampening information. + */ if (!CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) bgp_damp_withdraw(pi, dest, afi, safi, 1); } @@ -3985,7 +3983,7 @@ int bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id, #endif /* Update bgp route dampening information. */ - if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING) + if (get_active_bdc_from_pi(pi, afi, safi) && peer->sort == BGP_PEER_EBGP) { /* Now we do normal update dampening. */ ret = bgp_damp_update(pi, dest, afi, safi); @@ -10212,7 +10210,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, } if (path->extra && path->extra->damp_info) - bgp_damp_info_vty(vty, path, afi, safi, json_path); + bgp_damp_info_vty(vty, bgp, path, afi, safi, json_path); /* Remote Label */ if (path->extra && bgp_is_valid_label(&path->extra->label[0]) @@ -14162,7 +14160,8 @@ static int bgp_clear_damp_route(struct vty *vty, const char *view_name, if (pi->extra && pi->extra->damp_info) { pi_temp = pi->next; bgp_damp_info_free( - pi->extra->damp_info, + &pi->extra->damp_info, + &bgp->damp[afi][safi], 1, afi, safi); pi = pi_temp; } else @@ -14184,7 +14183,8 @@ static int bgp_clear_damp_route(struct vty *vty, const char *view_name, if (pi->extra && pi->extra->damp_info) { pi_temp = pi->next; bgp_damp_info_free( - pi->extra->damp_info, + &pi->extra->damp_info, + &bgp->damp[afi][safi], 1, afi, safi); pi = pi_temp; } else @@ -14207,7 +14207,9 @@ DEFUN (clear_ip_bgp_dampening, BGP_STR "Clear route flap dampening information\n") { - bgp_damp_info_clean(AFI_IP, SAFI_UNICAST); + VTY_DECLVAR_CONTEXT(bgp, bgp); + bgp_damp_info_clean(&bgp->damp[AFI_IP][SAFI_UNICAST], AFI_IP, + SAFI_UNICAST); return CMD_SUCCESS; } diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 1fdcb2c21..114a00cc3 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -8896,6 +8896,93 @@ DEFPY( return CMD_SUCCESS; } +DEFPY(neighbor_damp, + neighbor_damp_cmd, + "neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor dampening [(1-45)$half [(1-20000)$reuse (1-20000)$suppress (1-255)$max]]", + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Enable neighbor route-flap dampening\n" + "Half-life time for the penalty\n" + "Value to start reusing a route\n" + "Value to start suppressing a route\n" + "Maximum duration to suppress a stable route\n") +{ + struct peer *peer = peer_and_group_lookup_vty(vty, neighbor); + + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + if (!half) + half = DEFAULT_HALF_LIFE; + if (!reuse) { + reuse = DEFAULT_REUSE; + suppress = DEFAULT_SUPPRESS; + max = half * 4; + } + if (suppress < reuse) { + vty_out(vty, + "Suppress value cannot be less than reuse value\n"); + return CMD_WARNING_CONFIG_FAILED; + } + bgp_peer_damp_enable(peer, bgp_node_afi(vty), bgp_node_safi(vty), + half * 60, reuse, suppress, max * 60); + return CMD_SUCCESS; +} + +DEFPY(no_neighbor_damp, + no_neighbor_damp_cmd, + "no neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor dampening [HALF [REUSE SUPPRESS MAX]]", + NO_STR + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Enable neighbor route-flap dampening\n" + "Half-life time for the penalty\n" + "Value to start reusing a route\n" + "Value to start suppressing a route\n" + "Maximum duration to suppress a stable route\n") +{ + struct peer *peer = peer_and_group_lookup_vty(vty, neighbor); + + if (!peer) + return CMD_WARNING_CONFIG_FAILED; + bgp_peer_damp_disable(peer, bgp_node_afi(vty), bgp_node_safi(vty)); + return CMD_SUCCESS; +} + +DEFPY (show_ip_bgp_neighbor_damp_param, + show_ip_bgp_neighbor_damp_param_cmd, + "show [ip] bgp [<ipv4|ipv6> [unicast]] neighbors <A.B.C.D|X:X::X:X|WORD>$neighbor dampening parameters [json]$json", + SHOW_STR + IP_STR + BGP_STR + BGP_AFI_HELP_STR + "Address Family modifier\n" + NEIGHBOR_STR + NEIGHBOR_ADDR_STR2 + "Neighbor route-flap dampening information\n" + "Display detail of configured dampening parameters\n" + JSON_STR) +{ + bool use_json = false; + int idx = 0; + afi_t afi = AFI_IP; + safi_t safi = SAFI_UNICAST; + struct peer *peer; + + if (argv_find(argv, argc, "ip", &idx)) + afi = AFI_IP; + if (argv_find(argv, argc, "ipv4", &idx)) + afi = AFI_IP; + if (argv_find(argv, argc, "ipv6", &idx)) + afi = AFI_IP6; + peer = peer_and_group_lookup_vty(vty, neighbor); + if (!peer) + return CMD_WARNING; + if (json) + use_json = true; + bgp_show_peer_dampening_parameters(vty, peer, afi, safi, use_json); + return CMD_SUCCESS; +} + static int set_ecom_list(struct vty *vty, int argc, struct cmd_token **argv, struct ecommunity **list, bool is_rt6) { @@ -17000,7 +17087,15 @@ static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi, /* BGP flag dampening. */ if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)) - bgp_config_write_damp(vty, afi, safi); + bgp_config_write_damp(vty, bgp, afi, safi); + for (ALL_LIST_ELEMENTS_RO(bgp->group, node, group)) + if (peer_af_flag_check(group->conf, afi, safi, + PEER_FLAG_CONFIG_DAMPENING)) + bgp_config_write_peer_damp(vty, group->conf, afi, safi); + for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) + if (peer_af_flag_check(peer, afi, safi, + PEER_FLAG_CONFIG_DAMPENING)) + bgp_config_write_peer_damp(vty, peer, afi, safi); for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) bgp_config_write_peer_af(vty, bgp, group->conf, afi, safi); @@ -18765,6 +18860,23 @@ void bgp_vty_init(void) install_element(BGP_EVPN_NODE, &neighbor_allowas_in_cmd); install_element(BGP_EVPN_NODE, &no_neighbor_allowas_in_cmd); + /* "neighbor dampening" commands. */ + install_element(BGP_NODE, &neighbor_damp_cmd); + install_element(BGP_NODE, &no_neighbor_damp_cmd); + install_element(BGP_IPV4_NODE, &neighbor_damp_cmd); + install_element(BGP_IPV4_NODE, &no_neighbor_damp_cmd); + install_element(BGP_IPV4M_NODE, &neighbor_damp_cmd); + install_element(BGP_IPV4M_NODE, &no_neighbor_damp_cmd); + install_element(BGP_IPV4L_NODE, &neighbor_damp_cmd); + install_element(BGP_IPV4L_NODE, &no_neighbor_damp_cmd); + install_element(BGP_IPV6_NODE, &neighbor_damp_cmd); + install_element(BGP_IPV6_NODE, &no_neighbor_damp_cmd); + install_element(BGP_IPV6M_NODE, &neighbor_damp_cmd); + install_element(BGP_IPV6M_NODE, &no_neighbor_damp_cmd); + install_element(BGP_IPV6L_NODE, &neighbor_damp_cmd); + install_element(BGP_IPV6L_NODE, &no_neighbor_damp_cmd); + install_element(VIEW_NODE, &show_ip_bgp_neighbor_damp_param_cmd); + /* address-family commands. */ install_element(BGP_NODE, &address_family_ipv4_safi_cmd); install_element(BGP_NODE, &address_family_ipv6_safi_cmd); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 0c6f55a82..2386d1e86 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -2379,6 +2379,14 @@ int peer_delete(struct peer *peer) bgp_bfd_deregister_peer(peer); + /* Delete peer route flap dampening configuration. This needs to happen + * before removing the peer from peer groups. + */ + FOREACH_AFI_SAFI (afi, safi) + if (peer_af_flag_check(peer, afi, safi, + PEER_FLAG_CONFIG_DAMPENING)) + bgp_peer_damp_disable(peer, afi, safi); + /* If this peer belongs to peer group, clear up the relationship. */ if (peer->group) { @@ -3500,6 +3508,11 @@ int bgp_delete(struct bgp *bgp) BGP_TIMER_OFF(gr_info->t_route_select); } + /* Delete route flap dampening configuration */ + FOREACH_AFI_SAFI (afi, safi) { + bgp_damp_disable(bgp, afi, safi); + } + if (BGP_DEBUG(zebra, ZEBRA)) { if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) zlog_debug("Deleting Default VRF"); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 85188dd84..7bab6a0e4 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -43,6 +43,7 @@ #include "bgp_labelpool.h" #include "bgp_addpath_types.h" #include "bgp_nexthop.h" +#include "bgp_damp.h" #define BGP_MAX_HOSTNAME 64 /* Linux max, is larger than most other sys */ #define BGP_PEER_MAX_HASH_SIZE 16384 @@ -695,6 +696,9 @@ struct bgp { uint32_t condition_filter_count; struct thread *t_condition_check; + /* BGP route flap dampening configuration */ + struct bgp_damp_config damp[AFI_MAX][SAFI_MAX]; + QOBJ_FIELDS }; DECLARE_QOBJ_TYPE(bgp) @@ -1199,6 +1203,9 @@ struct peer { /* Last update packet sent time */ time_t pkt_stime[AFI_MAX][SAFI_MAX]; + /* Peer / peer group route flap dampening configuration */ + struct bgp_damp_config damp[AFI_MAX][SAFI_MAX]; + /* Peer Per AF flags */ /* * Please consult the comments for *flags_override*, *flags_invert* and @@ -1236,6 +1243,8 @@ struct peer { #define PEER_FLAG_SEND_LARGE_COMMUNITY (1U << 26) /* Send large Communities */ #define PEER_FLAG_MAX_PREFIX_OUT (1U << 27) /* outgoing maximum prefix */ #define PEER_FLAG_MAX_PREFIX_FORCE (1U << 28) /* maximum-prefix <num> force */ +#define PEER_FLAG_CONFIG_DAMPENING (1U << 29) /* route flap dampening */ + enum bgp_addpath_strat addpath_type[AFI_MAX][SAFI_MAX]; diff --git a/doc/user/bgp.rst b/doc/user/bgp.rst index e266be524..586dccb3c 100644 --- a/doc/user/bgp.rst +++ b/doc/user/bgp.rst @@ -480,28 +480,57 @@ Disable checking if nexthop is connected on EBGP sessions Route Flap Dampening -------------------- -.. clicmd:: bgp dampening (1-45) (1-20000) (1-20000) (1-255) +.. index:: [no] bgp dampening [(1-45) [(1-20000) (1-20000) (1-255)]] +.. clicmd:: [no] bgp dampening [(1-45) [(1-20000) (1-20000) (1-255)]] - This command enables BGP route-flap dampening and specifies dampening parameters. + This command enables (with optionally specified dampening parameters) or + disables route-flap dampening for all routes of a BGP instance. + +.. index:: [no] neighbor PEER dampening [(1-45) [(1-20000) (1-20000) (1-255)]] +.. clicmd:: [no] neighbor PEER dampening [(1-45) [(1-20000) (1-20000) (1-255)]] + + This command enables (with optionally specified dampening parameters) or + disables route-flap dampening for all routes learned from a BGP peer. + +.. index:: [no] neighbor GROUP dampening [(1-45) [(1-20000) (1-20000) (1-255)]] +.. clicmd:: [no] neighbor GROUP dampening [(1-45) [(1-20000) (1-20000) (1-255)]] + + This command enables (with optionally specified dampening parameters) or + disables route-flap dampening for all routes learned from peers of a peer + group. half-life - Half-life time for the penalty + Half-life time for the penalty in minutes (default value: 15). reuse-threshold - Value to start reusing a route + Value to start reusing a route (default value: 750). suppress-threshold - Value to start suppressing a route + Value to start suppressing a route (default value: 2000). max-suppress - Maximum duration to suppress a stable route + Maximum duration to suppress a stable route in minutes (default value: + 60). The route-flap damping algorithm is compatible with :rfc:`2439`. The use of - this command is not recommended nowadays. + these commands is not recommended nowadays. At the moment, route-flap dampening is not working per VRF and is working only for IPv4 unicast and multicast. + With different parameter sets configurable for BGP instances, peer groups and + peers, the active dampening profile for a route is chosen on the fly, + allowing for various changes in configuration (i.e. peer group memberships) + during runtime. The parameter sets are taking precedence in the following + order: + + 1. Peer + 2. Peer group + 3. BGP instance + + The negating commands do not allow to exclude a peer/peer group from a peer + group/BGP instances configuration. + .. seealso:: https://www.ripe.net/publications/docs/ripe-378 diff --git a/tests/topotests/bgp_features/exabgp.env b/tests/topotests/bgp_features/exabgp.env new file mode 100644 index 000000000..6c554f5fa --- /dev/null +++ b/tests/topotests/bgp_features/exabgp.env @@ -0,0 +1,53 @@ + +[exabgp.api] +encoder = text +highres = false +respawn = false +socket = '' + +[exabgp.bgp] +openwait = 60 + +[exabgp.cache] +attributes = true +nexthops = true + +[exabgp.daemon] +daemonize = true +pid = '/var/run/exabgp/exabgp.pid' +user = 'exabgp' + +[exabgp.log] +all = false +configuration = true +daemon = true +destination = '/var/log/exabgp.log' +enable = true +level = INFO +message = false +network = true +packets = false +parser = false +processes = true +reactor = true +rib = false +routes = false +short = false +timers = false + +[exabgp.pdb] +enable = false + +[exabgp.profile] +enable = false +file = '' + +[exabgp.reactor] +speed = 1.0 + +[exabgp.tcp] +acl = false +bind = '' +delay = 0 +once = false +port = 179 diff --git a/tests/topotests/bgp_features/peer1/exa_readpipe.py b/tests/topotests/bgp_features/peer1/exa_readpipe.py new file mode 100644 index 000000000..dba153638 --- /dev/null +++ b/tests/topotests/bgp_features/peer1/exa_readpipe.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +"Helper script to read api commands from a pipe and feed them to ExaBGP" + +import sys + +if len(sys.argv) != 2: + sys.exit(1) +fifo = sys.argv[1] + +while True: + pipe = open(fifo, 'r') + with pipe: + line = pipe.readline().strip() + if line != "": + sys.stdout.write("{}\n".format(line)) + sys.stdout.flush() + pipe.close() + +sys.exit(0) diff --git a/tests/topotests/bgp_features/peer1/exabgp.cfg b/tests/topotests/bgp_features/peer1/exabgp.cfg new file mode 100644 index 000000000..2e95252cf --- /dev/null +++ b/tests/topotests/bgp_features/peer1/exabgp.cfg @@ -0,0 +1,12 @@ +group exabgp { + process announce-routes { + run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer1.in"; + encoder text; + } + neighbor 192.168.101.1 { + router-id 192.168.101.3; + local-address 192.168.101.3; + local-as 65403; + peer-as 65000; + } +} diff --git a/tests/topotests/bgp_features/peer2/exa_readpipe.py b/tests/topotests/bgp_features/peer2/exa_readpipe.py new file mode 100644 index 000000000..dba153638 --- /dev/null +++ b/tests/topotests/bgp_features/peer2/exa_readpipe.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +"Helper script to read api commands from a pipe and feed them to ExaBGP" + +import sys + +if len(sys.argv) != 2: + sys.exit(1) +fifo = sys.argv[1] + +while True: + pipe = open(fifo, 'r') + with pipe: + line = pipe.readline().strip() + if line != "": + sys.stdout.write("{}\n".format(line)) + sys.stdout.flush() + pipe.close() + +sys.exit(0) diff --git a/tests/topotests/bgp_features/peer2/exabgp.cfg b/tests/topotests/bgp_features/peer2/exabgp.cfg new file mode 100644 index 000000000..1f65547bc --- /dev/null +++ b/tests/topotests/bgp_features/peer2/exabgp.cfg @@ -0,0 +1,12 @@ +group exabgp { + process announce-routes { + run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer2.in"; + encoder text; + } + neighbor 192.168.101.1 { + router-id 192.168.101.4; + local-address 192.168.101.4; + local-as 65404; + peer-as 65000; + } +} diff --git a/tests/topotests/bgp_features/peer3/exa_readpipe.py b/tests/topotests/bgp_features/peer3/exa_readpipe.py new file mode 100644 index 000000000..dba153638 --- /dev/null +++ b/tests/topotests/bgp_features/peer3/exa_readpipe.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +"Helper script to read api commands from a pipe and feed them to ExaBGP" + +import sys + +if len(sys.argv) != 2: + sys.exit(1) +fifo = sys.argv[1] + +while True: + pipe = open(fifo, 'r') + with pipe: + line = pipe.readline().strip() + if line != "": + sys.stdout.write("{}\n".format(line)) + sys.stdout.flush() + pipe.close() + +sys.exit(0) diff --git a/tests/topotests/bgp_features/peer3/exabgp.cfg b/tests/topotests/bgp_features/peer3/exabgp.cfg new file mode 100644 index 000000000..8632cc86c --- /dev/null +++ b/tests/topotests/bgp_features/peer3/exabgp.cfg @@ -0,0 +1,12 @@ +group exabgp { + process announce-routes { + run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer3.in"; + encoder text; + } + neighbor 192.168.101.1 { + router-id 192.168.101.5; + local-address 192.168.101.5; + local-as 65405; + peer-as 65000; + } +} diff --git a/tests/topotests/bgp_features/peer4/exa_readpipe.py b/tests/topotests/bgp_features/peer4/exa_readpipe.py new file mode 100644 index 000000000..dba153638 --- /dev/null +++ b/tests/topotests/bgp_features/peer4/exa_readpipe.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +"Helper script to read api commands from a pipe and feed them to ExaBGP" + +import sys + +if len(sys.argv) != 2: + sys.exit(1) +fifo = sys.argv[1] + +while True: + pipe = open(fifo, 'r') + with pipe: + line = pipe.readline().strip() + if line != "": + sys.stdout.write("{}\n".format(line)) + sys.stdout.flush() + pipe.close() + +sys.exit(0) diff --git a/tests/topotests/bgp_features/peer4/exabgp.cfg b/tests/topotests/bgp_features/peer4/exabgp.cfg new file mode 100644 index 000000000..06bc0d6e6 --- /dev/null +++ b/tests/topotests/bgp_features/peer4/exabgp.cfg @@ -0,0 +1,12 @@ +group exabgp { + process announce-routes { + run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer4.in"; + encoder text; + } + neighbor 192.168.101.1 { + router-id 192.168.101.6; + local-address 192.168.101.6; + local-as 65406; + peer-as 65000; + } +} diff --git a/tests/topotests/bgp_features/r1/bgp_damp_announced.json b/tests/topotests/bgp_features/r1/bgp_damp_announced.json new file mode 100644 index 000000000..cb4a2c9b2 --- /dev/null +++ b/tests/topotests/bgp_features/r1/bgp_damp_announced.json @@ -0,0 +1,21 @@ +{ + "localAS":65000, + "routes":{ + "192.168.31.0/24": [ { "valid":true, "network":"192.168.31.0\/24", "peerId":"192.168.101.3" } ], + "192.168.32.0/24": [ { "valid":true, "network":"192.168.32.0\/24", "peerId":"192.168.101.3" } ], + "192.168.33.0/24": [ { "valid":true, "network":"192.168.33.0\/24", "peerId":"192.168.101.3" } ], + "192.168.34.0/24": [ { "valid":true, "network":"192.168.34.0\/24", "peerId":"192.168.101.3" } ], + "192.168.41.0/24": [ { "valid":true, "network":"192.168.41.0\/24", "peerId":"192.168.101.4" } ], + "192.168.42.0/24": [ { "valid":true, "network":"192.168.42.0\/24", "peerId":"192.168.101.4" } ], + "192.168.43.0/24": [ { "valid":true, "network":"192.168.43.0\/24", "peerId":"192.168.101.4" } ], + "192.168.44.0/24": [ { "valid":true, "network":"192.168.44.0\/24", "peerId":"192.168.101.4" } ], + "192.168.51.0/24": [ { "valid":true, "network":"192.168.51.0\/24", "peerId":"192.168.101.5" } ], + "192.168.52.0/24": [ { "valid":true, "network":"192.168.52.0\/24", "peerId":"192.168.101.5" } ], + "192.168.53.0/24": [ { "valid":true, "network":"192.168.53.0\/24", "peerId":"192.168.101.5" } ], + "192.168.54.0/24": [ { "valid":true, "network":"192.168.54.0\/24", "peerId":"192.168.101.5" } ], + "192.168.61.0/24": [ { "valid":true, "network":"192.168.61.0\/24", "peerId":"192.168.101.6" } ], + "192.168.62.0/24": [ { "valid":true, "network":"192.168.62.0\/24", "peerId":"192.168.101.6" } ], + "192.168.63.0/24": [ { "valid":true, "network":"192.168.63.0\/24", "peerId":"192.168.101.6" } ], + "192.168.64.0/24": [ { "valid":true, "network":"192.168.64.0\/24", "peerId":"192.168.101.6" } ] + } +} diff --git a/tests/topotests/bgp_features/r1/bgp_damp_setup.json b/tests/topotests/bgp_features/r1/bgp_damp_setup.json new file mode 100644 index 000000000..f9f89db89 --- /dev/null +++ b/tests/topotests/bgp_features/r1/bgp_damp_setup.json @@ -0,0 +1,10 @@ +{ + "ipv4Unicast":{ + "peers":{ + "192.168.101.3":{"remoteAs":65403, "state":"Established"}, + "192.168.101.4":{"remoteAs":65404, "state":"Established"}, + "192.168.101.5":{"remoteAs":65405, "state":"Established"}, + "192.168.101.6":{"remoteAs":65406, "state":"Established"} + } + } +} diff --git a/tests/topotests/bgp_features/r2/bgp_damp_announced.json b/tests/topotests/bgp_features/r2/bgp_damp_announced.json new file mode 100644 index 000000000..9394358f8 --- /dev/null +++ b/tests/topotests/bgp_features/r2/bgp_damp_announced.json @@ -0,0 +1,21 @@ +{ + "localAS":65000, + "routes":{ + "192.168.31.0/24": [ { "network":"192.168.31.0\/24", "peerId":"192.168.0.1" } ], + "192.168.32.0/24": [ { "network":"192.168.32.0\/24", "peerId":"192.168.0.1" } ], + "192.168.33.0/24": [ { "network":"192.168.33.0\/24", "peerId":"192.168.0.1" } ], + "192.168.34.0/24": [ { "network":"192.168.34.0\/24", "peerId":"192.168.0.1" } ], + "192.168.41.0/24": [ { "network":"192.168.41.0\/24", "peerId":"192.168.0.1" } ], + "192.168.42.0/24": [ { "network":"192.168.42.0\/24", "peerId":"192.168.0.1" } ], + "192.168.43.0/24": [ { "network":"192.168.43.0\/24", "peerId":"192.168.0.1" } ], + "192.168.44.0/24": [ { "network":"192.168.44.0\/24", "peerId":"192.168.0.1" } ], + "192.168.51.0/24": [ { "network":"192.168.51.0\/24", "peerId":"192.168.0.1" } ], + "192.168.52.0/24": [ { "network":"192.168.52.0\/24", "peerId":"192.168.0.1" } ], + "192.168.53.0/24": [ { "network":"192.168.53.0\/24", "peerId":"192.168.0.1" } ], + "192.168.54.0/24": [ { "network":"192.168.54.0\/24", "peerId":"192.168.0.1" } ], + "192.168.61.0/24": [ { "network":"192.168.61.0\/24", "peerId":"192.168.0.1" } ], + "192.168.62.0/24": [ { "network":"192.168.62.0\/24", "peerId":"192.168.0.1" } ], + "192.168.63.0/24": [ { "network":"192.168.63.0\/24", "peerId":"192.168.0.1" } ], + "192.168.64.0/24": [ { "network":"192.168.64.0\/24", "peerId":"192.168.0.1" } ] + } +} diff --git a/tests/topotests/bgp_features/r2/bgp_damp_withdrawn.json b/tests/topotests/bgp_features/r2/bgp_damp_withdrawn.json new file mode 100644 index 000000000..f3c54a70a --- /dev/null +++ b/tests/topotests/bgp_features/r2/bgp_damp_withdrawn.json @@ -0,0 +1,18 @@ +{ + "192.168.31.0/24": null, + "192.168.32.0/24": null, + "192.168.33.0/24": null, + "192.168.34.0/24": null, + "192.168.41.0/24": null, + "192.168.42.0/24": null, + "192.168.43.0/24": null, + "192.168.44.0/24": null, + "192.168.51.0/24": null, + "192.168.52.0/24": null, + "192.168.53.0/24": null, + "192.168.54.0/24": null, + "192.168.61.0/24": null, + "192.168.62.0/24": null, + "192.168.63.0/24": null, + "192.168.64.0/24": null +} diff --git a/tests/topotests/bgp_features/test_bgp_features.py b/tests/topotests/bgp_features/test_bgp_features.py index 5f3809c2b..3d963b4cf 100644 --- a/tests/topotests/bgp_features/test_bgp_features.py +++ b/tests/topotests/bgp_features/test_bgp_features.py @@ -33,6 +33,7 @@ import sys import pytest import re import time +from time import sleep # Save the Current Working Directory to find configuration files. CWD = os.path.dirname(os.path.realpath(__file__)) @@ -64,6 +65,14 @@ class BGPFeaturesTopo1(Topo): for rtrNum in range(1, 6): tgen.add_router("r{}".format(rtrNum)) + # create ExaBGP peers + for peer_num in range(1, 5): + tgen.add_exabgp_peer( + "peer{}".format(peer_num), + ip="192.168.101.{}".format(peer_num + 2), + defaultRoute="via 192.168.101.1", + ) + # Setup Switches and connections for swNum in range(1, 11): tgen.add_switch("sw{}".format(swNum)) @@ -89,6 +98,12 @@ class BGPFeaturesTopo1(Topo): tgen.gears["r2"].add_link(tgen.gears["sw5"]) tgen.gears["r5"].add_link(tgen.gears["sw5"]) + # Add ExaBGP peers to sw4 + tgen.gears["peer1"].add_link(tgen.gears["sw4"]) + tgen.gears["peer2"].add_link(tgen.gears["sw4"]) + tgen.gears["peer3"].add_link(tgen.gears["sw4"]) + tgen.gears["peer4"].add_link(tgen.gears["sw4"]) + ##################################################### # @@ -1093,6 +1108,662 @@ def test_bgp_delayopen_dual(): # end test_bgp_delayopen_dual +def test_bgp_dampening_setup(): + "BGP route-flap dampening test setup" + + # This test starts four ExaBGP peers, adds them as neighbors to the + # configuration of router r1 and checks if connections get established. + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Starting BGP route-flap dampening test setup") + + # Start ExaBGP peers connected to r1 via switch 4 + logger.info("Starting ExaBGP peers") + for peer_num in range(1, 5): + logger.info("Creating named pipe for ExaBGP peer peer{}".format(peer_num)) + fifo_in = "/var/run/exabgp_peer{}.in".format(peer_num) + if os.path.exists(fifo_in): + os.remove(fifo_in) + os.mkfifo(fifo_in, 0o777) + logger.info("Starting ExaBGP on peer peer{}".format(peer_num)) + peer = tgen.gears["peer{}".format(peer_num)] + peer_dir = os.path.join(CWD, "peer{}".format(peer_num)) + env_file = os.path.join(CWD, "exabgp.env") + peer.start(peer_dir, env_file) + + # Add ExaBGP peers to configuration of router r2 + logger.info("Adding ExaBGP peers as neighbors to configuration of router r2") + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.3 remote-as 65403"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.3 route-map testmap-in"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.3 route-map testmap-out"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.4 remote-as 65404"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.4 route-map testmap-in"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.4 route-map testmap-out"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.5 remote-as 65405"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.5 route-map testmap-in"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.5 route-map testmap-out"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.6 remote-as 65406"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.6 route-map testmap-in"' + ) + tgen.net["r1"].cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.6 route-map testmap-out"' + ) + + # Check if exabgp peers are up and running + logger.info("Checking for established connections to ExaBGP peers on router r1") + router = tgen.gears["r1"] + reffile = os.path.join(CWD, "r1/bgp_damp_setup.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp summary json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertmsg = ( + "BGP session on r1 did not establish connections with one ore more ExaBGP peers" + ) + assert res is None, assertmsg + + # end test_bgp_dampening_setup + + +def test_bgp_dampening_route_announce(): + "Test of BGP route-flap dampening route announcement" + + # This test checks if the four ExaBGP peers can announce routes to router + # r1 and if these routes get forwarded to router r2. + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Starting test of BGP route-flap dampening route announcement") + + # Announce routes on exabgp peers to r2 + logger.info("Announcing routes on ExaBGP peers to r1") + for prefix_iter in range(1, 5): + for peer_num in range(1, 5): + pipe = open("/run/exabgp_peer{}.in".format(peer_num), "w") + with pipe: + pipe.write( + "announce route 192.168.{}{}.0/24 next-hop 192.168.101.{}\n".format( + (peer_num + 2), prefix_iter, (peer_num + 2) + ) + ) + pipe.close() + sleep(0.1) # ExaBGP API command processing delay + + # Check if routes announced by ExaBGP peers are present in RIB of router r1 + logger.info( + "Checking if routes announced by ExaBGP peers are present in RIB of router r1" + ) + router = tgen.gears["r1"] + reffile = os.path.join(CWD, "r1/bgp_damp_announced.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertmsg = ( + "BGP session on router r1 did not receive routes announced by ExaBGP peers" + ) + assert res is None, assertmsg + + # Check if routes announced by ExaBGP peers to router r1 have been forwarded + # and are now present in RIB of router r2 + logger.info( + "Checking if forwarded routes announced by ExaBGP peers are present in RIB of router r2" + ) + router = tgen.gears["r2"] + reffile = os.path.join(CWD, "r2/bgp_damp_announced.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertmsg = "BGP session on router r2 did not receive routes announced by ExaBGP peers forwarded by router r1" + assert res is None, assertmsg + + # end test_bgp_dampening_route_announce + + +def test_bgp_dampening_disabled(): + "Test of BGP route-flapping with dampening disabled" + + # This test verifies that flapped routes do not get withdrawn from the RIB + # of router r1 if dampening is disabled. + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Starting test of BGP route-flapping with dampening disabled") + + # Flapping routes on ExaBGP peer peer1 + logger.info( + "Flapping routes on ExaBGP peer peer1 with route-flap dampening disabled" + ) + for _ in range(1, 5): + for prefix_iter in range(1, 5): + pipe = open("/run/exabgp_peer1.in", "w") + with pipe: + pipe.write( + "withdraw route 192.168.3{}.0/24 next-hop 192.168.101.3\n".format( + prefix_iter + ) + ) + pipe.close() + sleep(0.1) # ExaBGP API command processing delay + sleep(1) # Give the BGP session on router r1 time to process routes + for prefix_iter in range(1, 5): + pipe = open("/run/exabgp_peer1.in", "w") + with pipe: + pipe.write( + "announce route 192.168.3{}.0/24 next-hop 192.168.101.3\n".format( + prefix_iter + ) + ) + pipe.close() + sleep(0.1) # ExaBGP API command processing delay + + # Verify flapped routes are still present in RIB of router r1 + logger.info( + "Verifying that the flapped routes are still present in RIB of router r1" + ) + router = tgen.gears["r1"] + reffile = os.path.join(CWD, "r1/bgp_damp_announced.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=1) + assertmsg = "BGP session on router r1 removed flapped routes despite route-flap dampening being disabled" + assert res is None, assertmsg + + # end test_bgp_dampening_disabled + + +def test_bgp_dampening_config(): + "Test of BGP route-flap dampening configuration" + + # This test adds peer-group group1 with peers peer1 and peer2 to the + # configuration of router r1, sets up dampening configurations with + # different profiles and verifies the configured dampening parameters. + + tgen = get_topogen() + r_1 = tgen.net["r1"] + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Starting test of BGP route-flap dampening configuration") + + # Add peer-group group1 with peers peer1 and peer2 + logger.info( + "Creating peer-group group1 and adding ExaBGP peers peer1 and peer2 to it" + ) + r_1.cmd('vtysh -c "conf t" -c "router bgp 65000" -c "neighbor group1 peer-group"') + r_1.cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.3 peer-group group1"' + ) # peer1 + r_1.cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.4 peer-group group1"' + ) # peer2 + + # Enable different dampening profiles for peer1, peer3, group1 and global + # configuration + logger.info( + "Enabling different dampening profiles for peer1, peer3, group1 and global configuration" + ) + r_1.cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "bgp dampening 30 300 900 90"' + ) + r_1.cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor group1 dampening 20 200 600 60"' + ) + r_1.cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.3 dampening 10 100 300 30"' + ) # peer1 + r_1.cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.5 dampening 10 100 300 30"' + ) # peer3 + + # Verify route-flap dampening configuration + logger.info("Verifying route-flap dampening configuration on router r1") + vtyout = r_1.cmd('vtysh -c "show running-config"') + assertmsg = "BGP Session on r1 does not show enabled global route-flap dampening in running configuration" + assert re.search("bgp dampening 30 300 900 90", vtyout), assertmsg + assertmsg = "BGP Session on r1 does not show route-flap dampening enabled for peer-group group1 in running configuration" + assert re.search("neighbor group1 dampening 20 200 600 60", vtyout), assertmsg + assertmsg = "BGP Session on r1 does not show route-flap dampening enabled for peer peer1 in running configuration" + assert re.search( + "neighbor 192.168.101.3 dampening 10 100 300 30", vtyout + ), assertmsg + assertmsg = "BGP Session on r1 does not show route-flap dampening enabled for peer peer3 in running configuration" + assert re.search( + "neighbor 192.168.101.5 dampening 10 100 300 30", vtyout + ), assertmsg + + # end test_bgp_dampening_config + + +def test_bgp_dampening_profile_peer_over_group(): + "Test of BGP route-flap dampening profile preferences: peer over group" + + # This test verifies that the dampening profile of a peer takes precedence + # over the dampening profile of its peer-group by flapping the peers routes + # until dampened and comparing the reuse times to the one specified in the + # dampening configuration. + + tgen = get_topogen() + r_1 = tgen.net["r1"] + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info( + "Starting test of BGP route-flap dampening profile preferences: peer over group" + ) + + # Flapping routes on ExaBGP peer peer1 + logger.info( + "Flapping routes on ExaBGP peer peer1 with route-flap dampening enabled" + ) + for _ in range(1, 5): + for prefix_iter in range(1, 5): + pipe = open("/run/exabgp_peer1.in", "w") + with pipe: + pipe.write( + "withdraw route 192.168.3{}.0/24 next-hop 192.168.101.3\n".format( + prefix_iter + ) + ) + pipe.close() + sleep(0.1) # ExaBGP API command processing delay + sleep(1) # Give the BGP session on router r1 time to process routes + for prefix_iter in range(1, 5): + pipe = open("/run/exabgp_peer1.in", "w") + with pipe: + pipe.write( + "announce route 192.168.3{}.0/24 next-hop 192.168.101.3\n".format( + prefix_iter + ) + ) + pipe.close() + sleep(0.1) # ExaBGP API command processing delay + + # Check damped paths on r1 for routes of peer1 witn peer profile + logger.info( + "Checking if router r1 used the correct dampening profile on routes flapped by ExaBGP peer peer1" + ) + sleep(5) # Wait 5 seconds for paths to show up in dampened-paths list + vtyout = r_1.cmd('vtysh -c "show ip bgp dampening dampened-paths"') + routes = re.findall(r"\*d 192\.168\.3\d\.0\/24.*", vtyout) + assertmsg = ( + "BGP session on router r1 did not dampen routes flapped by ExaBGP peer peer1" + ) + assert len(routes) == 4, assertmsg + assertmsg = "BGP session on router r1 used wrong dampening profile for a route flapped by ExaBGP peer peer1" + for route in routes: + assert (int(route.split()[3].split(":")[0]) == 0) and ( # hours of reuse time + 35 > int(route.split()[3].split(":")[1]) > 25 + ), assertmsg # minutes of reuse time + + # end test_bgp_dampening_profile_peer_over_group + + +def test_bgp_dampening_profile_group_over_global(): + "Test of BGP route-flap dampening profile preferences: group over global" + + # This test verifies that the dampening profile of a peer-group takes + # precedence over the global dampening profile by flapping the routes of a + # peer-group member until dampened and comparing the reuse times to the one + # specified in the dampening configuration. + + tgen = get_topogen() + r_1 = tgen.net["r1"] + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info( + "Starting test of BGP route-flap dampening profile preferences: group over global" + ) + + # Flapping routes on ExaBGP peer peer2 + logger.info( + "Flapping routes on ExaBGP peer peer2 with route-flap dampening enabled" + ) + for _ in range(1, 5): + for prefix_iter in range(1, 5): + pipe = open("/run/exabgp_peer2.in", "w") + with pipe: + pipe.write( + "withdraw route 192.168.4{}.0/24 next-hop 192.168.101.4\n".format( + prefix_iter + ) + ) + pipe.close() + sleep(0.1) # ExaBGP API command processing delay + sleep(1) # Give the BGP session on router r1 time to process routes + for prefix_iter in range(1, 5): + pipe = open("/run/exabgp_peer2.in", "w") + with pipe: + pipe.write( + "announce route 192.168.4{}.0/24 next-hop 192.168.101.4\n".format( + prefix_iter + ) + ) + pipe.close() + sleep(0.1) # ExaBGP API command processing delay + + # Check damped paths on r1 for routes of peer2 witn group profile + logger.info( + "Checking if router r1 used the correct dampening profile on routes flapped by ExaBGP peer peer2" + ) + sleep(5) # wait 5 seconds for paths to shop up in damp list + vtyout = r_1.cmd('vtysh -c "show ip bgp dampening dampened-paths"') + routes = re.findall(r"\*d 192\.168\.4\d\.0\/24.*", vtyout) + assertmsg = ( + "BGP session on router r1 did not dampen routes flapped by ExaBGP peer peer2" + ) + assert len(routes) == 4, assertmsg + assertmsg = "BGP session on router r1 used wrong dampening profile for a route flapped by ExaBGP peer peer2" + for route in routes: + assert (int(route.split()[3].split(":")[0]) == 0) and ( # hours of reuse time + 65 > int(route.split()[3].split(":")[1]) > 55 + ), assertmsg # minutes of reuse time + + # end test_bgp_dampening_profile_group_over_global + + +def test_bgp_dampening_profile_peer_over_global(): + "Test of BGP route-flap dampening profile preferences: peer over global" + + # This test verifies that the dampening profile of a peer takes precedence + # over the global dampening profile by flapping the routes of the peer until + # dampened and comparing the reuse times to the one specified in the + # dampening configuration. + + tgen = get_topogen() + r_1 = tgen.net["r1"] + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info( + "Starting test of BGP route-flap dampening profile preferences: peer over global" + ) + + # Flapping routes on ExaBGP peer peer3 + logger.info( + "Flapping routes on ExaBGP peer peer3 with route-flap dampening enabled" + ) + for _ in range(1, 5): + for prefix_iter in range(1, 5): + pipe = open("/run/exabgp_peer3.in", "w") + with pipe: + pipe.write( + "withdraw route 192.168.5{}.0/24 next-hop 192.168.101.5\n".format( + prefix_iter + ) + ) + pipe.close() + sleep(0.1) # ExaBGP API command processing delay + sleep(1) # Give the BGP session on router r1 time to process routes + for prefix_iter in range(1, 5): + pipe = open("/run/exabgp_peer3.in", "w") + with pipe: + pipe.write( + "announce route 192.168.5{}.0/24 next-hop 192.168.101.5\n".format( + prefix_iter + ) + ) + pipe.close() + sleep(0.1) # ExaBGP API command processing delay + + # Check damped paths on r1 for routes of peer3 witn peer profile + logger.info( + "Checking if router r1 used the correct dampening profile on routes flapped by ExaBGP peer peer3" + ) + sleep(5) # wait 5 seconds for paths to shop up in damp list + vtyout = r_1.cmd('vtysh -c "show ip bgp dampening dampened-paths"') + routes = re.findall(r"\*d 192\.168\.5\d\.0\/24.*", vtyout) + assertmsg = ( + "BGP session on router r1 did not dampen routes flapped by ExaBGP peer peer3" + ) + assert len(routes) == 4, assertmsg + assertmsg = "BGP session on router r1 used wrong dampening profile for a route flapped by ExaBGP peer peer3" + for route in routes: + assert (int(route.split()[3].split(":")[0]) == 0) and ( # hours of reuse time + 35 > int(route.split()[3].split(":")[1]) > 25 + ), assertmsg # minutes of reuse time + + # end test_bgp_dampening_profile_peer_over_global + + +def test_bgp_dampening_profile_global(): + "Test of BGP route-flap dampening global profile" + + # This test verifies the application of the global dampening profile by + # flapping the routes of a peer until dampened and comparing the reuse times + # to the one specified in the dampening configuration. + + tgen = get_topogen() + r_1 = tgen.net["r1"] + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Starting test of BGP route-flap dampening global profile") + + # Flapping routes on ExaBGP peer peer4 + logger.info( + "Flapping routes on ExaBGP peer peer4 with route-flap dampening enabled" + ) + for _ in range(1, 5): + for prefix_iter in range(1, 5): + pipe = open("/run/exabgp_peer4.in", "w") + with pipe: + pipe.write( + "withdraw route 192.168.6{}.0/24 next-hop 192.168.101.6\n".format( + prefix_iter + ) + ) + pipe.close() + sleep(0.1) # ExaBGP API command processing delay + sleep(1) # Give the BGP session on router r1 time to process routes + for prefix_iter in range(1, 5): + pipe = open("/run/exabgp_peer4.in", "w") + with pipe: + pipe.write( + "announce route 192.168.6{}.0/24 next-hop 192.168.101.6\n".format( + prefix_iter + ) + ) + pipe.close() + sleep(0.1) # ExaBGP API command processing delay + + # Check damped paths on r1 for routes of peer4 witn global profile + logger.info( + "Checking if router r1 used the global dampening profile on routes flapped by ExaBGP peer peer4" + ) + sleep(5) # wait 5 seconds for paths to shop up in damp list + vtyout = r_1.cmd('vtysh -c "show ip bgp dampening dampened-paths"') + routes = re.findall(r"\*d 192\.168\.6\d\.0\/24.*", vtyout) + assertmsg = ( + "BGP session on router r1 did not dampen routes flapped by ExaBGP peer peer4" + ) + assert len(routes) == 4, assertmsg + assertmsg = "BGP session on router r1 did not use the global dampening profile for a route flapped by ExaBGP peer peer4" + for route in routes: + assert (int(route.split()[3].split(":")[0]) == 1) and ( # hours of reuse time + 35 > int(route.split()[3].split(":")[1]) > 25 + ), assertmsg # minutes of reuse time + + # end test_bgp_dampening_profile_global + + +def test_bgp_dampening_withdaw(): + "Test BGP route-flap dampening route withdraw" + + # This test verifies that the withrawl of dampened routes from the RIB of + # router r1 was propagated to router r2. + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Starting test of BGP route-flap dampening route withdraw") + + # Check if routes dampened on router r1 have been withdrawn from the RIB on + # router r2 + logger.info( + "Checking if routes dampened on router r1 have been withdrawn of RIB on router r2" + ) + reffile = os.path.join(CWD, "r2/bgp_damp_withdrawn.json") + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, tgen.gears["r2"], "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=5, wait=1) + assertmsg = "BGP session on router r2 did not receive withdraw of routes dampened on router r1" + assert res is None, assertmsg + + # end test_bgp_dampening_withdaw + + +def test_bgp_dampening_cleanup(): + "BGP route-flap dampening test cleanup" + + # This test cleans up after other tests associated with route-flap dampening + # by disabling all dampening configurations, removing added peers and + # peer-groups from the configuration on router r1, and shutting down ExaBGP + # peers peer1, peer2 and peer3. + + tgen = get_topogen() + r_1 = tgen.net["r1"] + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Starting BGP route-flap dampening test cleanup") + + # Disable all dampening configurations + logger.info("Disabling all dampening configurations") + r_1.cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "no bgp dampening"' + ) + r_1.cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "no neighbor group1 dampening"' + ) + r_1.cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "no neighbor 192.168.101.3 dampening"' + ) + r_1.cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "no neighbor 192.168.101.5 dampening"' + ) + + # Remove ExaBGP peers from configuration of router r1 + logger.info("Removing ExaBGP peers from configuration of router r1") + for router_num in range(3, 7): + r_1.cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.101.{}"'.format( + router_num + ) + ) + + # Remove peer-group group1 from configuration of router r1 + logger.info("Removing peer-group group1 peers from configuration of router r1") + r_1.cmd( + 'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor group1 peer-group"' + ) + + # Stop ExaBGP peers and remove associated named pipes + logger.info("Stopping ExaBGP peers and removing associated named pipes") + for peer_num in range(1, 5): + logger.info("Terminating ExaBGP on peer peer{}".format(peer_num)) + peer = tgen.gears["peer{}".format(peer_num)] + logger.info("Removing named pipe of ExaBGP peer peer{}".format(peer_num)) + fifo_in = "/var/run/exabgp_peer{}.in".format(peer_num) + peer.stop() + if os.path.exists(fifo_in): + os.remove(fifo_in) + + # end test_bgp_dampening_cleanup + + +def test_bgp_dampening_aftermath(): + "BGP route-flap dampening aftermath test" + + # This test verifies routers r1 and r2 not being affected by the route-flap + # dampening test series. + + tgen = get_topogen() + + # Skip if previous fatal error condition is raised + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Check BGP Summary on routers r1 and r2 + for rtr_num in [1, 2]: + logger.info( + "Checking if BGP router on r{} remains unaffected by route-flap dampening tests".format( + rtr_num + ) + ) + router = tgen.gears["r{}".format(rtr_num)] + reffile = os.path.join(CWD, "r{}/show_bgp.json".format(rtr_num)) + expected = json.loads(open(reffile).read()) + test_func = functools.partial( + topotest.router_json_cmp, router, "show ip bgp json", expected + ) + _, res = topotest.run_and_expect(test_func, None, count=10, wait=2) + assertmsg = "BGP routes on router r{} are wrong after route-flap dampening tests".format( + rtr_num + ) + assert res is None, assertmsg + + # end test_bgp_dampening_aftermath + + if __name__ == "__main__": args = ["-s"] + sys.argv[1:] sys.exit(pytest.main(args)) |