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