]>
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 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
) && !val_is_undefined(v1
));
499 METHOD_R(T_NET
, type
, T_ENUM_NETTYPE
, i
, v1
.val
.net
->type
);
500 METHOD_R(T_IP
, is_v4
, 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
] = val_empty(sym
->class & 0xff);
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
, T_PATH
, ad
, &null_adata
);
557 METHOD_R(T_CLIST
, empty
, T_CLIST
, ad
, &null_adata
);
558 METHOD_R(T_ECLIST
, empty
, T_ECLIST
, ad
, &null_adata
);
559 METHOD_R(T_LCLIST
, empty
, 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_ROUTES_BLOCK_FOR_NEXT
, 3, 0) {
622 ARG(1, T_ROUTES_BLOCK
);
628 v3
.val
.rte
= v2
.val
.rte
;
629 v2
.val
.rte
= v2
.val
.rte
->next
;
633 METHOD_CONSTRUCTOR("!for_next");
636 INST(FI_CONDITION
, 1, 0) {
644 INST(FI_PRINT
, 1, 0) {
648 if (!(fs
->flags
& FF_SILENT
))
649 val_format(&v1
, &fs
->buf
);
652 INST(FI_FLUSH
, 0, 0) {
654 if (!(fs
->flags
& FF_SILENT
))
655 /* After log_commit, the buffer is reset */
656 log_commit(*L_INFO
, &fs
->buf
);
661 FID_MEMBER(enum filter_return
, fret
, f1
->fret
!= f2
->fret
, "%s", filter_return_str(item
->fret
));
663 switch (whati
->fret
) {
664 case F_ACCEPT
: /* Should take care about turning ACCEPT into MODIFY */
666 case F_REJECT
: /* Maybe print complete route along with reason to reject route? */
667 return fret
; /* We have to return now, no more processing. */
669 bug( "unknown return type: Can't happen");
673 INST(FI_RTA_GET
, 1, 1) {
679 struct rta
*rta
= v1
.val
.rte
? v1
.val
.rte
->attrs
: (*fs
->rte
)->attrs
;
683 case SA_FROM
: RESULT(sa
.f_type
, ip
, rta
->from
); break;
684 case SA_GW
: RESULT(sa
.f_type
, ip
, rta
->nh
.gw
); break;
685 case SA_NET
: RESULT(sa
.f_type
, net
, (*fs
->rte
)->net
->n
.addr
); break;
686 case SA_PROTO
: RESULT(sa
.f_type
, s
, (*fs
->rte
)->src
->proto
->name
); break;
687 case SA_SOURCE
: RESULT(sa
.f_type
, i
, rta
->source
); break;
688 case SA_SCOPE
: RESULT(sa
.f_type
, i
, rta
->scope
); break;
689 case SA_DEST
: RESULT(sa
.f_type
, i
, rta
->dest
); break;
690 case SA_IFNAME
: RESULT(sa
.f_type
, s
, rta
->nh
.iface
? rta
->nh
.iface
->name
: ""); break;
691 case SA_IFINDEX
: RESULT(sa
.f_type
, i
, rta
->nh
.iface
? rta
->nh
.iface
->index
: 0); break;
692 case SA_WEIGHT
: RESULT(sa
.f_type
, i
, rta
->nh
.weight
+ 1); break;
693 case SA_PREF
: RESULT(sa
.f_type
, i
, rta
->pref
); break;
694 case SA_GW_MPLS
: RESULT(sa
.f_type
, i
, rta
->nh
.labels
? rta
->nh
.label
[0] : MPLS_NULL
); break;
695 case SA_GW_MPLS_STACK
:
697 uint len
= rta
->nh
.labels
* sizeof(u32
);
698 struct adata
*list
= falloc(sizeof(struct adata
) + len
);
700 memcpy(list
->data
, rta
->nh
.label
, len
);
701 RESULT(sa
.f_type
, ad
, list
);
705 case SA_ONLINK
: RESULT(sa
.f_type
, i
, rta
->nh
.flags
& RNF_ONLINK
? 1 : 0); break;
708 bug("Invalid static attribute access (%u/%u)", sa
.f_type
, sa
.sa_code
);
713 INST(FI_RTA_SET
, 1, 0) {
717 ARG_TYPE(1, sa
.f_type
);
721 struct rta
*rta
= (*fs
->rte
)->attrs
;
726 rta
->from
= v1
.val
.ip
;
731 ip_addr ip
= v1
.val
.ip
;
732 struct iface
*ifa
= ipa_is_link_local(ip
) || (rta
->nh
.flags
& RNF_ONLINK
) ? rta
->nh
.iface
: NULL
;
733 neighbor
*n
= neigh_find((*fs
->rte
)->src
->proto
, ip
, ifa
, (rta
->nh
.flags
& RNF_ONLINK
) ? NEF_ONLINK
: 0);
734 if (!n
|| (n
->scope
== SCOPE_HOST
))
735 runtime( "Invalid gw address" );
737 rta
->dest
= RTD_UNICAST
;
739 rta
->nh
.iface
= n
->iface
;
741 rta
->hostentry
= NULL
;
747 rta
->scope
= v1
.val
.i
;
753 if ((i
!= RTD_BLACKHOLE
) && (i
!= RTD_UNREACHABLE
) && (i
!= RTD_PROHIBIT
))
754 runtime( "Destination can be changed only to blackhole, unreachable or prohibit" );
757 rta
->nh
.gw
= IPA_NONE
;
758 rta
->nh
.iface
= NULL
;
760 rta
->hostentry
= NULL
;
767 struct iface
*ifa
= if_find_by_name(v1
.val
.s
);
769 runtime( "Invalid iface name" );
771 rta
->dest
= RTD_UNICAST
;
772 rta
->nh
.gw
= IPA_NONE
;
775 rta
->hostentry
= NULL
;
782 if (v1
.val
.i
>= 0x100000)
783 runtime( "Invalid MPLS label" );
785 if (v1
.val
.i
!= MPLS_NULL
)
787 rta
->nh
.label
[0] = v1
.val
.i
;
793 rta
->nh
.labels_orig
= rta
->hostentry
? rta
->nh
.labels
: 0;
797 case SA_GW_MPLS_STACK
:
799 int len
= int_set_get_size(v1
.val
.ad
);
800 u32
*l
= int_set_get_data(v1
.val
.ad
);
802 if (len
> MPLS_MAX_LABEL_STACK
)
803 runtime("Too many MPLS labels in stack (%d)", len
);
806 for (i
= 0; i
< len
; i
++)
810 if (label
>= 0x100000)
811 runtime("Invalid MPLS label (%u)", label
);
813 /* Ignore rest of label stack if implicit-NULL label (3) is set */
814 if (label
== MPLS_NULL
)
817 rta
->nh
.label
[i
] = label
;
821 rta
->nh
.labels_orig
= rta
->hostentry
? i
: 0;
828 if (i
< 1 || i
> 256)
829 runtime( "Setting weight value out of bounds" );
830 if (rta
->dest
!= RTD_UNICAST
)
831 runtime( "Setting weight needs regular nexthop " );
833 /* Set weight on all next hops */
834 for (struct nexthop
*nh
= &rta
->nh
; nh
; nh
= nh
->next
)
840 rta
->pref
= v1
.val
.i
;
846 rta
->nh
.flags
|= RNF_ONLINK
;
848 rta
->nh
.flags
&= ~RNF_ONLINK
;
853 bug("Invalid static attribute access (%u/%u)", sa
.f_type
, sa
.sa_code
);
858 INST(FI_EA_GET
, 1, 1) { /* Access to extended attributes */
863 RESULT_TYPE(da
.f_type
);
865 struct ea_list
*eal
= v1
.val
.rte
? v1
.val
.rte
->attrs
->eattrs
: *fs
->eattrs
;
866 eattr
*e
= ea_find(eal
, da
.ea_code
);
869 RESULT_VAL(val_empty(da
.f_type
));
873 switch (e
->type
& EAF_TYPE_MASK
) {
875 RESULT_(da
.f_type
, i
, e
->u
.data
);
877 case EAF_TYPE_ROUTER_ID
:
878 RESULT_(T_QUAD
, i
, e
->u
.data
);
880 case EAF_TYPE_OPAQUE
:
881 if (da
.f_type
== T_ENUM_EMPTY
)
882 RESULT_(T_ENUM_EMPTY
, i
, 0);
884 RESULT_(T_BYTESTRING
, ad
, e
->u
.ptr
);
886 case EAF_TYPE_IP_ADDRESS
:
887 RESULT_(T_IP
, ip
, *((ip_addr
*) e
->u
.ptr
->data
));
889 case EAF_TYPE_AS_PATH
:
890 RESULT_(T_PATH
, ad
, e
->u
.ptr
);
892 case EAF_TYPE_BITFIELD
:
893 RESULT_(T_BOOL
, i
, !!(e
->u
.data
& (1u << da
.bit
)));
895 case EAF_TYPE_INT_SET
:
896 RESULT_(T_CLIST
, ad
, e
->u
.ptr
);
898 case EAF_TYPE_EC_SET
:
899 RESULT_(T_ECLIST
, ad
, e
->u
.ptr
);
901 case EAF_TYPE_LC_SET
:
902 RESULT_(T_LCLIST
, ad
, e
->u
.ptr
);
904 case EAF_TYPE_STRING
:
905 RESULT_(T_STRING
, s
, (const char *) e
->u
.ptr
->data
);
908 bug("Unknown dynamic attribute type");
913 INST(FI_EA_SET
, 1, 0) {
918 ARG_TYPE(1, da
.f_type
);
921 if (da
.f_type
== T_ENUM_EMPTY
)
922 cf_error("Setting opaque attribute is not allowed");
926 struct ea_list
*l
= lp_alloc(fs
->pool
, sizeof(struct ea_list
) + sizeof(eattr
));
929 l
->flags
= EALF_SORTED
;
931 l
->attrs
[0].id
= da
.ea_code
;
932 l
->attrs
[0].flags
= da
.flags
;
933 l
->attrs
[0].type
= da
.type
;
934 l
->attrs
[0].originated
= 1;
935 l
->attrs
[0].fresh
= 1;
936 l
->attrs
[0].undef
= 0;
940 case EAF_TYPE_ROUTER_ID
:
941 l
->attrs
[0].u
.data
= v1
.val
.i
;
944 case EAF_TYPE_IP_ADDRESS
:;
945 int len
= sizeof(ip_addr
);
946 struct adata
*ad
= lp_alloc(fs
->pool
, sizeof(struct adata
) + len
);
948 (* (ip_addr
*) ad
->data
) = v1
.val
.ip
;
949 l
->attrs
[0].u
.ptr
= ad
;
952 case EAF_TYPE_OPAQUE
:
953 case EAF_TYPE_AS_PATH
:
954 case EAF_TYPE_INT_SET
:
955 case EAF_TYPE_EC_SET
:
956 case EAF_TYPE_LC_SET
:
957 l
->attrs
[0].u
.ptr
= v1
.val
.ad
;
960 case EAF_TYPE_STRING
:;
961 struct adata
*d
= lp_alloc_adata(fs
->pool
, strlen(v1
.val
.s
) + 1);
962 memcpy(d
->data
, v1
.val
.s
, d
->length
);
963 l
->attrs
[0].u
.ptr
= d
;
966 case EAF_TYPE_BITFIELD
:
968 /* First, we have to find the old value */
969 eattr
*e
= ea_find(*fs
->eattrs
, da
.ea_code
);
970 u32 data
= e
? e
->u
.data
: 0;
973 l
->attrs
[0].u
.data
= data
| (1u << da
.bit
);
975 l
->attrs
[0].u
.data
= data
& ~(1u << da
.bit
);
980 bug("Unknown dynamic attribute type");
984 l
->next
= *fs
->eattrs
;
989 INST(FI_EA_UNSET
, 0, 0) {
995 ea_unset_attr(fs
->eattrs
, fs
->pool
, 1, da
.ea_code
);
999 METHOD_R(T_NET
, len
, T_INT
, i
, net_pxlen(v1
.val
.net
));
1000 METHOD_R(T_PATH
, len
, T_INT
, i
, as_path_getlen(v1
.val
.ad
));
1001 METHOD_R(T_CLIST
, len
, T_INT
, i
, int_set_get_size(v1
.val
.ad
));
1002 METHOD_R(T_ECLIST
, len
, T_INT
, i
, ec_set_get_size(v1
.val
.ad
));
1003 METHOD_R(T_LCLIST
, len
, T_INT
, i
, lc_set_get_size(v1
.val
.ad
));
1005 INST(FI_NET_SRC
, 1, 1) { /* Get src prefix */
1007 METHOD_CONSTRUCTOR("src");
1009 net_addr_union
*net
= (void *) v1
.val
.net
;
1010 net_addr
*src
= falloc(sizeof(net_addr_ip6
));
1013 switch(v1
.val
.net
->type
) {
1015 part
= flow4_get_part(&net
->flow4
, FLOW_TYPE_SRC_PREFIX
);
1017 net_fill_ip4(src
, flow_read_ip4_part(part
), flow_read_pxlen(part
));
1019 net_fill_ip4(src
, IP4_NONE
, 0);
1023 part
= flow6_get_part(&net
->flow6
, FLOW_TYPE_SRC_PREFIX
);
1025 net_fill_ip6(src
, flow_read_ip6_part(part
), flow_read_pxlen(part
));
1027 net_fill_ip6(src
, IP6_NONE
, 0);
1031 net_fill_ip6(src
, net
->ip6_sadr
.src_prefix
, net
->ip6_sadr
.src_pxlen
);
1035 runtime( "Flow or SADR expected" );
1038 RESULT(T_NET
, net
, src
);
1041 INST(FI_NET_DST
, 1, 1) { /* Get dst prefix */
1043 METHOD_CONSTRUCTOR("dst");
1045 net_addr_union
*net
= (void *) v1
.val
.net
;
1046 net_addr
*dst
= falloc(sizeof(net_addr_ip6
));
1049 switch(v1
.val
.net
->type
) {
1051 part
= flow4_get_part(&net
->flow4
, FLOW_TYPE_DST_PREFIX
);
1053 net_fill_ip4(dst
, flow_read_ip4_part(part
), flow_read_pxlen(part
));
1055 net_fill_ip4(dst
, IP4_NONE
, 0);
1059 part
= flow6_get_part(&net
->flow6
, FLOW_TYPE_DST_PREFIX
);
1061 net_fill_ip6(dst
, flow_read_ip6_part(part
), flow_read_pxlen(part
));
1063 net_fill_ip6(dst
, IP6_NONE
, 0);
1067 net_fill_ip6(dst
, net
->ip6_sadr
.dst_prefix
, net
->ip6_sadr
.dst_pxlen
);
1071 runtime( "Flow or SADR expected" );
1074 RESULT(T_NET
, net
, dst
);
1077 /* Get ROA max prefix length */
1078 METHOD(T_NET
, maxlen
, 0, [[
1079 if (!net_is_roa(v1
.val
.net
))
1080 runtime( "ROA expected" );
1082 RESULT(T_INT
, i
, (v1
.val
.net
->type
== NET_ROA4
) ?
1083 ((net_addr_roa4
*) v1
.val
.net
)->max_pxlen
:
1084 ((net_addr_roa6
*) v1
.val
.net
)->max_pxlen
);
1088 METHOD(T_NET
, asn
, 0, [[
1089 if (!net_is_roa(v1
.val
.net
))
1090 runtime( "ROA expected" );
1092 RESULT(T_INT
, i
, (v1
.val
.net
->type
== NET_ROA4
) ?
1093 ((net_addr_roa4
*) v1
.val
.net
)->asn
:
1094 ((net_addr_roa6
*) v1
.val
.net
)->asn
);
1097 /* Convert prefix to IP */
1098 METHOD_R(T_NET
, ip
, T_IP
, ip
, net_prefix(v1
.val
.net
));
1100 INST(FI_ROUTE_DISTINGUISHER
, 1, 1) {
1102 METHOD_CONSTRUCTOR("rd");
1103 if (!net_is_vpn(v1
.val
.net
))
1104 runtime( "VPN address expected" );
1105 RESULT(T_RD
, ec
, net_rd(v1
.val
.net
));
1108 /* Get first ASN from AS PATH */
1109 METHOD_R(T_PATH
, first
, T_INT
, i
, ({ u32 as
= 0; as_path_get_first(v1
.val
.ad
, &as
); as
; }));
1111 /* Get last ASN from AS PATH */
1112 METHOD_R(T_PATH
, last
, T_INT
, i
, ({ u32 as
= 0; as_path_get_last(v1
.val
.ad
, &as
); as
; }));
1114 /* Get last ASN from non-aggregated part of AS PATH */
1115 METHOD_R(T_PATH
, last_nonaggregated
, T_INT
, i
, as_path_get_last_nonaggregated(v1
.val
.ad
));
1117 /* Get ASN part from the standard community ASN */
1118 METHOD_R(T_PAIR
, asn
, T_INT
, i
, v1
.val
.i
>> 16);
1120 /* Get data part from the standard community */
1121 METHOD_R(T_PAIR
, data
, T_INT
, i
, v1
.val
.i
& 0xFFFF);
1123 /* Get ASN part from the large community */
1124 METHOD_R(T_LC
, asn
, T_INT
, i
, v1
.val
.lc
.asn
);
1126 /* Get data1 part from the large community */
1127 METHOD_R(T_LC
, data1
, T_INT
, i
, v1
.val
.lc
.ldp1
);
1129 /* Get data2 part from the large community */
1130 METHOD_R(T_LC
, data2
, T_INT
, i
, v1
.val
.lc
.ldp2
);
1132 /* Get minimum element from clist */
1133 METHOD_R(T_CLIST
, min
, T_PAIR
, i
, ({ u32 val
= 0; int_set_min(v1
.val
.ad
, &val
); val
; }));
1135 /* Get maximum element from clist */
1136 METHOD_R(T_CLIST
, max
, T_PAIR
, i
, ({ u32 val
= 0; int_set_max(v1
.val
.ad
, &val
); val
; }));
1138 /* Get minimum element from eclist */
1139 METHOD_R(T_ECLIST
, min
, T_EC
, ec
, ({ u64 val
= 0; ec_set_min(v1
.val
.ad
, &val
); val
; }));
1141 /* Get maximum element from eclist */
1142 METHOD_R(T_ECLIST
, max
, T_EC
, ec
, ({ u64 val
= 0; ec_set_max(v1
.val
.ad
, &val
); val
; }));
1144 /* Get minimum element from lclist */
1145 METHOD_R(T_LCLIST
, min
, T_LC
, lc
, ({ lcomm val
= {}; lc_set_min(v1
.val
.ad
, &val
); val
; }));
1147 /* Get maximum element from lclist */
1148 METHOD_R(T_LCLIST
, max
, T_LC
, lc
, ({ lcomm val
= {}; lc_set_max(v1
.val
.ad
, &val
); val
; }));
1150 INST(FI_RETURN
, 1, 0) {
1152 /* Acquire the return value */
1154 uint retpos
= fstk
->vcnt
;
1156 /* Drop every sub-block including ourselves */
1158 while ((fstk
->ecnt
> 0) && !(fstk
->estk
[fstk
->ecnt
].emask
& FE_RETURN
));
1160 /* Now we are at the caller frame; if no such, try to convert to accept/reject. */
1163 if (fstk
->vstk
[retpos
].type
== T_BOOL
)
1164 return (fstk
->vstk
[retpos
].val
.i
) ? F_ACCEPT
: F_REJECT
;
1166 runtime("Can't return non-bool from non-function");
1169 /* Set the value stack position, overwriting the former implicit void */
1170 fstk
->vcnt
= fstk
->estk
[fstk
->ecnt
].ventry
- 1;
1172 /* Copy the return value */
1173 RESULT_VAL(fstk
->vstk
[retpos
]);
1176 INST(FI_CALL
, 0, 1) {
1180 RESULT_TYPE(sym
->function
->return_type
);
1183 ASSERT(sym
->class == SYM_FUNCTION
);
1185 if (whati
->varcount
!= sym
->function
->args
)
1186 cf_error("Function '%s' expects %u arguments, got %u arguments",
1187 sym
->name
, sym
->function
->args
, whati
->varcount
);
1189 /* Typecheck individual arguments */
1190 struct f_inst
*a
= fvar
;
1191 struct f_arg
*b
= sym
->function
->arg_list
;
1192 for (uint i
= 1; a
&& b
; a
= a
->next
, b
= b
->next
, i
++)
1194 enum f_type b_type
= b
->arg
->class & 0xff;
1196 if (a
->type
&& (a
->type
!= b_type
) && !f_const_promotion(a
, b_type
))
1197 cf_error("Argument %u of '%s' must be %s, got %s",
1198 i
, sym
->name
, f_type_name(b_type
), f_type_name(a
->type
));
1202 /* Add implicit void slot for the return value */
1203 struct f_inst
*tmp
= f_new_inst(FI_CONSTANT
, (struct f_val
) { .type
= T_VOID
});
1204 tmp
->next
= whati
->fvar
;
1206 what
->size
+= tmp
->size
;
1208 /* Mark recursive calls, they have dummy f_line */
1209 if (!sym
->function
->len
)
1210 what
->flags
|= FIF_RECURSIVE
;
1213 if (!(f1
->sym
->flags
& SYM_FLAG_SAME
) && !(f1_
->flags
& FIF_RECURSIVE
))
1217 if (!(what
->flags
& FIF_RECURSIVE
))
1218 BUFFER_PUSH(fit
->lines
) = whati
->sym
->function
;
1220 FID_INTERPRET_BODY()
1222 /* Push the body on stack */
1223 LINEX(sym
->function
);
1224 curline
.vbase
= curline
.ventry
;
1225 curline
.emask
|= FE_RETURN
;
1227 /* Arguments on stack */
1228 fstk
->vcnt
+= sym
->function
->args
;
1230 /* Storage for local variables */
1231 memset(&(fstk
->vstk
[fstk
->vcnt
]), 0, sizeof(struct f_val
) * sym
->function
->vars
);
1232 fstk
->vcnt
+= sym
->function
->vars
;
1235 INST(FI_DROP_RESULT
, 1, 0) {
1240 INST(FI_SWITCH
, 1, 0) {
1243 FID_MEMBER(struct f_tree
*, tree
, [[!same_tree(f1
->tree
, f2
->tree
)]], "tree %p", item
->tree
);
1245 FID_LINEARIZE_BODY()
1246 /* Linearize all branches in switch */
1247 struct f_inst
*last_inst
= NULL
;
1248 struct f_line
*last_line
= NULL
;
1249 for (struct f_tree
*t
= whati
->tree
; t
; t
= t
->left
)
1251 if (t
->data
!= last_inst
)
1253 last_inst
= t
->data
;
1254 last_line
= f_linearize(t
->data
, 0);
1257 t
->data
= last_line
;
1260 /* Balance the tree */
1261 item
->tree
= build_tree(whati
->tree
);
1264 tree_walk(whati
->tree
, f_add_tree_lines
, fit
);
1266 FID_INTERPRET_BODY()
1267 /* In parse-time use find_tree_linear(), in runtime use find_tree() */
1268 const struct f_tree
*t
= FID_HIC(,find_tree
,find_tree_linear
)(tree
, &v1
);
1271 t
= FID_HIC(,find_tree
,find_tree_linear
)(tree
, &v1
);
1273 debug( "No else statement?\n");
1274 FID_HIC(,break,return NULL
);
1281 INST(FI_IP_MASK
, 2, 1) { /* IP.MASK(val) */
1284 METHOD_CONSTRUCTOR("mask");
1285 RESULT(T_IP
, ip
, [[ ipa_is_ip4(v1
.val
.ip
) ?
1286 ipa_from_ip4(ip4_and(ipa_to_ip4(v1
.val
.ip
), ip4_mkmask(v2
.val
.i
))) :
1287 ipa_from_ip6(ip6_and(ipa_to_ip6(v1
.val
.ip
), ip6_mkmask(v2
.val
.i
))) ]]);
1290 INST(FI_PATH_PREPEND
, 2, 1) { /* Path prepend */
1293 METHOD_CONSTRUCTOR("prepend");
1294 RESULT(T_PATH
, ad
, [[ as_path_prepend(fpool
, v1
.val
.ad
, v2
.val
.i
) ]]);
1297 /* Community list add */
1298 INST(FI_CLIST_ADD_PAIR
, 2, 1) {
1301 METHOD_CONSTRUCTOR("add");
1302 RESULT(T_CLIST
, ad
, [[ int_set_add(fpool
, v1
.val
.ad
, v2
.val
.i
) ]]);
1305 /* Hack for gw_mpls_list */
1306 INST(FI_CLIST_ADD_INT
, 2, 1) {
1309 METHOD_CONSTRUCTOR("add");
1310 RESULT(T_CLIST
, ad
, [[ int_set_add(fpool
, v1
.val
.ad
, v2
.val
.i
) ]]);
1313 INST(FI_CLIST_ADD_IP
, 2, 1) {
1316 METHOD_CONSTRUCTOR("add");
1319 /* IP->Quad implicit conversion, must be before FI_CLIST_ADD_QUAD */
1320 cf_warn("Method add(clist, ip) is deprecated, please use add(clist, quad)");
1322 FID_INTERPRET_BODY();
1323 if (!val_is_ip4(&v2
)) runtime("Mismatched IP type");
1324 RESULT(T_CLIST
, ad
, [[ int_set_add(fpool
, v1
.val
.ad
, ipa_to_u32(v2
.val
.ip
)) ]]);
1327 INST(FI_CLIST_ADD_QUAD
, 2, 1) {
1330 METHOD_CONSTRUCTOR("add");
1331 RESULT(T_CLIST
, ad
, [[ int_set_add(fpool
, v1
.val
.ad
, v2
.val
.i
) ]]);
1334 INST(FI_CLIST_ADD_CLIST
, 2, 1) {
1337 METHOD_CONSTRUCTOR("add");
1338 RESULT(T_CLIST
, ad
, [[ int_set_union(fpool
, v1
.val
.ad
, v2
.val
.ad
) ]]);
1341 INST(FI_ECLIST_ADD_EC
, 2, 1) {
1344 METHOD_CONSTRUCTOR("add");
1345 RESULT(T_ECLIST
, ad
, [[ ec_set_add(fpool
, v1
.val
.ad
, v2
.val
.ec
) ]]);
1348 INST(FI_ECLIST_ADD_ECLIST
, 2, 1) {
1351 METHOD_CONSTRUCTOR("add");
1352 RESULT(T_ECLIST
, ad
, [[ ec_set_union(fpool
, v1
.val
.ad
, v2
.val
.ad
) ]]);
1355 INST(FI_LCLIST_ADD_LC
, 2, 1) {
1358 METHOD_CONSTRUCTOR("add");
1359 RESULT(T_LCLIST
, ad
, [[ lc_set_add(fpool
, v1
.val
.ad
, v2
.val
.lc
) ]]);
1362 INST(FI_LCLIST_ADD_LCLIST
, 2, 1) {
1365 METHOD_CONSTRUCTOR("add");
1366 RESULT(T_LCLIST
, ad
, [[ lc_set_union(fpool
, v1
.val
.ad
, v2
.val
.ad
) ]]);
1369 INST(FI_PATH_DELETE_INT
, 2, 1) {
1372 METHOD_CONSTRUCTOR("delete");
1373 RESULT(T_PATH
, ad
, [[ as_path_filter(fpool
, v1
.val
.ad
, &v2
, 0) ]]);
1376 INST(FI_PATH_DELETE_SET
, 2, 1) {
1379 METHOD_CONSTRUCTOR("delete");
1381 if (!path_set_type(v2
.val
.t
))
1382 runtime("Mismatched set type");
1384 RESULT(T_PATH
, ad
, [[ as_path_filter(fpool
, v1
.val
.ad
, &v2
, 0) ]]);
1387 /* Community list delete */
1388 INST(FI_CLIST_DELETE_PAIR
, 2, 1) {
1391 METHOD_CONSTRUCTOR("delete");
1392 RESULT(T_CLIST
, ad
, [[ int_set_del(fpool
, v1
.val
.ad
, v2
.val
.i
) ]]);
1395 /* Hack for gw_mpls_list */
1396 INST(FI_CLIST_DELETE_INT
, 2, 1) {
1399 METHOD_CONSTRUCTOR("delete");
1400 RESULT(T_CLIST
, ad
, [[ int_set_del(fpool
, v1
.val
.ad
, v2
.val
.i
) ]]);
1403 INST(FI_CLIST_DELETE_IP
, 2, 1) {
1406 METHOD_CONSTRUCTOR("delete");
1409 /* IP->Quad implicit conversion, must be before FI_CLIST_DELETE_QUAD */
1410 cf_warn("Method delete(clist, ip) is deprecated, please use delete(clist, quad)");
1412 FID_INTERPRET_BODY();
1413 if (!val_is_ip4(&v2
)) runtime("Mismatched IP type");
1414 RESULT(T_CLIST
, ad
, [[ int_set_del(fpool
, v1
.val
.ad
, ipa_to_u32(v2
.val
.ip
)) ]]);
1417 INST(FI_CLIST_DELETE_QUAD
, 2, 1) {
1420 METHOD_CONSTRUCTOR("delete");
1421 RESULT(T_CLIST
, ad
, [[ int_set_del(fpool
, v1
.val
.ad
, v2
.val
.i
) ]]);
1424 INST(FI_CLIST_DELETE_CLIST
, 2, 1) {
1427 METHOD_CONSTRUCTOR("delete");
1428 RESULT(T_CLIST
, ad
, [[ clist_filter(fpool
, v1
.val
.ad
, &v2
, 0) ]]);
1431 INST(FI_CLIST_DELETE_SET
, 2, 1) {
1434 METHOD_CONSTRUCTOR("delete");
1436 if (!clist_set_type(v2
.val
.t
, &(struct f_val
){}))
1437 runtime("Mismatched set type");
1439 RESULT(T_CLIST
, ad
, [[ clist_filter(fpool
, v1
.val
.ad
, &v2
, 0) ]]);
1442 INST(FI_ECLIST_DELETE_EC
, 2, 1) {
1445 METHOD_CONSTRUCTOR("delete");
1446 RESULT(T_ECLIST
, ad
, [[ ec_set_del(fpool
, v1
.val
.ad
, v2
.val
.ec
) ]]);
1449 INST(FI_ECLIST_DELETE_ECLIST
, 2, 1) {
1452 METHOD_CONSTRUCTOR("delete");
1453 RESULT(T_ECLIST
, ad
, [[ eclist_filter(fpool
, v1
.val
.ad
, &v2
, 0) ]]);
1456 INST(FI_ECLIST_DELETE_SET
, 2, 1) {
1459 METHOD_CONSTRUCTOR("delete");
1461 if (!eclist_set_type(v2
.val
.t
))
1462 runtime("Mismatched set type");
1464 RESULT(T_ECLIST
, ad
, [[ eclist_filter(fpool
, v1
.val
.ad
, &v2
, 0) ]]);
1467 INST(FI_LCLIST_DELETE_LC
, 2, 1) {
1470 METHOD_CONSTRUCTOR("delete");
1471 RESULT(T_LCLIST
, ad
, [[ lc_set_del(fpool
, v1
.val
.ad
, v2
.val
.lc
) ]]);
1474 INST(FI_LCLIST_DELETE_LCLIST
, 2, 1) {
1477 METHOD_CONSTRUCTOR("delete");
1478 RESULT(T_LCLIST
, ad
, [[ lclist_filter(fpool
, v1
.val
.ad
, &v2
, 0) ]]);
1481 INST(FI_LCLIST_DELETE_SET
, 2, 1) {
1484 METHOD_CONSTRUCTOR("delete");
1486 if (!lclist_set_type(v2
.val
.t
))
1487 runtime("Mismatched set type");
1489 RESULT(T_LCLIST
, ad
, [[ lclist_filter(fpool
, v1
.val
.ad
, &v2
, 0) ]]);
1492 INST(FI_PATH_FILTER_SET
, 2, 1) {
1495 METHOD_CONSTRUCTOR("filter");
1497 if (!path_set_type(v2
.val
.t
))
1498 runtime("Mismatched set type");
1500 RESULT(T_PATH
, ad
, [[ as_path_filter(fpool
, v1
.val
.ad
, &v2
, 1) ]]);
1503 INST(FI_CLIST_FILTER_CLIST
, 2, 1) {
1506 METHOD_CONSTRUCTOR("filter");
1507 RESULT(T_CLIST
, ad
, [[ clist_filter(fpool
, v1
.val
.ad
, &v2
, 1) ]]);
1510 INST(FI_CLIST_FILTER_SET
, 2, 1) {
1513 METHOD_CONSTRUCTOR("filter");
1515 if (!clist_set_type(v2
.val
.t
, &(struct f_val
){}))
1516 runtime("Mismatched set type");
1518 RESULT(T_CLIST
, ad
, [[ clist_filter(fpool
, v1
.val
.ad
, &v2
, 1) ]]);
1521 INST(FI_ECLIST_FILTER_ECLIST
, 2, 1) {
1524 METHOD_CONSTRUCTOR("filter");
1525 RESULT(T_ECLIST
, ad
, [[ eclist_filter(fpool
, v1
.val
.ad
, &v2
, 1) ]]);
1528 INST(FI_ECLIST_FILTER_SET
, 2, 1) {
1531 METHOD_CONSTRUCTOR("filter");
1533 if (!eclist_set_type(v2
.val
.t
))
1534 runtime("Mismatched set type");
1536 RESULT(T_ECLIST
, ad
, [[ eclist_filter(fpool
, v1
.val
.ad
, &v2
, 1) ]]);
1539 INST(FI_LCLIST_FILTER_LCLIST
, 2, 1) {
1542 METHOD_CONSTRUCTOR("filter");
1543 RESULT(T_LCLIST
, ad
, [[ lclist_filter(fpool
, v1
.val
.ad
, &v2
, 1) ]]);
1546 INST(FI_LCLIST_FILTER_SET
, 2, 1) {
1549 METHOD_CONSTRUCTOR("filter");
1551 if (!lclist_set_type(v2
.val
.t
))
1552 runtime("Mismatched set type");
1554 RESULT(T_LCLIST
, ad
, [[ lclist_filter(fpool
, v1
.val
.ad
, &v2
, 1) ]]);
1557 INST(FI_ROA_CHECK_IMPLICIT
, 0, 1) { /* ROA Check */
1560 struct rtable
*table
= rtc
->table
;
1563 const net_addr
*net
= (*fs
->rte
)->net
->n
.addr
;
1565 /* We ignore temporary attributes, probably not a problem here */
1566 /* 0x02 is a value of BA_AS_PATH, we don't want to include BGP headers */
1567 eattr
*e
= ea_find(*fs
->eattrs
, EA_CODE(PROTOCOL_BGP
, 0x02));
1569 if (!e
|| ((e
->type
& EAF_TYPE_MASK
) != EAF_TYPE_AS_PATH
))
1570 runtime("Missing AS_PATH attribute");
1573 as_path_get_last(e
->u
.ptr
, &as
);
1576 runtime("Missing ROA table");
1578 if (table
->addr_type
!= NET_ROA4
&& table
->addr_type
!= NET_ROA6
)
1579 runtime("Table type must be either ROA4 or ROA6");
1581 if (table
->addr_type
!= (net
->type
== NET_IP4
? NET_ROA4
: NET_ROA6
))
1582 RESULT(T_ENUM_ROA
, i
, ROA_UNKNOWN
); /* Prefix and table type mismatch */
1584 RESULT(T_ENUM_ROA
, i
, [[ net_roa_check(table
, net
, as
) ]]);
1587 INST(FI_ROA_CHECK_EXPLICIT
, 2, 1) { /* ROA Check */
1592 struct rtable
*table
= rtc
->table
;
1597 runtime("Missing ROA table");
1599 if (table
->addr_type
!= NET_ROA4
&& table
->addr_type
!= NET_ROA6
)
1600 runtime("Table type must be either ROA4 or ROA6");
1602 if (table
->addr_type
!= (v1
.val
.net
->type
== NET_IP4
? NET_ROA4
: NET_ROA6
))
1603 RESULT(T_ENUM_ROA
, i
, ROA_UNKNOWN
); /* Prefix and table type mismatch */
1605 RESULT(T_ENUM_ROA
, i
, [[ net_roa_check(table
, v1
.val
.net
, as
) ]]);
1609 INST(FI_ASPA_CHECK_EXPLICIT
, 1, 1) { /* ASPA Check */
1613 struct rtable
*table
= rtc
->table
;
1616 runtime("Missing ASPA table");
1618 if (table
->addr_type
!= NET_ASPA
)
1619 runtime("Table type must be ASPA");
1621 RESULT(T_ENUM_ASPA
, i
, [[ aspa_check(table
, v1
.val
.ad
) ]]);
1624 INST(FI_FROM_HEX
, 1, 1) { /* Convert hex text to bytestring */
1627 int len
= bstrhextobin(v1
.val
.s
, NULL
);
1629 runtime("Invalid hex string");
1632 bs
= falloc(sizeof(struct adata
) + len
);
1633 bs
->length
= bstrhextobin(v1
.val
.s
, bs
->data
);
1634 ASSERT(bs
->length
== (size_t) len
);
1636 RESULT(T_BYTESTRING
, bs
, bs
);
1639 INST(FI_FORMAT
, 1, 1) { /* Format */
1641 RESULT(T_STRING
, s
, val_format_str(fpool
, &v1
));
1644 INST(FI_ASSERT
, 1, 0) { /* Birdtest Assert */
1648 FID_MEMBER(char *, s
, [[strcmp(f1
->s
, f2
->s
)]], "string %s", item
->s
);
1652 if (!bt_assert_hook
)
1653 runtime("No bt_assert hook registered, can't assert");
1655 bt_assert_hook(v1
.val
.i
, what
);