1/* AS path filter list. 2 Copyright (C) 1999 Kunihiro Ishiguro 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 "command.h" 24#include "log.h" 25#include "memory.h" 26#include "buffer.h" 27 28#include "bgpd/bgpd.h" 29#include "bgpd/bgp_aspath.h" 30#include "bgpd/bgp_regex.h" 31#include "bgpd/bgp_filter.h" 32 33/* List of AS filter list. */ 34struct as_list_list 35{ 36 struct as_list *head; 37 struct as_list *tail; 38}; 39 40/* AS path filter master. */ 41struct as_list_master 42{ 43 /* List of access_list which name is number. */ 44 struct as_list_list num; 45 46 /* List of access_list which name is string. */ 47 struct as_list_list str; 48 49 /* Hook function which is executed when new access_list is added. */ 50 void (*add_hook) (); 51 52 /* Hook function which is executed when access_list is deleted. */ 53 void (*delete_hook) (); 54}; 55 56/* Element of AS path filter. */ 57struct as_filter 58{ 59 struct as_filter *next; 60 struct as_filter *prev; 61 62 enum as_filter_type type; 63 64 regex_t *reg; 65 char *reg_str; 66}; 67 68enum as_list_type 69{ 70 ACCESS_TYPE_STRING, 71 ACCESS_TYPE_NUMBER 72}; 73 74/* AS path filter list. */ 75struct as_list 76{ 77 char *name; 78 79 enum as_list_type type; 80 81 struct as_list *next; 82 struct as_list *prev; 83 84 struct as_filter *head; 85 struct as_filter *tail; 86}; 87 88/* ip as-path access-list 10 permit AS1. */ 89 90static struct as_list_master as_list_master = 91{ 92 {NULL, NULL}, 93 {NULL, NULL}, 94 NULL, 95 NULL 96}; 97 98/* Allocate new AS filter. */ 99struct as_filter * 100as_filter_new () 101{ 102 struct as_filter *new; 103 104 new = XMALLOC (MTYPE_AS_FILTER, sizeof (struct as_filter)); 105 memset (new, 0, sizeof (struct as_filter)); 106 return new; 107} 108 109/* Free allocated AS filter. */ 110void 111as_filter_free (struct as_filter *asfilter) 112{ 113 if (asfilter->reg) 114 bgp_regex_free (asfilter->reg); 115 if (asfilter->reg_str) 116 XFREE (MTYPE_AS_FILTER_STR, asfilter->reg_str); 117 XFREE (MTYPE_AS_FILTER, asfilter); 118} 119 120/* Make new AS filter. */ 121struct as_filter * 122as_filter_make (regex_t *reg, char *reg_str, enum as_filter_type type) 123{ 124 struct as_filter *asfilter; 125 126 asfilter = as_filter_new (); 127 asfilter->reg = reg; 128 asfilter->type = type; 129 asfilter->reg_str = XSTRDUP (MTYPE_AS_FILTER_STR, reg_str); 130 131 return asfilter; 132} 133 134struct as_filter * 135as_filter_lookup (struct as_list *aslist, char *reg_str, 136 enum as_filter_type type) 137{ 138 struct as_filter *asfilter; 139 140 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) 141 if (strcmp (reg_str, asfilter->reg_str) == 0) 142 return asfilter; 143 return NULL; 144} 145 146void 147as_list_filter_add (struct as_list *aslist, struct as_filter *asfilter) 148{ 149 asfilter->next = NULL; 150 asfilter->prev = aslist->tail; 151 152 if (aslist->tail) 153 aslist->tail->next = asfilter; 154 else 155 aslist->head = asfilter; 156 aslist->tail = asfilter; 157} 158 159/* Lookup as_list from list of as_list by name. */ 160struct as_list * 161as_list_lookup (char *name) 162{ 163 struct as_list *aslist; 164 165 if (name == NULL) 166 return NULL; 167 168 for (aslist = as_list_master.num.head; aslist; aslist = aslist->next) 169 if (strcmp (aslist->name, name) == 0) 170 return aslist; 171 172 for (aslist = as_list_master.str.head; aslist; aslist = aslist->next) 173 if (strcmp (aslist->name, name) == 0) 174 return aslist; 175 176 return NULL; 177} 178 179struct as_list * 180as_list_new () 181{ 182 struct as_list *new; 183 184 new = XMALLOC (MTYPE_AS_LIST, sizeof (struct as_list)); 185 memset (new, 0, sizeof (struct as_list)); 186 return new; 187} 188 189void 190as_list_free (struct as_list *aslist) 191{ 192 XFREE (MTYPE_AS_LIST, aslist); 193} 194 195/* Insert new AS list to list of as_list. Each as_list is sorted by 196 the name. */ 197struct as_list * 198as_list_insert (char *name) 199{ 200 int i; 201 long number; 202 struct as_list *aslist; 203 struct as_list *point; 204 struct as_list_list *list; 205 206 /* Allocate new access_list and copy given name. */ 207 aslist = as_list_new (); 208 aslist->name = strdup (name); 209 210 /* If name is made by all digit character. We treat it as 211 number. */ 212 for (number = 0, i = 0; i < strlen (name); i++) 213 { 214 if (isdigit ((int) name[i])) 215 number = (number * 10) + (name[i] - '0'); 216 else 217 break; 218 } 219 220 /* In case of name is all digit character */ 221 if (i == strlen (name)) 222 { 223 aslist->type = ACCESS_TYPE_NUMBER; 224 225 /* Set access_list to number list. */ 226 list = &as_list_master.num; 227 228 for (point = list->head; point; point = point->next) 229 if (atol (point->name) >= number) 230 break; 231 } 232 else 233 { 234 aslist->type = ACCESS_TYPE_STRING; 235 236 /* Set access_list to string list. */ 237 list = &as_list_master.str; 238 239 /* Set point to insertion point. */ 240 for (point = list->head; point; point = point->next) 241 if (strcmp (point->name, name) >= 0) 242 break; 243 } 244 245 /* In case of this is the first element of master. */ 246 if (list->head == NULL) 247 { 248 list->head = list->tail = aslist; 249 return aslist; 250 } 251 252 /* In case of insertion is made at the tail of access_list. */ 253 if (point == NULL) 254 { 255 aslist->prev = list->tail; 256 list->tail->next = aslist; 257 list->tail = aslist; 258 return aslist; 259 } 260 261 /* In case of insertion is made at the head of access_list. */ 262 if (point == list->head) 263 { 264 aslist->next = list->head; 265 list->head->prev = aslist; 266 list->head = aslist; 267 return aslist; 268 } 269 270 /* Insertion is made at middle of the access_list. */ 271 aslist->next = point; 272 aslist->prev = point->prev; 273 274 if (point->prev) 275 point->prev->next = aslist; 276 point->prev = aslist; 277 278 return aslist; 279} 280 281struct as_list * 282as_list_get (char *name) 283{ 284 struct as_list *aslist; 285 286 aslist = as_list_lookup (name); 287 if (aslist == NULL) 288 { 289 aslist = as_list_insert (name); 290 291 /* Run hook function. */ 292 if (as_list_master.add_hook) 293 (*as_list_master.add_hook) (); 294 } 295 296 return aslist; 297} 298 299static char * 300filter_type_str (enum as_filter_type type) 301{ 302 switch (type) 303 { 304 case AS_FILTER_PERMIT: 305 return "permit"; 306 break; 307 case AS_FILTER_DENY: 308 return "deny"; 309 break; 310 default: 311 return ""; 312 break; 313 } 314} 315 316void 317as_list_delete (struct as_list *aslist) 318{ 319 struct as_list_list *list; 320 struct as_filter *filter, *next; 321 322 for (filter = aslist->head; filter; filter = next) 323 { 324 next = filter->next; 325 as_filter_free (filter); 326 } 327 328 if (aslist->type == ACCESS_TYPE_NUMBER) 329 list = &as_list_master.num; 330 else 331 list = &as_list_master.str; 332 333 if (aslist->next) 334 aslist->next->prev = aslist->prev; 335 else 336 list->tail = aslist->prev; 337 338 if (aslist->prev) 339 aslist->prev->next = aslist->next; 340 else 341 list->head = aslist->next; 342 343 as_list_free (aslist); 344} 345 346static int 347as_list_empty (struct as_list *aslist) 348{ 349 if (aslist->head == NULL && aslist->tail == NULL) 350 return 1; 351 else 352 return 0; 353} 354 355void 356as_list_filter_delete (struct as_list *aslist, struct as_filter *asfilter) 357{ 358 if (asfilter->next) 359 asfilter->next->prev = asfilter->prev; 360 else 361 aslist->tail = asfilter->prev; 362 363 if (asfilter->prev) 364 asfilter->prev->next = asfilter->next; 365 else 366 aslist->head = asfilter->next; 367 368 as_filter_free (asfilter); 369 370 /* If access_list becomes empty delete it from access_master. */ 371 if (as_list_empty (aslist)) 372 as_list_delete (aslist); 373 374 /* Run hook function. */ 375 if (as_list_master.delete_hook) 376 (*as_list_master.delete_hook) (); 377} 378 379static int 380as_filter_match (struct as_filter *asfilter, struct aspath *aspath) 381{ 382 if (bgp_regexec (asfilter->reg, aspath) != REG_NOMATCH) 383 return 1; 384 return 0; 385} 386 387/* Apply AS path filter to AS. */ 388enum as_filter_type 389as_list_apply (struct as_list *aslist, void *object) 390{ 391 struct as_filter *asfilter; 392 struct aspath *aspath; 393 394 aspath = (struct aspath *) object; 395 396 if (aslist == NULL) 397 return AS_FILTER_DENY; 398 399 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) 400 { 401 if (as_filter_match (asfilter, aspath)) 402 return asfilter->type; 403 } 404 return AS_FILTER_DENY; 405} 406 407/* Add hook function. */ 408void 409as_list_add_hook (void (*func) ()) 410{ 411 as_list_master.add_hook = func; 412} 413 414/* Delete hook function. */ 415void 416as_list_delete_hook (void (*func) ()) 417{ 418 as_list_master.delete_hook = func; 419} 420 421int 422as_list_dup_check (struct as_list *aslist, struct as_filter *new) 423{ 424 struct as_filter *asfilter; 425 426 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) 427 { 428 if (asfilter->type == new->type 429 && strcmp (asfilter->reg_str, new->reg_str) == 0) 430 return 1; 431 } 432 return 0; 433} 434 435DEFUN (ip_as_path, ip_as_path_cmd, 436 "ip as-path access-list WORD (deny|permit) .LINE", 437 IP_STR 438 "BGP autonomous system path filter\n" 439 "Specify an access list name\n" 440 "Regular expression access list name\n" 441 "Specify packets to reject\n" 442 "Specify packets to forward\n" 443 "A regular-expression to match the BGP AS paths\n") 444{ 445 enum as_filter_type type; 446 struct as_filter *asfilter; 447 struct as_list *aslist; 448 regex_t *regex; 449 struct buffer *b; 450 int i; 451 char *regstr; 452 int first = 0; 453 454 /* Check the filter type. */ 455 if (strncmp (argv[1], "p", 1) == 0) 456 type = AS_FILTER_PERMIT; 457 else if (strncmp (argv[1], "d", 1) == 0) 458 type = AS_FILTER_DENY; 459 else 460 { 461 vty_out (vty, "filter type must be [permit|deny]%s", VTY_NEWLINE); 462 return CMD_WARNING; 463 } 464 465 /* Check AS path regex. */ 466 b = buffer_new (1024); 467 for (i = 2; i < argc; i++) 468 { 469 if (first) 470 buffer_putc (b, ' '); 471 else 472 first = 1; 473 474 buffer_putstr (b, argv[i]); 475 } 476 buffer_putc (b, '\0'); 477 478 regstr = buffer_getstr (b); 479 buffer_free (b); 480 481 regex = bgp_regcomp (regstr); 482 if (!regex) 483 { 484 free (regstr); 485 vty_out (vty, "can't compile regexp %s%s", argv[0], 486 VTY_NEWLINE); 487 return CMD_WARNING; 488 } 489 490 asfilter = as_filter_make (regex, regstr, type); 491 492 free (regstr); 493 494 /* Install new filter to the access_list. */ 495 aslist = as_list_get (argv[0]); 496 497 /* Duplicate insertion check. */; 498 if (as_list_dup_check (aslist, asfilter)) 499 as_filter_free (asfilter); 500 else 501 as_list_filter_add (aslist, asfilter); 502 503 return CMD_SUCCESS; 504} 505 506DEFUN (no_ip_as_path, 507 no_ip_as_path_cmd, 508 "no ip as-path access-list WORD (deny|permit) .LINE", 509 NO_STR 510 IP_STR 511 "BGP autonomous system path filter\n" 512 "Specify an access list name\n" 513 "Regular expression access list name\n" 514 "Specify packets to reject\n" 515 "Specify packets to forward\n" 516 "A regular-expression to match the BGP AS paths\n") 517{ 518 enum as_filter_type type; 519 struct as_filter *asfilter; 520 struct as_list *aslist; 521 struct buffer *b; 522 int i; 523 int first = 0; 524 char *regstr; 525 regex_t *regex; 526 527 /* Lookup AS list from AS path list. */ 528 aslist = as_list_lookup (argv[0]); 529 if (aslist == NULL) 530 { 531 vty_out (vty, "ip as-path access-list %s doesn't exist%s", argv[0], 532 VTY_NEWLINE); 533 return CMD_WARNING; 534 } 535 536 /* Check the filter type. */ 537 if (strncmp (argv[1], "p", 1) == 0) 538 type = AS_FILTER_PERMIT; 539 else if (strncmp (argv[1], "d", 1) == 0) 540 type = AS_FILTER_DENY; 541 else 542 { 543 vty_out (vty, "filter type must be [permit|deny]%s", VTY_NEWLINE); 544 return CMD_WARNING; 545 } 546 547 /* Compile AS path. */ 548 b = buffer_new (1024); 549 for (i = 2; i < argc; i++) 550 { 551 if (first) 552 buffer_putc (b, ' '); 553 else 554 first = 1; 555 556 buffer_putstr (b, argv[i]); 557 } 558 buffer_putc (b, '\0'); 559 560 regstr = buffer_getstr (b); 561 buffer_free (b); 562 563 regex = bgp_regcomp (regstr); 564 if (!regex) 565 { 566 free (regstr); 567 vty_out (vty, "can't compile regexp %s%s", argv[0], 568 VTY_NEWLINE); 569 return CMD_WARNING; 570 } 571 572 /* Lookup asfilter. */ 573 asfilter = as_filter_lookup (aslist, regstr, type); 574 575 free (regstr); 576 bgp_regex_free (regex); 577 578 if (asfilter == NULL) 579 { 580 vty_out (vty, "%s", VTY_NEWLINE); 581 return CMD_WARNING; 582 } 583 584 as_list_filter_delete (aslist, asfilter); 585 586 return CMD_SUCCESS; 587} 588 589DEFUN (no_ip_as_path_all, 590 no_ip_as_path_all_cmd, 591 "no ip as-path access-list WORD", 592 NO_STR 593 IP_STR 594 "BGP autonomous system path filter\n" 595 "Specify an access list name\n" 596 "Regular expression access list name\n") 597{ 598 struct as_list *aslist; 599 600 aslist = as_list_lookup (argv[0]); 601 if (aslist == NULL) 602 { 603 vty_out (vty, "ip as-path access-list %s doesn't exist%s", argv[0], 604 VTY_NEWLINE); 605 return CMD_WARNING; 606 } 607 608 as_list_delete (aslist); 609 610 return CMD_SUCCESS; 611} 612 613int 614config_write_as_list (struct vty *vty) 615{ 616 struct as_list *aslist; 617 struct as_filter *asfilter; 618 int write = 0; 619 620 for (aslist = as_list_master.num.head; aslist; aslist = aslist->next) 621 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) 622 { 623 vty_out (vty, "ip as-path access-list %s %s %s%s", 624 aslist->name, filter_type_str (asfilter->type), 625 asfilter->reg_str, 626 VTY_NEWLINE); 627 write++; 628 } 629 630 for (aslist = as_list_master.str.head; aslist; aslist = aslist->next) 631 for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) 632 { 633 vty_out (vty, "ip as-path access-list %s %s %s%s", 634 aslist->name, filter_type_str (asfilter->type), 635 asfilter->reg_str, 636 VTY_NEWLINE); 637 write++; 638 } 639 return write; 640} 641 642struct cmd_node as_list_node = 643{ 644 AS_LIST_NODE, 645 "", 646 1 647}; 648 649/* Register functions. */ 650void 651bgp_filter_init () 652{ 653 install_node (&as_list_node, config_write_as_list); 654 655 install_element (CONFIG_NODE, &ip_as_path_cmd); 656 install_element (CONFIG_NODE, &no_ip_as_path_cmd); 657 install_element (CONFIG_NODE, &no_ip_as_path_all_cmd); 658} 659