]> git.sommitrealweird.co.uk Git - quagga-debian.git/blob - bgpd/bgp_damp.c
New upstream release and new maintainer
[quagga-debian.git] / bgpd / bgp_damp.c
1 /* BGP flap dampening
2    Copyright (C) 2001 IP Infusion Inc.
3
4 This file is part of GNU Zebra.
5
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
9 later version.
10
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.
15
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
19 02111-1307, USA.  */
20
21 #include <zebra.h>
22 #include <math.h>
23
24 #include "prefix.h"
25 #include "memory.h"
26 #include "command.h"
27 #include "log.h"
28 #include "thread.h"
29 #include "filter.h"
30
31 #include "bgpd/bgpd.h"
32 #include "bgpd/bgp_damp.h"
33 #include "bgpd/bgp_table.h"
34 #include "bgpd/bgp_route.h"
35 #include "bgpd/bgp_attr.h" 
36 #include "bgpd/bgp_advertise.h"
37
38 /* Global variable to access damping configuration */
39 struct bgp_damp_config bgp_damp_cfg;
40 static struct bgp_damp_config *damp = &bgp_damp_cfg;
41
42 /* Utility macro to add and delete BGP dampening information to no
43    used list.  */
44 #define BGP_DAMP_LIST_ADD(N,A)  BGP_INFO_ADD(N,A,no_reuse_list)
45 #define BGP_DAMP_LIST_DEL(N,A)  BGP_INFO_DEL(N,A,no_reuse_list)
46
47 /* Calculate reuse list index by penalty value.  */
48 static int
49 bgp_reuse_index (int penalty)
50 {
51   unsigned int i;
52   int index;
53
54   i = (int)(((double) penalty / damp->reuse_limit - 1.0) * damp->scale_factor);
55   
56   if ( i >= damp->reuse_index_size )
57     i = damp->reuse_index_size - 1;
58
59   index = damp->reuse_index[i] - damp->reuse_index[0];
60
61   return (damp->reuse_offset + index) % damp->reuse_list_size;  
62 }
63
64 /* Add BGP dampening information to reuse list.  */
65 static void 
66 bgp_reuse_list_add (struct bgp_damp_info *bdi)
67 {
68   int index;
69
70   index = bdi->index = bgp_reuse_index (bdi->penalty);
71
72   bdi->prev = NULL;
73   bdi->next = damp->reuse_list[index];
74   if (damp->reuse_list[index])
75     damp->reuse_list[index]->prev = bdi;
76   damp->reuse_list[index] = bdi;
77 }
78
79 /* Delete BGP dampening information from reuse list.  */
80 static void
81 bgp_reuse_list_delete (struct bgp_damp_info *bdi)
82 {
83   if (bdi->next)
84     bdi->next->prev = bdi->prev;
85   if (bdi->prev)
86     bdi->prev->next = bdi->next;
87   else
88     damp->reuse_list[bdi->index] = bdi->next;
89 }   
90
91 /* Return decayed penalty value.  */
92 int 
93 bgp_damp_decay (time_t tdiff, int penalty)
94 {
95   unsigned int i;
96
97   i = (int) ((double) tdiff / DELTA_T);
98
99   if (i == 0)
100     return penalty; 
101   
102   if (i >= damp->decay_array_size)
103     return 0;
104
105   return (int) (penalty * damp->decay_array[i]);
106 }
107
108 /* Handler of reuse timer event.  Each route in the current reuse-list
109    is evaluated.  RFC2439 Section 4.8.7.  */
110 static int
111 bgp_reuse_timer (struct thread *t)
112 {
113   struct bgp_damp_info *bdi;
114   struct bgp_damp_info *next;
115   time_t t_now, t_diff;
116     
117   damp->t_reuse = NULL;
118   damp->t_reuse =
119     thread_add_timer (bm->master, bgp_reuse_timer, NULL, DELTA_REUSE);
120
121   t_now = bgp_clock ();
122
123   /* 1.  save a pointer to the current zeroth queue head and zero the
124      list head entry.  */
125   bdi = damp->reuse_list[damp->reuse_offset];
126   damp->reuse_list[damp->reuse_offset] = NULL;
127
128   /* 2.  set offset = modulo reuse-list-size ( offset + 1 ), thereby
129      rotating the circular queue of list-heads.  */
130   damp->reuse_offset = (damp->reuse_offset + 1) % damp->reuse_list_size;
131
132   /* 3. if ( the saved list head pointer is non-empty ) */
133   for (; bdi; bdi = next)
134     {
135       struct bgp *bgp = bdi->binfo->peer->bgp;
136       
137       next = bdi->next;
138
139       /* Set t-diff = t-now - t-updated.  */
140       t_diff = t_now - bdi->t_updated;
141
142       /* Set figure-of-merit = figure-of-merit * decay-array-ok [t-diff] */
143       bdi->penalty = bgp_damp_decay (t_diff, bdi->penalty);   
144
145       /* Set t-updated = t-now.  */
146       bdi->t_updated = t_now;
147
148       /* if (figure-of-merit < reuse).  */
149       if (bdi->penalty < damp->reuse_limit)
150         {
151           /* Reuse the route.  */
152           bgp_info_unset_flag (bdi->rn, bdi->binfo, BGP_INFO_DAMPED);
153           bdi->suppress_time = 0;
154
155           if (bdi->lastrecord == BGP_RECORD_UPDATE)
156             {
157               bgp_info_unset_flag (bdi->rn, bdi->binfo, BGP_INFO_HISTORY);
158               bgp_aggregate_increment (bgp, &bdi->rn->p, bdi->binfo,
159                                        bdi->afi, bdi->safi);   
160               bgp_process (bgp, bdi->rn, bdi->afi, bdi->safi);
161             }
162
163           if (bdi->penalty <= damp->reuse_limit / 2.0)
164             bgp_damp_info_free (bdi, 1);
165           else
166             BGP_DAMP_LIST_ADD (damp, bdi);
167         }
168       else
169         /* Re-insert into another list (See RFC2439 Section 4.8.6).  */
170         bgp_reuse_list_add (bdi);
171     }
172
173   return 0;
174 }
175
176 /* A route becomes unreachable (RFC2439 Section 4.8.2).  */
177 int
178 bgp_damp_withdraw (struct bgp_info *binfo, struct bgp_node *rn,
179                    afi_t afi, safi_t safi, int attr_change)
180 {
181   time_t t_now;
182   struct bgp_damp_info *bdi = NULL;
183   double last_penalty = 0;
184   
185   t_now = bgp_clock ();
186
187   /* Processing Unreachable Messages.  */
188   if (binfo->extra)
189     bdi = binfo->extra->damp_info;
190   
191   if (bdi == NULL)
192     {
193       /* If there is no previous stability history. */
194
195       /* RFC2439 said:
196          1. allocate a damping structure.
197          2. set figure-of-merit = 1.
198          3. withdraw the route.  */
199
200       bdi =  XCALLOC (MTYPE_BGP_DAMP_INFO, sizeof (struct bgp_damp_info));
201       bdi->binfo = binfo;
202       bdi->rn = rn;
203       bdi->penalty = (attr_change ? DEFAULT_PENALTY / 2 : DEFAULT_PENALTY);
204       bdi->flap = 1;
205       bdi->start_time = t_now;
206       bdi->suppress_time = 0;
207       bdi->index = -1;
208       bdi->afi = afi;
209       bdi->safi = safi;
210       (bgp_info_extra_get (binfo))->damp_info = bdi;
211       BGP_DAMP_LIST_ADD (damp, bdi);
212     }
213   else
214     {
215       last_penalty = bdi->penalty;
216
217       /* 1. Set t-diff = t-now - t-updated.  */
218       bdi->penalty = 
219         (bgp_damp_decay (t_now - bdi->t_updated, bdi->penalty) 
220          + (attr_change ? DEFAULT_PENALTY / 2 : DEFAULT_PENALTY));
221
222       if (bdi->penalty > damp->ceiling)
223         bdi->penalty = damp->ceiling;
224
225       bdi->flap++;
226     }
227   
228   assert ((rn == bdi->rn) && (binfo == bdi->binfo));
229   
230   bdi->lastrecord = BGP_RECORD_WITHDRAW;
231   bdi->t_updated = t_now;
232
233   /* Make this route as historical status.  */
234   bgp_info_set_flag (rn, binfo, BGP_INFO_HISTORY);
235
236   /* Remove the route from a reuse list if it is on one.  */
237   if (CHECK_FLAG (bdi->binfo->flags, BGP_INFO_DAMPED))
238     {
239       /* If decay rate isn't equal to 0, reinsert brn. */  
240       if (bdi->penalty != last_penalty)
241         {
242           bgp_reuse_list_delete (bdi);
243           bgp_reuse_list_add (bdi);  
244         }
245       return BGP_DAMP_SUPPRESSED; 
246     }
247
248   /* If not suppressed before, do annonunce this withdraw and
249      insert into reuse_list.  */
250   if (bdi->penalty >= damp->suppress_value)
251     {
252       bgp_info_set_flag (rn, binfo, BGP_INFO_DAMPED);
253       bdi->suppress_time = t_now;
254       BGP_DAMP_LIST_DEL (damp, bdi);
255       bgp_reuse_list_add (bdi);
256     }
257
258   return BGP_DAMP_USED;
259 }
260
261 int
262 bgp_damp_update (struct bgp_info *binfo, struct bgp_node *rn, 
263                  afi_t afi, safi_t safi)
264 {
265   time_t t_now;
266   struct bgp_damp_info *bdi;
267   int status;
268
269   if (!binfo->extra || !((bdi = binfo->extra->damp_info)))
270     return BGP_DAMP_USED;
271
272   t_now = bgp_clock ();
273   bgp_info_unset_flag (rn, binfo, BGP_INFO_HISTORY);
274
275   bdi->lastrecord = BGP_RECORD_UPDATE;
276   bdi->penalty = bgp_damp_decay (t_now - bdi->t_updated, bdi->penalty);
277
278   if (! CHECK_FLAG (bdi->binfo->flags, BGP_INFO_DAMPED)
279       && (bdi->penalty < damp->suppress_value))
280     status = BGP_DAMP_USED;
281   else if (CHECK_FLAG (bdi->binfo->flags, BGP_INFO_DAMPED)
282            && (bdi->penalty < damp->reuse_limit) )
283     {
284       bgp_info_unset_flag (rn, binfo, BGP_INFO_DAMPED);
285       bgp_reuse_list_delete (bdi);
286       BGP_DAMP_LIST_ADD (damp, bdi);
287       bdi->suppress_time = 0;
288       status = BGP_DAMP_USED;
289     }
290   else
291     status = BGP_DAMP_SUPPRESSED;  
292
293   if (bdi->penalty > damp->reuse_limit / 2.0)
294     bdi->t_updated = t_now;
295   else
296     bgp_damp_info_free (bdi, 0);
297         
298   return status;
299 }
300
301 /* Remove dampening information and history route.  */
302 int 
303 bgp_damp_scan (struct bgp_info *binfo, afi_t afi, safi_t safi)
304 {
305   time_t t_now, t_diff;
306   struct bgp_damp_info *bdi;
307   
308   assert (binfo->extra && binfo->extra->damp_info);
309   
310   t_now = bgp_clock ();
311   bdi = binfo->extra->damp_info;
312  
313   if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED))
314     {
315       t_diff = t_now - bdi->suppress_time;
316
317       if (t_diff >= damp->max_suppress_time)
318         {
319           bgp_info_unset_flag (bdi->rn, binfo, BGP_INFO_DAMPED);
320           bgp_reuse_list_delete (bdi);
321           BGP_DAMP_LIST_ADD (damp, bdi);
322           bdi->penalty = damp->reuse_limit;
323           bdi->suppress_time = 0;
324           bdi->t_updated = t_now;
325           
326           /* Need to announce UPDATE once this binfo is usable again. */
327           if (bdi->lastrecord == BGP_RECORD_UPDATE)
328             return 1;
329           else
330             return 0;
331         }
332     }
333   else
334     {
335       t_diff = t_now - bdi->t_updated;
336       bdi->penalty = bgp_damp_decay (t_diff, bdi->penalty);
337
338       if (bdi->penalty <= damp->reuse_limit / 2.0)
339         {
340           /* release the bdi, bdi->binfo. */  
341           bgp_damp_info_free (bdi, 1);
342           return 0;
343         }            
344       else
345         bdi->t_updated = t_now;
346     }       
347   return 0;
348 }
349
350 void
351 bgp_damp_info_free (struct bgp_damp_info *bdi, int withdraw)
352 {
353   struct bgp_info *binfo;
354
355   if (! bdi)
356     return;
357
358   binfo = bdi->binfo;
359   binfo->extra->damp_info = NULL;
360
361   if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED))
362     bgp_reuse_list_delete (bdi);
363   else
364     BGP_DAMP_LIST_DEL (damp, bdi);
365
366   bgp_info_unset_flag (bdi->rn, binfo, BGP_INFO_HISTORY|BGP_INFO_DAMPED);
367
368   if (bdi->lastrecord == BGP_RECORD_WITHDRAW && withdraw)
369     bgp_info_delete (bdi->rn, binfo);
370   
371   XFREE (MTYPE_BGP_DAMP_INFO, bdi);
372 }
373
374 static void
375 bgp_damp_parameter_set (int hlife, int reuse, int sup, int maxsup)
376 {
377   double reuse_max_ratio;
378   unsigned int i;
379   double j;
380         
381   damp->suppress_value = sup;
382   damp->half_life = hlife;
383   damp->reuse_limit = reuse;
384   damp->max_suppress_time = maxsup;
385
386   /* Initialize params per bgp_damp_config. */
387   damp->reuse_index_size = REUSE_ARRAY_SIZE;
388
389   damp->ceiling = (int)(damp->reuse_limit * (pow(2, (double)damp->max_suppress_time/damp->half_life))); 
390
391   /* Decay-array computations */
392   damp->decay_array_size = ceil ((double) damp->max_suppress_time / DELTA_T);
393   damp->decay_array = XMALLOC (MTYPE_BGP_DAMP_ARRAY,
394                                sizeof(double) * (damp->decay_array_size));
395   damp->decay_array[0] = 1.0;
396   damp->decay_array[1] = exp ((1.0/((double)damp->half_life/DELTA_T)) * log(0.5));
397
398   /* Calculate decay values for all possible times */
399   for (i = 2; i < damp->decay_array_size; i++)
400     damp->decay_array[i] = damp->decay_array[i-1] * damp->decay_array[1];
401         
402   /* Reuse-list computations */
403   i = ceil ((double)damp->max_suppress_time / DELTA_REUSE) + 1;
404   if (i > REUSE_LIST_SIZE || i == 0)
405     i = REUSE_LIST_SIZE;
406   damp->reuse_list_size = i; 
407
408   damp->reuse_list = XCALLOC (MTYPE_BGP_DAMP_ARRAY, 
409                               damp->reuse_list_size 
410                               * sizeof (struct bgp_reuse_node *));
411
412   /* Reuse-array computations */
413   damp->reuse_index = XCALLOC (MTYPE_BGP_DAMP_ARRAY,
414                                sizeof(int) * damp->reuse_index_size);
415
416   reuse_max_ratio = (double)damp->ceiling/damp->reuse_limit;
417   j = (exp((double)damp->max_suppress_time/damp->half_life) * log10(2.0));
418   if ( reuse_max_ratio > j && j != 0 )
419     reuse_max_ratio = j;
420
421   damp->scale_factor = (double)damp->reuse_index_size/(reuse_max_ratio - 1);
422
423   for (i = 0; i < damp->reuse_index_size; i++)
424     {
425       damp->reuse_index[i] = 
426         (int)(((double)damp->half_life / DELTA_REUSE)
427               * log10 (1.0 / (damp->reuse_limit * ( 1.0 + ((double)i/damp->scale_factor)))) / log10(0.5));
428     }
429 }
430
431 int
432 bgp_damp_enable (struct bgp *bgp, afi_t afi, safi_t safi, time_t half,
433                  unsigned int reuse, unsigned int suppress, time_t max)
434 {
435   if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING))
436     {
437       if (damp->half_life == half
438           && damp->reuse_limit == reuse
439           && damp->suppress_value == suppress
440           && damp->max_suppress_time == max)
441         return 0;
442       bgp_damp_disable (bgp, afi, safi);
443     }
444
445   SET_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING);
446   bgp_damp_parameter_set (half, reuse, suppress, max);
447
448   /* Register reuse timer.  */
449   if (! damp->t_reuse)
450     damp->t_reuse = 
451       thread_add_timer (bm->master, bgp_reuse_timer, NULL, DELTA_REUSE);
452
453   return 0;
454 }
455
456 static void
457 bgp_damp_config_clean (struct bgp_damp_config *damp)
458 {
459   /* Free decay array */
460   XFREE (MTYPE_BGP_DAMP_ARRAY, damp->decay_array);
461
462   /* Free reuse index array */
463   XFREE (MTYPE_BGP_DAMP_ARRAY, damp->reuse_index);
464
465   /* Free reuse list array. */
466   XFREE (MTYPE_BGP_DAMP_ARRAY, damp->reuse_list);
467 }
468
469 /* Clean all the bgp_damp_info stored in reuse_list. */
470 void
471 bgp_damp_info_clean (void)
472 {
473   unsigned int i;
474   struct bgp_damp_info *bdi, *next;
475
476   damp->reuse_offset = 0;
477
478   for (i = 0; i < damp->reuse_list_size; i++)
479     {
480       if (! damp->reuse_list[i])
481         continue;
482
483       for (bdi = damp->reuse_list[i]; bdi; bdi = next)
484         {
485           next = bdi->next;
486           bgp_damp_info_free (bdi, 1);
487         }
488       damp->reuse_list[i] = NULL;
489     }
490
491   for (bdi = damp->no_reuse_list; bdi; bdi = next)
492     {
493       next = bdi->next;
494       bgp_damp_info_free (bdi, 1);
495     }
496   damp->no_reuse_list = NULL;
497 }
498
499 int
500 bgp_damp_disable (struct bgp *bgp, afi_t afi, safi_t safi)
501 {
502   /* If it wasn't enabled, there's nothing to do. */
503   if (! CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING))
504     return 0;
505
506   /* Cancel reuse thread. */
507   if (damp->t_reuse )
508     thread_cancel (damp->t_reuse);
509   damp->t_reuse = NULL;
510
511   /* Clean BGP dampening information.  */
512   bgp_damp_info_clean ();
513
514   /* Clear configuration */
515   bgp_damp_config_clean (&bgp_damp_cfg);
516
517   UNSET_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING);
518   return 0;
519 }
520
521 void
522 bgp_config_write_damp (struct vty *vty)
523 {
524   if (bgp_damp_cfg.half_life == DEFAULT_HALF_LIFE*60
525       && bgp_damp_cfg.reuse_limit == DEFAULT_REUSE
526       && bgp_damp_cfg.suppress_value == DEFAULT_SUPPRESS
527       && bgp_damp_cfg.max_suppress_time == bgp_damp_cfg.half_life*4)
528     vty_out (vty, " bgp dampening%s", VTY_NEWLINE);
529   else if (bgp_damp_cfg.half_life != DEFAULT_HALF_LIFE*60
530            && bgp_damp_cfg.reuse_limit == DEFAULT_REUSE
531            && bgp_damp_cfg.suppress_value == DEFAULT_SUPPRESS
532            && bgp_damp_cfg.max_suppress_time == bgp_damp_cfg.half_life*4)
533     vty_out (vty, " bgp dampening %lld%s",
534              bgp_damp_cfg.half_life/60LL,
535              VTY_NEWLINE);
536   else
537     vty_out (vty, " bgp dampening %lld %d %d %lld%s",
538              bgp_damp_cfg.half_life/60LL,
539              bgp_damp_cfg.reuse_limit,
540              bgp_damp_cfg.suppress_value,
541              bgp_damp_cfg.max_suppress_time/60LL,
542              VTY_NEWLINE);
543 }
544
545 static const char *
546 bgp_get_reuse_time (unsigned int penalty, char *buf, size_t len)
547 {
548   time_t reuse_time = 0;
549   struct tm *tm = NULL;
550
551   if (penalty > damp->reuse_limit)
552     {
553       reuse_time = (int) (DELTA_T * ((log((double)damp->reuse_limit/penalty))/(log(damp->decay_array[1])))); 
554
555       if (reuse_time > damp->max_suppress_time)
556         reuse_time = damp->max_suppress_time;
557
558       tm = gmtime (&reuse_time);
559     }
560   else 
561     reuse_time = 0;
562
563   /* Making formatted timer strings. */
564 #define ONE_DAY_SECOND 60*60*24
565 #define ONE_WEEK_SECOND 60*60*24*7
566   if (reuse_time == 0)
567     snprintf (buf, len, "00:00:00");
568   else if (reuse_time < ONE_DAY_SECOND)
569     snprintf (buf, len, "%02d:%02d:%02d", 
570               tm->tm_hour, tm->tm_min, tm->tm_sec);
571   else if (reuse_time < ONE_WEEK_SECOND)
572     snprintf (buf, len, "%dd%02dh%02dm", 
573               tm->tm_yday, tm->tm_hour, tm->tm_min);
574   else
575     snprintf (buf, len, "%02dw%dd%02dh", 
576               tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour); 
577
578   return buf;
579 }
580  
581 void
582 bgp_damp_info_vty (struct vty *vty, struct bgp_info *binfo)  
583 {
584   struct bgp_damp_info *bdi;
585   time_t t_now, t_diff;
586   char timebuf[BGP_UPTIME_LEN];
587   int penalty;
588
589   if (!binfo->extra)
590     return;
591   
592   /* BGP dampening information.  */
593   bdi = binfo->extra->damp_info;
594
595   /* If dampening is not enabled or there is no dampening information,
596      return immediately.  */
597   if (! damp || ! bdi)
598     return;
599
600   /* Calculate new penalty.  */
601   t_now = bgp_clock ();
602   t_diff = t_now - bdi->t_updated;
603   penalty = bgp_damp_decay (t_diff, bdi->penalty);
604
605   vty_out (vty, "      Dampinfo: penalty %d, flapped %d times in %s",
606            penalty, bdi->flap,
607            peer_uptime (bdi->start_time, timebuf, BGP_UPTIME_LEN));
608
609   if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED)
610       && ! CHECK_FLAG (binfo->flags, BGP_INFO_HISTORY))
611     vty_out (vty, ", reuse in %s",
612              bgp_get_reuse_time (penalty, timebuf, BGP_UPTIME_LEN));
613
614   vty_out (vty, "%s", VTY_NEWLINE);
615 }
616
617 const char *
618 bgp_damp_reuse_time_vty (struct vty *vty, struct bgp_info *binfo,
619                          char *timebuf, size_t len)
620 {
621   struct bgp_damp_info *bdi;
622   time_t t_now, t_diff;
623   int penalty;
624   
625   if (!binfo->extra)
626     return NULL;
627   
628   /* BGP dampening information.  */
629   bdi = binfo->extra->damp_info;
630
631   /* If dampening is not enabled or there is no dampening information,
632      return immediately.  */
633   if (! damp || ! bdi)
634     return NULL;
635
636   /* Calculate new penalty.  */
637   t_now = bgp_clock ();
638   t_diff = t_now - bdi->t_updated;
639   penalty = bgp_damp_decay (t_diff, bdi->penalty);
640
641   return  bgp_get_reuse_time (penalty, timebuf, len);
642 }
643
644 int
645 bgp_show_dampening_parameters (struct vty *vty, afi_t afi, safi_t safi)
646 {
647   struct bgp *bgp;
648   bgp = bgp_get_default();
649
650   if (bgp == NULL)
651     {
652       vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE);
653       return CMD_WARNING;
654     }
655
656   if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING))
657     {
658       vty_out (vty, "Half-life time: %ld min%s",
659                     damp->half_life / 60, VTY_NEWLINE);
660       vty_out (vty, "Reuse penalty: %d%s",
661                     damp->reuse_limit, VTY_NEWLINE);
662       vty_out (vty, "Suppress penalty: %d%s",
663                     damp->suppress_value, VTY_NEWLINE);
664       vty_out (vty, "Max suppress time: %ld min%s",
665                     damp->max_suppress_time / 60, VTY_NEWLINE);
666       vty_out (vty, "Max suppress penalty: %u%s",
667                     damp->ceiling, VTY_NEWLINE);
668       vty_out (vty, "%s", VTY_NEWLINE);
669     }
670   else
671     vty_out (vty, "dampening not enabled for %s%s",
672                   afi == AFI_IP ? "IPv4" : "IPv6", VTY_NEWLINE);
673
674   return CMD_SUCCESS;
675 }