]>
Commit | Line | Data |
---|---|---|
f62a369f JMM |
1 | /* |
2 | * Filters: Instructions themselves | |
3 | * | |
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. | |
7 | * | |
8 | * Can be freely distributed and used under the terms of the GNU GPL. | |
9 | * | |
0da06b71 MM |
10 | * The filter code goes through several phases: |
11 | * | |
12 | * 1 Parsing | |
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. | |
22 | * | |
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. | |
29 | * | |
30 | * 3 Interpret | |
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. | |
35 | * | |
36 | * 4 Same | |
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. | |
40 | * | |
41 | * The main purpose of this rework was to improve filter performance | |
42 | * by making the interpreter non-recursive. | |
43 | * | |
44 | * The other outcome is concentration of instruction definitions to | |
45 | * one place -- right here. You shall define your instruction only here | |
e1ac6f1e MM |
46 | * and nowhere else. |
47 | * | |
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. | |
53 | * | |
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 | |
56 | * different places. | |
57 | * | |
26bfe59f MM |
58 | * All the arguments are processed literally; if you need an argument including comma, |
59 | * you have to quote it by [[ ... ]] | |
60 | * | |
e1ac6f1e MM |
61 | * What is the syntax here? |
62 | * m4_dnl INST(FI_NOP, in, out) { enum value, input args, output args | |
c0999a14 MM |
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 | |
cde8094c | 65 | * m4_dnl ARG_TYPE(num, type); just declare the type of argument |
c0999a14 | 66 | * m4_dnl VARARG; variable-length argument list; accessible by vv(i) and whati->varcount |
a2527ee5 | 67 | * m4_dnl LINE(num, out); this argument has to be converted to its own f_line |
63f49457 | 68 | * m4_dnl SYMBOL; symbol handed from config |
e1ac6f1e MM |
69 | * m4_dnl STATIC_ATTR; static attribute definition |
70 | * m4_dnl DYNAMIC_ATTR; dynamic attribute definition | |
71 | * m4_dnl RTC; route table config | |
e1ac6f1e MM |
72 | * m4_dnl ACCESS_RTE; this instruction needs route |
73 | * m4_dnl ACCESS_EATTRS; this instruction needs extended attributes | |
26bfe59f | 74 | * |
fc9d471b MM |
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 |
76 | * | |
26bfe59f MM |
77 | * m4_dnl FID_MEMBER( custom instruction member |
78 | * m4_dnl C type, for storage in structs | |
b40c0f02 | 79 | * m4_dnl name, how the member is named |
30667d50 | 80 | * m4_dnl comparator for same(), if different, this should be TRUE (CAVEAT) |
26bfe59f MM |
81 | * m4_dnl dump format string debug -> format string for bvsnprintf |
82 | * m4_dnl dump format args appropriate args | |
26bfe59f MM |
83 | * m4_dnl ) |
84 | * | |
e1ac6f1e | 85 | * m4_dnl RESULT(type, union-field, value); putting this on value stack |
cde8094c | 86 | * m4_dnl RESULT_(type, union-field, value); like RESULT(), but do not declare the type |
f74d1976 | 87 | * m4_dnl RESULT_VAL(value-struct); pass the struct f_val directly |
cde8094c | 88 | * m4_dnl RESULT_TYPE(type); just declare the type of result value |
f74d1976 | 89 | * m4_dnl RESULT_VOID; return undef |
e1ac6f1e MM |
90 | * m4_dnl } |
91 | * | |
cb339a30 OZ |
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. | |
96 | * | |
0da06b71 MM |
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. | |
101 | * | |
e1ac6f1e MM |
102 | * Other code is just copied into the interpreter part. |
103 | * | |
6d411fc7 MM |
104 | * It's also possible to declare type methods in a short way: |
105 | * | |
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) | |
108 | * | |
cde8094c OZ |
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. | |
114 | * | |
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(). | |
119 | * | |
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. | |
125 | * | |
126 | * | |
0da06b71 MM |
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. | |
129 | * | |
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): | |
133 | * | |
134 | * m4_dnl (102) struct f_inst *f_new_inst(FI_EXAMPLE [[ put it here ]]) | |
135 | * m4_dnl { | |
136 | * m4_dnl ... (common code) | |
137 | * m4_dnl (103) [[ put it here ]] | |
138 | * m4_dnl ... | |
139 | * m4_dnl if (all arguments are constant) | |
26194bd6 | 140 | * m4_dnl (108) [[ put it here ]] |
0da06b71 MM |
141 | * m4_dnl } |
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. | |
145 | * | |
146 | * m4_dnl struct f_inst { | |
147 | * m4_dnl ... (common fields) | |
148 | * m4_dnl union { | |
149 | * m4_dnl struct { | |
150 | * m4_dnl (101) [[ put it here ]] | |
151 | * m4_dnl } i_FI_EXAMPLE; | |
152 | * m4_dnl ... | |
153 | * m4_dnl }; | |
154 | * m4_dnl }; | |
155 | * m4_dnl This structure is returned from constructor. | |
156 | * m4_dnl For writing directly to this structure, use FID_STRUCT_IN. | |
157 | * | |
158 | * m4_dnl linearize(struct f_line *dest, const struct f_inst *what, uint pos) { | |
159 | * m4_dnl ... | |
160 | * m4_dnl switch (what->fi_code) { | |
161 | * m4_dnl case FI_EXAMPLE: | |
162 | * m4_dnl (105) [[ put it here ]] | |
163 | * m4_dnl break; | |
164 | * m4_dnl } | |
165 | * m4_dnl } | |
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. | |
171 | * | |
172 | * m4_dnl (107) struct f_line_item { | |
173 | * m4_dnl ... (common fields) | |
174 | * m4_dnl union { | |
175 | * m4_dnl struct { | |
176 | * m4_dnl (101) [[ put it here ]] | |
177 | * m4_dnl } i_FI_EXAMPLE; | |
178 | * m4_dnl ... | |
179 | * m4_dnl }; | |
180 | * m4_dnl }; | |
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. | |
184 | * | |
185 | * m4_dnl f_dump_line_item_FI_EXAMPLE(const struct f_line_item *item, const int indent) | |
186 | * m4_dnl { | |
187 | * m4_dnl (104) [[ put it here ]] | |
188 | * m4_dnl } | |
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. | |
194 | * | |
195 | * m4_dnl f_same(...) | |
196 | * m4_dnl { | |
197 | * m4_dnl switch (f1_->fi_code) { | |
198 | * m4_dnl case FI_EXAMPLE: | |
199 | * m4_dnl (106) [[ put it here ]] | |
200 | * m4_dnl break; | |
201 | * m4_dnl } | |
202 | * m4_dnl } | |
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. | |
207 | * | |
d06a875b OZ |
208 | * m4_dnl f_add_lines(...) |
209 | * m4_dnl { | |
210 | * m4_dnl switch (what_->fi_code) { | |
211 | * m4_dnl case FI_EXAMPLE: | |
212 | * m4_dnl (109) [[ put it here ]] | |
213 | * m4_dnl break; | |
214 | * m4_dnl } | |
215 | * m4_dnl } | |
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. | |
223 | * | |
0da06b71 MM |
224 | * m4_dnl interpret(...) |
225 | * m4_dnl { | |
226 | * m4_dnl switch (what->fi_code) { | |
227 | * m4_dnl case FI_EXAMPLE: | |
228 | * m4_dnl (108) [[ put it here ]] | |
229 | * m4_dnl break; | |
230 | * m4_dnl } | |
231 | * m4_dnl } | |
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 | |
249 | * | |
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. | |
cde8094c OZ |
252 | * |
253 | * | |
254 | * A note about soundness of the type system: | |
255 | * | |
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: | |
260 | * | |
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: | |
264 | * | |
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. | |
269 | * | |
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. | |
276 | * | |
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. | |
280 | * | |
281 | * Also note that FI_CALL is the only expression without properly declared | |
282 | * result type. | |
f62a369f JMM |
283 | */ |
284 | ||
285 | /* Binary operators */ | |
4c553c5a MM |
286 | INST(FI_ADD, 2, 1) { |
287 | ARG(1,T_INT); | |
288 | ARG(2,T_INT); | |
f74d1976 | 289 | RESULT(T_INT, i, v1.val.i + v2.val.i); |
4c553c5a MM |
290 | } |
291 | INST(FI_SUBTRACT, 2, 1) { | |
292 | ARG(1,T_INT); | |
293 | ARG(2,T_INT); | |
f74d1976 | 294 | RESULT(T_INT, i, v1.val.i - v2.val.i); |
4c553c5a MM |
295 | } |
296 | INST(FI_MULTIPLY, 2, 1) { | |
297 | ARG(1,T_INT); | |
298 | ARG(2,T_INT); | |
f74d1976 | 299 | RESULT(T_INT, i, v1.val.i * v2.val.i); |
4c553c5a MM |
300 | } |
301 | INST(FI_DIVIDE, 2, 1) { | |
302 | ARG(1,T_INT); | |
303 | ARG(2,T_INT); | |
304 | if (v2.val.i == 0) runtime( "Mother told me not to divide by 0" ); | |
f74d1976 | 305 | RESULT(T_INT, i, v1.val.i / v2.val.i); |
4c553c5a MM |
306 | } |
307 | INST(FI_AND, 1, 1) { | |
308 | ARG(1,T_BOOL); | |
ef8c4574 | 309 | ARG_TYPE_STATIC(2,T_BOOL); |
10c4cd96 OZ |
310 | RESULT_TYPE(T_BOOL); |
311 | ||
f74d1976 | 312 | if (v1.val.i) |
a2527ee5 | 313 | LINE(2,1); |
4c553c5a | 314 | else |
f74d1976 | 315 | RESULT_VAL(v1); |
967b88d9 | 316 | } |
4c553c5a MM |
317 | INST(FI_OR, 1, 1) { |
318 | ARG(1,T_BOOL); | |
ef8c4574 | 319 | ARG_TYPE_STATIC(2,T_BOOL); |
10c4cd96 OZ |
320 | RESULT_TYPE(T_BOOL); |
321 | ||
f74d1976 | 322 | if (!v1.val.i) |
a2527ee5 | 323 | LINE(2,1); |
4c553c5a | 324 | else |
f74d1976 | 325 | RESULT_VAL(v1); |
967b88d9 | 326 | } |
bfa15a64 | 327 | |
4c553c5a | 328 | INST(FI_PAIR_CONSTRUCT, 2, 1) { |
f62a369f JMM |
329 | ARG(1,T_INT); |
330 | ARG(2,T_INT); | |
4c553c5a MM |
331 | uint u1 = v1.val.i; |
332 | uint u2 = v2.val.i; | |
f62a369f JMM |
333 | if ((u1 > 0xFFFF) || (u2 > 0xFFFF)) |
334 | runtime( "Can't operate with value out of bounds in pair constructor" ); | |
4c553c5a | 335 | RESULT(T_PAIR, i, (u1 << 16) | u2); |
967b88d9 | 336 | } |
bfa15a64 | 337 | |
4c553c5a MM |
338 | INST(FI_EC_CONSTRUCT, 2, 1) { |
339 | ARG_ANY(1); | |
340 | ARG(2, T_INT); | |
26bfe59f | 341 | |
f634adc7 | 342 | FID_MEMBER(enum ec_subtype, ecs, f1->ecs != f2->ecs, "ec subtype %s", ec_subtype_str(item->ecs)); |
f62a369f | 343 | |
124d860f | 344 | int ipv4_used; |
4c553c5a | 345 | u32 key, val; |
f62a369f | 346 | |
4c553c5a MM |
347 | if (v1.type == T_INT) { |
348 | ipv4_used = 0; key = v1.val.i; | |
349 | } | |
350 | else if (v1.type == T_QUAD) { | |
351 | ipv4_used = 1; key = v1.val.i; | |
352 | } | |
353 | /* IP->Quad implicit conversion */ | |
354 | else if (val_is_ip4(&v1)) { | |
355 | ipv4_used = 1; key = ipa_to_u32(v1.val.ip); | |
356 | } | |
357 | else | |
bfa15a64 | 358 | runtime("Argument 1 of EC constructor must be integer or IPv4 address, got 0x%02x", v1.type); |
f62a369f | 359 | |
4c553c5a | 360 | val = v2.val.i; |
f62a369f | 361 | |
124d860f MM |
362 | if (ecs == EC_GENERIC) |
363 | RESULT(T_EC, ec, ec_generic(key, val)); | |
364 | else if (ipv4_used) | |
365 | if (val <= 0xFFFF) | |
366 | RESULT(T_EC, ec, ec_ip4(ecs, key, val)); | |
367 | else | |
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)); | |
371 | else | |
372 | if (val <= 0xFFFF) | |
373 | RESULT(T_EC, ec, ec_as4(ecs, key, val)); | |
374 | else | |
375 | runtime("4-byte value %u can't be used with 4-byte ASN in extended community", val); | |
4c553c5a | 376 | } |
f62a369f | 377 | |
4c553c5a MM |
378 | INST(FI_LC_CONSTRUCT, 3, 1) { |
379 | ARG(1, T_INT); | |
380 | ARG(2, T_INT); | |
381 | ARG(3, T_INT); | |
382 | RESULT(T_LC, lc, [[(lcomm) { v1.val.i, v2.val.i, v3.val.i }]]); | |
383 | } | |
f62a369f | 384 | |
4c553c5a | 385 | INST(FI_PATHMASK_CONSTRUCT, 0, 1) { |
c0999a14 | 386 | VARARG; |
4c553c5a | 387 | |
c0999a14 MM |
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; | |
b40c0f02 | 390 | |
c0999a14 MM |
391 | for (uint i=0; i<whati->varcount; i++) { |
392 | switch (vv(i).type) { | |
4c553c5a | 393 | case T_PATH_MASK_ITEM: |
ec430a7f OZ |
394 | if (vv(i).val.pmi.kind == PM_LOOP) |
395 | { | |
396 | if (i == 0) | |
397 | runtime("Path mask iterator '+' cannot be first"); | |
398 | ||
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; | |
402 | break; | |
403 | } | |
404 | ||
c0999a14 | 405 | pm->item[i] = vv(i).val.pmi; |
4c553c5a | 406 | break; |
9f3e0983 | 407 | |
4c553c5a MM |
408 | case T_INT: |
409 | pm->item[i] = (struct f_path_mask_item) { | |
c0999a14 | 410 | .asn = vv(i).val.i, |
4c553c5a MM |
411 | .kind = PM_ASN, |
412 | }; | |
413 | break; | |
9f3e0983 OZ |
414 | |
415 | case T_SET: | |
b2d6d294 | 416 | if (!path_set_type(vv(i).val.t)) |
9f3e0983 OZ |
417 | runtime("Only integer sets allowed in path mask"); |
418 | ||
419 | pm->item[i] = (struct f_path_mask_item) { | |
420 | .set = vv(i).val.t, | |
421 | .kind = PM_ASN_SET, | |
422 | }; | |
423 | break; | |
424 | ||
4c553c5a MM |
425 | default: |
426 | runtime( "Error resolving path mask template: value not an integer" ); | |
427 | } | |
f62a369f | 428 | } |
f62a369f | 429 | |
4c553c5a MM |
430 | RESULT(T_PATH_MASK, path_mask, pm); |
431 | } | |
f62a369f JMM |
432 | |
433 | /* Relational operators */ | |
434 | ||
4c553c5a | 435 | INST(FI_NEQ, 2, 1) { |
c5774939 MM |
436 | ARG_ANY(1); |
437 | ARG_ANY(2); | |
fb1d8f65 | 438 | ARG_PREFER_SAME_TYPE(1, 2); |
4c553c5a | 439 | RESULT(T_BOOL, i, !val_same(&v1, &v2)); |
967b88d9 | 440 | } |
f62a369f | 441 | |
4c553c5a | 442 | INST(FI_EQ, 2, 1) { |
c5774939 MM |
443 | ARG_ANY(1); |
444 | ARG_ANY(2); | |
fb1d8f65 | 445 | ARG_PREFER_SAME_TYPE(1, 2); |
4c553c5a | 446 | RESULT(T_BOOL, i, val_same(&v1, &v2)); |
967b88d9 | 447 | } |
c5774939 | 448 | |
4c553c5a | 449 | INST(FI_LT, 2, 1) { |
c5774939 MM |
450 | ARG_ANY(1); |
451 | ARG_ANY(2); | |
10c4cd96 OZ |
452 | ARG_SAME_TYPE(1, 2); |
453 | ||
4c553c5a | 454 | int i = val_compare(&v1, &v2); |
52893045 | 455 | if (i == F_CMP_ERROR) |
c5774939 | 456 | runtime( "Can't compare values of incompatible types" ); |
4c553c5a | 457 | RESULT(T_BOOL, i, (i == -1)); |
967b88d9 | 458 | } |
f62a369f | 459 | |
4c553c5a | 460 | INST(FI_LTE, 2, 1) { |
c5774939 MM |
461 | ARG_ANY(1); |
462 | ARG_ANY(2); | |
10c4cd96 OZ |
463 | ARG_SAME_TYPE(1, 2); |
464 | ||
4c553c5a | 465 | int i = val_compare(&v1, &v2); |
52893045 | 466 | if (i == F_CMP_ERROR) |
c5774939 | 467 | runtime( "Can't compare values of incompatible types" ); |
4c553c5a | 468 | RESULT(T_BOOL, i, (i != 1)); |
967b88d9 | 469 | } |
f62a369f | 470 | |
4c553c5a MM |
471 | INST(FI_NOT, 1, 1) { |
472 | ARG(1,T_BOOL); | |
473 | RESULT(T_BOOL, i, !v1.val.i); | |
967b88d9 | 474 | } |
f62a369f | 475 | |
4c553c5a | 476 | INST(FI_MATCH, 2, 1) { |
f62a369f JMM |
477 | ARG_ANY(1); |
478 | ARG_ANY(2); | |
4c553c5a | 479 | int i = val_in_range(&v1, &v2); |
52893045 | 480 | if (i == F_CMP_ERROR) |
f62a369f | 481 | runtime( "~ applied on unknown type pair" ); |
4c553c5a | 482 | RESULT(T_BOOL, i, !!i); |
967b88d9 | 483 | } |
f62a369f | 484 | |
4c553c5a | 485 | INST(FI_NOT_MATCH, 2, 1) { |
f62a369f JMM |
486 | ARG_ANY(1); |
487 | ARG_ANY(2); | |
4c553c5a | 488 | int i = val_in_range(&v1, &v2); |
fe503c7c | 489 | if (i == F_CMP_ERROR) |
f62a369f | 490 | runtime( "!~ applied on unknown type pair" ); |
4c553c5a | 491 | RESULT(T_BOOL, i, !i); |
967b88d9 | 492 | } |
f62a369f | 493 | |
4c553c5a | 494 | INST(FI_DEFINED, 1, 1) { |
f62a369f | 495 | ARG_ANY(1); |
4c553c5a | 496 | RESULT(T_BOOL, i, (v1.type != T_VOID) && !undef_value(v1)); |
967b88d9 | 497 | } |
4c553c5a | 498 | |
4cdd6f2e OZ |
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)); | |
f62a369f | 501 | |
cce48c6c | 502 | /* Add initialized variable */ |
1ac8e11b OZ |
503 | INST(FI_VAR_INIT, 1, 0) { |
504 | NEVER_CONSTANT; | |
505 | ARG_ANY(1); | |
506 | SYMBOL; | |
507 | ARG_TYPE(1, sym->class & 0xff); | |
508 | ||
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; | |
513 | } | |
514 | ||
cce48c6c OZ |
515 | /* Add uninitialized variable */ |
516 | INST(FI_VAR_INIT0, 0, 0) { | |
517 | NEVER_CONSTANT; | |
518 | SYMBOL; | |
519 | ||
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; | |
524 | } | |
525 | ||
4c553c5a | 526 | /* Set to indirect value prepared in v1 */ |
96d757c1 | 527 | INST(FI_VAR_SET, 1, 0) { |
b40c0f02 | 528 | NEVER_CONSTANT; |
63f49457 MM |
529 | ARG_ANY(1); |
530 | SYMBOL; | |
26194bd6 | 531 | ARG_TYPE(1, sym->class & 0xff); |
96d757c1 | 532 | |
1757a6fc | 533 | fstk->vstk[curline.vbase + sym->offset] = v1; |
96d757c1 JMM |
534 | } |
535 | ||
536 | INST(FI_VAR_GET, 0, 1) { | |
26bfe59f | 537 | SYMBOL; |
b40c0f02 | 538 | NEVER_CONSTANT; |
6fbcd891 | 539 | RESULT_TYPE(sym->class & 0xff); |
f74d1976 | 540 | RESULT_VAL(fstk->vstk[curline.vbase + sym->offset]); |
967b88d9 | 541 | } |
f62a369f | 542 | |
bfa15a64 | 543 | INST(FI_CONSTANT, 0, 1) { |
30667d50 MM |
544 | FID_MEMBER( |
545 | struct f_val, | |
546 | val, | |
30667d50 | 547 | [[ !val_same(&(f1->val), &(f2->val)) ]], |
f634adc7 | 548 | "value %s", |
30667d50 MM |
549 | val_dump(&(item->val)) |
550 | ); | |
ea4f55e3 | 551 | |
6fbcd891 | 552 | RESULT_TYPE(val.type); |
b40c0f02 | 553 | RESULT_VAL(val); |
967b88d9 | 554 | } |
bfa15a64 | 555 | |
4cdd6f2e OZ |
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); | |
fc9d471b | 560 | |
21faa54e MM |
561 | /* Common loop begin instruction, always created by f_for_cycle() */ |
562 | INST(FI_FOR_LOOP_START, 0, 3) { | |
cb339a30 | 563 | NEVER_CONSTANT; |
cb339a30 OZ |
564 | SYMBOL; |
565 | ||
21faa54e MM |
566 | /* Repeat the instruction which called us */ |
567 | ASSERT_DIE(fstk->ecnt > 1); | |
568 | prevline.pos--; | |
cb339a30 | 569 | |
21faa54e MM |
570 | /* There should be exactly three items on the value stack to be taken care of */ |
571 | fstk->vcnt += 3; | |
cb339a30 | 572 | |
21faa54e MM |
573 | /* And these should also stay there after we finish for the caller instruction */ |
574 | curline.ventry += 3; | |
cb339a30 | 575 | |
21faa54e MM |
576 | /* Assert the iterator variable positioning */ |
577 | ASSERT_DIE(curline.vbase + sym->offset == fstk->vcnt - 1); | |
cb339a30 | 578 | |
21faa54e MM |
579 | /* The result type declaration makes no sense here but is needed */ |
580 | RESULT_TYPE(T_VOID); | |
cb339a30 OZ |
581 | } |
582 | ||
21faa54e MM |
583 | /* Type-specific for_next iterators */ |
584 | INST(FI_PATH_FOR_NEXT, 3, 0) { | |
cb339a30 | 585 | NEVER_CONSTANT; |
21faa54e MM |
586 | ARG(1, T_PATH); |
587 | if (as_path_walk(v1.val.ad, &v2.val.i, &v3.val.i)) | |
588 | LINE(2,0); | |
cb339a30 | 589 | |
21faa54e MM |
590 | METHOD_CONSTRUCTOR("!for_next"); |
591 | } | |
cb339a30 | 592 | |
21faa54e MM |
593 | INST(FI_CLIST_FOR_NEXT, 3, 0) { |
594 | NEVER_CONSTANT; | |
595 | ARG(1, T_CLIST); | |
596 | if (int_set_walk(v1.val.ad, &v2.val.i, &v3.val.i)) | |
597 | LINE(2,0); | |
cb339a30 | 598 | |
21faa54e MM |
599 | METHOD_CONSTRUCTOR("!for_next"); |
600 | } | |
cb339a30 | 601 | |
21faa54e MM |
602 | INST(FI_ECLIST_FOR_NEXT, 3, 0) { |
603 | NEVER_CONSTANT; | |
604 | ARG(1, T_ECLIST); | |
605 | if (ec_set_walk(v1.val.ad, &v2.val.i, &v3.val.ec)) | |
606 | LINE(2,0); | |
cb339a30 | 607 | |
21faa54e MM |
608 | METHOD_CONSTRUCTOR("!for_next"); |
609 | } | |
cb339a30 | 610 | |
21faa54e MM |
611 | INST(FI_LCLIST_FOR_NEXT, 3, 0) { |
612 | NEVER_CONSTANT; | |
613 | ARG(1, T_LCLIST); | |
614 | if (lc_set_walk(v1.val.ad, &v2.val.i, &v3.val.lc)) | |
615 | LINE(2,0); | |
cb339a30 | 616 | |
21faa54e | 617 | METHOD_CONSTRUCTOR("!for_next"); |
cb339a30 OZ |
618 | } |
619 | ||
4c553c5a MM |
620 | INST(FI_CONDITION, 1, 0) { |
621 | ARG(1, T_BOOL); | |
b40c0f02 | 622 | if (v1.val.i) |
4c553c5a | 623 | LINE(2,0); |
224b77d4 | 624 | else |
a2527ee5 | 625 | LINE(3,0); |
967b88d9 | 626 | } |
9b46748d | 627 | |
fdd39c81 | 628 | INST(FI_PRINT, 1, 0) { |
0206c070 | 629 | NEVER_CONSTANT; |
fdd39c81 | 630 | ARG_ANY(1); |
0206c070 | 631 | |
fdd39c81 MM |
632 | if (!(fs->flags & FF_SILENT)) |
633 | val_format(&v1, &fs->buf); | |
0206c070 MM |
634 | } |
635 | ||
efd7c87b MM |
636 | INST(FI_FLUSH, 0, 0) { |
637 | NEVER_CONSTANT; | |
638 | if (!(fs->flags & FF_SILENT)) | |
639 | /* After log_commit, the buffer is reset */ | |
640 | log_commit(*L_INFO, &fs->buf); | |
641 | } | |
642 | ||
0206c070 MM |
643 | INST(FI_DIE, 0, 0) { |
644 | NEVER_CONSTANT; | |
f634adc7 | 645 | FID_MEMBER(enum filter_return, fret, f1->fret != f2->fret, "%s", filter_return_str(item->fret)); |
4c553c5a | 646 | |
0206c070 | 647 | switch (whati->fret) { |
efd7c87b | 648 | case F_ACCEPT: /* Should take care about turning ACCEPT into MODIFY */ |
f62a369f | 649 | case F_ERROR: |
efd7c87b | 650 | case F_REJECT: /* Maybe print complete route along with reason to reject route? */ |
4c553c5a | 651 | return fret; /* We have to return now, no more processing. */ |
f62a369f JMM |
652 | default: |
653 | bug( "unknown return type: Can't happen"); | |
654 | } | |
967b88d9 | 655 | } |
bfa15a64 OZ |
656 | |
657 | INST(FI_RTA_GET, 0, 1) { | |
f62a369f | 658 | { |
4c553c5a | 659 | STATIC_ATTR; |
f62a369f JMM |
660 | ACCESS_RTE; |
661 | struct rta *rta = (*fs->rte)->attrs; | |
f62a369f | 662 | |
4c553c5a | 663 | switch (sa.sa_code) |
f62a369f | 664 | { |
4c553c5a MM |
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; | |
5cff1d5f | 668 | case SA_PROTO: RESULT(sa.f_type, s, (*fs->rte)->src->proto->name); break; |
4c553c5a MM |
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; | |
8cc5bb09 | 674 | case SA_WEIGHT: RESULT(sa.f_type, i, rta->nh.weight + 1); break; |
eb937358 | 675 | case SA_PREF: RESULT(sa.f_type, i, rta->pref); break; |
e5468d16 | 676 | case SA_GW_MPLS: RESULT(sa.f_type, i, rta->nh.labels ? rta->nh.label[0] : MPLS_NULL); break; |
7144c9ca | 677 | case SA_ONLINK: RESULT(sa.f_type, i, rta->nh.flags & RNF_ONLINK ? 1 : 0); break; |
f62a369f JMM |
678 | |
679 | default: | |
4c553c5a | 680 | bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code); |
f62a369f JMM |
681 | } |
682 | } | |
967b88d9 | 683 | } |
4c553c5a | 684 | |
a84b8b6e MM |
685 | INST(FI_RTA_SET, 1, 0) { |
686 | ACCESS_RTE; | |
687 | ARG_ANY(1); | |
688 | STATIC_ATTR; | |
26194bd6 | 689 | ARG_TYPE(1, sa.f_type); |
a84b8b6e MM |
690 | |
691 | f_rta_cow(fs); | |
692 | { | |
693 | struct rta *rta = (*fs->rte)->attrs; | |
694 | ||
695 | switch (sa.sa_code) | |
696 | { | |
697 | case SA_FROM: | |
698 | rta->from = v1.val.ip; | |
699 | break; | |
700 | ||
701 | case SA_GW: | |
702 | { | |
703 | ip_addr ip = v1.val.ip; | |
7144c9ca RC |
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); | |
a84b8b6e MM |
706 | if (!n || (n->scope == SCOPE_HOST)) |
707 | runtime( "Invalid gw address" ); | |
708 | ||
709 | rta->dest = RTD_UNICAST; | |
710 | rta->nh.gw = ip; | |
711 | rta->nh.iface = n->iface; | |
712 | rta->nh.next = NULL; | |
713 | rta->hostentry = NULL; | |
e5468d16 | 714 | rta->nh.labels = 0; |
a84b8b6e MM |
715 | } |
716 | break; | |
717 | ||
718 | case SA_SCOPE: | |
719 | rta->scope = v1.val.i; | |
720 | break; | |
721 | ||
722 | case SA_DEST: | |
723 | { | |
724 | int i = 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" ); | |
727 | ||
728 | rta->dest = i; | |
729 | rta->nh.gw = IPA_NONE; | |
730 | rta->nh.iface = NULL; | |
731 | rta->nh.next = NULL; | |
732 | rta->hostentry = NULL; | |
e5468d16 | 733 | rta->nh.labels = 0; |
a84b8b6e MM |
734 | } |
735 | break; | |
736 | ||
737 | case SA_IFNAME: | |
738 | { | |
739 | struct iface *ifa = if_find_by_name(v1.val.s); | |
740 | if (!ifa) | |
741 | runtime( "Invalid iface name" ); | |
742 | ||
743 | rta->dest = RTD_UNICAST; | |
744 | rta->nh.gw = IPA_NONE; | |
745 | rta->nh.iface = ifa; | |
746 | rta->nh.next = NULL; | |
747 | rta->hostentry = NULL; | |
e5468d16 TB |
748 | rta->nh.labels = 0; |
749 | } | |
750 | break; | |
751 | ||
752 | case SA_GW_MPLS: | |
753 | { | |
754 | if (v1.val.i >= 0x100000) | |
755 | runtime( "Invalid MPLS label" ); | |
756 | ||
757 | if (v1.val.i != MPLS_NULL) | |
758 | { | |
759 | rta->nh.label[0] = v1.val.i; | |
760 | rta->nh.labels = 1; | |
761 | } | |
762 | else | |
763 | rta->nh.labels = 0; | |
a84b8b6e MM |
764 | } |
765 | break; | |
766 | ||
8cc5bb09 OZ |
767 | case SA_WEIGHT: |
768 | { | |
769 | int i = 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 " ); | |
774 | ||
775 | /* Set weight on all next hops */ | |
776 | for (struct nexthop *nh = &rta->nh; nh; nh = nh->next) | |
777 | nh->weight = i - 1; | |
778 | } | |
779 | break; | |
780 | ||
eb937358 MM |
781 | case SA_PREF: |
782 | rta->pref = v1.val.i; | |
783 | break; | |
784 | ||
7144c9ca RC |
785 | case SA_ONLINK: |
786 | { | |
787 | if (v1.val.i) | |
788 | rta->nh.flags |= RNF_ONLINK; | |
789 | else | |
790 | rta->nh.flags &= ~RNF_ONLINK; | |
791 | } | |
792 | break; | |
793 | ||
a84b8b6e MM |
794 | default: |
795 | bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code); | |
796 | } | |
797 | } | |
798 | } | |
799 | ||
4c553c5a MM |
800 | INST(FI_EA_GET, 0, 1) { /* Access to extended attributes */ |
801 | DYNAMIC_ATTR; | |
f62a369f JMM |
802 | ACCESS_RTE; |
803 | ACCESS_EATTRS; | |
6fbcd891 | 804 | RESULT_TYPE(da.f_type); |
f62a369f | 805 | { |
4c553c5a | 806 | eattr *e = ea_find(*fs->eattrs, da.ea_code); |
f62a369f JMM |
807 | |
808 | if (!e) { | |
809 | /* A special case: undefined as_path looks like empty as_path */ | |
4c553c5a | 810 | if (da.type == EAF_TYPE_AS_PATH) { |
6fbcd891 | 811 | RESULT_(T_PATH, ad, &null_adata); |
f62a369f JMM |
812 | break; |
813 | } | |
814 | ||
815 | /* The same special case for int_set */ | |
4c553c5a | 816 | if (da.type == EAF_TYPE_INT_SET) { |
6fbcd891 | 817 | RESULT_(T_CLIST, ad, &null_adata); |
f62a369f JMM |
818 | break; |
819 | } | |
820 | ||
821 | /* The same special case for ec_set */ | |
4c553c5a | 822 | if (da.type == EAF_TYPE_EC_SET) { |
6fbcd891 | 823 | RESULT_(T_ECLIST, ad, &null_adata); |
f62a369f JMM |
824 | break; |
825 | } | |
826 | ||
827 | /* The same special case for lc_set */ | |
4c553c5a | 828 | if (da.type == EAF_TYPE_LC_SET) { |
6fbcd891 | 829 | RESULT_(T_LCLIST, ad, &null_adata); |
f62a369f JMM |
830 | break; |
831 | } | |
832 | ||
833 | /* Undefined value */ | |
f74d1976 | 834 | RESULT_VOID; |
f62a369f JMM |
835 | break; |
836 | } | |
837 | ||
838 | switch (e->type & EAF_TYPE_MASK) { | |
839 | case EAF_TYPE_INT: | |
6fbcd891 | 840 | RESULT_(da.f_type, i, e->u.data); |
f62a369f JMM |
841 | break; |
842 | case EAF_TYPE_ROUTER_ID: | |
6fbcd891 | 843 | RESULT_(T_QUAD, i, e->u.data); |
f62a369f JMM |
844 | break; |
845 | case EAF_TYPE_OPAQUE: | |
6fbcd891 | 846 | RESULT_(T_ENUM_EMPTY, i, 0); |
f62a369f JMM |
847 | break; |
848 | case EAF_TYPE_IP_ADDRESS: | |
6fbcd891 | 849 | RESULT_(T_IP, ip, *((ip_addr *) e->u.ptr->data)); |
f62a369f JMM |
850 | break; |
851 | case EAF_TYPE_AS_PATH: | |
6fbcd891 | 852 | RESULT_(T_PATH, ad, e->u.ptr); |
f62a369f JMM |
853 | break; |
854 | case EAF_TYPE_BITFIELD: | |
6fbcd891 | 855 | RESULT_(T_BOOL, i, !!(e->u.data & (1u << da.bit))); |
f62a369f JMM |
856 | break; |
857 | case EAF_TYPE_INT_SET: | |
6fbcd891 | 858 | RESULT_(T_CLIST, ad, e->u.ptr); |
f62a369f JMM |
859 | break; |
860 | case EAF_TYPE_EC_SET: | |
6fbcd891 | 861 | RESULT_(T_ECLIST, ad, e->u.ptr); |
f62a369f JMM |
862 | break; |
863 | case EAF_TYPE_LC_SET: | |
6fbcd891 | 864 | RESULT_(T_LCLIST, ad, e->u.ptr); |
f62a369f | 865 | break; |
f62a369f | 866 | default: |
4c553c5a | 867 | bug("Unknown dynamic attribute type"); |
f62a369f JMM |
868 | } |
869 | } | |
967b88d9 | 870 | } |
4c553c5a | 871 | |
a84b8b6e MM |
872 | INST(FI_EA_SET, 1, 0) { |
873 | ACCESS_RTE; | |
874 | ACCESS_EATTRS; | |
875 | ARG_ANY(1); | |
876 | DYNAMIC_ATTR; | |
26194bd6 | 877 | ARG_TYPE(1, da.f_type); |
a84b8b6e MM |
878 | { |
879 | struct ea_list *l = lp_alloc(fs->pool, sizeof(struct ea_list) + sizeof(eattr)); | |
880 | ||
881 | l->next = NULL; | |
882 | l->flags = EALF_SORTED; | |
883 | l->count = 1; | |
884 | l->attrs[0].id = da.ea_code; | |
885 | l->attrs[0].flags = 0; | |
63cf5d5d MM |
886 | l->attrs[0].type = da.type; |
887 | l->attrs[0].originated = 1; | |
888 | l->attrs[0].fresh = 1; | |
5299fb9d | 889 | l->attrs[0].undef = 0; |
a84b8b6e MM |
890 | |
891 | switch (da.type) { | |
892 | case EAF_TYPE_INT: | |
a84b8b6e | 893 | case EAF_TYPE_ROUTER_ID: |
a84b8b6e MM |
894 | l->attrs[0].u.data = v1.val.i; |
895 | break; | |
896 | ||
897 | case EAF_TYPE_OPAQUE: | |
898 | runtime( "Setting opaque attribute is not allowed" ); | |
899 | break; | |
bfa15a64 | 900 | |
26194bd6 | 901 | case EAF_TYPE_IP_ADDRESS:; |
a84b8b6e MM |
902 | int len = sizeof(ip_addr); |
903 | struct adata *ad = lp_alloc(fs->pool, sizeof(struct adata) + len); | |
904 | ad->length = len; | |
905 | (* (ip_addr *) ad->data) = v1.val.ip; | |
906 | l->attrs[0].u.ptr = ad; | |
907 | break; | |
bfa15a64 | 908 | |
a84b8b6e | 909 | case EAF_TYPE_AS_PATH: |
26194bd6 OZ |
910 | case EAF_TYPE_INT_SET: |
911 | case EAF_TYPE_EC_SET: | |
912 | case EAF_TYPE_LC_SET: | |
a84b8b6e MM |
913 | l->attrs[0].u.ptr = v1.val.ad; |
914 | break; | |
bfa15a64 | 915 | |
a84b8b6e | 916 | case EAF_TYPE_BITFIELD: |
a84b8b6e MM |
917 | { |
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; | |
921 | ||
922 | if (v1.val.i) | |
923 | l->attrs[0].u.data = data | (1u << da.bit); | |
924 | else | |
925 | l->attrs[0].u.data = data & ~(1u << da.bit); | |
926 | } | |
927 | break; | |
bfa15a64 | 928 | |
bfa15a64 OZ |
929 | default: |
930 | bug("Unknown dynamic attribute type"); | |
a84b8b6e MM |
931 | } |
932 | ||
933 | f_rta_cow(fs); | |
934 | l->next = *fs->eattrs; | |
935 | *fs->eattrs = l; | |
936 | } | |
937 | } | |
938 | ||
9b46748d MM |
939 | INST(FI_EA_UNSET, 0, 0) { |
940 | DYNAMIC_ATTR; | |
941 | ACCESS_RTE; | |
942 | ACCESS_EATTRS; | |
943 | ||
0f685152 MM |
944 | f_rta_cow(fs); |
945 | ea_unset_attr(fs->eattrs, fs->pool, 1, da.ea_code); | |
9b46748d MM |
946 | } |
947 | ||
6d411fc7 | 948 | /* Get length of */ |
4cdd6f2e OZ |
949 | METHOD_R(T_NET, len, T_INT, i, net_pxlen(v1.val.net)); |
950 | METHOD_R(T_PATH, len, T_INT, i, as_path_getlen(v1.val.ad)); | |
951 | METHOD_R(T_CLIST, len, T_INT, i, int_set_get_size(v1.val.ad)); | |
952 | METHOD_R(T_ECLIST, len, T_INT, i, ec_set_get_size(v1.val.ad)); | |
953 | METHOD_R(T_LCLIST, len, T_INT, i, lc_set_get_size(v1.val.ad)); | |
4c553c5a | 954 | |
ff2ca10c | 955 | INST(FI_NET_SRC, 1, 1) { /* Get src prefix */ |
f62a369f | 956 | ARG(1, T_NET); |
fc9d471b | 957 | METHOD_CONSTRUCTOR("src"); |
f62a369f | 958 | |
ff2ca10c | 959 | net_addr_union *net = (void *) v1.val.net; |
b40c0f02 | 960 | net_addr *src = falloc(sizeof(net_addr_ip6)); |
ff2ca10c OZ |
961 | const byte *part; |
962 | ||
963 | switch(v1.val.net->type) { | |
964 | case NET_FLOW4: | |
965 | part = flow4_get_part(&net->flow4, FLOW_TYPE_SRC_PREFIX); | |
966 | if (part) | |
967 | net_fill_ip4(src, flow_read_ip4_part(part), flow_read_pxlen(part)); | |
968 | else | |
969 | net_fill_ip4(src, IP4_NONE, 0); | |
970 | break; | |
971 | ||
972 | case NET_FLOW6: | |
973 | part = flow6_get_part(&net->flow6, FLOW_TYPE_SRC_PREFIX); | |
974 | if (part) | |
975 | net_fill_ip6(src, flow_read_ip6_part(part), flow_read_pxlen(part)); | |
976 | else | |
977 | net_fill_ip6(src, IP6_NONE, 0); | |
978 | break; | |
979 | ||
980 | case NET_IP6_SADR: | |
981 | net_fill_ip6(src, net->ip6_sadr.src_prefix, net->ip6_sadr.src_pxlen); | |
982 | break; | |
983 | ||
984 | default: | |
985 | runtime( "Flow or SADR expected" ); | |
986 | } | |
f62a369f | 987 | |
4c553c5a | 988 | RESULT(T_NET, net, src); |
967b88d9 | 989 | } |
4c553c5a | 990 | |
ff2ca10c OZ |
991 | INST(FI_NET_DST, 1, 1) { /* Get dst prefix */ |
992 | ARG(1, T_NET); | |
fc9d471b | 993 | METHOD_CONSTRUCTOR("dst"); |
ff2ca10c OZ |
994 | |
995 | net_addr_union *net = (void *) v1.val.net; | |
996 | net_addr *dst = falloc(sizeof(net_addr_ip6)); | |
997 | const byte *part; | |
998 | ||
999 | switch(v1.val.net->type) { | |
1000 | case NET_FLOW4: | |
1001 | part = flow4_get_part(&net->flow4, FLOW_TYPE_DST_PREFIX); | |
1002 | if (part) | |
1003 | net_fill_ip4(dst, flow_read_ip4_part(part), flow_read_pxlen(part)); | |
1004 | else | |
1005 | net_fill_ip4(dst, IP4_NONE, 0); | |
1006 | break; | |
1007 | ||
1008 | case NET_FLOW6: | |
1009 | part = flow6_get_part(&net->flow6, FLOW_TYPE_DST_PREFIX); | |
1010 | if (part) | |
1011 | net_fill_ip6(dst, flow_read_ip6_part(part), flow_read_pxlen(part)); | |
1012 | else | |
1013 | net_fill_ip6(dst, IP6_NONE, 0); | |
1014 | break; | |
1015 | ||
1016 | case NET_IP6_SADR: | |
1017 | net_fill_ip6(dst, net->ip6_sadr.dst_prefix, net->ip6_sadr.dst_pxlen); | |
1018 | break; | |
1019 | ||
1020 | default: | |
1021 | runtime( "Flow or SADR expected" ); | |
1022 | } | |
1023 | ||
1024 | RESULT(T_NET, net, dst); | |
1025 | } | |
1026 | ||
6d411fc7 MM |
1027 | /* Get ROA max prefix length */ |
1028 | METHOD(T_NET, maxlen, 0, [[ | |
f62a369f JMM |
1029 | if (!net_is_roa(v1.val.net)) |
1030 | runtime( "ROA expected" ); | |
1031 | ||
4c553c5a | 1032 | RESULT(T_INT, i, (v1.val.net->type == NET_ROA4) ? |
f62a369f | 1033 | ((net_addr_roa4 *) v1.val.net)->max_pxlen : |
4c553c5a | 1034 | ((net_addr_roa6 *) v1.val.net)->max_pxlen); |
6d411fc7 | 1035 | ]]); |
4c553c5a | 1036 | |
6d411fc7 | 1037 | /* Get ROA ASN or community ASN part */ |
4cdd6f2e OZ |
1038 | METHOD_R(T_PAIR, asn, T_INT, i, v1.val.i >> 16); |
1039 | METHOD_R(T_LC, asn, T_INT, i, v1.val.lc.asn); | |
6d411fc7 MM |
1040 | |
1041 | METHOD(T_NET, asn, 0, [[ | |
a2a268da AZ |
1042 | if (!net_is_roa(v1.val.net)) |
1043 | runtime( "ROA expected" ); | |
f62a369f | 1044 | |
fc9d471b | 1045 | RESULT(T_INT, i, (v1.val.net->type == NET_ROA4) ? |
a2a268da AZ |
1046 | ((net_addr_roa4 *) v1.val.net)->asn : |
1047 | ((net_addr_roa6 *) v1.val.net)->asn); | |
6d411fc7 | 1048 | ]]); |
a2a268da | 1049 | |
4c553c5a | 1050 | |
6d411fc7 | 1051 | /* Convert prefix to IP */ |
4cdd6f2e | 1052 | METHOD_R(T_NET, ip, T_IP, ip, net_prefix(v1.val.net)); |
4c553c5a MM |
1053 | |
1054 | INST(FI_ROUTE_DISTINGUISHER, 1, 1) { | |
f62a369f | 1055 | ARG(1, T_NET); |
fc9d471b | 1056 | METHOD_CONSTRUCTOR("rd"); |
f62a369f JMM |
1057 | if (!net_is_vpn(v1.val.net)) |
1058 | runtime( "VPN address expected" ); | |
4c553c5a | 1059 | RESULT(T_RD, ec, net_rd(v1.val.net)); |
967b88d9 | 1060 | } |
f62a369f | 1061 | |
4c553c5a MM |
1062 | INST(FI_AS_PATH_FIRST, 1, 1) { /* Get first ASN from AS PATH */ |
1063 | ARG(1, T_PATH); | |
fc9d471b | 1064 | METHOD_CONSTRUCTOR("first"); |
10c4cd96 | 1065 | u32 as = 0; |
f62a369f | 1066 | as_path_get_first(v1.val.ad, &as); |
4c553c5a | 1067 | RESULT(T_INT, i, as); |
967b88d9 | 1068 | } |
f62a369f | 1069 | |
bfa15a64 | 1070 | INST(FI_AS_PATH_LAST, 1, 1) { /* Get last ASN from AS PATH */ |
4c553c5a | 1071 | ARG(1, T_PATH); |
fc9d471b | 1072 | METHOD_CONSTRUCTOR("last"); |
10c4cd96 | 1073 | u32 as = 0; |
f62a369f | 1074 | as_path_get_last(v1.val.ad, &as); |
4c553c5a | 1075 | RESULT(T_INT, i, as); |
967b88d9 | 1076 | } |
4c553c5a | 1077 | |
6d411fc7 | 1078 | /* Get last ASN from non-aggregated part of AS PATH */ |
4cdd6f2e | 1079 | METHOD_R(T_PATH, last_nonaggregated, T_INT, i, as_path_get_last_nonaggregated(v1.val.ad)); |
f62a369f | 1080 | |
6d411fc7 | 1081 | /* Get data part from the standard community */ |
4cdd6f2e | 1082 | METHOD_R(T_PAIR, data, T_INT, i, v1.val.i & 0xFFFF); |
a2a268da | 1083 | |
6d411fc7 | 1084 | /* Get data1 part from the large community */ |
4cdd6f2e | 1085 | METHOD_R(T_LC, data1, T_INT, i, v1.val.lc.ldp1); |
a2a268da | 1086 | |
6d411fc7 | 1087 | /* Get data2 part from the large community */ |
4cdd6f2e | 1088 | METHOD_R(T_LC, data2, T_INT, i, v1.val.lc.ldp2); |
a2a268da | 1089 | |
fc9d471b MM |
1090 | INST(FI_CLIST_MIN, 1, 1) { /* Get minimum element from list */ |
1091 | ARG(1, T_CLIST); | |
1092 | METHOD_CONSTRUCTOR("min"); | |
1093 | u32 val = 0; | |
1094 | int_set_min(v1.val.ad, &val); | |
1095 | RESULT(T_PAIR, i, val); | |
1096 | } | |
0e1fd7ea | 1097 | |
fc9d471b MM |
1098 | INST(FI_CLIST_MAX, 1, 1) { /* Get minimum element from list */ |
1099 | ARG(1, T_CLIST); | |
1100 | METHOD_CONSTRUCTOR("max"); | |
1101 | u32 val = 0; | |
1102 | int_set_max(v1.val.ad, &val); | |
1103 | RESULT(T_PAIR, i, val); | |
0e1fd7ea AZ |
1104 | } |
1105 | ||
fc9d471b MM |
1106 | INST(FI_ECLIST_MIN, 1, 1) { /* Get minimum element from list */ |
1107 | ARG(1, T_ECLIST); | |
1108 | METHOD_CONSTRUCTOR("min"); | |
1109 | u64 val = 0; | |
1110 | ec_set_min(v1.val.ad, &val); | |
1111 | RESULT(T_EC, ec, val); | |
1112 | } | |
0e1fd7ea | 1113 | |
fc9d471b MM |
1114 | INST(FI_ECLIST_MAX, 1, 1) { /* Get minimum element from list */ |
1115 | ARG(1, T_ECLIST); | |
1116 | METHOD_CONSTRUCTOR("max"); | |
1117 | u64 val = 0; | |
1118 | ec_set_max(v1.val.ad, &val); | |
1119 | RESULT(T_EC, ec, val); | |
1120 | } | |
0e1fd7ea | 1121 | |
fc9d471b MM |
1122 | INST(FI_LCLIST_MIN, 1, 1) { /* Get minimum element from list */ |
1123 | ARG(1, T_LCLIST); | |
1124 | METHOD_CONSTRUCTOR("min"); | |
1125 | lcomm val = {}; | |
1126 | lc_set_min(v1.val.ad, &val); | |
1127 | RESULT(T_LC, lc, val); | |
1128 | } | |
0e1fd7ea | 1129 | |
fc9d471b MM |
1130 | INST(FI_LCLIST_MAX, 1, 1) { /* Get minimum element from list */ |
1131 | ARG(1, T_LCLIST); | |
1132 | METHOD_CONSTRUCTOR("max"); | |
1133 | lcomm val = {}; | |
1134 | lc_set_max(v1.val.ad, &val); | |
1135 | RESULT(T_LC, lc, val); | |
0e1fd7ea AZ |
1136 | } |
1137 | ||
9b302c13 | 1138 | INST(FI_RETURN, 1, 0) { |
b40c0f02 | 1139 | NEVER_CONSTANT; |
a84b8b6e MM |
1140 | /* Acquire the return value */ |
1141 | ARG_ANY(1); | |
1142 | uint retpos = fstk->vcnt; | |
1143 | ||
1144 | /* Drop every sub-block including ourselves */ | |
9e263550 OZ |
1145 | do fstk->ecnt--; |
1146 | while ((fstk->ecnt > 0) && !(fstk->estk[fstk->ecnt].emask & FE_RETURN)); | |
a84b8b6e MM |
1147 | |
1148 | /* Now we are at the caller frame; if no such, try to convert to accept/reject. */ | |
1149 | if (!fstk->ecnt) | |
9e263550 | 1150 | { |
a84b8b6e | 1151 | if (fstk->vstk[retpos].type == T_BOOL) |
9e263550 | 1152 | return (fstk->vstk[retpos].val.i) ? F_ACCEPT : F_REJECT; |
a84b8b6e MM |
1153 | else |
1154 | runtime("Can't return non-bool from non-function"); | |
9e263550 | 1155 | } |
a84b8b6e MM |
1156 | |
1157 | /* Set the value stack position, overwriting the former implicit void */ | |
1158 | fstk->vcnt = fstk->estk[fstk->ecnt].ventry - 1; | |
1159 | ||
1160 | /* Copy the return value */ | |
1161 | RESULT_VAL(fstk->vstk[retpos]); | |
1162 | } | |
1163 | ||
4c553c5a | 1164 | INST(FI_CALL, 0, 1) { |
b40c0f02 | 1165 | NEVER_CONSTANT; |
4c0c507b | 1166 | VARARG; |
96d757c1 | 1167 | SYMBOL; |
4f082dfa | 1168 | |
9b302c13 | 1169 | /* Fake result type declaration */ |
062ff656 | 1170 | RESULT_TYPE(sym->function->return_type); |
9b302c13 | 1171 | |
4c0c507b OZ |
1172 | FID_NEW_BODY() |
1173 | ASSERT(sym->class == SYM_FUNCTION); | |
1174 | ||
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); | |
1178 | ||
93d6096c OZ |
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++) | |
1183 | { | |
1184 | enum f_type b_type = b->arg->class & 0xff; | |
1185 | ||
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)); | |
1189 | } | |
1190 | ASSERT(!a && !b); | |
1191 | ||
4c0c507b OZ |
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; | |
1195 | whati->fvar = tmp; | |
1196 | what->size += tmp->size; | |
1197 | ||
26bc4f99 OZ |
1198 | /* Mark recursive calls, they have dummy f_line */ |
1199 | if (!sym->function->len) | |
1200 | what->flags |= FIF_RECURSIVE; | |
1201 | ||
3f477ccb | 1202 | FID_SAME_BODY() |
26bc4f99 OZ |
1203 | if (!(f1->sym->flags & SYM_FLAG_SAME) && !(f1_->flags & FIF_RECURSIVE)) |
1204 | return 0; | |
d06a875b OZ |
1205 | |
1206 | FID_ITERATE_BODY() | |
26bc4f99 | 1207 | if (!(what->flags & FIF_RECURSIVE)) |
d06a875b OZ |
1208 | BUFFER_PUSH(fit->lines) = whati->sym->function; |
1209 | ||
3f477ccb MM |
1210 | FID_INTERPRET_BODY() |
1211 | ||
96d757c1 JMM |
1212 | /* Push the body on stack */ |
1213 | LINEX(sym->function); | |
4c0c507b | 1214 | curline.vbase = curline.ventry; |
ea4f55e3 | 1215 | curline.emask |= FE_RETURN; |
bfa15a64 | 1216 | |
4c0c507b OZ |
1217 | /* Arguments on stack */ |
1218 | fstk->vcnt += sym->function->args; | |
96d757c1 JMM |
1219 | |
1220 | /* Storage for local variables */ | |
1757a6fc MM |
1221 | memset(&(fstk->vstk[fstk->vcnt]), 0, sizeof(struct f_val) * sym->function->vars); |
1222 | fstk->vcnt += sym->function->vars; | |
967b88d9 | 1223 | } |
4c553c5a MM |
1224 | |
1225 | INST(FI_DROP_RESULT, 1, 0) { | |
b40c0f02 | 1226 | NEVER_CONSTANT; |
4c553c5a | 1227 | ARG_ANY(1); |
967b88d9 | 1228 | } |
4c553c5a | 1229 | |
4c553c5a | 1230 | INST(FI_SWITCH, 1, 0) { |
f62a369f | 1231 | ARG_ANY(1); |
26bfe59f | 1232 | |
f634adc7 | 1233 | FID_MEMBER(struct f_tree *, tree, [[!same_tree(f1->tree, f2->tree)]], "tree %p", item->tree); |
26bfe59f | 1234 | |
e20bef69 OZ |
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) | |
1240 | { | |
1241 | if (t->data != last_inst) | |
1242 | { | |
1243 | last_inst = t->data; | |
1244 | last_line = f_linearize(t->data, 0); | |
1245 | } | |
1246 | ||
1247 | t->data = last_line; | |
1248 | } | |
1249 | ||
1250 | /* Balance the tree */ | |
1251 | item->tree = build_tree(whati->tree); | |
1252 | ||
d06a875b | 1253 | FID_ITERATE_BODY() |
e20bef69 | 1254 | tree_walk(whati->tree, f_add_tree_lines, fit); |
d06a875b OZ |
1255 | |
1256 | FID_INTERPRET_BODY() | |
e20bef69 OZ |
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); | |
32793ab6 | 1259 | if (!t) { |
4c553c5a | 1260 | v1.type = T_VOID; |
e20bef69 | 1261 | t = FID_HIC(,find_tree,find_tree_linear)(tree, &v1); |
32793ab6 | 1262 | if (!t) { |
4c553c5a | 1263 | debug( "No else statement?\n"); |
b40c0f02 | 1264 | FID_HIC(,break,return NULL); |
f62a369f | 1265 | } |
f62a369f | 1266 | } |
4c553c5a | 1267 | |
32793ab6 | 1268 | LINEX(t->data); |
967b88d9 | 1269 | } |
4c553c5a MM |
1270 | |
1271 | INST(FI_IP_MASK, 2, 1) { /* IP.MASK(val) */ | |
f62a369f JMM |
1272 | ARG(1, T_IP); |
1273 | ARG(2, T_INT); | |
fc9d471b | 1274 | METHOD_CONSTRUCTOR("mask"); |
4c553c5a | 1275 | RESULT(T_IP, ip, [[ ipa_is_ip4(v1.val.ip) ? |
f62a369f | 1276 | ipa_from_ip4(ip4_and(ipa_to_ip4(v1.val.ip), ip4_mkmask(v2.val.i))) : |
4c553c5a | 1277 | ipa_from_ip6(ip6_and(ipa_to_ip6(v1.val.ip), ip6_mkmask(v2.val.i))) ]]); |
967b88d9 | 1278 | } |
f62a369f | 1279 | |
4c553c5a | 1280 | INST(FI_PATH_PREPEND, 2, 1) { /* Path prepend */ |
f62a369f JMM |
1281 | ARG(1, T_PATH); |
1282 | ARG(2, T_INT); | |
fc9d471b | 1283 | METHOD_CONSTRUCTOR("prepend"); |
b40c0f02 | 1284 | RESULT(T_PATH, ad, [[ as_path_prepend(fpool, v1.val.ad, v2.val.i) ]]); |
4c553c5a MM |
1285 | } |
1286 | ||
1287 | INST(FI_CLIST_ADD, 2, 1) { /* (Extended) Community list add */ | |
fc9d471b | 1288 | ARG(1, T_CLIST); |
4c553c5a | 1289 | ARG_ANY(2); |
fc9d471b | 1290 | METHOD_CONSTRUCTOR("add"); |
4c553c5a | 1291 | struct f_val dummy; |
4c553c5a | 1292 | if ((v2.type == T_PAIR) || (v2.type == T_QUAD)) |
fc9d471b | 1293 | RESULT(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, v2.val.i) ]]); |
4c553c5a MM |
1294 | /* IP->Quad implicit conversion */ |
1295 | else if (val_is_ip4(&v2)) | |
fc9d471b | 1296 | RESULT(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]); |
4c553c5a MM |
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) | |
fc9d471b | 1300 | RESULT(T_CLIST, ad, [[ int_set_union(fpool, v1.val.ad, v2.val.ad) ]]); |
4c553c5a MM |
1301 | else |
1302 | runtime("Can't add non-pair"); | |
fc9d471b | 1303 | } |
f62a369f | 1304 | |
fc9d471b MM |
1305 | INST(FI_ECLIST_ADD, 2, 1) { |
1306 | ARG(1, T_ECLIST); | |
1307 | ARG_ANY(2); | |
1308 | METHOD_CONSTRUCTOR("add"); | |
4c553c5a MM |
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) | |
fc9d471b | 1313 | RESULT(T_ECLIST, ad, [[ ec_set_union(fpool, v1.val.ad, v2.val.ad) ]]); |
4c553c5a MM |
1314 | else if (v2.type != T_EC) |
1315 | runtime("Can't add non-ec"); | |
1316 | else | |
fc9d471b MM |
1317 | RESULT(T_ECLIST, ad, [[ ec_set_add(fpool, v1.val.ad, v2.val.ec) ]]); |
1318 | } | |
4c553c5a | 1319 | |
fc9d471b MM |
1320 | INST(FI_LCLIST_ADD, 2, 1) { |
1321 | ARG(1, T_LCLIST); | |
1322 | ARG_ANY(2); | |
1323 | METHOD_CONSTRUCTOR("add"); | |
4c553c5a MM |
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) | |
fc9d471b | 1328 | RESULT(T_LCLIST, ad, [[ lc_set_union(fpool, v1.val.ad, v2.val.ad) ]]); |
4c553c5a MM |
1329 | else if (v2.type != T_LC) |
1330 | runtime("Can't add non-lc"); | |
1331 | else | |
fc9d471b | 1332 | RESULT(T_LCLIST, ad, [[ lc_set_add(fpool, v1.val.ad, v2.val.lc) ]]); |
967b88d9 | 1333 | } |
f62a369f | 1334 | |
fc9d471b MM |
1335 | INST(FI_PATH_DEL, 2, 1) { /* Path delete */ |
1336 | ARG(1, T_PATH); | |
f62a369f | 1337 | ARG_ANY(2); |
fc9d471b | 1338 | METHOD_CONSTRUCTOR("delete"); |
8f3c6151 | 1339 | if ((v2.type == T_SET) && path_set_type(v2.val.t) || (v2.type == T_INT)) |
fc9d471b | 1340 | RESULT(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 0) ]]); |
f62a369f JMM |
1341 | else |
1342 | runtime("Can't delete non-integer (set)"); | |
fc9d471b | 1343 | } |
4c553c5a | 1344 | |
fc9d471b MM |
1345 | INST(FI_CLIST_DEL, 2, 1) { /* (Extended) Community list add or delete */ |
1346 | ARG(1, T_CLIST); | |
1347 | ARG_ANY(2); | |
1348 | METHOD_CONSTRUCTOR("delete"); | |
f62a369f JMM |
1349 | /* Community (or cluster) list */ |
1350 | struct f_val dummy; | |
f62a369f JMM |
1351 | |
1352 | if ((v2.type == T_PAIR) || (v2.type == T_QUAD)) | |
fc9d471b | 1353 | RESULT(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, v2.val.i) ]]); |
f62a369f | 1354 | /* IP->Quad implicit conversion */ |
4c553c5a | 1355 | else if (val_is_ip4(&v2)) |
fc9d471b | 1356 | RESULT(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]); |
4c553c5a | 1357 | else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy) || (v2.type == T_CLIST)) |
fc9d471b | 1358 | RESULT(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 0) ]]); |
f62a369f | 1359 | else |
4c553c5a | 1360 | runtime("Can't delete non-pair"); |
fc9d471b | 1361 | } |
4c553c5a | 1362 | |
fc9d471b MM |
1363 | INST(FI_ECLIST_DEL, 2, 1) { /* (Extended) Community list add or delete */ |
1364 | ARG(1, T_ECLIST); | |
1365 | ARG_ANY(2); | |
1366 | METHOD_CONSTRUCTOR("delete"); | |
f62a369f | 1367 | /* v2.val is either EC or EC-set */ |
4c553c5a | 1368 | if ((v2.type == T_SET) && eclist_set_type(v2.val.t) || (v2.type == T_ECLIST)) |
fc9d471b | 1369 | RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 0) ]]); |
f62a369f | 1370 | else if (v2.type != T_EC) |
4c553c5a MM |
1371 | runtime("Can't delete non-ec"); |
1372 | else | |
fc9d471b MM |
1373 | RESULT(T_ECLIST, ad, [[ ec_set_del(fpool, v1.val.ad, v2.val.ec) ]]); |
1374 | } | |
4c553c5a | 1375 | |
fc9d471b MM |
1376 | INST(FI_LCLIST_DEL, 2, 1) { /* (Extended) Community list add or delete */ |
1377 | ARG(1, T_LCLIST); | |
1378 | ARG_ANY(2); | |
1379 | METHOD_CONSTRUCTOR("delete"); | |
f62a369f | 1380 | /* v2.val is either LC or LC-set */ |
4c553c5a | 1381 | if ((v2.type == T_SET) && lclist_set_type(v2.val.t) || (v2.type == T_LCLIST)) |
fc9d471b | 1382 | RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 0) ]]); |
f62a369f | 1383 | else if (v2.type != T_LC) |
4c553c5a MM |
1384 | runtime("Can't delete non-lc"); |
1385 | else | |
fc9d471b | 1386 | RESULT(T_LCLIST, ad, [[ lc_set_del(fpool, v1.val.ad, v2.val.lc) ]]); |
4c553c5a | 1387 | } |
f62a369f | 1388 | |
fc9d471b MM |
1389 | INST(FI_PATH_FILTER, 2, 1) { /* (Extended) Community list add or delete */ |
1390 | ARG(1, T_PATH); | |
4c553c5a | 1391 | ARG_ANY(2); |
fc9d471b | 1392 | METHOD_CONSTRUCTOR("filter"); |
6fbcd891 | 1393 | |
8f3c6151 | 1394 | if ((v2.type == T_SET) && path_set_type(v2.val.t)) |
fc9d471b | 1395 | RESULT(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 1) ]]); |
4c553c5a MM |
1396 | else |
1397 | runtime("Can't filter integer"); | |
f62a369f | 1398 | } |
f62a369f | 1399 | |
fc9d471b MM |
1400 | INST(FI_CLIST_FILTER, 2, 1) { |
1401 | ARG(1, T_CLIST); | |
1402 | ARG_ANY(2); | |
1403 | METHOD_CONSTRUCTOR("filter"); | |
4c553c5a MM |
1404 | /* Community (or cluster) list */ |
1405 | struct f_val dummy; | |
f62a369f | 1406 | |
4c553c5a | 1407 | if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy) || (v2.type == T_CLIST)) |
fc9d471b | 1408 | RESULT(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 1) ]]); |
4c553c5a MM |
1409 | else |
1410 | runtime("Can't filter pair"); | |
fc9d471b | 1411 | } |
4c553c5a | 1412 | |
fc9d471b MM |
1413 | INST(FI_ECLIST_FILTER, 2, 1) { |
1414 | ARG(1, T_ECLIST); | |
1415 | ARG_ANY(2); | |
1416 | METHOD_CONSTRUCTOR("filter"); | |
4c553c5a MM |
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)) | |
fc9d471b | 1419 | RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 1) ]]); |
4c553c5a MM |
1420 | else |
1421 | runtime("Can't filter ec"); | |
fc9d471b | 1422 | } |
f62a369f | 1423 | |
fc9d471b MM |
1424 | INST(FI_LCLIST_FILTER, 2, 1) { |
1425 | ARG(1, T_LCLIST); | |
1426 | ARG_ANY(2); | |
1427 | METHOD_CONSTRUCTOR("filter"); | |
4c553c5a MM |
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)) | |
fc9d471b | 1430 | RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 1) ]]); |
4c553c5a MM |
1431 | else |
1432 | runtime("Can't filter lc"); | |
4c553c5a | 1433 | } |
f62a369f | 1434 | |
4c553c5a | 1435 | INST(FI_ROA_CHECK_IMPLICIT, 0, 1) { /* ROA Check */ |
b40c0f02 | 1436 | NEVER_CONSTANT; |
4c553c5a | 1437 | RTC(1); |
b40c0f02 | 1438 | struct rtable *table = rtc->table; |
4c553c5a MM |
1439 | ACCESS_RTE; |
1440 | ACCESS_EATTRS; | |
1441 | const net_addr *net = (*fs->rte)->net->n.addr; | |
f62a369f | 1442 | |
4c553c5a MM |
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)); | |
f62a369f | 1446 | |
4c553c5a MM |
1447 | if (!e || ((e->type & EAF_TYPE_MASK) != EAF_TYPE_AS_PATH)) |
1448 | runtime("Missing AS_PATH attribute"); | |
1449 | ||
1450 | u32 as = 0; | |
1451 | as_path_get_last(e->u.ptr, &as); | |
f62a369f | 1452 | |
f62a369f JMM |
1453 | if (!table) |
1454 | runtime("Missing ROA table"); | |
1455 | ||
1456 | if (table->addr_type != NET_ROA4 && table->addr_type != NET_ROA6) | |
1457 | runtime("Table type must be either ROA4 or ROA6"); | |
1458 | ||
4c553c5a MM |
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 */ | |
1461 | else | |
1462 | RESULT(T_ENUM_ROA, i, [[ net_roa_check(table, net, as) ]]); | |
1463 | } | |
1464 | ||
1465 | INST(FI_ROA_CHECK_EXPLICIT, 2, 1) { /* ROA Check */ | |
b40c0f02 | 1466 | NEVER_CONSTANT; |
4c553c5a MM |
1467 | ARG(1, T_NET); |
1468 | ARG(2, T_INT); | |
1469 | RTC(3); | |
b40c0f02 | 1470 | struct rtable *table = rtc->table; |
4c553c5a MM |
1471 | |
1472 | u32 as = v2.val.i; | |
1473 | ||
1474 | if (!table) | |
1475 | runtime("Missing ROA table"); | |
1476 | ||
1477 | if (table->addr_type != NET_ROA4 && table->addr_type != NET_ROA6) | |
1478 | runtime("Table type must be either ROA4 or ROA6"); | |
f62a369f JMM |
1479 | |
1480 | if (table->addr_type != (v1.val.net->type == NET_IP4 ? NET_ROA4 : NET_ROA6)) | |
4c553c5a | 1481 | RESULT(T_ENUM_ROA, i, ROA_UNKNOWN); /* Prefix and table type mismatch */ |
f62a369f | 1482 | else |
4c553c5a | 1483 | RESULT(T_ENUM_ROA, i, [[ net_roa_check(table, v1.val.net, as) ]]); |
f62a369f | 1484 | |
967b88d9 | 1485 | } |
f62a369f | 1486 | |
fc354788 AZ |
1487 | INST(FI_FROM_HEX, 1, 1) { /* Convert hex text to bytestring */ |
1488 | ARG(1, T_STRING); | |
1489 | ||
1490 | int len = bstrhextobin(v1.val.s, NULL); | |
1491 | if (len < 0) | |
1492 | runtime("Invalid hex string"); | |
1493 | ||
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); | |
1498 | ||
1499 | RESULT(T_BYTESTRING, bs, bs); | |
1500 | } | |
1501 | ||
9b302c13 | 1502 | INST(FI_FORMAT, 1, 1) { /* Format */ |
f62a369f | 1503 | ARG_ANY(1); |
b40c0f02 | 1504 | RESULT(T_STRING, s, val_format_str(fpool, &v1)); |
967b88d9 | 1505 | } |
f62a369f | 1506 | |
4c553c5a | 1507 | INST(FI_ASSERT, 1, 0) { /* Birdtest Assert */ |
b40c0f02 | 1508 | NEVER_CONSTANT; |
f62a369f | 1509 | ARG(1, T_BOOL); |
550a6488 | 1510 | |
f634adc7 | 1511 | FID_MEMBER(char *, s, [[strcmp(f1->s, f2->s)]], "string %s", item->s); |
b40c0f02 MM |
1512 | |
1513 | ASSERT(s); | |
26bfe59f | 1514 | |
c0e958e0 MM |
1515 | if (!bt_assert_hook) |
1516 | runtime("No bt_assert hook registered, can't assert"); | |
1517 | ||
b40c0f02 | 1518 | bt_assert_hook(v1.val.i, what); |
967b88d9 | 1519 | } |