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 damp
->t_reuse
= NULL
;
118 thread_add_timer (master
, bgp_reuse_timer
, NULL
, DELTA_REUSE
);
122 /* 1. save a pointer to the current zeroth queue head and zero the
124 bdi
= damp
->reuse_list
[damp
->reuse_offset
];
125 damp
->reuse_list
[damp
->reuse_offset
] = NULL
;
127 /* 2. set offset = modulo reuse-list-size ( offset + 1 ), thereby
128 rotating the circular queue of list-heads. */
129 damp
->reuse_offset
= (damp
->reuse_offset
+ 1) % damp
->reuse_list_size
;
131 /* 3. if ( the saved list head pointer is non-empty ) */
132 for (; bdi
; bdi
= next
)
134 struct bgp
*bgp
= bdi
->binfo
->peer
->bgp
;
138 /* Set t-diff = t-now - t-updated. */
139 t_diff
= t_now
- bdi
->t_updated
;
141 /* Set figure-of-merit = figure-of-merit * decay-array-ok [t-diff] */
142 bdi
->penalty
= bgp_damp_decay (t_diff
, bdi
->penalty
);
144 /* Set t-updated = t-now. */
145 bdi
->t_updated
= t_now
;
147 /* if (figure-of-merit < reuse). */
148 if (bdi
->penalty
< damp
->reuse_limit
)
150 /* Reuse the route. */
151 bgp_info_unset_flag (bdi
->rn
, bdi
->binfo
, BGP_INFO_DAMPED
);
152 bdi
->suppress_time
= 0;
154 if (bdi
->lastrecord
== BGP_RECORD_UPDATE
)
156 bgp_info_unset_flag (bdi
->rn
, bdi
->binfo
, BGP_INFO_HISTORY
);
157 bgp_aggregate_increment (bgp
, &bdi
->rn
->p
, bdi
->binfo
,
158 bdi
->afi
, bdi
->safi
);
159 bgp_process (bgp
, bdi
->rn
, bdi
->afi
, bdi
->safi
);
162 if (bdi
->penalty
<= damp
->reuse_limit
/ 2.0)
163 bgp_damp_info_free (bdi
, 1);
165 BGP_DAMP_LIST_ADD (damp
, bdi
);
168 /* Re-insert into another list (See RFC2439 Section 4.8.6). */
169 bgp_reuse_list_add (bdi
);
175 /* A route becomes unreachable (RFC2439 Section 4.8.2). */
177 bgp_damp_withdraw (struct bgp_info
*binfo
, struct bgp_node
*rn
,
178 afi_t afi
, safi_t safi
, int attr_change
)
181 struct bgp_damp_info
*bdi
= NULL
;
182 double last_penalty
= 0;
186 /* Processing Unreachable Messages. */
188 bdi
= binfo
->extra
->damp_info
;
192 /* If there is no previous stability history. */
195 1. allocate a damping structure.
196 2. set figure-of-merit = 1.
197 3. withdraw the route. */
199 bdi
= XCALLOC (MTYPE_BGP_DAMP_INFO
, sizeof (struct bgp_damp_info
));
202 bdi
->penalty
= (attr_change
? DEFAULT_PENALTY
/ 2 : DEFAULT_PENALTY
);
204 bdi
->start_time
= t_now
;
205 bdi
->suppress_time
= 0;
209 (bgp_info_extra_get (binfo
))->damp_info
= bdi
;
210 BGP_DAMP_LIST_ADD (damp
, bdi
);
214 last_penalty
= bdi
->penalty
;
216 /* 1. Set t-diff = t-now - t-updated. */
218 (bgp_damp_decay (t_now
- bdi
->t_updated
, bdi
->penalty
)
219 + (attr_change
? DEFAULT_PENALTY
/ 2 : DEFAULT_PENALTY
));
221 if (bdi
->penalty
> damp
->ceiling
)
222 bdi
->penalty
= damp
->ceiling
;
227 assert ((rn
== bdi
->rn
) && (binfo
== bdi
->binfo
));
229 bdi
->lastrecord
= BGP_RECORD_WITHDRAW
;
230 bdi
->t_updated
= t_now
;
232 /* Make this route as historical status. */
233 bgp_info_set_flag (rn
, binfo
, BGP_INFO_HISTORY
);
235 /* Remove the route from a reuse list if it is on one. */
236 if (CHECK_FLAG (bdi
->binfo
->flags
, BGP_INFO_DAMPED
))
238 /* If decay rate isn't equal to 0, reinsert brn. */
239 if (bdi
->penalty
!= last_penalty
)
241 bgp_reuse_list_delete (bdi
);
242 bgp_reuse_list_add (bdi
);
244 return BGP_DAMP_SUPPRESSED
;
247 /* If not suppressed before, do annonunce this withdraw and
248 insert into reuse_list. */
249 if (bdi
->penalty
>= damp
->suppress_value
)
251 bgp_info_set_flag (rn
, binfo
, BGP_INFO_DAMPED
);
252 bdi
->suppress_time
= t_now
;
253 BGP_DAMP_LIST_DEL (damp
, bdi
);
254 bgp_reuse_list_add (bdi
);
257 return BGP_DAMP_USED
;
261 bgp_damp_update (struct bgp_info
*binfo
, struct bgp_node
*rn
,
262 afi_t afi
, safi_t safi
)
265 struct bgp_damp_info
*bdi
;
268 if (!binfo
->extra
|| !((bdi
= binfo
->extra
->damp_info
)))
269 return BGP_DAMP_USED
;
272 bgp_info_unset_flag (rn
, binfo
, BGP_INFO_HISTORY
);
274 bdi
->lastrecord
= BGP_RECORD_UPDATE
;
275 bdi
->penalty
= bgp_damp_decay (t_now
- bdi
->t_updated
, bdi
->penalty
);
277 if (! CHECK_FLAG (bdi
->binfo
->flags
, BGP_INFO_DAMPED
)
278 && (bdi
->penalty
< damp
->suppress_value
))
279 status
= BGP_DAMP_USED
;
280 else if (CHECK_FLAG (bdi
->binfo
->flags
, BGP_INFO_DAMPED
)
281 && (bdi
->penalty
< damp
->reuse_limit
) )
283 bgp_info_unset_flag (rn
, binfo
, BGP_INFO_DAMPED
);
284 bgp_reuse_list_delete (bdi
);
285 BGP_DAMP_LIST_ADD (damp
, bdi
);
286 bdi
->suppress_time
= 0;
287 status
= BGP_DAMP_USED
;
290 status
= BGP_DAMP_SUPPRESSED
;
292 if (bdi
->penalty
> damp
->reuse_limit
/ 2.0)
293 bdi
->t_updated
= t_now
;
295 bgp_damp_info_free (bdi
, 0);
300 /* Remove dampening information and history route. */
302 bgp_damp_scan (struct bgp_info
*binfo
, afi_t afi
, safi_t safi
)
304 time_t t_now
, t_diff
;
305 struct bgp_damp_info
*bdi
;
307 assert (binfo
->extra
&& binfo
->extra
->damp_info
);
310 bdi
= binfo
->extra
->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 bgp_info_unset_flag (bdi
->rn
, binfo
, 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
;
358 binfo
->extra
->damp_info
= NULL
;
360 if (CHECK_FLAG (binfo
->flags
, BGP_INFO_DAMPED
))
361 bgp_reuse_list_delete (bdi
);
363 BGP_DAMP_LIST_DEL (damp
, bdi
);
365 bgp_info_unset_flag (bdi
->rn
, binfo
, BGP_INFO_HISTORY
|BGP_INFO_DAMPED
);
367 if (bdi
->lastrecord
== BGP_RECORD_WITHDRAW
&& withdraw
)
368 bgp_info_delete (bdi
->rn
, binfo
);
370 XFREE (MTYPE_BGP_DAMP_INFO
, bdi
);
374 bgp_damp_parameter_set (int hlife
, int reuse
, int sup
, int maxsup
)
376 double reuse_max_ratio
;
380 damp
->suppress_value
= sup
;
381 damp
->half_life
= hlife
;
382 damp
->reuse_limit
= reuse
;
383 damp
->max_suppress_time
= maxsup
;
385 /* Initialize params per bgp_damp_config. */
386 damp
->reuse_index_size
= REUSE_ARRAY_SIZE
;
388 damp
->ceiling
= (int)(damp
->reuse_limit
* (pow(2, (double)damp
->max_suppress_time
/damp
->half_life
)));
390 /* Decay-array computations */
391 damp
->decay_array_size
= ceil ((double) damp
->max_suppress_time
/ DELTA_T
);
392 damp
->decay_array
= XMALLOC (MTYPE_BGP_DAMP_ARRAY
,
393 sizeof(double) * (damp
->decay_array_size
));
394 damp
->decay_array
[0] = 1.0;
395 damp
->decay_array
[1] = exp ((1.0/((double)damp
->half_life
/DELTA_T
)) * log(0.5));
397 /* Calculate decay values for all possible times */
398 for (i
= 2; i
< damp
->decay_array_size
; i
++)
399 damp
->decay_array
[i
] = damp
->decay_array
[i
-1] * damp
->decay_array
[1];
401 /* Reuse-list computations */
402 i
= ceil ((double)damp
->max_suppress_time
/ DELTA_REUSE
) + 1;
403 if (i
> REUSE_LIST_SIZE
|| i
== 0)
405 damp
->reuse_list_size
= i
;
407 damp
->reuse_list
= XCALLOC (MTYPE_BGP_DAMP_ARRAY
,
408 damp
->reuse_list_size
409 * sizeof (struct bgp_reuse_node
*));
410 memset (damp
->reuse_list
, 0x00,
411 damp
->reuse_list_size
* sizeof (struct bgp_reuse_node
*));
413 /* Reuse-array computations */
414 damp
->reuse_index
= XMALLOC (MTYPE_BGP_DAMP_ARRAY
,
415 sizeof(int) * damp
->reuse_index_size
);
416 memset (damp
->reuse_index
, 0x00,
417 damp
->reuse_list_size
* sizeof (int));
419 reuse_max_ratio
= (double)damp
->ceiling
/damp
->reuse_limit
;
420 j
= (exp((double)damp
->max_suppress_time
/damp
->half_life
) * log10(2.0));
421 if ( reuse_max_ratio
> j
&& j
!= 0 )
424 damp
->scale_factor
= (double)damp
->reuse_index_size
/(reuse_max_ratio
- 1);
426 for (i
= 0; i
< damp
->reuse_index_size
; i
++)
428 damp
->reuse_index
[i
] =
429 (int)(((double)damp
->half_life
/ DELTA_REUSE
)
430 * log10 (1.0 / (damp
->reuse_limit
* ( 1.0 + ((double)i
/damp
->scale_factor
)))) / log10(0.5));
435 bgp_damp_enable (struct bgp
*bgp
, afi_t afi
, safi_t safi
, time_t half
,
436 unsigned int reuse
, unsigned int suppress
, time_t max
)
438 if (CHECK_FLAG (bgp
->af_flags
[afi
][safi
], BGP_CONFIG_DAMPENING
))
440 if (damp
->half_life
== half
441 && damp
->reuse_limit
== reuse
442 && damp
->suppress_value
== suppress
443 && damp
->max_suppress_time
== max
)
445 bgp_damp_disable (bgp
, afi
, safi
);
448 SET_FLAG (bgp
->af_flags
[afi
][safi
], BGP_CONFIG_DAMPENING
);
449 bgp_damp_parameter_set (half
, reuse
, suppress
, max
);
451 /* Register reuse timer. */
454 thread_add_timer (master
, bgp_reuse_timer
, NULL
, DELTA_REUSE
);
460 bgp_damp_config_clean (struct bgp_damp_config
*damp
)
462 /* Free decay array */
463 XFREE (MTYPE_BGP_DAMP_ARRAY
, damp
->decay_array
);
465 /* Free reuse index array */
466 XFREE (MTYPE_BGP_DAMP_ARRAY
, damp
->reuse_index
);
468 /* Free reuse list array. */
469 XFREE (MTYPE_BGP_DAMP_ARRAY
, damp
->reuse_list
);
472 /* Clean all the bgp_damp_info stored in reuse_list. */
474 bgp_damp_info_clean (void)
477 struct bgp_damp_info
*bdi
, *next
;
479 damp
->reuse_offset
= 0;
481 for (i
= 0; i
< damp
->reuse_list_size
; i
++)
483 if (! damp
->reuse_list
[i
])
486 for (bdi
= damp
->reuse_list
[i
]; bdi
; bdi
= next
)
489 bgp_damp_info_free (bdi
, 1);
491 damp
->reuse_list
[i
] = NULL
;
494 for (bdi
= damp
->no_reuse_list
; bdi
; bdi
= next
)
497 bgp_damp_info_free (bdi
, 1);
499 damp
->no_reuse_list
= NULL
;
503 bgp_damp_disable (struct bgp
*bgp
, afi_t afi
, safi_t safi
)
505 /* Cancel reuse thread. */
507 thread_cancel (damp
->t_reuse
);
508 damp
->t_reuse
= NULL
;
510 /* Clean BGP dampening information. */
511 bgp_damp_info_clean ();
513 /* Clear configuration */
514 bgp_damp_config_clean (&bgp_damp_cfg
);
516 UNSET_FLAG (bgp
->af_flags
[afi
][safi
], BGP_CONFIG_DAMPENING
);
521 bgp_config_write_damp (struct vty
*vty
)
525 if (bgp_damp_cfg
.half_life
== DEFAULT_HALF_LIFE
*60
526 && bgp_damp_cfg
.reuse_limit
== DEFAULT_REUSE
527 && bgp_damp_cfg
.suppress_value
== DEFAULT_SUPPRESS
528 && bgp_damp_cfg
.max_suppress_time
== bgp_damp_cfg
.half_life
*4)
529 vty_out (vty
, " bgp dampening%s", VTY_NEWLINE
);
530 else if (bgp_damp_cfg
.half_life
!= DEFAULT_HALF_LIFE
*60
531 && bgp_damp_cfg
.reuse_limit
== DEFAULT_REUSE
532 && bgp_damp_cfg
.suppress_value
== DEFAULT_SUPPRESS
533 && bgp_damp_cfg
.max_suppress_time
== bgp_damp_cfg
.half_life
*4)
534 vty_out (vty
, " bgp dampening %ld%s",
535 bgp_damp_cfg
.half_life
/60,
538 vty_out (vty
, " bgp dampening %ld %d %d %ld%s",
539 bgp_damp_cfg
.half_life
/60,
540 bgp_damp_cfg
.reuse_limit
,
541 bgp_damp_cfg
.suppress_value
,
542 bgp_damp_cfg
.max_suppress_time
/60,
549 #define BGP_UPTIME_LEN 25
552 bgp_get_reuse_time (unsigned int penalty
, char *buf
, size_t len
)
554 time_t reuse_time
= 0;
555 struct tm
*tm
= NULL
;
557 if (penalty
> damp
->reuse_limit
)
559 reuse_time
= (int) (DELTA_T
* ((log((double)damp
->reuse_limit
/penalty
))/(log(damp
->decay_array
[1]))));
561 if (reuse_time
> damp
->max_suppress_time
)
562 reuse_time
= damp
->max_suppress_time
;
564 tm
= gmtime (&reuse_time
);
569 /* Making formatted timer strings. */
570 #define ONE_DAY_SECOND 60*60*24
571 #define ONE_WEEK_SECOND 60*60*24*7
573 snprintf (buf
, len
, "00:00:00");
574 else if (reuse_time
< ONE_DAY_SECOND
)
575 snprintf (buf
, len
, "%02d:%02d:%02d",
576 tm
->tm_hour
, tm
->tm_min
, tm
->tm_sec
);
577 else if (reuse_time
< ONE_WEEK_SECOND
)
578 snprintf (buf
, len
, "%dd%02dh%02dm",
579 tm
->tm_yday
, tm
->tm_hour
, tm
->tm_min
);
581 snprintf (buf
, len
, "%02dw%dd%02dh",
582 tm
->tm_yday
/7, tm
->tm_yday
- ((tm
->tm_yday
/7) * 7), tm
->tm_hour
);
588 bgp_damp_info_vty (struct vty
*vty
, struct bgp_info
*binfo
)
590 struct bgp_damp_info
*bdi
;
591 time_t t_now
, t_diff
;
592 char timebuf
[BGP_UPTIME_LEN
];
598 /* BGP dampening information. */
599 bdi
= binfo
->extra
->damp_info
;
601 /* If dampening is not enabled or there is no dampening information,
602 return immediately. */
606 /* Calculate new penalty. */
608 t_diff
= t_now
- bdi
->t_updated
;
609 penalty
= bgp_damp_decay (t_diff
, bdi
->penalty
);
611 vty_out (vty
, " Dampinfo: penalty %d, flapped %d times in %s",
613 peer_uptime (bdi
->start_time
, timebuf
, BGP_UPTIME_LEN
));
615 if (CHECK_FLAG (binfo
->flags
, BGP_INFO_DAMPED
)
616 && ! CHECK_FLAG (binfo
->flags
, BGP_INFO_HISTORY
))
617 vty_out (vty
, ", reuse in %s",
618 bgp_get_reuse_time (penalty
, timebuf
, BGP_UPTIME_LEN
));
620 vty_out (vty
, "%s", VTY_NEWLINE
);
624 bgp_damp_reuse_time_vty (struct vty
*vty
, struct bgp_info
*binfo
)
626 struct bgp_damp_info
*bdi
;
627 time_t t_now
, t_diff
;
628 char timebuf
[BGP_UPTIME_LEN
];
634 /* BGP dampening information. */
635 bdi
= binfo
->extra
->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
);