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