1 /* $OpenBSD: rde_community.c,v 1.14 2023/10/10 14:36:28 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 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 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 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 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 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 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 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 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 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 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 421 community_add(struct rde_community *comm, int flags, void *buf, size_t len) 422 { 423 struct community set = { .flags = COMMUNITY_TYPE_BASIC }; 424 uint8_t *b = buf; 425 uint16_t c; 426 size_t l; 427 428 if (len == 0 || len % 4 != 0) 429 return -1; 430 431 if (flags & ATTR_PARTIAL) 432 comm->flags |= PARTIAL_COMMUNITIES; 433 434 for (l = 0; l < len; l += 4, b += 4) { 435 memcpy(&c, b, sizeof(c)); 436 set.data1 = ntohs(c); 437 memcpy(&c, b + 2, sizeof(c)); 438 set.data2 = ntohs(c); 439 insert_community(comm, &set); 440 } 441 442 return 0; 443 } 444 445 int 446 community_large_add(struct rde_community *comm, int flags, void *buf, 447 size_t len) 448 { 449 struct community set = { .flags = COMMUNITY_TYPE_LARGE }; 450 uint8_t *b = buf; 451 size_t l; 452 453 if (len == 0 || len % 12 != 0) 454 return -1; 455 456 if (flags & ATTR_PARTIAL) 457 comm->flags |= PARTIAL_LARGE_COMMUNITIES; 458 459 for (l = 0; l < len; l += 12, b += 12) { 460 memcpy(&set.data1, b, sizeof(set.data1)); 461 memcpy(&set.data2, b + 4, sizeof(set.data2)); 462 memcpy(&set.data3, b + 8, sizeof(set.data3)); 463 set.data1 = ntohl(set.data1); 464 set.data2 = ntohl(set.data2); 465 set.data3 = ntohl(set.data3); 466 insert_community(comm, &set); 467 } 468 469 return 0; 470 } 471 472 int 473 community_ext_add(struct rde_community *comm, int flags, int ebgp, 474 void *buf, size_t len) 475 { 476 struct community set = { .flags = COMMUNITY_TYPE_EXT }; 477 uint8_t *b = buf, type; 478 uint64_t c; 479 size_t l; 480 481 if (len == 0 || len % 8 != 0) 482 return -1; 483 484 if (flags & ATTR_PARTIAL) 485 comm->flags |= PARTIAL_EXT_COMMUNITIES; 486 487 for (l = 0; l < len; l += 8, b += 8) { 488 memcpy(&c, b, 8); 489 490 c = be64toh(c); 491 type = c >> 56; 492 /* filter out non-transitive ext communuties from ebgp peers */ 493 if (ebgp && (type & EXT_COMMUNITY_NON_TRANSITIVE)) 494 continue; 495 switch (type & EXT_COMMUNITY_VALUE) { 496 case EXT_COMMUNITY_TRANS_TWO_AS: 497 case EXT_COMMUNITY_TRANS_OPAQUE: 498 case EXT_COMMUNITY_TRANS_EVPN: 499 set.data1 = c >> 32 & 0xffff; 500 set.data2 = c; 501 break; 502 case EXT_COMMUNITY_TRANS_FOUR_AS: 503 case EXT_COMMUNITY_TRANS_IPV4: 504 set.data1 = c >> 16; 505 set.data2 = c & 0xffff; 506 break; 507 } 508 set.data3 = c >> 48; 509 510 insert_community(comm, &set); 511 } 512 513 return 0; 514 } 515 516 /* 517 * Convert communities back to the wireformat. 518 * When writing ATTR_EXT_COMMUNITIES non-transitive communities need to 519 * be skipped if they are sent to an ebgp peer. 520 */ 521 int 522 community_writebuf(struct rde_community *comm, uint8_t type, int ebgp, 523 struct ibuf *buf) 524 { 525 struct community *cp; 526 uint64_t ext; 527 int l, size, start, end, num; 528 int flags = ATTR_OPTIONAL | ATTR_TRANSITIVE; 529 uint8_t t; 530 531 switch (type) { 532 case ATTR_COMMUNITIES: 533 if (comm->flags & PARTIAL_COMMUNITIES) 534 flags |= ATTR_PARTIAL; 535 size = 4; 536 t = COMMUNITY_TYPE_BASIC; 537 break; 538 case ATTR_EXT_COMMUNITIES: 539 if (comm->flags & PARTIAL_EXT_COMMUNITIES) 540 flags |= ATTR_PARTIAL; 541 size = 8; 542 t = COMMUNITY_TYPE_EXT; 543 break; 544 case ATTR_LARGE_COMMUNITIES: 545 if (comm->flags & PARTIAL_LARGE_COMMUNITIES) 546 flags |= ATTR_PARTIAL; 547 size = 12; 548 t = COMMUNITY_TYPE_LARGE; 549 break; 550 default: 551 return -1; 552 } 553 554 /* first count how many communities will be written */ 555 num = 0; 556 start = -1; 557 for (l = 0; l < comm->nentries; l++) { 558 cp = &comm->communities[l]; 559 if ((uint8_t)cp->flags == t) { 560 if (ebgp && non_transitive_ext_community(cp)) 561 continue; 562 num++; 563 if (start == -1) 564 start = l; 565 } 566 if ((uint8_t)cp->flags > t) 567 break; 568 } 569 end = l; 570 571 /* no communities for this type present */ 572 if (num == 0) 573 return 0; 574 575 if (num > INT16_MAX / size) 576 return -1; 577 578 /* write attribute header */ 579 if (attr_writebuf(buf, flags, type, NULL, num * size) == -1) 580 return -1; 581 582 /* write out the communities */ 583 for (l = start; l < end; l++) { 584 cp = &comm->communities[l]; 585 586 switch (type) { 587 case ATTR_COMMUNITIES: 588 if (ibuf_add_n16(buf, cp->data1) == -1) 589 return -1; 590 if (ibuf_add_n16(buf, cp->data2) == -1) 591 return -1; 592 break; 593 case ATTR_EXT_COMMUNITIES: 594 if (ebgp && non_transitive_ext_community(cp)) 595 continue; 596 597 ext = (uint64_t)cp->data3 << 48; 598 switch ((cp->data3 >> 8) & EXT_COMMUNITY_VALUE) { 599 case EXT_COMMUNITY_TRANS_TWO_AS: 600 case EXT_COMMUNITY_TRANS_OPAQUE: 601 case EXT_COMMUNITY_TRANS_EVPN: 602 ext |= ((uint64_t)cp->data1 & 0xffff) << 32; 603 ext |= (uint64_t)cp->data2; 604 break; 605 case EXT_COMMUNITY_TRANS_FOUR_AS: 606 case EXT_COMMUNITY_TRANS_IPV4: 607 ext |= (uint64_t)cp->data1 << 16; 608 ext |= (uint64_t)cp->data2 & 0xffff; 609 break; 610 } 611 if (ibuf_add_n64(buf, ext) == -1) 612 return -1; 613 break; 614 case ATTR_LARGE_COMMUNITIES: 615 if (ibuf_add_n32(buf, cp->data1) == -1) 616 return -1; 617 if (ibuf_add_n32(buf, cp->data2) == -1) 618 return -1; 619 if (ibuf_add_n32(buf, cp->data3) == -1) 620 return -1; 621 break; 622 } 623 } 624 return 0; 625 } 626 627 /* 628 * Global RIB cache for communities 629 */ 630 static inline int 631 communities_compare(struct rde_community *a, struct rde_community *b) 632 { 633 if (a->nentries != b->nentries) 634 return a->nentries > b->nentries ? 1 : -1; 635 if (a->flags != b->flags) 636 return a->flags > b->flags ? 1 : -1; 637 638 return memcmp(a->communities, b->communities, 639 a->nentries * sizeof(struct community)); 640 } 641 642 RB_HEAD(comm_tree, rde_community) commtable = RB_INITIALIZER(&commtable); 643 RB_GENERATE_STATIC(comm_tree, rde_community, entry, communities_compare); 644 645 void 646 communities_shutdown(void) 647 { 648 if (!RB_EMPTY(&commtable)) 649 log_warnx("%s: free non-free table", __func__); 650 } 651 652 struct rde_community * 653 communities_lookup(struct rde_community *comm) 654 { 655 return RB_FIND(comm_tree, &commtable, comm); 656 } 657 658 struct rde_community * 659 communities_link(struct rde_community *comm) 660 { 661 struct rde_community *n, *f; 662 663 if ((n = malloc(sizeof(*n))) == NULL) 664 fatal(__func__); 665 communities_copy(n, comm); 666 667 if ((f = RB_INSERT(comm_tree, &commtable, n)) != NULL) { 668 log_warnx("duplicate communities collection inserted"); 669 free(n->communities); 670 free(n); 671 return f; 672 } 673 n->refcnt = 1; /* initial reference by the cache */ 674 675 rdemem.comm_size += n->size; 676 rdemem.comm_nmemb += n->nentries; 677 rdemem.comm_cnt++; 678 679 return n; 680 } 681 682 void 683 communities_unlink(struct rde_community *comm) 684 { 685 if (comm->refcnt != 1) 686 fatalx("%s: unlinking still referenced communities", __func__); 687 688 RB_REMOVE(comm_tree, &commtable, comm); 689 690 rdemem.comm_size -= comm->size; 691 rdemem.comm_nmemb -= comm->nentries; 692 rdemem.comm_cnt--; 693 694 free(comm->communities); 695 free(comm); 696 } 697 698 /* 699 * Return true/1 if the two communities collections are identical, 700 * otherwise returns zero. 701 */ 702 int 703 communities_equal(struct rde_community *a, struct rde_community *b) 704 { 705 if (a->nentries != b->nentries) 706 return 0; 707 if (a->flags != b->flags) 708 return 0; 709 710 return (memcmp(a->communities, b->communities, 711 a->nentries * sizeof(struct community)) == 0); 712 } 713 714 /* 715 * Copy communities to a new unreferenced struct. Needs to call 716 * communities_clean() when done. to can be statically allocated, 717 * it will be cleaned first. 718 */ 719 void 720 communities_copy(struct rde_community *to, struct rde_community *from) 721 { 722 memset(to, 0, sizeof(*to)); 723 724 /* ignore from->size and allocate the perfect amount */ 725 to->size = from->size; 726 to->nentries = from->nentries; 727 to->flags = from->flags; 728 729 if ((to->communities = reallocarray(NULL, to->size, 730 sizeof(struct community))) == NULL) 731 fatal(__func__); 732 733 memcpy(to->communities, from->communities, 734 to->nentries * sizeof(struct community)); 735 memset(to->communities + to->nentries, 0, sizeof(struct community) * 736 (to->size - to->nentries)); 737 } 738 739 /* 740 * Clean up the communities by freeing any dynamically allocated memory. 741 */ 742 void 743 communities_clean(struct rde_community *comm) 744 { 745 if (comm->refcnt != 0) 746 fatalx("%s: cleaning still referenced communities", __func__); 747 748 free(comm->communities); 749 memset(comm, 0, sizeof(*comm)); 750 } 751 752 int 753 community_to_rd(struct community *fc, uint64_t *community) 754 { 755 struct community c; 756 uint64_t rd; 757 758 if (fc2c(fc, NULL, &c, NULL) == -1) 759 return -1; 760 761 switch ((c.data3 >> 8) & EXT_COMMUNITY_VALUE) { 762 case EXT_COMMUNITY_TRANS_TWO_AS: 763 rd = (0ULL << 48); 764 rd |= ((uint64_t)c.data1 & 0xffff) << 32; 765 rd |= (uint64_t)c.data2; 766 break; 767 case EXT_COMMUNITY_TRANS_IPV4: 768 rd = (1ULL << 48); 769 rd |= (uint64_t)c.data1 << 16; 770 rd |= (uint64_t)c.data2 & 0xffff; 771 break; 772 case EXT_COMMUNITY_TRANS_FOUR_AS: 773 rd = (2ULL << 48); 774 rd |= (uint64_t)c.data1 << 16; 775 rd |= (uint64_t)c.data2 & 0xffff; 776 break; 777 default: 778 return -1; 779 } 780 781 *community = htobe64(rd); 782 return 0; 783 } 784