]>
Commit | Line | Data |
---|---|---|
718e3744 | 1 | /* BGP Extended Communities Attribute |
896014f4 DL |
2 | * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org> |
3 | * | |
4 | * This file is part of GNU Zebra. | |
5 | * | |
6 | * GNU Zebra is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation; either version 2, or (at your option) any | |
9 | * later version. | |
10 | * | |
11 | * GNU Zebra is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License along | |
17 | * with this program; see the file COPYING; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
718e3744 | 20 | |
21 | #include <zebra.h> | |
22 | ||
23 | #include "hash.h" | |
24 | #include "memory.h" | |
25 | #include "prefix.h" | |
26 | #include "command.h" | |
3f9c7369 | 27 | #include "queue.h" |
039f3a34 | 28 | #include "filter.h" |
3f65c5b1 | 29 | #include "jhash.h" |
937652c6 | 30 | #include "stream.h" |
718e3744 | 31 | |
23d0a753 DA |
32 | #include "lib/printfrr.h" |
33 | ||
718e3744 | 34 | #include "bgpd/bgpd.h" |
35 | #include "bgpd/bgp_ecommunity.h" | |
57d187bc | 36 | #include "bgpd/bgp_lcommunity.h" |
0b2aa3a0 | 37 | #include "bgpd/bgp_aspath.h" |
a8d72b61 | 38 | #include "bgpd/bgp_flowspec_private.h" |
dacf6ec1 | 39 | #include "bgpd/bgp_pbr.h" |
a8d72b61 PG |
40 | |
41 | /* struct used to dump the rate contained in FS set traffic-rate EC */ | |
42 | union traffic_rate { | |
43 | float rate_float; | |
44 | uint8_t rate_byte[4]; | |
45 | }; | |
718e3744 | 46 | |
47 | /* Hash of community attribute. */ | |
ffa4e2c4 | 48 | static struct hash *ecomhash; |
6b0655a2 | 49 | |
718e3744 | 50 | /* Allocate a new ecommunities. */ |
d62a17ae | 51 | struct ecommunity *ecommunity_new(void) |
718e3744 | 52 | { |
9a659715 PG |
53 | struct ecommunity *ecom; |
54 | ||
55 | ecom = (struct ecommunity *)XCALLOC(MTYPE_ECOMMUNITY, | |
56 | sizeof(struct ecommunity)); | |
57 | ecom->unit_size = ECOMMUNITY_SIZE; | |
58 | return ecom; | |
718e3744 | 59 | } |
60 | ||
c7ee6c35 DS |
61 | void ecommunity_strfree(char **s) |
62 | { | |
63 | XFREE(MTYPE_ECOMMUNITY_STR, *s); | |
64 | } | |
65 | ||
718e3744 | 66 | /* Allocate ecommunities. */ |
d62a17ae | 67 | void ecommunity_free(struct ecommunity **ecom) |
718e3744 | 68 | { |
2c15754e JC |
69 | if (!(*ecom)) |
70 | return; | |
71 | ||
0a22ddfb QY |
72 | XFREE(MTYPE_ECOMMUNITY_VAL, (*ecom)->val); |
73 | XFREE(MTYPE_ECOMMUNITY_STR, (*ecom)->str); | |
d62a17ae | 74 | XFREE(MTYPE_ECOMMUNITY, *ecom); |
718e3744 | 75 | } |
76 | ||
d62a17ae | 77 | static void ecommunity_hash_free(struct ecommunity *ecom) |
7bae2fb9 | 78 | { |
d62a17ae | 79 | ecommunity_free(&ecom); |
7bae2fb9 LB |
80 | } |
81 | ||
82 | ||
718e3744 | 83 | /* Add a new Extended Communities value to Extended Communities |
84 | Attribute structure. When the value is already exists in the | |
85 | structure, we don't add the value. Newly added value is sorted by | |
86 | numerical order. When the value is added to the structure return 1 | |
1207a5bc | 87 | else return 0. |
88 | The additional parameters 'unique' and 'overwrite' ensure a particular | |
89 | extended community (based on type and sub-type) is present only | |
90 | once and whether the new value should replace what is existing or | |
91 | not. | |
92 | */ | |
4371bf91 PG |
93 | static bool ecommunity_add_val_internal(struct ecommunity *ecom, |
94 | const void *eval, | |
95 | bool unique, bool overwrite, | |
96 | uint8_t ecom_size) | |
718e3744 | 97 | { |
1207a5bc | 98 | int c, ins_idx; |
9a659715 | 99 | const struct ecommunity_val *eval4 = (struct ecommunity_val *)eval; |
4371bf91 PG |
100 | const struct ecommunity_val_ipv6 *eval6 = |
101 | (struct ecommunity_val_ipv6 *)eval; | |
d62a17ae | 102 | |
91085f97 | 103 | /* When this is fist value, just add it. */ |
d62a17ae | 104 | if (ecom->val == NULL) { |
91085f97 | 105 | ecom->size = 1; |
9a659715 PG |
106 | ecom->val = XMALLOC(MTYPE_ECOMMUNITY_VAL, |
107 | ecom_length_size(ecom, ecom_size)); | |
108 | memcpy(ecom->val, eval, ecom_size); | |
3dc339cd | 109 | return true; |
d62a17ae | 110 | } |
111 | ||
112 | /* If the value already exists in the structure return 0. */ | |
1207a5bc | 113 | /* check also if the extended community itself exists. */ |
d62a17ae | 114 | c = 0; |
9a659715 | 115 | |
1207a5bc | 116 | ins_idx = -1; |
91085f97 | 117 | for (uint8_t *p = ecom->val; c < ecom->size; |
9a659715 | 118 | p += ecom_size, c++) { |
1207a5bc | 119 | if (unique) { |
9a659715 PG |
120 | if (ecom_size == ECOMMUNITY_SIZE) { |
121 | if (p[0] == eval4->val[0] && | |
122 | p[1] == eval4->val[1]) { | |
123 | if (overwrite) { | |
4371bf91 PG |
124 | memcpy(p, eval4->val, |
125 | ecom_size); | |
9a659715 PG |
126 | return true; |
127 | } | |
128 | return false; | |
129 | } | |
130 | } else { | |
131 | if (p[0] == eval6->val[0] && | |
132 | p[1] == eval6->val[1]) { | |
133 | if (overwrite) { | |
4371bf91 PG |
134 | memcpy(p, eval6->val, |
135 | ecom_size); | |
9a659715 PG |
136 | return true; |
137 | } | |
138 | return false; | |
1207a5bc | 139 | } |
1207a5bc | 140 | } |
141 | } | |
9a659715 | 142 | int ret = memcmp(p, eval, ecom_size); |
d62a17ae | 143 | if (ret == 0) |
e2369003 | 144 | return false; |
1207a5bc | 145 | if (ret > 0) { |
146 | if (!unique) | |
147 | break; | |
148 | if (ins_idx == -1) | |
149 | ins_idx = c; | |
150 | } | |
d62a17ae | 151 | } |
152 | ||
1207a5bc | 153 | if (ins_idx == -1) |
154 | ins_idx = c; | |
155 | ||
d62a17ae | 156 | /* Add the value to the structure with numerical sorting. */ |
157 | ecom->size++; | |
91085f97 | 158 | ecom->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom->val, |
9a659715 PG |
159 | ecom_length_size(ecom, ecom_size)); |
160 | ||
d62a17ae | 161 | |
9a659715 PG |
162 | memmove(ecom->val + ((ins_idx + 1) * ecom_size), |
163 | ecom->val + (ins_idx * ecom_size), | |
164 | (ecom->size - 1 - ins_idx) * ecom_size); | |
165 | memcpy(ecom->val + (ins_idx * ecom_size), | |
166 | eval, ecom_size); | |
d62a17ae | 167 | |
3dc339cd | 168 | return true; |
718e3744 | 169 | } |
170 | ||
9a659715 | 171 | /* Add a new Extended Communities value to Extended Communities |
4371bf91 PG |
172 | * Attribute structure. When the value is already exists in the |
173 | * structure, we don't add the value. Newly added value is sorted by | |
174 | * numerical order. When the value is added to the structure return 1 | |
175 | * else return 0. | |
176 | */ | |
9a659715 PG |
177 | bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval, |
178 | bool unique, bool overwrite) | |
179 | { | |
180 | return ecommunity_add_val_internal(ecom, (const void *)eval, unique, | |
181 | overwrite, ECOMMUNITY_SIZE); | |
182 | } | |
183 | ||
4371bf91 PG |
184 | bool ecommunity_add_val_ipv6(struct ecommunity *ecom, |
185 | struct ecommunity_val_ipv6 *eval, | |
186 | bool unique, bool overwrite) | |
9a659715 PG |
187 | { |
188 | return ecommunity_add_val_internal(ecom, (const void *)eval, unique, | |
189 | overwrite, IPV6_ECOMMUNITY_SIZE); | |
190 | } | |
191 | ||
192 | static struct ecommunity * | |
193 | ecommunity_uniq_sort_internal(struct ecommunity *ecom, | |
194 | unsigned short ecom_size) | |
718e3744 | 195 | { |
d62a17ae | 196 | int i; |
197 | struct ecommunity *new; | |
9a659715 | 198 | const void *eval; |
d62a17ae | 199 | |
200 | if (!ecom) | |
201 | return NULL; | |
202 | ||
203 | new = ecommunity_new(); | |
9a659715 | 204 | new->unit_size = ecom_size; |
d62a17ae | 205 | |
206 | for (i = 0; i < ecom->size; i++) { | |
8f242187 | 207 | eval = (void *)(ecom->val + (i * ecom_size)); |
9a659715 | 208 | ecommunity_add_val_internal(new, eval, false, false, ecom_size); |
d62a17ae | 209 | } |
210 | return new; | |
718e3744 | 211 | } |
212 | ||
9a659715 | 213 | /* This function takes pointer to Extended Communites strucutre then |
4371bf91 PG |
214 | * create a new Extended Communities structure by uniq and sort each |
215 | * Extended Communities value. | |
216 | */ | |
9a659715 PG |
217 | struct ecommunity *ecommunity_uniq_sort(struct ecommunity *ecom) |
218 | { | |
219 | return ecommunity_uniq_sort_internal(ecom, ECOMMUNITY_SIZE); | |
220 | } | |
221 | ||
718e3744 | 222 | /* Parse Extended Communites Attribute in BGP packet. */ |
9a659715 PG |
223 | static struct ecommunity *ecommunity_parse_internal(uint8_t *pnt, |
224 | unsigned short length, | |
225 | unsigned short size_ecom) | |
718e3744 | 226 | { |
d62a17ae | 227 | struct ecommunity tmp; |
228 | struct ecommunity *new; | |
718e3744 | 229 | |
d62a17ae | 230 | /* Length check. */ |
9a659715 | 231 | if (length % size_ecom) |
d62a17ae | 232 | return NULL; |
718e3744 | 233 | |
d62a17ae | 234 | /* Prepare tmporary structure for making a new Extended Communities |
235 | Attribute. */ | |
9a659715 | 236 | tmp.size = length / size_ecom; |
d62a17ae | 237 | tmp.val = pnt; |
718e3744 | 238 | |
d62a17ae | 239 | /* Create a new Extended Communities Attribute by uniq and sort each |
240 | Extended Communities value */ | |
9a659715 | 241 | new = ecommunity_uniq_sort_internal(&tmp, size_ecom); |
718e3744 | 242 | |
d62a17ae | 243 | return ecommunity_intern(new); |
718e3744 | 244 | } |
245 | ||
9a659715 PG |
246 | struct ecommunity *ecommunity_parse(uint8_t *pnt, |
247 | unsigned short length) | |
248 | { | |
249 | return ecommunity_parse_internal(pnt, length, ECOMMUNITY_SIZE); | |
250 | } | |
251 | ||
252 | struct ecommunity *ecommunity_parse_ipv6(uint8_t *pnt, | |
253 | unsigned short length) | |
254 | { | |
255 | return ecommunity_parse_internal(pnt, length, | |
256 | IPV6_ECOMMUNITY_SIZE); | |
257 | } | |
258 | ||
718e3744 | 259 | /* Duplicate the Extended Communities Attribute structure. */ |
d62a17ae | 260 | struct ecommunity *ecommunity_dup(struct ecommunity *ecom) |
718e3744 | 261 | { |
d62a17ae | 262 | struct ecommunity *new; |
263 | ||
264 | new = XCALLOC(MTYPE_ECOMMUNITY, sizeof(struct ecommunity)); | |
265 | new->size = ecom->size; | |
9a659715 | 266 | new->unit_size = ecom->unit_size; |
d62a17ae | 267 | if (new->size) { |
268 | new->val = XMALLOC(MTYPE_ECOMMUNITY_VAL, | |
9a659715 | 269 | ecom->size * ecom->unit_size); |
8da920d3 DS |
270 | memcpy(new->val, ecom->val, |
271 | (size_t)ecom->size * (size_t)ecom->unit_size); | |
d62a17ae | 272 | } else |
273 | new->val = NULL; | |
274 | return new; | |
718e3744 | 275 | } |
276 | ||
4372df71 | 277 | /* Retrun string representation of communities attribute. */ |
d62a17ae | 278 | char *ecommunity_str(struct ecommunity *ecom) |
4372df71 | 279 | { |
d62a17ae | 280 | if (!ecom->str) |
281 | ecom->str = | |
282 | ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_DISPLAY, 0); | |
283 | return ecom->str; | |
4372df71 | 284 | } |
285 | ||
718e3744 | 286 | /* Merge two Extended Communities Attribute structure. */ |
d62a17ae | 287 | struct ecommunity *ecommunity_merge(struct ecommunity *ecom1, |
288 | struct ecommunity *ecom2) | |
718e3744 | 289 | { |
d62a17ae | 290 | if (ecom1->val) |
8da920d3 DS |
291 | ecom1->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom1->val, |
292 | (size_t)(ecom1->size + ecom2->size) | |
293 | * (size_t)ecom1->unit_size); | |
d62a17ae | 294 | else |
8da920d3 DS |
295 | ecom1->val = XMALLOC(MTYPE_ECOMMUNITY_VAL, |
296 | (size_t)(ecom1->size + ecom2->size) | |
297 | * (size_t)ecom1->unit_size); | |
d62a17ae | 298 | |
9a659715 | 299 | memcpy(ecom1->val + (ecom1->size * ecom1->unit_size), ecom2->val, |
8da920d3 | 300 | (size_t)ecom2->size * (size_t)ecom1->unit_size); |
d62a17ae | 301 | ecom1->size += ecom2->size; |
302 | ||
303 | return ecom1; | |
718e3744 | 304 | } |
305 | ||
306 | /* Intern Extended Communities Attribute. */ | |
d62a17ae | 307 | struct ecommunity *ecommunity_intern(struct ecommunity *ecom) |
718e3744 | 308 | { |
d62a17ae | 309 | struct ecommunity *find; |
718e3744 | 310 | |
d62a17ae | 311 | assert(ecom->refcnt == 0); |
d62a17ae | 312 | find = (struct ecommunity *)hash_get(ecomhash, ecom, hash_alloc_intern); |
d62a17ae | 313 | if (find != ecom) |
314 | ecommunity_free(&ecom); | |
718e3744 | 315 | |
d62a17ae | 316 | find->refcnt++; |
718e3744 | 317 | |
d62a17ae | 318 | if (!find->str) |
319 | find->str = | |
320 | ecommunity_ecom2str(find, ECOMMUNITY_FORMAT_DISPLAY, 0); | |
718e3744 | 321 | |
d62a17ae | 322 | return find; |
718e3744 | 323 | } |
324 | ||
325 | /* Unintern Extended Communities Attribute. */ | |
d62a17ae | 326 | void ecommunity_unintern(struct ecommunity **ecom) |
718e3744 | 327 | { |
d62a17ae | 328 | struct ecommunity *ret; |
329 | ||
df7d4670 DS |
330 | if (!*ecom) |
331 | return; | |
332 | ||
d62a17ae | 333 | if ((*ecom)->refcnt) |
334 | (*ecom)->refcnt--; | |
335 | ||
336 | /* Pull off from hash. */ | |
337 | if ((*ecom)->refcnt == 0) { | |
338 | /* Extended community must be in the hash. */ | |
339 | ret = (struct ecommunity *)hash_release(ecomhash, *ecom); | |
340 | assert(ret != NULL); | |
341 | ||
342 | ecommunity_free(ecom); | |
343 | } | |
718e3744 | 344 | } |
345 | ||
346 | /* Utinity function to make hash key. */ | |
d8b87afe | 347 | unsigned int ecommunity_hash_make(const void *arg) |
718e3744 | 348 | { |
d62a17ae | 349 | const struct ecommunity *ecom = arg; |
9a659715 | 350 | int size = ecom->size * ecom->unit_size; |
d62a17ae | 351 | |
3f65c5b1 | 352 | return jhash(ecom->val, size, 0x564321ab); |
718e3744 | 353 | } |
354 | ||
355 | /* Compare two Extended Communities Attribute structure. */ | |
74df8d6d | 356 | bool ecommunity_cmp(const void *arg1, const void *arg2) |
718e3744 | 357 | { |
d62a17ae | 358 | const struct ecommunity *ecom1 = arg1; |
359 | const struct ecommunity *ecom2 = arg2; | |
360 | ||
361 | if (ecom1 == NULL && ecom2 == NULL) | |
74df8d6d | 362 | return true; |
813d4307 | 363 | |
d62a17ae | 364 | if (ecom1 == NULL || ecom2 == NULL) |
74df8d6d | 365 | return false; |
813d4307 | 366 | |
9a659715 PG |
367 | if (ecom1->unit_size != ecom2->unit_size) |
368 | return false; | |
369 | ||
d62a17ae | 370 | return (ecom1->size == ecom2->size |
9a659715 PG |
371 | && memcmp(ecom1->val, ecom2->val, ecom1->size * |
372 | ecom1->unit_size) == 0); | |
718e3744 | 373 | } |
374 | ||
375 | /* Initialize Extended Comminities related hash. */ | |
d62a17ae | 376 | void ecommunity_init(void) |
718e3744 | 377 | { |
996c9314 | 378 | ecomhash = hash_create(ecommunity_hash_make, ecommunity_cmp, |
3f65c5b1 | 379 | "BGP ecommunity hash"); |
718e3744 | 380 | } |
228da428 | 381 | |
d62a17ae | 382 | void ecommunity_finish(void) |
228da428 | 383 | { |
d62a17ae | 384 | hash_clean(ecomhash, (void (*)(void *))ecommunity_hash_free); |
385 | hash_free(ecomhash); | |
386 | ecomhash = NULL; | |
228da428 | 387 | } |
6b0655a2 | 388 | |
718e3744 | 389 | /* Extended Communities token enum. */ |
d62a17ae | 390 | enum ecommunity_token { |
391 | ecommunity_token_unknown = 0, | |
392 | ecommunity_token_rt, | |
393 | ecommunity_token_soo, | |
394 | ecommunity_token_val, | |
9a659715 PG |
395 | ecommunity_token_rt6, |
396 | ecommunity_token_val6, | |
718e3744 | 397 | }; |
398 | ||
9a659715 PG |
399 | static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type, |
400 | int trans, as_t as, | |
401 | struct in_addr *ip, | |
402 | struct in6_addr *ip6, | |
403 | uint32_t val, | |
404 | void *eval_ptr) | |
c5900768 | 405 | { |
9a659715 PG |
406 | struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr; |
407 | struct ecommunity_val_ipv6 *eval6 = | |
408 | (struct ecommunity_val_ipv6 *)eval_ptr; | |
409 | ||
d62a17ae | 410 | assert(eval); |
411 | if (type == ECOMMUNITY_ENCODE_AS) { | |
412 | if (as > BGP_AS_MAX) | |
413 | return -1; | |
414 | } else if (type == ECOMMUNITY_ENCODE_IP | |
415 | || type == ECOMMUNITY_ENCODE_AS4) { | |
416 | if (val > UINT16_MAX) | |
417 | return -1; | |
9a659715 PG |
418 | } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP && |
419 | sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6 && | |
420 | (!ip6 || val > UINT16_MAX)) { | |
421 | return -1; | |
d62a17ae | 422 | } |
423 | ||
424 | /* Fill in the values. */ | |
425 | eval->val[0] = type; | |
426 | if (!trans) | |
427 | eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE; | |
428 | eval->val[1] = sub_type; | |
429 | if (type == ECOMMUNITY_ENCODE_AS) { | |
430 | eval->val[2] = (as >> 8) & 0xff; | |
431 | eval->val[3] = as & 0xff; | |
432 | eval->val[4] = (val >> 24) & 0xff; | |
433 | eval->val[5] = (val >> 16) & 0xff; | |
434 | eval->val[6] = (val >> 8) & 0xff; | |
435 | eval->val[7] = val & 0xff; | |
436 | } else if (type == ECOMMUNITY_ENCODE_IP) { | |
9a659715 | 437 | memcpy(&eval->val[2], ip, sizeof(struct in_addr)); |
d62a17ae | 438 | eval->val[6] = (val >> 8) & 0xff; |
439 | eval->val[7] = val & 0xff; | |
9a659715 PG |
440 | } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP && |
441 | sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) { | |
442 | memcpy(&eval6->val[2], ip6, sizeof(struct in6_addr)); | |
443 | eval6->val[18] = (val >> 8) & 0xff; | |
444 | eval6->val[19] = val & 0xff; | |
d62a17ae | 445 | } else { |
446 | eval->val[2] = (as >> 24) & 0xff; | |
447 | eval->val[3] = (as >> 16) & 0xff; | |
448 | eval->val[4] = (as >> 8) & 0xff; | |
449 | eval->val[5] = as & 0xff; | |
450 | eval->val[6] = (val >> 8) & 0xff; | |
451 | eval->val[7] = val & 0xff; | |
452 | } | |
453 | ||
454 | return 0; | |
c5900768 | 455 | } |
456 | ||
9a659715 PG |
457 | /* |
458 | * Encode BGP extended community from passed values. Supports types | |
459 | * defined in RFC 4360 and well-known sub-types. | |
460 | */ | |
461 | static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as, | |
462 | struct in_addr ip, uint32_t val, | |
463 | struct ecommunity_val *eval) | |
464 | { | |
465 | return ecommunity_encode_internal(type, sub_type, trans, as, | |
466 | &ip, NULL, val, (void *)eval); | |
467 | } | |
468 | ||
718e3744 | 469 | /* Get next Extended Communities token from the string. */ |
d62a17ae | 470 | static const char *ecommunity_gettoken(const char *str, |
9a659715 | 471 | void *eval_ptr, |
d62a17ae | 472 | enum ecommunity_token *token) |
718e3744 | 473 | { |
d62a17ae | 474 | int ret; |
475 | int dot = 0; | |
476 | int digit = 0; | |
477 | int separator = 0; | |
478 | const char *p = str; | |
479 | char *endptr; | |
480 | struct in_addr ip; | |
9a659715 | 481 | struct in6_addr ip6; |
d62a17ae | 482 | as_t as = 0; |
d7c0a89a QY |
483 | uint32_t val = 0; |
484 | uint8_t ecomm_type; | |
d62a17ae | 485 | char buf[INET_ADDRSTRLEN + 1]; |
9a659715 | 486 | struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr; |
d62a17ae | 487 | /* Skip white space. */ |
fefa5e0f | 488 | while (isspace((unsigned char)*p)) { |
d62a17ae | 489 | p++; |
490 | str++; | |
718e3744 | 491 | } |
d62a17ae | 492 | |
493 | /* Check the end of the line. */ | |
494 | if (*p == '\0') | |
495 | return NULL; | |
496 | ||
497 | /* "rt" and "soo" keyword parse. */ | |
fefa5e0f | 498 | if (!isdigit((unsigned char)*p)) { |
d62a17ae | 499 | /* "rt" match check. */ |
fefa5e0f | 500 | if (tolower((unsigned char)*p) == 'r') { |
d62a17ae | 501 | p++; |
fefa5e0f | 502 | if (tolower((unsigned char)*p) == 't') { |
d62a17ae | 503 | p++; |
9a659715 PG |
504 | if (*p != '\0' && tolower((int)*p) == '6') |
505 | *token = ecommunity_token_rt6; | |
506 | else | |
507 | *token = ecommunity_token_rt; | |
d62a17ae | 508 | return p; |
509 | } | |
fefa5e0f | 510 | if (isspace((unsigned char)*p) || *p == '\0') { |
d62a17ae | 511 | *token = ecommunity_token_rt; |
512 | return p; | |
513 | } | |
514 | goto error; | |
718e3744 | 515 | } |
d62a17ae | 516 | /* "soo" match check. */ |
fefa5e0f | 517 | else if (tolower((unsigned char)*p) == 's') { |
d62a17ae | 518 | p++; |
fefa5e0f | 519 | if (tolower((unsigned char)*p) == 'o') { |
d62a17ae | 520 | p++; |
fefa5e0f | 521 | if (tolower((unsigned char)*p) == 'o') { |
d62a17ae | 522 | p++; |
523 | *token = ecommunity_token_soo; | |
524 | return p; | |
525 | } | |
fefa5e0f | 526 | if (isspace((unsigned char)*p) || *p == '\0') { |
d62a17ae | 527 | *token = ecommunity_token_soo; |
528 | return p; | |
529 | } | |
530 | goto error; | |
531 | } | |
fefa5e0f | 532 | if (isspace((unsigned char)*p) || *p == '\0') { |
d62a17ae | 533 | *token = ecommunity_token_soo; |
534 | return p; | |
535 | } | |
536 | goto error; | |
718e3744 | 537 | } |
d62a17ae | 538 | goto error; |
718e3744 | 539 | } |
d62a17ae | 540 | |
541 | /* What a mess, there are several possibilities: | |
542 | * | |
543 | * a) A.B.C.D:MN | |
544 | * b) EF:OPQR | |
545 | * c) GHJK:MN | |
9a659715 | 546 | * d) <IPV6>:MN (only with rt6) |
d62a17ae | 547 | * |
548 | * A.B.C.D: Four Byte IP | |
549 | * EF: Two byte ASN | |
550 | * GHJK: Four-byte ASN | |
551 | * MN: Two byte value | |
552 | * OPQR: Four byte value | |
553 | * | |
554 | */ | |
9a659715 PG |
555 | /* IPv6 case : look for last ':' */ |
556 | if (*token == ecommunity_token_rt6 || | |
557 | *token == ecommunity_token_val6) { | |
558 | char *limit; | |
559 | ||
560 | limit = endptr = strrchr(p, ':'); | |
561 | if (!endptr) | |
562 | goto error; | |
8f242187 | 563 | |
9a659715 PG |
564 | endptr++; |
565 | as = strtoul(endptr, &endptr, 10); | |
566 | if (*endptr != '\0' || as == BGP_AS4_MAX) | |
567 | goto error; | |
8f242187 | 568 | |
9a659715 PG |
569 | memcpy(buf, p, (limit - p)); |
570 | buf[limit - p] = '\0'; | |
571 | ret = inet_pton(AF_INET6, buf, &ip6); | |
572 | if (ret == 0) | |
573 | goto error; | |
8f242187 | 574 | |
9a659715 PG |
575 | ecomm_type = ECOMMUNITY_ENCODE_TRANS_EXP; |
576 | if (ecommunity_encode_internal(ecomm_type, | |
8f242187 PG |
577 | ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6, |
578 | 1, 0, NULL, &ip6, as, eval_ptr)) | |
9a659715 | 579 | goto error; |
8f242187 | 580 | |
9a659715 PG |
581 | *token = ecommunity_token_val6; |
582 | while (isdigit((int)*p) || *p == ':' || *p == '.') { | |
583 | p++; | |
584 | } | |
585 | return p; | |
586 | } | |
fefa5e0f | 587 | while (isdigit((unsigned char)*p) || *p == ':' || *p == '.') { |
d62a17ae | 588 | if (*p == ':') { |
589 | if (separator) | |
590 | goto error; | |
591 | ||
592 | separator = 1; | |
593 | digit = 0; | |
594 | ||
595 | if ((p - str) > INET_ADDRSTRLEN) | |
596 | goto error; | |
597 | memset(buf, 0, INET_ADDRSTRLEN + 1); | |
598 | memcpy(buf, str, p - str); | |
599 | ||
600 | if (dot) { | |
601 | /* Parsing A.B.C.D in: | |
602 | * A.B.C.D:MN | |
603 | */ | |
604 | ret = inet_aton(buf, &ip); | |
605 | if (ret == 0) | |
606 | goto error; | |
607 | } else { | |
608 | /* ASN */ | |
609 | as = strtoul(buf, &endptr, 10); | |
610 | if (*endptr != '\0' || as == BGP_AS4_MAX) | |
611 | goto error; | |
612 | } | |
613 | } else if (*p == '.') { | |
614 | if (separator) | |
615 | goto error; | |
616 | dot++; | |
617 | if (dot > 4) | |
618 | goto error; | |
619 | } else { | |
620 | digit = 1; | |
621 | ||
622 | /* We're past the IP/ASN part */ | |
623 | if (separator) { | |
624 | val *= 10; | |
625 | val += (*p - '0'); | |
626 | } | |
627 | } | |
628 | p++; | |
718e3744 | 629 | } |
d62a17ae | 630 | |
631 | /* Low digit part must be there. */ | |
632 | if (!digit || !separator) | |
633 | goto error; | |
634 | ||
635 | /* Encode result into extended community. */ | |
636 | if (dot) | |
637 | ecomm_type = ECOMMUNITY_ENCODE_IP; | |
638 | else if (as > BGP_AS_MAX) | |
639 | ecomm_type = ECOMMUNITY_ENCODE_AS4; | |
640 | else | |
641 | ecomm_type = ECOMMUNITY_ENCODE_AS; | |
642 | if (ecommunity_encode(ecomm_type, 0, 1, as, ip, val, eval)) | |
643 | goto error; | |
644 | *token = ecommunity_token_val; | |
645 | return p; | |
646 | ||
647 | error: | |
648 | *token = ecommunity_token_unknown; | |
649 | return p; | |
718e3744 | 650 | } |
651 | ||
9a659715 PG |
652 | static struct ecommunity *ecommunity_str2com_internal(const char *str, int type, |
653 | int keyword_included, | |
654 | bool is_ipv6_extcomm) | |
718e3744 | 655 | { |
d62a17ae | 656 | struct ecommunity *ecom = NULL; |
657 | enum ecommunity_token token = ecommunity_token_unknown; | |
9a659715 | 658 | struct ecommunity_val_ipv6 eval; |
d62a17ae | 659 | int keyword = 0; |
660 | ||
9a659715 PG |
661 | if (is_ipv6_extcomm) |
662 | token = ecommunity_token_rt6; | |
663 | while ((str = ecommunity_gettoken(str, (void *)&eval, &token))) { | |
d62a17ae | 664 | switch (token) { |
665 | case ecommunity_token_rt: | |
666 | case ecommunity_token_soo: | |
667 | if (!keyword_included || keyword) { | |
668 | if (ecom) | |
669 | ecommunity_free(&ecom); | |
670 | return NULL; | |
671 | } | |
672 | keyword = 1; | |
673 | ||
9a659715 PG |
674 | if (token == ecommunity_token_rt || |
675 | token == ecommunity_token_rt6) { | |
d62a17ae | 676 | type = ECOMMUNITY_ROUTE_TARGET; |
677 | } | |
678 | if (token == ecommunity_token_soo) { | |
679 | type = ECOMMUNITY_SITE_ORIGIN; | |
680 | } | |
681 | break; | |
682 | case ecommunity_token_val: | |
683 | if (keyword_included) { | |
684 | if (!keyword) { | |
8f242187 | 685 | ecommunity_free(&ecom); |
d62a17ae | 686 | return NULL; |
687 | } | |
688 | keyword = 0; | |
689 | } | |
690 | if (ecom == NULL) | |
691 | ecom = ecommunity_new(); | |
692 | eval.val[1] = type; | |
4371bf91 PG |
693 | ecommunity_add_val_internal(ecom, (void *)&eval, |
694 | false, false, | |
9a659715 PG |
695 | ecom->unit_size); |
696 | break; | |
697 | case ecommunity_token_val6: | |
698 | if (keyword_included) { | |
699 | if (!keyword) { | |
8f242187 | 700 | ecommunity_free(&ecom); |
9a659715 PG |
701 | return NULL; |
702 | } | |
703 | keyword = 0; | |
704 | } | |
705 | if (ecom == NULL) | |
706 | ecom = ecommunity_new(); | |
707 | ecom->unit_size = IPV6_ECOMMUNITY_SIZE; | |
708 | eval.val[1] = type; | |
709 | ecommunity_add_val_internal(ecom, (void *)&eval, false, false, | |
710 | ecom->unit_size); | |
d62a17ae | 711 | break; |
712 | case ecommunity_token_unknown: | |
713 | default: | |
714 | if (ecom) | |
715 | ecommunity_free(&ecom); | |
716 | return NULL; | |
718e3744 | 717 | } |
718e3744 | 718 | } |
d62a17ae | 719 | return ecom; |
718e3744 | 720 | } |
721 | ||
9a659715 | 722 | /* Convert string to extended community attribute. |
4371bf91 PG |
723 | * |
724 | * When type is already known, please specify both str and type. str | |
725 | * should not include keyword such as "rt" and "soo". Type is | |
726 | * ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN. | |
727 | * keyword_included should be zero. | |
728 | * | |
729 | * For example route-map's "set extcommunity" command case: | |
730 | * | |
731 | * "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3" | |
732 | * type = ECOMMUNITY_ROUTE_TARGET | |
733 | * keyword_included = 0 | |
734 | * | |
735 | * "soo 100:1" -> str = "100:1" | |
736 | * type = ECOMMUNITY_SITE_ORIGIN | |
737 | * keyword_included = 0 | |
738 | * | |
739 | * When string includes keyword for each extended community value. | |
740 | * Please specify keyword_included as non-zero value. | |
741 | * | |
742 | * For example standard extcommunity-list case: | |
743 | * | |
744 | * "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1" | |
745 | * type = 0 | |
746 | * keyword_include = 1 | |
747 | */ | |
9a659715 PG |
748 | struct ecommunity *ecommunity_str2com(const char *str, int type, |
749 | int keyword_included) | |
750 | { | |
751 | return ecommunity_str2com_internal(str, type, | |
752 | keyword_included, false); | |
753 | } | |
754 | ||
755 | struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type, | |
756 | int keyword_included) | |
757 | { | |
758 | return ecommunity_str2com_internal(str, type, | |
759 | keyword_included, true); | |
760 | } | |
761 | ||
762 | static int ecommunity_rt_soo_str_internal(char *buf, size_t bufsz, | |
763 | const uint8_t *pnt, int type, | |
764 | int sub_type, int format, | |
765 | unsigned short ecom_size) | |
c5900768 | 766 | { |
d62a17ae | 767 | int len = 0; |
768 | const char *prefix; | |
9a659715 | 769 | char buf_local[INET6_ADDRSTRLEN]; |
d62a17ae | 770 | |
771 | /* For parse Extended Community attribute tupple. */ | |
5a0ccebf QY |
772 | struct ecommunity_as eas; |
773 | struct ecommunity_ip eip; | |
9a659715 | 774 | struct ecommunity_ip6 eip6; |
d62a17ae | 775 | |
776 | /* Determine prefix for string, if any. */ | |
777 | switch (format) { | |
778 | case ECOMMUNITY_FORMAT_COMMUNITY_LIST: | |
779 | prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo "); | |
780 | break; | |
781 | case ECOMMUNITY_FORMAT_DISPLAY: | |
782 | prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:"); | |
783 | break; | |
784 | case ECOMMUNITY_FORMAT_ROUTE_MAP: | |
785 | prefix = ""; | |
786 | break; | |
787 | default: | |
788 | prefix = ""; | |
789 | break; | |
790 | } | |
791 | ||
792 | /* Put string into buffer. */ | |
793 | if (type == ECOMMUNITY_ENCODE_AS4) { | |
937652c6 | 794 | pnt = ptr_get_be32(pnt, &eas.as); |
d62a17ae | 795 | eas.val = (*pnt++ << 8); |
796 | eas.val |= (*pnt++); | |
797 | ||
91085f97 | 798 | len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val); |
d62a17ae | 799 | } else if (type == ECOMMUNITY_ENCODE_AS) { |
9a659715 PG |
800 | if (ecom_size == ECOMMUNITY_SIZE) { |
801 | eas.as = (*pnt++ << 8); | |
802 | eas.as |= (*pnt++); | |
803 | pnt = ptr_get_be32(pnt, &eas.val); | |
d62a17ae | 804 | |
4371bf91 PG |
805 | len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, |
806 | eas.val); | |
9a659715 PG |
807 | } else { |
808 | /* this is an IPv6 ext community | |
c6423c31 | 809 | * first 16 bytes stands for IPv6 addres |
9a659715 PG |
810 | */ |
811 | memcpy(&eip6.ip, pnt, 16); | |
812 | pnt += 16; | |
813 | eip6.val = (*pnt++ << 8); | |
814 | eip6.val |= (*pnt++); | |
815 | ||
4371bf91 PG |
816 | inet_ntop(AF_INET6, &eip6.ip, buf_local, |
817 | sizeof(buf_local)); | |
9a659715 PG |
818 | len = snprintf(buf, bufsz, "%s%s:%u", prefix, |
819 | buf_local, eip6.val); | |
820 | } | |
d62a17ae | 821 | } else if (type == ECOMMUNITY_ENCODE_IP) { |
822 | memcpy(&eip.ip, pnt, 4); | |
823 | pnt += 4; | |
824 | eip.val = (*pnt++ << 8); | |
825 | eip.val |= (*pnt++); | |
826 | ||
23d0a753 DA |
827 | len = snprintfrr(buf, bufsz, "%s%pI4:%u", prefix, &eip.ip, |
828 | eip.val); | |
d62a17ae | 829 | } |
91085f97 QY |
830 | |
831 | /* consume value */ | |
832 | (void)pnt; | |
d62a17ae | 833 | |
834 | return len; | |
c5900768 | 835 | } |
836 | ||
9a659715 PG |
837 | static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt, |
838 | int type, int sub_type, int format) | |
839 | { | |
840 | return ecommunity_rt_soo_str_internal(buf, bufsz, pnt, type, | |
841 | sub_type, format, | |
842 | ECOMMUNITY_SIZE); | |
843 | } | |
844 | ||
7e3ebfd1 | 845 | static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt) |
846 | { | |
847 | int len = 0; | |
848 | as_t as; | |
849 | uint32_t bw; | |
850 | char bps_buf[20] = {0}; | |
851 | ||
852 | #define ONE_GBPS_BYTES (1000 * 1000 * 1000 / 8) | |
853 | #define ONE_MBPS_BYTES (1000 * 1000 / 8) | |
854 | #define ONE_KBPS_BYTES (1000 / 8) | |
855 | ||
856 | as = (*pnt++ << 8); | |
857 | as |= (*pnt++); | |
83ac8d12 | 858 | (void)ptr_get_be32(pnt, &bw); |
7e3ebfd1 | 859 | if (bw >= ONE_GBPS_BYTES) |
772270f3 QY |
860 | snprintf(bps_buf, sizeof(bps_buf), "%.3f Gbps", |
861 | (float)(bw / ONE_GBPS_BYTES)); | |
7e3ebfd1 | 862 | else if (bw >= ONE_MBPS_BYTES) |
772270f3 QY |
863 | snprintf(bps_buf, sizeof(bps_buf), "%.3f Mbps", |
864 | (float)(bw / ONE_MBPS_BYTES)); | |
7e3ebfd1 | 865 | else if (bw >= ONE_KBPS_BYTES) |
772270f3 QY |
866 | snprintf(bps_buf, sizeof(bps_buf), "%.3f Kbps", |
867 | (float)(bw / ONE_KBPS_BYTES)); | |
7e3ebfd1 | 868 | else |
772270f3 | 869 | snprintf(bps_buf, sizeof(bps_buf), "%u bps", bw * 8); |
7e3ebfd1 | 870 | |
871 | len = snprintf(buf, bufsz, "LB:%u:%u (%s)", as, bw, bps_buf); | |
872 | return len; | |
873 | } | |
874 | ||
d62a17ae | 875 | /* Convert extended community attribute to string. |
718e3744 | 876 | |
877 | Due to historical reason of industry standard implementation, there | |
878 | are three types of format. | |
879 | ||
880 | route-map set extcommunity format | |
3efd0893 | 881 | "rt 100:1 100:2soo 100:3" |
718e3744 | 882 | |
883 | extcommunity-list | |
3efd0893 | 884 | "rt 100:1 rt 100:2 soo 100:3show [ip] bgp" and extcommunity-list regular expression matching |
d62a17ae | 885 | "RT:100:1 RT:100:2 SoO:100:3" |
718e3744 | 886 | |
887 | For each formath please use below definition for format: | |
888 | ||
889 | ECOMMUNITY_FORMAT_ROUTE_MAP | |
890 | ECOMMUNITY_FORMAT_COMMUNITY_LIST | |
891 | ECOMMUNITY_FORMAT_DISPLAY | |
e82202b7 | 892 | |
d62a17ae | 893 | Filter is added to display only ECOMMUNITY_ROUTE_TARGET in some cases. |
e82202b7 | 894 | 0 value displays all |
718e3744 | 895 | */ |
d62a17ae | 896 | char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter) |
718e3744 | 897 | { |
d62a17ae | 898 | int i; |
d7c0a89a | 899 | uint8_t *pnt; |
1ef3c51f PG |
900 | uint8_t type = 0; |
901 | uint8_t sub_type = 0; | |
91085f97 | 902 | #define ECOMMUNITY_STRLEN 64 |
d62a17ae | 903 | int str_size; |
d62a17ae | 904 | char *str_buf; |
d62a17ae | 905 | |
6a37bfb7 | 906 | if (!ecom || ecom->size == 0) |
91085f97 QY |
907 | return XCALLOC(MTYPE_ECOMMUNITY_STR, 1); |
908 | ||
909 | /* ecom strlen + space + null term */ | |
910 | str_size = (ecom->size * (ECOMMUNITY_STRLEN + 1)) + 1; | |
911 | str_buf = XCALLOC(MTYPE_ECOMMUNITY_STR, str_size); | |
d62a17ae | 912 | |
91085f97 | 913 | char encbuf[128]; |
d62a17ae | 914 | |
915 | for (i = 0; i < ecom->size; i++) { | |
916 | int unk_ecom = 0; | |
91085f97 | 917 | memset(encbuf, 0x00, sizeof(encbuf)); |
d62a17ae | 918 | |
919 | /* Space between each value. */ | |
91085f97 QY |
920 | if (i > 0) |
921 | strlcat(str_buf, " ", str_size); | |
d62a17ae | 922 | |
91085f97 | 923 | /* Retrieve value field */ |
d62a17ae | 924 | pnt = ecom->val + (i * 8); |
925 | ||
91085f97 | 926 | /* High-order octet is the type */ |
d62a17ae | 927 | type = *pnt++; |
928 | ||
929 | if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_IP | |
930 | || type == ECOMMUNITY_ENCODE_AS4) { | |
931 | /* Low-order octet of type. */ | |
932 | sub_type = *pnt++; | |
933 | if (sub_type != ECOMMUNITY_ROUTE_TARGET | |
2551b26e PG |
934 | && sub_type != ECOMMUNITY_SITE_ORIGIN) { |
935 | if (sub_type == | |
936 | ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4 && | |
937 | type == ECOMMUNITY_ENCODE_IP) { | |
938 | struct in_addr *ipv4 = | |
939 | (struct in_addr *)pnt; | |
940 | char ipv4str[INET_ADDRSTRLEN]; | |
941 | ||
942 | inet_ntop(AF_INET, ipv4, | |
943 | ipv4str, | |
944 | INET_ADDRSTRLEN); | |
91085f97 QY |
945 | snprintf(encbuf, sizeof(encbuf), |
946 | "NH:%s:%d", ipv4str, pnt[5]); | |
7e3ebfd1 | 947 | } else if (sub_type == |
948 | ECOMMUNITY_LINK_BANDWIDTH && | |
949 | type == ECOMMUNITY_ENCODE_AS) { | |
950 | ecommunity_lb_str(encbuf, | |
951 | sizeof(encbuf), pnt); | |
2551b26e PG |
952 | } else |
953 | unk_ecom = 1; | |
91085f97 QY |
954 | } else { |
955 | ecommunity_rt_soo_str(encbuf, sizeof(encbuf), | |
956 | pnt, type, sub_type, | |
957 | format); | |
958 | } | |
d62a17ae | 959 | } else if (type == ECOMMUNITY_ENCODE_OPAQUE) { |
960 | if (filter == ECOMMUNITY_ROUTE_TARGET) | |
961 | continue; | |
962 | if (*pnt == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP) { | |
963 | uint16_t tunneltype; | |
964 | memcpy(&tunneltype, pnt + 5, 2); | |
965 | tunneltype = ntohs(tunneltype); | |
91085f97 QY |
966 | |
967 | snprintf(encbuf, sizeof(encbuf), "ET:%d", | |
968 | tunneltype); | |
996c9314 | 969 | } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW) { |
91085f97 QY |
970 | strlcpy(encbuf, "Default Gateway", |
971 | sizeof(encbuf)); | |
972 | } else { | |
d62a17ae | 973 | unk_ecom = 1; |
91085f97 | 974 | } |
d62a17ae | 975 | } else if (type == ECOMMUNITY_ENCODE_EVPN) { |
976 | if (filter == ECOMMUNITY_ROUTE_TARGET) | |
977 | continue; | |
bc59a672 MK |
978 | if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC) { |
979 | struct ethaddr rmac; | |
d62a17ae | 980 | pnt++; |
bc59a672 | 981 | memcpy(&rmac, pnt, ETH_ALEN); |
91085f97 QY |
982 | |
983 | snprintf(encbuf, sizeof(encbuf), | |
984 | "Rmac:%02x:%02x:%02x:%02x:%02x:%02x", | |
985 | (uint8_t)rmac.octet[0], | |
986 | (uint8_t)rmac.octet[1], | |
987 | (uint8_t)rmac.octet[2], | |
988 | (uint8_t)rmac.octet[3], | |
989 | (uint8_t)rmac.octet[4], | |
990 | (uint8_t)rmac.octet[5]); | |
d62a17ae | 991 | } else if (*pnt |
992 | == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) { | |
d7c0a89a QY |
993 | uint32_t seqnum; |
994 | uint8_t flags = *++pnt; | |
d62a17ae | 995 | |
996 | memcpy(&seqnum, pnt + 2, 4); | |
997 | seqnum = ntohl(seqnum); | |
91085f97 QY |
998 | |
999 | snprintf(encbuf, sizeof(encbuf), "MM:%u", | |
1000 | seqnum); | |
1001 | ||
1002 | if (CHECK_FLAG( | |
1003 | flags, | |
1004 | ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY)) | |
1005 | strlcat(encbuf, ", sticky MAC", | |
1006 | sizeof(encbuf)); | |
5cc359b2 CS |
1007 | } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ND) { |
1008 | uint8_t flags = *++pnt; | |
1009 | ||
91085f97 QY |
1010 | if (CHECK_FLAG( |
1011 | flags, | |
1012 | ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG)) | |
1013 | strlcpy(encbuf, "ND:Router Flag", | |
1014 | sizeof(encbuf)); | |
7904e9fd AK |
1015 | if (CHECK_FLAG( |
1016 | flags, | |
1017 | ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG)) | |
1018 | strlcpy(encbuf, "ND:Proxy", | |
1019 | sizeof(encbuf)); | |
4248407b AK |
1020 | } else if (*pnt |
1021 | == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT) { | |
1022 | struct ethaddr mac; | |
1023 | ||
1024 | pnt++; | |
1025 | memcpy(&mac, pnt, ETH_ALEN); | |
1026 | snprintf(encbuf, | |
1027 | sizeof(encbuf), | |
1028 | "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x", | |
1029 | (uint8_t)mac.octet[0], | |
1030 | (uint8_t)mac.octet[1], | |
1031 | (uint8_t)mac.octet[2], | |
1032 | (uint8_t)mac.octet[3], | |
1033 | (uint8_t)mac.octet[4], | |
1034 | (uint8_t)mac.octet[5]); | |
1035 | } else if (*pnt | |
1036 | == ECOMMUNITY_EVPN_SUBTYPE_ESI_LABEL) { | |
1037 | uint8_t flags = *++pnt; | |
1038 | ||
1039 | snprintf(encbuf, | |
1040 | sizeof(encbuf), "ESI-label-Rt:%s", | |
1041 | (flags & | |
1042 | ECOMMUNITY_EVPN_SUBTYPE_ESI_SA_FLAG) ? | |
1043 | "SA":"AA"); | |
74e2bd89 AK |
1044 | } else if (*pnt |
1045 | == ECOMMUNITY_EVPN_SUBTYPE_DF_ELECTION) { | |
1046 | uint8_t alg; | |
1047 | uint16_t pref; | |
1048 | uint16_t bmap; | |
1049 | ||
1050 | alg = *(pnt + 1); | |
1051 | memcpy(&bmap, pnt + 2, 2); | |
1052 | bmap = ntohs(bmap); | |
1053 | memcpy(&pref, pnt + 5, 2); | |
1054 | pref = ntohs(pref); | |
1055 | ||
1056 | if (bmap) | |
1057 | snprintf( | |
1058 | encbuf, sizeof(encbuf), | |
1059 | "DF: (alg: %u, bmap: 0x%x pref: %u)", | |
1060 | alg, bmap, pref); | |
1061 | else | |
1062 | snprintf(encbuf, sizeof(encbuf), | |
1063 | "DF: (alg: %u, pref: %u)", alg, | |
1064 | pref); | |
d62a17ae | 1065 | } else |
1066 | unk_ecom = 1; | |
b72220fc PG |
1067 | } else if (type == ECOMMUNITY_ENCODE_REDIRECT_IP_NH) { |
1068 | sub_type = *pnt++; | |
1069 | if (sub_type == ECOMMUNITY_REDIRECT_IP_NH) { | |
91085f97 QY |
1070 | snprintf(encbuf, sizeof(encbuf), |
1071 | "FS:redirect IP 0x%x", *(pnt + 5)); | |
b72220fc PG |
1072 | } else |
1073 | unk_ecom = 1; | |
1ef3c51f PG |
1074 | } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP || |
1075 | type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 || | |
1076 | type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_3) { | |
a8d72b61 | 1077 | sub_type = *pnt++; |
9a659715 PG |
1078 | |
1079 | if (sub_type == ECOMMUNITY_ROUTE_TARGET) { | |
8f242187 | 1080 | char buf[ECOMMUNITY_STRLEN]; |
9a659715 PG |
1081 | |
1082 | memset(buf, 0, sizeof(buf)); | |
1083 | ecommunity_rt_soo_str_internal(buf, sizeof(buf), | |
1084 | (const uint8_t *)pnt, | |
1085 | type & | |
1086 | ~ECOMMUNITY_ENCODE_TRANS_EXP, | |
1087 | ECOMMUNITY_ROUTE_TARGET, | |
1088 | format, | |
1089 | ecom->unit_size); | |
1090 | snprintf(encbuf, sizeof(encbuf), "%s", buf); | |
4371bf91 PG |
1091 | } else if (sub_type == |
1092 | ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) { | |
9a659715 PG |
1093 | char buf[64]; |
1094 | ||
1095 | memset(buf, 0, sizeof(buf)); | |
1096 | ecommunity_rt_soo_str_internal(buf, sizeof(buf), | |
1097 | (const uint8_t *)pnt, | |
1098 | type & | |
1099 | ~ECOMMUNITY_ENCODE_TRANS_EXP, | |
1100 | ECOMMUNITY_ROUTE_TARGET, | |
1101 | ECOMMUNITY_FORMAT_DISPLAY, | |
1102 | ecom->unit_size); | |
4371bf91 PG |
1103 | snprintf(encbuf, sizeof(encbuf), |
1104 | "FS:redirect VRF %s", buf); | |
9a659715 PG |
1105 | } else if (sub_type == ECOMMUNITY_REDIRECT_VRF) { |
1106 | char buf[16]; | |
1107 | ||
1108 | memset(buf, 0, sizeof(buf)); | |
4371bf91 PG |
1109 | ecommunity_rt_soo_str(buf, sizeof(buf), |
1110 | (const uint8_t *)pnt, | |
1111 | type & | |
1112 | ~ECOMMUNITY_ENCODE_TRANS_EXP, | |
1113 | ECOMMUNITY_ROUTE_TARGET, | |
1114 | ECOMMUNITY_FORMAT_DISPLAY); | |
1115 | snprintf(encbuf, sizeof(encbuf), | |
1116 | "FS:redirect VRF %s", buf); | |
91085f97 QY |
1117 | snprintf(encbuf, sizeof(encbuf), |
1118 | "FS:redirect VRF %s", buf); | |
1ef3c51f PG |
1119 | } else if (type != ECOMMUNITY_ENCODE_TRANS_EXP) |
1120 | unk_ecom = 1; | |
1121 | else if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) { | |
a8d72b61 | 1122 | char action[64]; |
a8d72b61 PG |
1123 | |
1124 | if (*(pnt+3) == | |
1125 | 1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL) | |
91085f97 QY |
1126 | strlcpy(action, "terminate (apply)", |
1127 | sizeof(action)); | |
a8d72b61 | 1128 | else |
91085f97 QY |
1129 | strlcpy(action, "eval stops", |
1130 | sizeof(action)); | |
1131 | ||
a8d72b61 PG |
1132 | if (*(pnt+3) == |
1133 | 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE) | |
91085f97 QY |
1134 | strlcat(action, ", sample", |
1135 | sizeof(action)); | |
1136 | ||
1137 | ||
1138 | snprintf(encbuf, sizeof(encbuf), "FS:action %s", | |
1139 | action); | |
a8d72b61 PG |
1140 | } else if (sub_type == ECOMMUNITY_TRAFFIC_RATE) { |
1141 | union traffic_rate data; | |
1142 | ||
1143 | data.rate_byte[3] = *(pnt+2); | |
1144 | data.rate_byte[2] = *(pnt+3); | |
1145 | data.rate_byte[1] = *(pnt+4); | |
1146 | data.rate_byte[0] = *(pnt+5); | |
91085f97 QY |
1147 | snprintf(encbuf, sizeof(encbuf), "FS:rate %f", |
1148 | data.rate_float); | |
a8d72b61 | 1149 | } else if (sub_type == ECOMMUNITY_TRAFFIC_MARKING) { |
91085f97 QY |
1150 | snprintf(encbuf, sizeof(encbuf), |
1151 | "FS:marking %u", *(pnt + 5)); | |
a8d72b61 PG |
1152 | } else |
1153 | unk_ecom = 1; | |
7e3ebfd1 | 1154 | } else if (type == ECOMMUNITY_ENCODE_AS_NON_TRANS) { |
1155 | sub_type = *pnt++; | |
1156 | if (sub_type == ECOMMUNITY_LINK_BANDWIDTH) | |
1157 | ecommunity_lb_str(encbuf, sizeof(encbuf), pnt); | |
1158 | else | |
1159 | unk_ecom = 1; | |
1ef3c51f PG |
1160 | } else { |
1161 | sub_type = *pnt++; | |
d62a17ae | 1162 | unk_ecom = 1; |
1ef3c51f | 1163 | } |
d62a17ae | 1164 | |
1165 | if (unk_ecom) | |
91085f97 QY |
1166 | snprintf(encbuf, sizeof(encbuf), "UNK:%d, %d", type, |
1167 | sub_type); | |
d62a17ae | 1168 | |
91085f97 QY |
1169 | int r = strlcat(str_buf, encbuf, str_size); |
1170 | assert(r < str_size); | |
94431dbc C |
1171 | } |
1172 | ||
d62a17ae | 1173 | return str_buf; |
718e3744 | 1174 | } |
4372df71 | 1175 | |
3dc339cd DA |
1176 | bool ecommunity_match(const struct ecommunity *ecom1, |
1177 | const struct ecommunity *ecom2) | |
4372df71 | 1178 | { |
d62a17ae | 1179 | int i = 0; |
1180 | int j = 0; | |
1181 | ||
1182 | if (ecom1 == NULL && ecom2 == NULL) | |
3dc339cd | 1183 | return true; |
d62a17ae | 1184 | |
1185 | if (ecom1 == NULL || ecom2 == NULL) | |
3dc339cd | 1186 | return false; |
d62a17ae | 1187 | |
1188 | if (ecom1->size < ecom2->size) | |
3dc339cd | 1189 | return false; |
d62a17ae | 1190 | |
1191 | /* Every community on com2 needs to be on com1 for this to match */ | |
1192 | while (i < ecom1->size && j < ecom2->size) { | |
9a659715 PG |
1193 | if (memcmp(ecom1->val + i * ecom1->unit_size, |
1194 | ecom2->val + j * ecom2->unit_size, | |
1195 | ecom2->unit_size) | |
d62a17ae | 1196 | == 0) |
1197 | j++; | |
1198 | i++; | |
1199 | } | |
1200 | ||
1201 | if (j == ecom2->size) | |
3dc339cd | 1202 | return true; |
d62a17ae | 1203 | else |
3dc339cd | 1204 | return false; |
4372df71 | 1205 | } |
1e27ef50 PG |
1206 | |
1207 | /* return first occurence of type */ | |
d62a17ae | 1208 | extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom, |
1209 | uint8_t type, uint8_t subtype) | |
1e27ef50 | 1210 | { |
d7c0a89a | 1211 | uint8_t *p; |
d62a17ae | 1212 | int c; |
1213 | ||
1214 | /* If the value already exists in the structure return 0. */ | |
1215 | c = 0; | |
9a659715 | 1216 | for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) { |
d62a17ae | 1217 | if (p == NULL) { |
1218 | continue; | |
1219 | } | |
1220 | if (p[0] == type && p[1] == subtype) | |
1221 | return (struct ecommunity_val *)p; | |
1222 | } | |
1223 | return NULL; | |
1e27ef50 PG |
1224 | } |
1225 | ||
1226 | /* remove ext. community matching type and subtype | |
1227 | * return 1 on success ( removed ), 0 otherwise (not present) | |
1228 | */ | |
1207a5bc | 1229 | bool ecommunity_strip(struct ecommunity *ecom, uint8_t type, |
1230 | uint8_t subtype) | |
1e27ef50 | 1231 | { |
003bc275 | 1232 | uint8_t *p, *q, *new; |
d62a17ae | 1233 | int c, found = 0; |
1234 | /* When this is fist value, just add it. */ | |
3dc339cd DA |
1235 | if (ecom == NULL || ecom->val == NULL) |
1236 | return false; | |
d62a17ae | 1237 | |
003bc275 | 1238 | /* Check if any existing ext community matches. */ |
1239 | /* Certain extended communities like the Route Target can be present | |
1240 | * multiple times, handle that. | |
1241 | */ | |
d62a17ae | 1242 | c = 0; |
9a659715 | 1243 | for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) { |
003bc275 | 1244 | if (p[0] == type && p[1] == subtype) |
1245 | found++; | |
d62a17ae | 1246 | } |
003bc275 | 1247 | /* If no matching ext community exists, return. */ |
d62a17ae | 1248 | if (found == 0) |
3dc339cd | 1249 | return false; |
003bc275 | 1250 | |
1251 | /* Handle the case where everything needs to be stripped. */ | |
1252 | if (found == ecom->size) { | |
1253 | XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val); | |
1254 | ecom->size = 0; | |
3dc339cd | 1255 | return true; |
003bc275 | 1256 | } |
1257 | ||
1258 | /* Strip matching ext community(ies). */ | |
1259 | new = XMALLOC(MTYPE_ECOMMUNITY_VAL, | |
9a659715 | 1260 | (ecom->size - found) * ecom->unit_size); |
003bc275 | 1261 | q = new; |
9a659715 | 1262 | for (c = 0, p = ecom->val; c < ecom->size; c++, p += ecom->unit_size) { |
003bc275 | 1263 | if (!(p[0] == type && p[1] == subtype)) { |
9a659715 PG |
1264 | memcpy(q, p, ecom->unit_size); |
1265 | q += ecom->unit_size; | |
003bc275 | 1266 | } |
1267 | } | |
1268 | XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val); | |
1269 | ecom->val = new; | |
1270 | ecom->size -= found; | |
3dc339cd | 1271 | return true; |
1e27ef50 | 1272 | } |
44338987 | 1273 | |
1274 | /* | |
1275 | * Remove specified extended community value from extended community. | |
1276 | * Returns 1 if value was present (and hence, removed), 0 otherwise. | |
1277 | */ | |
3dc339cd | 1278 | bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval) |
44338987 | 1279 | { |
f27f864e | 1280 | uint8_t *p; |
44338987 | 1281 | int c, found = 0; |
1282 | ||
1283 | /* Make sure specified value exists. */ | |
1284 | if (ecom == NULL || ecom->val == NULL) | |
3dc339cd | 1285 | return false; |
44338987 | 1286 | c = 0; |
9a659715 PG |
1287 | for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) { |
1288 | if (!memcmp(p, eval->val, ecom->unit_size)) { | |
44338987 | 1289 | found = 1; |
1290 | break; | |
1291 | } | |
1292 | } | |
1293 | if (found == 0) | |
3dc339cd | 1294 | return false; |
44338987 | 1295 | |
1296 | /* Delete the selected value */ | |
1297 | ecom->size--; | |
9a659715 | 1298 | p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ecom->unit_size); |
44338987 | 1299 | if (c != 0) |
9a659715 | 1300 | memcpy(p, ecom->val, c * ecom->unit_size); |
44338987 | 1301 | if ((ecom->size - c) != 0) |
9a659715 PG |
1302 | memcpy(p + (c)*ecom->unit_size, |
1303 | ecom->val + (c + 1) * ecom->unit_size, | |
1304 | (ecom->size - c) * ecom->unit_size); | |
e6a6870b | 1305 | XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val); |
44338987 | 1306 | ecom->val = p; |
3dc339cd | 1307 | return true; |
44338987 | 1308 | } |
dacf6ec1 PG |
1309 | |
1310 | int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval, | |
f01e580f PG |
1311 | struct bgp_pbr_entry_action *api, |
1312 | afi_t afi) | |
dacf6ec1 PG |
1313 | { |
1314 | if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_RATE) { | |
1315 | api->action = ACTION_TRAFFICRATE; | |
1316 | api->u.r.rate_info[3] = ecom_eval->val[4]; | |
1317 | api->u.r.rate_info[2] = ecom_eval->val[5]; | |
1318 | api->u.r.rate_info[1] = ecom_eval->val[6]; | |
1319 | api->u.r.rate_info[0] = ecom_eval->val[7]; | |
1320 | } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_ACTION) { | |
1321 | api->action = ACTION_TRAFFIC_ACTION; | |
1322 | /* else distribute code is set by default */ | |
1323 | if (ecom_eval->val[5] & (1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL)) | |
1324 | api->u.za.filter |= TRAFFIC_ACTION_TERMINATE; | |
1325 | else | |
1326 | api->u.za.filter |= TRAFFIC_ACTION_DISTRIBUTE; | |
1327 | if (ecom_eval->val[5] == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE) | |
1328 | api->u.za.filter |= TRAFFIC_ACTION_SAMPLE; | |
1329 | ||
1330 | } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_MARKING) { | |
1331 | api->action = ACTION_MARKING; | |
1332 | api->u.marking_dscp = ecom_eval->val[7]; | |
1333 | } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_VRF) { | |
1334 | /* must use external function */ | |
1335 | return 0; | |
f01e580f PG |
1336 | } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH && |
1337 | afi == AFI_IP) { | |
dacf6ec1 PG |
1338 | /* see draft-ietf-idr-flowspec-redirect-ip-02 |
1339 | * Q1: how come a ext. community can host ipv6 address | |
1340 | * Q2 : from cisco documentation: | |
1341 | * Announces the reachability of one or more flowspec NLRI. | |
1342 | * When a BGP speaker receives an UPDATE message with the | |
1343 | * redirect-to-IP extended community, it is expected to | |
1344 | * create a traffic filtering rule for every flow-spec | |
1345 | * NLRI in the message that has this path as its best | |
1346 | * path. The filter entry matches the IP packets | |
1347 | * described in the NLRI field and redirects them or | |
1348 | * copies them towards the IPv4 or IPv6 address specified | |
1349 | * in the 'Network Address of Next- Hop' | |
1350 | * field of the associated MP_REACH_NLRI. | |
1351 | */ | |
1352 | struct ecommunity_ip *ip_ecom = (struct ecommunity_ip *) | |
1353 | ecom_eval + 2; | |
1354 | ||
1355 | api->u.zr.redirect_ip_v4 = ip_ecom->ip; | |
1356 | } else | |
1357 | return -1; | |
1358 | return 0; | |
1359 | } | |
5b820d9e NT |
1360 | |
1361 | static struct ecommunity *bgp_aggr_ecommunity_lookup( | |
1362 | struct bgp_aggregate *aggregate, | |
1363 | struct ecommunity *ecommunity) | |
1364 | { | |
1365 | return hash_lookup(aggregate->ecommunity_hash, ecommunity); | |
1366 | } | |
1367 | ||
1368 | static void *bgp_aggr_ecommunty_hash_alloc(void *p) | |
1369 | { | |
1370 | struct ecommunity *ref = (struct ecommunity *)p; | |
1371 | struct ecommunity *ecommunity = NULL; | |
1372 | ||
1373 | ecommunity = ecommunity_dup(ref); | |
1374 | return ecommunity; | |
1375 | } | |
1376 | ||
7f5818fb | 1377 | static void bgp_aggr_ecommunity_prepare(struct hash_bucket *hb, void *arg) |
5b820d9e | 1378 | { |
5b820d9e NT |
1379 | struct ecommunity *hb_ecommunity = hb->data; |
1380 | struct ecommunity **aggr_ecommunity = arg; | |
1381 | ||
4edd83f9 | 1382 | if (*aggr_ecommunity) |
1383 | *aggr_ecommunity = ecommunity_merge(*aggr_ecommunity, | |
1384 | hb_ecommunity); | |
1385 | else | |
5b820d9e NT |
1386 | *aggr_ecommunity = ecommunity_dup(hb_ecommunity); |
1387 | } | |
1388 | ||
1389 | void bgp_aggr_ecommunity_remove(void *arg) | |
1390 | { | |
1391 | struct ecommunity *ecommunity = arg; | |
1392 | ||
1393 | ecommunity_free(&ecommunity); | |
1394 | } | |
1395 | ||
1396 | void bgp_compute_aggregate_ecommunity(struct bgp_aggregate *aggregate, | |
1397 | struct ecommunity *ecommunity) | |
4edd83f9 | 1398 | { |
1399 | bgp_compute_aggregate_ecommunity_hash(aggregate, ecommunity); | |
1400 | bgp_compute_aggregate_ecommunity_val(aggregate); | |
1401 | } | |
1402 | ||
1403 | ||
1404 | void bgp_compute_aggregate_ecommunity_hash(struct bgp_aggregate *aggregate, | |
1405 | struct ecommunity *ecommunity) | |
5b820d9e NT |
1406 | { |
1407 | struct ecommunity *aggr_ecommunity = NULL; | |
1408 | ||
1409 | if ((aggregate == NULL) || (ecommunity == NULL)) | |
1410 | return; | |
1411 | ||
1412 | /* Create hash if not already created. | |
1413 | */ | |
1414 | if (aggregate->ecommunity_hash == NULL) | |
1415 | aggregate->ecommunity_hash = hash_create( | |
1416 | ecommunity_hash_make, ecommunity_cmp, | |
1417 | "BGP Aggregator ecommunity hash"); | |
1418 | ||
1419 | aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity); | |
1420 | if (aggr_ecommunity == NULL) { | |
1421 | /* Insert ecommunity into hash. | |
1422 | */ | |
1423 | aggr_ecommunity = hash_get(aggregate->ecommunity_hash, | |
1424 | ecommunity, | |
1425 | bgp_aggr_ecommunty_hash_alloc); | |
4edd83f9 | 1426 | } |
5b820d9e | 1427 | |
4edd83f9 | 1428 | /* Increment reference counter. |
1429 | */ | |
1430 | aggr_ecommunity->refcnt++; | |
1431 | } | |
5b820d9e | 1432 | |
4edd83f9 | 1433 | void bgp_compute_aggregate_ecommunity_val(struct bgp_aggregate *aggregate) |
1434 | { | |
1435 | struct ecommunity *ecommerge = NULL; | |
1436 | ||
1437 | if (aggregate == NULL) | |
1438 | return; | |
1439 | ||
1440 | /* Re-compute aggregate's ecommunity. | |
1441 | */ | |
1442 | if (aggregate->ecommunity) | |
1443 | ecommunity_free(&aggregate->ecommunity); | |
1444 | if (aggregate->ecommunity_hash | |
1445 | && aggregate->ecommunity_hash->count) { | |
5b820d9e NT |
1446 | hash_iterate(aggregate->ecommunity_hash, |
1447 | bgp_aggr_ecommunity_prepare, | |
1448 | &aggregate->ecommunity); | |
4edd83f9 | 1449 | ecommerge = aggregate->ecommunity; |
1450 | aggregate->ecommunity = ecommunity_uniq_sort(ecommerge); | |
1451 | if (ecommerge) | |
1452 | ecommunity_free(&ecommerge); | |
5b820d9e | 1453 | } |
5b820d9e NT |
1454 | } |
1455 | ||
1456 | void bgp_remove_ecommunity_from_aggregate(struct bgp_aggregate *aggregate, | |
1457 | struct ecommunity *ecommunity) | |
1458 | { | |
1459 | struct ecommunity *aggr_ecommunity = NULL; | |
1460 | struct ecommunity *ret_ecomm = NULL; | |
1461 | ||
4edd83f9 | 1462 | if ((!aggregate) |
1463 | || (!aggregate->ecommunity_hash) | |
1464 | || (!ecommunity)) | |
5b820d9e NT |
1465 | return; |
1466 | ||
1467 | /* Look-up the ecommunity in the hash. | |
1468 | */ | |
1469 | aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity); | |
1470 | if (aggr_ecommunity) { | |
1471 | aggr_ecommunity->refcnt--; | |
1472 | ||
1473 | if (aggr_ecommunity->refcnt == 0) { | |
1474 | ret_ecomm = hash_release(aggregate->ecommunity_hash, | |
1475 | aggr_ecommunity); | |
1476 | ecommunity_free(&ret_ecomm); | |
4edd83f9 | 1477 | bgp_compute_aggregate_ecommunity_val(aggregate); |
1478 | } | |
1479 | } | |
1480 | } | |
1481 | ||
1482 | void bgp_remove_ecomm_from_aggregate_hash(struct bgp_aggregate *aggregate, | |
1483 | struct ecommunity *ecommunity) | |
1484 | { | |
1485 | ||
1486 | struct ecommunity *aggr_ecommunity = NULL; | |
1487 | struct ecommunity *ret_ecomm = NULL; | |
5b820d9e | 1488 | |
4edd83f9 | 1489 | if ((!aggregate) |
1490 | || (!aggregate->ecommunity_hash) | |
1491 | || (!ecommunity)) | |
1492 | return; | |
5b820d9e | 1493 | |
4edd83f9 | 1494 | /* Look-up the ecommunity in the hash. |
1495 | */ | |
1496 | aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity); | |
1497 | if (aggr_ecommunity) { | |
1498 | aggr_ecommunity->refcnt--; | |
1499 | ||
1500 | if (aggr_ecommunity->refcnt == 0) { | |
1501 | ret_ecomm = hash_release(aggregate->ecommunity_hash, | |
1502 | aggr_ecommunity); | |
1503 | ecommunity_free(&ret_ecomm); | |
5b820d9e NT |
1504 | } |
1505 | } | |
1506 | } | |
d901dc13 | 1507 | |
1508 | /* | |
1509 | * return the BGP link bandwidth extended community, if present; | |
1510 | * the actual bandwidth is returned via param | |
1511 | */ | |
1512 | const uint8_t *ecommunity_linkbw_present(struct ecommunity *ecom, uint32_t *bw) | |
1513 | { | |
1514 | const uint8_t *eval; | |
1515 | int i; | |
1516 | ||
1517 | if (bw) | |
1518 | *bw = 0; | |
1519 | ||
1520 | if (!ecom || !ecom->size) | |
1521 | return NULL; | |
1522 | ||
1523 | for (i = 0; i < ecom->size; i++) { | |
1524 | const uint8_t *pnt; | |
1525 | uint8_t type, sub_type; | |
1526 | uint32_t bwval; | |
1527 | ||
1528 | eval = pnt = (ecom->val + (i * ECOMMUNITY_SIZE)); | |
1529 | type = *pnt++; | |
1530 | sub_type = *pnt++; | |
1531 | ||
1532 | if ((type == ECOMMUNITY_ENCODE_AS || | |
1533 | type == ECOMMUNITY_ENCODE_AS_NON_TRANS) && | |
1534 | sub_type == ECOMMUNITY_LINK_BANDWIDTH) { | |
1535 | pnt += 2; /* bandwidth is encoded as AS:val */ | |
1536 | pnt = ptr_get_be32(pnt, &bwval); | |
1537 | (void)pnt; /* consume value */ | |
1538 | if (bw) | |
1539 | *bw = bwval; | |
1540 | return eval; | |
1541 | } | |
1542 | } | |
1543 | ||
1544 | return NULL; | |
1545 | } | |
7b651a32 | 1546 | |
1547 | ||
1548 | struct ecommunity *ecommunity_replace_linkbw(as_t as, | |
1549 | struct ecommunity *ecom, | |
1550 | uint64_t cum_bw) | |
1551 | { | |
1552 | struct ecommunity *new; | |
1553 | struct ecommunity_val lb_eval; | |
1554 | const uint8_t *eval; | |
1555 | uint8_t type; | |
1556 | uint32_t cur_bw; | |
1557 | ||
1558 | /* Nothing to replace if link-bandwidth doesn't exist or | |
1559 | * is non-transitive - just return existing extcommunity. | |
1560 | */ | |
1561 | new = ecom; | |
1562 | if (!ecom || !ecom->size) | |
1563 | return new; | |
1564 | ||
1565 | eval = ecommunity_linkbw_present(ecom, &cur_bw); | |
1566 | if (!eval) | |
1567 | return new; | |
1568 | ||
1569 | type = *eval; | |
1570 | if (type & ECOMMUNITY_FLAG_NON_TRANSITIVE) | |
1571 | return new; | |
1572 | ||
1573 | /* Transitive link-bandwidth exists, replace with the passed | |
1574 | * (cumulative) bandwidth value. We need to create a new | |
1575 | * extcommunity for this - refer to AS-Path replace function | |
1576 | * for reference. | |
1577 | */ | |
1578 | if (cum_bw > 0xFFFFFFFF) | |
1579 | cum_bw = 0xFFFFFFFF; | |
1580 | encode_lb_extcomm(as > BGP_AS_MAX ? BGP_AS_TRANS : as, cum_bw, | |
1581 | false, &lb_eval); | |
1582 | new = ecommunity_dup(ecom); | |
1583 | ecommunity_add_val(new, &lb_eval, true, true); | |
1584 | ||
1585 | return new; | |
1586 | } |