1 /* $OpenBSD: rde_filter.c,v 1.77 2016/06/03 17:36:37 benno Exp $ */ 2 3 /* 4 * Copyright (c) 2004 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/types.h> 19 #include <sys/queue.h> 20 21 #include <limits.h> 22 #include <stdlib.h> 23 #include <string.h> 24 25 #include "bgpd.h" 26 #include "rde.h" 27 28 int rde_filter_match(struct filter_rule *, struct rde_aspath *, 29 struct bgpd_addr *, u_int8_t, struct rde_peer *, struct rde_peer *); 30 int filterset_equal(struct filter_set_head *, struct filter_set_head *); 31 32 void 33 rde_apply_set(struct rde_aspath *asp, struct filter_set_head *sh, 34 u_int8_t aid, struct rde_peer *from, struct rde_peer *peer) 35 { 36 struct filter_set *set; 37 u_char *np; 38 int as, type; 39 u_int32_t prep_as; 40 u_int16_t nl; 41 u_int8_t prepend; 42 43 if (asp == NULL) 44 return; 45 46 TAILQ_FOREACH(set, sh, entry) { 47 switch (set->type) { 48 case ACTION_SET_LOCALPREF: 49 asp->lpref = set->action.metric; 50 break; 51 case ACTION_SET_RELATIVE_LOCALPREF: 52 if (set->action.relative > 0) { 53 if (set->action.relative + asp->lpref < 54 asp->lpref) 55 asp->lpref = UINT_MAX; 56 else 57 asp->lpref += set->action.relative; 58 } else { 59 if ((u_int32_t)-set->action.relative > 60 asp->lpref) 61 asp->lpref = 0; 62 else 63 asp->lpref += set->action.relative; 64 } 65 break; 66 case ACTION_SET_MED: 67 asp->flags |= F_ATTR_MED | F_ATTR_MED_ANNOUNCE; 68 asp->med = set->action.metric; 69 break; 70 case ACTION_SET_RELATIVE_MED: 71 asp->flags |= F_ATTR_MED | F_ATTR_MED_ANNOUNCE; 72 if (set->action.relative > 0) { 73 if (set->action.relative + asp->med < 74 asp->med) 75 asp->med = UINT_MAX; 76 else 77 asp->med += set->action.relative; 78 } else { 79 if ((u_int32_t)-set->action.relative > 80 asp->med) 81 asp->med = 0; 82 else 83 asp->med += set->action.relative; 84 } 85 break; 86 case ACTION_SET_WEIGHT: 87 asp->weight = set->action.metric; 88 break; 89 case ACTION_SET_RELATIVE_WEIGHT: 90 if (set->action.relative > 0) { 91 if (set->action.relative + asp->weight < 92 asp->weight) 93 asp->weight = UINT_MAX; 94 else 95 asp->weight += set->action.relative; 96 } else { 97 if ((u_int32_t)-set->action.relative > 98 asp->weight) 99 asp->weight = 0; 100 else 101 asp->weight += set->action.relative; 102 } 103 break; 104 case ACTION_SET_PREPEND_SELF: 105 prep_as = rde_local_as(); 106 prepend = set->action.prepend; 107 np = aspath_prepend(asp->aspath, prep_as, prepend, &nl); 108 aspath_put(asp->aspath); 109 asp->aspath = aspath_get(np, nl); 110 free(np); 111 break; 112 case ACTION_SET_PREPEND_PEER: 113 if (from == NULL) 114 break; 115 prep_as = from->conf.remote_as; 116 prepend = set->action.prepend; 117 np = aspath_prepend(asp->aspath, prep_as, prepend, &nl); 118 aspath_put(asp->aspath); 119 asp->aspath = aspath_get(np, nl); 120 free(np); 121 break; 122 case ACTION_SET_NEXTHOP: 123 case ACTION_SET_NEXTHOP_REJECT: 124 case ACTION_SET_NEXTHOP_BLACKHOLE: 125 case ACTION_SET_NEXTHOP_NOMODIFY: 126 case ACTION_SET_NEXTHOP_SELF: 127 nexthop_modify(asp, &set->action.nexthop, set->type, 128 aid); 129 break; 130 case ACTION_SET_COMMUNITY: 131 switch (set->action.community.as) { 132 case COMMUNITY_ERROR: 133 case COMMUNITY_ANY: 134 fatalx("rde_apply_set bad community string"); 135 case COMMUNITY_NEIGHBOR_AS: 136 as = peer->conf.remote_as; 137 break; 138 default: 139 as = set->action.community.as; 140 break; 141 } 142 143 switch (set->action.community.type) { 144 case COMMUNITY_ERROR: 145 case COMMUNITY_ANY: 146 fatalx("rde_apply_set bad community string"); 147 case COMMUNITY_NEIGHBOR_AS: 148 type = peer->conf.remote_as; 149 break; 150 default: 151 type = set->action.community.type; 152 break; 153 } 154 155 community_set(asp, as, type); 156 break; 157 case ACTION_DEL_COMMUNITY: 158 switch (set->action.community.as) { 159 case COMMUNITY_ERROR: 160 fatalx("rde_apply_set bad community string"); 161 case COMMUNITY_NEIGHBOR_AS: 162 as = peer->conf.remote_as; 163 break; 164 case COMMUNITY_ANY: 165 default: 166 as = set->action.community.as; 167 break; 168 } 169 170 switch (set->action.community.type) { 171 case COMMUNITY_ERROR: 172 fatalx("rde_apply_set bad community string"); 173 case COMMUNITY_NEIGHBOR_AS: 174 type = peer->conf.remote_as; 175 break; 176 case COMMUNITY_ANY: 177 default: 178 type = set->action.community.type; 179 break; 180 } 181 182 community_delete(asp, as, type); 183 break; 184 case ACTION_PFTABLE: 185 /* convert pftable name to an id */ 186 set->action.id = pftable_name2id(set->action.pftable); 187 set->type = ACTION_PFTABLE_ID; 188 /* FALLTHROUGH */ 189 case ACTION_PFTABLE_ID: 190 pftable_unref(asp->pftableid); 191 asp->pftableid = set->action.id; 192 pftable_ref(asp->pftableid); 193 break; 194 case ACTION_RTLABEL: 195 /* convert the route label to an id for faster access */ 196 set->action.id = rtlabel_name2id(set->action.rtlabel); 197 set->type = ACTION_RTLABEL_ID; 198 /* FALLTHROUGH */ 199 case ACTION_RTLABEL_ID: 200 rtlabel_unref(asp->rtlabelid); 201 asp->rtlabelid = set->action.id; 202 rtlabel_ref(asp->rtlabelid); 203 break; 204 case ACTION_SET_ORIGIN: 205 asp->origin = set->action.origin; 206 break; 207 case ACTION_SET_EXT_COMMUNITY: 208 community_ext_set(asp, &set->action.ext_community, 209 peer->conf.remote_as); 210 break; 211 case ACTION_DEL_EXT_COMMUNITY: 212 community_ext_delete(asp, &set->action.ext_community, 213 peer->conf.remote_as); 214 break; 215 } 216 } 217 } 218 219 int 220 rde_filter_match(struct filter_rule *f, struct rde_aspath *asp, 221 struct bgpd_addr *prefix, u_int8_t plen, struct rde_peer *peer, 222 struct rde_peer *from) 223 { 224 u_int32_t pas; 225 int cas, type; 226 227 if (asp != NULL && f->match.as.type != AS_NONE) { 228 if (f->match.as.flags & AS_FLAG_NEIGHBORAS) 229 pas = peer->conf.remote_as; 230 else 231 pas = f->match.as.as; 232 if (aspath_match(asp->aspath->data, asp->aspath->len, 233 &f->match.as, pas) == 0) 234 return (0); 235 } 236 237 if (asp != NULL && f->match.aslen.type != ASLEN_NONE) 238 if (aspath_lenmatch(asp->aspath, f->match.aslen.type, 239 f->match.aslen.aslen) == 0) 240 return (0); 241 242 if (asp != NULL && f->match.community.as != COMMUNITY_UNSET) { 243 switch (f->match.community.as) { 244 case COMMUNITY_ERROR: 245 fatalx("rde_apply_set bad community string"); 246 case COMMUNITY_NEIGHBOR_AS: 247 cas = peer->conf.remote_as; 248 break; 249 default: 250 cas = f->match.community.as; 251 break; 252 } 253 254 switch (f->match.community.type) { 255 case COMMUNITY_ERROR: 256 fatalx("rde_apply_set bad community string"); 257 case COMMUNITY_NEIGHBOR_AS: 258 type = peer->conf.remote_as; 259 break; 260 default: 261 type = f->match.community.type; 262 break; 263 } 264 265 if (community_match(asp, cas, type) == 0) 266 return (0); 267 } 268 if (asp != NULL && 269 (f->match.ext_community.flags & EXT_COMMUNITY_FLAG_VALID)) 270 if (community_ext_match(asp, &f->match.ext_community, 271 peer->conf.remote_as) == 0) 272 return (0); 273 274 if (f->match.prefix.addr.aid != 0) { 275 if (f->match.prefix.addr.aid != prefix->aid) 276 /* don't use IPv4 rules for IPv6 and vice versa */ 277 return (0); 278 279 if (prefix_compare(prefix, &f->match.prefix.addr, 280 f->match.prefix.len)) 281 return (0); 282 283 /* test prefixlen stuff too */ 284 switch (f->match.prefix.op) { 285 case OP_NONE: /* perfect match */ 286 return (plen == f->match.prefix.len); 287 case OP_EQ: 288 return (plen == f->match.prefix.len_min); 289 case OP_NE: 290 return (plen != f->match.prefix.len_min); 291 case OP_RANGE: 292 return ((plen >= f->match.prefix.len_min) && 293 (plen <= f->match.prefix.len_max)); 294 case OP_XRANGE: 295 return ((plen < f->match.prefix.len_min) || 296 (plen > f->match.prefix.len_max)); 297 case OP_LE: 298 return (plen <= f->match.prefix.len_min); 299 case OP_LT: 300 return (plen < f->match.prefix.len_min); 301 case OP_GE: 302 return (plen >= f->match.prefix.len_min); 303 case OP_GT: 304 return (plen > f->match.prefix.len_min); 305 } 306 /* NOTREACHED */ 307 } 308 if (f->match.nexthop.flags != 0) { 309 struct bgpd_addr *nexthop, *cmpaddr; 310 if (asp != NULL && asp->nexthop == NULL) 311 /* no nexthop, skip */ 312 return (0); 313 nexthop = &asp->nexthop->exit_nexthop; 314 if (f->match.nexthop.flags == FILTER_NEXTHOP_ADDR) 315 cmpaddr = &f->match.nexthop.addr; 316 else 317 cmpaddr = &from->remote_addr; 318 if (cmpaddr->aid != nexthop->aid) 319 /* don't use IPv4 rules for IPv6 and vice versa */ 320 return (0); 321 322 switch (cmpaddr->aid) { 323 case AID_INET: 324 if (cmpaddr->v4.s_addr != nexthop->v4.s_addr) 325 return (0); 326 break; 327 case AID_INET6: 328 if (memcmp(&cmpaddr->v6, &nexthop->v6, 329 sizeof(struct in6_addr))) 330 return (0); 331 break; 332 default: 333 fatalx("King Bula lost in address space"); 334 } 335 } 336 337 /* matched somewhen or is anymatch rule */ 338 return (1); 339 } 340 341 int 342 rde_filter_equal(struct filter_head *a, struct filter_head *b, 343 struct rde_peer *peer) 344 { 345 struct filter_rule *fa, *fb; 346 347 fa = a ? TAILQ_FIRST(a) : NULL; 348 fb = b ? TAILQ_FIRST(b) : NULL; 349 350 while (fa != NULL || fb != NULL) { 351 /* skip all rules with wrong peer */ 352 if (peer != NULL && fa != NULL && fa->peer.groupid != 0 && 353 fa->peer.groupid != peer->conf.groupid) { 354 fa = TAILQ_NEXT(fa, entry); 355 continue; 356 } 357 if (peer != NULL && fa != NULL && fa->peer.peerid != 0 && 358 fa->peer.peerid != peer->conf.id) { 359 fa = TAILQ_NEXT(fa, entry); 360 continue; 361 } 362 363 if (peer != NULL && fb != NULL && fb->peer.groupid != 0 && 364 fb->peer.groupid != peer->conf.groupid) { 365 fb = TAILQ_NEXT(fb, entry); 366 continue; 367 } 368 if (peer != NULL && fb != NULL && fb->peer.peerid != 0 && 369 fb->peer.peerid != peer->conf.id) { 370 fb = TAILQ_NEXT(fb, entry); 371 continue; 372 } 373 374 if (peer != NULL && fa != NULL && fa->peer.remote_as != 0 && 375 fa->peer.remote_as != peer->conf.remote_as) { 376 fa = TAILQ_NEXT(fa, entry); 377 continue; 378 } 379 380 /* compare the two rules */ 381 if ((fa == NULL && fb != NULL) || (fa != NULL && fb == NULL)) 382 /* new rule added or removed */ 383 return (0); 384 385 if (fa->action != fb->action || fa->quick != fb->quick) 386 return (0); 387 if (memcmp(&fa->peer, &fb->peer, sizeof(fa->peer))) 388 return (0); 389 if (memcmp(&fa->match, &fb->match, sizeof(fa->match))) 390 return (0); 391 if (!filterset_equal(&fa->set, &fb->set)) 392 return (0); 393 394 fa = TAILQ_NEXT(fa, entry); 395 fb = TAILQ_NEXT(fb, entry); 396 } 397 return (1); 398 } 399 400 void 401 filterlist_free(struct filter_head *fh) 402 { 403 struct filter_rule *r; 404 405 if (fh == NULL) 406 return; 407 408 while ((r = TAILQ_FIRST(fh)) != NULL) { 409 TAILQ_REMOVE(fh, r, entry); 410 filterset_free(&r->set); 411 free(r); 412 } 413 free(fh); 414 } 415 416 /* free a filterset and take care of possible name2id references */ 417 void 418 filterset_free(struct filter_set_head *sh) 419 { 420 struct filter_set *s; 421 struct nexthop *nh; 422 423 if (sh == NULL) 424 return; 425 426 while ((s = TAILQ_FIRST(sh)) != NULL) { 427 TAILQ_REMOVE(sh, s, entry); 428 if (s->type == ACTION_RTLABEL_ID) 429 rtlabel_unref(s->action.id); 430 else if (s->type == ACTION_PFTABLE_ID) 431 pftable_unref(s->action.id); 432 else if (s->type == ACTION_SET_NEXTHOP && 433 bgpd_process == PROC_RDE) { 434 nh = nexthop_get(&s->action.nexthop); 435 --nh->refcnt; 436 (void)nexthop_delete(nh); 437 } 438 free(s); 439 } 440 } 441 442 /* 443 * this function is a bit more complicated than a memcmp() because there are 444 * types that need to be considered equal e.g. ACTION_SET_MED and 445 * ACTION_SET_RELATIVE_MED. Also ACTION_SET_COMMUNITY and ACTION_SET_NEXTHOP 446 * need some special care. It only checks the types and not the values so 447 * it does not do a real compare. 448 */ 449 int 450 filterset_cmp(struct filter_set *a, struct filter_set *b) 451 { 452 if (strcmp(filterset_name(a->type), filterset_name(b->type))) 453 return (a->type - b->type); 454 455 if (a->type == ACTION_SET_COMMUNITY || 456 a->type == ACTION_DEL_COMMUNITY) { /* a->type == b->type */ 457 /* compare community */ 458 if (a->action.community.as - b->action.community.as != 0) 459 return (a->action.community.as - 460 b->action.community.as); 461 return (a->action.community.type - b->action.community.type); 462 } 463 464 if (a->type == ACTION_SET_EXT_COMMUNITY || 465 a->type == ACTION_DEL_EXT_COMMUNITY) { /* a->type == b->type */ 466 return (memcmp(&a->action.ext_community, 467 &b->action.ext_community, sizeof(a->action.ext_community))); 468 } 469 470 if (a->type == ACTION_SET_NEXTHOP && b->type == ACTION_SET_NEXTHOP) { 471 /* 472 * This is the only interesting case, all others are considered 473 * equal. It does not make sense to e.g. set a nexthop and 474 * reject it at the same time. Allow one IPv4 and one IPv6 475 * per filter set or only one of the other nexthop modifiers. 476 */ 477 return (a->action.nexthop.aid - b->action.nexthop.aid); 478 } 479 480 /* equal */ 481 return (0); 482 } 483 484 void 485 filterset_move(struct filter_set_head *source, struct filter_set_head *dest) 486 { 487 struct filter_set *s; 488 489 TAILQ_INIT(dest); 490 491 if (source == NULL) 492 return; 493 494 while ((s = TAILQ_FIRST(source)) != NULL) { 495 TAILQ_REMOVE(source, s, entry); 496 TAILQ_INSERT_TAIL(dest, s, entry); 497 } 498 } 499 500 int 501 filterset_equal(struct filter_set_head *ah, struct filter_set_head *bh) 502 { 503 struct filter_set *a, *b; 504 const char *as, *bs; 505 506 for (a = TAILQ_FIRST(ah), b = TAILQ_FIRST(bh); 507 a != NULL && b != NULL; 508 a = TAILQ_NEXT(a, entry), b = TAILQ_NEXT(b, entry)) { 509 switch (a->type) { 510 case ACTION_SET_PREPEND_SELF: 511 case ACTION_SET_PREPEND_PEER: 512 if (a->type == b->type && 513 a->action.prepend == b->action.prepend) 514 continue; 515 break; 516 case ACTION_SET_LOCALPREF: 517 case ACTION_SET_MED: 518 case ACTION_SET_WEIGHT: 519 if (a->type == b->type && 520 a->action.metric == b->action.metric) 521 continue; 522 break; 523 case ACTION_SET_RELATIVE_LOCALPREF: 524 case ACTION_SET_RELATIVE_MED: 525 case ACTION_SET_RELATIVE_WEIGHT: 526 if (a->type == b->type && 527 a->action.relative == b->action.relative) 528 continue; 529 break; 530 case ACTION_SET_NEXTHOP: 531 if (a->type == b->type && 532 memcmp(&a->action.nexthop, &b->action.nexthop, 533 sizeof(a->action.nexthop)) == 0) 534 continue; 535 break; 536 case ACTION_SET_NEXTHOP_BLACKHOLE: 537 case ACTION_SET_NEXTHOP_REJECT: 538 case ACTION_SET_NEXTHOP_NOMODIFY: 539 case ACTION_SET_NEXTHOP_SELF: 540 if (a->type == b->type) 541 continue; 542 break; 543 case ACTION_DEL_COMMUNITY: 544 case ACTION_SET_COMMUNITY: 545 if (a->type == b->type && 546 memcmp(&a->action.community, &b->action.community, 547 sizeof(a->action.community)) == 0) 548 continue; 549 break; 550 case ACTION_PFTABLE: 551 case ACTION_PFTABLE_ID: 552 if (b->type == ACTION_PFTABLE) 553 bs = b->action.pftable; 554 else if (b->type == ACTION_PFTABLE_ID) 555 bs = pftable_id2name(b->action.id); 556 else 557 break; 558 559 if (a->type == ACTION_PFTABLE) 560 as = a->action.pftable; 561 else 562 as = pftable_id2name(a->action.id); 563 564 if (strcmp(as, bs) == 0) 565 continue; 566 break; 567 case ACTION_RTLABEL: 568 case ACTION_RTLABEL_ID: 569 if (b->type == ACTION_RTLABEL) 570 bs = b->action.rtlabel; 571 else if (b->type == ACTION_RTLABEL_ID) 572 bs = rtlabel_id2name(b->action.id); 573 else 574 break; 575 576 if (a->type == ACTION_RTLABEL) 577 as = a->action.rtlabel; 578 else 579 as = rtlabel_id2name(a->action.id); 580 581 if (strcmp(as, bs) == 0) 582 continue; 583 break; 584 case ACTION_SET_ORIGIN: 585 if (a->type == b->type && 586 a->action.origin == b->action.origin) 587 continue; 588 break; 589 case ACTION_SET_EXT_COMMUNITY: 590 case ACTION_DEL_EXT_COMMUNITY: 591 if (a->type == b->type && memcmp( 592 &a->action.ext_community, 593 &b->action.ext_community, 594 sizeof(a->action.ext_community)) == 0) 595 continue; 596 break; 597 } 598 /* compare failed */ 599 return (0); 600 } 601 if (a != NULL || b != NULL) 602 return (0); 603 return (1); 604 } 605 606 const char * 607 filterset_name(enum action_types type) 608 { 609 switch (type) { 610 case ACTION_SET_LOCALPREF: 611 case ACTION_SET_RELATIVE_LOCALPREF: 612 return ("localpref"); 613 case ACTION_SET_MED: 614 case ACTION_SET_RELATIVE_MED: 615 return ("metric"); 616 case ACTION_SET_WEIGHT: 617 case ACTION_SET_RELATIVE_WEIGHT: 618 return ("weight"); 619 case ACTION_SET_PREPEND_SELF: 620 return ("prepend-self"); 621 case ACTION_SET_PREPEND_PEER: 622 return ("prepend-peer"); 623 case ACTION_SET_NEXTHOP: 624 case ACTION_SET_NEXTHOP_REJECT: 625 case ACTION_SET_NEXTHOP_BLACKHOLE: 626 case ACTION_SET_NEXTHOP_NOMODIFY: 627 case ACTION_SET_NEXTHOP_SELF: 628 return ("nexthop"); 629 case ACTION_SET_COMMUNITY: 630 return ("community"); 631 case ACTION_DEL_COMMUNITY: 632 return ("community delete"); 633 case ACTION_PFTABLE: 634 case ACTION_PFTABLE_ID: 635 return ("pftable"); 636 case ACTION_RTLABEL: 637 case ACTION_RTLABEL_ID: 638 return ("rtlabel"); 639 case ACTION_SET_ORIGIN: 640 return ("origin"); 641 case ACTION_SET_EXT_COMMUNITY: 642 return ("ext-community"); 643 case ACTION_DEL_EXT_COMMUNITY: 644 return ("ext-community delete"); 645 } 646 647 fatalx("filterset_name: got lost"); 648 } 649 650 /* 651 * Copyright (c) 2001 Daniel Hartmeier 652 * All rights reserved. 653 * 654 * Redistribution and use in source and binary forms, with or without 655 * modification, are permitted provided that the following conditions 656 * are met: 657 * 658 * - Redistributions of source code must retain the above copyright 659 * notice, this list of conditions and the following disclaimer. 660 * - Redistributions in binary form must reproduce the above 661 * copyright notice, this list of conditions and the following 662 * disclaimer in the documentation and/or other materials provided 663 * with the distribution. 664 * 665 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 666 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 667 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 668 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 669 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 670 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 671 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 672 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 673 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 674 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 675 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 676 * POSSIBILITY OF SUCH DAMAGE. 677 * 678 * Effort sponsored in part by the Defense Advanced Research Projects 679 * Agency (DARPA) and Air Force Research Laboratory, Air Force 680 * Materiel Command, USAF, under agreement number F30602-01-2-0537. 681 * 682 */ 683 684 #define RDE_FILTER_SET_SKIP_STEPS(i) \ 685 do { \ 686 while (head[i] != cur) { \ 687 head[i]->skip[i].ptr = cur; \ 688 head[i] = TAILQ_NEXT(head[i], entry); \ 689 } \ 690 } while (0) 691 692 struct peer; 693 void print_rule(struct peer *, struct filter_rule *); 694 695 void 696 rde_filter_calc_skip_steps(struct filter_head *rules) 697 { 698 struct filter_rule *cur, *prev, *head[RDE_FILTER_SKIP_COUNT]; 699 int i; 700 701 if (rules == NULL) 702 return; 703 704 cur = TAILQ_FIRST(rules); 705 706 prev = cur; 707 for (i = 0; i < RDE_FILTER_SKIP_COUNT; ++i) 708 head[i] = cur; 709 while (cur != NULL) { 710 if (cur->peer.groupid != prev->peer.groupid) 711 RDE_FILTER_SET_SKIP_STEPS(RDE_FILTER_SKIP_GROUPID); 712 if (cur->peer.remote_as != prev->peer.remote_as) 713 RDE_FILTER_SET_SKIP_STEPS(RDE_FILTER_SKIP_REMOTE_AS); 714 if (cur->peer.peerid != prev->peer.peerid) 715 RDE_FILTER_SET_SKIP_STEPS(RDE_FILTER_SKIP_PEERID); 716 prev = cur; 717 cur = TAILQ_NEXT(cur, entry); 718 } 719 for (i = 0; i < RDE_FILTER_SKIP_COUNT; ++i) 720 RDE_FILTER_SET_SKIP_STEPS(i); 721 722 } 723 724 #define RDE_FILTER_TEST_ATTRIB(t, a) \ 725 do { \ 726 if (t) { \ 727 f = a; \ 728 goto nextrule; \ 729 } \ 730 } while (0) 731 732 enum filter_actions 733 rde_filter(struct filter_head *rules, struct rde_aspath **new, 734 struct rde_peer *peer, struct rde_aspath *asp, struct bgpd_addr *prefix, 735 u_int8_t prefixlen, struct rde_peer *from) 736 { 737 struct filter_rule *f; 738 enum filter_actions action = ACTION_ALLOW; /* default allow */ 739 740 if (new != NULL) 741 *new = NULL; 742 743 if (asp->flags & F_ATTR_PARSE_ERR) 744 /* 745 * don't try to filter bad updates just deny them 746 * so they act as implicit withdraws 747 */ 748 return (ACTION_DENY); 749 750 if (rules == NULL) 751 return (action); 752 753 f = TAILQ_FIRST(rules); 754 while (f != NULL) { 755 RDE_FILTER_TEST_ATTRIB( 756 (f->peer.groupid && 757 f->peer.groupid != peer->conf.groupid), 758 f->skip[RDE_FILTER_SKIP_GROUPID].ptr); 759 RDE_FILTER_TEST_ATTRIB( 760 (f->peer.remote_as && 761 f->peer.remote_as != peer->conf.remote_as), 762 f->skip[RDE_FILTER_SKIP_REMOTE_AS].ptr); 763 RDE_FILTER_TEST_ATTRIB( 764 (f->peer.peerid && 765 f->peer.peerid != peer->conf.id), 766 f->skip[RDE_FILTER_SKIP_PEERID].ptr); 767 if (rde_filter_match(f, asp, prefix, prefixlen, peer, from)) { 768 if (asp != NULL && new != NULL) { 769 /* asp may get modified so create a copy */ 770 if (*new == NULL) { 771 *new = path_copy(asp); 772 /* ... and use the copy from now on */ 773 asp = *new; 774 } 775 rde_apply_set(asp, &f->set, prefix->aid, 776 from, peer); 777 } 778 if (f->action != ACTION_NONE) 779 action = f->action; 780 if (f->quick) 781 return (action); 782 } 783 f = TAILQ_NEXT(f, entry); 784 nextrule: ; 785 } 786 return (action); 787 } 788