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