1/* BGP Extended Communities Attribute 2 Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org> 3 4This file is part of GNU Zebra. 5 6GNU Zebra is free software; you can redistribute it and/or modify it 7under the terms of the GNU General Public License as published by the 8Free Software Foundation; either version 2, or (at your option) any 9later version. 10 11GNU Zebra is distributed in the hope that it will be useful, but 12WITHOUT ANY WARRANTY; without even the implied warranty of 13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14General Public License for more details. 15 16You should have received a copy of the GNU General Public License 17along with GNU Zebra; see the file COPYING. If not, write to the Free 18Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 1902111-1307, USA. */ 20 21#include <zebra.h> 22 23#include "hash.h" 24#include "memory.h" 25#include "prefix.h" 26#include "command.h" 27 28#include "bgpd/bgpd.h" 29#include "bgpd/bgp_ecommunity.h" 30 31/* Extended community value is eight octet. */ 32struct ecommunity_val 33{ 34 char val[8]; 35}; 36 37/* For parse Extended Community attribute tupple. */ 38struct ecommunity_as 39{ 40 as_t as; 41 u_int32_t val; 42}; 43 44struct ecommunity_ip 45{ 46 struct in_addr ip; 47 u_int16_t val; 48}; 49 50/* Hash of community attribute. */ 51struct hash *ecomhash; 52 53/* Allocate a new ecommunities. */ 54struct ecommunity * 55ecommunity_new () 56{ 57 return (struct ecommunity *) XCALLOC (MTYPE_ECOMMUNITY, 58 sizeof (struct ecommunity)); 59} 60 61/* Allocate ecommunities. */ 62void 63ecommunity_free (struct ecommunity *ecom) 64{ 65 if (ecom->val) 66 XFREE (MTYPE_ECOMMUNITY_VAL, ecom->val); 67 68 if (ecom->str) 69 XFREE (MTYPE_ECOMMUNITY_STR, ecom->str); 70 71 XFREE (MTYPE_ECOMMUNITY, ecom); 72} 73 74void * 75ecommunity_hash_alloc (struct ecommunity *val) 76{ 77 struct ecommunity *ecom; 78 79 ecom = ecommunity_new (); 80 81 ecom->size = val->size; 82 ecom->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, val->size * BGP_RD_SIZE); 83 memcpy (ecom->val, val->val, val->size * BGP_RD_SIZE); 84 85 return ecom; 86} 87 88struct ecommunity * 89ecommunity_parse (char *pnt, u_short length) 90{ 91 struct ecommunity tmp; 92 struct ecommunity *find; 93 94 if (length % BGP_RD_SIZE) 95 return NULL; 96 97 tmp.size = length / BGP_RD_SIZE; 98 tmp.val = pnt; 99 100 find = (struct ecommunity *) hash_get (ecomhash, &tmp, 101 ecommunity_hash_alloc); 102 find->refcnt++; 103 104 return find; 105} 106 107struct ecommunity * 108ecommunity_dup (struct ecommunity *ecom) 109{ 110 struct ecommunity *new; 111 112 new = XMALLOC (MTYPE_ECOMMUNITY, sizeof (struct ecommunity)); 113 memset (new, 0, sizeof (struct ecommunity)); 114 new->size = ecom->size; 115 if (new->size) 116 { 117 new->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, ecom->size * BGP_RD_SIZE); 118 memcpy (new->val, ecom->val, ecom->size * BGP_RD_SIZE); 119 } 120 else 121 new->val = NULL; 122 return new; 123} 124 125struct ecommunity * 126ecommunity_merge (struct ecommunity *ecom1, struct ecommunity *ecom2) 127{ 128 if (ecom1->val) 129 ecom1->val = XREALLOC (MTYPE_ECOMMUNITY_VAL, ecom1->val, 130 (ecom1->size + ecom2->size) * BGP_RD_SIZE); 131 else 132 ecom1->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, 133 (ecom1->size + ecom2->size) * BGP_RD_SIZE); 134 135 memcpy (ecom1->val + (ecom1->size * BGP_RD_SIZE), 136 ecom2->val, ecom2->size * BGP_RD_SIZE); 137 ecom1->size += ecom2->size; 138 139 return ecom1; 140} 141 142struct ecommunity * 143ecommunity_intern (struct ecommunity *ecom) 144{ 145 struct ecommunity *find; 146 147 assert (ecom->refcnt == 0); 148 149 find = (struct ecommunity *) hash_get (ecomhash, ecom, hash_alloc_intern); 150 151 if (find != ecom) 152 ecommunity_free (ecom); 153 154 find->refcnt++; 155 156 if (! find->str) 157 find->str = ecommunity_ecom2str (find, ECOMMUNITY_FORMAT_DISPLAY); 158 159 return find; 160} 161 162void 163ecommunity_unintern (struct ecommunity *ecom) 164{ 165 if (ecom->refcnt) 166 ecom->refcnt--; 167 168 if (ecom->refcnt == 0) 169 { 170 struct ecommunity *ret; 171 172 ret = (struct ecommunity *) hash_release (ecomhash, ecom); 173 assert (ret != NULL); 174 175 ecommunity_free (ecom); 176 } 177} 178 179unsigned int 180ecommunity_hash_make (struct ecommunity *ecom) 181{ 182 int c; 183 unsigned int key; 184 unsigned char *pnt; 185 186 key = 0; 187 pnt = (unsigned char *)ecom->val; 188 189 for (c = 0; c < ecom->size * BGP_RD_SIZE; c++) 190 key += pnt[c]; 191 192 return key; 193} 194 195int 196ecommunity_cmp (struct ecommunity *ecom1, struct ecommunity *ecom2) 197{ 198 if (ecom1->size == ecom2->size 199 && memcmp (ecom1->val, ecom2->val, ecom1->size * BGP_RD_SIZE) == 0) 200 return 1; 201 return 0; 202} 203 204int 205ecommunity_add_val (struct ecommunity *ecom, struct bgp_rd *rd) 206{ 207 u_char *p; 208 int ret; 209 int c; 210 211 if (ecom->val == NULL) 212 { 213 ecom->size++; 214 ecom->val = XMALLOC (MTYPE_ECOMMUNITY_VAL, ecom_length (ecom)); 215 memcpy (ecom->val, rd->val, BGP_RD_SIZE); 216 return 1; 217 } 218 219 c = 0; 220 for (p = ecom->val; c < ecom->size; p += 8, c++) 221 { 222 ret = memcmp (p, rd->val, BGP_RD_SIZE); 223 if (ret == 0) 224 return 0; 225 226 if (ret > 0) 227 break; 228 } 229 230 ecom->size++; 231 ecom->val = XREALLOC (MTYPE_ECOMMUNITY_VAL, ecom->val, ecom_length (ecom)); 232 233 memmove (ecom->val + (c + 1) * BGP_RD_SIZE, 234 ecom->val + c * BGP_RD_SIZE, (ecom->size - 1 - c) * BGP_RD_SIZE); 235 memcpy (ecom->val + c * BGP_RD_SIZE, rd->val, BGP_RD_SIZE); 236 237 return 1; 238} 239 240/* Extended Communities token enum. */ 241enum ecommunity_token 242{ 243 ecommunity_token_rt, 244 ecommunity_token_soo, 245 ecommunity_token_val, 246 ecommunity_token_unknown 247}; 248 249/* Get next Extended Communities token from the string. */ 250char * 251ecommunity_gettoken (char *str, struct ecommunity_val *eval, 252 enum ecommunity_token *token) 253{ 254 int ret; 255 int dot = 0; 256 int digit = 0; 257 int separator = 0; 258 u_int32_t val_low = 0; 259 u_int32_t val_high = 0; 260 char *p = str; 261 struct in_addr ip; 262 char ipstr[INET_ADDRSTRLEN + 1]; 263 264 /* Skip white space. */ 265 while (isspace ((int) *p)) 266 { 267 p++; 268 str++; 269 } 270 271 /* Check the end of the line. */ 272 if (*p == '\0') 273 return NULL; 274 275 /* "rt" and "soo" keyword parse. */ 276 if (! isdigit ((int) *p)) 277 { 278 /* "rt" match check. */ 279 if (tolower ((int) *p) == 'r') 280 { 281 p++; 282 if (tolower ((int) *p) == 't') 283 { 284 p++; 285 *token = ecommunity_token_rt; 286 return p; 287 } 288 if (isspace ((int) *p) || *p == '\0') 289 { 290 *token = ecommunity_token_rt; 291 return p; 292 } 293 goto error; 294 } 295 /* "soo" match check. */ 296 else if (tolower ((int) *p) == 's') 297 { 298 p++; 299 if (tolower ((int) *p) == 'o') 300 { 301 p++; 302 if (tolower ((int) *p) == 'o') 303 { 304 p++; 305 *token = ecommunity_token_soo; 306 return p; 307 } 308 if (isspace ((int) *p) || *p == '\0') 309 { 310 *token = ecommunity_token_soo; 311 return p; 312 } 313 goto error; 314 } 315 if (isspace ((int) *p) || *p == '\0') 316 { 317 *token = ecommunity_token_soo; 318 return p; 319 } 320 goto error; 321 } 322 goto error; 323 } 324 325 while (isdigit ((int) *p) || *p == ':' || *p == '.') 326 { 327 if (*p == ':') 328 { 329 if (separator) 330 goto error; 331 332 separator = 1; 333 digit = 0; 334 335 if (dot) 336 { 337 if ((p - str) > INET_ADDRSTRLEN) 338 goto error; 339 340 memset (ipstr, 0, INET_ADDRSTRLEN + 1); 341 memcpy (ipstr, str, p - str); 342 343 ret = inet_aton (ipstr, &ip); 344 if (ret == 0) 345 goto error; 346 } 347 else 348 val_high = val_low; 349 350 val_low = 0; 351 } 352 else if (*p == '.') 353 { 354 if (separator) 355 goto error; 356 dot++; 357 if (dot > 4) 358 goto error; 359 } 360 else 361 { 362 digit = 1; 363 val_low *= 10; 364 val_low += (*p - '0'); 365 } 366 p++; 367 } 368 369 /* Low digit part must be there. */ 370 if (! digit || ! separator) 371 goto error; 372 373 /* Encode result into routing distinguisher. */ 374 if (dot) 375 { 376 eval->val[0] = ECOMMUNITY_ENCODE_IP; 377 eval->val[1] = 0; 378 memcpy (&eval->val[2], &ip, sizeof (struct in_addr)); 379 eval->val[6] = (val_low >> 8) & 0xff; 380 eval->val[7] = val_low & 0xff; 381 } 382 else 383 { 384 eval->val[0] = ECOMMUNITY_ENCODE_AS; 385 eval->val[1] = 0; 386 eval->val[2] = (val_high >>8) & 0xff; 387 eval->val[3] = val_high & 0xff; 388 eval->val[4] = (val_low >>24) & 0xff; 389 eval->val[5] = (val_low >>16) & 0xff; 390 eval->val[6] = (val_low >>8) & 0xff; 391 eval->val[7] = val_low & 0xff; 392 } 393 *token = ecommunity_token_val; 394 return p; 395 396 error: 397 *token = ecommunity_token_unknown; 398 return p; 399} 400 401/* Convert string to extended community attribute. 402 403 When type is already known, please specify both str and type. str 404 should not include keyword such as "rt" and "soo". Type is 405 ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN. 406 keyword_included should be zero. 407 408 For example route-map's "set extcommunity" command case: 409 410 "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3" 411 type = ECOMMUNITY_ROUTE_TARGET 412 keyword_included = 0 413 414 "soo 100:1" -> str = "100:1" 415 type = ECOMMUNITY_SITE_ORIGIN 416 keyword_included = 0 417 418 When string includes keyword for each extended community value. 419 Please specify keyword_included as non-zero value. 420 421 For example standard extcommunity-list case: 422 423 "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1" 424 type = 0 425 keyword_include = 1 426*/ 427struct ecommunity * 428ecommunity_str2com (char *str, int type, int keyword_included) 429{ 430 struct ecommunity *ecom = NULL; 431 enum ecommunity_token token; 432 struct ecommunity_val eval; 433 int keyword = 0; 434 435 while ((str = ecommunity_gettoken (str, &eval, &token))) 436 { 437 switch (token) 438 { 439 case ecommunity_token_rt: 440 case ecommunity_token_soo: 441 if (! keyword_included || keyword) 442 { 443 if (ecom) 444 ecommunity_free (ecom); 445 return NULL; 446 } 447 keyword = 1; 448 449 if (token == ecommunity_token_rt) 450 { 451 type = ECOMMUNITY_ROUTE_TARGET; 452 } 453 if (token == ecommunity_token_soo) 454 { 455 type = ECOMMUNITY_SITE_ORIGIN; 456 } 457 break; 458 case ecommunity_token_val: 459 if (keyword_included) 460 { 461 if (! keyword) 462 { 463 if (ecom) 464 ecommunity_free (ecom); 465 return NULL; 466 } 467 keyword = 0; 468 } 469 if (ecom == NULL) 470 ecom = ecommunity_new (); 471 eval.val[1] = type; 472 ecommunity_add_val (ecom, (struct bgp_rd *) &eval); 473 break; 474 case ecommunity_token_unknown: 475 default: 476 if (ecom) 477 ecommunity_free (ecom); 478 return NULL; 479 break; 480 } 481 } 482 return ecom; 483} 484 485/* Convert extended community attribute to string. 486 487 Due to historical reason of industry standard implementation, there 488 are three types of format. 489 490 route-map set extcommunity format 491 "rt 100:1 100:2" 492 "soo 100:3" 493 494 extcommunity-list 495 "rt 100:1 rt 100:2 soo 100:3" 496 497 "show ip bgp" and extcommunity-list regular expression matching 498 "RT:100:1 RT:100:2 SoO:100:3" 499 500 For each formath please use below definition for format: 501 502 ECOMMUNITY_FORMAT_ROUTE_MAP 503 ECOMMUNITY_FORMAT_COMMUNITY_LIST 504 ECOMMUNITY_FORMAT_DISPLAY 505*/ 506char * 507ecommunity_ecom2str (struct ecommunity *ecom, int format) 508{ 509 int i; 510 u_char *pnt; 511 struct ecommunity_as eas; 512 struct ecommunity_ip eip; 513 int encode = 0; 514 int type = 0; 515#define ECOMMUNITY_STR_DEFAULT_LEN 26 516 int str_size; 517 int str_pnt; 518 u_char *str_buf; 519 char *prefix; 520 int len = 0; 521 int first = 1; 522 523 if (ecom->size == 0) 524 { 525 str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, 1); 526 str_buf[0] = '\0'; 527 return str_buf; 528 } 529 530 /* Prepare buffer. */ 531 str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, ECOMMUNITY_STR_DEFAULT_LEN + 1); 532 str_size = ECOMMUNITY_STR_DEFAULT_LEN + 1; 533 str_pnt = 0; 534 535 for (i = 0; i < ecom->size; i++) 536 { 537 pnt = ecom->val + (i * 8); 538 539 /* High-order octet of type. */ 540 encode = *pnt++; 541 if (encode != ECOMMUNITY_ENCODE_AS && encode != ECOMMUNITY_ENCODE_IP) 542 { 543 if (str_buf) 544 XFREE (MTYPE_ECOMMUNITY_STR, str_buf); 545 return "Unknown"; 546 } 547 548 /* Low-order octet of type. */ 549 type = *pnt++; 550 if (type != ECOMMUNITY_ROUTE_TARGET && type != ECOMMUNITY_SITE_ORIGIN) 551 { 552 if (str_buf) 553 XFREE (MTYPE_ECOMMUNITY_STR, str_buf); 554 return "Unknown"; 555 } 556 557 switch (format) 558 { 559 case ECOMMUNITY_FORMAT_COMMUNITY_LIST: 560 prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo "); 561 break; 562 case ECOMMUNITY_FORMAT_DISPLAY: 563 prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:"); 564 break; 565 case ECOMMUNITY_FORMAT_ROUTE_MAP: 566 prefix = ""; 567 break; 568 default: 569 if (str_buf) 570 XFREE (MTYPE_ECOMMUNITY_STR, str_buf); 571 return "Unknown"; 572 break; 573 } 574 575 /* Make it sure size is enough. */ 576 while (str_pnt + ECOMMUNITY_STR_DEFAULT_LEN >= str_size) 577 { 578 str_size *= 2; 579 str_buf = XREALLOC (MTYPE_ECOMMUNITY_STR, str_buf, str_size); 580 } 581 582 /* Space between each value. */ 583 if (! first) 584 str_buf[str_pnt++] = ' '; 585 586 /* Put string into buffer. */ 587 if (encode == ECOMMUNITY_ENCODE_AS) 588 { 589 eas.as = (*pnt++ << 8); 590 eas.as |= (*pnt++); 591 592 eas.val = (*pnt++ << 24); 593 eas.val |= (*pnt++ << 16); 594 eas.val |= (*pnt++ << 8); 595 eas.val |= (*pnt++); 596 597 len = sprintf (str_buf + str_pnt, "%s%d:%d", prefix, 598 eas.as, eas.val); 599 str_pnt += len; 600 first = 0; 601 } 602 else if (encode == ECOMMUNITY_ENCODE_IP) 603 { 604 memcpy (&eip.ip, pnt, 4); 605 pnt += 4; 606 eip.val = (*pnt++ << 8); 607 eip.val |= (*pnt++); 608 609 len = sprintf (str_buf + str_pnt, "%s%s:%d", prefix, 610 inet_ntoa (eip.ip), eip.val); 611 str_pnt += len; 612 first = 0; 613 } 614 } 615 return str_buf; 616} 617 618/* Transform RFC2547 routing distinguisher to extended communities 619 type. */ 620void 621ecommunity_rd2com (struct bgp_rd *rd, u_char type) 622{ 623 rd->val[0] = rd->val[1]; 624 rd->val[1] = type; 625} 626 627void 628ecommunity_vty_out (struct vty *vty, struct ecommunity *ecom) 629{ 630 int i; 631 u_char *pnt; 632 struct ecommunity_as eas; 633 struct ecommunity_ip eip; 634 int encode = 0; 635 int type = 0; 636 637 for (i = 0; i < ecom->size; i++) 638 { 639 pnt = ecom->val + (i * 8); 640 641 /* High-order octet of type. */ 642 if (*pnt == ECOMMUNITY_ENCODE_AS) 643 encode = ECOMMUNITY_ENCODE_AS; 644 else if (*pnt == ECOMMUNITY_ENCODE_IP) 645 encode = ECOMMUNITY_ENCODE_IP; 646 pnt++; 647 648 /* Low-order octet of type. */ 649 if (*pnt == ECOMMUNITY_ROUTE_TARGET) 650 { 651 if (type != ECOMMUNITY_ROUTE_TARGET) 652 vty_out (vty, " RT:"); 653 type = ECOMMUNITY_ROUTE_TARGET; 654 } 655 else if (*pnt == ECOMMUNITY_SITE_ORIGIN) 656 { 657 if (type != ECOMMUNITY_SITE_ORIGIN) 658 vty_out (vty, " SoO:"); 659 type = ECOMMUNITY_SITE_ORIGIN; 660 } 661 pnt++; 662 663 if (encode == ECOMMUNITY_ENCODE_AS) 664 { 665 eas.as = (*pnt++ << 8); 666 eas.as |= (*pnt++); 667 668 eas.val = (*pnt++ << 24); 669 eas.val |= (*pnt++ << 16); 670 eas.val |= (*pnt++ << 8); 671 eas.val |= (*pnt++); 672 673 vty_out (vty, "%d:%d", eas.as, eas.val); 674 } 675 else if (encode == ECOMMUNITY_ENCODE_IP) 676 { 677 memcpy (&eip.ip, pnt, 4); 678 pnt += 4; 679 eip.val = (*pnt++ << 8); 680 eip.val |= (*pnt++); 681 682 vty_out (vty, "%s:%d", inet_ntoa (eip.ip), eip.val); 683 } 684 } 685} 686 687/* Initialize Extended Comminities related hash. */ 688void 689ecommunity_init () 690{ 691 ecomhash = hash_create (ecommunity_hash_make, ecommunity_cmp); 692} 693