1 /* $OpenBSD: rde_community.c,v 1.16 2024/09/10 08:53:20 claudio Exp $ */
2
3 /*
4 * Copyright (c) 2019 Claudio Jeker <claudio@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 #include <sys/queue.h>
19
20 #include <endian.h>
21 #include <limits.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "bgpd.h"
26 #include "rde.h"
27 #include "log.h"
28
29 static int
apply_flag(uint32_t in,uint8_t flag,struct rde_peer * peer,uint32_t * out,uint32_t * mask)30 apply_flag(uint32_t in, uint8_t flag, struct rde_peer *peer, uint32_t *out,
31 uint32_t *mask)
32 {
33 switch (flag) {
34 case COMMUNITY_ANY:
35 if (mask == NULL)
36 return -1;
37 *out = 0;
38 *mask = 0;
39 return 0;
40 case COMMUNITY_NEIGHBOR_AS:
41 if (peer == NULL)
42 return -1;
43 *out = peer->conf.remote_as;
44 break;
45 case COMMUNITY_LOCAL_AS:
46 if (peer == NULL)
47 return -1;
48 *out = peer->conf.local_as;
49 break;
50 default:
51 *out = in;
52 break;
53 }
54 if (mask)
55 *mask = UINT32_MAX;
56 return 0;
57 }
58
59 static int
fc2c(struct community * fc,struct rde_peer * peer,struct community * c,struct community * m)60 fc2c(struct community *fc, struct rde_peer *peer, struct community *c,
61 struct community *m)
62 {
63 int type;
64 uint8_t subtype;
65
66 memset(c, 0, sizeof(*c));
67 if (m)
68 memset(m, 0xff, sizeof(*m));
69
70 c->flags = (uint8_t)fc->flags;
71
72 switch ((uint8_t)c->flags) {
73 case COMMUNITY_TYPE_BASIC:
74 if (apply_flag(fc->data1, fc->flags >> 8, peer,
75 &c->data1, m ? &m->data1 : NULL))
76 return -1;
77 if (apply_flag(fc->data2, fc->flags >> 16, peer,
78 &c->data2, m ? &m->data2 : NULL))
79 return -1;
80
81 /* check that values fit */
82 if (c->data1 > USHRT_MAX || c->data2 > USHRT_MAX)
83 return -1;
84 return 0;
85 case COMMUNITY_TYPE_LARGE:
86 if (apply_flag(fc->data1, fc->flags >> 8, peer,
87 &c->data1, m ? &m->data1 : NULL))
88 return -1;
89 if (apply_flag(fc->data2, fc->flags >> 16, peer,
90 &c->data2, m ? &m->data2 : NULL))
91 return -1;
92 if (apply_flag(fc->data3, fc->flags >> 24, peer,
93 &c->data3, m ? &m->data3 : NULL))
94 return -1;
95 return 0;
96 case COMMUNITY_TYPE_EXT:
97 type = (int32_t)fc->data3 >> 8;
98 subtype = fc->data3 & 0xff;
99
100 if ((fc->flags >> 24 & 0xff) == COMMUNITY_ANY) {
101 /* special case for 'ext-community * *' */
102 if (m == NULL)
103 return -1;
104 m->data1 = 0;
105 m->data2 = 0;
106 m->data3 = 0;
107 return 0;
108 }
109
110 if (type == -1) {
111 /* special case for 'ext-community rt *' */
112 if ((fc->flags >> 8 & 0xff) != COMMUNITY_ANY ||
113 m == NULL)
114 return -1;
115 c->data3 = subtype;
116 m->data1 = 0;
117 m->data2 = 0;
118 m->data3 = 0xff;
119 return 0;
120 }
121
122 c->data3 = type << 8 | subtype;
123 switch (type & EXT_COMMUNITY_VALUE) {
124 case EXT_COMMUNITY_TRANS_TWO_AS:
125 case EXT_COMMUNITY_TRANS_FOUR_AS:
126 if ((fc->flags >> 8 & 0xff) == COMMUNITY_ANY)
127 break;
128
129 if (apply_flag(fc->data1, fc->flags >> 8, peer,
130 &c->data1, m ? &m->data1 : NULL))
131 return -1;
132 if (apply_flag(fc->data2, fc->flags >> 16, peer,
133 &c->data2, m ? &m->data2 : NULL))
134 return -1;
135 if (m)
136 m->data3 &= ~(EXT_COMMUNITY_TRANS_FOUR_AS << 8);
137 return 0;
138 case EXT_COMMUNITY_TRANS_IPV4:
139 if ((fc->flags >> 8 & 0xff) == COMMUNITY_ANY)
140 break;
141
142 if (apply_flag(fc->data1, fc->flags >> 8, peer,
143 &c->data1, m ? &m->data1 : NULL))
144 return -1;
145 if (apply_flag(fc->data2, fc->flags >> 16, peer,
146 &c->data2, m ? &m->data2 : NULL))
147 return -1;
148 /* check that values fit */
149 if (c->data2 > USHRT_MAX)
150 return -1;
151 return 0;
152 case EXT_COMMUNITY_TRANS_OPAQUE:
153 case EXT_COMMUNITY_TRANS_EVPN:
154 if ((fc->flags >> 8 & 0xff) == COMMUNITY_ANY)
155 break;
156
157 c->data1 = fc->data1;
158 c->data2 = fc->data2;
159 return 0;
160 }
161
162 /* this is for 'ext-community subtype *' */
163 if (m == NULL)
164 return -1;
165 m->data1 = 0;
166 m->data2 = 0;
167 return 0;
168 default:
169 fatalx("%s: unknown type %d", __func__, (uint8_t)c->flags);
170 }
171 }
172
173 static int
fast_match(const void * va,const void * vb)174 fast_match(const void *va, const void *vb)
175 {
176 const struct community *a = va;
177 const struct community *b = vb;
178
179 if ((uint8_t)a->flags != (uint8_t)b->flags)
180 return (uint8_t)a->flags > (uint8_t)b->flags ? 1 : -1;
181
182 if (a->data1 != b->data1)
183 return a->data1 > b->data1 ? 1 : -1;
184 if (a->data2 != b->data2)
185 return a->data2 > b->data2 ? 1 : -1;
186 if (a->data3 != b->data3)
187 return a->data3 > b->data3 ? 1 : -1;
188 return 0;
189 }
190
191 static int
mask_match(struct community * a,struct community * b,struct community * m)192 mask_match(struct community *a, struct community *b, struct community *m)
193 {
194 if ((uint8_t)a->flags != (uint8_t)b->flags)
195 return (uint8_t)a->flags > (uint8_t)b->flags ? 1 : -1;
196
197 if ((a->data1 & m->data1) != (b->data1 & m->data1)) {
198 if ((a->data1 & m->data1) > (b->data1 & m->data1))
199 return 1;
200 return -1;
201 }
202 if ((a->data2 & m->data2) != (b->data2 & m->data2)) {
203 if ((a->data2 & m->data2) > (b->data2 & m->data2))
204 return 1;
205 return -1;
206 }
207 if ((a->data3 & m->data3) != (b->data3 & m->data3)) {
208 if ((a->data3 & m->data3) > (b->data3 & m->data3))
209 return 1;
210 return -1;
211 }
212 return 0;
213 }
214
215 /*
216 * Insert a community keeping the list sorted. Don't add if already present.
217 */
218 static void
insert_community(struct rde_community * comm,struct community * c)219 insert_community(struct rde_community *comm, struct community *c)
220 {
221 int l;
222 int r;
223
224 if (comm->nentries + 1 > comm->size) {
225 struct community *new;
226 int newsize = comm->size + 8;
227
228 if ((new = recallocarray(comm->communities, comm->size,
229 newsize, sizeof(struct community))) == NULL)
230 fatal(__func__);
231 comm->communities = new;
232 comm->size = newsize;
233 }
234
235 /* XXX can be made faster by binary search */
236 for (l = 0; l < comm->nentries; l++) {
237 r = fast_match(comm->communities + l, c);
238 if (r == 0) {
239 /* already present, nothing to do */
240 return;
241 } else if (r > 0) {
242 /* shift reminder by one slot */
243 memmove(comm->communities + l + 1,
244 comm->communities + l,
245 (comm->nentries - l) * sizeof(*c));
246 break;
247 }
248 }
249
250 /* insert community at slot l */
251 comm->communities[l] = *c;
252 comm->nentries++;
253 }
254
255 static int
non_transitive_ext_community(struct community * c)256 non_transitive_ext_community(struct community *c)
257 {
258 if ((uint8_t)c->flags != COMMUNITY_TYPE_EXT)
259 return 0;
260 if ((c->data3 >> 8) & EXT_COMMUNITY_NON_TRANSITIVE)
261 return 1;
262 return 0;
263 }
264
265 /*
266 * Check if a community is present. This function will expand local-as and
267 * neighbor-as and also mask of bits to support partial matches.
268 */
269 int
community_match(struct rde_community * comm,struct community * fc,struct rde_peer * peer)270 community_match(struct rde_community *comm, struct community *fc,
271 struct rde_peer *peer)
272 {
273 struct community test, mask;
274 int l;
275
276 if (fc->flags >> 8 == 0) {
277 /* fast path */
278 return (bsearch(fc, comm->communities, comm->nentries,
279 sizeof(*fc), fast_match) != NULL);
280 } else {
281 /* slow path */
282 if (fc2c(fc, peer, &test, &mask) == -1)
283 return 0;
284
285 for (l = 0; l < comm->nentries; l++) {
286 if (mask_match(&comm->communities[l], &test,
287 &mask) == 0)
288 return 1;
289 }
290 return 0;
291 }
292 }
293
294 /*
295 * Count the number of communities of type type.
296 */
297 int
community_count(struct rde_community * comm,uint8_t type)298 community_count(struct rde_community *comm, uint8_t type)
299 {
300 int l;
301 int count = 0;
302
303 /* use the fact that the array is ordered by type */
304 switch (type) {
305 case COMMUNITY_TYPE_BASIC:
306 for (l = 0; l < comm->nentries; l++) {
307 if ((uint8_t)comm->communities[l].flags == type)
308 count++;
309 else
310 break;
311 }
312 break;
313 case COMMUNITY_TYPE_EXT:
314 for (l = 0; l < comm->nentries; l++) {
315 if ((uint8_t)comm->communities[l].flags == type)
316 count++;
317 else if ((uint8_t)comm->communities[l].flags > type)
318 break;
319 }
320 break;
321 case COMMUNITY_TYPE_LARGE:
322 for (l = comm->nentries; l > 0; l--) {
323 if ((uint8_t)comm->communities[l - 1].flags == type)
324 count++;
325 else
326 break;
327 }
328 break;
329 }
330 return count;
331 }
332
333 /*
334 * Insert a community, expanding local-as and neighbor-as if needed.
335 */
336 int
community_set(struct rde_community * comm,struct community * fc,struct rde_peer * peer)337 community_set(struct rde_community *comm, struct community *fc,
338 struct rde_peer *peer)
339 {
340 struct community set;
341
342 if (fc->flags >> 8 == 0) {
343 /* fast path */
344 insert_community(comm, fc);
345 } else {
346 if (fc2c(fc, peer, &set, NULL) == -1)
347 return 0;
348 if ((uint8_t)set.flags == COMMUNITY_TYPE_EXT) {
349 int type = (int)set.data3 >> 8;
350 switch (type & EXT_COMMUNITY_VALUE) {
351 case EXT_COMMUNITY_TRANS_TWO_AS:
352 case EXT_COMMUNITY_TRANS_FOUR_AS:
353 /* check that values fit */
354 if (set.data1 > USHRT_MAX &&
355 set.data2 > USHRT_MAX)
356 return 0;
357 if (set.data1 > USHRT_MAX)
358 set.data3 = (set.data3 & 0xff) |
359 EXT_COMMUNITY_TRANS_FOUR_AS << 8;
360 else
361 set.data3 = (set.data3 & 0xff) |
362 EXT_COMMUNITY_TRANS_TWO_AS << 8;
363 break;
364 }
365 }
366 insert_community(comm, &set);
367 }
368 return 1;
369 }
370
371 /*
372 * Remove a community if present, This function will expand local-as and
373 * neighbor-as and also mask of bits to support partial matches.
374 */
375 void
community_delete(struct rde_community * comm,struct community * fc,struct rde_peer * peer)376 community_delete(struct rde_community *comm, struct community *fc,
377 struct rde_peer *peer)
378 {
379 struct community test, mask;
380 struct community *match;
381 int l = 0;
382
383 if (fc->flags >> 8 == 0) {
384 /* fast path */
385 match = bsearch(fc, comm->communities, comm->nentries,
386 sizeof(*fc), fast_match);
387 if (match == NULL)
388 return;
389 /* move everything after match down by 1 */
390 memmove(match, match + 1,
391 (char *)(comm->communities + comm->nentries) -
392 (char *)(match + 1));
393 comm->nentries--;
394 return;
395 } else {
396 if (fc2c(fc, peer, &test, &mask) == -1)
397 return;
398
399 while (l < comm->nentries) {
400 if (mask_match(&comm->communities[l], &test,
401 &mask) == 0) {
402 memmove(comm->communities + l,
403 comm->communities + l + 1,
404 (comm->nentries - l - 1) * sizeof(test));
405 comm->nentries--;
406 continue;
407 }
408 l++;
409 }
410 }
411 }
412
413 /*
414 * Internalize communities from the wireformat.
415 * Store the partial flag in struct rde_community so it is not lost.
416 * - community_add for ATTR_COMMUNITUES
417 * - community_large_add for ATTR_LARGE_COMMUNITIES
418 * - community_ext_add for ATTR_EXT_COMMUNITIES
419 */
420 int
community_add(struct rde_community * comm,int flags,struct ibuf * buf)421 community_add(struct rde_community *comm, int flags, struct ibuf *buf)
422 {
423 struct community set = { .flags = COMMUNITY_TYPE_BASIC };
424 uint16_t data1, data2;
425
426 if (ibuf_size(buf) == 0 || ibuf_size(buf) % 4 != 0)
427 return -1;
428
429 if (flags & ATTR_PARTIAL)
430 comm->flags |= PARTIAL_COMMUNITIES;
431
432 while (ibuf_size(buf) > 0) {
433 if (ibuf_get_n16(buf, &data1) == -1 ||
434 ibuf_get_n16(buf, &data2) == -1)
435 return -1;
436 set.data1 = data1;
437 set.data2 = data2;
438 insert_community(comm, &set);
439 }
440
441 return 0;
442 }
443
444 int
community_large_add(struct rde_community * comm,int flags,struct ibuf * buf)445 community_large_add(struct rde_community *comm, int flags, struct ibuf *buf)
446 {
447 struct community set = { .flags = COMMUNITY_TYPE_LARGE };
448
449 if (ibuf_size(buf) == 0 || ibuf_size(buf) % 12 != 0)
450 return -1;
451
452 if (flags & ATTR_PARTIAL)
453 comm->flags |= PARTIAL_LARGE_COMMUNITIES;
454
455 while (ibuf_size(buf) > 0) {
456 if (ibuf_get_n32(buf, &set.data1) == -1 ||
457 ibuf_get_n32(buf, &set.data2) == -1 ||
458 ibuf_get_n32(buf, &set.data3) == -1)
459 return -1;
460 insert_community(comm, &set);
461 }
462
463 return 0;
464 }
465
466 int
community_ext_add(struct rde_community * comm,int flags,int ebgp,struct ibuf * buf)467 community_ext_add(struct rde_community *comm, int flags, int ebgp,
468 struct ibuf *buf)
469 {
470 struct community set = { .flags = COMMUNITY_TYPE_EXT };
471 uint64_t c;
472 uint8_t type;
473
474 if (ibuf_size(buf) == 0 || ibuf_size(buf) % 8 != 0)
475 return -1;
476
477 if (flags & ATTR_PARTIAL)
478 comm->flags |= PARTIAL_EXT_COMMUNITIES;
479
480 while (ibuf_size(buf) > 0) {
481 if (ibuf_get_n64(buf, &c) == -1)
482 return (-1);
483
484 type = c >> 56;
485 /* filter out non-transitive ext communuties from ebgp peers */
486 if (ebgp && (type & EXT_COMMUNITY_NON_TRANSITIVE))
487 continue;
488 switch (type & EXT_COMMUNITY_VALUE) {
489 case EXT_COMMUNITY_TRANS_TWO_AS:
490 case EXT_COMMUNITY_TRANS_OPAQUE:
491 case EXT_COMMUNITY_TRANS_EVPN:
492 set.data1 = c >> 32 & 0xffff;
493 set.data2 = c;
494 break;
495 case EXT_COMMUNITY_TRANS_FOUR_AS:
496 case EXT_COMMUNITY_TRANS_IPV4:
497 set.data1 = c >> 16;
498 set.data2 = c & 0xffff;
499 break;
500 }
501 set.data3 = c >> 48;
502
503 insert_community(comm, &set);
504 }
505
506 return 0;
507 }
508
509 /*
510 * Convert communities back to the wireformat.
511 * When writing ATTR_EXT_COMMUNITIES non-transitive communities need to
512 * be skipped if they are sent to an ebgp peer.
513 */
514 int
community_writebuf(struct rde_community * comm,uint8_t type,int ebgp,struct ibuf * buf)515 community_writebuf(struct rde_community *comm, uint8_t type, int ebgp,
516 struct ibuf *buf)
517 {
518 struct community *cp;
519 uint64_t ext;
520 int l, size, start, end, num;
521 int flags = ATTR_OPTIONAL | ATTR_TRANSITIVE;
522 uint8_t t;
523
524 switch (type) {
525 case ATTR_COMMUNITIES:
526 if (comm->flags & PARTIAL_COMMUNITIES)
527 flags |= ATTR_PARTIAL;
528 size = 4;
529 t = COMMUNITY_TYPE_BASIC;
530 break;
531 case ATTR_EXT_COMMUNITIES:
532 if (comm->flags & PARTIAL_EXT_COMMUNITIES)
533 flags |= ATTR_PARTIAL;
534 size = 8;
535 t = COMMUNITY_TYPE_EXT;
536 break;
537 case ATTR_LARGE_COMMUNITIES:
538 if (comm->flags & PARTIAL_LARGE_COMMUNITIES)
539 flags |= ATTR_PARTIAL;
540 size = 12;
541 t = COMMUNITY_TYPE_LARGE;
542 break;
543 default:
544 return -1;
545 }
546
547 /* first count how many communities will be written */
548 num = 0;
549 start = -1;
550 for (l = 0; l < comm->nentries; l++) {
551 cp = &comm->communities[l];
552 if ((uint8_t)cp->flags == t) {
553 if (ebgp && non_transitive_ext_community(cp))
554 continue;
555 num++;
556 if (start == -1)
557 start = l;
558 }
559 if ((uint8_t)cp->flags > t)
560 break;
561 }
562 end = l;
563
564 /* no communities for this type present */
565 if (num == 0)
566 return 0;
567
568 if (num > INT16_MAX / size)
569 return -1;
570
571 /* write attribute header */
572 if (attr_writebuf(buf, flags, type, NULL, num * size) == -1)
573 return -1;
574
575 /* write out the communities */
576 for (l = start; l < end; l++) {
577 cp = &comm->communities[l];
578
579 switch (type) {
580 case ATTR_COMMUNITIES:
581 if (ibuf_add_n16(buf, cp->data1) == -1)
582 return -1;
583 if (ibuf_add_n16(buf, cp->data2) == -1)
584 return -1;
585 break;
586 case ATTR_EXT_COMMUNITIES:
587 if (ebgp && non_transitive_ext_community(cp))
588 continue;
589
590 ext = (uint64_t)cp->data3 << 48;
591 switch ((cp->data3 >> 8) & EXT_COMMUNITY_VALUE) {
592 case EXT_COMMUNITY_TRANS_TWO_AS:
593 case EXT_COMMUNITY_TRANS_OPAQUE:
594 case EXT_COMMUNITY_TRANS_EVPN:
595 ext |= ((uint64_t)cp->data1 & 0xffff) << 32;
596 ext |= (uint64_t)cp->data2;
597 break;
598 case EXT_COMMUNITY_TRANS_FOUR_AS:
599 case EXT_COMMUNITY_TRANS_IPV4:
600 ext |= (uint64_t)cp->data1 << 16;
601 ext |= (uint64_t)cp->data2 & 0xffff;
602 break;
603 }
604 if (ibuf_add_n64(buf, ext) == -1)
605 return -1;
606 break;
607 case ATTR_LARGE_COMMUNITIES:
608 if (ibuf_add_n32(buf, cp->data1) == -1)
609 return -1;
610 if (ibuf_add_n32(buf, cp->data2) == -1)
611 return -1;
612 if (ibuf_add_n32(buf, cp->data3) == -1)
613 return -1;
614 break;
615 }
616 }
617 return 0;
618 }
619
620 /*
621 * Global RIB cache for communities
622 */
623 static inline int
communities_compare(struct rde_community * a,struct rde_community * b)624 communities_compare(struct rde_community *a, struct rde_community *b)
625 {
626 if (a->nentries != b->nentries)
627 return a->nentries > b->nentries ? 1 : -1;
628 if (a->flags != b->flags)
629 return a->flags > b->flags ? 1 : -1;
630
631 return memcmp(a->communities, b->communities,
632 a->nentries * sizeof(struct community));
633 }
634
635 RB_HEAD(comm_tree, rde_community) commtable = RB_INITIALIZER(&commtable);
636 RB_GENERATE_STATIC(comm_tree, rde_community, entry, communities_compare);
637
638 void
communities_shutdown(void)639 communities_shutdown(void)
640 {
641 if (!RB_EMPTY(&commtable))
642 log_warnx("%s: free non-free table", __func__);
643 }
644
645 struct rde_community *
communities_lookup(struct rde_community * comm)646 communities_lookup(struct rde_community *comm)
647 {
648 return RB_FIND(comm_tree, &commtable, comm);
649 }
650
651 struct rde_community *
communities_link(struct rde_community * comm)652 communities_link(struct rde_community *comm)
653 {
654 struct rde_community *n, *f;
655
656 if ((n = malloc(sizeof(*n))) == NULL)
657 fatal(__func__);
658 communities_copy(n, comm);
659
660 if ((f = RB_INSERT(comm_tree, &commtable, n)) != NULL) {
661 log_warnx("duplicate communities collection inserted");
662 free(n->communities);
663 free(n);
664 return f;
665 }
666 n->refcnt = 1; /* initial reference by the cache */
667
668 rdemem.comm_size += n->size;
669 rdemem.comm_nmemb += n->nentries;
670 rdemem.comm_cnt++;
671
672 return n;
673 }
674
675 void
communities_unlink(struct rde_community * comm)676 communities_unlink(struct rde_community *comm)
677 {
678 if (comm->refcnt != 1)
679 fatalx("%s: unlinking still referenced communities", __func__);
680
681 RB_REMOVE(comm_tree, &commtable, comm);
682
683 rdemem.comm_size -= comm->size;
684 rdemem.comm_nmemb -= comm->nentries;
685 rdemem.comm_cnt--;
686
687 free(comm->communities);
688 free(comm);
689 }
690
691 /*
692 * Return true/1 if the two communities collections are identical,
693 * otherwise returns zero.
694 */
695 int
communities_equal(struct rde_community * a,struct rde_community * b)696 communities_equal(struct rde_community *a, struct rde_community *b)
697 {
698 if (a->nentries != b->nentries)
699 return 0;
700 if (a->flags != b->flags)
701 return 0;
702
703 return (memcmp(a->communities, b->communities,
704 a->nentries * sizeof(struct community)) == 0);
705 }
706
707 /*
708 * Copy communities to a new unreferenced struct. Needs to call
709 * communities_clean() when done. to can be statically allocated,
710 * it will be cleaned first.
711 */
712 void
communities_copy(struct rde_community * to,struct rde_community * from)713 communities_copy(struct rde_community *to, struct rde_community *from)
714 {
715 memset(to, 0, sizeof(*to));
716
717 /* ignore from->size and allocate the perfect amount */
718 to->size = from->nentries;
719 to->nentries = from->nentries;
720 to->flags = from->flags;
721
722 if (to->nentries == 0)
723 return;
724
725 if ((to->communities = reallocarray(NULL, to->size,
726 sizeof(struct community))) == NULL)
727 fatal(__func__);
728
729 memcpy(to->communities, from->communities,
730 to->nentries * sizeof(struct community));
731 }
732
733 /*
734 * Clean up the communities by freeing any dynamically allocated memory.
735 */
736 void
communities_clean(struct rde_community * comm)737 communities_clean(struct rde_community *comm)
738 {
739 if (comm->refcnt != 0)
740 fatalx("%s: cleaning still referenced communities", __func__);
741
742 free(comm->communities);
743 memset(comm, 0, sizeof(*comm));
744 }
745
746 int
community_to_rd(struct community * fc,uint64_t * community)747 community_to_rd(struct community *fc, uint64_t *community)
748 {
749 struct community c;
750 uint64_t rd;
751
752 if (fc2c(fc, NULL, &c, NULL) == -1)
753 return -1;
754
755 switch ((c.data3 >> 8) & EXT_COMMUNITY_VALUE) {
756 case EXT_COMMUNITY_TRANS_TWO_AS:
757 rd = (0ULL << 48);
758 rd |= ((uint64_t)c.data1 & 0xffff) << 32;
759 rd |= (uint64_t)c.data2;
760 break;
761 case EXT_COMMUNITY_TRANS_IPV4:
762 rd = (1ULL << 48);
763 rd |= (uint64_t)c.data1 << 16;
764 rd |= (uint64_t)c.data2 & 0xffff;
765 break;
766 case EXT_COMMUNITY_TRANS_FOUR_AS:
767 rd = (2ULL << 48);
768 rd |= (uint64_t)c.data1 << 16;
769 rd |= (uint64_t)c.data2 & 0xffff;
770 break;
771 default:
772 return -1;
773 }
774
775 *community = htobe64(rd);
776 return 0;
777 }
778