]>
git.ipfire.org Git - thirdparty/bird.git/blob - filter/f-inst.c
6977f65612212e5af25b12fdb01c6acfc30e21da
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 ARG_TYPE(num, type); just declare the type of argument
66 * m4_dnl VARARG; variable-length argument list; accessible by vv(i) and whati->varcount
67 * m4_dnl LINE(num, out); this argument has to be converted to its own f_line
68 * m4_dnl SYMBOL; symbol handed from config
69 * m4_dnl STATIC_ATTR; static attribute definition
70 * m4_dnl DYNAMIC_ATTR; dynamic attribute definition
71 * m4_dnl RTC; route table config
72 * m4_dnl ACCESS_RTE; this instruction needs route
73 * m4_dnl ACCESS_EATTRS; this instruction needs extended attributes
75 * m4_dnl METHOD_CONSTRUCTOR(name); this instruction is in fact a method of the first argument's type; register it with the given name for that type
77 * m4_dnl FID_MEMBER( custom instruction member
78 * m4_dnl C type, for storage in structs
79 * m4_dnl name, how the member is named
80 * m4_dnl comparator for same(), if different, this should be TRUE (CAVEAT)
81 * m4_dnl dump format string debug -> format string for bvsnprintf
82 * m4_dnl dump format args appropriate args
85 * m4_dnl RESULT(type, union-field, value); putting this on value stack
86 * m4_dnl RESULT_(type, union-field, value); like RESULT(), but do not declare the type
87 * m4_dnl RESULT_VAL(value-struct); pass the struct f_val directly
88 * m4_dnl RESULT_TYPE(type); just declare the type of result value
89 * m4_dnl RESULT_VOID; return undef
92 * Note that runtime arguments m4_dnl (ARG*, VARARG) must be defined before
93 * parse-time arguments m4_dnl (LINE, SYMBOL, ...). During linearization,
94 * first ones move position in f_line by linearizing arguments first, while
95 * second ones store data to the current position.
97 * Also note that the { ... } blocks are not respected by M4 at all.
98 * If you get weird unmatched-brace-pair errors, check what it generated and why.
99 * What is really considered as one instruction is not the { ... } block
100 * after m4_dnl INST() but all the code between them.
102 * Other code is just copied into the interpreter part.
104 * It's also possible to declare type methods in a short way:
106 * m4_dnl METHOD(type, method name, argument count, code)
107 * m4_dnl METHOD_R(type, method name, argument count, result type, union-field, value)
109 * The filter language uses a simple type system, where values have types
110 * (constants T_*) and also terms (instructions) are statically typed. Our
111 * static typing is partial (some terms do not declare types of arguments
112 * or results), therefore it can detect most but not all type errors and
113 * therefore we still have runtime type checks.
115 * m4_dnl Types of arguments are declared by macros ARG() and ARG_TYPE(),
116 * m4_dnl types of results are declared by RESULT() and RESULT_TYPE().
117 * m4_dnl Macros ARG_ANY(), RESULT_() and RESULT_VAL() do not declare types
118 * m4_dnl themselves, but can be combined with ARG_TYPE() / RESULT_TYPE().
120 * m4_dnl Note that types should be declared only once. If there are
121 * m4_dnl multiple RESULT() macros in an instruction definition, they must
122 * m4_dnl use the exact same expression for type, or they should be replaced
123 * m4_dnl by multiple RESULT_() macros and a common RESULT_TYPE() macro.
124 * m4_dnl See e.g. FI_EA_GET or FI_MIN instructions.
127 * If you are satisfied with this, you don't need to read the following
128 * detailed description of what is really done with the instruction definitions.
130 * m4_dnl Now let's look under the cover. The code between each INST()
131 * m4_dnl is copied to several places, namely these (numbered by the M4 diversions
132 * m4_dnl used in filter/decl.m4):
134 * m4_dnl (102) struct f_inst *f_new_inst(FI_EXAMPLE [[ put it here ]])
136 * m4_dnl ... (common code)
137 * m4_dnl (103) [[ put it here ]]
139 * m4_dnl if (all arguments are constant)
140 * m4_dnl (108) [[ put it here ]]
142 * m4_dnl For writing directly to constructor argument list, use FID_NEW_ARGS.
143 * m4_dnl For computing something in constructor (103), use FID_NEW_BODY.
144 * m4_dnl For constant pre-interpretation (108), see below at FID_INTERPRET_BODY.
146 * m4_dnl struct f_inst {
147 * m4_dnl ... (common fields)
150 * m4_dnl (101) [[ put it here ]]
151 * m4_dnl } i_FI_EXAMPLE;
155 * m4_dnl This structure is returned from constructor.
156 * m4_dnl For writing directly to this structure, use FID_STRUCT_IN.
158 * m4_dnl linearize(struct f_line *dest, const struct f_inst *what, uint pos) {
160 * m4_dnl switch (what->fi_code) {
161 * m4_dnl case FI_EXAMPLE:
162 * m4_dnl (105) [[ put it here ]]
166 * m4_dnl This is called when translating from struct f_inst to struct f_line_item.
167 * m4_dnl For accessing your custom instruction data, use following macros:
168 * m4_dnl whati -> for accessing (struct f_inst).i_FI_EXAMPLE
169 * m4_dnl item -> for accessing (struct f_line)[pos].i_FI_EXAMPLE
170 * m4_dnl For writing directly here, use FID_LINEARIZE_BODY.
172 * m4_dnl (107) struct f_line_item {
173 * m4_dnl ... (common fields)
176 * m4_dnl (101) [[ put it here ]]
177 * m4_dnl } i_FI_EXAMPLE;
181 * m4_dnl The same as FID_STRUCT_IN (101) but for the other structure.
182 * m4_dnl This structure is returned from the linearizer (105).
183 * m4_dnl For writing directly to this structure, use FID_LINE_IN.
185 * m4_dnl f_dump_line_item_FI_EXAMPLE(const struct f_line_item *item, const int indent)
187 * m4_dnl (104) [[ put it here ]]
189 * m4_dnl This code dumps the instruction on debug. Note that the argument
190 * m4_dnl is the linearized instruction; if the instruction has arguments,
191 * m4_dnl their code has already been linearized and their value is taken
192 * m4_dnl from the value stack.
193 * m4_dnl For writing directly here, use FID_DUMP_BODY.
197 * m4_dnl switch (f1_->fi_code) {
198 * m4_dnl case FI_EXAMPLE:
199 * m4_dnl (106) [[ put it here ]]
203 * m4_dnl This code compares the two given instrucions (f1_ and f2_)
204 * m4_dnl on reconfigure. For accessing your custom instruction data,
205 * m4_dnl use macros f1 and f2.
206 * m4_dnl For writing directly here, use FID_SAME_BODY.
208 * m4_dnl f_add_lines(...)
210 * m4_dnl switch (what_->fi_code) {
211 * m4_dnl case FI_EXAMPLE:
212 * m4_dnl (109) [[ put it here ]]
216 * m4_dnl This code adds new filter lines reachable from the instruction
217 * m4_dnl to the filter iterator line buffer. This is for instructions
218 * m4_dnl that changes conrol flow, like FI_CONDITION or FI_CALL, most
219 * m4_dnl instructions do not need to update it. It is used in generic
220 * m4_dnl filter iteration code (FILTER_ITERATE*). For accessing your
221 * m4_dnl custom instruction data, use macros f1 and f2. For writing
222 * m4_dnl directly here, use FID_ITERATE_BODY.
224 * m4_dnl interpret(...)
226 * m4_dnl switch (what->fi_code) {
227 * m4_dnl case FI_EXAMPLE:
228 * m4_dnl (108) [[ put it here ]]
232 * m4_dnl This code executes the instruction. Every pre-defined macro
233 * m4_dnl resets the output here. For setting it explicitly,
234 * m4_dnl use FID_INTERPRET_BODY.
235 * m4_dnl This code is put on two places; one is the interpreter, the other
236 * m4_dnl is instruction constructor. If you need to distinguish between
237 * m4_dnl these two, use FID_INTERPRET_EXEC or FID_INTERPRET_NEW respectively.
238 * m4_dnl To address the difference between interpreter and constructor
239 * m4_dnl environments, there are several convenience macros defined:
240 * m4_dnl runtime() -> for spitting out runtime error like division by zero
241 * m4_dnl RESULT(...) -> declare result; may overwrite arguments
242 * m4_dnl v1, v2, v3 -> positional arguments, may be overwritten by RESULT()
243 * m4_dnl falloc(size) -> allocate memory from the appropriate linpool
244 * m4_dnl fpool -> the current linpool
245 * m4_dnl NEVER_CONSTANT-> don't generate pre-interpretation code at all
246 * m4_dnl ACCESS_RTE -> check that route is available, also NEVER_CONSTANT
247 * m4_dnl ACCESS_EATTRS -> pre-cache the eattrs; use only with ACCESS_RTE
248 * m4_dnl f_rta_cow(fs) -> function to call before any change to route should be done
250 * m4_dnl If you are stymied, see FI_CALL or FI_CONSTANT or just search for
251 * m4_dnl the mentioned macros in this file to see what is happening there in wild.
254 * A note about soundness of the type system:
256 * A type system is sound when types of expressions are consistent with
257 * types of values resulting from evaluation of such expressions. Untyped
258 * expressions are ok, but badly typed expressions are not sound. So is
259 * the type system of BIRD filtering code sound? There are some points:
261 * All cases of (one) m4_dnl RESULT() macro are obviously ok, as the macro
262 * both declares a type and returns a value. One have to check instructions
263 * that use m4_dnl RESULT_TYPE() macro. There are two issues:
265 * FI_AND, FI_OR - second argument is statically checked to be T_BOOL and
266 * passed as result without dynamic typecheck, declared to be T_BOOL. If
267 * an untyped non-bool expression is used as a second argument, then
268 * the mismatched type is returned.
270 * FI_VAR_GET - soundness depends on consistency of declared symbol types
271 * and stored values. This is maintained when values are stored by
272 * FI_VAR_SET, but when they are stored by FI_CALL, only static checking is
273 * used, so when an untyped expression returning mismatched value is used
274 * as a function argument, then inconsistent value is stored and subsequent
275 * FI_VAR_GET would be unsound.
277 * Both of these issues are inconsequential, as mismatched values from
278 * unsound expressions will be caught by dynamic typechecks like mismatched
279 * values from untyped expressions.
281 * Also note that FI_CALL is the only expression without properly declared
285 /* Binary operators */
289 RESULT(T_INT
, i
, v1
.val
.i
+ v2
.val
.i
);
291 INST(FI_SUBTRACT
, 2, 1) {
294 RESULT(T_INT
, i
, v1
.val
.i
- v2
.val
.i
);
296 INST(FI_MULTIPLY
, 2, 1) {
299 RESULT(T_INT
, i
, v1
.val
.i
* v2
.val
.i
);
301 INST(FI_DIVIDE
, 2, 1) {
304 if (v2
.val
.i
== 0) runtime( "Mother told me not to divide by 0" );
305 RESULT(T_INT
, i
, v1
.val
.i
/ v2
.val
.i
);
309 ARG_TYPE_STATIC(2,T_BOOL
);
319 ARG_TYPE_STATIC(2,T_BOOL
);
328 INST(FI_PAIR_CONSTRUCT
, 2, 1) {
333 if ((u1
> 0xFFFF) || (u2
> 0xFFFF))
334 runtime( "Can't operate with value out of bounds in pair constructor" );
335 RESULT(T_PAIR
, i
, (u1
<< 16) | u2
);
338 INST(FI_EC_CONSTRUCT
, 2, 1) {
342 FID_MEMBER(enum ec_subtype
, ecs
, f1
->ecs
!= f2
->ecs
, "ec subtype %s", ec_subtype_str(item
->ecs
));
347 if (v1
.type
== T_INT
) {
348 ipv4_used
= 0; key
= v1
.val
.i
;
350 else if (v1
.type
== T_QUAD
) {
351 ipv4_used
= 1; key
= v1
.val
.i
;
353 /* IP->Quad implicit conversion */
354 else if (val_is_ip4(&v1
)) {
355 ipv4_used
= 1; key
= ipa_to_u32(v1
.val
.ip
);
358 runtime("Argument 1 of EC constructor must be integer or IPv4 address, got 0x%02x", v1
.type
);
362 if (ecs
== EC_GENERIC
)
363 RESULT(T_EC
, ec
, ec_generic(key
, val
));
366 RESULT(T_EC
, ec
, ec_ip4(ecs
, key
, val
));
368 runtime("4-byte value %u can't be used with IP-address key in extended community", val
);
369 else if (key
< 0x10000)
370 RESULT(T_EC
, ec
, ec_as2(ecs
, key
, val
));
373 RESULT(T_EC
, ec
, ec_as4(ecs
, key
, val
));
375 runtime("4-byte value %u can't be used with 4-byte ASN in extended community", val
);
378 INST(FI_LC_CONSTRUCT
, 3, 1) {
382 RESULT(T_LC
, lc
, [[(lcomm
) { v1
.val
.i
, v2
.val
.i
, v3
.val
.i
}]]);
385 INST(FI_PATHMASK_CONSTRUCT
, 0, 1) {
388 struct f_path_mask
*pm
= falloc(sizeof(struct f_path_mask
) + whati
->varcount
* sizeof(struct f_path_mask_item
));
389 pm
->len
= whati
->varcount
;
391 for (uint i
=0; i
<whati
->varcount
; i
++) {
392 switch (vv(i
).type
) {
393 case T_PATH_MASK_ITEM
:
394 if (vv(i
).val
.pmi
.kind
== PM_LOOP
)
397 runtime("Path mask iterator '+' cannot be first");
399 /* We want PM_LOOP as prefix operator */
400 pm
->item
[i
] = pm
->item
[i
- 1];
401 pm
->item
[i
- 1] = vv(i
).val
.pmi
;
405 pm
->item
[i
] = vv(i
).val
.pmi
;
409 pm
->item
[i
] = (struct f_path_mask_item
) {
416 if (!path_set_type(vv(i
).val
.t
))
417 runtime("Only integer sets allowed in path mask");
419 pm
->item
[i
] = (struct f_path_mask_item
) {
426 runtime( "Error resolving path mask template: value not an integer" );
430 RESULT(T_PATH_MASK
, path_mask
, pm
);
433 /* Relational operators */
438 ARG_PREFER_SAME_TYPE(1, 2);
439 RESULT(T_BOOL
, i
, !val_same(&v1
, &v2
));
445 ARG_PREFER_SAME_TYPE(1, 2);
446 RESULT(T_BOOL
, i
, val_same(&v1
, &v2
));
454 int i
= val_compare(&v1
, &v2
);
455 if (i
== F_CMP_ERROR
)
456 runtime( "Can't compare values of incompatible types" );
457 RESULT(T_BOOL
, i
, (i
== -1));
465 int i
= val_compare(&v1
, &v2
);
466 if (i
== F_CMP_ERROR
)
467 runtime( "Can't compare values of incompatible types" );
468 RESULT(T_BOOL
, i
, (i
!= 1));
473 RESULT(T_BOOL
, i
, !v1
.val
.i
);
476 INST(FI_MATCH
, 2, 1) {
479 int i
= val_in_range(&v1
, &v2
);
480 if (i
== F_CMP_ERROR
)
481 runtime( "~ applied on unknown type pair" );
482 RESULT(T_BOOL
, i
, !!i
);
485 INST(FI_NOT_MATCH
, 2, 1) {
488 int i
= val_in_range(&v1
, &v2
);
489 if (i
== F_CMP_ERROR
)
490 runtime( "!~ applied on unknown type pair" );
491 RESULT(T_BOOL
, i
, !i
);
494 INST(FI_DEFINED
, 1, 1) {
496 RESULT(T_BOOL
, i
, (v1
.type
!= T_VOID
) && !undef_value(v1
));
499 METHOD_R(T_NET
, type
, 0, T_ENUM_NETTYPE
, i
, v1
.val
.net
->type
);
500 METHOD_R(T_IP
, is_v4
, 0, T_BOOL
, i
, ipa_is_ip4(v1
.val
.ip
));
502 /* Add initialized variable */
503 INST(FI_VAR_INIT
, 1, 0) {
507 ARG_TYPE(1, sym
->class & 0xff);
509 /* New variable is always the last on stack */
510 uint pos
= curline
.vbase
+ sym
->offset
;
511 fstk
->vstk
[pos
] = v1
;
512 fstk
->vcnt
= pos
+ 1;
515 /* Add uninitialized variable */
516 INST(FI_VAR_INIT0
, 0, 0) {
520 /* New variable is always the last on stack */
521 uint pos
= curline
.vbase
+ sym
->offset
;
522 fstk
->vstk
[pos
] = (struct f_val
) { };
523 fstk
->vcnt
= pos
+ 1;
526 /* Set to indirect value prepared in v1 */
527 INST(FI_VAR_SET
, 1, 0) {
531 ARG_TYPE(1, sym
->class & 0xff);
533 fstk
->vstk
[curline
.vbase
+ sym
->offset
] = v1
;
536 INST(FI_VAR_GET
, 0, 1) {
539 RESULT_TYPE(sym
->class & 0xff);
540 RESULT_VAL(fstk
->vstk
[curline
.vbase
+ sym
->offset
]);
543 INST(FI_CONSTANT
, 0, 1) {
547 [[ !val_same(&(f1
->val
), &(f2
->val
)) ]],
549 val_dump(&(item
->val
))
552 RESULT_TYPE(val
.type
);
556 METHOD_R(T_PATH
, empty
, 0, T_PATH
, ad
, &null_adata
);
557 METHOD_R(T_CLIST
, empty
, 0, T_CLIST
, ad
, &null_adata
);
558 METHOD_R(T_ECLIST
, empty
, 0, T_ECLIST
, ad
, &null_adata
);
559 METHOD_R(T_LCLIST
, empty
, 0, T_LCLIST
, ad
, &null_adata
);
561 /* Common loop begin instruction, always created by f_for_cycle() */
562 INST(FI_FOR_LOOP_START
, 0, 3) {
566 /* Repeat the instruction which called us */
567 ASSERT_DIE(fstk
->ecnt
> 1);
570 /* There should be exactly three items on the value stack to be taken care of */
573 /* And these should also stay there after we finish for the caller instruction */
576 /* Assert the iterator variable positioning */
577 ASSERT_DIE(curline
.vbase
+ sym
->offset
== fstk
->vcnt
- 1);
579 /* The result type declaration makes no sense here but is needed */
583 /* Type-specific for_next iterators */
584 INST(FI_PATH_FOR_NEXT
, 3, 0) {
587 if (as_path_walk(v1
.val
.ad
, &v2
.val
.i
, &v3
.val
.i
))
590 METHOD_CONSTRUCTOR("!for_next");
593 INST(FI_CLIST_FOR_NEXT
, 3, 0) {
596 if (int_set_walk(v1
.val
.ad
, &v2
.val
.i
, &v3
.val
.i
))
599 METHOD_CONSTRUCTOR("!for_next");
602 INST(FI_ECLIST_FOR_NEXT
, 3, 0) {
605 if (ec_set_walk(v1
.val
.ad
, &v2
.val
.i
, &v3
.val
.ec
))
608 METHOD_CONSTRUCTOR("!for_next");
611 INST(FI_LCLIST_FOR_NEXT
, 3, 0) {
614 if (lc_set_walk(v1
.val
.ad
, &v2
.val
.i
, &v3
.val
.lc
))
617 METHOD_CONSTRUCTOR("!for_next");
620 INST(FI_CONDITION
, 1, 0) {
628 INST(FI_PRINT
, 1, 0) {
632 if (!(fs
->flags
& FF_SILENT
))
633 val_format(&v1
, &fs
->buf
);
636 INST(FI_FLUSH
, 0, 0) {
638 if (!(fs
->flags
& FF_SILENT
))
639 /* After log_commit, the buffer is reset */
640 log_commit(*L_INFO
, &fs
->buf
);
645 FID_MEMBER(enum filter_return
, fret
, f1
->fret
!= f2
->fret
, "%s", filter_return_str(item
->fret
));
647 switch (whati
->fret
) {
648 case F_ACCEPT
: /* Should take care about turning ACCEPT into MODIFY */
650 case F_REJECT
: /* Maybe print complete route along with reason to reject route? */
651 return fret
; /* We have to return now, no more processing. */
653 bug( "unknown return type: Can't happen");
657 INST(FI_RTA_GET
, 0, 1) {
661 struct rta
*rta
= (*fs
->rte
)->attrs
;
665 case SA_FROM
: RESULT(sa
.f_type
, ip
, rta
->from
); break;
666 case SA_GW
: RESULT(sa
.f_type
, ip
, rta
->nh
.gw
); break;
667 case SA_NET
: RESULT(sa
.f_type
, net
, (*fs
->rte
)->net
->n
.addr
); break;
668 case SA_PROTO
: RESULT(sa
.f_type
, s
, (*fs
->rte
)->src
->proto
->name
); break;
669 case SA_SOURCE
: RESULT(sa
.f_type
, i
, rta
->source
); break;
670 case SA_SCOPE
: RESULT(sa
.f_type
, i
, rta
->scope
); break;
671 case SA_DEST
: RESULT(sa
.f_type
, i
, rta
->dest
); break;
672 case SA_IFNAME
: RESULT(sa
.f_type
, s
, rta
->nh
.iface
? rta
->nh
.iface
->name
: ""); break;
673 case SA_IFINDEX
: RESULT(sa
.f_type
, i
, rta
->nh
.iface
? rta
->nh
.iface
->index
: 0); break;
674 case SA_WEIGHT
: RESULT(sa
.f_type
, i
, rta
->nh
.weight
+ 1); break;
675 case SA_PREF
: RESULT(sa
.f_type
, i
, rta
->pref
); break;
676 case SA_GW_MPLS
: RESULT(sa
.f_type
, i
, rta
->nh
.labels
? rta
->nh
.label
[0] : MPLS_NULL
); break;
677 case SA_ONLINK
: RESULT(sa
.f_type
, i
, rta
->nh
.flags
& RNF_ONLINK
? 1 : 0); break;
680 bug("Invalid static attribute access (%u/%u)", sa
.f_type
, sa
.sa_code
);
685 INST(FI_RTA_SET
, 1, 0) {
689 ARG_TYPE(1, sa
.f_type
);
693 struct rta
*rta
= (*fs
->rte
)->attrs
;
698 rta
->from
= v1
.val
.ip
;
703 ip_addr ip
= v1
.val
.ip
;
704 struct iface
*ifa
= ipa_is_link_local(ip
) || (rta
->nh
.flags
& RNF_ONLINK
) ? rta
->nh
.iface
: NULL
;
705 neighbor
*n
= neigh_find((*fs
->rte
)->src
->proto
, ip
, ifa
, (rta
->nh
.flags
& RNF_ONLINK
) ? NEF_ONLINK
: 0);
706 if (!n
|| (n
->scope
== SCOPE_HOST
))
707 runtime( "Invalid gw address" );
709 rta
->dest
= RTD_UNICAST
;
711 rta
->nh
.iface
= n
->iface
;
713 rta
->hostentry
= NULL
;
719 rta
->scope
= v1
.val
.i
;
725 if ((i
!= RTD_BLACKHOLE
) && (i
!= RTD_UNREACHABLE
) && (i
!= RTD_PROHIBIT
))
726 runtime( "Destination can be changed only to blackhole, unreachable or prohibit" );
729 rta
->nh
.gw
= IPA_NONE
;
730 rta
->nh
.iface
= NULL
;
732 rta
->hostentry
= NULL
;
739 struct iface
*ifa
= if_find_by_name(v1
.val
.s
);
741 runtime( "Invalid iface name" );
743 rta
->dest
= RTD_UNICAST
;
744 rta
->nh
.gw
= IPA_NONE
;
747 rta
->hostentry
= NULL
;
754 if (v1
.val
.i
>= 0x100000)
755 runtime( "Invalid MPLS label" );
757 if (v1
.val
.i
!= MPLS_NULL
)
759 rta
->nh
.label
[0] = v1
.val
.i
;
770 if (i
< 1 || i
> 256)
771 runtime( "Setting weight value out of bounds" );
772 if (rta
->dest
!= RTD_UNICAST
)
773 runtime( "Setting weight needs regular nexthop " );
775 /* Set weight on all next hops */
776 for (struct nexthop
*nh
= &rta
->nh
; nh
; nh
= nh
->next
)
782 rta
->pref
= v1
.val
.i
;
788 rta
->nh
.flags
|= RNF_ONLINK
;
790 rta
->nh
.flags
&= ~RNF_ONLINK
;
795 bug("Invalid static attribute access (%u/%u)", sa
.f_type
, sa
.sa_code
);
800 INST(FI_EA_GET
, 0, 1) { /* Access to extended attributes */
804 RESULT_TYPE(da
.f_type
);
806 eattr
*e
= ea_find(*fs
->eattrs
, da
.ea_code
);
809 /* A special case: undefined as_path looks like empty as_path */
810 if (da
.type
== EAF_TYPE_AS_PATH
) {
811 RESULT_(T_PATH
, ad
, &null_adata
);
815 /* The same special case for int_set */
816 if (da
.type
== EAF_TYPE_INT_SET
) {
817 RESULT_(T_CLIST
, ad
, &null_adata
);
821 /* The same special case for ec_set */
822 if (da
.type
== EAF_TYPE_EC_SET
) {
823 RESULT_(T_ECLIST
, ad
, &null_adata
);
827 /* The same special case for lc_set */
828 if (da
.type
== EAF_TYPE_LC_SET
) {
829 RESULT_(T_LCLIST
, ad
, &null_adata
);
833 /* Undefined value */
838 switch (e
->type
& EAF_TYPE_MASK
) {
840 RESULT_(da
.f_type
, i
, e
->u
.data
);
842 case EAF_TYPE_ROUTER_ID
:
843 RESULT_(T_QUAD
, i
, e
->u
.data
);
845 case EAF_TYPE_OPAQUE
:
846 RESULT_(T_ENUM_EMPTY
, i
, 0);
848 case EAF_TYPE_IP_ADDRESS
:
849 RESULT_(T_IP
, ip
, *((ip_addr
*) e
->u
.ptr
->data
));
851 case EAF_TYPE_AS_PATH
:
852 RESULT_(T_PATH
, ad
, e
->u
.ptr
);
854 case EAF_TYPE_BITFIELD
:
855 RESULT_(T_BOOL
, i
, !!(e
->u
.data
& (1u << da
.bit
)));
857 case EAF_TYPE_INT_SET
:
858 RESULT_(T_CLIST
, ad
, e
->u
.ptr
);
860 case EAF_TYPE_EC_SET
:
861 RESULT_(T_ECLIST
, ad
, e
->u
.ptr
);
863 case EAF_TYPE_LC_SET
:
864 RESULT_(T_LCLIST
, ad
, e
->u
.ptr
);
867 bug("Unknown dynamic attribute type");
872 INST(FI_EA_SET
, 1, 0) {
877 ARG_TYPE(1, da
.f_type
);
879 struct ea_list
*l
= lp_alloc(fs
->pool
, sizeof(struct ea_list
) + sizeof(eattr
));
882 l
->flags
= EALF_SORTED
;
884 l
->attrs
[0].id
= da
.ea_code
;
885 l
->attrs
[0].flags
= 0;
886 l
->attrs
[0].type
= da
.type
;
887 l
->attrs
[0].originated
= 1;
888 l
->attrs
[0].fresh
= 1;
889 l
->attrs
[0].undef
= 0;
893 case EAF_TYPE_ROUTER_ID
:
894 l
->attrs
[0].u
.data
= v1
.val
.i
;
897 case EAF_TYPE_OPAQUE
:
898 runtime( "Setting opaque attribute is not allowed" );
901 case EAF_TYPE_IP_ADDRESS
:;
902 int len
= sizeof(ip_addr
);
903 struct adata
*ad
= lp_alloc(fs
->pool
, sizeof(struct adata
) + len
);
905 (* (ip_addr
*) ad
->data
) = v1
.val
.ip
;
906 l
->attrs
[0].u
.ptr
= ad
;
909 case EAF_TYPE_AS_PATH
:
910 case EAF_TYPE_INT_SET
:
911 case EAF_TYPE_EC_SET
:
912 case EAF_TYPE_LC_SET
:
913 l
->attrs
[0].u
.ptr
= v1
.val
.ad
;
916 case EAF_TYPE_BITFIELD
:
918 /* First, we have to find the old value */
919 eattr
*e
= ea_find(*fs
->eattrs
, da
.ea_code
);
920 u32 data
= e
? e
->u
.data
: 0;
923 l
->attrs
[0].u
.data
= data
| (1u << da
.bit
);
925 l
->attrs
[0].u
.data
= data
& ~(1u << da
.bit
);
930 bug("Unknown dynamic attribute type");
934 l
->next
= *fs
->eattrs
;
939 INST(FI_EA_UNSET
, 0, 0) {
945 ea_unset_attr(fs
->eattrs
, fs
->pool
, 1, da
.ea_code
);
949 METHOD_R(T_NET
, len
, 0, T_INT
, i
, net_pxlen(v1
.val
.net
));
950 METHOD_R(T_PATH
, len
, 0, T_INT
, i
, as_path_getlen(v1
.val
.ad
));
951 METHOD_R(T_CLIST
, len
, 0, T_INT
, i
, int_set_get_size(v1
.val
.ad
));
952 METHOD_R(T_ECLIST
, len
, 0, T_INT
, i
, ec_set_get_size(v1
.val
.ad
));
953 METHOD_R(T_LCLIST
, len
, 0, T_INT
, i
, lc_set_get_size(v1
.val
.ad
));
955 INST(FI_NET_SRC
, 1, 1) { /* Get src prefix */
957 METHOD_CONSTRUCTOR("src");
959 net_addr_union
*net
= (void *) v1
.val
.net
;
960 net_addr
*src
= falloc(sizeof(net_addr_ip6
));
963 switch(v1
.val
.net
->type
) {
965 part
= flow4_get_part(&net
->flow4
, FLOW_TYPE_SRC_PREFIX
);
967 net_fill_ip4(src
, flow_read_ip4_part(part
), flow_read_pxlen(part
));
969 net_fill_ip4(src
, IP4_NONE
, 0);
973 part
= flow6_get_part(&net
->flow6
, FLOW_TYPE_SRC_PREFIX
);
975 net_fill_ip6(src
, flow_read_ip6_part(part
), flow_read_pxlen(part
));
977 net_fill_ip6(src
, IP6_NONE
, 0);
981 net_fill_ip6(src
, net
->ip6_sadr
.src_prefix
, net
->ip6_sadr
.src_pxlen
);
985 runtime( "Flow or SADR expected" );
988 RESULT(T_NET
, net
, src
);
991 INST(FI_NET_DST
, 1, 1) { /* Get dst prefix */
993 METHOD_CONSTRUCTOR("dst");
995 net_addr_union
*net
= (void *) v1
.val
.net
;
996 net_addr
*dst
= falloc(sizeof(net_addr_ip6
));
999 switch(v1
.val
.net
->type
) {
1001 part
= flow4_get_part(&net
->flow4
, FLOW_TYPE_DST_PREFIX
);
1003 net_fill_ip4(dst
, flow_read_ip4_part(part
), flow_read_pxlen(part
));
1005 net_fill_ip4(dst
, IP4_NONE
, 0);
1009 part
= flow6_get_part(&net
->flow6
, FLOW_TYPE_DST_PREFIX
);
1011 net_fill_ip6(dst
, flow_read_ip6_part(part
), flow_read_pxlen(part
));
1013 net_fill_ip6(dst
, IP6_NONE
, 0);
1017 net_fill_ip6(dst
, net
->ip6_sadr
.dst_prefix
, net
->ip6_sadr
.dst_pxlen
);
1021 runtime( "Flow or SADR expected" );
1024 RESULT(T_NET
, net
, dst
);
1027 /* Get ROA max prefix length */
1028 METHOD(T_NET
, maxlen
, 0, [[
1029 if (!net_is_roa(v1
.val
.net
))
1030 runtime( "ROA expected" );
1032 RESULT(T_INT
, i
, (v1
.val
.net
->type
== NET_ROA4
) ?
1033 ((net_addr_roa4
*) v1
.val
.net
)->max_pxlen
:
1034 ((net_addr_roa6
*) v1
.val
.net
)->max_pxlen
);
1037 /* Get ROA ASN or community ASN part */
1038 METHOD_R(T_PAIR
, asn
, 0, T_INT
, i
, v1
.val
.i
>> 16);
1039 METHOD_R(T_LC
, asn
, 0, T_INT
, i
, v1
.val
.lc
.asn
);
1041 METHOD(T_NET
, asn
, 0, [[
1042 if (!net_is_roa(v1
.val
.net
))
1043 runtime( "ROA expected" );
1045 RESULT(T_INT
, i
, (v1
.val
.net
->type
== NET_ROA4
) ?
1046 ((net_addr_roa4
*) v1
.val
.net
)->asn
:
1047 ((net_addr_roa6
*) v1
.val
.net
)->asn
);
1051 /* Convert prefix to IP */
1052 METHOD_R(T_NET
, ip
, 0, T_IP
, ip
, net_prefix(v1
.val
.net
));
1054 INST(FI_ROUTE_DISTINGUISHER
, 1, 1) {
1056 METHOD_CONSTRUCTOR("rd");
1057 if (!net_is_vpn(v1
.val
.net
))
1058 runtime( "VPN address expected" );
1059 RESULT(T_RD
, ec
, net_rd(v1
.val
.net
));
1062 INST(FI_AS_PATH_FIRST
, 1, 1) { /* Get first ASN from AS PATH */
1064 METHOD_CONSTRUCTOR("first");
1066 as_path_get_first(v1
.val
.ad
, &as
);
1067 RESULT(T_INT
, i
, as
);
1070 INST(FI_AS_PATH_LAST
, 1, 1) { /* Get last ASN from AS PATH */
1072 METHOD_CONSTRUCTOR("last");
1074 as_path_get_last(v1
.val
.ad
, &as
);
1075 RESULT(T_INT
, i
, as
);
1078 /* Get last ASN from non-aggregated part of AS PATH */
1079 METHOD_R(T_PATH
, last_nonaggregated
, 0, T_INT
, i
, as_path_get_last_nonaggregated(v1
.val
.ad
));
1081 /* Get data part from the standard community */
1082 METHOD_R(T_PAIR
, data
, 0, T_INT
, i
, v1
.val
.i
& 0xFFFF);
1084 /* Get data1 part from the large community */
1085 METHOD_R(T_LC
, data1
, 0, T_INT
, i
, v1
.val
.lc
.ldp1
);
1087 /* Get data2 part from the large community */
1088 METHOD_R(T_LC
, data2
, 0, T_INT
, i
, v1
.val
.lc
.ldp2
);
1090 INST(FI_CLIST_MIN
, 1, 1) { /* Get minimum element from list */
1092 METHOD_CONSTRUCTOR("min");
1094 int_set_min(v1
.val
.ad
, &val
);
1095 RESULT(T_PAIR
, i
, val
);
1098 INST(FI_CLIST_MAX
, 1, 1) { /* Get minimum element from list */
1100 METHOD_CONSTRUCTOR("max");
1102 int_set_max(v1
.val
.ad
, &val
);
1103 RESULT(T_PAIR
, i
, val
);
1106 INST(FI_ECLIST_MIN
, 1, 1) { /* Get minimum element from list */
1108 METHOD_CONSTRUCTOR("min");
1110 ec_set_min(v1
.val
.ad
, &val
);
1111 RESULT(T_EC
, ec
, val
);
1114 INST(FI_ECLIST_MAX
, 1, 1) { /* Get minimum element from list */
1116 METHOD_CONSTRUCTOR("max");
1118 ec_set_max(v1
.val
.ad
, &val
);
1119 RESULT(T_EC
, ec
, val
);
1122 INST(FI_LCLIST_MIN
, 1, 1) { /* Get minimum element from list */
1124 METHOD_CONSTRUCTOR("min");
1126 lc_set_min(v1
.val
.ad
, &val
);
1127 RESULT(T_LC
, lc
, val
);
1130 INST(FI_LCLIST_MAX
, 1, 1) { /* Get minimum element from list */
1132 METHOD_CONSTRUCTOR("max");
1134 lc_set_max(v1
.val
.ad
, &val
);
1135 RESULT(T_LC
, lc
, val
);
1138 INST(FI_RETURN
, 1, 0) {
1140 /* Acquire the return value */
1142 uint retpos
= fstk
->vcnt
;
1144 /* Drop every sub-block including ourselves */
1146 while ((fstk
->ecnt
> 0) && !(fstk
->estk
[fstk
->ecnt
].emask
& FE_RETURN
));
1148 /* Now we are at the caller frame; if no such, try to convert to accept/reject. */
1151 if (fstk
->vstk
[retpos
].type
== T_BOOL
)
1152 return (fstk
->vstk
[retpos
].val
.i
) ? F_ACCEPT
: F_REJECT
;
1154 runtime("Can't return non-bool from non-function");
1157 /* Set the value stack position, overwriting the former implicit void */
1158 fstk
->vcnt
= fstk
->estk
[fstk
->ecnt
].ventry
- 1;
1160 /* Copy the return value */
1161 RESULT_VAL(fstk
->vstk
[retpos
]);
1164 INST(FI_CALL
, 0, 1) {
1169 /* Fake result type declaration */
1170 RESULT_TYPE(sym
->function
->return_type
);
1173 ASSERT(sym
->class == SYM_FUNCTION
);
1175 if (whati
->varcount
!= sym
->function
->args
)
1176 cf_error("Function '%s' expects %u arguments, got %u arguments",
1177 sym
->name
, sym
->function
->args
, whati
->varcount
);
1179 /* Typecheck individual arguments */
1180 struct f_inst
*a
= fvar
;
1181 struct f_arg
*b
= sym
->function
->arg_list
;
1182 for (uint i
= 1; a
&& b
; a
= a
->next
, b
= b
->next
, i
++)
1184 enum f_type b_type
= b
->arg
->class & 0xff;
1186 if (a
->type
&& (a
->type
!= b_type
) && !f_const_promotion(a
, b_type
))
1187 cf_error("Argument %u of '%s' must be %s, got %s",
1188 i
, sym
->name
, f_type_name(b_type
), f_type_name(a
->type
));
1192 /* Add implicit void slot for the return value */
1193 struct f_inst
*tmp
= f_new_inst(FI_CONSTANT
, (struct f_val
) { .type
= T_VOID
});
1194 tmp
->next
= whati
->fvar
;
1196 what
->size
+= tmp
->size
;
1198 /* Mark recursive calls, they have dummy f_line */
1199 if (!sym
->function
->len
)
1200 what
->flags
|= FIF_RECURSIVE
;
1203 if (!(f1
->sym
->flags
& SYM_FLAG_SAME
) && !(f1_
->flags
& FIF_RECURSIVE
))
1207 if (!(what
->flags
& FIF_RECURSIVE
))
1208 BUFFER_PUSH(fit
->lines
) = whati
->sym
->function
;
1210 FID_INTERPRET_BODY()
1212 /* Push the body on stack */
1213 LINEX(sym
->function
);
1214 curline
.vbase
= curline
.ventry
;
1215 curline
.emask
|= FE_RETURN
;
1217 /* Arguments on stack */
1218 fstk
->vcnt
+= sym
->function
->args
;
1220 /* Storage for local variables */
1221 memset(&(fstk
->vstk
[fstk
->vcnt
]), 0, sizeof(struct f_val
) * sym
->function
->vars
);
1222 fstk
->vcnt
+= sym
->function
->vars
;
1225 INST(FI_DROP_RESULT
, 1, 0) {
1230 INST(FI_SWITCH
, 1, 0) {
1233 FID_MEMBER(struct f_tree
*, tree
, [[!same_tree(f1
->tree
, f2
->tree
)]], "tree %p", item
->tree
);
1235 FID_LINEARIZE_BODY()
1236 /* Linearize all branches in switch */
1237 struct f_inst
*last_inst
= NULL
;
1238 struct f_line
*last_line
= NULL
;
1239 for (struct f_tree
*t
= whati
->tree
; t
; t
= t
->left
)
1241 if (t
->data
!= last_inst
)
1243 last_inst
= t
->data
;
1244 last_line
= f_linearize(t
->data
, 0);
1247 t
->data
= last_line
;
1250 /* Balance the tree */
1251 item
->tree
= build_tree(whati
->tree
);
1254 tree_walk(whati
->tree
, f_add_tree_lines
, fit
);
1256 FID_INTERPRET_BODY()
1257 /* In parse-time use find_tree_linear(), in runtime use find_tree() */
1258 const struct f_tree
*t
= FID_HIC(,find_tree
,find_tree_linear
)(tree
, &v1
);
1261 t
= FID_HIC(,find_tree
,find_tree_linear
)(tree
, &v1
);
1263 debug( "No else statement?\n");
1264 FID_HIC(,break,return NULL
);
1271 INST(FI_IP_MASK
, 2, 1) { /* IP.MASK(val) */
1274 METHOD_CONSTRUCTOR("mask");
1275 RESULT(T_IP
, ip
, [[ ipa_is_ip4(v1
.val
.ip
) ?
1276 ipa_from_ip4(ip4_and(ipa_to_ip4(v1
.val
.ip
), ip4_mkmask(v2
.val
.i
))) :
1277 ipa_from_ip6(ip6_and(ipa_to_ip6(v1
.val
.ip
), ip6_mkmask(v2
.val
.i
))) ]]);
1280 INST(FI_PATH_PREPEND
, 2, 1) { /* Path prepend */
1283 METHOD_CONSTRUCTOR("prepend");
1284 RESULT(T_PATH
, ad
, [[ as_path_prepend(fpool
, v1
.val
.ad
, v2
.val
.i
) ]]);
1287 INST(FI_CLIST_ADD
, 2, 1) { /* (Extended) Community list add */
1290 METHOD_CONSTRUCTOR("add");
1292 if ((v2
.type
== T_PAIR
) || (v2
.type
== T_QUAD
))
1293 RESULT(T_CLIST
, ad
, [[ int_set_add(fpool
, v1
.val
.ad
, v2
.val
.i
) ]]);
1294 /* IP->Quad implicit conversion */
1295 else if (val_is_ip4(&v2
))
1296 RESULT(T_CLIST
, ad
, [[ int_set_add(fpool
, v1
.val
.ad
, ipa_to_u32(v2
.val
.ip
)) ]]);
1297 else if ((v2
.type
== T_SET
) && clist_set_type(v2
.val
.t
, &dummy
))
1298 runtime("Can't add set");
1299 else if (v2
.type
== T_CLIST
)
1300 RESULT(T_CLIST
, ad
, [[ int_set_union(fpool
, v1
.val
.ad
, v2
.val
.ad
) ]]);
1302 runtime("Can't add non-pair");
1305 INST(FI_ECLIST_ADD
, 2, 1) {
1308 METHOD_CONSTRUCTOR("add");
1309 /* v2.val is either EC or EC-set */
1310 if ((v2
.type
== T_SET
) && eclist_set_type(v2
.val
.t
))
1311 runtime("Can't add set");
1312 else if (v2
.type
== T_ECLIST
)
1313 RESULT(T_ECLIST
, ad
, [[ ec_set_union(fpool
, v1
.val
.ad
, v2
.val
.ad
) ]]);
1314 else if (v2
.type
!= T_EC
)
1315 runtime("Can't add non-ec");
1317 RESULT(T_ECLIST
, ad
, [[ ec_set_add(fpool
, v1
.val
.ad
, v2
.val
.ec
) ]]);
1320 INST(FI_LCLIST_ADD
, 2, 1) {
1323 METHOD_CONSTRUCTOR("add");
1324 /* v2.val is either LC or LC-set */
1325 if ((v2
.type
== T_SET
) && lclist_set_type(v2
.val
.t
))
1326 runtime("Can't add set");
1327 else if (v2
.type
== T_LCLIST
)
1328 RESULT(T_LCLIST
, ad
, [[ lc_set_union(fpool
, v1
.val
.ad
, v2
.val
.ad
) ]]);
1329 else if (v2
.type
!= T_LC
)
1330 runtime("Can't add non-lc");
1332 RESULT(T_LCLIST
, ad
, [[ lc_set_add(fpool
, v1
.val
.ad
, v2
.val
.lc
) ]]);
1335 INST(FI_PATH_DEL
, 2, 1) { /* Path delete */
1338 METHOD_CONSTRUCTOR("delete");
1339 if ((v2
.type
== T_SET
) && path_set_type(v2
.val
.t
) || (v2
.type
== T_INT
))
1340 RESULT(T_PATH
, ad
, [[ as_path_filter(fpool
, v1
.val
.ad
, &v2
, 0) ]]);
1342 runtime("Can't delete non-integer (set)");
1345 INST(FI_CLIST_DEL
, 2, 1) { /* (Extended) Community list add or delete */
1348 METHOD_CONSTRUCTOR("delete");
1349 /* Community (or cluster) list */
1352 if ((v2
.type
== T_PAIR
) || (v2
.type
== T_QUAD
))
1353 RESULT(T_CLIST
, ad
, [[ int_set_del(fpool
, v1
.val
.ad
, v2
.val
.i
) ]]);
1354 /* IP->Quad implicit conversion */
1355 else if (val_is_ip4(&v2
))
1356 RESULT(T_CLIST
, ad
, [[ int_set_del(fpool
, v1
.val
.ad
, ipa_to_u32(v2
.val
.ip
)) ]]);
1357 else if ((v2
.type
== T_SET
) && clist_set_type(v2
.val
.t
, &dummy
) || (v2
.type
== T_CLIST
))
1358 RESULT(T_CLIST
, ad
, [[ clist_filter(fpool
, v1
.val
.ad
, &v2
, 0) ]]);
1360 runtime("Can't delete non-pair");
1363 INST(FI_ECLIST_DEL
, 2, 1) { /* (Extended) Community list add or delete */
1366 METHOD_CONSTRUCTOR("delete");
1367 /* v2.val is either EC or EC-set */
1368 if ((v2
.type
== T_SET
) && eclist_set_type(v2
.val
.t
) || (v2
.type
== T_ECLIST
))
1369 RESULT(T_ECLIST
, ad
, [[ eclist_filter(fpool
, v1
.val
.ad
, &v2
, 0) ]]);
1370 else if (v2
.type
!= T_EC
)
1371 runtime("Can't delete non-ec");
1373 RESULT(T_ECLIST
, ad
, [[ ec_set_del(fpool
, v1
.val
.ad
, v2
.val
.ec
) ]]);
1376 INST(FI_LCLIST_DEL
, 2, 1) { /* (Extended) Community list add or delete */
1379 METHOD_CONSTRUCTOR("delete");
1380 /* v2.val is either LC or LC-set */
1381 if ((v2
.type
== T_SET
) && lclist_set_type(v2
.val
.t
) || (v2
.type
== T_LCLIST
))
1382 RESULT(T_LCLIST
, ad
, [[ lclist_filter(fpool
, v1
.val
.ad
, &v2
, 0) ]]);
1383 else if (v2
.type
!= T_LC
)
1384 runtime("Can't delete non-lc");
1386 RESULT(T_LCLIST
, ad
, [[ lc_set_del(fpool
, v1
.val
.ad
, v2
.val
.lc
) ]]);
1389 INST(FI_PATH_FILTER
, 2, 1) { /* (Extended) Community list add or delete */
1392 METHOD_CONSTRUCTOR("filter");
1394 if ((v2
.type
== T_SET
) && path_set_type(v2
.val
.t
))
1395 RESULT(T_PATH
, ad
, [[ as_path_filter(fpool
, v1
.val
.ad
, &v2
, 1) ]]);
1397 runtime("Can't filter integer");
1400 INST(FI_CLIST_FILTER
, 2, 1) {
1403 METHOD_CONSTRUCTOR("filter");
1404 /* Community (or cluster) list */
1407 if ((v2
.type
== T_SET
) && clist_set_type(v2
.val
.t
, &dummy
) || (v2
.type
== T_CLIST
))
1408 RESULT(T_CLIST
, ad
, [[ clist_filter(fpool
, v1
.val
.ad
, &v2
, 1) ]]);
1410 runtime("Can't filter pair");
1413 INST(FI_ECLIST_FILTER
, 2, 1) {
1416 METHOD_CONSTRUCTOR("filter");
1417 /* v2.val is either EC or EC-set */
1418 if ((v2
.type
== T_SET
) && eclist_set_type(v2
.val
.t
) || (v2
.type
== T_ECLIST
))
1419 RESULT(T_ECLIST
, ad
, [[ eclist_filter(fpool
, v1
.val
.ad
, &v2
, 1) ]]);
1421 runtime("Can't filter ec");
1424 INST(FI_LCLIST_FILTER
, 2, 1) {
1427 METHOD_CONSTRUCTOR("filter");
1428 /* v2.val is either LC or LC-set */
1429 if ((v2
.type
== T_SET
) && lclist_set_type(v2
.val
.t
) || (v2
.type
== T_LCLIST
))
1430 RESULT(T_LCLIST
, ad
, [[ lclist_filter(fpool
, v1
.val
.ad
, &v2
, 1) ]]);
1432 runtime("Can't filter lc");
1435 INST(FI_ROA_CHECK_IMPLICIT
, 0, 1) { /* ROA Check */
1438 struct rtable
*table
= rtc
->table
;
1441 const net_addr
*net
= (*fs
->rte
)->net
->n
.addr
;
1443 /* We ignore temporary attributes, probably not a problem here */
1444 /* 0x02 is a value of BA_AS_PATH, we don't want to include BGP headers */
1445 eattr
*e
= ea_find(*fs
->eattrs
, EA_CODE(PROTOCOL_BGP
, 0x02));
1447 if (!e
|| ((e
->type
& EAF_TYPE_MASK
) != EAF_TYPE_AS_PATH
))
1448 runtime("Missing AS_PATH attribute");
1451 as_path_get_last(e
->u
.ptr
, &as
);
1454 runtime("Missing ROA table");
1456 if (table
->addr_type
!= NET_ROA4
&& table
->addr_type
!= NET_ROA6
)
1457 runtime("Table type must be either ROA4 or ROA6");
1459 if (table
->addr_type
!= (net
->type
== NET_IP4
? NET_ROA4
: NET_ROA6
))
1460 RESULT(T_ENUM_ROA
, i
, ROA_UNKNOWN
); /* Prefix and table type mismatch */
1462 RESULT(T_ENUM_ROA
, i
, [[ net_roa_check(table
, net
, as
) ]]);
1465 INST(FI_ROA_CHECK_EXPLICIT
, 2, 1) { /* ROA Check */
1470 struct rtable
*table
= rtc
->table
;
1475 runtime("Missing ROA table");
1477 if (table
->addr_type
!= NET_ROA4
&& table
->addr_type
!= NET_ROA6
)
1478 runtime("Table type must be either ROA4 or ROA6");
1480 if (table
->addr_type
!= (v1
.val
.net
->type
== NET_IP4
? NET_ROA4
: NET_ROA6
))
1481 RESULT(T_ENUM_ROA
, i
, ROA_UNKNOWN
); /* Prefix and table type mismatch */
1483 RESULT(T_ENUM_ROA
, i
, [[ net_roa_check(table
, v1
.val
.net
, as
) ]]);
1487 INST(FI_FROM_HEX
, 1, 1) { /* Convert hex text to bytestring */
1490 int len
= bstrhextobin(v1
.val
.s
, NULL
);
1492 runtime("Invalid hex string");
1494 struct bytestring
*bs
;
1495 bs
= falloc(sizeof(struct bytestring
) + len
);
1496 bs
->length
= bstrhextobin(v1
.val
.s
, bs
->data
);
1497 ASSERT(bs
->length
== (size_t) len
);
1499 RESULT(T_BYTESTRING
, bs
, bs
);
1502 INST(FI_FORMAT
, 1, 1) { /* Format */
1504 RESULT(T_STRING
, s
, val_format_str(fpool
, &v1
));
1507 INST(FI_ASSERT
, 1, 0) { /* Birdtest Assert */
1511 FID_MEMBER(char *, s
, [[strcmp(f1
->s
, f2
->s
)]], "string %s", item
->s
);
1515 if (!bt_assert_hook
)
1516 runtime("No bt_assert hook registered, can't assert");
1518 bt_assert_hook(v1
.val
.i
, what
);