rde_filter.c revision 1.50
1/* $OpenBSD: rde_filter.c,v 1.50 2006/05/28 23:24:15 claudio 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 28int rde_filter_match(struct filter_rule *, struct rde_aspath *, 29 struct bgpd_addr *, u_int8_t, struct rde_peer *); 30int filterset_equal(struct filter_set_head *, struct filter_set_head *); 31 32enum filter_actions 33rde_filter(struct rde_aspath **new, struct filter_head *rules, 34 struct rde_peer *peer, struct rde_aspath *asp, struct bgpd_addr *prefix, 35 u_int8_t prefixlen, struct rde_peer *from, enum directions dir) 36{ 37 struct filter_rule *f; 38 enum filter_actions action = ACTION_ALLOW; /* default allow */ 39 40 if (new != NULL) 41 *new = NULL; 42 43 TAILQ_FOREACH(f, rules, entry) { 44 if (dir != f->dir) 45 continue; 46 if (f->peer.groupid != 0 && 47 f->peer.groupid != peer->conf.groupid) 48 continue; 49 if (f->peer.peerid != 0 && 50 f->peer.peerid != peer->conf.id) 51 continue; 52 if (rde_filter_match(f, asp, prefix, prefixlen, peer)) { 53 if (asp != NULL && new != NULL) { 54 /* asp may get modified so create a copy */ 55 if (*new == NULL) { 56 *new = path_copy(asp); 57 /* ... and use the copy from now on */ 58 asp = *new; 59 } 60 rde_apply_set(asp, &f->set, prefix->af, 61 from, peer); 62 } 63 if (f->action != ACTION_NONE) 64 action = f->action; 65 if (f->quick) 66 return (action); 67 } 68 } 69 return (action); 70} 71 72void 73rde_apply_set(struct rde_aspath *asp, struct filter_set_head *sh, 74 sa_family_t af, struct rde_peer *from, struct rde_peer *peer) 75{ 76 struct filter_set *set; 77 struct aspath *new; 78 int as, type; 79 u_int16_t prep_as; 80 u_int8_t prepend; 81 82 if (asp == NULL) 83 return; 84 85 TAILQ_FOREACH(set, sh, entry) { 86 switch (set->type) { 87 case ACTION_SET_LOCALPREF: 88 asp->lpref = set->action.metric; 89 break; 90 case ACTION_SET_RELATIVE_LOCALPREF: 91 if (set->action.relative > 0) { 92 if (set->action.relative + asp->lpref < 93 asp->lpref) 94 asp->lpref = UINT_MAX; 95 else 96 asp->lpref += set->action.relative; 97 } else { 98 if ((u_int32_t)-set->action.relative > 99 asp->lpref) 100 asp->lpref = 0; 101 else 102 asp->lpref += set->action.relative; 103 } 104 break; 105 case ACTION_SET_MED: 106 asp->flags |= F_ATTR_MED | F_ATTR_MED_ANNOUNCE; 107 asp->med = set->action.metric; 108 break; 109 case ACTION_SET_RELATIVE_MED: 110 asp->flags |= F_ATTR_MED | F_ATTR_MED_ANNOUNCE; 111 if (set->action.relative > 0) { 112 if (set->action.relative + asp->med < 113 asp->med) 114 asp->med = UINT_MAX; 115 else 116 asp->med += set->action.relative; 117 } else { 118 if ((u_int32_t)-set->action.relative > 119 asp->med) 120 asp->med = 0; 121 else 122 asp->med += set->action.relative; 123 } 124 break; 125 case ACTION_SET_WEIGHT: 126 asp->weight = set->action.metric; 127 break; 128 case ACTION_SET_RELATIVE_WEIGHT: 129 if (set->action.relative > 0) { 130 if (set->action.relative + asp->weight < 131 asp->weight) 132 asp->weight = UINT_MAX; 133 else 134 asp->weight += set->action.relative; 135 } else { 136 if ((u_int32_t)-set->action.relative > 137 asp->weight) 138 asp->weight = 0; 139 else 140 asp->weight += set->action.relative; 141 } 142 break; 143 case ACTION_SET_PREPEND_SELF: 144 as = rde_local_as(); 145 prepend = set->action.prepend; 146 new = aspath_prepend(asp->aspath, as, prepend); 147 aspath_put(asp->aspath); 148 asp->aspath = new; 149 break; 150 case ACTION_SET_PREPEND_PEER: 151 if (from == NULL) 152 break; 153 prep_as = from->conf.remote_as; 154 prepend = set->action.prepend; 155 new = aspath_prepend(asp->aspath, prep_as, prepend); 156 aspath_put(asp->aspath); 157 asp->aspath = new; 158 break; 159 case ACTION_SET_NEXTHOP: 160 case ACTION_SET_NEXTHOP_REJECT: 161 case ACTION_SET_NEXTHOP_BLACKHOLE: 162 case ACTION_SET_NEXTHOP_NOMODIFY: 163 case ACTION_SET_NEXTHOP_SELF: 164 nexthop_modify(asp, &set->action.nexthop, set->type, 165 af); 166 break; 167 case ACTION_SET_COMMUNITY: 168 switch (set->action.community.as) { 169 case COMMUNITY_ERROR: 170 case COMMUNITY_ANY: 171 fatalx("rde_apply_set bad community string"); 172 case COMMUNITY_NEIGHBOR_AS: 173 as = peer->conf.remote_as; 174 break; 175 default: 176 as = set->action.community.as; 177 break; 178 } 179 180 switch (set->action.community.type) { 181 case COMMUNITY_ERROR: 182 case COMMUNITY_ANY: 183 fatalx("rde_apply_set bad community string"); 184 case COMMUNITY_NEIGHBOR_AS: 185 type = peer->conf.remote_as; 186 break; 187 default: 188 type = set->action.community.type; 189 break; 190 } 191 192 community_set(asp, as, type); 193 break; 194 case ACTION_DEL_COMMUNITY: 195 switch (set->action.community.as) { 196 case COMMUNITY_ERROR: 197 fatalx("rde_apply_set bad community string"); 198 case COMMUNITY_NEIGHBOR_AS: 199 as = peer->conf.remote_as; 200 break; 201 case COMMUNITY_ANY: 202 default: 203 as = set->action.community.as; 204 break; 205 } 206 207 switch (set->action.community.type) { 208 case COMMUNITY_ERROR: 209 fatalx("rde_apply_set bad community string"); 210 case COMMUNITY_NEIGHBOR_AS: 211 type = peer->conf.remote_as; 212 break; 213 case COMMUNITY_ANY: 214 default: 215 type = set->action.community.type; 216 break; 217 } 218 219 community_delete(asp, as, type); 220 break; 221 case ACTION_PFTABLE: 222 /* convert pftable name to an id */ 223 set->action.id = pftable_name2id(set->action.pftable); 224 set->type = ACTION_PFTABLE_ID; 225 /* FALLTHROUGH */ 226 case ACTION_PFTABLE_ID: 227 pftable_unref(asp->pftableid); 228 asp->pftableid = set->action.id; 229 pftable_ref(asp->pftableid); 230 break; 231 case ACTION_RTLABEL: 232 /* convert the route label to an id for faster access */ 233 set->action.id = rtlabel_name2id(set->action.rtlabel); 234 set->type = ACTION_RTLABEL_ID; 235 /* FALLTHROUGH */ 236 case ACTION_RTLABEL_ID: 237 rtlabel_unref(asp->rtlabelid); 238 asp->rtlabelid = set->action.id; 239 rtlabel_ref(asp->rtlabelid); 240 break; 241 } 242 } 243} 244 245int 246rde_filter_match(struct filter_rule *f, struct rde_aspath *asp, 247 struct bgpd_addr *prefix, u_int8_t plen, struct rde_peer *peer) 248{ 249 int as, type; 250 251 if (asp != NULL && f->match.as.type != AS_NONE) 252 if (aspath_match(asp->aspath, f->match.as.type, 253 f->match.as.as) == 0) 254 return (0); 255 256 if (asp != NULL && f->match.community.as != 0) { 257 switch (f->match.community.as) { 258 case COMMUNITY_ERROR: 259 fatalx("rde_apply_set bad community string"); 260 case COMMUNITY_NEIGHBOR_AS: 261 as = peer->conf.remote_as; 262 break; 263 default: 264 as = f->match.community.as; 265 break; 266 } 267 268 switch (f->match.community.type) { 269 case COMMUNITY_ERROR: 270 fatalx("rde_apply_set bad community string"); 271 case COMMUNITY_NEIGHBOR_AS: 272 type = peer->conf.remote_as; 273 break; 274 default: 275 type = f->match.community.type; 276 break; 277 } 278 279 if (rde_filter_community(asp, as, type) == 0) 280 return (0); 281 } 282 283 if (f->match.prefix.addr.af != 0 && 284 f->match.prefix.addr.af == prefix->af) { 285 if (prefix_compare(prefix, &f->match.prefix.addr, 286 f->match.prefix.len)) 287 return (0); 288 289 /* test prefixlen stuff too */ 290 switch (f->match.prefixlen.op) { 291 case OP_NONE: 292 /* perfect match */ 293 return (plen == f->match.prefix.len); 294 case OP_RANGE: 295 return ((plen >= f->match.prefixlen.len_min) && 296 (plen <= f->match.prefixlen.len_max)); 297 case OP_XRANGE: 298 return ((plen < f->match.prefixlen.len_min) || 299 (plen > f->match.prefixlen.len_max)); 300 case OP_EQ: 301 return (plen == f->match.prefixlen.len_min); 302 case OP_NE: 303 return (plen != f->match.prefixlen.len_min); 304 case OP_LE: 305 return (plen <= f->match.prefixlen.len_min); 306 case OP_LT: 307 return (plen < f->match.prefixlen.len_min); 308 case OP_GE: 309 return (plen >= f->match.prefixlen.len_min); 310 case OP_GT: 311 return (plen > f->match.prefixlen.len_min); 312 } 313 /* NOTREACHED */ 314 } else if (f->match.prefixlen.op != OP_NONE) { 315 /* only prefixlen without a prefix */ 316 317 if (f->match.prefixlen.af != prefix->af) 318 /* don't use IPv4 rules for IPv6 and vice versa */ 319 return (0); 320 321 switch (f->match.prefixlen.op) { 322 case OP_NONE: 323 fatalx("internal filter bug"); 324 case OP_RANGE: 325 return ((plen >= f->match.prefixlen.len_min) && 326 (plen <= f->match.prefixlen.len_max)); 327 case OP_XRANGE: 328 return ((plen < f->match.prefixlen.len_min) || 329 (plen > f->match.prefixlen.len_max)); 330 case OP_EQ: 331 return (plen == f->match.prefixlen.len_min); 332 case OP_NE: 333 return (plen != f->match.prefixlen.len_min); 334 case OP_LE: 335 return (plen <= f->match.prefixlen.len_min); 336 case OP_LT: 337 return (plen < f->match.prefixlen.len_min); 338 case OP_GE: 339 return (plen >= f->match.prefixlen.len_min); 340 case OP_GT: 341 return (plen > f->match.prefixlen.len_min); 342 } 343 /* NOTREACHED */ 344 } 345 346 /* matched somewhen or is anymatch rule */ 347 return (1); 348} 349 350int 351rde_filter_community(struct rde_aspath *asp, int as, int type) 352{ 353 struct attr *a; 354 355 a = attr_optget(asp, ATTR_COMMUNITIES); 356 if (a == NULL) 357 /* no communities, no match */ 358 return (0); 359 360 return (community_match(a->data, a->len, as, type)); 361} 362 363int 364rde_filter_equal(struct filter_head *a, struct filter_head *b, 365 struct rde_peer *peer, enum directions dir) 366{ 367 struct filter_rule *fa, *fb; 368 369 fa = TAILQ_FIRST(a); 370 fb = TAILQ_FIRST(b); 371 372 while (fa != NULL || fb != NULL) { 373 /* skip all rules with wrong direction */ 374 if (fa != NULL && dir != fa->dir) { 375 fa = TAILQ_NEXT(fa, entry); 376 continue; 377 } 378 if (fb != NULL && dir != fb->dir) { 379 fb = TAILQ_NEXT(fb, entry); 380 continue; 381 } 382 383 /* skip all rules with wrong peer */ 384 if (fa != NULL && fa->peer.groupid != 0 && 385 fa->peer.groupid != peer->conf.groupid) { 386 fa = TAILQ_NEXT(fa, entry); 387 continue; 388 } 389 if (fa != NULL && fa->peer.peerid != 0 && 390 fa->peer.peerid != peer->conf.id) { 391 fa = TAILQ_NEXT(fa, entry); 392 continue; 393 } 394 395 if (fb != NULL && fb->peer.groupid != 0 && 396 fb->peer.groupid != peer->conf.groupid) { 397 fb = TAILQ_NEXT(fb, entry); 398 continue; 399 } 400 if (fb != NULL && fb->peer.peerid != 0 && 401 fb->peer.peerid != peer->conf.id) { 402 fb = TAILQ_NEXT(fb, entry); 403 continue; 404 } 405 406 /* compare the two rules */ 407 if ((fa == NULL && fb != NULL) || (fa != NULL && fb == NULL)) 408 /* new rule added or removed */ 409 return (0); 410 411 if (fa->action != fb->action || fa->quick != fb->quick) 412 return (0); 413 if (memcmp(&fa->peer, &fb->peer, sizeof(fa->peer))) 414 return (0); 415 if (memcmp(&fa->match, &fb->match, sizeof(fa->match))) 416 return (0); 417 if (!filterset_equal(&fa->set, &fb->set)) 418 return (0); 419 420 fa = TAILQ_NEXT(fa, entry); 421 fb = TAILQ_NEXT(fb, entry); 422 } 423 return (1); 424} 425 426/* free a filterset and take care of possible name2id references */ 427void 428filterset_free(struct filter_set_head *sh) 429{ 430 struct filter_set *s; 431 struct nexthop *nh; 432 433 while ((s = TAILQ_FIRST(sh)) != NULL) { 434 TAILQ_REMOVE(sh, s, entry); 435 if (s->type == ACTION_RTLABEL_ID) 436 rtlabel_unref(s->action.id); 437 else if (s->type == ACTION_PFTABLE_ID) 438 pftable_unref(s->action.id); 439 else if (s->type == ACTION_SET_NEXTHOP && 440 bgpd_process == PROC_RDE) { 441 nh = nexthop_get(&s->action.nexthop); 442 --nh->refcnt; 443 (void)nexthop_delete(nh); 444 } 445 free(s); 446 } 447} 448 449/* 450 * this function is a bit more complicated than a memcmp() because there are 451 * types that need to be considered equal e.g. ACTION_SET_MED and 452 * ACTION_SET_RELATIVE_MED. Also ACTION_SET_COMMUNITY and ACTION_SET_NEXTHOP 453 * need some special care. It only checks the types and not the values so 454 * it does not do a real compare. 455 */ 456int 457filterset_cmp(struct filter_set *a, struct filter_set *b) 458{ 459 if (strcmp(filterset_name(a->type), filterset_name(b->type))) 460 return (a->type - b->type); 461 462 if (a->type == ACTION_SET_COMMUNITY) { /* a->type == b->type */ 463 /* compare community */ 464 if (a->action.community.as - b->action.community.as != 0) 465 return (a->action.community.as - 466 b->action.community.as); 467 return (a->action.community.type - b->action.community.type); 468 } 469 470 if (a->type == ACTION_SET_NEXTHOP && b->type == ACTION_SET_NEXTHOP) { 471 /* 472 * This is the only intresting 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.af - b->action.nexthop.af); 478 } 479 480 /* equal */ 481 return (0); 482} 483 484int 485filterset_equal(struct filter_set_head *ah, struct filter_set_head *bh) 486{ 487 struct filter_set *a, *b; 488 const char *as, *bs; 489 490 for (a = TAILQ_FIRST(ah), b = TAILQ_FIRST(bh); 491 a != NULL && b != NULL; 492 a = TAILQ_NEXT(a, entry), b = TAILQ_NEXT(b, entry)) { 493 switch (a->type) { 494 case ACTION_SET_PREPEND_SELF: 495 case ACTION_SET_PREPEND_PEER: 496 if (a->type == b->type && 497 a->action.prepend == b->action.prepend) 498 continue; 499 break; 500 case ACTION_SET_LOCALPREF: 501 case ACTION_SET_MED: 502 case ACTION_SET_WEIGHT: 503 if (a->type == b->type && 504 a->action.metric == b->action.metric) 505 continue; 506 break; 507 case ACTION_SET_RELATIVE_LOCALPREF: 508 case ACTION_SET_RELATIVE_MED: 509 case ACTION_SET_RELATIVE_WEIGHT: 510 if (a->type == b->type && 511 a->action.relative == b->action.relative) 512 continue; 513 break; 514 case ACTION_SET_NEXTHOP: 515 if (a->type == b->type && 516 memcmp(&a->action.nexthop, &b->action.nexthop, 517 sizeof(a->action.nexthop)) == 0) 518 continue; 519 break; 520 case ACTION_SET_NEXTHOP_BLACKHOLE: 521 case ACTION_SET_NEXTHOP_REJECT: 522 case ACTION_SET_NEXTHOP_NOMODIFY: 523 case ACTION_SET_NEXTHOP_SELF: 524 if (a->type == b->type) 525 continue; 526 break; 527 case ACTION_DEL_COMMUNITY: 528 case ACTION_SET_COMMUNITY: 529 if (a->type == b->type && 530 memcmp(&a->action.community, &b->action.community, 531 sizeof(a->action.community)) == 0) 532 continue; 533 break; 534 case ACTION_PFTABLE: 535 case ACTION_PFTABLE_ID: 536 if (b->type == ACTION_PFTABLE) 537 bs = b->action.pftable; 538 else if (b->type == ACTION_PFTABLE_ID) 539 bs = pftable_id2name(b->action.id); 540 else 541 break; 542 543 if (a->type == ACTION_PFTABLE) 544 as = a->action.pftable; 545 else 546 as = pftable_id2name(a->action.id); 547 548 if (strcmp(as, bs) == 0) 549 continue; 550 break; 551 case ACTION_RTLABEL: 552 case ACTION_RTLABEL_ID: 553 if (b->type == ACTION_RTLABEL) 554 bs = b->action.rtlabel; 555 else if (b->type == ACTION_RTLABEL_ID) 556 bs = rtlabel_id2name(b->action.id); 557 else 558 break; 559 560 if (a->type == ACTION_RTLABEL) 561 as = a->action.rtlabel; 562 else 563 as = rtlabel_id2name(a->action.id); 564 565 if (strcmp(as, bs) == 0) 566 continue; 567 break; 568 } 569 /* compare failed */ 570 return (0); 571 } 572 if (a != NULL || b != NULL) 573 return (0); 574 return (1); 575} 576 577const char * 578filterset_name(enum action_types type) 579{ 580 switch (type) { 581 case ACTION_SET_LOCALPREF: 582 case ACTION_SET_RELATIVE_LOCALPREF: 583 return ("localpref"); 584 case ACTION_SET_MED: 585 case ACTION_SET_RELATIVE_MED: 586 return ("metric"); 587 case ACTION_SET_WEIGHT: 588 case ACTION_SET_RELATIVE_WEIGHT: 589 return ("weight"); 590 case ACTION_SET_PREPEND_SELF: 591 return ("prepend-self"); 592 case ACTION_SET_PREPEND_PEER: 593 return ("prepend-peer"); 594 case ACTION_SET_NEXTHOP: 595 case ACTION_SET_NEXTHOP_REJECT: 596 case ACTION_SET_NEXTHOP_BLACKHOLE: 597 case ACTION_SET_NEXTHOP_NOMODIFY: 598 case ACTION_SET_NEXTHOP_SELF: 599 return ("nexthop"); 600 case ACTION_SET_COMMUNITY: 601 return ("community"); 602 case ACTION_DEL_COMMUNITY: 603 return ("community delete"); 604 case ACTION_PFTABLE: 605 case ACTION_PFTABLE_ID: 606 return ("pftable"); 607 case ACTION_RTLABEL: 608 case ACTION_RTLABEL_ID: 609 return ("rtlabel"); 610 } 611 612 fatalx("filterset_name: got lost"); 613} 614