paul | 718e374 | 2002-12-13 20:15:29 +0000 | [diff] [blame] | 1 | /* |
| 2 | * OSPF flap dampening by Manav Bhatia |
| 3 | * Copyright (C) 2002 |
| 4 | * |
| 5 | * This file is part of GNU Zebra. |
| 6 | * |
| 7 | * GNU Zebra is free software; you can redistribute it and/or modify it |
| 8 | * under the terms of the GNU General Public License as published by the |
| 9 | * Free Software Foundation; either version 2, or (at your option) any |
| 10 | * later version. |
| 11 | * |
| 12 | * GNU Zebra is distributed in the hope that it will be useful, but |
| 13 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 15 | * General Public License for more details. |
| 16 | * |
| 17 | * You should have received a copy of the GNU General Public License |
| 18 | * along with GNU Zebra; see the file COPYING. If not, write to the Free |
| 19 | * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA |
| 20 | * 02111-1307, USA. |
| 21 | */ |
| 22 | |
| 23 | #include <zebra.h> |
| 24 | #include <math.h> |
| 25 | |
| 26 | #include "log.h" |
| 27 | #include "prefix.h" |
| 28 | #include "thread.h" |
| 29 | #include "table.h" |
| 30 | #include "command.h" |
| 31 | #include "vty.h" |
| 32 | |
| 33 | extern struct thread_master *master; |
| 34 | |
| 35 | #include "ospf6_damp.h" |
| 36 | |
| 37 | #ifdef HAVE_OSPF6_DAMP |
| 38 | |
| 39 | #define DELTA_REUSE 10 /* Time granularity for reuse lists */ |
| 40 | #define DELTA_T 5 /* Time granularity for decay arrays */ |
| 41 | #define DEFAULT_HALF_LIFE 60 /* (sec) 1 min */ |
| 42 | |
| 43 | #define DEFAULT_PENALTY 1000 |
| 44 | #define DEFAULT_REUSE 750 |
| 45 | #define DEFAULT_SUPPRESS 2000 |
| 46 | |
| 47 | #define REUSE_LIST_SIZE 256 |
| 48 | #define REUSE_ARRAY_SIZE 1024 |
| 49 | |
| 50 | /* Global variable to access damping configuration */ |
| 51 | struct ospf6_damp_config damp_config; |
| 52 | struct ospf6_damp_config *dc = &damp_config; |
| 53 | u_int reuse_array_offset = 0; |
| 54 | struct route_table *damp_info_table[OSPF6_DAMP_TYPE_MAX]; |
| 55 | struct thread *ospf6_reuse_thread = NULL; |
| 56 | |
| 57 | int ospf6_damp_debug = 0; |
| 58 | #define IS_OSPF6_DEBUG_DAMP (ospf6_damp_debug) |
| 59 | |
| 60 | static struct ospf6_damp_info * |
| 61 | ospf6_damp_lookup (u_short type, struct prefix *name) |
| 62 | { |
| 63 | struct route_node *node; |
| 64 | |
| 65 | node = route_node_lookup (damp_info_table[type], name); |
| 66 | if (node && node->info) |
| 67 | return (struct ospf6_damp_info *) node->info; |
| 68 | return NULL; |
| 69 | } |
| 70 | |
| 71 | static struct ospf6_damp_info * |
| 72 | ospf6_damp_create (u_short type, struct prefix *name) |
| 73 | { |
| 74 | struct route_node *node; |
| 75 | struct ospf6_damp_info *di; |
| 76 | char namebuf[64]; |
| 77 | |
| 78 | di = ospf6_damp_lookup (type, name); |
| 79 | if (di) |
| 80 | return di; |
| 81 | |
| 82 | if (IS_OSPF6_DEBUG_DAMP) |
| 83 | { |
| 84 | prefix2str (name, namebuf, sizeof (namebuf)); |
| 85 | zlog_info ("DAMP: create: type: %d, name: %s", type, namebuf); |
| 86 | } |
| 87 | |
| 88 | di = (struct ospf6_damp_info *) |
| 89 | malloc (sizeof (struct ospf6_damp_info)); |
| 90 | memset (di, 0, sizeof (struct ospf6_damp_info)); |
| 91 | di->type = type; |
| 92 | prefix_copy (&di->name, name); |
| 93 | |
| 94 | node = route_node_get (damp_info_table[type], name); |
| 95 | node->info = di; |
| 96 | |
| 97 | return di; |
| 98 | } |
| 99 | |
| 100 | static void |
| 101 | ospf6_damp_delete (u_short type, struct prefix *name) |
| 102 | { |
| 103 | struct route_node *node; |
| 104 | struct ospf6_damp_info *di; |
| 105 | char namebuf[64]; |
| 106 | |
| 107 | node = route_node_lookup (damp_info_table[type], name); |
| 108 | if (! node || ! node->info) |
| 109 | return; |
| 110 | |
| 111 | di = node->info; |
| 112 | |
| 113 | if (IS_OSPF6_DEBUG_DAMP) |
| 114 | { |
| 115 | prefix2str (&di->name, namebuf, sizeof (namebuf)); |
| 116 | zlog_info ("DAMP: delete: type: %d, name: %s", |
| 117 | di->type, namebuf); |
| 118 | } |
| 119 | |
| 120 | node->info = NULL; |
| 121 | free (di); |
| 122 | } |
| 123 | |
| 124 | /* compute and fill the configuration parameter */ |
| 125 | void |
| 126 | ospf6_damp_init_config (u_int half_life, u_int reuse, |
| 127 | u_int suppress, u_int t_hold) |
| 128 | { |
| 129 | int i; |
| 130 | double max_ratio, max_ratio1, max_ratio2; |
| 131 | |
| 132 | dc->half_life = half_life ? half_life : DEFAULT_HALF_LIFE; |
| 133 | dc->reuse = reuse ? reuse : DEFAULT_REUSE; |
| 134 | dc->suppress = suppress ? suppress : DEFAULT_SUPPRESS; |
| 135 | dc->t_hold = t_hold ? t_hold : 4 * dc->half_life; |
| 136 | |
| 137 | /* Initialize system-wide params */ |
| 138 | dc->delta_t = DELTA_T; |
| 139 | dc->delta_reuse = DELTA_REUSE; |
| 140 | dc->default_penalty = DEFAULT_PENALTY; |
| 141 | dc->reuse_index_array_size = REUSE_ARRAY_SIZE; |
| 142 | |
| 143 | /* ceiling is the maximum penalty a route may attain */ |
| 144 | /* ceiling = reuse * 2^(T-hold/half-life) */ |
| 145 | dc->ceiling = (int) |
| 146 | (dc->reuse * (pow (2, (double) dc->t_hold / dc->half_life))); |
| 147 | |
| 148 | /* Decay-array computations */ |
| 149 | /* decay_array_size = decay memory/time granularity */ |
| 150 | dc->decay_array_size = ceil ((double) dc->t_hold / dc->delta_t); |
| 151 | dc->decay_array = malloc (sizeof (double) * (dc->decay_array_size)); |
| 152 | |
| 153 | /* Each i-th element is per tick delay raised to the i-th power */ |
| 154 | dc->decay_array[0] = 1.0; |
| 155 | dc->decay_array[1] = exp ((1.0 / (dc->half_life / dc->delta_t)) * log (0.5)); |
| 156 | for (i = 2; i < dc->decay_array_size; i++) |
| 157 | dc->decay_array[i] = dc->decay_array[i - 1] * dc->decay_array[1]; |
| 158 | |
| 159 | /* Reuse-list computations (reuse queue head array ?) */ |
| 160 | dc->reuse_list_size = ceil ((double) dc->t_hold / dc->delta_reuse) + 1; |
| 161 | if (dc->reuse_list_size == 0 || dc->reuse_list_size > REUSE_LIST_SIZE) |
| 162 | dc->reuse_list_size = REUSE_LIST_SIZE; |
| 163 | dc->reuse_list_array = (struct ospf6_damp_info **) |
| 164 | malloc (dc->reuse_list_size * sizeof (struct ospf6_reuse_list *)); |
| 165 | memset (dc->reuse_list_array, 0x00, |
| 166 | dc->reuse_list_size * sizeof (struct ospf6_reuse_list *)); |
| 167 | |
| 168 | /* Reuse-array computations */ |
| 169 | dc->reuse_index_array = malloc (sizeof (int) * dc->reuse_index_array_size); |
| 170 | |
| 171 | /* |
| 172 | * This is the maximum ratio between the current value of the penalty and |
| 173 | * the reuse value which can be indexed by the reuse array. It will be |
| 174 | * limited by the ceiling or by the amount of time that the reuse list |
| 175 | * covers |
| 176 | */ |
| 177 | max_ratio1 = (double) dc->ceiling / dc->reuse; |
| 178 | max_ratio2 = exp ((double) dc->t_hold / dc->half_life) * log10 (2.0); |
| 179 | max_ratio = (max_ratio2 != 0 && max_ratio2 < max_ratio1 ? |
| 180 | max_ratio2 : max_ratio1); |
| 181 | |
| 182 | /* |
| 183 | * reuse array is just an estimator and we need something |
| 184 | * to use the full array |
| 185 | */ |
| 186 | dc->scale_factor = (double) dc->reuse_index_array_size / (max_ratio - 1); |
| 187 | |
| 188 | for (i = 0; i < dc->reuse_index_array_size; i++) |
| 189 | { |
| 190 | dc->reuse_index_array[i] = (int) |
| 191 | (((double) dc->half_life / dc->delta_reuse) * |
| 192 | log10 (1.0 / (dc->reuse * (1.0 + ((double) i / dc->scale_factor)))) |
| 193 | / log10 (0.5)); |
| 194 | } |
| 195 | |
| 196 | dc->enabled = ON; |
| 197 | } |
| 198 | |
| 199 | static double |
| 200 | ospf6_damp_decay (time_t tdiff) |
| 201 | { |
| 202 | int index = tdiff / dc->delta_t; |
| 203 | |
| 204 | if (index >= dc->decay_array_size) |
| 205 | return 0; |
| 206 | |
| 207 | return dc->decay_array[index]; |
| 208 | } |
| 209 | |
| 210 | static int |
| 211 | ospf6_damp_reuse_index (int penalty) |
| 212 | { |
| 213 | int index; |
| 214 | |
| 215 | index = (int) (((double) penalty / dc->reuse - 1.0) * dc->scale_factor); |
| 216 | |
| 217 | if (index >= dc->reuse_index_array_size) |
| 218 | index = dc->reuse_index_array_size - 1; |
| 219 | |
| 220 | return (dc->reuse_index_array[index] - dc->reuse_index_array[0]); |
| 221 | } |
| 222 | |
| 223 | static int |
| 224 | ospf6_reuse_list_lookup (struct ospf6_damp_info *di) |
| 225 | { |
| 226 | struct ospf6_damp_info *info; |
| 227 | |
| 228 | for (info = dc->reuse_list_array[di->index]; info; info = info->next) |
| 229 | { |
| 230 | if (info == di) |
| 231 | return 1; |
| 232 | } |
| 233 | return 0; |
| 234 | } |
| 235 | |
| 236 | static void |
| 237 | ospf6_reuse_list_remove (struct ospf6_damp_info *di) |
| 238 | { |
| 239 | if (di->prev) |
| 240 | di->prev->next = di->next; |
| 241 | else |
| 242 | dc->reuse_list_array[di->index] = di->next; |
| 243 | if (di->next) |
| 244 | di->next->prev = di->prev; |
| 245 | |
| 246 | di->index = -1; |
| 247 | di->prev = NULL; |
| 248 | di->next = NULL; |
| 249 | } |
| 250 | |
| 251 | static void |
| 252 | ospf6_reuse_list_add (struct ospf6_damp_info *di) |
| 253 | { |
| 254 | /* set the index of reuse-array */ |
| 255 | di->index = (reuse_array_offset + (ospf6_damp_reuse_index (di->penalty))) |
| 256 | % dc->reuse_list_size; |
| 257 | |
| 258 | /* insert to the head of the reuse list */ |
| 259 | di->next = dc->reuse_list_array[di->index]; |
| 260 | if (di->next) |
| 261 | di->next->prev = di; |
| 262 | di->prev = NULL; |
| 263 | dc->reuse_list_array[di->index] = di; |
| 264 | } |
| 265 | |
| 266 | /* When we quit damping for a target, we should execute proper event |
| 267 | which have been postponed during damping */ |
| 268 | static void |
| 269 | ospf6_damp_stop (struct ospf6_damp_info *di) |
| 270 | { |
| 271 | time_t t_now; |
| 272 | char namebuf[64]; |
| 273 | struct timeval now; |
| 274 | |
| 275 | if (IS_OSPF6_DEBUG_DAMP) |
| 276 | { |
| 277 | t_now = time (NULL); |
| 278 | prefix2str (&di->name, namebuf, sizeof (namebuf)); |
| 279 | gettimeofday (&now, NULL); |
| 280 | zlog_info ("DAMP: %lu.%06lu stop damping: %ld: type: %d, name: %s", |
| 281 | now.tv_sec, now.tv_usec, |
| 282 | t_now, di->type, namebuf); |
| 283 | } |
| 284 | |
| 285 | /* set flag indicates that we're damping this target */ |
| 286 | di->damping = OFF; |
| 287 | |
| 288 | /* if the target's current status differ from that it should be, |
| 289 | execute the proper event to repair his status */ |
| 290 | if (di->target_status != di->event_type) |
| 291 | { |
| 292 | (*(di->event)) (di->target); |
| 293 | di->target_status = di->event_type; |
| 294 | |
| 295 | di->event = NULL; |
| 296 | di->event_type = event_none; |
| 297 | } |
| 298 | } |
| 299 | |
| 300 | /* ospf6_reuse_timer is called every DELTA_REUSE seconds. |
| 301 | Each route in the current reuse-list is evaluated |
| 302 | and is used or requeued */ |
| 303 | int |
| 304 | ospf6_damp_reuse_timer (struct thread *t) |
| 305 | { |
| 306 | struct ospf6_damp_info *di, *next; |
| 307 | time_t t_now, t_diff; |
| 308 | char namebuf[64]; |
| 309 | struct timeval now; |
| 310 | |
| 311 | /* Restart the reuse timer */ |
| 312 | ospf6_reuse_thread = |
| 313 | thread_add_timer (master, ospf6_damp_reuse_timer, NULL, dc->delta_reuse); |
| 314 | |
| 315 | t_now = time (NULL); |
| 316 | |
| 317 | /* get the damp info list head */ |
| 318 | di = dc->reuse_list_array[reuse_array_offset]; |
| 319 | dc->reuse_list_array[reuse_array_offset] = NULL; |
| 320 | |
| 321 | /* rotate the circular reuse list head array */ |
| 322 | reuse_array_offset = (reuse_array_offset + 1) % dc->reuse_list_size; |
| 323 | |
| 324 | /* for each damp info */ |
| 325 | while (di) |
| 326 | { |
| 327 | next = di->next; |
| 328 | di->next = NULL; |
| 329 | |
| 330 | /* Update penalty */ |
| 331 | t_diff = t_now - di->t_updated; |
| 332 | di->t_updated = t_now; |
| 333 | di->penalty = (int) |
| 334 | ((double) di->penalty * ospf6_damp_decay (t_diff)); |
| 335 | /* configration of ceiling may be just changed */ |
| 336 | if (di->penalty > dc->ceiling) |
| 337 | di->penalty = dc->ceiling; |
| 338 | |
| 339 | if (IS_OSPF6_DEBUG_DAMP) |
| 340 | { |
| 341 | prefix2str (&di->name, namebuf, sizeof (namebuf)); |
| 342 | gettimeofday (&now, NULL); |
| 343 | zlog_info ("DAMP: %lu.%06lu update penalty: type: %d, name: %s, penalty: %d", |
| 344 | now.tv_sec, now.tv_usec, |
| 345 | di->type, namebuf, di->penalty); |
| 346 | } |
| 347 | |
| 348 | /* If the penalty becomes under reuse, |
| 349 | call real event that we have been postponed. */ |
| 350 | if (di->penalty < dc->reuse && di->damping == ON) |
| 351 | ospf6_damp_stop (di); |
| 352 | |
| 353 | /* If the penalty becomes less than the half of the |
| 354 | reuse value, this damp info will be freed from reuse-list, |
| 355 | by assuming that it is considered to be stable enough already, |
| 356 | and there's no need to maintain flapping history for this. */ |
| 357 | if (di->penalty <= dc->reuse / 2) |
| 358 | { |
| 359 | ospf6_damp_delete (di->type, &di->name); |
| 360 | di = next; |
| 361 | continue; |
| 362 | } |
| 363 | |
| 364 | /* re-insert to the reuse-list */ |
| 365 | ospf6_reuse_list_add (di); |
| 366 | |
| 367 | di = next; |
| 368 | } |
| 369 | |
| 370 | return 0; |
| 371 | } |
| 372 | |
| 373 | static void |
| 374 | ospf6_damp_event (damp_event_t event_type, |
| 375 | u_short type, struct prefix *name, |
| 376 | int (*event) (void *), void *target) |
| 377 | { |
| 378 | time_t t_now, t_diff; |
| 379 | struct ospf6_damp_info *di; |
| 380 | char namebuf[64]; |
| 381 | struct timeval now; |
| 382 | |
| 383 | if (dc->enabled == OFF) |
| 384 | { |
| 385 | (*event) (target); |
| 386 | return; |
| 387 | } |
| 388 | |
| 389 | di = ospf6_damp_lookup (type, name); |
| 390 | if (! di) |
| 391 | di = ospf6_damp_create (type, name); |
| 392 | |
| 393 | t_now = time (NULL); |
| 394 | |
| 395 | di->event = event; |
| 396 | di->target = target; |
| 397 | di->event_type = event_type; |
| 398 | |
| 399 | if (! ospf6_reuse_list_lookup (di)) |
| 400 | di->t_start = t_now; |
| 401 | else |
| 402 | { |
| 403 | ospf6_reuse_list_remove (di); |
| 404 | |
| 405 | t_diff = t_now - di->t_updated; |
| 406 | di->penalty = (int) (di->penalty * ospf6_damp_decay (t_diff)); |
| 407 | } |
| 408 | |
| 409 | /* penalty only on down event */ |
| 410 | if (event_type == event_down) |
| 411 | { |
| 412 | di->flap++; |
| 413 | di->penalty += dc->default_penalty; |
| 414 | } |
| 415 | |
| 416 | /* limit penalty up to ceiling */ |
| 417 | if (di->penalty > dc->ceiling) |
| 418 | di->penalty = dc->ceiling; |
| 419 | |
| 420 | if (IS_OSPF6_DEBUG_DAMP) |
| 421 | { |
| 422 | prefix2str (&di->name, namebuf, sizeof (namebuf)); |
| 423 | gettimeofday (&now, NULL); |
| 424 | zlog_info ("DAMP: %lu.%06lu update penalty: type: %d, name: %s, penalty: %d", |
| 425 | now.tv_sec, now.tv_usec, |
| 426 | di->type, namebuf, di->penalty); |
| 427 | } |
| 428 | |
| 429 | /* if penalty < reuse, stop damping here */ |
| 430 | if (di->penalty < dc->reuse && di->damping == ON) |
| 431 | { |
| 432 | if (IS_OSPF6_DEBUG_DAMP) |
| 433 | { |
| 434 | prefix2str (&di->name, namebuf, sizeof (namebuf)); |
| 435 | gettimeofday (&now, NULL); |
| 436 | zlog_info ("DAMP: %lu.%06lu stop damping: %ld: type: %d, name: %s", |
| 437 | now.tv_sec, now.tv_usec, |
| 438 | t_now, di->type, namebuf); |
| 439 | } |
| 440 | di->damping = OFF; |
| 441 | } |
| 442 | |
| 443 | /* if event == up and if penalty >= suppress , start damping here */ |
| 444 | if (di->event_type == event_up && di->penalty >= dc->suppress && |
| 445 | di->damping == OFF) |
| 446 | { |
| 447 | if (IS_OSPF6_DEBUG_DAMP) |
| 448 | { |
| 449 | prefix2str (&di->name, namebuf, sizeof (namebuf)); |
| 450 | gettimeofday (&now, NULL); |
| 451 | zlog_info ("DAMP: %lu.%06lu start damping: %ld: type: %d, name: %s", |
| 452 | now.tv_sec, now.tv_usec, |
| 453 | t_now, type, namebuf); |
| 454 | } |
| 455 | di->damping = ON; |
| 456 | } |
| 457 | |
| 458 | /* execute event if we're not damping */ |
| 459 | if (di->damping == OFF) |
| 460 | { |
| 461 | (*(di->event)) (di->target); |
| 462 | di->target_status = di->event_type; |
| 463 | } |
| 464 | |
| 465 | /* if the penalty goes beyond suppress value, start damping */ |
| 466 | if (di->penalty >= dc->suppress && di->damping == OFF) |
| 467 | { |
| 468 | if (IS_OSPF6_DEBUG_DAMP) |
| 469 | { |
| 470 | prefix2str (name, namebuf, sizeof (namebuf)); |
| 471 | gettimeofday (&now, NULL); |
| 472 | zlog_info ("DAMP: %lu.%06lu start damping: %ld: type: %d, name: %s", |
| 473 | now.tv_sec, now.tv_usec, |
| 474 | t_now, type, namebuf); |
| 475 | } |
| 476 | di->damping = ON; |
| 477 | } |
| 478 | |
| 479 | /* update last-updated-time field */ |
| 480 | di->t_updated = t_now; |
| 481 | |
| 482 | /* Insert it into the reuse list */ |
| 483 | ospf6_reuse_list_add (di); |
| 484 | } |
| 485 | |
| 486 | void |
| 487 | ospf6_damp_event_up (u_short type, struct prefix *name, |
| 488 | int (*event) (void *), void *target) |
| 489 | { |
| 490 | struct timeval now; |
| 491 | |
| 492 | gettimeofday (&now, NULL); |
| 493 | if (IS_OSPF6_DEBUG_DAMP) |
| 494 | zlog_info ("DAMP: Up Event at %lu.%06lu", now.tv_sec, now.tv_usec); |
| 495 | |
| 496 | ospf6_damp_event (event_up, type, name, event, target); |
| 497 | } |
| 498 | |
| 499 | void |
| 500 | ospf6_damp_event_down (u_short type, struct prefix *name, |
| 501 | int (*event) (void *), void *target) |
| 502 | { |
| 503 | struct timeval now; |
| 504 | |
| 505 | gettimeofday (&now, NULL); |
| 506 | if (IS_OSPF6_DEBUG_DAMP) |
| 507 | zlog_info ("DAMP: Down Event at %lu.%06lu", now.tv_sec, now.tv_usec); |
| 508 | |
| 509 | ospf6_damp_event (event_down, type, name, event, target); |
| 510 | } |
| 511 | |
| 512 | int |
| 513 | ospf6_damp_debug_thread (struct thread *thread) |
| 514 | { |
| 515 | int i; |
| 516 | struct ospf6_damp_info *di; |
| 517 | char buf[256]; |
| 518 | time_t t_now; |
| 519 | struct timeval now; |
| 520 | |
| 521 | for (i = 0; i < dc->reuse_list_size; i++) |
| 522 | { |
| 523 | for (di = dc->reuse_list_array[i]; di; di = di->next) |
| 524 | { |
| 525 | t_now = time (NULL); |
| 526 | gettimeofday (&now, NULL); |
| 527 | prefix2str (&di->name, buf, sizeof (buf)); |
| 528 | zlog_info ("DAMP: %lu.%06lu %c %-32s penalty %7u", |
| 529 | now.tv_sec, now.tv_usec, |
| 530 | (di->damping == ON ? 'D' : 'A'), buf, |
| 531 | (u_int) (di->penalty * |
| 532 | ospf6_damp_decay (t_now - di->t_updated))); |
| 533 | } |
| 534 | } |
| 535 | thread_add_timer (master, ospf6_damp_debug_thread, NULL, 1); |
| 536 | return 0; |
| 537 | } |
| 538 | |
| 539 | DEFUN (show_ipv6_ospf6_route_flapping, |
| 540 | show_ipv6_ospf6_route_flapping_cmd, |
| 541 | "show ipv6 ospf6 route flapping", |
| 542 | SHOW_STR |
| 543 | IP6_STR |
| 544 | OSPF6_STR) |
| 545 | { |
| 546 | int i; |
| 547 | struct ospf6_damp_info *di; |
| 548 | char buf[256]; |
| 549 | time_t t_now; |
| 550 | |
| 551 | t_now = time (NULL); |
| 552 | vty_out (vty, "%c %-32s %7s%s", ' ', "Prefix", "penalty", VTY_NEWLINE); |
| 553 | |
| 554 | for (i = 0; i < dc->reuse_list_size; i++) |
| 555 | { |
| 556 | for (di = dc->reuse_list_array[i]; di; di = di->next) |
| 557 | { |
| 558 | prefix2str (&di->name, buf, sizeof (buf)); |
| 559 | vty_out (vty, "%c %-32s %7u%s", |
| 560 | (di->damping == ON ? 'D' : ' '), buf, |
| 561 | (u_int) (di->penalty * |
| 562 | ospf6_damp_decay (t_now - di->t_updated)), |
| 563 | VTY_NEWLINE); |
| 564 | } |
| 565 | } |
| 566 | |
| 567 | return CMD_SUCCESS; |
| 568 | } |
| 569 | |
| 570 | DEFUN (flap_damping_route, |
| 571 | flap_damping_route_cmd, |
| 572 | "flap-damping route <0-4294967295> <0-4294967295> " |
| 573 | "<0-4294967295> <0-4294967295>", |
| 574 | "enable flap dampening\n" |
| 575 | "enable route flap dampening\n" |
| 576 | "half-life in second\n" |
| 577 | "reuse value\n" |
| 578 | "suppress value\n" |
| 579 | "t-hold in second (maximum time that the target can be damped)\n" |
| 580 | ) |
| 581 | { |
| 582 | u_int half_life, reuse, suppress, t_hold; |
| 583 | |
| 584 | if (argc) |
| 585 | { |
| 586 | half_life = (u_int) strtoul (argv[0], NULL, 10); |
| 587 | reuse = (u_int) strtoul (argv[1], NULL, 10); |
| 588 | suppress = (u_int) strtoul (argv[2], NULL, 10); |
| 589 | t_hold = (u_int) strtoul (argv[3], NULL, 10); |
| 590 | } |
| 591 | else |
| 592 | { |
| 593 | half_life = (u_int) DEFAULT_HALF_LIFE; |
| 594 | reuse = (u_int) DEFAULT_REUSE; |
| 595 | suppress = (u_int) DEFAULT_SUPPRESS; |
| 596 | t_hold = (u_int) DEFAULT_HALF_LIFE * 4; |
| 597 | } |
| 598 | |
| 599 | if (reuse && suppress && reuse >= suppress) |
| 600 | { |
| 601 | vty_out (vty, "reuse value exceeded suppress value, failed%s\n", |
| 602 | VTY_NEWLINE); |
| 603 | return CMD_SUCCESS; |
| 604 | } |
| 605 | |
| 606 | if (half_life && t_hold && half_life >= t_hold) |
| 607 | { |
| 608 | vty_out (vty, "half-life exceeded t-hold, failed%s\n", VTY_NEWLINE); |
| 609 | return CMD_SUCCESS; |
| 610 | } |
| 611 | |
| 612 | ospf6_damp_init_config (half_life, reuse, suppress, t_hold); |
| 613 | |
| 614 | if (ospf6_reuse_thread == NULL) |
| 615 | ospf6_reuse_thread = |
| 616 | thread_add_timer (master, ospf6_damp_reuse_timer, NULL, dc->delta_reuse); |
| 617 | |
| 618 | return CMD_SUCCESS; |
| 619 | } |
| 620 | |
| 621 | DEFUN (show_ipv6_ospf6_damp_config, |
| 622 | show_ipv6_ospf6_camp_config_cmd, |
| 623 | "show ipv6 ospf6 damp config", |
| 624 | SHOW_STR |
| 625 | IP6_STR |
| 626 | OSPF6_STR |
| 627 | "Flap-dampening information\n" |
| 628 | "shows dampening configuration\n" |
| 629 | ) |
| 630 | { |
| 631 | int i; |
| 632 | |
| 633 | vty_out (vty, "%10s %10s %10s %10s%s", |
| 634 | "Half life", "Suppress", "Reuse", "T-hold", |
| 635 | VTY_NEWLINE); |
| 636 | vty_out (vty, "%10u %10u %10u %10u%s", |
| 637 | dc->half_life, dc->suppress, dc->reuse, dc->t_hold, |
| 638 | VTY_NEWLINE); |
| 639 | vty_out (vty, "%s", VTY_NEWLINE); |
| 640 | |
| 641 | vty_out (vty, "Delta-t = %u%s", dc->delta_t, VTY_NEWLINE); |
| 642 | vty_out (vty, "Delta-Reuse = %u%s", dc->delta_reuse, VTY_NEWLINE); |
| 643 | vty_out (vty, "Default-Penalty = %u%s", dc->default_penalty, VTY_NEWLINE); |
| 644 | vty_out (vty, "Ceiling = %u%s", dc->ceiling, VTY_NEWLINE); |
| 645 | vty_out (vty, "ScaleFactor = %f%s", dc->scale_factor, VTY_NEWLINE); |
| 646 | |
| 647 | vty_out (vty, "DecayArray(%d) =%s", dc->decay_array_size, VTY_NEWLINE); |
| 648 | for (i = 0; i < dc->decay_array_size; i++) |
| 649 | { |
| 650 | if (i % 10 == 0) |
| 651 | vty_out (vty, " "); |
| 652 | vty_out (vty, " %f", dc->decay_array[i]); |
| 653 | if (i % 10 == 0) |
| 654 | vty_out (vty, "%s", VTY_NEWLINE); |
| 655 | } |
| 656 | vty_out (vty, "%s", VTY_NEWLINE); |
| 657 | |
| 658 | vty_out (vty, "ReuseIndexArray(%d) =%s", |
| 659 | dc->reuse_index_array_size, VTY_NEWLINE); |
| 660 | for (i = 0; i < dc->reuse_index_array_size; i++) |
| 661 | { |
| 662 | if (i % 10 == 0) |
| 663 | vty_out (vty, " "); |
| 664 | vty_out (vty, " %d", dc->reuse_index_array[i]); |
| 665 | if (i % 10 == 0) |
| 666 | vty_out (vty, "%s", VTY_NEWLINE); |
| 667 | } |
| 668 | vty_out (vty, "%s", VTY_NEWLINE); |
| 669 | |
| 670 | return CMD_SUCCESS; |
| 671 | } |
| 672 | |
| 673 | void |
| 674 | ospf6_damp_config_write (struct vty *vty) |
| 675 | { |
| 676 | if (dc->enabled == ON) |
| 677 | { |
| 678 | vty_out (vty, " flap-damping route %u %u %u %u%s", |
| 679 | dc->half_life, dc->reuse, dc->suppress, dc->t_hold, |
| 680 | VTY_NEWLINE); |
| 681 | } |
| 682 | } |
| 683 | |
| 684 | DEFUN (debug_ospf6_damp, |
| 685 | debug_ospf6_damp_cmd, |
| 686 | "debug ospf6 damp", |
| 687 | DEBUG_STR |
| 688 | OSPF6_STR |
| 689 | "Flap-dampening information\n" |
| 690 | ) |
| 691 | { |
| 692 | ospf6_damp_debug = 1; |
| 693 | return CMD_SUCCESS; |
| 694 | } |
| 695 | |
| 696 | DEFUN (no_debug_ospf6_damp, |
| 697 | no_debug_ospf6_damp_cmd, |
| 698 | "no debug ospf6 damp", |
| 699 | NO_STR |
| 700 | DEBUG_STR |
| 701 | OSPF6_STR |
| 702 | "Flap-dampening information\n" |
| 703 | ) |
| 704 | { |
| 705 | ospf6_damp_debug = 0; |
| 706 | return CMD_SUCCESS; |
| 707 | } |
| 708 | |
| 709 | DEFUN (show_debug_ospf6_damp, |
| 710 | show_debug_ospf6_damp_cmd, |
| 711 | "show debugging ospf6 damp", |
| 712 | SHOW_STR |
| 713 | DEBUG_STR |
| 714 | OSPF6_STR |
| 715 | "Flap-dampening information\n" |
| 716 | ) |
| 717 | { |
| 718 | vty_out (vty, "debugging ospf6 damp is "); |
| 719 | if (IS_OSPF6_DEBUG_DAMP) |
| 720 | vty_out (vty, "enabled."); |
| 721 | else |
| 722 | vty_out (vty, "disabled."); |
| 723 | vty_out (vty, "%s", VTY_NEWLINE); |
| 724 | return CMD_SUCCESS; |
| 725 | } |
| 726 | |
| 727 | void |
| 728 | ospf6_damp_init () |
| 729 | { |
| 730 | int i; |
| 731 | for (i = 0; i < OSPF6_DAMP_TYPE_MAX; i++) |
| 732 | damp_info_table[i] = route_table_init (); |
| 733 | |
| 734 | install_element (VIEW_NODE, &show_ipv6_ospf6_route_flapping_cmd); |
| 735 | install_element (ENABLE_NODE, &show_ipv6_ospf6_route_flapping_cmd); |
| 736 | install_element (ENABLE_NODE, &show_ipv6_ospf6_camp_config_cmd); |
| 737 | install_element (OSPF6_NODE, &flap_damping_route_cmd); |
| 738 | |
| 739 | install_element (ENABLE_NODE, &show_debug_ospf6_damp_cmd); |
| 740 | install_element (CONFIG_NODE, &debug_ospf6_damp_cmd); |
| 741 | install_element (CONFIG_NODE, &no_debug_ospf6_damp_cmd); |
| 742 | |
| 743 | thread_add_event (master, ospf6_damp_debug_thread, NULL, 0); |
| 744 | } |
| 745 | |
| 746 | #endif /* HAVE_OSPF6_DAMP */ |
| 747 | |
| 748 | |