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