xref: /openbsd-src/usr.sbin/bgpd/rde_filter.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
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