]>
git.ipfire.org Git - thirdparty/bird.git/blob - filter/f-inst.c
2 * Filters: Instructions themselves
4 * Copyright 1998 Pavel Machek <pavel@ucw.cz>
5 * Copyright 2018 Maria Matejka <mq@jmq.cz>
6 * Copyright 2018 CZ.NIC z.s.p.o.
8 * Can be freely distributed and used under the terms of the GNU GPL.
10 * The filter code goes through several phases:
13 * Flex- and Bison-generated parser decodes the human-readable data into
14 * a struct f_inst tree. This is an infix tree that was interpreted by
15 * depth-first search execution in previous versions of the interpreter.
16 * All instructions have their constructor: f_new_inst(FI_EXAMPLE, ...)
17 * translates into f_new_inst_FI_EXAMPLE(...) and the types are checked in
18 * compile time. If the result of the instruction is always the same,
19 * it's reduced to FI_CONSTANT directly in constructor. This phase also
20 * counts how many instructions are underlying in means of f_line_item
21 * fields to know how much we have to allocate in the next phase.
23 * 2 Linearize before interpreting
24 * The infix tree is always interpreted in the same order. Therefore we
25 * sort the instructions one after another into struct f_line. Results
26 * and arguments of these instructions are implicitly put on a value
27 * stack; e.g. the + operation just takes two arguments from the value
28 * stack and puts the result on there.
31 * The given line is put on a custom execution stack. If needed (FI_CALL,
32 * FI_SWITCH, FI_AND, FI_OR, FI_CONDITION, ...), another line is put on top
33 * of the stack; when that line finishes, the execution continues on the
34 * older lines on the stack where it stopped before.
37 * On config reload, the filters have to be compared whether channel
38 * reload is needed or not. The comparison is done by comparing the
39 * struct f_line's recursively.
41 * The main purpose of this rework was to improve filter performance
42 * by making the interpreter non-recursive.
44 * The other outcome is concentration of instruction definitions to
45 * one place -- right here. You shall define your instruction only here
48 * Beware. This file is interpreted by M4 macros. These macros
49 * may be more stupid than you could imagine. If something strange
50 * happens after changing this file, compare the results before and
51 * after your change (see the Makefile to find out where the results are)
52 * and see what really happened.
54 * This file is not directly a C source code -> it is a generator input
55 * for several C sources; every instruction block gets expanded into many
58 * All the arguments are processed literally; if you need an argument including comma,
59 * you have to quote it by [[ ... ]]
61 * What is the syntax here?
62 * m4_dnl INST(FI_NOP, in, out) { enum value, input args, output args
63 * m4_dnl ARG(num, type); argument, its id (in data fields) and type accessible by v1, v2, v3
64 * m4_dnl ARG_ANY(num); argument with no type check accessible by v1, v2, v3
65 * m4_dnl VARARG; variable-length argument list; accessible by vv(i) and whati->varcount
66 * m4_dnl LINE(num, unused); this argument has to be converted to its own f_line
67 * m4_dnl SYMBOL; symbol handed from config
68 * m4_dnl STATIC_ATTR; static attribute definition
69 * m4_dnl DYNAMIC_ATTR; dynamic attribute definition
70 * m4_dnl RTC; route table config
71 * m4_dnl ACCESS_RTE; this instruction needs route
72 * m4_dnl ACCESS_EATTRS; this instruction needs extended attributes
74 * m4_dnl FID_MEMBER( custom instruction member
75 * m4_dnl C type, for storage in structs
76 * m4_dnl name, how the member is named
77 * m4_dnl comparator for same(), if different, this should be TRUE (CAVEAT)
78 * m4_dnl dump format string debug -> format string for bvsnprintf
79 * m4_dnl dump format args appropriate args
82 * m4_dnl RESULT(type, union-field, value); putting this on value stack
83 * m4_dnl RESULT_VAL(value-struct); pass the struct f_val directly
84 * m4_dnl RESULT_VOID; return undef
87 * Also note that the { ... } blocks are not respected by M4 at all.
88 * If you get weird unmatched-brace-pair errors, check what it generated and why.
89 * What is really considered as one instruction is not the { ... } block
90 * after m4_dnl INST() but all the code between them.
92 * Other code is just copied into the interpreter part.
94 * If you are satisfied with this, you don't need to read the following
95 * detailed description of what is really done with the instruction definitions.
97 * m4_dnl Now let's look under the cover. The code between each INST()
98 * m4_dnl is copied to several places, namely these (numbered by the M4 diversions
99 * m4_dnl used in filter/decl.m4):
101 * m4_dnl (102) struct f_inst *f_new_inst(FI_EXAMPLE [[ put it here ]])
103 * m4_dnl ... (common code)
104 * m4_dnl (103) [[ put it here ]]
106 * m4_dnl if (all arguments are constant)
107 * m4_dnl (108) [[ put it here ]]
109 * m4_dnl For writing directly to constructor argument list, use FID_NEW_ARGS.
110 * m4_dnl For computing something in constructor (103), use FID_NEW_BODY.
111 * m4_dnl For constant pre-interpretation (108), see below at FID_INTERPRET_BODY.
113 * m4_dnl struct f_inst {
114 * m4_dnl ... (common fields)
117 * m4_dnl (101) [[ put it here ]]
118 * m4_dnl } i_FI_EXAMPLE;
122 * m4_dnl This structure is returned from constructor.
123 * m4_dnl For writing directly to this structure, use FID_STRUCT_IN.
125 * m4_dnl linearize(struct f_line *dest, const struct f_inst *what, uint pos) {
127 * m4_dnl switch (what->fi_code) {
128 * m4_dnl case FI_EXAMPLE:
129 * m4_dnl (105) [[ put it here ]]
133 * m4_dnl This is called when translating from struct f_inst to struct f_line_item.
134 * m4_dnl For accessing your custom instruction data, use following macros:
135 * m4_dnl whati -> for accessing (struct f_inst).i_FI_EXAMPLE
136 * m4_dnl item -> for accessing (struct f_line)[pos].i_FI_EXAMPLE
137 * m4_dnl For writing directly here, use FID_LINEARIZE_BODY.
139 * m4_dnl (107) struct f_line_item {
140 * m4_dnl ... (common fields)
143 * m4_dnl (101) [[ put it here ]]
144 * m4_dnl } i_FI_EXAMPLE;
148 * m4_dnl The same as FID_STRUCT_IN (101) but for the other structure.
149 * m4_dnl This structure is returned from the linearizer (105).
150 * m4_dnl For writing directly to this structure, use FID_LINE_IN.
152 * m4_dnl f_dump_line_item_FI_EXAMPLE(const struct f_line_item *item, const int indent)
154 * m4_dnl (104) [[ put it here ]]
156 * m4_dnl This code dumps the instruction on debug. Note that the argument
157 * m4_dnl is the linearized instruction; if the instruction has arguments,
158 * m4_dnl their code has already been linearized and their value is taken
159 * m4_dnl from the value stack.
160 * m4_dnl For writing directly here, use FID_DUMP_BODY.
164 * m4_dnl switch (f1_->fi_code) {
165 * m4_dnl case FI_EXAMPLE:
166 * m4_dnl (106) [[ put it here ]]
170 * m4_dnl This code compares the two given instrucions (f1_ and f2_)
171 * m4_dnl on reconfigure. For accessing your custom instruction data,
172 * m4_dnl use macros f1 and f2.
173 * m4_dnl For writing directly here, use FID_SAME_BODY.
175 * m4_dnl interpret(...)
177 * m4_dnl switch (what->fi_code) {
178 * m4_dnl case FI_EXAMPLE:
179 * m4_dnl (108) [[ put it here ]]
183 * m4_dnl This code executes the instruction. Every pre-defined macro
184 * m4_dnl resets the output here. For setting it explicitly,
185 * m4_dnl use FID_INTERPRET_BODY.
186 * m4_dnl This code is put on two places; one is the interpreter, the other
187 * m4_dnl is instruction constructor. If you need to distinguish between
188 * m4_dnl these two, use FID_INTERPRET_EXEC or FID_INTERPRET_NEW respectively.
189 * m4_dnl To address the difference between interpreter and constructor
190 * m4_dnl environments, there are several convenience macros defined:
191 * m4_dnl runtime() -> for spitting out runtime error like division by zero
192 * m4_dnl RESULT(...) -> declare result; may overwrite arguments
193 * m4_dnl v1, v2, v3 -> positional arguments, may be overwritten by RESULT()
194 * m4_dnl falloc(size) -> allocate memory from the appropriate linpool
195 * m4_dnl fpool -> the current linpool
196 * m4_dnl NEVER_CONSTANT-> don't generate pre-interpretation code at all
197 * m4_dnl ACCESS_RTE -> check that route is available, also NEVER_CONSTANT
198 * m4_dnl ACCESS_EATTRS -> pre-cache the eattrs; use only with ACCESS_RTE
199 * m4_dnl f_rta_cow(fs) -> function to call before any change to route should be done
201 * m4_dnl If you are stymied, see FI_CALL or FI_CONSTANT or just search for
202 * m4_dnl the mentioned macros in this file to see what is happening there in wild.
205 /* Binary operators */
209 RESULT(T_INT
, i
, v1
.val
.i
+ v2
.val
.i
);
211 INST(FI_SUBTRACT
, 2, 1) {
214 RESULT(T_INT
, i
, v1
.val
.i
- v2
.val
.i
);
216 INST(FI_MULTIPLY
, 2, 1) {
219 RESULT(T_INT
, i
, v1
.val
.i
* v2
.val
.i
);
221 INST(FI_DIVIDE
, 2, 1) {
224 if (v2
.val
.i
== 0) runtime( "Mother told me not to divide by 0" );
225 RESULT(T_INT
, i
, v1
.val
.i
/ v2
.val
.i
);
229 ARG_TYPE_STATIC(2,T_BOOL
);
239 ARG_TYPE_STATIC(2,T_BOOL
);
248 INST(FI_PAIR_CONSTRUCT
, 2, 1) {
253 if ((u1
> 0xFFFF) || (u2
> 0xFFFF))
254 runtime( "Can't operate with value out of bounds in pair constructor" );
255 RESULT(T_PAIR
, i
, (u1
<< 16) | u2
);
258 INST(FI_EC_CONSTRUCT
, 2, 1) {
262 FID_MEMBER(enum ec_subtype
, ecs
, f1
->ecs
!= f2
->ecs
, "ec subtype %s", ec_subtype_str(item
->ecs
));
267 if (v1
.type
== T_INT
) {
268 ipv4_used
= 0; key
= v1
.val
.i
;
270 else if (v1
.type
== T_QUAD
) {
271 ipv4_used
= 1; key
= v1
.val
.i
;
273 /* IP->Quad implicit conversion */
274 else if (val_is_ip4(&v1
)) {
275 ipv4_used
= 1; key
= ipa_to_u32(v1
.val
.ip
);
278 runtime("Argument 1 of EC constructor must be integer or IPv4 address, got 0x%02x", v1
.type
);
282 if (ecs
== EC_GENERIC
)
283 RESULT(T_EC
, ec
, ec_generic(key
, val
));
286 RESULT(T_EC
, ec
, ec_ip4(ecs
, key
, val
));
288 runtime("4-byte value %u can't be used with IP-address key in extended community", val
);
289 else if (key
< 0x10000)
290 RESULT(T_EC
, ec
, ec_as2(ecs
, key
, val
));
293 RESULT(T_EC
, ec
, ec_as4(ecs
, key
, val
));
295 runtime("4-byte value %u can't be used with 4-byte ASN in extended community", val
);
298 INST(FI_LC_CONSTRUCT
, 3, 1) {
302 RESULT(T_LC
, lc
, [[(lcomm
) { v1
.val
.i
, v2
.val
.i
, v3
.val
.i
}]]);
305 INST(FI_PATHMASK_CONSTRUCT
, 0, 1) {
308 struct f_path_mask
*pm
= falloc(sizeof(struct f_path_mask
) + whati
->varcount
* sizeof(struct f_path_mask_item
));
309 pm
->len
= whati
->varcount
;
311 for (uint i
=0; i
<whati
->varcount
; i
++) {
312 switch (vv(i
).type
) {
313 case T_PATH_MASK_ITEM
:
314 pm
->item
[i
] = vv(i
).val
.pmi
;
318 pm
->item
[i
] = (struct f_path_mask_item
) {
325 if (vv(i
).val
.t
->from
.type
!= T_INT
)
326 runtime("Only integer sets allowed in path mask");
328 pm
->item
[i
] = (struct f_path_mask_item
) {
335 runtime( "Error resolving path mask template: value not an integer" );
339 RESULT(T_PATH_MASK
, path_mask
, pm
);
342 /* Relational operators */
347 RESULT(T_BOOL
, i
, !val_same(&v1
, &v2
));
353 RESULT(T_BOOL
, i
, val_same(&v1
, &v2
));
361 int i
= val_compare(&v1
, &v2
);
362 if (i
== F_CMP_ERROR
)
363 runtime( "Can't compare values of incompatible types" );
364 RESULT(T_BOOL
, i
, (i
== -1));
372 int i
= val_compare(&v1
, &v2
);
373 if (i
== F_CMP_ERROR
)
374 runtime( "Can't compare values of incompatible types" );
375 RESULT(T_BOOL
, i
, (i
!= 1));
380 RESULT(T_BOOL
, i
, !v1
.val
.i
);
383 INST(FI_MATCH
, 2, 1) {
386 int i
= val_in_range(&v1
, &v2
);
387 if (i
== F_CMP_ERROR
)
388 runtime( "~ applied on unknown type pair" );
389 RESULT(T_BOOL
, i
, !!i
);
392 INST(FI_NOT_MATCH
, 2, 1) {
395 int i
= val_in_range(&v1
, &v2
);
396 if (i
== F_CMP_ERROR
)
397 runtime( "!~ applied on unknown type pair" );
398 RESULT(T_BOOL
, i
, !i
);
401 INST(FI_DEFINED
, 1, 1) {
403 RESULT(T_BOOL
, i
, (v1
.type
!= T_VOID
) && !undef_value(v1
));
406 INST(FI_TYPE
, 1, 1) {
407 ARG_ANY(1); /* There may be more types supporting this operation */
411 RESULT(T_ENUM_NETTYPE
, i
, v1
.val
.net
->type
);
414 runtime( "Can't determine type of this item" );
418 INST(FI_IS_V4
, 1, 1) {
420 RESULT(T_BOOL
, i
, ipa_is_ip4(v1
.val
.ip
));
423 /* Set to indirect value prepared in v1 */
424 INST(FI_VAR_SET
, 1, 0) {
428 ARG_TYPE(1, sym
->class & 0xff);
430 fstk
->vstk
[curline
.vbase
+ sym
->offset
] = v1
;
433 INST(FI_VAR_GET
, 0, 1) {
436 RESULT_TYPE(sym
->class & 0xff);
437 RESULT_VAL(fstk
->vstk
[curline
.vbase
+ sym
->offset
]);
440 INST(FI_CONSTANT
, 0, 1) {
444 [[ !val_same(&(f1
->val
), &(f2
->val
)) ]],
446 val_dump(&(item
->val
))
449 RESULT_TYPE(val
.type
);
453 INST(FI_CONDITION
, 1, 0) {
461 INST(FI_PRINT
, 0, 0) {
465 if (whati
->varcount
&& !(fs
->flags
& FF_SILENT
))
466 for (uint i
=0; i
<whati
->varcount
; i
++)
467 val_format(&(vv(i
)), &fs
->buf
);
470 INST(FI_FLUSH
, 0, 0) {
472 if (!(fs
->flags
& FF_SILENT
))
473 /* After log_commit, the buffer is reset */
474 log_commit(*L_INFO
, &fs
->buf
);
479 FID_MEMBER(enum filter_return
, fret
, f1
->fret
!= f2
->fret
, "%s", filter_return_str(item
->fret
));
481 switch (whati
->fret
) {
483 die( "Filter asked me to die" );
484 case F_ACCEPT
: /* Should take care about turning ACCEPT into MODIFY */
486 case F_REJECT
: /* Maybe print complete route along with reason to reject route? */
487 return fret
; /* We have to return now, no more processing. */
489 bug( "unknown return type: Can't happen");
493 INST(FI_RTA_GET
, 0, 1) {
497 struct rta
*rta
= (*fs
->rte
)->attrs
;
501 case SA_FROM
: RESULT(sa
.f_type
, ip
, rta
->from
); break;
502 case SA_GW
: RESULT(sa
.f_type
, ip
, rta
->nh
.gw
); break;
503 case SA_NET
: RESULT(sa
.f_type
, net
, (*fs
->rte
)->net
->n
.addr
); break;
504 case SA_PROTO
: RESULT(sa
.f_type
, s
, rta
->src
->proto
->name
); break;
505 case SA_SOURCE
: RESULT(sa
.f_type
, i
, rta
->source
); break;
506 case SA_SCOPE
: RESULT(sa
.f_type
, i
, rta
->scope
); break;
507 case SA_DEST
: RESULT(sa
.f_type
, i
, rta
->dest
); break;
508 case SA_IFNAME
: RESULT(sa
.f_type
, s
, rta
->nh
.iface
? rta
->nh
.iface
->name
: ""); break;
509 case SA_IFINDEX
: RESULT(sa
.f_type
, i
, rta
->nh
.iface
? rta
->nh
.iface
->index
: 0); break;
512 bug("Invalid static attribute access (%u/%u)", sa
.f_type
, sa
.sa_code
);
517 INST(FI_RTA_SET
, 1, 0) {
521 ARG_TYPE(1, sa
.f_type
);
525 struct rta
*rta
= (*fs
->rte
)->attrs
;
530 rta
->from
= v1
.val
.ip
;
535 ip_addr ip
= v1
.val
.ip
;
536 neighbor
*n
= neigh_find(rta
->src
->proto
, ip
, NULL
, 0);
537 if (!n
|| (n
->scope
== SCOPE_HOST
))
538 runtime( "Invalid gw address" );
540 rta
->dest
= RTD_UNICAST
;
542 rta
->nh
.iface
= n
->iface
;
544 rta
->hostentry
= NULL
;
549 rta
->scope
= v1
.val
.i
;
555 if ((i
!= RTD_BLACKHOLE
) && (i
!= RTD_UNREACHABLE
) && (i
!= RTD_PROHIBIT
))
556 runtime( "Destination can be changed only to blackhole, unreachable or prohibit" );
559 rta
->nh
.gw
= IPA_NONE
;
560 rta
->nh
.iface
= NULL
;
562 rta
->hostentry
= NULL
;
568 struct iface
*ifa
= if_find_by_name(v1
.val
.s
);
570 runtime( "Invalid iface name" );
572 rta
->dest
= RTD_UNICAST
;
573 rta
->nh
.gw
= IPA_NONE
;
576 rta
->hostentry
= NULL
;
581 bug("Invalid static attribute access (%u/%u)", sa
.f_type
, sa
.sa_code
);
586 INST(FI_EA_GET
, 0, 1) { /* Access to extended attributes */
590 RESULT_TYPE(da
.f_type
);
592 eattr
*e
= ea_find(*fs
->eattrs
, da
.ea_code
);
595 /* A special case: undefined as_path looks like empty as_path */
596 if (da
.type
== EAF_TYPE_AS_PATH
) {
597 RESULT_(T_PATH
, ad
, &null_adata
);
601 /* The same special case for int_set */
602 if (da
.type
== EAF_TYPE_INT_SET
) {
603 RESULT_(T_CLIST
, ad
, &null_adata
);
607 /* The same special case for ec_set */
608 if (da
.type
== EAF_TYPE_EC_SET
) {
609 RESULT_(T_ECLIST
, ad
, &null_adata
);
613 /* The same special case for lc_set */
614 if (da
.type
== EAF_TYPE_LC_SET
) {
615 RESULT_(T_LCLIST
, ad
, &null_adata
);
619 /* Undefined value */
624 switch (e
->type
& EAF_TYPE_MASK
) {
626 RESULT_(da
.f_type
, i
, e
->u
.data
);
628 case EAF_TYPE_ROUTER_ID
:
629 RESULT_(T_QUAD
, i
, e
->u
.data
);
631 case EAF_TYPE_OPAQUE
:
632 RESULT_(T_ENUM_EMPTY
, i
, 0);
634 case EAF_TYPE_IP_ADDRESS
:
635 RESULT_(T_IP
, ip
, *((ip_addr
*) e
->u
.ptr
->data
));
637 case EAF_TYPE_AS_PATH
:
638 RESULT_(T_PATH
, ad
, e
->u
.ptr
);
640 case EAF_TYPE_BITFIELD
:
641 RESULT_(T_BOOL
, i
, !!(e
->u
.data
& (1u << da
.bit
)));
643 case EAF_TYPE_INT_SET
:
644 RESULT_(T_CLIST
, ad
, e
->u
.ptr
);
646 case EAF_TYPE_EC_SET
:
647 RESULT_(T_ECLIST
, ad
, e
->u
.ptr
);
649 case EAF_TYPE_LC_SET
:
650 RESULT_(T_LCLIST
, ad
, e
->u
.ptr
);
656 bug("Unknown dynamic attribute type");
661 INST(FI_EA_SET
, 1, 0) {
666 ARG_TYPE(1, da
.f_type
);
668 struct ea_list
*l
= lp_alloc(fs
->pool
, sizeof(struct ea_list
) + sizeof(eattr
));
671 l
->flags
= EALF_SORTED
;
673 l
->attrs
[0].id
= da
.ea_code
;
674 l
->attrs
[0].flags
= 0;
675 l
->attrs
[0].type
= da
.type
| EAF_ORIGINATED
| EAF_FRESH
;
679 case EAF_TYPE_ROUTER_ID
:
680 l
->attrs
[0].u
.data
= v1
.val
.i
;
683 case EAF_TYPE_OPAQUE
:
684 runtime( "Setting opaque attribute is not allowed" );
687 case EAF_TYPE_IP_ADDRESS
:;
688 int len
= sizeof(ip_addr
);
689 struct adata
*ad
= lp_alloc(fs
->pool
, sizeof(struct adata
) + len
);
691 (* (ip_addr
*) ad
->data
) = v1
.val
.ip
;
692 l
->attrs
[0].u
.ptr
= ad
;
695 case EAF_TYPE_AS_PATH
:
696 case EAF_TYPE_INT_SET
:
697 case EAF_TYPE_EC_SET
:
698 case EAF_TYPE_LC_SET
:
699 l
->attrs
[0].u
.ptr
= v1
.val
.ad
;
702 case EAF_TYPE_BITFIELD
:
704 /* First, we have to find the old value */
705 eattr
*e
= ea_find(*fs
->eattrs
, da
.ea_code
);
706 u32 data
= e
? e
->u
.data
: 0;
709 l
->attrs
[0].u
.data
= data
| (1u << da
.bit
);
711 l
->attrs
[0].u
.data
= data
& ~(1u << da
.bit
);
716 bug("Unknown dynamic attribute type");
720 l
->next
= *fs
->eattrs
;
725 INST(FI_EA_UNSET
, 0, 0) {
731 struct ea_list
*l
= lp_alloc(fs
->pool
, sizeof(struct ea_list
) + sizeof(eattr
));
734 l
->flags
= EALF_SORTED
;
736 l
->attrs
[0].id
= da
.ea_code
;
737 l
->attrs
[0].flags
= 0;
738 l
->attrs
[0].type
= EAF_TYPE_UNDEF
| EAF_ORIGINATED
| EAF_FRESH
;
739 l
->attrs
[0].u
.data
= 0;
742 l
->next
= *fs
->eattrs
;
747 INST(FI_PREF_GET
, 0, 1) {
749 RESULT(T_INT
, i
, (*fs
->rte
)->pref
);
752 INST(FI_PREF_SET
, 1, 0) {
755 if (v1
.val
.i
> 0xFFFF)
756 runtime( "Setting preference value out of bounds" );
758 (*fs
->rte
)->pref
= v1
.val
.i
;
761 INST(FI_LENGTH
, 1, 1) { /* Get length of */
764 case T_NET
: RESULT(T_INT
, i
, net_pxlen(v1
.val
.net
)); break;
765 case T_PATH
: RESULT(T_INT
, i
, as_path_getlen(v1
.val
.ad
)); break;
766 case T_CLIST
: RESULT(T_INT
, i
, int_set_get_size(v1
.val
.ad
)); break;
767 case T_ECLIST
: RESULT(T_INT
, i
, ec_set_get_size(v1
.val
.ad
)); break;
768 case T_LCLIST
: RESULT(T_INT
, i
, lc_set_get_size(v1
.val
.ad
)); break;
769 default: runtime( "Prefix, path, clist or eclist expected" );
773 INST(FI_NET_SRC
, 1, 1) { /* Get src prefix */
776 net_addr_union
*net
= (void *) v1
.val
.net
;
777 net_addr
*src
= falloc(sizeof(net_addr_ip6
));
780 switch(v1
.val
.net
->type
) {
782 part
= flow4_get_part(&net
->flow4
, FLOW_TYPE_SRC_PREFIX
);
784 net_fill_ip4(src
, flow_read_ip4_part(part
), flow_read_pxlen(part
));
786 net_fill_ip4(src
, IP4_NONE
, 0);
790 part
= flow6_get_part(&net
->flow6
, FLOW_TYPE_SRC_PREFIX
);
792 net_fill_ip6(src
, flow_read_ip6_part(part
), flow_read_pxlen(part
));
794 net_fill_ip6(src
, IP6_NONE
, 0);
798 net_fill_ip6(src
, net
->ip6_sadr
.src_prefix
, net
->ip6_sadr
.src_pxlen
);
802 runtime( "Flow or SADR expected" );
805 RESULT(T_NET
, net
, src
);
808 INST(FI_NET_DST
, 1, 1) { /* Get dst prefix */
811 net_addr_union
*net
= (void *) v1
.val
.net
;
812 net_addr
*dst
= falloc(sizeof(net_addr_ip6
));
815 switch(v1
.val
.net
->type
) {
817 part
= flow4_get_part(&net
->flow4
, FLOW_TYPE_DST_PREFIX
);
819 net_fill_ip4(dst
, flow_read_ip4_part(part
), flow_read_pxlen(part
));
821 net_fill_ip4(dst
, IP4_NONE
, 0);
825 part
= flow6_get_part(&net
->flow6
, FLOW_TYPE_DST_PREFIX
);
827 net_fill_ip6(dst
, flow_read_ip6_part(part
), flow_read_pxlen(part
));
829 net_fill_ip6(dst
, IP6_NONE
, 0);
833 net_fill_ip6(dst
, net
->ip6_sadr
.dst_prefix
, net
->ip6_sadr
.dst_pxlen
);
837 runtime( "Flow or SADR expected" );
840 RESULT(T_NET
, net
, dst
);
843 INST(FI_ROA_MAXLEN
, 1, 1) { /* Get ROA max prefix length */
845 if (!net_is_roa(v1
.val
.net
))
846 runtime( "ROA expected" );
848 RESULT(T_INT
, i
, (v1
.val
.net
->type
== NET_ROA4
) ?
849 ((net_addr_roa4
*) v1
.val
.net
)->max_pxlen
:
850 ((net_addr_roa6
*) v1
.val
.net
)->max_pxlen
);
853 INST(FI_ROA_ASN
, 1, 1) { /* Get ROA ASN */
855 if (!net_is_roa(v1
.val
.net
))
856 runtime( "ROA expected" );
858 RESULT(T_INT
, i
, (v1
.val
.net
->type
== NET_ROA4
) ?
859 ((net_addr_roa4
*) v1
.val
.net
)->asn
:
860 ((net_addr_roa6
*) v1
.val
.net
)->asn
);
863 INST(FI_IP
, 1, 1) { /* Convert prefix to ... */
865 RESULT(T_IP
, ip
, net_prefix(v1
.val
.net
));
868 INST(FI_ROUTE_DISTINGUISHER
, 1, 1) {
870 if (!net_is_vpn(v1
.val
.net
))
871 runtime( "VPN address expected" );
872 RESULT(T_RD
, ec
, net_rd(v1
.val
.net
));
875 INST(FI_AS_PATH_FIRST
, 1, 1) { /* Get first ASN from AS PATH */
878 as_path_get_first(v1
.val
.ad
, &as
);
879 RESULT(T_INT
, i
, as
);
882 INST(FI_AS_PATH_LAST
, 1, 1) { /* Get last ASN from AS PATH */
885 as_path_get_last(v1
.val
.ad
, &as
);
886 RESULT(T_INT
, i
, as
);
889 INST(FI_AS_PATH_LAST_NAG
, 1, 1) { /* Get last ASN from non-aggregated part of AS PATH */
891 RESULT(T_INT
, i
, as_path_get_last_nonaggregated(v1
.val
.ad
));
894 INST(FI_RETURN
, 1, 1) {
896 /* Acquire the return value */
898 uint retpos
= fstk
->vcnt
;
900 /* Drop every sub-block including ourselves */
901 while ((fstk
->ecnt
-- > 0) && !(fstk
->estk
[fstk
->ecnt
].emask
& FE_RETURN
))
904 /* Now we are at the caller frame; if no such, try to convert to accept/reject. */
906 if (fstk
->vstk
[retpos
].type
== T_BOOL
)
907 if (fstk
->vstk
[retpos
].val
.i
)
912 runtime("Can't return non-bool from non-function");
914 /* Set the value stack position, overwriting the former implicit void */
915 fstk
->vcnt
= fstk
->estk
[fstk
->ecnt
].ventry
- 1;
917 /* Copy the return value */
918 RESULT_VAL(fstk
->vstk
[retpos
]);
921 INST(FI_CALL
, 0, 1) {
926 if (!(f1
->sym
->flags
& SYM_FLAG_SAME
))
930 /* Push the body on stack */
931 LINEX(sym
->function
);
932 curline
.emask
|= FE_RETURN
;
934 /* Before this instruction was called, there was the T_VOID
935 * automatic return value pushed on value stack and also
936 * sym->function->args function arguments. Setting the
937 * vbase to point to first argument. */
938 ASSERT(curline
.ventry
>= sym
->function
->args
);
939 curline
.ventry
-= sym
->function
->args
;
940 curline
.vbase
= curline
.ventry
;
942 /* Storage for local variables */
943 memset(&(fstk
->vstk
[fstk
->vcnt
]), 0, sizeof(struct f_val
) * sym
->function
->vars
);
944 fstk
->vcnt
+= sym
->function
->vars
;
947 INST(FI_DROP_RESULT
, 1, 0) {
952 INST(FI_SWITCH
, 1, 0) {
955 FID_MEMBER(struct f_tree
*, tree
, [[!same_tree(f1
->tree
, f2
->tree
)]], "tree %p", item
->tree
);
957 const struct f_tree
*t
= find_tree(tree
, &v1
);
960 t
= find_tree(tree
, &v1
);
962 debug( "No else statement?\n");
963 FID_HIC(,break,return NULL
);
966 /* It is actually possible to have t->data NULL */
971 INST(FI_IP_MASK
, 2, 1) { /* IP.MASK(val) */
974 RESULT(T_IP
, ip
, [[ ipa_is_ip4(v1
.val
.ip
) ?
975 ipa_from_ip4(ip4_and(ipa_to_ip4(v1
.val
.ip
), ip4_mkmask(v2
.val
.i
))) :
976 ipa_from_ip6(ip6_and(ipa_to_ip6(v1
.val
.ip
), ip6_mkmask(v2
.val
.i
))) ]]);
979 INST(FI_PATH_PREPEND
, 2, 1) { /* Path prepend */
982 RESULT(T_PATH
, ad
, [[ as_path_prepend(fpool
, v1
.val
.ad
, v2
.val
.i
) ]]);
985 INST(FI_CLIST_ADD
, 2, 1) { /* (Extended) Community list add */
988 RESULT_TYPE(f1
->type
);
990 if (v1
.type
== T_PATH
)
991 runtime("Can't add to path");
993 else if (v1
.type
== T_CLIST
)
995 /* Community (or cluster) list */
998 if ((v2
.type
== T_PAIR
) || (v2
.type
== T_QUAD
))
999 RESULT_(T_CLIST
, ad
, [[ int_set_add(fpool
, v1
.val
.ad
, v2
.val
.i
) ]]);
1000 /* IP->Quad implicit conversion */
1001 else if (val_is_ip4(&v2
))
1002 RESULT_(T_CLIST
, ad
, [[ int_set_add(fpool
, v1
.val
.ad
, ipa_to_u32(v2
.val
.ip
)) ]]);
1003 else if ((v2
.type
== T_SET
) && clist_set_type(v2
.val
.t
, &dummy
))
1004 runtime("Can't add set");
1005 else if (v2
.type
== T_CLIST
)
1006 RESULT_(T_CLIST
, ad
, [[ int_set_union(fpool
, v1
.val
.ad
, v2
.val
.ad
) ]]);
1008 runtime("Can't add non-pair");
1011 else if (v1
.type
== T_ECLIST
)
1013 /* v2.val is either EC or EC-set */
1014 if ((v2
.type
== T_SET
) && eclist_set_type(v2
.val
.t
))
1015 runtime("Can't add set");
1016 else if (v2
.type
== T_ECLIST
)
1017 RESULT_(T_ECLIST
, ad
, [[ ec_set_union(fpool
, v1
.val
.ad
, v2
.val
.ad
) ]]);
1018 else if (v2
.type
!= T_EC
)
1019 runtime("Can't add non-ec");
1021 RESULT_(T_ECLIST
, ad
, [[ ec_set_add(fpool
, v1
.val
.ad
, v2
.val
.ec
) ]]);
1024 else if (v1
.type
== T_LCLIST
)
1026 /* v2.val is either LC or LC-set */
1027 if ((v2
.type
== T_SET
) && lclist_set_type(v2
.val
.t
))
1028 runtime("Can't add set");
1029 else if (v2
.type
== T_LCLIST
)
1030 RESULT_(T_LCLIST
, ad
, [[ lc_set_union(fpool
, v1
.val
.ad
, v2
.val
.ad
) ]]);
1031 else if (v2
.type
!= T_LC
)
1032 runtime("Can't add non-lc");
1034 RESULT_(T_LCLIST
, ad
, [[ lc_set_add(fpool
, v1
.val
.ad
, v2
.val
.lc
) ]]);
1039 runtime("Can't add to non-[e|l]clist");
1042 INST(FI_CLIST_DEL
, 2, 1) { /* (Extended) Community list add or delete */
1045 RESULT_TYPE(f1
->type
);
1047 if (v1
.type
== T_PATH
)
1049 const struct f_tree
*set
= NULL
;
1052 if (v2
.type
== T_INT
)
1054 else if ((v2
.type
== T_SET
) && (v2
.val
.t
->from
.type
== T_INT
))
1057 runtime("Can't delete non-integer (set)");
1059 RESULT_(T_PATH
, ad
, [[ as_path_filter(fpool
, v1
.val
.ad
, set
, key
, 0) ]]);
1062 else if (v1
.type
== T_CLIST
)
1064 /* Community (or cluster) list */
1067 if ((v2
.type
== T_PAIR
) || (v2
.type
== T_QUAD
))
1068 RESULT_(T_CLIST
, ad
, [[ int_set_del(fpool
, v1
.val
.ad
, v2
.val
.i
) ]]);
1069 /* IP->Quad implicit conversion */
1070 else if (val_is_ip4(&v2
))
1071 RESULT_(T_CLIST
, ad
, [[ int_set_del(fpool
, v1
.val
.ad
, ipa_to_u32(v2
.val
.ip
)) ]]);
1072 else if ((v2
.type
== T_SET
) && clist_set_type(v2
.val
.t
, &dummy
) || (v2
.type
== T_CLIST
))
1073 RESULT_(T_CLIST
, ad
, [[ clist_filter(fpool
, v1
.val
.ad
, &v2
, 0) ]]);
1075 runtime("Can't delete non-pair");
1078 else if (v1
.type
== T_ECLIST
)
1080 /* v2.val is either EC or EC-set */
1081 if ((v2
.type
== T_SET
) && eclist_set_type(v2
.val
.t
) || (v2
.type
== T_ECLIST
))
1082 RESULT_(T_ECLIST
, ad
, [[ eclist_filter(fpool
, v1
.val
.ad
, &v2
, 0) ]]);
1083 else if (v2
.type
!= T_EC
)
1084 runtime("Can't delete non-ec");
1086 RESULT_(T_ECLIST
, ad
, [[ ec_set_del(fpool
, v1
.val
.ad
, v2
.val
.ec
) ]]);
1089 else if (v1
.type
== T_LCLIST
)
1091 /* v2.val is either LC or LC-set */
1092 if ((v2
.type
== T_SET
) && lclist_set_type(v2
.val
.t
) || (v2
.type
== T_LCLIST
))
1093 RESULT_(T_LCLIST
, ad
, [[ lclist_filter(fpool
, v1
.val
.ad
, &v2
, 0) ]]);
1094 else if (v2
.type
!= T_LC
)
1095 runtime("Can't delete non-lc");
1097 RESULT_(T_LCLIST
, ad
, [[ lc_set_del(fpool
, v1
.val
.ad
, v2
.val
.lc
) ]]);
1101 runtime("Can't delete in non-[e|l]clist");
1104 INST(FI_CLIST_FILTER
, 2, 1) { /* (Extended) Community list add or delete */
1107 RESULT_TYPE(f1
->type
);
1109 if (v1
.type
== T_PATH
)
1113 if ((v2
.type
== T_SET
) && (v2
.val
.t
->from
.type
== T_INT
))
1114 RESULT_(T_PATH
, ad
, [[ as_path_filter(fpool
, v1
.val
.ad
, v2
.val
.t
, key
, 1) ]]);
1116 runtime("Can't filter integer");
1119 else if (v1
.type
== T_CLIST
)
1121 /* Community (or cluster) list */
1124 if ((v2
.type
== T_SET
) && clist_set_type(v2
.val
.t
, &dummy
) || (v2
.type
== T_CLIST
))
1125 RESULT_(T_CLIST
, ad
, [[ clist_filter(fpool
, v1
.val
.ad
, &v2
, 1) ]]);
1127 runtime("Can't filter pair");
1130 else if (v1
.type
== T_ECLIST
)
1132 /* v2.val is either EC or EC-set */
1133 if ((v2
.type
== T_SET
) && eclist_set_type(v2
.val
.t
) || (v2
.type
== T_ECLIST
))
1134 RESULT_(T_ECLIST
, ad
, [[ eclist_filter(fpool
, v1
.val
.ad
, &v2
, 1) ]]);
1136 runtime("Can't filter ec");
1139 else if (v1
.type
== T_LCLIST
)
1141 /* v2.val is either LC or LC-set */
1142 if ((v2
.type
== T_SET
) && lclist_set_type(v2
.val
.t
) || (v2
.type
== T_LCLIST
))
1143 RESULT_(T_LCLIST
, ad
, [[ lclist_filter(fpool
, v1
.val
.ad
, &v2
, 1) ]]);
1145 runtime("Can't filter lc");
1149 runtime("Can't filter non-[e|l]clist");
1152 INST(FI_ROA_CHECK_IMPLICIT
, 0, 1) { /* ROA Check */
1155 struct rtable
*table
= rtc
->table
;
1158 const net_addr
*net
= (*fs
->rte
)->net
->n
.addr
;
1160 /* We ignore temporary attributes, probably not a problem here */
1161 /* 0x02 is a value of BA_AS_PATH, we don't want to include BGP headers */
1162 eattr
*e
= ea_find(*fs
->eattrs
, EA_CODE(PROTOCOL_BGP
, 0x02));
1164 if (!e
|| ((e
->type
& EAF_TYPE_MASK
) != EAF_TYPE_AS_PATH
))
1165 runtime("Missing AS_PATH attribute");
1168 as_path_get_last(e
->u
.ptr
, &as
);
1171 runtime("Missing ROA table");
1173 if (table
->addr_type
!= NET_ROA4
&& table
->addr_type
!= NET_ROA6
)
1174 runtime("Table type must be either ROA4 or ROA6");
1176 if (table
->addr_type
!= (net
->type
== NET_IP4
? NET_ROA4
: NET_ROA6
))
1177 RESULT(T_ENUM_ROA
, i
, ROA_UNKNOWN
); /* Prefix and table type mismatch */
1179 RESULT(T_ENUM_ROA
, i
, [[ net_roa_check(table
, net
, as
) ]]);
1182 INST(FI_ROA_CHECK_EXPLICIT
, 2, 1) { /* ROA Check */
1187 struct rtable
*table
= rtc
->table
;
1192 runtime("Missing ROA table");
1194 if (table
->addr_type
!= NET_ROA4
&& table
->addr_type
!= NET_ROA6
)
1195 runtime("Table type must be either ROA4 or ROA6");
1197 if (table
->addr_type
!= (v1
.val
.net
->type
== NET_IP4
? NET_ROA4
: NET_ROA6
))
1198 RESULT(T_ENUM_ROA
, i
, ROA_UNKNOWN
); /* Prefix and table type mismatch */
1200 RESULT(T_ENUM_ROA
, i
, [[ net_roa_check(table
, v1
.val
.net
, as
) ]]);
1204 INST(FI_FORMAT
, 1, 0) { /* Format */
1206 RESULT(T_STRING
, s
, val_format_str(fpool
, &v1
));
1209 INST(FI_ASSERT
, 1, 0) { /* Birdtest Assert */
1213 FID_MEMBER(char *, s
, [[strcmp(f1
->s
, f2
->s
)]], "string %s", item
->s
);
1217 if (!bt_assert_hook
)
1218 runtime("No bt_assert hook registered, can't assert");
1220 bt_assert_hook(v1
.val
.i
, what
);