2
Copyright (C) 2001 IP Infusion Inc.
4
This file is part of GNU Zebra.
6
GNU Zebra is free software; you can redistribute it and/or modify it
7
under the terms of the GNU General Public License as published by the
8
Free Software Foundation; either version 2, or (at your option) any
11
GNU Zebra is distributed in the hope that it will be useful, but
12
WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
General Public License for more details.
16
You should have received a copy of the GNU General Public License
17
along with GNU Zebra; see the file COPYING. If not, write to the Free
18
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
30
#include "bgpd/bgpd.h"
31
#include "bgpd/bgp_damp.h"
32
#include "bgpd/bgp_table.h"
33
#include "bgpd/bgp_route.h"
34
#include "bgpd/bgp_attr.h"
35
#include "bgpd/bgp_advertise.h"
37
/* Global variable to access damping configuration */
38
struct bgp_damp_config bgp_damp_cfg;
39
struct bgp_damp_config *damp = &bgp_damp_cfg;
41
/* Utility macro to add and delete BGP dampening information to no
43
#define BGP_DAMP_LIST_ADD(N,A) BGP_INFO_ADD(N,A,no_reuse_list)
44
#define BGP_DAMP_LIST_DEL(N,A) BGP_INFO_DEL(N,A,no_reuse_list)
46
/* Calculate reuse list index by penalty value. */
48
bgp_reuse_index (int penalty)
53
i = (int)(((double) penalty / damp->reuse_limit - 1.0) * damp->scale_factor);
55
if ( i >= damp->reuse_index_size )
56
i = damp->reuse_index_size - 1;
58
index = damp->reuse_index[i] - damp->reuse_index[0];
60
return (damp->reuse_offset + index) % damp->reuse_list_size;
63
/* Add BGP dampening information to reuse list. */
65
bgp_reuse_list_add (struct bgp_damp_info *bdi)
69
index = bdi->index = bgp_reuse_index (bdi->penalty);
72
bdi->next = damp->reuse_list[index];
73
if (damp->reuse_list[index])
74
damp->reuse_list[index]->prev = bdi;
75
damp->reuse_list[index] = bdi;
78
/* Delete BGP dampening information from reuse list. */
80
bgp_reuse_list_delete (struct bgp_damp_info *bdi)
83
bdi->next->prev = bdi->prev;
85
bdi->prev->next = bdi->next;
87
damp->reuse_list[bdi->index] = bdi->next;
90
/* Return decayed penalty value. */
92
bgp_damp_decay (time_t tdiff, int penalty)
96
i = (int) ((double) tdiff / DELTA_T);
101
if (i >= damp->decay_array_size)
104
return (int) (penalty * damp->decay_array[i]);
107
/* Handler of reuse timer event. Each route in the current reuse-list
108
is evaluated. RFC2439 Section 4.8.7. */
110
bgp_reuse_timer (struct thread *t)
112
struct bgp_damp_info *bdi;
113
struct bgp_damp_info *next;
114
time_t t_now, t_diff;
116
int bgp_process (struct bgp *, struct bgp_node *, afi_t, safi_t);
118
damp->t_reuse = NULL;
120
thread_add_timer (master, bgp_reuse_timer, NULL, DELTA_REUSE);
122
bgp = bgp_get_default ();
128
/* 1. save a pointer to the current zeroth queue head and zero the
130
bdi = damp->reuse_list[damp->reuse_offset];
131
damp->reuse_list[damp->reuse_offset] = NULL;
133
/* 2. set offset = modulo reuse-list-size ( offset + 1 ), thereby
134
rotating the circular queue of list-heads. */
135
damp->reuse_offset = (damp->reuse_offset + 1) % damp->reuse_list_size;
137
/* 3. if ( the saved list head pointer is non-empty ) */
138
for (; bdi; bdi = next)
142
/* Set t-diff = t-now - t-updated. */
143
t_diff = t_now - bdi->t_updated;
145
/* Set figure-of-merit = figure-of-merit * decay-array-ok [t-diff] */
146
bdi->penalty = bgp_damp_decay (t_diff, bdi->penalty);
148
/* Set t-updated = t-now. */
149
bdi->t_updated = t_now;
151
/* if (figure-of-merit < reuse). */
152
if (bdi->penalty < damp->reuse_limit)
154
/* Reuse the route. */
155
UNSET_FLAG (bdi->binfo->flags, BGP_INFO_DAMPED);
156
bdi->suppress_time = 0;
158
if (bdi->lastrecord == BGP_RECORD_UPDATE)
160
UNSET_FLAG (bdi->binfo->flags, BGP_INFO_HISTORY);
161
bgp_aggregate_increment (bgp, &bdi->rn->p, bdi->binfo,
162
bdi->afi, bdi->safi);
163
bgp_process (bgp, bdi->rn, bdi->afi, bdi->safi);
166
if (bdi->penalty <= damp->reuse_limit / 2.0)
167
bgp_damp_info_free (bdi, 1);
169
BGP_DAMP_LIST_ADD (damp, bdi);
172
/* Re-insert into another list (See RFC2439 Section 4.8.6). */
173
bgp_reuse_list_add (bdi);
179
/* A route becomes unreachable (RFC2439 Section 4.8.2). */
181
bgp_damp_withdraw (struct bgp_info *binfo, struct bgp_node *rn,
182
afi_t afi, safi_t safi, int attr_change)
185
struct bgp_damp_info *bdi;
186
double last_penalty = 0;
190
/* Processing Unreachable Messages. */
191
bdi = binfo->damp_info;
195
/* If there is no previous stability history. */
198
1. allocate a damping structure.
199
2. set figure-of-merit = 1.
200
3. withdraw the route. */
202
bdi = XCALLOC (MTYPE_BGP_DAMP_INFO, sizeof (struct bgp_damp_info));
205
bdi->penalty = (attr_change ? DEFAULT_PENALTY / 2 : DEFAULT_PENALTY);
207
bdi->start_time = t_now;
208
bdi->suppress_time = 0;
212
binfo->damp_info = bdi;
213
BGP_DAMP_LIST_ADD (damp, bdi);
217
last_penalty = bdi->penalty;
219
/* 1. Set t-diff = t-now - t-updated. */
221
(bgp_damp_decay (t_now - bdi->t_updated, bdi->penalty)
222
+ (attr_change ? DEFAULT_PENALTY / 2 : DEFAULT_PENALTY));
224
if (bdi->penalty > damp->ceiling)
225
bdi->penalty = damp->ceiling;
230
bdi->lastrecord = BGP_RECORD_WITHDRAW;
231
bdi->t_updated = t_now;
233
/* Make this route as historical status. */
234
SET_FLAG (binfo->flags, BGP_INFO_HISTORY);
236
/* Remove the route from a reuse list if it is on one. */
237
if (CHECK_FLAG (bdi->binfo->flags, BGP_INFO_DAMPED))
239
/* If decay rate isn't equal to 0, reinsert brn. */
240
if (bdi->penalty != last_penalty)
242
bgp_reuse_list_delete (bdi);
243
bgp_reuse_list_add (bdi);
245
return BGP_DAMP_SUPPRESSED;
248
/* If not suppressed before, do annonunce this withdraw and
249
insert into reuse_list. */
250
if (bdi->penalty >= damp->suppress_value)
252
SET_FLAG (bdi->binfo->flags, BGP_INFO_DAMPED);
253
bdi->suppress_time = t_now;
254
BGP_DAMP_LIST_DEL (damp, bdi);
255
bgp_reuse_list_add (bdi);
258
return BGP_DAMP_USED;
262
bgp_damp_update (struct bgp_info *binfo, struct bgp_node *rn,
263
afi_t afi, safi_t safi)
266
struct bgp_damp_info *bdi;
269
bdi = binfo->damp_info;
271
return BGP_DAMP_USED;
274
UNSET_FLAG (binfo->flags, BGP_INFO_HISTORY);
276
bdi->lastrecord = BGP_RECORD_UPDATE;
277
bdi->penalty = bgp_damp_decay (t_now - bdi->t_updated, bdi->penalty);
279
if (! CHECK_FLAG (bdi->binfo->flags, BGP_INFO_DAMPED)
280
&& (bdi->penalty < damp->suppress_value))
281
status = BGP_DAMP_USED;
282
else if (CHECK_FLAG (bdi->binfo->flags, BGP_INFO_DAMPED)
283
&& (bdi->penalty < damp->reuse_limit) )
285
UNSET_FLAG (bdi->binfo->flags, BGP_INFO_DAMPED);
286
bgp_reuse_list_delete (bdi);
287
BGP_DAMP_LIST_ADD (damp, bdi);
288
bdi->suppress_time = 0;
289
status = BGP_DAMP_USED;
292
status = BGP_DAMP_SUPPRESSED;
294
if (bdi->penalty > damp->reuse_limit / 2.0)
295
bdi->t_updated = t_now;
297
bgp_damp_info_free (bdi, 0);
302
/* Remove dampening information and history route. */
304
bgp_damp_scan (struct bgp_info *binfo, afi_t afi, safi_t safi)
306
time_t t_now, t_diff;
307
struct bgp_damp_info *bdi;
310
bdi = binfo->damp_info;
312
if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED))
314
t_diff = t_now - bdi->suppress_time;
316
if (t_diff >= damp->max_suppress_time)
318
UNSET_FLAG (binfo->flags, BGP_INFO_DAMPED);
319
bgp_reuse_list_delete (bdi);
320
BGP_DAMP_LIST_ADD (damp, bdi);
321
bdi->penalty = damp->reuse_limit;
322
bdi->suppress_time = 0;
323
bdi->t_updated = t_now;
325
/* Need to announce UPDATE once this binfo is usable again. */
326
if (bdi->lastrecord == BGP_RECORD_UPDATE)
334
t_diff = t_now - bdi->t_updated;
335
bdi->penalty = bgp_damp_decay (t_diff, bdi->penalty);
337
if (bdi->penalty <= damp->reuse_limit / 2.0)
339
/* release the bdi, bdi->binfo. */
340
bgp_damp_info_free (bdi, 1);
344
bdi->t_updated = t_now;
350
bgp_damp_info_free (struct bgp_damp_info *bdi, int withdraw)
352
struct bgp_info *binfo;
353
void bgp_info_delete (struct bgp_node *, struct bgp_info *);
354
void bgp_info_free (struct bgp_info *);
360
binfo->damp_info = NULL;
362
if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED))
363
bgp_reuse_list_delete (bdi);
365
BGP_DAMP_LIST_DEL (damp, bdi);
367
UNSET_FLAG (binfo->flags, BGP_INFO_DAMPED);
368
UNSET_FLAG (binfo->flags, BGP_INFO_HISTORY);
370
if (bdi->lastrecord == BGP_RECORD_WITHDRAW && withdraw)
372
bgp_info_delete (bdi->rn, binfo);
373
bgp_info_free (binfo);
374
bgp_unlock_node (bdi->rn);
376
XFREE (MTYPE_BGP_DAMP_INFO, bdi);
380
bgp_damp_parameter_set (int hlife, int reuse, int sup, int maxsup)
382
double reuse_max_ratio;
386
damp->suppress_value = sup;
387
damp->half_life = hlife;
388
damp->reuse_limit = reuse;
389
damp->max_suppress_time = maxsup;
391
/* Initialize params per bgp_damp_config. */
392
damp->reuse_index_size = REUSE_ARRAY_SIZE;
394
damp->ceiling = (int)(damp->reuse_limit * (pow(2, (double)damp->max_suppress_time/damp->half_life)));
396
/* Decay-array computations */
397
damp->decay_array_size = ceil ((double) damp->max_suppress_time / DELTA_T);
398
damp->decay_array = XMALLOC (MTYPE_BGP_DAMP_ARRAY,
399
sizeof(double) * (damp->decay_array_size));
400
damp->decay_array[0] = 1.0;
401
damp->decay_array[1] = exp ((1.0/((double)damp->half_life/DELTA_T)) * log(0.5));
403
/* Calculate decay values for all possible times */
404
for (i = 2; i < damp->decay_array_size; i++)
405
damp->decay_array[i] = damp->decay_array[i-1] * damp->decay_array[1];
407
/* Reuse-list computations */
408
i = ceil ((double)damp->max_suppress_time / DELTA_REUSE) + 1;
409
if (i > REUSE_LIST_SIZE || i == 0)
411
damp->reuse_list_size = i;
413
damp->reuse_list = XCALLOC (MTYPE_BGP_DAMP_ARRAY,
414
damp->reuse_list_size
415
* sizeof (struct bgp_reuse_node *));
416
memset (damp->reuse_list, 0x00,
417
damp->reuse_list_size * sizeof (struct bgp_reuse_node *));
419
/* Reuse-array computations */
420
damp->reuse_index = XMALLOC (MTYPE_BGP_DAMP_ARRAY,
421
sizeof(int) * damp->reuse_index_size);
422
memset (damp->reuse_index, 0x00,
423
damp->reuse_list_size * sizeof (int));
425
reuse_max_ratio = (double)damp->ceiling/damp->reuse_limit;
426
j = (exp((double)damp->max_suppress_time/damp->half_life) * log10(2.0));
427
if ( reuse_max_ratio > j && j != 0 )
430
damp->scale_factor = (double)damp->reuse_index_size/(reuse_max_ratio - 1);
432
for (i = 0; i < damp->reuse_index_size; i++)
434
damp->reuse_index[i] =
435
(int)(((double)damp->half_life / DELTA_REUSE)
436
* log10 (1.0 / (damp->reuse_limit * ( 1.0 + ((double)i/damp->scale_factor)))) / log10(0.5));
441
bgp_damp_enable (struct bgp *bgp, afi_t afi, safi_t safi, time_t half,
442
unsigned int reuse, unsigned int suppress, time_t max)
444
if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING))
446
if (damp->half_life == half
447
&& damp->reuse_limit == reuse
448
&& damp->suppress_value == suppress
449
&& damp->max_suppress_time == max)
451
bgp_damp_disable (bgp, afi, safi);
454
SET_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING);
455
bgp_damp_parameter_set (half, reuse, suppress, max);
457
/* Register reuse timer. */
460
thread_add_timer (master, bgp_reuse_timer, NULL, DELTA_REUSE);
466
bgp_damp_config_clean (struct bgp_damp_config *damp)
468
/* Free decay array */
469
XFREE (MTYPE_BGP_DAMP_ARRAY, damp->decay_array);
471
/* Free reuse index array */
472
XFREE (MTYPE_BGP_DAMP_ARRAY, damp->reuse_index);
474
/* Free reuse list array. */
475
XFREE (MTYPE_BGP_DAMP_ARRAY, damp->reuse_list);
478
/* Clean all the bgp_damp_info stored in reuse_list. */
480
bgp_damp_info_clean ()
483
struct bgp_damp_info *bdi, *next;
485
damp->reuse_offset = 0;
487
for (i = 0; i < damp->reuse_list_size; i++)
489
if (! damp->reuse_list[i])
492
for (bdi = damp->reuse_list[i]; bdi; bdi = next)
495
bgp_damp_info_free (bdi, 1);
497
damp->reuse_list[i] = NULL;
500
for (bdi = damp->no_reuse_list; bdi; bdi = next)
503
bgp_damp_info_free (bdi, 1);
505
damp->no_reuse_list = NULL;
509
bgp_damp_disable (struct bgp *bgp, afi_t afi, safi_t safi)
511
/* Cancel reuse thread. */
513
thread_cancel (damp->t_reuse);
514
damp->t_reuse = NULL;
516
/* Clean BGP dampening information. */
517
bgp_damp_info_clean ();
519
/* Clear configuration */
520
bgp_damp_config_clean (&bgp_damp_cfg);
522
UNSET_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING);
527
bgp_config_write_damp (struct vty *vty)
531
if (bgp_damp_cfg.half_life == DEFAULT_HALF_LIFE*60
532
&& bgp_damp_cfg.reuse_limit == DEFAULT_REUSE
533
&& bgp_damp_cfg.suppress_value == DEFAULT_SUPPRESS
534
&& bgp_damp_cfg.max_suppress_time == bgp_damp_cfg.half_life*4)
535
vty_out (vty, " bgp dampening%s", VTY_NEWLINE);
536
else if (bgp_damp_cfg.half_life != DEFAULT_HALF_LIFE*60
537
&& bgp_damp_cfg.reuse_limit == DEFAULT_REUSE
538
&& bgp_damp_cfg.suppress_value == DEFAULT_SUPPRESS
539
&& bgp_damp_cfg.max_suppress_time == bgp_damp_cfg.half_life*4)
540
vty_out (vty, " bgp dampening %ld%s",
541
bgp_damp_cfg.half_life/60,
544
vty_out (vty, " bgp dampening %ld %d %d %ld%s",
545
bgp_damp_cfg.half_life/60,
546
bgp_damp_cfg.reuse_limit,
547
bgp_damp_cfg.suppress_value,
548
bgp_damp_cfg.max_suppress_time/60,
555
#define BGP_UPTIME_LEN 25
558
bgp_get_reuse_time (unsigned int penalty, char *buf, size_t len)
560
time_t reuse_time = 0;
561
struct tm *tm = NULL;
563
if (penalty > damp->reuse_limit)
565
reuse_time = (int) (DELTA_T * ((log((double)damp->reuse_limit/penalty))/(log(damp->decay_array[1]))));
567
if (reuse_time > damp->max_suppress_time)
568
reuse_time = damp->max_suppress_time;
570
tm = gmtime (&reuse_time);
575
/* Making formatted timer strings. */
576
#define ONE_DAY_SECOND 60*60*24
577
#define ONE_WEEK_SECOND 60*60*24*7
579
snprintf (buf, len, "00:00:00");
580
else if (reuse_time < ONE_DAY_SECOND)
581
snprintf (buf, len, "%02d:%02d:%02d",
582
tm->tm_hour, tm->tm_min, tm->tm_sec);
583
else if (reuse_time < ONE_WEEK_SECOND)
584
snprintf (buf, len, "%dd%02dh%02dm",
585
tm->tm_yday, tm->tm_hour, tm->tm_min);
587
snprintf (buf, len, "%02dw%dd%02dh",
588
tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour);
594
bgp_damp_info_vty (struct vty *vty, struct bgp_info *binfo)
596
struct bgp_damp_info *bdi;
597
time_t t_now, t_diff;
598
char timebuf[BGP_UPTIME_LEN];
601
/* BGP dampening information. */
602
bdi = binfo->damp_info;
604
/* If dampening is not enabled or there is no dampening information,
605
return immediately. */
609
/* Calculate new penalty. */
611
t_diff = t_now - bdi->t_updated;
612
penalty = bgp_damp_decay (t_diff, bdi->penalty);
614
vty_out (vty, " Dampinfo: penalty %d, flapped %d times in %s",
616
peer_uptime (bdi->start_time, timebuf, BGP_UPTIME_LEN));
618
if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED)
619
&& ! CHECK_FLAG (binfo->flags, BGP_INFO_HISTORY))
620
vty_out (vty, ", reuse in %s",
621
bgp_get_reuse_time (penalty, timebuf, BGP_UPTIME_LEN));
623
vty_out (vty, "%s", VTY_NEWLINE);
627
bgp_damp_reuse_time_vty (struct vty *vty, struct bgp_info *binfo)
629
struct bgp_damp_info *bdi;
630
time_t t_now, t_diff;
631
char timebuf[BGP_UPTIME_LEN];
634
/* BGP dampening information. */
635
bdi = binfo->damp_info;
637
/* If dampening is not enabled or there is no dampening information,
638
return immediately. */
642
/* Calculate new penalty. */
644
t_diff = t_now - bdi->t_updated;
645
penalty = bgp_damp_decay (t_diff, bdi->penalty);
647
return bgp_get_reuse_time (penalty, timebuf, BGP_UPTIME_LEN);