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