rde_filter.c revision 1.103
1/* $OpenBSD: rde_filter.c,v 1.103 2018/09/07 10:49:22 claudio Exp $ */ 2 3/* 4 * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> 5 * Copyright (c) 2016 Job Snijders <job@instituut.net> 6 * Copyright (c) 2016 Peter Hessler <phessler@openbsd.org> 7 * Copyright (c) 2018 Sebastian Benoit <benno@openbsd.org> 8 * 9 * Permission to use, copy, modify, and distribute this software for any 10 * purpose with or without fee is hereby granted, provided that the above 11 * copyright notice and this permission notice appear in all copies. 12 * 13 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 19 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 20 */ 21#include <sys/types.h> 22#include <sys/queue.h> 23 24#include <limits.h> 25#include <stdlib.h> 26#include <string.h> 27 28#include "bgpd.h" 29#include "rde.h" 30#include "log.h" 31 32int rde_filter_match(struct filter_rule *, struct rde_peer *, 33 struct filterstate *, struct prefix *); 34int rde_prefix_match(struct filter_prefix *, struct prefix *); 35int filterset_equal(struct filter_set_head *, struct filter_set_head *); 36 37void 38rde_apply_set(struct filter_set_head *sh, struct filterstate *state, 39 u_int8_t aid, struct rde_peer *from, struct rde_peer *peer) 40{ 41 struct filter_set *set; 42 u_char *np; 43 int as, type; 44 int64_t las, ld1, ld2; 45 u_int32_t prep_as; 46 u_int16_t nl; 47 u_int8_t prepend; 48 49 if (state == NULL) 50 return; 51 52 TAILQ_FOREACH(set, sh, entry) { 53 switch (set->type) { 54 case ACTION_SET_LOCALPREF: 55 state->aspath.lpref = set->action.metric; 56 break; 57 case ACTION_SET_RELATIVE_LOCALPREF: 58 if (set->action.relative > 0) { 59 if (set->action.relative + state->aspath.lpref < 60 state->aspath.lpref) 61 state->aspath.lpref = UINT_MAX; 62 else 63 state->aspath.lpref += 64 set->action.relative; 65 } else { 66 if ((u_int32_t)-set->action.relative > 67 state->aspath.lpref) 68 state->aspath.lpref = 0; 69 else 70 state->aspath.lpref += 71 set->action.relative; 72 } 73 break; 74 case ACTION_SET_MED: 75 state->aspath.flags |= F_ATTR_MED | F_ATTR_MED_ANNOUNCE; 76 state->aspath.med = set->action.metric; 77 break; 78 case ACTION_SET_RELATIVE_MED: 79 state->aspath.flags |= F_ATTR_MED | F_ATTR_MED_ANNOUNCE; 80 if (set->action.relative > 0) { 81 if (set->action.relative + state->aspath.med < 82 state->aspath.med) 83 state->aspath.med = UINT_MAX; 84 else 85 state->aspath.med += 86 set->action.relative; 87 } else { 88 if ((u_int32_t)-set->action.relative > 89 state->aspath.med) 90 state->aspath.med = 0; 91 else 92 state->aspath.med += 93 set->action.relative; 94 } 95 break; 96 case ACTION_SET_WEIGHT: 97 state->aspath.weight = set->action.metric; 98 break; 99 case ACTION_SET_RELATIVE_WEIGHT: 100 if (set->action.relative > 0) { 101 if (set->action.relative + state->aspath.weight < 102 state->aspath.weight) 103 state->aspath.weight = UINT_MAX; 104 else 105 state->aspath.weight += 106 set->action.relative; 107 } else { 108 if ((u_int32_t)-set->action.relative > 109 state->aspath.weight) 110 state->aspath.weight = 0; 111 else 112 state->aspath.weight += 113 set->action.relative; 114 } 115 break; 116 case ACTION_SET_PREPEND_SELF: 117 prep_as = peer->conf.local_as; 118 prepend = set->action.prepend; 119 np = aspath_prepend(state->aspath.aspath, prep_as, 120 prepend, &nl); 121 aspath_put(state->aspath.aspath); 122 state->aspath.aspath = aspath_get(np, nl); 123 free(np); 124 break; 125 case ACTION_SET_PREPEND_PEER: 126 if (from == NULL) 127 break; 128 prep_as = from->conf.remote_as; 129 prepend = set->action.prepend; 130 np = aspath_prepend(state->aspath.aspath, prep_as, 131 prepend, &nl); 132 aspath_put(state->aspath.aspath); 133 state->aspath.aspath = aspath_get(np, nl); 134 free(np); 135 break; 136 case ACTION_SET_NEXTHOP: 137 case ACTION_SET_NEXTHOP_REJECT: 138 case ACTION_SET_NEXTHOP_BLACKHOLE: 139 case ACTION_SET_NEXTHOP_NOMODIFY: 140 case ACTION_SET_NEXTHOP_SELF: 141 nexthop_modify(set->action.nh, set->type, aid, 142 &state->nexthop, &state->nhflags); 143 break; 144 case ACTION_SET_COMMUNITY: 145 switch (set->action.community.as) { 146 case COMMUNITY_ERROR: 147 case COMMUNITY_ANY: 148 fatalx("rde_apply_set bad community string"); 149 case COMMUNITY_NEIGHBOR_AS: 150 as = peer->conf.remote_as; 151 break; 152 case COMMUNITY_LOCAL_AS: 153 as = peer->conf.local_as; 154 break; 155 default: 156 as = set->action.community.as; 157 break; 158 } 159 160 switch (set->action.community.type) { 161 case COMMUNITY_ERROR: 162 case COMMUNITY_ANY: 163 fatalx("rde_apply_set bad community string"); 164 case COMMUNITY_NEIGHBOR_AS: 165 type = peer->conf.remote_as; 166 break; 167 case COMMUNITY_LOCAL_AS: 168 type = peer->conf.local_as; 169 break; 170 default: 171 type = set->action.community.type; 172 break; 173 } 174 175 community_set(&state->aspath, as, type); 176 break; 177 case ACTION_DEL_COMMUNITY: 178 switch (set->action.community.as) { 179 case COMMUNITY_ERROR: 180 fatalx("rde_apply_set bad community string"); 181 case COMMUNITY_NEIGHBOR_AS: 182 as = peer->conf.remote_as; 183 break; 184 case COMMUNITY_LOCAL_AS: 185 as = peer->conf.local_as; 186 break; 187 case COMMUNITY_ANY: 188 default: 189 as = set->action.community.as; 190 break; 191 } 192 193 switch (set->action.community.type) { 194 case COMMUNITY_ERROR: 195 fatalx("rde_apply_set bad community string"); 196 case COMMUNITY_NEIGHBOR_AS: 197 type = peer->conf.remote_as; 198 break; 199 case COMMUNITY_LOCAL_AS: 200 type = peer->conf.local_as; 201 break; 202 case COMMUNITY_ANY: 203 default: 204 type = set->action.community.type; 205 break; 206 } 207 208 community_delete(&state->aspath, as, type); 209 break; 210 case ACTION_SET_LARGE_COMMUNITY: 211 switch (set->action.large_community.as) { 212 case COMMUNITY_ERROR: 213 fatalx("rde_apply_set bad large community string"); 214 case COMMUNITY_NEIGHBOR_AS: 215 las = peer->conf.remote_as; 216 break; 217 case COMMUNITY_LOCAL_AS: 218 las = peer->conf.local_as; 219 break; 220 case COMMUNITY_ANY: 221 default: 222 las = set->action.large_community.as; 223 break; 224 } 225 226 switch (set->action.large_community.ld1) { 227 case COMMUNITY_ERROR: 228 fatalx("rde_apply_set bad large community string"); 229 case COMMUNITY_NEIGHBOR_AS: 230 ld1 = peer->conf.remote_as; 231 break; 232 case COMMUNITY_LOCAL_AS: 233 ld1 = peer->conf.local_as; 234 break; 235 case COMMUNITY_ANY: 236 default: 237 ld1 = set->action.large_community.ld1; 238 break; 239 } 240 241 switch (set->action.large_community.ld2) { 242 case COMMUNITY_ERROR: 243 fatalx("rde_apply_set bad large community string"); 244 case COMMUNITY_NEIGHBOR_AS: 245 ld2 = peer->conf.remote_as; 246 break; 247 case COMMUNITY_LOCAL_AS: 248 ld2 = peer->conf.local_as; 249 break; 250 case COMMUNITY_ANY: 251 default: 252 ld2 = set->action.large_community.ld2; 253 break; 254 } 255 256 community_large_set(&state->aspath, las, ld1, ld2); 257 break; 258 case ACTION_DEL_LARGE_COMMUNITY: 259 switch (set->action.large_community.as) { 260 case COMMUNITY_ERROR: 261 fatalx("rde_apply_set bad large community string"); 262 case COMMUNITY_NEIGHBOR_AS: 263 las = peer->conf.remote_as; 264 break; 265 case COMMUNITY_LOCAL_AS: 266 las = peer->conf.local_as; 267 break; 268 case COMMUNITY_ANY: 269 default: 270 las = set->action.large_community.as; 271 break; 272 } 273 274 switch (set->action.large_community.ld1) { 275 case COMMUNITY_ERROR: 276 fatalx("rde_apply_set bad large community string"); 277 case COMMUNITY_NEIGHBOR_AS: 278 ld1 = peer->conf.remote_as; 279 break; 280 case COMMUNITY_LOCAL_AS: 281 ld1 = peer->conf.local_as; 282 break; 283 case COMMUNITY_ANY: 284 default: 285 ld1 = set->action.large_community.ld1; 286 break; 287 } 288 289 switch (set->action.large_community.ld2) { 290 case COMMUNITY_ERROR: 291 fatalx("rde_apply_set bad large community string"); 292 case COMMUNITY_NEIGHBOR_AS: 293 ld2 = peer->conf.remote_as; 294 break; 295 case COMMUNITY_LOCAL_AS: 296 ld2 = peer->conf.local_as; 297 break; 298 case COMMUNITY_ANY: 299 default: 300 ld2 = set->action.large_community.ld2; 301 break; 302 } 303 304 community_large_delete(&state->aspath, las, ld1, ld2); 305 break; 306 case ACTION_PFTABLE: 307 /* convert pftable name to an id */ 308 set->action.id = pftable_name2id(set->action.pftable); 309 set->type = ACTION_PFTABLE_ID; 310 /* FALLTHROUGH */ 311 case ACTION_PFTABLE_ID: 312 pftable_unref(state->aspath.pftableid); 313 state->aspath.pftableid = pftable_ref(set->action.id); 314 break; 315 case ACTION_RTLABEL: 316 /* convert the route label to an id for faster access */ 317 set->action.id = rtlabel_name2id(set->action.rtlabel); 318 set->type = ACTION_RTLABEL_ID; 319 /* FALLTHROUGH */ 320 case ACTION_RTLABEL_ID: 321 rtlabel_unref(state->aspath.rtlabelid); 322 state->aspath.rtlabelid = rtlabel_ref(set->action.id); 323 break; 324 case ACTION_SET_ORIGIN: 325 state->aspath.origin = set->action.origin; 326 break; 327 case ACTION_SET_EXT_COMMUNITY: 328 community_ext_set(&state->aspath, &set->action.ext_community, 329 peer->conf.remote_as); 330 break; 331 case ACTION_DEL_EXT_COMMUNITY: 332 community_ext_delete(&state->aspath, &set->action.ext_community, 333 peer->conf.remote_as); 334 break; 335 } 336 } 337} 338 339int 340rde_filter_match(struct filter_rule *f, struct rde_peer *peer, 341 struct filterstate *state, struct prefix *p) 342{ 343 int cas, type; 344 int64_t las, ld1, ld2; 345 struct rde_aspath *asp = NULL; 346 347 if (state != NULL) 348 asp = &state->aspath; 349 350 if (f->peer.ebgp && !peer->conf.ebgp) 351 return (0); 352 if (f->peer.ibgp && peer->conf.ebgp) 353 return (0); 354 355 if (asp != NULL && f->match.as.type != AS_NONE) { 356 if (aspath_match(asp->aspath->data, asp->aspath->len, 357 &f->match.as, peer->conf.remote_as) == 0) 358 return (0); 359 } 360 361 if (asp != NULL && f->match.aslen.type != ASLEN_NONE) 362 if (aspath_lenmatch(asp->aspath, f->match.aslen.type, 363 f->match.aslen.aslen) == 0) 364 return (0); 365 366 if (asp != NULL && f->match.community.as != COMMUNITY_UNSET) { 367 switch (f->match.community.as) { 368 case COMMUNITY_ERROR: 369 fatalx("rde_filter_match bad community string"); 370 case COMMUNITY_NEIGHBOR_AS: 371 cas = peer->conf.remote_as; 372 break; 373 case COMMUNITY_LOCAL_AS: 374 cas = peer->conf.local_as; 375 break; 376 default: 377 cas = f->match.community.as; 378 break; 379 } 380 381 switch (f->match.community.type) { 382 case COMMUNITY_ERROR: 383 fatalx("rde_filter_match bad community string"); 384 case COMMUNITY_NEIGHBOR_AS: 385 type = peer->conf.remote_as; 386 break; 387 case COMMUNITY_LOCAL_AS: 388 type = peer->conf.local_as; 389 break; 390 default: 391 type = f->match.community.type; 392 break; 393 } 394 395 if (community_match(asp, cas, type) == 0) 396 return (0); 397 } 398 if (asp != NULL && 399 (f->match.ext_community.flags & EXT_COMMUNITY_FLAG_VALID)) 400 if (community_ext_match(asp, &f->match.ext_community, 401 peer->conf.remote_as) == 0) 402 return (0); 403 if (asp != NULL && f->match.large_community.as != 404 COMMUNITY_UNSET) { 405 switch (f->match.large_community.as) { 406 case COMMUNITY_ERROR: 407 fatalx("rde_filter_match bad community string"); 408 case COMMUNITY_NEIGHBOR_AS: 409 las = peer->conf.remote_as; 410 break; 411 case COMMUNITY_LOCAL_AS: 412 las = peer->conf.local_as; 413 break; 414 default: 415 las = f->match.large_community.as; 416 break; 417 } 418 419 switch (f->match.large_community.ld1) { 420 case COMMUNITY_ERROR: 421 fatalx("rde_filter_match bad community string"); 422 case COMMUNITY_NEIGHBOR_AS: 423 ld1 = peer->conf.remote_as; 424 break; 425 case COMMUNITY_LOCAL_AS: 426 ld1 = peer->conf.local_as; 427 break; 428 default: 429 ld1 = f->match.large_community.ld1; 430 break; 431 } 432 433 switch (f->match.large_community.ld2) { 434 case COMMUNITY_ERROR: 435 fatalx("rde_filter_match bad community string"); 436 case COMMUNITY_NEIGHBOR_AS: 437 ld2 = peer->conf.remote_as; 438 break; 439 case COMMUNITY_LOCAL_AS: 440 ld2 = peer->conf.local_as; 441 break; 442 default: 443 ld2 = f->match.large_community.ld2; 444 break; 445 } 446 447 if (community_large_match(asp, las, ld1, ld2) == 0) 448 return (0); 449 } 450 451 if (state != NULL && f->match.nexthop.flags != 0) { 452 struct bgpd_addr *nexthop, *cmpaddr; 453 if (state->nexthop == NULL) 454 /* no nexthop, skip */ 455 return (0); 456 nexthop = &state->nexthop->exit_nexthop; 457 if (f->match.nexthop.flags == FILTER_NEXTHOP_ADDR) 458 cmpaddr = &f->match.nexthop.addr; 459 else 460 cmpaddr = &prefix_peer(p)->remote_addr; 461 if (cmpaddr->aid != nexthop->aid) 462 /* don't use IPv4 rules for IPv6 and vice versa */ 463 return (0); 464 465 switch (cmpaddr->aid) { 466 case AID_INET: 467 if (cmpaddr->v4.s_addr != nexthop->v4.s_addr) 468 return (0); 469 break; 470 case AID_INET6: 471 if (memcmp(&cmpaddr->v6, &nexthop->v6, 472 sizeof(struct in6_addr))) 473 return (0); 474 break; 475 default: 476 fatalx("King Bula lost in address space"); 477 } 478 } 479 480 /* 481 * XXX must be second to last because we unconditionally return here. 482 * prefixset and prefix filter rules are mutual exclusive 483 */ 484 if (f->match.prefixset.flags != 0) { 485 struct bgpd_addr addr, *prefix = &addr; 486 u_int8_t plen; 487 488 pt_getaddr(p->re->prefix, prefix); 489 plen = p->re->prefix->prefixlen; 490 491 if (f->match.prefixset.ps == NULL || 492 !trie_match(&f->match.prefixset.ps->th, prefix, plen)) 493 return (0); 494 } else if (f->match.prefix.addr.aid != 0) 495 return (rde_prefix_match(&f->match.prefix, p)); 496 497 /* matched somewhen or is anymatch rule */ 498 return (1); 499} 500 501/* return 1 when prefix matches filter_prefix, 0 if not */ 502int 503rde_prefix_match(struct filter_prefix *fp, struct prefix *p) 504{ 505 struct bgpd_addr addr, *prefix = &addr; 506 u_int8_t plen; 507 508 pt_getaddr(p->re->prefix, prefix); 509 plen = p->re->prefix->prefixlen; 510 511 if (fp->addr.aid != prefix->aid) 512 /* don't use IPv4 rules for IPv6 and vice versa */ 513 return (0); 514 515 if (prefix_compare(prefix, &fp->addr, fp->len)) 516 return (0); 517 518 /* test prefixlen stuff too */ 519 switch (fp->op) { 520 case OP_NONE: /* perfect match */ 521 return (plen == fp->len); 522 case OP_EQ: 523 return (plen == fp->len_min); 524 case OP_NE: 525 return (plen != fp->len_min); 526 case OP_RANGE: 527 return ((plen >= fp->len_min) && 528 (plen <= fp->len_max)); 529 case OP_XRANGE: 530 return ((plen < fp->len_min) || 531 (plen > fp->len_max)); 532 default: 533 log_warnx("%s: unsupported prefix operation", __func__); 534 return (0); 535 } 536} 537 538/* return true when the rule f can never match for this peer */ 539static int 540rde_filter_skip_rule(struct rde_peer *peer, struct filter_rule *f) 541{ 542 /* if any of the two is unset then rule can't be skipped */ 543 if (peer == NULL || f == NULL) 544 return (0); 545 546 if (f->peer.groupid != 0 && 547 f->peer.groupid != peer->conf.groupid) 548 return (1); 549 550 if (f->peer.peerid != 0 && 551 f->peer.peerid != peer->conf.id) 552 return (1); 553 554 if (f->peer.remote_as != 0 && 555 f->peer.remote_as != peer->conf.remote_as) 556 return (1); 557 558 if (f->peer.ebgp != 0 && 559 f->peer.ebgp != peer->conf.ebgp) 560 return (1); 561 562 if (f->peer.ibgp != 0 && 563 f->peer.ibgp != !peer->conf.ebgp) 564 return (1); 565 566 return (0); 567} 568 569int 570rde_filter_equal(struct filter_head *a, struct filter_head *b, 571 struct rde_peer *peer) 572{ 573 struct filter_rule *fa, *fb; 574 struct rde_prefixset *psa, *psb; 575 struct as_set *asa, *asb; 576 577 fa = a ? TAILQ_FIRST(a) : NULL; 578 fb = b ? TAILQ_FIRST(b) : NULL; 579 580 while (fa != NULL || fb != NULL) { 581 /* skip all rules with wrong peer */ 582 if (rde_filter_skip_rule(peer, fa)) { 583 fa = TAILQ_NEXT(fa, entry); 584 continue; 585 } 586 if (rde_filter_skip_rule(peer, fb)) { 587 fb = TAILQ_NEXT(fb, entry); 588 continue; 589 } 590 591 /* compare the two rules */ 592 if ((fa == NULL && fb != NULL) || (fa != NULL && fb == NULL)) 593 /* new rule added or removed */ 594 return (0); 595 596 if (fa->action != fb->action || fa->quick != fb->quick) 597 return (0); 598 if (memcmp(&fa->peer, &fb->peer, sizeof(fa->peer))) 599 return (0); 600 601 /* compare filter_rule.match without the prefixset pointer */ 602 psa = fa->match.prefixset.ps; 603 psb = fb->match.prefixset.ps; 604 asa = fa->match.as.aset; 605 asb = fb->match.as.aset; 606 fa->match.prefixset.ps = fb->match.prefixset.ps = NULL; 607 fa->match.as.aset = fb->match.as.aset = NULL; 608 if (memcmp(&fa->match, &fb->match, sizeof(fa->match))) 609 return (0); 610 fa->match.prefixset.ps = psa; 611 fb->match.prefixset.ps = psb; 612 fa->match.as.aset = asa; 613 fb->match.as.aset = asb; 614 615 if (fa->match.prefixset.flags != 0 && 616 fa->match.prefixset.ps != NULL && 617 fa->match.prefixset.ps->dirty) { 618 log_debug("%s: prefixset %s has changed", 619 __func__, fa->match.prefixset.name); 620 return (0); 621 } 622 623 if ((fa->match.as.flags & AS_FLAG_AS_SET) && 624 as_set_dirty(fa->match.as.aset)) { 625 log_debug("%s: as-set %s has changed", 626 __func__, fa->match.as.name); 627 return (0); 628 } 629 630 if (!filterset_equal(&fa->set, &fb->set)) 631 return (0); 632 633 fa = TAILQ_NEXT(fa, entry); 634 fb = TAILQ_NEXT(fb, entry); 635 } 636 return (1); 637} 638 639void 640rde_filterstate_prep(struct filterstate *state, struct rde_aspath *asp, 641 struct nexthop *nh, u_int8_t nhflags) 642{ 643 memset(state, 0, sizeof(*state)); 644 645 path_prep(&state->aspath); 646 if (asp) 647 path_copy(&state->aspath, asp); 648 state->nexthop = nexthop_ref(nh); 649 state->nhflags = nhflags; 650} 651 652void 653rde_filterstate_clean(struct filterstate *state) 654{ 655 path_clean(&state->aspath); 656 nexthop_put(state->nexthop); 657 state->nexthop = NULL; 658} 659 660void 661filterlist_free(struct filter_head *fh) 662{ 663 struct filter_rule *r; 664 665 if (fh == NULL) 666 return; 667 668 while ((r = TAILQ_FIRST(fh)) != NULL) { 669 TAILQ_REMOVE(fh, r, entry); 670 filterset_free(&r->set); 671 free(r); 672 } 673 free(fh); 674} 675 676/* free a filterset and take care of possible name2id references */ 677void 678filterset_free(struct filter_set_head *sh) 679{ 680 struct filter_set *s; 681 682 if (sh == NULL) 683 return; 684 685 while ((s = TAILQ_FIRST(sh)) != NULL) { 686 TAILQ_REMOVE(sh, s, entry); 687 if (s->type == ACTION_RTLABEL_ID) 688 rtlabel_unref(s->action.id); 689 else if (s->type == ACTION_PFTABLE_ID) 690 pftable_unref(s->action.id); 691 else if (s->type == ACTION_SET_NEXTHOP && 692 bgpd_process == PROC_RDE) 693 nexthop_put(s->action.nh); 694 free(s); 695 } 696} 697 698/* 699 * this function is a bit more complicated than a memcmp() because there are 700 * types that need to be considered equal e.g. ACTION_SET_MED and 701 * ACTION_SET_RELATIVE_MED. Also ACTION_SET_COMMUNITY and ACTION_SET_NEXTHOP 702 * need some special care. It only checks the types and not the values so 703 * it does not do a real compare. 704 */ 705int 706filterset_cmp(struct filter_set *a, struct filter_set *b) 707{ 708 if (strcmp(filterset_name(a->type), filterset_name(b->type))) 709 return (a->type - b->type); 710 711 if (a->type == ACTION_SET_COMMUNITY || 712 a->type == ACTION_DEL_COMMUNITY) { /* a->type == b->type */ 713 /* compare community */ 714 if (a->action.community.as - b->action.community.as != 0) 715 return (a->action.community.as - 716 b->action.community.as); 717 return (a->action.community.type - b->action.community.type); 718 } 719 720 if (a->type == ACTION_SET_EXT_COMMUNITY || 721 a->type == ACTION_DEL_EXT_COMMUNITY) { /* a->type == b->type */ 722 return (memcmp(&a->action.ext_community, 723 &b->action.ext_community, sizeof(a->action.ext_community))); 724 } 725 726 if (a->type == ACTION_SET_LARGE_COMMUNITY || 727 a->type == ACTION_DEL_LARGE_COMMUNITY) { /* a->type == b->type */ 728 /* compare community */ 729 if (a->action.large_community.as - 730 b->action.large_community.as != 0) 731 return (a->action.large_community.as - 732 b->action.large_community.as); 733 if (a->action.large_community.ld1 - 734 b->action.large_community.ld1 != 0) 735 return (a->action.large_community.ld1 - 736 b->action.large_community.ld1); 737 return (a->action.large_community.ld2 - 738 b->action.large_community.ld2); 739 } 740 741 if (a->type == ACTION_SET_NEXTHOP && b->type == ACTION_SET_NEXTHOP) { 742 /* 743 * This is the only interesting case, all others are considered 744 * equal. It does not make sense to e.g. set a nexthop and 745 * reject it at the same time. Allow one IPv4 and one IPv6 746 * per filter set or only one of the other nexthop modifiers. 747 */ 748 return (a->action.nexthop.aid - b->action.nexthop.aid); 749 } 750 751 /* equal */ 752 return (0); 753} 754 755void 756filterset_move(struct filter_set_head *source, struct filter_set_head *dest) 757{ 758 struct filter_set *s; 759 760 TAILQ_INIT(dest); 761 762 if (source == NULL) 763 return; 764 765 while ((s = TAILQ_FIRST(source)) != NULL) { 766 TAILQ_REMOVE(source, s, entry); 767 TAILQ_INSERT_TAIL(dest, s, entry); 768 } 769} 770 771int 772filterset_equal(struct filter_set_head *ah, struct filter_set_head *bh) 773{ 774 struct filter_set *a, *b; 775 const char *as, *bs; 776 777 for (a = TAILQ_FIRST(ah), b = TAILQ_FIRST(bh); 778 a != NULL && b != NULL; 779 a = TAILQ_NEXT(a, entry), b = TAILQ_NEXT(b, entry)) { 780 switch (a->type) { 781 case ACTION_SET_PREPEND_SELF: 782 case ACTION_SET_PREPEND_PEER: 783 if (a->type == b->type && 784 a->action.prepend == b->action.prepend) 785 continue; 786 break; 787 case ACTION_SET_LOCALPREF: 788 case ACTION_SET_MED: 789 case ACTION_SET_WEIGHT: 790 if (a->type == b->type && 791 a->action.metric == b->action.metric) 792 continue; 793 break; 794 case ACTION_SET_RELATIVE_LOCALPREF: 795 case ACTION_SET_RELATIVE_MED: 796 case ACTION_SET_RELATIVE_WEIGHT: 797 if (a->type == b->type && 798 a->action.relative == b->action.relative) 799 continue; 800 break; 801 case ACTION_SET_NEXTHOP: 802 if (a->type == b->type && 803 memcmp(&a->action.nexthop, &b->action.nexthop, 804 sizeof(a->action.nexthop)) == 0) 805 continue; 806 break; 807 case ACTION_SET_NEXTHOP_BLACKHOLE: 808 case ACTION_SET_NEXTHOP_REJECT: 809 case ACTION_SET_NEXTHOP_NOMODIFY: 810 case ACTION_SET_NEXTHOP_SELF: 811 if (a->type == b->type) 812 continue; 813 break; 814 case ACTION_DEL_COMMUNITY: 815 case ACTION_SET_COMMUNITY: 816 if (a->type == b->type && 817 memcmp(&a->action.community, &b->action.community, 818 sizeof(a->action.community)) == 0) 819 continue; 820 break; 821 case ACTION_DEL_LARGE_COMMUNITY: 822 case ACTION_SET_LARGE_COMMUNITY: 823 if (a->type == b->type && 824 memcmp(&a->action.large_community, 825 &b->action.large_community, 826 sizeof(a->action.large_community)) == 0) 827 continue; 828 break; 829 case ACTION_PFTABLE: 830 case ACTION_PFTABLE_ID: 831 if (b->type == ACTION_PFTABLE) 832 bs = b->action.pftable; 833 else if (b->type == ACTION_PFTABLE_ID) 834 bs = pftable_id2name(b->action.id); 835 else 836 break; 837 838 if (a->type == ACTION_PFTABLE) 839 as = a->action.pftable; 840 else 841 as = pftable_id2name(a->action.id); 842 843 if (strcmp(as, bs) == 0) 844 continue; 845 break; 846 case ACTION_RTLABEL: 847 case ACTION_RTLABEL_ID: 848 if (b->type == ACTION_RTLABEL) 849 bs = b->action.rtlabel; 850 else if (b->type == ACTION_RTLABEL_ID) 851 bs = rtlabel_id2name(b->action.id); 852 else 853 break; 854 855 if (a->type == ACTION_RTLABEL) 856 as = a->action.rtlabel; 857 else 858 as = rtlabel_id2name(a->action.id); 859 860 if (strcmp(as, bs) == 0) 861 continue; 862 break; 863 case ACTION_SET_ORIGIN: 864 if (a->type == b->type && 865 a->action.origin == b->action.origin) 866 continue; 867 break; 868 case ACTION_SET_EXT_COMMUNITY: 869 case ACTION_DEL_EXT_COMMUNITY: 870 if (a->type == b->type && memcmp( 871 &a->action.ext_community, 872 &b->action.ext_community, 873 sizeof(a->action.ext_community)) == 0) 874 continue; 875 break; 876 } 877 /* compare failed */ 878 return (0); 879 } 880 if (a != NULL || b != NULL) 881 return (0); 882 return (1); 883} 884 885const char * 886filterset_name(enum action_types type) 887{ 888 switch (type) { 889 case ACTION_SET_LOCALPREF: 890 case ACTION_SET_RELATIVE_LOCALPREF: 891 return ("localpref"); 892 case ACTION_SET_MED: 893 case ACTION_SET_RELATIVE_MED: 894 return ("metric"); 895 case ACTION_SET_WEIGHT: 896 case ACTION_SET_RELATIVE_WEIGHT: 897 return ("weight"); 898 case ACTION_SET_PREPEND_SELF: 899 return ("prepend-self"); 900 case ACTION_SET_PREPEND_PEER: 901 return ("prepend-peer"); 902 case ACTION_SET_NEXTHOP: 903 case ACTION_SET_NEXTHOP_REJECT: 904 case ACTION_SET_NEXTHOP_BLACKHOLE: 905 case ACTION_SET_NEXTHOP_NOMODIFY: 906 case ACTION_SET_NEXTHOP_SELF: 907 return ("nexthop"); 908 case ACTION_SET_COMMUNITY: 909 return ("community"); 910 case ACTION_DEL_COMMUNITY: 911 return ("community delete"); 912 case ACTION_SET_LARGE_COMMUNITY: 913 return ("large-community"); 914 case ACTION_DEL_LARGE_COMMUNITY: 915 return ("large-community delete"); 916 case ACTION_PFTABLE: 917 case ACTION_PFTABLE_ID: 918 return ("pftable"); 919 case ACTION_RTLABEL: 920 case ACTION_RTLABEL_ID: 921 return ("rtlabel"); 922 case ACTION_SET_ORIGIN: 923 return ("origin"); 924 case ACTION_SET_EXT_COMMUNITY: 925 return ("ext-community"); 926 case ACTION_DEL_EXT_COMMUNITY: 927 return ("ext-community delete"); 928 } 929 930 fatalx("filterset_name: got lost"); 931} 932 933/* 934 * Copyright (c) 2001 Daniel Hartmeier 935 * All rights reserved. 936 * 937 * Redistribution and use in source and binary forms, with or without 938 * modification, are permitted provided that the following conditions 939 * are met: 940 * 941 * - Redistributions of source code must retain the above copyright 942 * notice, this list of conditions and the following disclaimer. 943 * - Redistributions in binary form must reproduce the above 944 * copyright notice, this list of conditions and the following 945 * disclaimer in the documentation and/or other materials provided 946 * with the distribution. 947 * 948 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 949 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 950 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 951 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 952 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 953 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 954 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 955 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 956 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 957 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 958 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 959 * POSSIBILITY OF SUCH DAMAGE. 960 * 961 * Effort sponsored in part by the Defense Advanced Research Projects 962 * Agency (DARPA) and Air Force Research Laboratory, Air Force 963 * Materiel Command, USAF, under agreement number F30602-01-2-0537. 964 * 965 */ 966 967#define RDE_FILTER_SET_SKIP_STEPS(i) \ 968 do { \ 969 while (head[i] != cur) { \ 970 head[i]->skip[i].ptr = cur; \ 971 head[i] = TAILQ_NEXT(head[i], entry); \ 972 } \ 973 } while (0) 974 975void 976rde_filter_calc_skip_steps(struct filter_head *rules) 977{ 978 struct filter_rule *cur, *prev, *head[RDE_FILTER_SKIP_COUNT]; 979 int i; 980 981 if (rules == NULL) 982 return; 983 984 cur = TAILQ_FIRST(rules); 985 986 prev = cur; 987 for (i = 0; i < RDE_FILTER_SKIP_COUNT; ++i) 988 head[i] = cur; 989 while (cur != NULL) { 990 if (cur->peer.groupid != prev->peer.groupid) 991 RDE_FILTER_SET_SKIP_STEPS(RDE_FILTER_SKIP_GROUPID); 992 if (cur->peer.remote_as != prev->peer.remote_as) 993 RDE_FILTER_SET_SKIP_STEPS(RDE_FILTER_SKIP_REMOTE_AS); 994 if (cur->peer.peerid != prev->peer.peerid) 995 RDE_FILTER_SET_SKIP_STEPS(RDE_FILTER_SKIP_PEERID); 996 prev = cur; 997 cur = TAILQ_NEXT(cur, entry); 998 } 999 for (i = 0; i < RDE_FILTER_SKIP_COUNT; ++i) 1000 RDE_FILTER_SET_SKIP_STEPS(i); 1001 1002} 1003 1004#define RDE_FILTER_TEST_ATTRIB(t, a) \ 1005 do { \ 1006 if (t) { \ 1007 f = a; \ 1008 goto nextrule; \ 1009 } \ 1010 } while (0) 1011 1012enum filter_actions 1013rde_filter(struct filter_head *rules, struct rde_peer *peer, 1014 struct prefix *p, struct filterstate *state) 1015{ 1016 struct filter_rule *f; 1017 enum filter_actions action = ACTION_DENY; /* default deny */ 1018 1019 if (state && state->aspath.flags & F_ATTR_PARSE_ERR) 1020 /* 1021 * don't try to filter bad updates just deny them 1022 * so they act as implicit withdraws 1023 */ 1024 return (ACTION_DENY); 1025 1026 if (rules == NULL) 1027 return (action); 1028 1029 f = TAILQ_FIRST(rules); 1030 while (f != NULL) { 1031 RDE_FILTER_TEST_ATTRIB( 1032 (f->peer.groupid && 1033 f->peer.groupid != peer->conf.groupid), 1034 f->skip[RDE_FILTER_SKIP_GROUPID].ptr); 1035 RDE_FILTER_TEST_ATTRIB( 1036 (f->peer.remote_as && 1037 f->peer.remote_as != peer->conf.remote_as), 1038 f->skip[RDE_FILTER_SKIP_REMOTE_AS].ptr); 1039 RDE_FILTER_TEST_ATTRIB( 1040 (f->peer.peerid && 1041 f->peer.peerid != peer->conf.id), 1042 f->skip[RDE_FILTER_SKIP_PEERID].ptr); 1043 1044 if (rde_filter_match(f, peer, state, p)) { 1045 if (state != NULL) { 1046 rde_apply_set(&f->set, state, 1047 p->re->prefix->aid, prefix_peer(p), peer); 1048 } 1049 if (f->action != ACTION_NONE) 1050 action = f->action; 1051 if (f->quick) 1052 return (action); 1053 } 1054 f = TAILQ_NEXT(f, entry); 1055 nextrule: ; 1056 } 1057 return (action); 1058} 1059