Initial revision
diff --git a/bgpd/bgp_damp.c b/bgpd/bgp_damp.c
new file mode 100644
index 0000000..d70105f
--- /dev/null
+++ b/bgpd/bgp_damp.c
@@ -0,0 +1,648 @@
+/* BGP flap dampening
+ Copyright (C) 2001 IP Infusion Inc.
+
+This file is part of GNU Zebra.
+
+GNU Zebra is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by the
+Free Software Foundation; either version 2, or (at your option) any
+later version.
+
+GNU Zebra is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Zebra; see the file COPYING. If not, write to the Free
+Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
+
+#include <zebra.h>
+#include <math.h>
+
+#include "prefix.h"
+#include "memory.h"
+#include "command.h"
+#include "log.h"
+#include "thread.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"
+
+/* Global variable to access damping configuration */
+struct bgp_damp_config bgp_damp_cfg;
+struct bgp_damp_config *damp = &bgp_damp_cfg;
+
+/* Utility macro to add and delete BGP dampening information to no
+ used list. */
+#define BGP_DAMP_LIST_ADD(N,A) BGP_INFO_ADD(N,A,no_reuse_list)
+#define BGP_DAMP_LIST_DEL(N,A) BGP_INFO_DEL(N,A,no_reuse_list)
+
+/* Calculate reuse list index by penalty value. */
+static int
+bgp_reuse_index (int penalty)
+{
+ int i;
+ int index;
+
+ i = (int)(((double) penalty / damp->reuse_limit - 1.0) * damp->scale_factor);
+
+ if ( i >= damp->reuse_index_size )
+ i = damp->reuse_index_size - 1;
+
+ index = damp->reuse_index[i] - damp->reuse_index[0];
+
+ return (damp->reuse_offset + index) % damp->reuse_list_size;
+}
+
+/* Add BGP dampening information to reuse list. */
+static void
+bgp_reuse_list_add (struct bgp_damp_info *bdi)
+{
+ int index;
+
+ index = bdi->index = bgp_reuse_index (bdi->penalty);
+
+ bdi->prev = NULL;
+ bdi->next = damp->reuse_list[index];
+ if (damp->reuse_list[index])
+ damp->reuse_list[index]->prev = bdi;
+ damp->reuse_list[index] = bdi;
+}
+
+/* Delete BGP dampening information from reuse list. */
+static void
+bgp_reuse_list_delete (struct bgp_damp_info *bdi)
+{
+ if (bdi->next)
+ bdi->next->prev = bdi->prev;
+ if (bdi->prev)
+ bdi->prev->next = bdi->next;
+ else
+ damp->reuse_list[bdi->index] = bdi->next;
+}
+
+/* Return decayed penalty value. */
+int
+bgp_damp_decay (time_t tdiff, int penalty)
+{
+ int i;
+
+ i = (int) ((double) tdiff / DELTA_T);
+
+ if (i == 0)
+ return penalty;
+
+ if (i >= damp->decay_array_size)
+ return 0;
+
+ return (int) (penalty * damp->decay_array[i]);
+}
+
+/* Handler of reuse timer event. Each route in the current reuse-list
+ is evaluated. RFC2439 Section 4.8.7. */
+int
+bgp_reuse_timer (struct thread *t)
+{
+ struct bgp_damp_info *bdi;
+ struct bgp_damp_info *next;
+ time_t t_now, t_diff;
+ struct bgp *bgp;
+ int bgp_process (struct bgp *, struct bgp_node *, afi_t, safi_t);
+
+ damp->t_reuse = NULL;
+ damp->t_reuse =
+ thread_add_timer (master, bgp_reuse_timer, NULL, DELTA_REUSE);
+
+ bgp = bgp_get_default ();
+ if (! bgp)
+ return 0;
+
+ t_now = time (NULL);
+
+ /* 1. save a pointer to the current zeroth queue head and zero the
+ list head entry. */
+ bdi = damp->reuse_list[damp->reuse_offset];
+ damp->reuse_list[damp->reuse_offset] = NULL;
+
+ /* 2. set offset = modulo reuse-list-size ( offset + 1 ), thereby
+ rotating the circular queue of list-heads. */
+ damp->reuse_offset = (damp->reuse_offset + 1) % damp->reuse_list_size;
+
+ /* 3. if ( the saved list head pointer is non-empty ) */
+ for (; bdi; bdi = next)
+ {
+ 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);
+
+ /* Set t-updated = t-now. */
+ bdi->t_updated = t_now;
+
+ /* if (figure-of-merit < reuse). */
+ if (bdi->penalty < damp->reuse_limit)
+ {
+ /* Reuse the route. */
+ UNSET_FLAG (bdi->binfo->flags, BGP_INFO_DAMPED);
+ bdi->suppress_time = 0;
+
+ if (bdi->lastrecord == BGP_RECORD_UPDATE)
+ {
+ UNSET_FLAG (bdi->binfo->flags, BGP_INFO_HISTORY);
+ bgp_aggregate_increment (bgp, &bdi->rn->p, bdi->binfo,
+ bdi->afi, bdi->safi);
+ bgp_process (bgp, bdi->rn, bdi->afi, bdi->safi);
+ }
+
+ if (bdi->penalty <= damp->reuse_limit / 2.0)
+ bgp_damp_info_free (bdi, 1);
+ else
+ BGP_DAMP_LIST_ADD (damp, bdi);
+ }
+ else
+ /* Re-insert into another list (See RFC2439 Section 4.8.6). */
+ bgp_reuse_list_add (bdi);
+ }
+
+ return 0;
+}
+
+/* A route becomes unreachable (RFC2439 Section 4.8.2). */
+int
+bgp_damp_withdraw (struct bgp_info *binfo, struct bgp_node *rn,
+ afi_t afi, safi_t safi, int attr_change)
+{
+ time_t t_now;
+ struct bgp_damp_info *bdi;
+ double last_penalty = 0;
+
+ t_now = time (NULL);
+
+ /* Processing Unreachable Messages. */
+ bdi = binfo->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->binfo = binfo;
+ bdi->rn = rn;
+ 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;
+ binfo->damp_info = bdi;
+ BGP_DAMP_LIST_ADD (damp, 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)
+ + (attr_change ? DEFAULT_PENALTY / 2 : DEFAULT_PENALTY));
+
+ if (bdi->penalty > damp->ceiling)
+ bdi->penalty = damp->ceiling;
+
+ bdi->flap++;
+ }
+
+ bdi->lastrecord = BGP_RECORD_WITHDRAW;
+ bdi->t_updated = t_now;
+
+ /* Make this route as historical status. */
+ SET_FLAG (binfo->flags, BGP_INFO_HISTORY);
+
+ /* Remove the route from a reuse list if it is on one. */
+ if (CHECK_FLAG (bdi->binfo->flags, BGP_INFO_DAMPED))
+ {
+ /* If decay rate isn't equal to 0, reinsert brn. */
+ if (bdi->penalty != last_penalty)
+ {
+ bgp_reuse_list_delete (bdi);
+ bgp_reuse_list_add (bdi);
+ }
+ return BGP_DAMP_SUPPRESSED;
+ }
+
+ /* If not suppressed before, do annonunce this withdraw and
+ insert into reuse_list. */
+ if (bdi->penalty >= damp->suppress_value)
+ {
+ SET_FLAG (bdi->binfo->flags, BGP_INFO_DAMPED);
+ bdi->suppress_time = t_now;
+ BGP_DAMP_LIST_DEL (damp, bdi);
+ bgp_reuse_list_add (bdi);
+ }
+
+ return BGP_DAMP_USED;
+}
+
+int
+bgp_damp_update (struct bgp_info *binfo, struct bgp_node *rn,
+ afi_t afi, safi_t safi)
+{
+ time_t t_now;
+ struct bgp_damp_info *bdi;
+ int status;
+
+ bdi = binfo->damp_info;
+ if (! bdi)
+ return BGP_DAMP_USED;
+
+ t_now = time (NULL);
+ UNSET_FLAG (binfo->flags, BGP_INFO_HISTORY);
+
+ bdi->lastrecord = BGP_RECORD_UPDATE;
+ bdi->penalty = bgp_damp_decay (t_now - bdi->t_updated, bdi->penalty);
+
+ if (! CHECK_FLAG (bdi->binfo->flags, BGP_INFO_DAMPED)
+ && (bdi->penalty < damp->suppress_value))
+ status = BGP_DAMP_USED;
+ else if (CHECK_FLAG (bdi->binfo->flags, BGP_INFO_DAMPED)
+ && (bdi->penalty < damp->reuse_limit) )
+ {
+ UNSET_FLAG (bdi->binfo->flags, BGP_INFO_DAMPED);
+ bgp_reuse_list_delete (bdi);
+ BGP_DAMP_LIST_ADD (damp, bdi);
+ bdi->suppress_time = 0;
+ status = BGP_DAMP_USED;
+ }
+ else
+ status = BGP_DAMP_SUPPRESSED;
+
+ if (bdi->penalty > damp->reuse_limit / 2.0)
+ bdi->t_updated = t_now;
+ else
+ bgp_damp_info_free (bdi, 0);
+
+ return status;
+}
+
+/* Remove dampening information and history route. */
+int
+bgp_damp_scan (struct bgp_info *binfo, afi_t afi, safi_t safi)
+{
+ time_t t_now, t_diff;
+ struct bgp_damp_info *bdi;
+
+ t_now = time (NULL);
+ bdi = binfo->damp_info;
+
+ if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED))
+ {
+ t_diff = t_now - bdi->suppress_time;
+
+ if (t_diff >= damp->max_suppress_time)
+ {
+ UNSET_FLAG (binfo->flags, BGP_INFO_DAMPED);
+ bgp_reuse_list_delete (bdi);
+ BGP_DAMP_LIST_ADD (damp, bdi);
+ bdi->penalty = damp->reuse_limit;
+ bdi->suppress_time = 0;
+ bdi->t_updated = t_now;
+
+ /* Need to announce UPDATE once this binfo is usable again. */
+ if (bdi->lastrecord == BGP_RECORD_UPDATE)
+ return 1;
+ else
+ return 0;
+ }
+ }
+ else
+ {
+ t_diff = t_now - bdi->t_updated;
+ bdi->penalty = bgp_damp_decay (t_diff, bdi->penalty);
+
+ if (bdi->penalty <= damp->reuse_limit / 2.0)
+ {
+ /* release the bdi, bdi->binfo. */
+ bgp_damp_info_free (bdi, 1);
+ return 0;
+ }
+ else
+ bdi->t_updated = t_now;
+ }
+ return 0;
+}
+
+void
+bgp_damp_info_free (struct bgp_damp_info *bdi, int withdraw)
+{
+ struct bgp_info *binfo;
+ void bgp_info_delete (struct bgp_node *, struct bgp_info *);
+ void bgp_info_free (struct bgp_info *);
+
+ if (! bdi)
+ return;
+
+ binfo = bdi->binfo;
+ binfo->damp_info = NULL;
+
+ if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED))
+ bgp_reuse_list_delete (bdi);
+ else
+ BGP_DAMP_LIST_DEL (damp, bdi);
+
+ UNSET_FLAG (binfo->flags, BGP_INFO_DAMPED);
+ UNSET_FLAG (binfo->flags, BGP_INFO_HISTORY);
+
+ if (bdi->lastrecord == BGP_RECORD_WITHDRAW && withdraw)
+ {
+ bgp_info_delete (bdi->rn, binfo);
+ bgp_info_free (binfo);
+ bgp_unlock_node (bdi->rn);
+ }
+ XFREE (MTYPE_BGP_DAMP_INFO, bdi);
+}
+
+void
+bgp_damp_parameter_set (int hlife, int reuse, int sup, int maxsup)
+{
+ double reuse_max_ratio;
+ int i;
+ double j;
+
+ damp->suppress_value = sup;
+ damp->half_life = hlife;
+ damp->reuse_limit = reuse;
+ damp->max_suppress_time = maxsup;
+
+ /* Initialize params per bgp_damp_config. */
+ damp->reuse_index_size = REUSE_ARRAY_SIZE;
+
+ damp->ceiling = (int)(damp->reuse_limit * (pow(2, (double)damp->max_suppress_time/damp->half_life)));
+
+ /* Decay-array computations */
+ damp->decay_array_size = ceil ((double) damp->max_suppress_time / DELTA_T);
+ damp->decay_array = XMALLOC (MTYPE_BGP_DAMP_ARRAY,
+ sizeof(double) * (damp->decay_array_size));
+ damp->decay_array[0] = 1.0;
+ damp->decay_array[1] = exp ((1.0/((double)damp->half_life/DELTA_T)) * log(0.5));
+
+ /* Calculate decay values for all possible times */
+ for (i = 2; i < damp->decay_array_size; i++)
+ damp->decay_array[i] = damp->decay_array[i-1] * damp->decay_array[1];
+
+ /* Reuse-list computations */
+ i = ceil ((double)damp->max_suppress_time / DELTA_REUSE) + 1;
+ if (i > REUSE_LIST_SIZE || i == 0)
+ i = REUSE_LIST_SIZE;
+ damp->reuse_list_size = i;
+
+ damp->reuse_list = XCALLOC (MTYPE_BGP_DAMP_ARRAY,
+ damp->reuse_list_size
+ * sizeof (struct bgp_reuse_node *));
+ memset (damp->reuse_list, 0x00,
+ damp->reuse_list_size * sizeof (struct bgp_reuse_node *));
+
+ /* Reuse-array computations */
+ damp->reuse_index = XMALLOC (MTYPE_BGP_DAMP_ARRAY,
+ sizeof(int) * damp->reuse_index_size);
+ memset (damp->reuse_index, 0x00,
+ damp->reuse_list_size * sizeof (int));
+
+ reuse_max_ratio = (double)damp->ceiling/damp->reuse_limit;
+ j = (exp((double)damp->max_suppress_time/damp->half_life) * log10(2.0));
+ if ( reuse_max_ratio > j && j != 0 )
+ reuse_max_ratio = j;
+
+ damp->scale_factor = (double)damp->reuse_index_size/(reuse_max_ratio - 1);
+
+ for (i = 0; i < damp->reuse_index_size; i++)
+ {
+ damp->reuse_index[i] =
+ (int)(((double)damp->half_life / DELTA_REUSE)
+ * log10 (1.0 / (damp->reuse_limit * ( 1.0 + ((double)i/damp->scale_factor)))) / log10(0.5));
+ }
+}
+
+int
+bgp_damp_enable (struct bgp *bgp, afi_t afi, safi_t safi, int half,
+ int reuse, int suppress, int max)
+{
+ if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING))
+ {
+ if (damp->half_life == half
+ && damp->reuse_limit == reuse
+ && damp->suppress_value == suppress
+ && damp->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);
+
+ /* Register reuse timer. */
+ if (! damp->t_reuse)
+ damp->t_reuse =
+ thread_add_timer (master, bgp_reuse_timer, NULL, DELTA_REUSE);
+
+ return 0;
+}
+
+void
+bgp_damp_config_clean (struct bgp_damp_config *damp)
+{
+ /* Free decay array */
+ XFREE (MTYPE_BGP_DAMP_ARRAY, damp->decay_array);
+
+ /* Free reuse index array */
+ XFREE (MTYPE_BGP_DAMP_ARRAY, damp->reuse_index);
+
+ /* Free reuse list array. */
+ XFREE (MTYPE_BGP_DAMP_ARRAY, damp->reuse_list);
+}
+
+/* Clean all the bgp_damp_info stored in reuse_list. */
+void
+bgp_damp_info_clean ()
+{
+ int i;
+ struct bgp_damp_info *bdi, *next;
+
+ damp->reuse_offset = 0;
+
+ for (i = 0; i < damp->reuse_list_size; i++)
+ {
+ if (! damp->reuse_list[i])
+ continue;
+
+ for (bdi = damp->reuse_list[i]; bdi; bdi = next)
+ {
+ next = bdi->next;
+ bgp_damp_info_free (bdi, 1);
+ }
+ damp->reuse_list[i] = NULL;
+ }
+
+ for (bdi = damp->no_reuse_list; bdi; bdi = next)
+ {
+ next = bdi->next;
+ bgp_damp_info_free (bdi, 1);
+ }
+ damp->no_reuse_list = NULL;
+}
+
+int
+bgp_damp_disable (struct bgp *bgp, afi_t afi, safi_t safi)
+{
+ /* Cancel reuse thread. */
+ if (damp->t_reuse )
+ thread_cancel (damp->t_reuse);
+ damp->t_reuse = NULL;
+
+ /* Clean BGP dampening information. */
+ bgp_damp_info_clean ();
+
+ /* Clear configuration */
+ bgp_damp_config_clean (&bgp_damp_cfg);
+
+ UNSET_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING);
+ return 0;
+}
+
+int
+bgp_config_write_damp (struct vty *vty)
+{
+ if (&bgp_damp_cfg)
+ {
+ if (bgp_damp_cfg.half_life == DEFAULT_HALF_LIFE*60
+ && bgp_damp_cfg.reuse_limit == DEFAULT_REUSE
+ && bgp_damp_cfg.suppress_value == DEFAULT_SUPPRESS
+ && bgp_damp_cfg.max_suppress_time == bgp_damp_cfg.half_life*4)
+ vty_out (vty, " bgp dampening%s", VTY_NEWLINE);
+ else if (bgp_damp_cfg.half_life != DEFAULT_HALF_LIFE*60
+ && bgp_damp_cfg.reuse_limit == DEFAULT_REUSE
+ && bgp_damp_cfg.suppress_value == DEFAULT_SUPPRESS
+ && bgp_damp_cfg.max_suppress_time == bgp_damp_cfg.half_life*4)
+ vty_out (vty, " bgp dampening %d%s",
+ bgp_damp_cfg.half_life/60,
+ VTY_NEWLINE);
+ else
+ vty_out (vty, " bgp dampening %d %d %d %d%s",
+ bgp_damp_cfg.half_life/60,
+ bgp_damp_cfg.reuse_limit,
+ bgp_damp_cfg.suppress_value,
+ bgp_damp_cfg.max_suppress_time/60,
+ VTY_NEWLINE);
+ return 1;
+ }
+ return 0;
+}
+
+#define BGP_UPTIME_LEN 25
+
+char *
+bgp_get_reuse_time (int penalty, char *buf, size_t len)
+{
+ time_t reuse_time = 0;
+ struct tm *tm = NULL;
+
+ if (penalty > damp->reuse_limit)
+ {
+ reuse_time = (int) (DELTA_T * ((log((double)damp->reuse_limit/penalty))/(log(damp->decay_array[1]))));
+
+ if (reuse_time > damp->max_suppress_time)
+ reuse_time = damp->max_suppress_time;
+
+ tm = gmtime (&reuse_time);
+ }
+ else
+ reuse_time = 0;
+
+ /* Making formatted timer strings. */
+#define ONE_DAY_SECOND 60*60*24
+#define ONE_WEEK_SECOND 60*60*24*7
+ if (reuse_time == 0)
+ snprintf (buf, len, "00:00:00");
+ else if (reuse_time < ONE_DAY_SECOND)
+ snprintf (buf, len, "%02d:%02d:%02d",
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+ else if (reuse_time < ONE_WEEK_SECOND)
+ snprintf (buf, len, "%dd%02dh%02dm",
+ tm->tm_yday, tm->tm_hour, tm->tm_min);
+ 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_info *binfo)
+{
+ struct bgp_damp_info *bdi;
+ time_t t_now, t_diff;
+ char timebuf[BGP_UPTIME_LEN];
+ int penalty;
+
+ /* BGP dampening information. */
+ bdi = binfo->damp_info;
+
+ /* If dampening is not enabled or there is no dampening information,
+ return immediately. */
+ if (! damp || ! bdi)
+ return;
+
+ /* Calculate new penalty. */
+ t_now = time (NULL);
+ t_diff = t_now - bdi->t_updated;
+ penalty = bgp_damp_decay (t_diff, bdi->penalty);
+
+ vty_out (vty, " Dampinfo: penalty %d, flapped %d times in %s",
+ penalty, bdi->flap,
+ peer_uptime (bdi->start_time, timebuf, BGP_UPTIME_LEN));
+
+ if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED)
+ && ! CHECK_FLAG (binfo->flags, BGP_INFO_HISTORY))
+ vty_out (vty, ", reuse in %s",
+ bgp_get_reuse_time (penalty, timebuf, BGP_UPTIME_LEN));
+
+ vty_out (vty, "%s", VTY_NEWLINE);
+}
+
+char *
+bgp_damp_reuse_time_vty (struct vty *vty, struct bgp_info *binfo)
+{
+ struct bgp_damp_info *bdi;
+ time_t t_now, t_diff;
+ char timebuf[BGP_UPTIME_LEN];
+ int penalty;
+
+ /* BGP dampening information. */
+ bdi = binfo->damp_info;
+
+ /* If dampening is not enabled or there is no dampening information,
+ return immediately. */
+ if (! damp || ! bdi)
+ return NULL;
+
+ /* Calculate new penalty. */
+ t_now = time (NULL);
+ t_diff = t_now - bdi->t_updated;
+ penalty = bgp_damp_decay (t_diff, bdi->penalty);
+
+ return bgp_get_reuse_time (penalty, timebuf, BGP_UPTIME_LEN);
+}