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