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