added files
[bcm963xx.git] / userapps / opensource / zebra / 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
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"
36
37 /* Global variable to access damping configuration */
38 struct bgp_damp_config bgp_damp_cfg;
39 struct bgp_damp_config *damp = &bgp_damp_cfg;
40
41 /* Utility macro to add and delete BGP dampening information to no
42    used list.  */
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)
45 \f
46 /* Calculate reuse list index by penalty value.  */
47 static int
48 bgp_reuse_index (int penalty)
49 {
50   int i;
51   int index;
52
53   i = (int)(((double) penalty / damp->reuse_limit - 1.0) * damp->scale_factor);
54   
55   if ( i >= damp->reuse_index_size )
56     i = damp->reuse_index_size - 1;
57
58   index = damp->reuse_index[i] - damp->reuse_index[0];
59
60   return (damp->reuse_offset + index) % damp->reuse_list_size;  
61 }
62
63 /* Add BGP dampening information to reuse list.  */
64 static void 
65 bgp_reuse_list_add (struct bgp_damp_info *bdi)
66 {
67   int index;
68
69   index = bdi->index = bgp_reuse_index (bdi->penalty);
70
71   bdi->prev = NULL;
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;
76 }
77
78 /* Delete BGP dampening information from reuse list.  */
79 static void
80 bgp_reuse_list_delete (struct bgp_damp_info *bdi)
81 {
82   if (bdi->next)
83     bdi->next->prev = bdi->prev;
84   if (bdi->prev)
85     bdi->prev->next = bdi->next;
86   else
87     damp->reuse_list[bdi->index] = bdi->next;
88 }   
89 \f
90 /* Return decayed penalty value.  */
91 int 
92 bgp_damp_decay (time_t tdiff, int penalty)
93 {
94   int i;
95
96   i = (int) ((double) tdiff / DELTA_T);
97
98   if (i == 0)
99     return penalty; 
100   
101   if (i >= damp->decay_array_size)
102     return 0;
103
104   return (int) (penalty * damp->decay_array[i]);
105 }
106
107 /* Handler of reuse timer event.  Each route in the current reuse-list
108    is evaluated.  RFC2439 Section 4.8.7.  */
109 int
110 bgp_reuse_timer (struct thread *t)
111 {
112   struct bgp_damp_info *bdi;
113   struct bgp_damp_info *next;
114   time_t t_now, t_diff;
115   struct bgp *bgp;
116   int bgp_process (struct bgp *, struct bgp_node *, afi_t, safi_t);
117
118   damp->t_reuse = NULL;
119   damp->t_reuse =
120     thread_add_timer (master, bgp_reuse_timer, NULL, DELTA_REUSE);
121
122   bgp = bgp_get_default ();
123   if (! bgp)
124     return 0;
125
126   t_now = time (NULL);
127
128   /* 1.  save a pointer to the current zeroth queue head and zero the
129      list head entry.  */
130   bdi = damp->reuse_list[damp->reuse_offset];
131   damp->reuse_list[damp->reuse_offset] = NULL;
132
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;
136
137   /* 3. if ( the saved list head pointer is non-empty ) */
138   for (; bdi; bdi = next)
139     {
140       next = bdi->next;
141
142       /* Set t-diff = t-now - t-updated.  */
143       t_diff = t_now - bdi->t_updated;
144
145       /* Set figure-of-merit = figure-of-merit * decay-array-ok [t-diff] */
146       bdi->penalty = bgp_damp_decay (t_diff, bdi->penalty);   
147
148       /* Set t-updated = t-now.  */
149       bdi->t_updated = t_now;
150
151       /* if (figure-of-merit < reuse).  */
152       if (bdi->penalty < damp->reuse_limit)
153         {
154           /* Reuse the route.  */
155           UNSET_FLAG (bdi->binfo->flags, BGP_INFO_DAMPED);
156           bdi->suppress_time = 0;
157
158           if (bdi->lastrecord == BGP_RECORD_UPDATE)
159             {
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);
164             }
165
166           if (bdi->penalty <= damp->reuse_limit / 2.0)
167             bgp_damp_info_free (bdi, 1);
168           else
169             BGP_DAMP_LIST_ADD (damp, bdi);
170         }
171       else
172         /* Re-insert into another list (See RFC2439 Section 4.8.6).  */
173         bgp_reuse_list_add (bdi);
174     }
175
176   return 0;
177 }
178
179 /* A route becomes unreachable (RFC2439 Section 4.8.2).  */
180 int
181 bgp_damp_withdraw (struct bgp_info *binfo, struct bgp_node *rn,
182                    afi_t afi, safi_t safi, int attr_change)
183 {
184   time_t t_now;
185   struct bgp_damp_info *bdi;
186   double last_penalty = 0;
187   
188   t_now = time (NULL);
189
190   /* Processing Unreachable Messages.  */
191   bdi = binfo->damp_info;
192
193   if (bdi == NULL)
194     {
195       /* If there is no previous stability history. */
196
197       /* RFC2439 said:
198          1. allocate a damping structure.
199          2. set figure-of-merit = 1.
200          3. withdraw the route.  */
201
202       bdi =  XCALLOC (MTYPE_BGP_DAMP_INFO, sizeof (struct bgp_damp_info));
203       bdi->binfo = binfo;
204       bdi->rn = rn;
205       bdi->penalty = (attr_change ? DEFAULT_PENALTY / 2 : DEFAULT_PENALTY);
206       bdi->flap = 1;
207       bdi->start_time = t_now;
208       bdi->suppress_time = 0;
209       bdi->index = -1;
210       bdi->afi = afi;
211       bdi->safi = safi;
212       binfo->damp_info = bdi;
213       BGP_DAMP_LIST_ADD (damp, bdi);
214     }
215   else
216     {
217       last_penalty = bdi->penalty;
218
219       /* 1. Set t-diff = t-now - t-updated.  */
220       bdi->penalty = 
221         (bgp_damp_decay (t_now - bdi->t_updated, bdi->penalty) 
222          + (attr_change ? DEFAULT_PENALTY / 2 : DEFAULT_PENALTY));
223
224       if (bdi->penalty > damp->ceiling)
225         bdi->penalty = damp->ceiling;
226
227       bdi->flap++;
228     }
229   
230   bdi->lastrecord = BGP_RECORD_WITHDRAW;
231   bdi->t_updated = t_now;
232
233   /* Make this route as historical status.  */
234   SET_FLAG (binfo->flags, 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       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);
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   bdi = binfo->damp_info;
270   if (! bdi)
271     return BGP_DAMP_USED;
272
273   t_now = time (NULL);
274   UNSET_FLAG (binfo->flags, BGP_INFO_HISTORY); 
275
276   bdi->lastrecord = BGP_RECORD_UPDATE;
277   bdi->penalty = bgp_damp_decay (t_now - bdi->t_updated, bdi->penalty);
278
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) )
284     {
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;
290     }
291   else
292     status = BGP_DAMP_SUPPRESSED;  
293
294   if (bdi->penalty > damp->reuse_limit / 2.0)
295     bdi->t_updated = t_now;
296   else
297     bgp_damp_info_free (bdi, 0);
298         
299   return status;
300 }
301
302 /* Remove dampening information and history route.  */
303 int 
304 bgp_damp_scan (struct bgp_info *binfo, afi_t afi, safi_t safi)
305 {
306   time_t t_now, t_diff;
307   struct bgp_damp_info *bdi;
308
309   t_now = time (NULL);
310   bdi = binfo->damp_info;
311  
312   if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED))
313     {
314       t_diff = t_now - bdi->suppress_time;
315
316       if (t_diff >= damp->max_suppress_time)
317         {
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;
324           
325           /* Need to announce UPDATE once this binfo is usable again. */
326           if (bdi->lastrecord == BGP_RECORD_UPDATE)
327             return 1;
328           else
329             return 0;
330         }
331     }
332   else
333     {
334       t_diff = t_now - bdi->t_updated;
335       bdi->penalty = bgp_damp_decay (t_diff, bdi->penalty);
336
337       if (bdi->penalty <= damp->reuse_limit / 2.0)
338         {
339           /* release the bdi, bdi->binfo. */  
340           bgp_damp_info_free (bdi, 1);
341           return 0;
342         }            
343       else
344         bdi->t_updated = t_now;
345     }       
346   return 0;
347 }
348
349 void
350 bgp_damp_info_free (struct bgp_damp_info *bdi, int withdraw)
351 {
352   struct bgp_info *binfo;
353   void bgp_info_delete (struct bgp_node *, struct bgp_info *);
354   void bgp_info_free (struct bgp_info *);
355
356   if (! bdi)
357     return;
358
359   binfo = bdi->binfo;
360   binfo->damp_info = NULL;
361
362   if (CHECK_FLAG (binfo->flags, BGP_INFO_DAMPED))
363     bgp_reuse_list_delete (bdi);
364   else
365     BGP_DAMP_LIST_DEL (damp, bdi);
366
367   UNSET_FLAG (binfo->flags, BGP_INFO_DAMPED);
368   UNSET_FLAG (binfo->flags, BGP_INFO_HISTORY);
369
370   if (bdi->lastrecord == BGP_RECORD_WITHDRAW && withdraw)
371     {
372       bgp_info_delete (bdi->rn, binfo);
373       bgp_info_free (binfo);
374       bgp_unlock_node (bdi->rn);
375     }
376   XFREE (MTYPE_BGP_DAMP_INFO, bdi);
377 }
378
379 void
380 bgp_damp_parameter_set (int hlife, int reuse, int sup, int maxsup)
381 {
382   double reuse_max_ratio;
383   int i;
384   double j;
385         
386   damp->suppress_value = sup;
387   damp->half_life = hlife;
388   damp->reuse_limit = reuse;
389   damp->max_suppress_time = maxsup;
390
391   /* Initialize params per bgp_damp_config. */
392   damp->reuse_index_size = REUSE_ARRAY_SIZE;
393
394   damp->ceiling = (int)(damp->reuse_limit * (pow(2, (double)damp->max_suppress_time/damp->half_life))); 
395
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));
402
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];
406         
407   /* Reuse-list computations */
408   i = ceil ((double)damp->max_suppress_time / DELTA_REUSE) + 1;
409   if (i > REUSE_LIST_SIZE || i == 0)
410     i = REUSE_LIST_SIZE;
411   damp->reuse_list_size = i; 
412
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 *));  
418
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));
424
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 )
428     reuse_max_ratio = j;
429
430   damp->scale_factor = (double)damp->reuse_index_size/(reuse_max_ratio - 1);
431
432   for (i = 0; i < damp->reuse_index_size; i++)
433     {
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));
437     }
438 }
439
440 int
441 bgp_damp_enable (struct bgp *bgp, afi_t afi, safi_t safi, int half,
442                  int reuse, int suppress, int max)
443 {
444   if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING))
445     {
446       if (damp->half_life == half
447           && damp->reuse_limit == reuse
448           && damp->suppress_value == suppress
449           && damp->max_suppress_time == max)
450         return 0;
451       bgp_damp_disable (bgp, afi, safi);
452     }
453
454   SET_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING);
455   bgp_damp_parameter_set (half, reuse, suppress, max);
456
457   /* Register reuse timer.  */
458   if (! damp->t_reuse)
459     damp->t_reuse = 
460       thread_add_timer (master, bgp_reuse_timer, NULL, DELTA_REUSE);
461
462   return 0;
463 }
464
465 void
466 bgp_damp_config_clean (struct bgp_damp_config *damp)
467 {
468   /* Free decay array */
469   XFREE (MTYPE_BGP_DAMP_ARRAY, damp->decay_array);
470
471   /* Free reuse index array */
472   XFREE (MTYPE_BGP_DAMP_ARRAY, damp->reuse_index);
473
474   /* Free reuse list array. */
475   XFREE (MTYPE_BGP_DAMP_ARRAY, damp->reuse_list);
476 }
477
478 /* Clean all the bgp_damp_info stored in reuse_list. */
479 void
480 bgp_damp_info_clean ()
481 {
482   int i;
483   struct bgp_damp_info *bdi, *next;
484
485   damp->reuse_offset = 0;
486
487   for (i = 0; i < damp->reuse_list_size; i++)
488     {
489       if (! damp->reuse_list[i])
490         continue;
491
492       for (bdi = damp->reuse_list[i]; bdi; bdi = next)
493         {
494           next = bdi->next;
495           bgp_damp_info_free (bdi, 1);
496         }
497       damp->reuse_list[i] = NULL;
498     }
499
500   for (bdi = damp->no_reuse_list; bdi; bdi = next)
501     {
502       next = bdi->next;
503       bgp_damp_info_free (bdi, 1);
504     }
505   damp->no_reuse_list = NULL;
506 }
507
508 int
509 bgp_damp_disable (struct bgp *bgp, afi_t afi, safi_t safi)
510 {
511   /* Cancel reuse thread. */
512   if (damp->t_reuse )
513     thread_cancel (damp->t_reuse);
514   damp->t_reuse = NULL;
515
516   /* Clean BGP dampening information.  */
517   bgp_damp_info_clean ();
518
519   /* Clear configuration */
520   bgp_damp_config_clean (&bgp_damp_cfg);
521
522   UNSET_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING);
523   return 0;
524 }
525
526 int
527 bgp_config_write_damp (struct vty *vty)
528 {
529   if (&bgp_damp_cfg)
530     {
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 %d%s",
541                  bgp_damp_cfg.half_life/60,
542                  VTY_NEWLINE);
543       else
544         vty_out (vty, " bgp dampening %d %d %d %d%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,
549                  VTY_NEWLINE);
550       return 1;
551     }
552   return 0;
553 }
554
555 #define BGP_UPTIME_LEN 25
556
557 char *
558 bgp_get_reuse_time (int penalty, char *buf, size_t len)
559 {
560   time_t reuse_time = 0;
561   struct tm *tm = NULL;
562
563   if (penalty > damp->reuse_limit)
564     {
565       reuse_time = (int) (DELTA_T * ((log((double)damp->reuse_limit/penalty))/(log(damp->decay_array[1])))); 
566
567       if (reuse_time > damp->max_suppress_time)
568         reuse_time = damp->max_suppress_time;
569
570       tm = gmtime (&reuse_time);
571     }
572   else 
573     reuse_time = 0;
574
575   /* Making formatted timer strings. */
576 #define ONE_DAY_SECOND 60*60*24
577 #define ONE_WEEK_SECOND 60*60*24*7
578   if (reuse_time == 0)
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);
586   else
587     snprintf (buf, len, "%02dw%dd%02dh", 
588               tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour); 
589
590   return buf;
591 }
592  
593 void
594 bgp_damp_info_vty (struct vty *vty, struct bgp_info *binfo)  
595 {
596   struct bgp_damp_info *bdi;
597   time_t t_now, t_diff;
598   char timebuf[BGP_UPTIME_LEN];
599   int penalty;
600
601   /* BGP dampening information.  */
602   bdi = binfo->damp_info;
603
604   /* If dampening is not enabled or there is no dampening information,
605      return immediately.  */
606   if (! damp || ! bdi)
607     return;
608
609   /* Calculate new penalty.  */
610   t_now = time (NULL);
611   t_diff = t_now - bdi->t_updated;
612   penalty = bgp_damp_decay (t_diff, bdi->penalty);
613
614   vty_out (vty, "      Dampinfo: penalty %d, flapped %d times in %s",
615            penalty, bdi->flap,
616            peer_uptime (bdi->start_time, timebuf, BGP_UPTIME_LEN));
617
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));
622
623   vty_out (vty, "%s", VTY_NEWLINE);
624 }
625
626 char *
627 bgp_damp_reuse_time_vty (struct vty *vty, struct bgp_info *binfo)
628 {
629   struct bgp_damp_info *bdi;
630   time_t t_now, t_diff;
631   char timebuf[BGP_UPTIME_LEN];
632   int penalty;
633
634   /* BGP dampening information.  */
635   bdi = binfo->damp_info;
636
637   /* If dampening is not enabled or there is no dampening information,
638      return immediately.  */
639   if (! damp || ! bdi)
640     return NULL;
641
642   /* Calculate new penalty.  */
643   t_now = time (NULL);
644   t_diff = t_now - bdi->t_updated;
645   penalty = bgp_damp_decay (t_diff, bdi->penalty);
646
647   return  bgp_get_reuse_time (penalty, timebuf, BGP_UPTIME_LEN);
648 }