/src/frr/bgpd/bgp_lcommunity.c
Line | Count | Source (jump to first uncovered line) |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /* BGP Large Communities Attribute |
3 | | * |
4 | | * Copyright (C) 2016 Keyur Patel <keyur@arrcus.com> |
5 | | */ |
6 | | |
7 | | #include <zebra.h> |
8 | | |
9 | | #include "hash.h" |
10 | | #include "memory.h" |
11 | | #include "prefix.h" |
12 | | #include "command.h" |
13 | | #include "filter.h" |
14 | | #include "jhash.h" |
15 | | #include "stream.h" |
16 | | |
17 | | #include "bgpd/bgpd.h" |
18 | | #include "bgpd/bgp_lcommunity.h" |
19 | | #include "bgpd/bgp_community_alias.h" |
20 | | #include "bgpd/bgp_aspath.h" |
21 | | |
22 | | /* Hash of community attribute. */ |
23 | | static struct hash *lcomhash; |
24 | | |
25 | | /* Allocate a new lcommunities. */ |
26 | | static struct lcommunity *lcommunity_new(void) |
27 | 10 | { |
28 | 10 | return XCALLOC(MTYPE_LCOMMUNITY, sizeof(struct lcommunity)); |
29 | 10 | } |
30 | | |
31 | | /* Allocate lcommunities. */ |
32 | | void lcommunity_free(struct lcommunity **lcom) |
33 | 10 | { |
34 | 10 | if (!(*lcom)) |
35 | 0 | return; |
36 | | |
37 | 10 | XFREE(MTYPE_LCOMMUNITY_VAL, (*lcom)->val); |
38 | 10 | XFREE(MTYPE_LCOMMUNITY_STR, (*lcom)->str); |
39 | 10 | if ((*lcom)->json) |
40 | 0 | json_object_free((*lcom)->json); |
41 | 10 | XFREE(MTYPE_LCOMMUNITY, *lcom); |
42 | 10 | } |
43 | | |
44 | | static void lcommunity_hash_free(struct lcommunity *lcom) |
45 | 0 | { |
46 | 0 | lcommunity_free(&lcom); |
47 | 0 | } |
48 | | |
49 | | /* Add a new Large Communities value to Large Communities |
50 | | Attribute structure. When the value is already exists in the |
51 | | structure, we don't add the value. Newly added value is sorted by |
52 | | numerical order. When the value is added to the structure return 1 |
53 | | else return 0. */ |
54 | | static bool lcommunity_add_val(struct lcommunity *lcom, |
55 | | struct lcommunity_val *lval) |
56 | 360 | { |
57 | 360 | uint8_t *p; |
58 | 360 | int ret; |
59 | 360 | int c; |
60 | | |
61 | | /* When this is fist value, just add it. */ |
62 | 360 | if (lcom->val == NULL) { |
63 | 10 | lcom->size++; |
64 | 10 | lcom->val = XMALLOC(MTYPE_LCOMMUNITY_VAL, lcom_length(lcom)); |
65 | 10 | memcpy(lcom->val, lval->val, LCOMMUNITY_SIZE); |
66 | 10 | return true; |
67 | 10 | } |
68 | | |
69 | | /* If the value already exists in the structure return 0. */ |
70 | 350 | c = 0; |
71 | 2.19k | for (p = lcom->val; c < lcom->size; p += LCOMMUNITY_SIZE, c++) { |
72 | 2.17k | ret = memcmp(p, lval->val, LCOMMUNITY_SIZE); |
73 | 2.17k | if (ret == 0) |
74 | 117 | return false; |
75 | 2.05k | if (ret > 0) |
76 | 207 | break; |
77 | 2.05k | } |
78 | | |
79 | | /* Add the value to the structure with numerical sorting. */ |
80 | 233 | lcom->size++; |
81 | 233 | lcom->val = |
82 | 233 | XREALLOC(MTYPE_LCOMMUNITY_VAL, lcom->val, lcom_length(lcom)); |
83 | | |
84 | 233 | memmove(lcom->val + (c + 1) * LCOMMUNITY_SIZE, |
85 | 233 | lcom->val + c * LCOMMUNITY_SIZE, |
86 | 233 | (lcom->size - 1 - c) * LCOMMUNITY_SIZE); |
87 | 233 | memcpy(lcom->val + c * LCOMMUNITY_SIZE, lval->val, LCOMMUNITY_SIZE); |
88 | | |
89 | 233 | return true; |
90 | 350 | } |
91 | | |
92 | | /* This function takes pointer to Large Communites structure then |
93 | | create a new Large Communities structure by uniq and sort each |
94 | | Large Communities value. */ |
95 | | struct lcommunity *lcommunity_uniq_sort(struct lcommunity *lcom) |
96 | 10 | { |
97 | 10 | int i; |
98 | 10 | struct lcommunity *new; |
99 | 10 | struct lcommunity_val *lval; |
100 | | |
101 | 10 | if (!lcom) |
102 | 0 | return NULL; |
103 | | |
104 | 10 | new = lcommunity_new(); |
105 | | |
106 | 370 | for (i = 0; i < lcom->size; i++) { |
107 | 360 | lval = (struct lcommunity_val *)(lcom->val |
108 | 360 | + (i * LCOMMUNITY_SIZE)); |
109 | 360 | lcommunity_add_val(new, lval); |
110 | 360 | } |
111 | 10 | return new; |
112 | 10 | } |
113 | | |
114 | | /* Parse Large Communites Attribute in BGP packet. */ |
115 | | struct lcommunity *lcommunity_parse(uint8_t *pnt, unsigned short length) |
116 | 13 | { |
117 | 13 | struct lcommunity tmp; |
118 | 13 | struct lcommunity *new; |
119 | | |
120 | | /* Length check. */ |
121 | 13 | if (length % LCOMMUNITY_SIZE) |
122 | 3 | return NULL; |
123 | | |
124 | | /* Prepare tmporary structure for making a new Large Communities |
125 | | Attribute. */ |
126 | 10 | tmp.size = length / LCOMMUNITY_SIZE; |
127 | 10 | tmp.val = pnt; |
128 | | |
129 | | /* Create a new Large Communities Attribute by uniq and sort each |
130 | | Large Communities value */ |
131 | 10 | new = lcommunity_uniq_sort(&tmp); |
132 | | |
133 | 10 | return lcommunity_intern(new); |
134 | 13 | } |
135 | | |
136 | | /* Duplicate the Large Communities Attribute structure. */ |
137 | | struct lcommunity *lcommunity_dup(struct lcommunity *lcom) |
138 | 0 | { |
139 | 0 | struct lcommunity *new; |
140 | |
|
141 | 0 | new = lcommunity_new(); |
142 | 0 | new->size = lcom->size; |
143 | 0 | if (new->size) { |
144 | 0 | new->val = XMALLOC(MTYPE_LCOMMUNITY_VAL, lcom_length(lcom)); |
145 | 0 | memcpy(new->val, lcom->val, lcom_length(lcom)); |
146 | 0 | } else |
147 | 0 | new->val = NULL; |
148 | 0 | return new; |
149 | 0 | } |
150 | | |
151 | | /* Merge two Large Communities Attribute structure. */ |
152 | | struct lcommunity *lcommunity_merge(struct lcommunity *lcom1, |
153 | | struct lcommunity *lcom2) |
154 | 0 | { |
155 | 0 | lcom1->val = XREALLOC(MTYPE_LCOMMUNITY_VAL, lcom1->val, |
156 | 0 | lcom_length(lcom1) + lcom_length(lcom2)); |
157 | |
|
158 | 0 | memcpy(lcom1->val + lcom_length(lcom1), lcom2->val, lcom_length(lcom2)); |
159 | 0 | lcom1->size += lcom2->size; |
160 | |
|
161 | 0 | return lcom1; |
162 | 0 | } |
163 | | |
164 | | static void set_lcommunity_string(struct lcommunity *lcom, bool make_json, |
165 | | bool translate_alias) |
166 | 10 | { |
167 | 10 | int i; |
168 | 10 | int len; |
169 | 10 | char *str_buf; |
170 | 10 | const uint8_t *pnt; |
171 | 10 | uint32_t global, local1, local2; |
172 | 10 | json_object *json_lcommunity_list = NULL; |
173 | 10 | json_object *json_string = NULL; |
174 | | |
175 | | /* 3 32-bit integers, 2 colons, and a space */ |
176 | 10 | #define LCOMMUNITY_STRLEN (10 * 3 + 2 + 1) |
177 | | |
178 | 10 | if (!lcom) |
179 | 0 | return; |
180 | | |
181 | 10 | if (make_json) { |
182 | 0 | lcom->json = json_object_new_object(); |
183 | 0 | json_lcommunity_list = json_object_new_array(); |
184 | 0 | } |
185 | | |
186 | 10 | if (lcom->size == 0) { |
187 | 0 | str_buf = XCALLOC(MTYPE_LCOMMUNITY_STR, 1); |
188 | |
|
189 | 0 | if (make_json) { |
190 | 0 | json_object_string_add(lcom->json, "string", ""); |
191 | 0 | json_object_object_add(lcom->json, "list", |
192 | 0 | json_lcommunity_list); |
193 | 0 | } |
194 | |
|
195 | 0 | lcom->str = str_buf; |
196 | 0 | return; |
197 | 0 | } |
198 | | |
199 | | /* 1 space + lcom->size lcom strings + null terminator */ |
200 | 10 | size_t str_buf_sz = (LCOMMUNITY_STRLEN * lcom->size) + 2; |
201 | 10 | str_buf = XCALLOC(MTYPE_LCOMMUNITY_STR, str_buf_sz); |
202 | | |
203 | 10 | len = 0; |
204 | 253 | for (i = 0; i < lcom->size; i++) { |
205 | 243 | if (i > 0) |
206 | 233 | len = strlcat(str_buf, " ", str_buf_sz); |
207 | | |
208 | 243 | pnt = lcom->val + (i * LCOMMUNITY_SIZE); |
209 | 243 | pnt = ptr_get_be32(pnt, &global); |
210 | 243 | pnt = ptr_get_be32(pnt, &local1); |
211 | 243 | pnt = ptr_get_be32(pnt, &local2); |
212 | 243 | (void)pnt; |
213 | | |
214 | 243 | char lcsb[LCOMMUNITY_STRLEN + 1]; |
215 | | |
216 | 243 | snprintf(lcsb, sizeof(lcsb), "%u:%u:%u", global, local1, |
217 | 243 | local2); |
218 | | |
219 | | /* |
220 | | * Aliases can cause havoc, if the alias length is greater |
221 | | * than the LCOMMUNITY_STRLEN for a particular item |
222 | | * then we need to realloc the memory associated |
223 | | * with the string so that it can fit |
224 | | */ |
225 | 243 | const char *com2alias = |
226 | 243 | translate_alias ? bgp_community2alias(lcsb) : lcsb; |
227 | 243 | size_t individual_len = strlen(com2alias); |
228 | 243 | if (individual_len + len > str_buf_sz) { |
229 | 0 | str_buf_sz = individual_len + len + 1; |
230 | 0 | str_buf = XREALLOC(MTYPE_LCOMMUNITY_STR, str_buf, |
231 | 0 | str_buf_sz); |
232 | 0 | } |
233 | | |
234 | 243 | len = strlcat(str_buf, com2alias, str_buf_sz); |
235 | | |
236 | 243 | if (make_json) { |
237 | 0 | json_string = json_object_new_string(com2alias); |
238 | 0 | json_object_array_add(json_lcommunity_list, |
239 | 0 | json_string); |
240 | 0 | } |
241 | 243 | } |
242 | | |
243 | 10 | if (make_json) { |
244 | 0 | json_object_string_add(lcom->json, "string", str_buf); |
245 | 0 | json_object_object_add(lcom->json, "list", |
246 | 0 | json_lcommunity_list); |
247 | 0 | } |
248 | | |
249 | 10 | lcom->str = str_buf; |
250 | 10 | } |
251 | | |
252 | | /* Intern Large Communities Attribute. */ |
253 | | struct lcommunity *lcommunity_intern(struct lcommunity *lcom) |
254 | 10 | { |
255 | 10 | struct lcommunity *find; |
256 | | |
257 | 10 | assert(lcom->refcnt == 0); |
258 | | |
259 | 10 | find = (struct lcommunity *)hash_get(lcomhash, lcom, hash_alloc_intern); |
260 | | |
261 | 10 | if (find != lcom) |
262 | 0 | lcommunity_free(&lcom); |
263 | | |
264 | 10 | find->refcnt++; |
265 | | |
266 | 10 | if (!find->str) |
267 | 10 | set_lcommunity_string(find, false, true); |
268 | | |
269 | 10 | return find; |
270 | 10 | } |
271 | | |
272 | | /* Unintern Large Communities Attribute. */ |
273 | | void lcommunity_unintern(struct lcommunity **lcom) |
274 | 538 | { |
275 | 538 | struct lcommunity *ret; |
276 | | |
277 | 538 | if (!*lcom) |
278 | 528 | return; |
279 | | |
280 | 10 | if ((*lcom)->refcnt) |
281 | 10 | (*lcom)->refcnt--; |
282 | | |
283 | | /* Pull off from hash. */ |
284 | 10 | if ((*lcom)->refcnt == 0) { |
285 | | /* Large community must be in the hash. */ |
286 | 10 | ret = (struct lcommunity *)hash_release(lcomhash, *lcom); |
287 | 10 | assert(ret != NULL); |
288 | | |
289 | 10 | lcommunity_free(lcom); |
290 | 10 | } |
291 | 10 | } |
292 | | |
293 | | /* Return string representation of lcommunities attribute. */ |
294 | | char *lcommunity_str(struct lcommunity *lcom, bool make_json, |
295 | | bool translate_alias) |
296 | 0 | { |
297 | 0 | if (!lcom) |
298 | 0 | return NULL; |
299 | | |
300 | 0 | if (make_json && !lcom->json && lcom->str) |
301 | 0 | XFREE(MTYPE_LCOMMUNITY_STR, lcom->str); |
302 | |
|
303 | 0 | if (!lcom->str) |
304 | 0 | set_lcommunity_string(lcom, make_json, translate_alias); |
305 | |
|
306 | 0 | return lcom->str; |
307 | 0 | } |
308 | | |
309 | | /* Utility function to make hash key. */ |
310 | | unsigned int lcommunity_hash_make(const void *arg) |
311 | 20 | { |
312 | 20 | const struct lcommunity *lcom = arg; |
313 | 20 | int size = lcom_length(lcom); |
314 | | |
315 | 20 | return jhash(lcom->val, size, 0xab125423); |
316 | 20 | } |
317 | | |
318 | | /* Compare two Large Communities Attribute structure. */ |
319 | | bool lcommunity_cmp(const void *arg1, const void *arg2) |
320 | 10 | { |
321 | 10 | const struct lcommunity *lcom1 = arg1; |
322 | 10 | const struct lcommunity *lcom2 = arg2; |
323 | | |
324 | 10 | if (lcom1 == NULL && lcom2 == NULL) |
325 | 0 | return true; |
326 | | |
327 | 10 | if (lcom1 == NULL || lcom2 == NULL) |
328 | 0 | return false; |
329 | | |
330 | 10 | return (lcom1->size == lcom2->size |
331 | 10 | && memcmp(lcom1->val, lcom2->val, lcom_length(lcom1)) == 0); |
332 | 10 | } |
333 | | |
334 | | /* Return communities hash. */ |
335 | | struct hash *lcommunity_hash(void) |
336 | 0 | { |
337 | 0 | return lcomhash; |
338 | 0 | } |
339 | | |
340 | | /* Initialize Large Comminities related hash. */ |
341 | | void lcommunity_init(void) |
342 | 1 | { |
343 | 1 | lcomhash = hash_create(lcommunity_hash_make, lcommunity_cmp, |
344 | 1 | "BGP lcommunity hash"); |
345 | 1 | } |
346 | | |
347 | | void lcommunity_finish(void) |
348 | 0 | { |
349 | 0 | hash_clean_and_free(&lcomhash, (void (*)(void *))lcommunity_hash_free); |
350 | 0 | } |
351 | | |
352 | | /* Get next Large Communities token from the string. |
353 | | * Assumes str is space-delimeted and describes 0 or more |
354 | | * valid large communities |
355 | | */ |
356 | | static const char *lcommunity_gettoken(const char *str, |
357 | | struct lcommunity_val *lval) |
358 | 0 | { |
359 | 0 | const char *p = str; |
360 | | |
361 | | /* Skip white space. */ |
362 | 0 | while (isspace((unsigned char)*p)) { |
363 | 0 | p++; |
364 | 0 | str++; |
365 | 0 | } |
366 | | |
367 | | /* Check the end of the line. */ |
368 | 0 | if (*p == '\0') |
369 | 0 | return NULL; |
370 | | |
371 | | /* Community value. */ |
372 | 0 | int separator = 0; |
373 | 0 | int digit = 0; |
374 | 0 | uint32_t globaladmin = 0; |
375 | 0 | uint32_t localdata1 = 0; |
376 | 0 | uint32_t localdata2 = 0; |
377 | |
|
378 | 0 | while (*p && *p != ' ') { |
379 | | /* large community valid chars */ |
380 | 0 | assert(isdigit((unsigned char)*p) || *p == ':'); |
381 | | |
382 | 0 | if (*p == ':') { |
383 | 0 | separator++; |
384 | 0 | digit = 0; |
385 | 0 | if (separator == 1) { |
386 | 0 | globaladmin = localdata2; |
387 | 0 | } else { |
388 | 0 | localdata1 = localdata2; |
389 | 0 | } |
390 | 0 | localdata2 = 0; |
391 | 0 | } else { |
392 | 0 | digit = 1; |
393 | | /* left shift the accumulated value and add current |
394 | | * digit |
395 | | */ |
396 | 0 | localdata2 *= 10; |
397 | 0 | localdata2 += (*p - '0'); |
398 | 0 | } |
399 | 0 | p++; |
400 | 0 | } |
401 | | |
402 | | /* Assert str was a valid large community */ |
403 | 0 | assert(separator == 2 && digit == 1); |
404 | | |
405 | | /* |
406 | | * Copy the large comm. |
407 | | */ |
408 | 0 | lval->val[0] = (globaladmin >> 24) & 0xff; |
409 | 0 | lval->val[1] = (globaladmin >> 16) & 0xff; |
410 | 0 | lval->val[2] = (globaladmin >> 8) & 0xff; |
411 | 0 | lval->val[3] = globaladmin & 0xff; |
412 | 0 | lval->val[4] = (localdata1 >> 24) & 0xff; |
413 | 0 | lval->val[5] = (localdata1 >> 16) & 0xff; |
414 | 0 | lval->val[6] = (localdata1 >> 8) & 0xff; |
415 | 0 | lval->val[7] = localdata1 & 0xff; |
416 | 0 | lval->val[8] = (localdata2 >> 24) & 0xff; |
417 | 0 | lval->val[9] = (localdata2 >> 16) & 0xff; |
418 | 0 | lval->val[10] = (localdata2 >> 8) & 0xff; |
419 | 0 | lval->val[11] = localdata2 & 0xff; |
420 | |
|
421 | 0 | return p; |
422 | 0 | } |
423 | | |
424 | | /* |
425 | | Convert string to large community attribute. |
426 | | When type is already known, please specify both str and type. |
427 | | |
428 | | When string includes keyword for each large community value. |
429 | | Please specify keyword_included as non-zero value. |
430 | | */ |
431 | | struct lcommunity *lcommunity_str2com(const char *str) |
432 | 0 | { |
433 | 0 | struct lcommunity *lcom = NULL; |
434 | 0 | struct lcommunity_val lval; |
435 | |
|
436 | 0 | if (!lcommunity_list_valid(str, LARGE_COMMUNITY_LIST_STANDARD)) |
437 | 0 | return NULL; |
438 | | |
439 | 0 | do { |
440 | 0 | str = lcommunity_gettoken(str, &lval); |
441 | 0 | if (lcom == NULL) |
442 | 0 | lcom = lcommunity_new(); |
443 | 0 | lcommunity_add_val(lcom, &lval); |
444 | 0 | } while (str); |
445 | |
|
446 | 0 | return lcom; |
447 | 0 | } |
448 | | |
449 | | bool lcommunity_include(struct lcommunity *lcom, uint8_t *ptr) |
450 | 0 | { |
451 | 0 | int i; |
452 | 0 | uint8_t *lcom_ptr; |
453 | |
|
454 | 0 | for (i = 0; i < lcom->size; i++) { |
455 | 0 | lcom_ptr = lcom->val + (i * LCOMMUNITY_SIZE); |
456 | 0 | if (memcmp(ptr, lcom_ptr, LCOMMUNITY_SIZE) == 0) |
457 | 0 | return true; |
458 | 0 | } |
459 | 0 | return false; |
460 | 0 | } |
461 | | |
462 | | bool lcommunity_match(const struct lcommunity *lcom1, |
463 | | const struct lcommunity *lcom2) |
464 | 0 | { |
465 | 0 | int i = 0; |
466 | 0 | int j = 0; |
467 | |
|
468 | 0 | if (lcom1 == NULL && lcom2 == NULL) |
469 | 0 | return true; |
470 | | |
471 | 0 | if (lcom1 == NULL || lcom2 == NULL) |
472 | 0 | return false; |
473 | | |
474 | 0 | if (lcom1->size < lcom2->size) |
475 | 0 | return false; |
476 | | |
477 | | /* Every community on com2 needs to be on com1 for this to match */ |
478 | 0 | while (i < lcom1->size && j < lcom2->size) { |
479 | 0 | if (memcmp(lcom1->val + (i * LCOMMUNITY_SIZE), |
480 | 0 | lcom2->val + (j * LCOMMUNITY_SIZE), LCOMMUNITY_SIZE) |
481 | 0 | == 0) |
482 | 0 | j++; |
483 | 0 | i++; |
484 | 0 | } |
485 | |
|
486 | 0 | if (j == lcom2->size) |
487 | 0 | return true; |
488 | 0 | else |
489 | 0 | return false; |
490 | 0 | } |
491 | | |
492 | | /* Delete one lcommunity. */ |
493 | | void lcommunity_del_val(struct lcommunity *lcom, uint8_t *ptr) |
494 | 0 | { |
495 | 0 | int i = 0; |
496 | 0 | int c = 0; |
497 | |
|
498 | 0 | if (!lcom->val) |
499 | 0 | return; |
500 | | |
501 | 0 | while (i < lcom->size) { |
502 | 0 | if (memcmp(lcom->val + i * LCOMMUNITY_SIZE, ptr, |
503 | 0 | LCOMMUNITY_SIZE) |
504 | 0 | == 0) { |
505 | 0 | c = lcom->size - i - 1; |
506 | |
|
507 | 0 | if (c > 0) |
508 | 0 | memmove(lcom->val + i * LCOMMUNITY_SIZE, |
509 | 0 | lcom->val + (i + 1) * LCOMMUNITY_SIZE, |
510 | 0 | c * LCOMMUNITY_SIZE); |
511 | |
|
512 | 0 | lcom->size--; |
513 | |
|
514 | 0 | if (lcom->size > 0) |
515 | 0 | lcom->val = |
516 | 0 | XREALLOC(MTYPE_LCOMMUNITY_VAL, |
517 | 0 | lcom->val, lcom_length(lcom)); |
518 | 0 | else { |
519 | 0 | XFREE(MTYPE_LCOMMUNITY_VAL, lcom->val); |
520 | 0 | } |
521 | 0 | return; |
522 | 0 | } |
523 | 0 | i++; |
524 | 0 | } |
525 | 0 | } |
526 | | |
527 | | static struct lcommunity *bgp_aggr_lcommunity_lookup( |
528 | | struct bgp_aggregate *aggregate, |
529 | | struct lcommunity *lcommunity) |
530 | 0 | { |
531 | 0 | return hash_lookup(aggregate->lcommunity_hash, lcommunity); |
532 | 0 | } |
533 | | |
534 | | static void *bgp_aggr_lcommunty_hash_alloc(void *p) |
535 | 0 | { |
536 | 0 | struct lcommunity *ref = (struct lcommunity *)p; |
537 | 0 | struct lcommunity *lcommunity = NULL; |
538 | |
|
539 | 0 | lcommunity = lcommunity_dup(ref); |
540 | 0 | return lcommunity; |
541 | 0 | } |
542 | | |
543 | | static void bgp_aggr_lcommunity_prepare(struct hash_bucket *hb, void *arg) |
544 | 0 | { |
545 | 0 | struct lcommunity *hb_lcommunity = hb->data; |
546 | 0 | struct lcommunity **aggr_lcommunity = arg; |
547 | |
|
548 | 0 | if (*aggr_lcommunity) |
549 | 0 | *aggr_lcommunity = lcommunity_merge(*aggr_lcommunity, |
550 | 0 | hb_lcommunity); |
551 | 0 | else |
552 | 0 | *aggr_lcommunity = lcommunity_dup(hb_lcommunity); |
553 | 0 | } |
554 | | |
555 | | void bgp_aggr_lcommunity_remove(void *arg) |
556 | 0 | { |
557 | 0 | struct lcommunity *lcommunity = arg; |
558 | |
|
559 | 0 | lcommunity_free(&lcommunity); |
560 | 0 | } |
561 | | |
562 | | void bgp_compute_aggregate_lcommunity(struct bgp_aggregate *aggregate, |
563 | | struct lcommunity *lcommunity) |
564 | 0 | { |
565 | |
|
566 | 0 | bgp_compute_aggregate_lcommunity_hash(aggregate, lcommunity); |
567 | 0 | bgp_compute_aggregate_lcommunity_val(aggregate); |
568 | 0 | } |
569 | | |
570 | | void bgp_compute_aggregate_lcommunity_hash(struct bgp_aggregate *aggregate, |
571 | | struct lcommunity *lcommunity) |
572 | 0 | { |
573 | |
|
574 | 0 | struct lcommunity *aggr_lcommunity = NULL; |
575 | |
|
576 | 0 | if ((aggregate == NULL) || (lcommunity == NULL)) |
577 | 0 | return; |
578 | | |
579 | | /* Create hash if not already created. |
580 | | */ |
581 | 0 | if (aggregate->lcommunity_hash == NULL) |
582 | 0 | aggregate->lcommunity_hash = hash_create( |
583 | 0 | lcommunity_hash_make, lcommunity_cmp, |
584 | 0 | "BGP Aggregator lcommunity hash"); |
585 | |
|
586 | 0 | aggr_lcommunity = bgp_aggr_lcommunity_lookup(aggregate, lcommunity); |
587 | 0 | if (aggr_lcommunity == NULL) { |
588 | | /* Insert lcommunity into hash. |
589 | | */ |
590 | 0 | aggr_lcommunity = hash_get(aggregate->lcommunity_hash, |
591 | 0 | lcommunity, |
592 | 0 | bgp_aggr_lcommunty_hash_alloc); |
593 | 0 | } |
594 | | |
595 | | /* Increment reference counter. |
596 | | */ |
597 | 0 | aggr_lcommunity->refcnt++; |
598 | 0 | } |
599 | | |
600 | | void bgp_compute_aggregate_lcommunity_val(struct bgp_aggregate *aggregate) |
601 | 0 | { |
602 | 0 | struct lcommunity *lcommerge = NULL; |
603 | |
|
604 | 0 | if (aggregate == NULL) |
605 | 0 | return; |
606 | | |
607 | | /* Re-compute aggregate's lcommunity. |
608 | | */ |
609 | 0 | if (aggregate->lcommunity) |
610 | 0 | lcommunity_free(&aggregate->lcommunity); |
611 | 0 | if (aggregate->lcommunity_hash && |
612 | 0 | aggregate->lcommunity_hash->count) { |
613 | 0 | hash_iterate(aggregate->lcommunity_hash, |
614 | 0 | bgp_aggr_lcommunity_prepare, |
615 | 0 | &aggregate->lcommunity); |
616 | 0 | lcommerge = aggregate->lcommunity; |
617 | 0 | aggregate->lcommunity = lcommunity_uniq_sort(lcommerge); |
618 | 0 | if (lcommerge) |
619 | 0 | lcommunity_free(&lcommerge); |
620 | 0 | } |
621 | 0 | } |
622 | | |
623 | | void bgp_remove_lcommunity_from_aggregate(struct bgp_aggregate *aggregate, |
624 | | struct lcommunity *lcommunity) |
625 | 0 | { |
626 | 0 | struct lcommunity *aggr_lcommunity = NULL; |
627 | 0 | struct lcommunity *ret_lcomm = NULL; |
628 | |
|
629 | 0 | if ((!aggregate) |
630 | 0 | || (!aggregate->lcommunity_hash) |
631 | 0 | || (!lcommunity)) |
632 | 0 | return; |
633 | | |
634 | | /* Look-up the lcommunity in the hash. |
635 | | */ |
636 | 0 | aggr_lcommunity = bgp_aggr_lcommunity_lookup(aggregate, lcommunity); |
637 | 0 | if (aggr_lcommunity) { |
638 | 0 | aggr_lcommunity->refcnt--; |
639 | |
|
640 | 0 | if (aggr_lcommunity->refcnt == 0) { |
641 | 0 | ret_lcomm = hash_release(aggregate->lcommunity_hash, |
642 | 0 | aggr_lcommunity); |
643 | 0 | lcommunity_free(&ret_lcomm); |
644 | |
|
645 | 0 | bgp_compute_aggregate_lcommunity_val(aggregate); |
646 | |
|
647 | 0 | } |
648 | 0 | } |
649 | 0 | } |
650 | | |
651 | | void bgp_remove_lcomm_from_aggregate_hash(struct bgp_aggregate *aggregate, |
652 | | struct lcommunity *lcommunity) |
653 | 0 | { |
654 | 0 | struct lcommunity *aggr_lcommunity = NULL; |
655 | 0 | struct lcommunity *ret_lcomm = NULL; |
656 | |
|
657 | 0 | if ((!aggregate) |
658 | 0 | || (!aggregate->lcommunity_hash) |
659 | 0 | || (!lcommunity)) |
660 | 0 | return; |
661 | | |
662 | | /* Look-up the lcommunity in the hash. |
663 | | */ |
664 | 0 | aggr_lcommunity = bgp_aggr_lcommunity_lookup(aggregate, lcommunity); |
665 | 0 | if (aggr_lcommunity) { |
666 | 0 | aggr_lcommunity->refcnt--; |
667 | |
|
668 | 0 | if (aggr_lcommunity->refcnt == 0) { |
669 | 0 | ret_lcomm = hash_release(aggregate->lcommunity_hash, |
670 | 0 | aggr_lcommunity); |
671 | 0 | lcommunity_free(&ret_lcomm); |
672 | 0 | } |
673 | 0 | } |
674 | 0 | } |