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