1 /* $OpenBSD: rde_community.c,v 1.15 2024/01/24 14:51:12 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, 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 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 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 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 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 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 * 646 communities_lookup(struct rde_community *comm) 647 { 648 return RB_FIND(comm_tree, &commtable, comm); 649 } 650 651 struct rde_community * 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 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 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 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->size; 719 to->nentries = from->nentries; 720 to->flags = from->flags; 721 722 if ((to->communities = reallocarray(NULL, to->size, 723 sizeof(struct community))) == NULL) 724 fatal(__func__); 725 726 memcpy(to->communities, from->communities, 727 to->nentries * sizeof(struct community)); 728 memset(to->communities + to->nentries, 0, sizeof(struct community) * 729 (to->size - to->nentries)); 730 } 731 732 /* 733 * Clean up the communities by freeing any dynamically allocated memory. 734 */ 735 void 736 communities_clean(struct rde_community *comm) 737 { 738 if (comm->refcnt != 0) 739 fatalx("%s: cleaning still referenced communities", __func__); 740 741 free(comm->communities); 742 memset(comm, 0, sizeof(*comm)); 743 } 744 745 int 746 community_to_rd(struct community *fc, uint64_t *community) 747 { 748 struct community c; 749 uint64_t rd; 750 751 if (fc2c(fc, NULL, &c, NULL) == -1) 752 return -1; 753 754 switch ((c.data3 >> 8) & EXT_COMMUNITY_VALUE) { 755 case EXT_COMMUNITY_TRANS_TWO_AS: 756 rd = (0ULL << 48); 757 rd |= ((uint64_t)c.data1 & 0xffff) << 32; 758 rd |= (uint64_t)c.data2; 759 break; 760 case EXT_COMMUNITY_TRANS_IPV4: 761 rd = (1ULL << 48); 762 rd |= (uint64_t)c.data1 << 16; 763 rd |= (uint64_t)c.data2 & 0xffff; 764 break; 765 case EXT_COMMUNITY_TRANS_FOUR_AS: 766 rd = (2ULL << 48); 767 rd |= (uint64_t)c.data1 << 16; 768 rd |= (uint64_t)c.data2 & 0xffff; 769 break; 770 default: 771 return -1; 772 } 773 774 *community = htobe64(rd); 775 return 0; 776 } 777