summaryrefslogtreecommitdiffstats
path: root/bgpd/bgp_damp.c
diff options
context:
space:
mode:
Diffstat (limited to 'bgpd/bgp_damp.c')
-rw-r--r--bgpd/bgp_damp.c731
1 files changed, 731 insertions, 0 deletions
diff --git a/bgpd/bgp_damp.c b/bgpd/bgp_damp.c
new file mode 100644
index 0000000..80425eb
--- /dev/null
+++ b/bgpd/bgp_damp.c
@@ -0,0 +1,731 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* BGP flap dampening
+ * Copyright (C) 2001 IP Infusion Inc.
+ */
+
+#include <zebra.h>
+#include <math.h>
+
+#include "prefix.h"
+#include "memory.h"
+#include "command.h"
+#include "log.h"
+#include "frrevent.h"
+#include "queue.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_damp.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#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];
+
+/* 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)
+
+/* Calculate reuse list index by penalty value. */
+static int bgp_reuse_index(int penalty, struct bgp_damp_config *bdc)
+{
+ unsigned int i;
+ int index;
+
+ /*
+ * reuse_limit can't be zero, this is for Coverity
+ * to bypass division by zero test.
+ */
+ assert(bdc->reuse_limit);
+
+ i = (int)(((double)penalty / bdc->reuse_limit - 1.0)
+ * bdc->scale_factor);
+
+ if (i >= bdc->reuse_index_size)
+ i = bdc->reuse_index_size - 1;
+
+ index = bdc->reuse_index[i] - bdc->reuse_index[0];
+
+ return (bdc->reuse_offset + index) % bdc->reuse_list_size;
+}
+
+/* Add BGP dampening information to reuse list. */
+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;
+}
+
+/* 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;
+}
+
+/* Return decayed penalty value. */
+int bgp_damp_decay(time_t tdiff, int penalty, struct bgp_damp_config *bdc)
+{
+ unsigned int i;
+
+ i = (int)((double)tdiff / DELTA_T);
+
+ if (i == 0)
+ return penalty;
+
+ if (i >= bdc->decay_array_size)
+ return 0;
+
+ return (int)(penalty * bdc->decay_array[i]);
+}
+
+/* Handler of reuse timer event. Each route in the current reuse-list
+ is evaluated. RFC2439 Section 4.8.7. */
+static void bgp_reuse_timer(struct event *t)
+{
+ struct bgp_damp_info *bdi;
+ struct bgp_damp_info *next;
+ time_t t_now, t_diff;
+
+ struct bgp_damp_config *bdc = EVENT_ARG(t);
+
+ bdc->t_reuse = NULL;
+ event_add_timer(bm->master, bgp_reuse_timer, bdc, DELTA_REUSE,
+ &bdc->t_reuse);
+
+ t_now = monotime(NULL);
+
+ /* 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;
+
+ /* 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;
+
+ /* 3. if ( the saved list head pointer is non-empty ) */
+ for (; bdi; bdi = next) {
+ struct bgp *bgp = bdi->path->peer->bgp;
+
+ next = bdi->next;
+
+ /* Set t-diff = t-now - t-updated. */
+ t_diff = t_now - bdi->t_updated;
+
+ /* Set figure-of-merit = figure-of-merit * decay-array-ok
+ * [t-diff] */
+ bdi->penalty = bgp_damp_decay(t_diff, bdi->penalty, bdc);
+
+ /* Set t-updated = t-now. */
+ bdi->t_updated = t_now;
+
+ /* if (figure-of-merit < reuse). */
+ if (bdi->penalty < bdc->reuse_limit) {
+ /* Reuse the route. */
+ bgp_path_info_unset_flag(bdi->dest, bdi->path,
+ BGP_PATH_DAMPED);
+ bdi->suppress_time = 0;
+
+ if (bdi->lastrecord == BGP_RECORD_UPDATE) {
+ bgp_path_info_unset_flag(bdi->dest, bdi->path,
+ BGP_PATH_HISTORY);
+ bgp_aggregate_increment(
+ bgp, bgp_dest_get_prefix(bdi->dest),
+ bdi->path, bdi->afi, bdi->safi);
+ bgp_process(bgp, bdi->dest, bdi->afi,
+ 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
+ /* Re-insert into another list (See RFC2439 Section
+ * 4.8.6). */
+ bgp_reuse_list_add(bdi, bdc);
+ }
+}
+
+/* A route becomes unreachable (RFC2439 Section 4.8.2). */
+int bgp_damp_withdraw(struct bgp_path_info *path, struct bgp_dest *dest,
+ afi_t afi, safi_t safi, int attr_change)
+{
+ time_t t_now;
+ struct bgp_damp_info *bdi = NULL;
+ unsigned int last_penalty = 0;
+ struct bgp_damp_config *bdc = &damp[afi][safi];
+
+ t_now = monotime(NULL);
+
+ /* Processing Unreachable Messages. */
+ if (path->extra)
+ bdi = path->extra->damp_info;
+
+ if (bdi == NULL) {
+ /* If there is no previous stability history. */
+
+ /* RFC2439 said:
+ 1. allocate a damping structure.
+ 2. set figure-of-merit = 1.
+ 3. withdraw the route. */
+
+ bdi = XCALLOC(MTYPE_BGP_DAMP_INFO,
+ sizeof(struct bgp_damp_info));
+ bdi->path = path;
+ bdi->dest = dest;
+ bdi->penalty =
+ (attr_change ? DEFAULT_PENALTY / 2 : DEFAULT_PENALTY);
+ bdi->flap = 1;
+ bdi->start_time = t_now;
+ bdi->suppress_time = 0;
+ bdi->index = -1;
+ bdi->afi = afi;
+ bdi->safi = safi;
+ (bgp_path_info_extra_get(path))->damp_info = bdi;
+ BGP_DAMP_LIST_ADD(bdc, bdi);
+ } else {
+ last_penalty = bdi->penalty;
+
+ /* 1. Set t-diff = t-now - t-updated. */
+ bdi->penalty = (bgp_damp_decay(t_now - bdi->t_updated,
+ bdi->penalty, bdc)
+ + (attr_change ? DEFAULT_PENALTY / 2
+ : DEFAULT_PENALTY));
+
+ if (bdi->penalty > bdc->ceiling)
+ bdi->penalty = bdc->ceiling;
+
+ bdi->flap++;
+ }
+
+ assert((dest == bdi->dest) && (path == bdi->path));
+
+ bdi->lastrecord = BGP_RECORD_WITHDRAW;
+ bdi->t_updated = t_now;
+
+ /* Make this route as historical status. */
+ bgp_path_info_set_flag(dest, path, BGP_PATH_HISTORY);
+
+ /* 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) {
+ bgp_reuse_list_delete(bdi, bdc);
+ bgp_reuse_list_add(bdi, bdc);
+ }
+ return BGP_DAMP_SUPPRESSED;
+ }
+
+ /* If not suppressed before, do annonunce this withdraw and
+ insert into reuse_list. */
+ 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_reuse_list_add(bdi, bdc);
+ }
+
+ return BGP_DAMP_USED;
+}
+
+int bgp_damp_update(struct bgp_path_info *path, struct bgp_dest *dest,
+ afi_t afi, safi_t safi)
+{
+ time_t t_now;
+ struct bgp_damp_info *bdi;
+ int status;
+ struct bgp_damp_config *bdc = &damp[afi][safi];
+
+ if (!path->extra || !((bdi = path->extra->damp_info)))
+ return BGP_DAMP_USED;
+
+ t_now = monotime(NULL);
+ bgp_path_info_unset_flag(dest, path, BGP_PATH_HISTORY);
+
+ bdi->lastrecord = BGP_RECORD_UPDATE;
+ bdi->penalty =
+ bgp_damp_decay(t_now - bdi->t_updated, bdi->penalty, bdc);
+
+ if (!CHECK_FLAG(bdi->path->flags, BGP_PATH_DAMPED)
+ && (bdi->penalty < bdc->suppress_value))
+ status = BGP_DAMP_USED;
+ else if (CHECK_FLAG(bdi->path->flags, BGP_PATH_DAMPED)
+ && (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);
+ bdi->suppress_time = 0;
+ status = BGP_DAMP_USED;
+ } else
+ status = BGP_DAMP_SUPPRESSED;
+
+ if (bdi->penalty > bdc->reuse_limit / 2.0)
+ bdi->t_updated = t_now;
+ else
+ bgp_damp_info_free(bdi, 0, afi, safi);
+
+ return status;
+}
+
+void bgp_damp_info_free(struct bgp_damp_info *bdi, int withdraw, afi_t afi,
+ safi_t safi)
+{
+ struct bgp_path_info *path;
+ struct bgp_damp_config *bdc = &damp[afi][safi];
+
+ if (!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,
+ 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);
+}
+
+static void bgp_damp_parameter_set(time_t hlife, unsigned int reuse,
+ unsigned int sup, time_t maxsup,
+ struct bgp_damp_config *bdc)
+{
+ double reuse_max_ratio;
+ unsigned int i;
+ double j;
+
+ bdc->suppress_value = sup;
+ bdc->half_life = hlife;
+ bdc->reuse_limit = reuse;
+ bdc->max_suppress_time = maxsup;
+
+ /* Initialize params per bgp_damp_config. */
+ bdc->reuse_index_size = REUSE_ARRAY_SIZE;
+
+ bdc->ceiling = (int)(bdc->reuse_limit
+ * (pow(2, (double)bdc->max_suppress_time
+ / bdc->half_life)));
+
+ /* Decay-array computations */
+ bdc->decay_array_size = ceil((double)bdc->max_suppress_time / DELTA_T);
+ bdc->decay_array = XMALLOC(MTYPE_BGP_DAMP_ARRAY,
+ sizeof(double) * (bdc->decay_array_size));
+ bdc->decay_array[0] = 1.0;
+ bdc->decay_array[1] =
+ exp((1.0 / ((double)bdc->half_life / DELTA_T)) * log(0.5));
+
+ /* Calculate decay values for all possible times */
+ for (i = 2; i < bdc->decay_array_size; i++)
+ bdc->decay_array[i] =
+ bdc->decay_array[i - 1] * bdc->decay_array[1];
+
+ /* Reuse-list computations */
+ i = ceil((double)bdc->max_suppress_time / DELTA_REUSE) + 1;
+ if (i > REUSE_LIST_SIZE || i == 0)
+ i = REUSE_LIST_SIZE;
+ bdc->reuse_list_size = i;
+
+ bdc->reuse_list =
+ XCALLOC(MTYPE_BGP_DAMP_ARRAY,
+ bdc->reuse_list_size * sizeof(struct bgp_reuse_node *));
+
+ /* Reuse-array computations */
+ bdc->reuse_index = XCALLOC(MTYPE_BGP_DAMP_ARRAY,
+ sizeof(int) * bdc->reuse_index_size);
+
+ reuse_max_ratio = (double)bdc->ceiling / bdc->reuse_limit;
+ j = (exp((double)bdc->max_suppress_time / bdc->half_life) * log10(2.0));
+ if (reuse_max_ratio > j && j != 0)
+ reuse_max_ratio = j;
+
+ bdc->scale_factor =
+ (double)bdc->reuse_index_size / (reuse_max_ratio - 1);
+
+ for (i = 0; i < bdc->reuse_index_size; i++) {
+ bdc->reuse_index[i] =
+ (int)(((double)bdc->half_life / DELTA_REUSE)
+ * log10(1.0
+ / (bdc->reuse_limit
+ * (1.0
+ + ((double)i / bdc->scale_factor))))
+ / log10(0.5));
+ }
+}
+
+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];
+
+ if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)) {
+ if (bdc->half_life == half && bdc->reuse_limit == reuse
+ && bdc->suppress_value == suppress
+ && bdc->max_suppress_time == max)
+ return 0;
+ bgp_damp_disable(bgp, afi, safi);
+ }
+
+ SET_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING);
+ bgp_damp_parameter_set(half, reuse, suppress, max, bdc);
+
+ /* Register reuse timer. */
+ event_add_timer(bm->master, bgp_reuse_timer, bdc, DELTA_REUSE,
+ &bdc->t_reuse);
+
+ return 0;
+}
+
+static void bgp_damp_config_clean(struct bgp_damp_config *bdc)
+{
+ /* Free decay array */
+ XFREE(MTYPE_BGP_DAMP_ARRAY, bdc->decay_array);
+ bdc->decay_array_size = 0;
+
+ /* Free reuse index array */
+ XFREE(MTYPE_BGP_DAMP_ARRAY, bdc->reuse_index);
+ bdc->reuse_index_size = 0;
+
+ /* Free reuse list array. */
+ 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;
+}
+
+int bgp_damp_disable(struct bgp *bgp, afi_t afi, safi_t safi)
+{
+ struct bgp_damp_config *bdc = &damp[afi][safi];
+ /* 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. */
+ EVENT_OFF(bdc->t_reuse);
+
+ /* Clean BGP dampening information. */
+ bgp_damp_info_clean(afi, safi);
+
+ /* Clear configuration */
+ bgp_damp_config_clean(bdc);
+
+ 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)
+{
+ 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\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
+ 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);
+}
+
+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)
+{
+ time_t reuse_time = 0;
+ struct tm tm;
+ int time_store = 0;
+
+ if (penalty > damp[afi][safi].reuse_limit) {
+ reuse_time = (int)(DELTA_T
+ * ((log((double)damp[afi][safi].reuse_limit
+ / penalty))
+ / (log(damp[afi][safi].decay_array[1]))));
+
+ if (reuse_time > damp[afi][safi].max_suppress_time)
+ reuse_time = damp[afi][safi].max_suppress_time;
+
+ gmtime_r(&reuse_time, &tm);
+ } else
+ reuse_time = 0;
+
+ /* Making formatted timer strings. */
+ if (reuse_time == 0) {
+ if (use_json)
+ json_object_int_add(json, "reuseTimerMsecs", 0);
+ else
+ snprintf(buf, len, "00:00:00");
+ } else if (reuse_time < ONE_DAY_SECOND) {
+ if (use_json) {
+ time_store = (3600000 * tm.tm_hour)
+ + (60000 * tm.tm_min)
+ + (1000 * tm.tm_sec);
+ json_object_int_add(json, "reuseTimerMsecs",
+ time_store);
+ } else
+ snprintf(buf, len, "%02d:%02d:%02d", tm.tm_hour,
+ tm.tm_min, tm.tm_sec);
+ } else if (reuse_time < ONE_WEEK_SECOND) {
+ if (use_json) {
+ time_store = (86400000 * tm.tm_yday)
+ + (3600000 * tm.tm_hour)
+ + (60000 * tm.tm_min)
+ + (1000 * tm.tm_sec);
+ json_object_int_add(json, "reuseTimerMsecs",
+ time_store);
+ } else
+ snprintf(buf, len, "%dd%02dh%02dm", tm.tm_yday,
+ tm.tm_hour, tm.tm_min);
+ } else {
+ if (use_json) {
+ time_store =
+ (604800000 * tm.tm_yday / 7)
+ + (86400000
+ * (tm.tm_yday - ((tm.tm_yday / 7) * 7)))
+ + (3600000 * tm.tm_hour) + (60000 * tm.tm_min)
+ + (1000 * tm.tm_sec);
+ json_object_int_add(json, "reuseTimerMsecs",
+ time_store);
+ } else
+ snprintf(buf, len, "%02dw%dd%02dh", tm.tm_yday / 7,
+ tm.tm_yday - ((tm.tm_yday / 7) * 7),
+ tm.tm_hour);
+ }
+
+ 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)
+{
+ 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];
+
+ if (!path->extra)
+ return;
+
+ /* BGP dampening information. */
+ bdi = path->extra->damp_info;
+
+ /* If dampening is not enabled or there is no dampening information,
+ return immediately. */
+ if (!CHECK_FLAG(path->peer->bgp->af_flags[afi][safi],
+ BGP_CONFIG_DAMPENING) ||
+ !bdi)
+ return;
+
+ /* Calculate new penalty. */
+ t_now = monotime(NULL);
+ t_diff = t_now - bdi->t_updated;
+ penalty = bgp_damp_decay(t_diff, bdi->penalty, bdc);
+
+ if (json_path) {
+ json_object_int_add(json_path, "dampeningPenalty", penalty);
+ json_object_int_add(json_path, "dampeningFlapCount", bdi->flap);
+ peer_uptime(bdi->start_time, timebuf, BGP_UPTIME_LEN, 1,
+ json_path);
+
+ 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);
+ } else {
+ vty_out(vty,
+ " Dampinfo: penalty %d, flapped %d times in %s",
+ penalty, bdi->flap,
+ peer_uptime(bdi->start_time, timebuf, BGP_UPTIME_LEN, 0,
+ json_path));
+
+ 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,
+ 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,
+ json_object *json)
+{
+ struct bgp_damp_info *bdi;
+ time_t t_now, t_diff;
+ int penalty;
+ struct bgp_damp_config *bdc = &damp[afi][safi];
+
+ if (!path->extra)
+ return NULL;
+
+ /* BGP dampening information. */
+ bdi = path->extra->damp_info;
+
+ /* If dampening is not enabled or there is no dampening information,
+ return immediately. */
+ if (!CHECK_FLAG(path->peer->bgp->af_flags[afi][safi],
+ BGP_CONFIG_DAMPENING) ||
+ !bdi)
+ return NULL;
+
+ /* Calculate new penalty. */
+ t_now = monotime(NULL);
+ t_diff = t_now - bdi->t_updated;
+ penalty = bgp_damp_decay(t_diff, bdi->penalty, bdc);
+
+ return bgp_get_reuse_time(penalty, timebuf, len, afi, safi, use_json,
+ json);
+}
+
+static int bgp_print_dampening_parameters(struct bgp *bgp, struct vty *vty,
+ afi_t afi, safi_t safi, bool use_json)
+{
+ if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)) {
+ struct bgp_damp_config *bdc = &damp[afi][safi];
+
+ if (use_json) {
+ json_object *json = json_object_new_object();
+
+ 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);
+
+ vty_json(vty, json);
+ } 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, "dampening not enabled for %s\n",
+ get_afi_safi_str(afi, safi, false));
+
+ return CMD_SUCCESS;
+}
+
+int bgp_show_dampening_parameters(struct vty *vty, afi_t afi, safi_t safi,
+ uint16_t show_flags)
+{
+ struct bgp *bgp;
+ bool use_json = CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+
+ bgp = bgp_get_default();
+
+ if (bgp == NULL) {
+ vty_out(vty, "No BGP process is configured\n");
+ return CMD_WARNING;
+ }
+
+ if (!CHECK_FLAG(show_flags, BGP_SHOW_OPT_AFI_ALL))
+ return bgp_print_dampening_parameters(bgp, vty, afi, safi,
+ use_json);
+
+ if (CHECK_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP)
+ || CHECK_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP6)) {
+ afi = CHECK_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP) ? AFI_IP
+ : AFI_IP6;
+ FOREACH_SAFI (safi) {
+ if (strmatch(get_afi_safi_str(afi, safi, true),
+ "Unknown"))
+ continue;
+
+ if (!use_json)
+ vty_out(vty, "\nFor address family: %s\n\n",
+ get_afi_safi_str(afi, safi, false));
+
+ bgp_print_dampening_parameters(bgp, vty, afi, safi,
+ use_json);
+ }
+ } else {
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (strmatch(get_afi_safi_str(afi, safi, true),
+ "Unknown"))
+ continue;
+
+ if (!use_json)
+ vty_out(vty, "\nFor address family: %s\n",
+ get_afi_safi_str(afi, safi, false));
+
+ bgp_print_dampening_parameters(bgp, vty, afi, safi,
+ use_json);
+ }
+ }
+ return CMD_SUCCESS;
+}