GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/bgpd/rde_filter.c Lines: 5 445 1.1 %
Date: 2017-11-13 Branches: 3 460 0.7 %

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