]>
git.ipfire.org Git - thirdparty/bird.git/blob - filter/filter.c
fd6ea35263c3df387b3104f2c1bd6a7d10ef66b1
2 * Filters: utility functions
4 * Copyright 1998 Pavel Machek <pavel@ucw.cz>
6 * Can be freely distributed and used under the terms of the GNU GPL.
8 * Notice that pair is stored as integer: first << 16 | second
14 #include "nest/bird.h"
15 #include "lib/lists.h"
16 #include "lib/resource.h"
17 #include "lib/socket.h"
18 #include "lib/string.h"
19 #include "lib/unaligned.h"
20 #include "nest/route.h"
21 #include "nest/protocol.h"
22 #include "nest/iface.h"
23 #include "nest/attrs.h"
24 #include "conf/conf.h"
25 #include "filter/filter.h"
27 #define P(a,b) ((a<<8) | b)
29 struct f_inst
*startup_func
= NULL
;
33 /* Compare two values, returns -1, 0, 1 compared, ERROR 999 */
35 val_compare(struct f_val v1
, struct f_val v2
)
37 if ((v1
.type
== T_VOID
) && (v2
.type
== T_VOID
))
39 if (v1
.type
== T_VOID
) /* Hack for else */
41 if (v2
.type
== T_VOID
)
44 if (v1
.type
!= v2
.type
)
50 if (v1
.val
.i
== v2
.val
.i
) return 0;
51 if (v1
.val
.i
< v2
.val
.i
) return -1;
55 return ipa_compare(v1
.val
.px
.ip
, v2
.val
.px
.ip
);
62 val_simple_in_range(struct f_val v1
, struct f_val v2
)
64 if ((v1
.type
== T_PATH
) && (v2
.type
== T_PATH_MASK
))
65 return as_path_match(v1
.val
.ad
, v2
.val
.path_mask
);
66 if ((v1
.type
== T_PAIR
) && (v2
.type
== T_CLIST
))
67 return int_set_contains(v2
.val
.ad
, v1
.val
.i
);
69 if ((v1
.type
== T_IP
) && (v2
.type
== T_PREFIX
))
70 return !(ipa_compare(ipa_and(v2
.val
.px
.ip
, ipa_mkmask(v2
.val
.px
.len
)), ipa_and(v1
.val
.px
.ip
, ipa_mkmask(v2
.val
.px
.len
))));
72 if ((v1
.type
== T_PREFIX
) && (v2
.type
== T_PREFIX
)) {
74 if (v1
.val
.px
.len
& (LEN_PLUS
| LEN_MINUS
| LEN_RANGE
))
76 mask
= ipa_mkmask( v2
.val
.px
.len
& LEN_MASK
);
77 if (ipa_compare(ipa_and(v2
.val
.px
.ip
, mask
), ipa_and(v1
.val
.px
.ip
, mask
)))
80 if ((v2
.val
.px
.len
& LEN_MINUS
) && (v1
.val
.px
.len
<= (v2
.val
.px
.len
& LEN_MASK
)))
82 if ((v2
.val
.px
.len
& LEN_PLUS
) && (v1
.val
.px
.len
< (v2
.val
.px
.len
& LEN_MASK
)))
84 if ((v2
.val
.px
.len
& LEN_RANGE
) && ((v1
.val
.px
.len
< (0xff & (v2
.val
.px
.len
>> 16)))
85 || (v1
.val
.px
.len
> (0xff & (v2
.val
.px
.len
>> 8)))))
93 val_in_range(struct f_val v1
, struct f_val v2
)
97 res
= val_simple_in_range(v1
, v2
);
102 if (((v1
.type
== T_INT
) || ((v1
.type
== T_IP
) || (v1
.type
== T_PREFIX
)) && (v2
.type
== T_SET
))) {
104 n
= find_tree(v2
.val
.t
, v1
);
107 return !! (val_simple_in_range(v1
, n
->from
)); /* We turn CMP_ERROR into compared ok, and that's fine */
113 tree_print(struct f_tree
*t
)
120 tree_print( t
->left
);
121 debug( ", " ); val_print( t
->from
); debug( ".." ); val_print( t
->to
); debug( ", " );
122 tree_print( t
->right
);
127 val_print(struct f_val v
)
131 #define PRINTF(a...) bsnprintf( buf, 2040, a )
134 case T_VOID
: PRINTF( "(void)" ); break;
135 case T_BOOL
: PRINTF( v
.val
.i
? "TRUE" : "FALSE" ); break;
136 case T_INT
: PRINTF( "%d ", v
.val
.i
); break;
137 case T_STRING
: PRINTF( "%s", v
.val
.s
); break;
138 case T_IP
: PRINTF( "%I", v
.val
.px
.ip
); break;
139 case T_PREFIX
: PRINTF( "%I/%d", v
.val
.px
.ip
, v
.val
.px
.len
); break;
140 case T_PAIR
: PRINTF( "(%d,%d)", v
.val
.i
>> 16, v
.val
.i
& 0xffff ); break;
141 case T_SET
: tree_print( v
.val
.t
); PRINTF( "\n" ); break;
142 case T_ENUM
: PRINTF( "(enum %x)%d", v
.type
, v
.val
.i
); break;
143 case T_PATH
: as_path_format(v
.val
.ad
, buf2
, 1020); PRINTF( "(path %s)", buf2
); break;
144 case T_CLIST
: int_set_format(v
.val
.ad
, buf2
, 1020); PRINTF( "(clist %s)", buf2
); break;
145 case T_PATH_MASK
: debug( "(pathmask " ); { struct f_path_mask
*p
= v
.val
.s
; while (p
) { debug("%d ", p
->val
); p
=p
->next
; } debug(")" ); } break;
146 default: PRINTF( "[unknown type %x]", v
.type
);
152 static struct rte
**f_rte
, *f_rte_old
;
153 static struct linpool
*f_pool
;
154 static struct ea_list
**f_tmp_attrs
;
156 static rta
*f_rta_copy
;
158 #define runtime(x) do { \
160 res.type = T_RETURN; \
161 res.val.i = F_ERROR; \
166 x = interpret(what->y); \
167 if (x.type & T_RETURN) \
170 #define ONEARG ARG(v1, a1.p)
171 #define TWOARGS ARG(v1, a1.p) \
173 #define TWOARGS_C TWOARGS \
174 if (v1.type != v2.type) \
175 runtime( "Can not operate with values of incompatible types" );
178 interpret(struct f_inst
*what
)
181 struct f_val v1
, v2
, res
;
193 /* Binary operators */
196 switch (res
.type
= v1
.type
) {
197 case T_VOID
: runtime( "Can not operate with values of type void" );
198 case T_INT
: res
.val
.i
= v1
.val
.i
+ v2
.val
.i
; break;
199 default: runtime( "Usage of unknown type" );
204 switch (res
.type
= v1
.type
) {
205 case T_VOID
: runtime( "Can not operate with values of type void" );
206 case T_INT
: res
.val
.i
= v1
.val
.i
/ v2
.val
.i
; break;
207 case T_IP
: if (v2
.type
!= T_INT
)
208 runtime( "Operator / is <ip>/<int>" );
210 default: runtime( "Usage of unknown type" );
214 /* Relational operators */
219 i = val_compare(v1, v2); \
221 runtime( "Error in comparation" ); \
225 case P('!','='): COMPARE(i
!=0);
226 case P('=','='): COMPARE(i
==0);
227 case '<': COMPARE(i
==-1);
228 case P('<','='): COMPARE(i
!=1);
232 if (v1
.type
!= T_BOOL
)
233 runtime( "not applied to non-boolean" );
235 res
.val
.i
= !res
.val
.i
;
241 res
.val
.i
= val_in_range(v1
, v2
);
242 if (res
.val
.i
== CMP_ERROR
)
243 runtime( "~ applied on unknown type pair" );
248 res
.val
.i
= (v1
.type
!= T_VOID
);
251 /* Set to indirect value, a1 = variable, a2 = value */
255 switch (res
.type
= v2
.type
) {
256 case T_VOID
: runtime( "Can not assign void values" );
265 if (sym
->class != (SYM_VARIABLE
| v2
.type
))
266 runtime( "Variable of bad type" );
267 * (struct f_val
*) sym
->aux2
= v2
;
270 bug( "Set to invalid type" );
274 case 'c': /* integer (or simple type) constant */
275 res
.type
= what
->aux
;
276 res
.val
.i
= what
->a2
.i
;
279 res
= * ((struct f_val
*) what
->a1
.p
);
285 case '?': /* ? has really strange error value, so we can implement if ... else nicely :-) */
287 if (v1
.type
!= T_BOOL
)
288 runtime( "If requires bool expression" );
292 } else res
.val
.i
= 1;
296 debug( "No operation\n" );
300 if (what
->a2
.i
== F_NOP
|| (what
->a2
.i
!= F_NONL
&& what
->a1
.p
))
303 switch (what
->a2
.i
) {
305 die( "Filter asked me to die" );
307 /* Should take care about turning ACCEPT into MODIFY */
309 case F_REJECT
: /* FIXME (noncritical) Should print complete route along with reason to reject route */
311 res
.val
.i
= what
->a2
.i
;
312 return res
; /* We have to return now, no more processing. */
317 bug( "unknown return type: can not happen");
320 case 'a': /* rta access */
322 struct rta
*rta
= (*f_rte
)->attrs
;
323 res
.type
= what
->aux
;
326 res
.val
.px
.ip
= * (ip_addr
*) ((char *) rta
+ what
->a2
.i
);
329 res
.val
.i
= * ((char *) rta
+ what
->a2
.i
);
331 case T_PREFIX
: /* Warning: this works only for prefix of network */
333 res
.val
.px
.ip
= (*f_rte
)->net
->n
.prefix
;
334 res
.val
.px
.len
= (*f_rte
)->net
->n
.pxlen
;
338 bug( "Invalid type for rta access (%x)", res
.type
);
342 case P('e','a'): /* Access to extended attributes */
345 if (!(f_flags
& FF_FORCE_TMPATTR
))
346 e
= ea_find( (*f_rte
)->attrs
->eattrs
, what
->a2
.i
);
348 e
= ea_find( (*f_tmp_attrs
), what
->a2
.i
);
349 if ((!e
) && (f_flags
& FF_FORCE_TMPATTR
))
350 e
= ea_find( (*f_rte
)->attrs
->eattrs
, what
->a2
.i
);
356 res
.type
= what
->aux
; /* FIXME: should check type? */
359 res
.val
.i
= e
->u
.data
;
363 res
.val
.ad
= e
->u
.ptr
;
366 bug("Unknown type in e,a\n");
372 if (v1
.type
!= what
->aux
)
373 runtime("Wrong type when setting dynamic attribute");
376 struct ea_list
*l
= lp_alloc(f_pool
, sizeof(struct ea_list
) + sizeof(eattr
));
379 l
->flags
= EALF_SORTED
;
381 l
->attrs
[0].id
= what
->a2
.i
;
382 l
->attrs
[0].flags
= 0;
383 l
->attrs
[0].type
= what
->aux
| EAF_ORIGINATED
;
384 switch (what
->aux
& EAF_TYPE_MASK
) {
386 if (v1
.type
!= T_INT
)
387 runtime( "Setting int attribute to non-int value" );
388 l
->attrs
[0].u
.data
= v1
.val
.i
;
390 case EAF_TYPE_AS_PATH
:
391 if (v1
.type
!= T_PATH
)
392 runtime( "Setting path attribute to non-path value" );
393 l
->attrs
[0].u
.ptr
= v1
.val
.ad
;
395 case EAF_TYPE_INT_SET
:
396 if (v1
.type
!= T_CLIST
)
397 runtime( "Setting int set attribute to non-clist value" );
398 l
->attrs
[0].u
.ptr
= v1
.val
.ad
;
401 if (v1
.type
!= T_VOID
)
402 runtime( "Setting void attribute to non-void value" );
403 l
->attrs
[0].u
.data
= 0;
407 if (!(what
->aux
& EAF_TEMP
) && (!(f_flags
& FF_FORCE_TMPATTR
))) {
409 f_rta_copy
= lp_alloc(f_pool
, sizeof(rta
));
410 memcpy(f_rta_copy
, (*f_rte
)->attrs
, sizeof(rta
));
411 f_rta_copy
->aflags
= 0;
412 *f_rte
= rte_cow(*f_rte
);
413 (*f_rte
)->attrs
= f_rta_copy
;
415 l
->next
= f_rta_copy
->eattrs
;
416 f_rta_copy
->eattrs
= l
;
418 l
->next
= (*f_tmp_attrs
);
424 case 'L': /* Get length of */
428 case T_PREFIX
: res
.val
.i
= v1
.val
.px
.len
; break;
429 case T_PATH
: res
.val
.i
= as_path_getlen(v1
.val
.ad
); break;
430 default: bug( "Length of what?" );
433 case P('c','p'): /* Convert prefix to ... */
435 if (v1
.type
!= T_PREFIX
)
436 runtime( "Can not convert non-prefix this way" );
437 res
.type
= what
->aux
;
439 /* case T_INT: res.val.i = v1.val.px.len; break; Not needed any more */
440 case T_IP
: res
.val
.px
.ip
= v1
.val
.px
.ip
; break;
441 default: bug( "Unknown prefix to conversion" );
447 res
.type
|= T_RETURN
;
449 case P('c','a'): /* CALL: this is special: if T_RETURN and returning some value, mask it out */
451 res
= interpret(what
->a2
.p
);
452 if (res
.type
== T_RETURN
)
454 res
.type
&= ~T_RETURN
;
459 struct f_tree
*t
= find_tree(what
->a2
.p
, v1
);
462 t
= find_tree(what
->a2
.p
, v1
);
464 debug( "No else statement?\n ");
469 bug( "Impossible: no code associated!" );
470 return interpret(t
->data
);
473 case P('i','M'): /* IP.MASK(val) */
475 if (v2
.type
!= T_INT
)
476 runtime( "Can not use this type for mask.");
478 runtime( "You can mask only IP addresses." );
480 ip_addr mask
= ipa_mkmask(v2
.val
.i
);
482 res
.val
.px
.ip
= ipa_and(mask
, v1
.val
.px
.ip
);
486 case 'E': /* Create empty attribute */
487 res
.type
= what
->aux
;
488 res
.val
.ad
= adata_empty(f_pool
);
490 case P('A','p'): /* Path prepend */
492 if (v1
.type
!= T_PATH
)
493 runtime("Can't prepend to non-path");
494 if (v2
.type
!= T_INT
)
495 runtime("Can't prepend non-integer");
498 res
.val
.ad
= as_path_prepend(f_pool
, v1
.val
.ad
, v2
.val
.i
);
501 case P('C','a'): /* Community list add or delete */
503 if (v1
.type
!= T_CLIST
)
504 runtime("Can't add/delete to non-clist");
505 if (v2
.type
!= T_PAIR
)
506 runtime("Can't add/delete non-pair");
510 case 'a': res
.val
.ad
= int_set_add(f_pool
, v1
.val
.ad
, v2
.val
.i
); break;
511 case 'd': res
.val
.ad
= int_set_del(f_pool
, v1
.val
.ad
, v2
.val
.i
); break;
512 default: bug("unknown Ca operation");
517 bug( "Unknown instruction %d (%c)", what
->code
, what
->code
& 0xff);
520 return interpret(what
->next
);
526 if (!i_same(f1->y, f2->y)) \
529 #define ONEARG ARG(v1, a1.p)
530 #define TWOARGS ARG(v1, a1.p) \
533 #define A2_SAME if (f1->a2.i != f2->a2.i) return 0;
536 i_same(struct f_inst
*f1
, struct f_inst
*f2
)
538 if ((!!f1
) != (!!f2
))
542 if (f1
->aux
!= f2
->aux
)
544 if (f1
->code
!= f2
->code
)
546 if (f1
== f2
) /* It looks strange, but it is possible with call rewriting trickery */
550 case ',': /* fall through */
556 case P('<','='): TWOARGS
; break;
558 case '!': ONEARG
; break;
559 case '~': TWOARGS
; break;
560 case P('d','e'): ONEARG
; break;
565 struct symbol
*s1
, *s2
;
568 if (strcmp(s1
->name
, s2
->name
))
570 if (s1
->class != s2
->class)
575 case 'c': A2_SAME
; break;
577 if (val_compare(* (struct f_val
*) f1
->a1
.p
, * (struct f_val
*) f2
->a2
.p
))
580 case 'p': case 'L': ONEARG
; break;
581 case '?': TWOARGS
; break;
582 case '0': case 'E': break;
583 case P('p',','): ONEARG
; A2_SAME
; break;
584 case 'a': A2_SAME
; break;
585 case P('e','a'): A2_SAME
; break;
586 case P('e','S'): ONEARG
; A2_SAME
; break;
588 case 'r': ONEARG
; break;
589 case P('c','p'): ONEARG
; break;
590 case P('c','a'): /* Call rewriting trickery to avoid exponential behaviour */
592 if (!i_same(f1
->a2
.p
, f2
->a2
.p
))
596 case P('S','W'): ONEARG
; if (!same_tree(f1
->a2
.p
, f2
->a2
.p
)) return 0; break;
597 case P('i','M'): TWOARGS
; break;
598 case P('A','p'): TWOARGS
; break;
599 case P('C','a'): TWOARGS
; break;
601 bug( "Unknown instruction %d in same (%c)", f1
->code
, f1
->code
& 0xff);
603 return i_same(f1
->next
, f2
->next
);
607 f_run(struct filter
*filter
, struct rte
**rte
, struct ea_list
**tmp_attrs
, struct linpool
*tmp_pool
, int flags
)
611 DBG( "Running filter `%s'...", filter
->name
);
614 f_tmp_attrs
= tmp_attrs
;
620 res
= interpret(inst
);
621 if (res
.type
!= T_RETURN
)
623 DBG( "done (%d)\n", res
.val
.i
);
628 filters_postconfig(void)
632 debug( "Launching startup function...\n" );
633 f_pool
= lp_new(&root_pool
, 1024);
634 res
= interpret(startup_func
);
635 if (res
.type
== F_ERROR
)
636 die( "Startup function resulted in error." );
642 filter_same(struct filter
*new, struct filter
*old
)
644 if (old
== new) /* Handle FILTER_ACCEPT and FILTER_REJECT */
646 if (old
== FILTER_ACCEPT
|| old
== FILTER_REJECT
||
647 new == FILTER_ACCEPT
|| new == FILTER_REJECT
)
649 return i_same(new->root
, old
->root
);
652 /* This should end up far away from here!
655 comlist_add(struct linpool
*pool
, struct adata
*list
, u32 val
)
657 struct adata
*res
= lp_alloc(pool
, list
->length
+ sizeof(struct adata
) + 4);
658 res
->length
= list
->length
+4;
659 * (u32
*) res
->data
= val
;
660 memcpy((char *) res
->data
+ 4, list
->data
, list
->length
);
665 comlist_contains(struct adata
*list
, u32 val
)
667 u32
*l
= &(list
->data
);
669 for (i
=0; i
<list
->length
/4; i
++)
676 comlist_del(struct linpool
*pool
, struct adata
*list
, u32 val
)
682 if (!comlist_contains(list
, val
))
685 res
= lp_alloc(pool
, list
->length
+ sizeof(struct adata
) - 4);
686 res
->length
= list
->length
-4;
690 for (i
=0; i
<list
->length
/4; i
++)
698 adata_empty(struct linpool
*pool
)
700 struct adata
*res
= lp_alloc(pool
, sizeof(struct adata
));