]>
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
);
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
));
264 int check
, ipv4_used
;
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 check
= 0; RESULT(T_EC
, ec
, ec_generic(key
, val
));
285 else if (ipv4_used
) {
286 check
= 1; RESULT(T_EC
, ec
, ec_ip4(ecs
, key
, val
));
288 else if (key
< 0x10000) {
289 check
= 0; RESULT(T_EC
, ec
, ec_as2(ecs
, key
, val
));
292 check
= 1; RESULT(T_EC
, ec
, ec_as4(ecs
, key
, val
));
295 if (check
&& (val
> 0xFFFF))
296 runtime("Value %u > %u out of bounds in EC constructor", val
, 0xFFFF);
299 INST(FI_LC_CONSTRUCT
, 3, 1) {
303 RESULT(T_LC
, lc
, [[(lcomm
) { v1
.val
.i
, v2
.val
.i
, v3
.val
.i
}]]);
306 INST(FI_PATHMASK_CONSTRUCT
, 0, 1) {
309 struct f_path_mask
*pm
= falloc(sizeof(struct f_path_mask
) + whati
->varcount
* sizeof(struct f_path_mask_item
));
310 pm
->len
= whati
->varcount
;
312 for (uint i
=0; i
<whati
->varcount
; i
++) {
313 switch (vv(i
).type
) {
314 case T_PATH_MASK_ITEM
:
315 pm
->item
[i
] = vv(i
).val
.pmi
;
319 pm
->item
[i
] = (struct f_path_mask_item
) {
326 if (vv(i
).val
.t
->from
.type
!= T_INT
)
327 runtime("Only integer sets allowed in path mask");
329 pm
->item
[i
] = (struct f_path_mask_item
) {
336 runtime( "Error resolving path mask template: value not an integer" );
340 RESULT(T_PATH_MASK
, path_mask
, pm
);
343 /* Relational operators */
348 RESULT(T_BOOL
, i
, !val_same(&v1
, &v2
));
354 RESULT(T_BOOL
, i
, val_same(&v1
, &v2
));
362 int i
= val_compare(&v1
, &v2
);
363 if (i
== F_CMP_ERROR
)
364 runtime( "Can't compare values of incompatible types" );
365 RESULT(T_BOOL
, i
, (i
== -1));
373 int i
= val_compare(&v1
, &v2
);
374 if (i
== F_CMP_ERROR
)
375 runtime( "Can't compare values of incompatible types" );
376 RESULT(T_BOOL
, i
, (i
!= 1));
381 RESULT(T_BOOL
, i
, !v1
.val
.i
);
384 INST(FI_MATCH
, 2, 1) {
387 int i
= val_in_range(&v1
, &v2
);
388 if (i
== F_CMP_ERROR
)
389 runtime( "~ applied on unknown type pair" );
390 RESULT(T_BOOL
, i
, !!i
);
393 INST(FI_NOT_MATCH
, 2, 1) {
396 int i
= val_in_range(&v1
, &v2
);
397 if (i
== F_CMP_ERROR
)
398 runtime( "!~ applied on unknown type pair" );
399 RESULT(T_BOOL
, i
, !i
);
402 INST(FI_DEFINED
, 1, 1) {
404 RESULT(T_BOOL
, i
, (v1
.type
!= T_VOID
) && !undef_value(v1
));
407 INST(FI_TYPE
, 1, 1) {
408 ARG_ANY(1); /* There may be more types supporting this operation */
412 RESULT(T_ENUM_NETTYPE
, i
, v1
.val
.net
->type
);
415 runtime( "Can't determine type of this item" );
419 INST(FI_IS_V4
, 1, 1) {
421 RESULT(T_BOOL
, i
, ipa_is_ip4(v1
.val
.ip
));
424 /* Set to indirect value prepared in v1 */
425 INST(FI_VAR_SET
, 1, 0) {
429 ARG_TYPE(1, sym
->class & 0xff);
431 fstk
->vstk
[curline
.vbase
+ sym
->offset
] = v1
;
434 INST(FI_VAR_GET
, 0, 1) {
437 RESULT_TYPE(sym
->class & 0xff);
438 RESULT_VAL(fstk
->vstk
[curline
.vbase
+ sym
->offset
]);
441 INST(FI_CONSTANT
, 0, 1) {
445 [[ !val_same(&(f1
->val
), &(f2
->val
)) ]],
447 val_dump(&(item
->val
))
450 RESULT_TYPE(val
.type
);
454 INST(FI_CONDITION
, 1, 0) {
462 INST(FI_PRINT
, 0, 0) {
466 if (whati
->varcount
&& !(fs
->flags
& FF_SILENT
))
467 for (uint i
=0; i
<whati
->varcount
; i
++)
468 val_format(&(vv(i
)), &fs
->buf
);
471 INST(FI_FLUSH
, 0, 0) {
473 if (!(fs
->flags
& FF_SILENT
))
474 /* After log_commit, the buffer is reset */
475 log_commit(*L_INFO
, &fs
->buf
);
480 FID_MEMBER(enum filter_return
, fret
, f1
->fret
!= f2
->fret
, "%s", filter_return_str(item
->fret
));
482 switch (whati
->fret
) {
484 die( "Filter asked me to die" );
485 case F_ACCEPT
: /* Should take care about turning ACCEPT into MODIFY */
487 case F_REJECT
: /* Maybe print complete route along with reason to reject route? */
488 return fret
; /* We have to return now, no more processing. */
490 bug( "unknown return type: Can't happen");
494 INST(FI_RTA_GET
, 0, 1) {
498 struct rta
*rta
= (*fs
->rte
)->attrs
;
502 case SA_FROM
: RESULT(sa
.f_type
, ip
, rta
->from
); break;
503 case SA_GW
: RESULT(sa
.f_type
, ip
, rta
->nh
.gw
); break;
504 case SA_NET
: RESULT(sa
.f_type
, net
, (*fs
->rte
)->net
->n
.addr
); break;
505 case SA_PROTO
: RESULT(sa
.f_type
, s
, rta
->src
->proto
->name
); break;
506 case SA_SOURCE
: RESULT(sa
.f_type
, i
, rta
->source
); break;
507 case SA_SCOPE
: RESULT(sa
.f_type
, i
, rta
->scope
); break;
508 case SA_DEST
: RESULT(sa
.f_type
, i
, rta
->dest
); break;
509 case SA_IFNAME
: RESULT(sa
.f_type
, s
, rta
->nh
.iface
? rta
->nh
.iface
->name
: ""); break;
510 case SA_IFINDEX
: RESULT(sa
.f_type
, i
, rta
->nh
.iface
? rta
->nh
.iface
->index
: 0); break;
513 bug("Invalid static attribute access (%u/%u)", sa
.f_type
, sa
.sa_code
);
518 INST(FI_RTA_SET
, 1, 0) {
522 ARG_TYPE(1, sa
.f_type
);
526 struct rta
*rta
= (*fs
->rte
)->attrs
;
531 rta
->from
= v1
.val
.ip
;
536 ip_addr ip
= v1
.val
.ip
;
537 neighbor
*n
= neigh_find(rta
->src
->proto
, ip
, NULL
, 0);
538 if (!n
|| (n
->scope
== SCOPE_HOST
))
539 runtime( "Invalid gw address" );
541 rta
->dest
= RTD_UNICAST
;
543 rta
->nh
.iface
= n
->iface
;
545 rta
->hostentry
= NULL
;
550 rta
->scope
= v1
.val
.i
;
556 if ((i
!= RTD_BLACKHOLE
) && (i
!= RTD_UNREACHABLE
) && (i
!= RTD_PROHIBIT
))
557 runtime( "Destination can be changed only to blackhole, unreachable or prohibit" );
560 rta
->nh
.gw
= IPA_NONE
;
561 rta
->nh
.iface
= NULL
;
563 rta
->hostentry
= NULL
;
569 struct iface
*ifa
= if_find_by_name(v1
.val
.s
);
571 runtime( "Invalid iface name" );
573 rta
->dest
= RTD_UNICAST
;
574 rta
->nh
.gw
= IPA_NONE
;
577 rta
->hostentry
= NULL
;
582 bug("Invalid static attribute access (%u/%u)", sa
.f_type
, sa
.sa_code
);
587 INST(FI_EA_GET
, 0, 1) { /* Access to extended attributes */
591 RESULT_TYPE(da
.f_type
);
593 eattr
*e
= ea_find(*fs
->eattrs
, da
.ea_code
);
596 /* A special case: undefined as_path looks like empty as_path */
597 if (da
.type
== EAF_TYPE_AS_PATH
) {
598 RESULT_(T_PATH
, ad
, &null_adata
);
602 /* The same special case for int_set */
603 if (da
.type
== EAF_TYPE_INT_SET
) {
604 RESULT_(T_CLIST
, ad
, &null_adata
);
608 /* The same special case for ec_set */
609 if (da
.type
== EAF_TYPE_EC_SET
) {
610 RESULT_(T_ECLIST
, ad
, &null_adata
);
614 /* The same special case for lc_set */
615 if (da
.type
== EAF_TYPE_LC_SET
) {
616 RESULT_(T_LCLIST
, ad
, &null_adata
);
620 /* Undefined value */
625 switch (e
->type
& EAF_TYPE_MASK
) {
627 RESULT_(da
.f_type
, i
, e
->u
.data
);
629 case EAF_TYPE_ROUTER_ID
:
630 RESULT_(T_QUAD
, i
, e
->u
.data
);
632 case EAF_TYPE_OPAQUE
:
633 RESULT_(T_ENUM_EMPTY
, i
, 0);
635 case EAF_TYPE_IP_ADDRESS
:
636 RESULT_(T_IP
, ip
, *((ip_addr
*) e
->u
.ptr
->data
));
638 case EAF_TYPE_AS_PATH
:
639 RESULT_(T_PATH
, ad
, e
->u
.ptr
);
641 case EAF_TYPE_BITFIELD
:
642 RESULT_(T_BOOL
, i
, !!(e
->u
.data
& (1u << da
.bit
)));
644 case EAF_TYPE_INT_SET
:
645 RESULT_(T_CLIST
, ad
, e
->u
.ptr
);
647 case EAF_TYPE_EC_SET
:
648 RESULT_(T_ECLIST
, ad
, e
->u
.ptr
);
650 case EAF_TYPE_LC_SET
:
651 RESULT_(T_LCLIST
, ad
, e
->u
.ptr
);
657 bug("Unknown dynamic attribute type");
662 INST(FI_EA_SET
, 1, 0) {
667 ARG_TYPE(1, da
.f_type
);
669 struct ea_list
*l
= lp_alloc(fs
->pool
, sizeof(struct ea_list
) + sizeof(eattr
));
672 l
->flags
= EALF_SORTED
;
674 l
->attrs
[0].id
= da
.ea_code
;
675 l
->attrs
[0].flags
= 0;
676 l
->attrs
[0].type
= da
.type
| EAF_ORIGINATED
| EAF_FRESH
;
680 case EAF_TYPE_ROUTER_ID
:
681 l
->attrs
[0].u
.data
= v1
.val
.i
;
684 case EAF_TYPE_OPAQUE
:
685 runtime( "Setting opaque attribute is not allowed" );
688 case EAF_TYPE_IP_ADDRESS
:;
689 int len
= sizeof(ip_addr
);
690 struct adata
*ad
= lp_alloc(fs
->pool
, sizeof(struct adata
) + len
);
692 (* (ip_addr
*) ad
->data
) = v1
.val
.ip
;
693 l
->attrs
[0].u
.ptr
= ad
;
696 case EAF_TYPE_AS_PATH
:
697 case EAF_TYPE_INT_SET
:
698 case EAF_TYPE_EC_SET
:
699 case EAF_TYPE_LC_SET
:
700 l
->attrs
[0].u
.ptr
= v1
.val
.ad
;
703 case EAF_TYPE_BITFIELD
:
705 /* First, we have to find the old value */
706 eattr
*e
= ea_find(*fs
->eattrs
, da
.ea_code
);
707 u32 data
= e
? e
->u
.data
: 0;
710 l
->attrs
[0].u
.data
= data
| (1u << da
.bit
);
712 l
->attrs
[0].u
.data
= data
& ~(1u << da
.bit
);
717 bug("Unknown dynamic attribute type");
721 l
->next
= *fs
->eattrs
;
726 INST(FI_EA_UNSET
, 0, 0) {
732 struct ea_list
*l
= lp_alloc(fs
->pool
, sizeof(struct ea_list
) + sizeof(eattr
));
735 l
->flags
= EALF_SORTED
;
737 l
->attrs
[0].id
= da
.ea_code
;
738 l
->attrs
[0].flags
= 0;
739 l
->attrs
[0].type
= EAF_TYPE_UNDEF
| EAF_ORIGINATED
| EAF_FRESH
;
740 l
->attrs
[0].u
.data
= 0;
743 l
->next
= *fs
->eattrs
;
748 INST(FI_PREF_GET
, 0, 1) {
750 RESULT(T_INT
, i
, (*fs
->rte
)->pref
);
753 INST(FI_PREF_SET
, 1, 0) {
756 if (v1
.val
.i
> 0xFFFF)
757 runtime( "Setting preference value out of bounds" );
759 (*fs
->rte
)->pref
= v1
.val
.i
;
762 INST(FI_LENGTH
, 1, 1) { /* Get length of */
765 case T_NET
: RESULT(T_INT
, i
, net_pxlen(v1
.val
.net
)); break;
766 case T_PATH
: RESULT(T_INT
, i
, as_path_getlen(v1
.val
.ad
)); break;
767 case T_CLIST
: RESULT(T_INT
, i
, int_set_get_size(v1
.val
.ad
)); break;
768 case T_ECLIST
: RESULT(T_INT
, i
, ec_set_get_size(v1
.val
.ad
)); break;
769 case T_LCLIST
: RESULT(T_INT
, i
, lc_set_get_size(v1
.val
.ad
)); break;
770 default: runtime( "Prefix, path, clist or eclist expected" );
774 INST(FI_NET_SRC
, 1, 1) { /* Get src prefix */
777 net_addr_union
*net
= (void *) v1
.val
.net
;
778 net_addr
*src
= falloc(sizeof(net_addr_ip6
));
781 switch(v1
.val
.net
->type
) {
783 part
= flow4_get_part(&net
->flow4
, FLOW_TYPE_SRC_PREFIX
);
785 net_fill_ip4(src
, flow_read_ip4_part(part
), flow_read_pxlen(part
));
787 net_fill_ip4(src
, IP4_NONE
, 0);
791 part
= flow6_get_part(&net
->flow6
, FLOW_TYPE_SRC_PREFIX
);
793 net_fill_ip6(src
, flow_read_ip6_part(part
), flow_read_pxlen(part
));
795 net_fill_ip6(src
, IP6_NONE
, 0);
799 net_fill_ip6(src
, net
->ip6_sadr
.src_prefix
, net
->ip6_sadr
.src_pxlen
);
803 runtime( "Flow or SADR expected" );
806 RESULT(T_NET
, net
, src
);
809 INST(FI_NET_DST
, 1, 1) { /* Get dst prefix */
812 net_addr_union
*net
= (void *) v1
.val
.net
;
813 net_addr
*dst
= falloc(sizeof(net_addr_ip6
));
816 switch(v1
.val
.net
->type
) {
818 part
= flow4_get_part(&net
->flow4
, FLOW_TYPE_DST_PREFIX
);
820 net_fill_ip4(dst
, flow_read_ip4_part(part
), flow_read_pxlen(part
));
822 net_fill_ip4(dst
, IP4_NONE
, 0);
826 part
= flow6_get_part(&net
->flow6
, FLOW_TYPE_DST_PREFIX
);
828 net_fill_ip6(dst
, flow_read_ip6_part(part
), flow_read_pxlen(part
));
830 net_fill_ip6(dst
, IP6_NONE
, 0);
834 net_fill_ip6(dst
, net
->ip6_sadr
.dst_prefix
, net
->ip6_sadr
.dst_pxlen
);
838 runtime( "Flow or SADR expected" );
841 RESULT(T_NET
, net
, dst
);
844 INST(FI_ROA_MAXLEN
, 1, 1) { /* Get ROA max prefix length */
846 if (!net_is_roa(v1
.val
.net
))
847 runtime( "ROA expected" );
849 RESULT(T_INT
, i
, (v1
.val
.net
->type
== NET_ROA4
) ?
850 ((net_addr_roa4
*) v1
.val
.net
)->max_pxlen
:
851 ((net_addr_roa6
*) v1
.val
.net
)->max_pxlen
);
854 INST(FI_ROA_ASN
, 1, 1) { /* Get ROA ASN */
856 if (!net_is_roa(v1
.val
.net
))
857 runtime( "ROA expected" );
859 RESULT(T_INT
, i
, (v1
.val
.net
->type
== NET_ROA4
) ?
860 ((net_addr_roa4
*) v1
.val
.net
)->asn
:
861 ((net_addr_roa6
*) v1
.val
.net
)->asn
);
864 INST(FI_IP
, 1, 1) { /* Convert prefix to ... */
866 RESULT(T_IP
, ip
, net_prefix(v1
.val
.net
));
869 INST(FI_ROUTE_DISTINGUISHER
, 1, 1) {
871 if (!net_is_vpn(v1
.val
.net
))
872 runtime( "VPN address expected" );
873 RESULT(T_RD
, ec
, net_rd(v1
.val
.net
));
876 INST(FI_AS_PATH_FIRST
, 1, 1) { /* Get first ASN from AS PATH */
879 as_path_get_first(v1
.val
.ad
, &as
);
880 RESULT(T_INT
, i
, as
);
883 INST(FI_AS_PATH_LAST
, 1, 1) { /* Get last ASN from AS PATH */
886 as_path_get_last(v1
.val
.ad
, &as
);
887 RESULT(T_INT
, i
, as
);
890 INST(FI_AS_PATH_LAST_NAG
, 1, 1) { /* Get last ASN from non-aggregated part of AS PATH */
892 RESULT(T_INT
, i
, as_path_get_last_nonaggregated(v1
.val
.ad
));
895 INST(FI_RETURN
, 1, 1) {
897 /* Acquire the return value */
899 uint retpos
= fstk
->vcnt
;
901 /* Drop every sub-block including ourselves */
902 while ((fstk
->ecnt
-- > 0) && !(fstk
->estk
[fstk
->ecnt
].emask
& FE_RETURN
))
905 /* Now we are at the caller frame; if no such, try to convert to accept/reject. */
907 if (fstk
->vstk
[retpos
].type
== T_BOOL
)
908 if (fstk
->vstk
[retpos
].val
.i
)
913 runtime("Can't return non-bool from non-function");
915 /* Set the value stack position, overwriting the former implicit void */
916 fstk
->vcnt
= fstk
->estk
[fstk
->ecnt
].ventry
- 1;
918 /* Copy the return value */
919 RESULT_VAL(fstk
->vstk
[retpos
]);
922 INST(FI_CALL
, 0, 1) {
927 if (!(f1
->sym
->flags
& SYM_FLAG_SAME
))
931 /* Push the body on stack */
932 LINEX(sym
->function
);
933 curline
.emask
|= FE_RETURN
;
935 /* Before this instruction was called, there was the T_VOID
936 * automatic return value pushed on value stack and also
937 * sym->function->args function arguments. Setting the
938 * vbase to point to first argument. */
939 ASSERT(curline
.ventry
>= sym
->function
->args
);
940 curline
.ventry
-= sym
->function
->args
;
941 curline
.vbase
= curline
.ventry
;
943 /* Storage for local variables */
944 memset(&(fstk
->vstk
[fstk
->vcnt
]), 0, sizeof(struct f_val
) * sym
->function
->vars
);
945 fstk
->vcnt
+= sym
->function
->vars
;
948 INST(FI_DROP_RESULT
, 1, 0) {
953 INST(FI_SWITCH
, 1, 0) {
956 FID_MEMBER(struct f_tree
*, tree
, [[!same_tree(f1
->tree
, f2
->tree
)]], "tree %p", item
->tree
);
958 const struct f_tree
*t
= find_tree(tree
, &v1
);
961 t
= find_tree(tree
, &v1
);
963 debug( "No else statement?\n");
964 FID_HIC(,break,return NULL
);
967 /* It is actually possible to have t->data NULL */
972 INST(FI_IP_MASK
, 2, 1) { /* IP.MASK(val) */
975 RESULT(T_IP
, ip
, [[ ipa_is_ip4(v1
.val
.ip
) ?
976 ipa_from_ip4(ip4_and(ipa_to_ip4(v1
.val
.ip
), ip4_mkmask(v2
.val
.i
))) :
977 ipa_from_ip6(ip6_and(ipa_to_ip6(v1
.val
.ip
), ip6_mkmask(v2
.val
.i
))) ]]);
980 INST(FI_PATH_PREPEND
, 2, 1) { /* Path prepend */
983 RESULT(T_PATH
, ad
, [[ as_path_prepend(fpool
, v1
.val
.ad
, v2
.val
.i
) ]]);
986 INST(FI_CLIST_ADD
, 2, 1) { /* (Extended) Community list add */
989 RESULT_TYPE(f1
->type
);
991 if (v1
.type
== T_PATH
)
992 runtime("Can't add to path");
994 else if (v1
.type
== T_CLIST
)
996 /* Community (or cluster) list */
999 if ((v2
.type
== T_PAIR
) || (v2
.type
== T_QUAD
))
1000 RESULT_(T_CLIST
, ad
, [[ int_set_add(fpool
, v1
.val
.ad
, v2
.val
.i
) ]]);
1001 /* IP->Quad implicit conversion */
1002 else if (val_is_ip4(&v2
))
1003 RESULT_(T_CLIST
, ad
, [[ int_set_add(fpool
, v1
.val
.ad
, ipa_to_u32(v2
.val
.ip
)) ]]);
1004 else if ((v2
.type
== T_SET
) && clist_set_type(v2
.val
.t
, &dummy
))
1005 runtime("Can't add set");
1006 else if (v2
.type
== T_CLIST
)
1007 RESULT_(T_CLIST
, ad
, [[ int_set_union(fpool
, v1
.val
.ad
, v2
.val
.ad
) ]]);
1009 runtime("Can't add non-pair");
1012 else if (v1
.type
== T_ECLIST
)
1014 /* v2.val is either EC or EC-set */
1015 if ((v2
.type
== T_SET
) && eclist_set_type(v2
.val
.t
))
1016 runtime("Can't add set");
1017 else if (v2
.type
== T_ECLIST
)
1018 RESULT_(T_ECLIST
, ad
, [[ ec_set_union(fpool
, v1
.val
.ad
, v2
.val
.ad
) ]]);
1019 else if (v2
.type
!= T_EC
)
1020 runtime("Can't add non-ec");
1022 RESULT_(T_ECLIST
, ad
, [[ ec_set_add(fpool
, v1
.val
.ad
, v2
.val
.ec
) ]]);
1025 else if (v1
.type
== T_LCLIST
)
1027 /* v2.val is either LC or LC-set */
1028 if ((v2
.type
== T_SET
) && lclist_set_type(v2
.val
.t
))
1029 runtime("Can't add set");
1030 else if (v2
.type
== T_LCLIST
)
1031 RESULT_(T_LCLIST
, ad
, [[ lc_set_union(fpool
, v1
.val
.ad
, v2
.val
.ad
) ]]);
1032 else if (v2
.type
!= T_LC
)
1033 runtime("Can't add non-lc");
1035 RESULT_(T_LCLIST
, ad
, [[ lc_set_add(fpool
, v1
.val
.ad
, v2
.val
.lc
) ]]);
1040 runtime("Can't add to non-[e|l]clist");
1043 INST(FI_CLIST_DEL
, 2, 1) { /* (Extended) Community list add or delete */
1046 RESULT_TYPE(f1
->type
);
1048 if (v1
.type
== T_PATH
)
1050 const struct f_tree
*set
= NULL
;
1053 if (v2
.type
== T_INT
)
1055 else if ((v2
.type
== T_SET
) && (v2
.val
.t
->from
.type
== T_INT
))
1058 runtime("Can't delete non-integer (set)");
1060 RESULT_(T_PATH
, ad
, [[ as_path_filter(fpool
, v1
.val
.ad
, set
, key
, 0) ]]);
1063 else if (v1
.type
== T_CLIST
)
1065 /* Community (or cluster) list */
1068 if ((v2
.type
== T_PAIR
) || (v2
.type
== T_QUAD
))
1069 RESULT_(T_CLIST
, ad
, [[ int_set_del(fpool
, v1
.val
.ad
, v2
.val
.i
) ]]);
1070 /* IP->Quad implicit conversion */
1071 else if (val_is_ip4(&v2
))
1072 RESULT_(T_CLIST
, ad
, [[ int_set_del(fpool
, v1
.val
.ad
, ipa_to_u32(v2
.val
.ip
)) ]]);
1073 else if ((v2
.type
== T_SET
) && clist_set_type(v2
.val
.t
, &dummy
) || (v2
.type
== T_CLIST
))
1074 RESULT_(T_CLIST
, ad
, [[ clist_filter(fpool
, v1
.val
.ad
, &v2
, 0) ]]);
1076 runtime("Can't delete non-pair");
1079 else if (v1
.type
== T_ECLIST
)
1081 /* v2.val is either EC or EC-set */
1082 if ((v2
.type
== T_SET
) && eclist_set_type(v2
.val
.t
) || (v2
.type
== T_ECLIST
))
1083 RESULT_(T_ECLIST
, ad
, [[ eclist_filter(fpool
, v1
.val
.ad
, &v2
, 0) ]]);
1084 else if (v2
.type
!= T_EC
)
1085 runtime("Can't delete non-ec");
1087 RESULT_(T_ECLIST
, ad
, [[ ec_set_del(fpool
, v1
.val
.ad
, v2
.val
.ec
) ]]);
1090 else if (v1
.type
== T_LCLIST
)
1092 /* v2.val is either LC or LC-set */
1093 if ((v2
.type
== T_SET
) && lclist_set_type(v2
.val
.t
) || (v2
.type
== T_LCLIST
))
1094 RESULT_(T_LCLIST
, ad
, [[ lclist_filter(fpool
, v1
.val
.ad
, &v2
, 0) ]]);
1095 else if (v2
.type
!= T_LC
)
1096 runtime("Can't delete non-lc");
1098 RESULT_(T_LCLIST
, ad
, [[ lc_set_del(fpool
, v1
.val
.ad
, v2
.val
.lc
) ]]);
1102 runtime("Can't delete in non-[e|l]clist");
1105 INST(FI_CLIST_FILTER
, 2, 1) { /* (Extended) Community list add or delete */
1108 RESULT_TYPE(f1
->type
);
1110 if (v1
.type
== T_PATH
)
1114 if ((v2
.type
== T_SET
) && (v2
.val
.t
->from
.type
== T_INT
))
1115 RESULT_(T_PATH
, ad
, [[ as_path_filter(fpool
, v1
.val
.ad
, v2
.val
.t
, key
, 1) ]]);
1117 runtime("Can't filter integer");
1120 else if (v1
.type
== T_CLIST
)
1122 /* Community (or cluster) list */
1125 if ((v2
.type
== T_SET
) && clist_set_type(v2
.val
.t
, &dummy
) || (v2
.type
== T_CLIST
))
1126 RESULT_(T_CLIST
, ad
, [[ clist_filter(fpool
, v1
.val
.ad
, &v2
, 1) ]]);
1128 runtime("Can't filter pair");
1131 else if (v1
.type
== T_ECLIST
)
1133 /* v2.val is either EC or EC-set */
1134 if ((v2
.type
== T_SET
) && eclist_set_type(v2
.val
.t
) || (v2
.type
== T_ECLIST
))
1135 RESULT_(T_ECLIST
, ad
, [[ eclist_filter(fpool
, v1
.val
.ad
, &v2
, 1) ]]);
1137 runtime("Can't filter ec");
1140 else if (v1
.type
== T_LCLIST
)
1142 /* v2.val is either LC or LC-set */
1143 if ((v2
.type
== T_SET
) && lclist_set_type(v2
.val
.t
) || (v2
.type
== T_LCLIST
))
1144 RESULT_(T_LCLIST
, ad
, [[ lclist_filter(fpool
, v1
.val
.ad
, &v2
, 1) ]]);
1146 runtime("Can't filter lc");
1150 runtime("Can't filter non-[e|l]clist");
1153 INST(FI_ROA_CHECK_IMPLICIT
, 0, 1) { /* ROA Check */
1156 struct rtable
*table
= rtc
->table
;
1159 const net_addr
*net
= (*fs
->rte
)->net
->n
.addr
;
1161 /* We ignore temporary attributes, probably not a problem here */
1162 /* 0x02 is a value of BA_AS_PATH, we don't want to include BGP headers */
1163 eattr
*e
= ea_find(*fs
->eattrs
, EA_CODE(PROTOCOL_BGP
, 0x02));
1165 if (!e
|| ((e
->type
& EAF_TYPE_MASK
) != EAF_TYPE_AS_PATH
))
1166 runtime("Missing AS_PATH attribute");
1169 as_path_get_last(e
->u
.ptr
, &as
);
1172 runtime("Missing ROA table");
1174 if (table
->addr_type
!= NET_ROA4
&& table
->addr_type
!= NET_ROA6
)
1175 runtime("Table type must be either ROA4 or ROA6");
1177 if (table
->addr_type
!= (net
->type
== NET_IP4
? NET_ROA4
: NET_ROA6
))
1178 RESULT(T_ENUM_ROA
, i
, ROA_UNKNOWN
); /* Prefix and table type mismatch */
1180 RESULT(T_ENUM_ROA
, i
, [[ net_roa_check(table
, net
, as
) ]]);
1183 INST(FI_ROA_CHECK_EXPLICIT
, 2, 1) { /* ROA Check */
1188 struct rtable
*table
= rtc
->table
;
1193 runtime("Missing ROA table");
1195 if (table
->addr_type
!= NET_ROA4
&& table
->addr_type
!= NET_ROA6
)
1196 runtime("Table type must be either ROA4 or ROA6");
1198 if (table
->addr_type
!= (v1
.val
.net
->type
== NET_IP4
? NET_ROA4
: NET_ROA6
))
1199 RESULT(T_ENUM_ROA
, i
, ROA_UNKNOWN
); /* Prefix and table type mismatch */
1201 RESULT(T_ENUM_ROA
, i
, [[ net_roa_check(table
, v1
.val
.net
, as
) ]]);
1205 INST(FI_FORMAT
, 1, 0) { /* Format */
1207 RESULT(T_STRING
, s
, val_format_str(fpool
, &v1
));
1210 INST(FI_ASSERT
, 1, 0) { /* Birdtest Assert */
1214 FID_MEMBER(char *, s
, [[strcmp(f1
->s
, f2
->s
)]], "string %s", item
->s
);
1218 if (!bt_assert_hook
)
1219 runtime("No bt_assert hook registered, can't assert");
1221 bt_assert_hook(v1
.val
.i
, what
);