GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/bgpd/rde_filter.c Lines: 0 317 0.0 %
Date: 2016-12-06 Branches: 0 343 0.0 %

Line Branch Exec Source
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
}