]>
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 | |
65 | * m4_dnl VARARG; variable-length argument list; accessible by vv(i) and whati->varcount | |
e1ac6f1e | 66 | * m4_dnl LINE(num, unused); this argument has to be converted to its own f_line |
63f49457 | 67 | * m4_dnl SYMBOL; symbol handed from config |
e1ac6f1e MM |
68 | * m4_dnl STATIC_ATTR; static attribute definition |
69 | * m4_dnl DYNAMIC_ATTR; dynamic attribute definition | |
70 | * m4_dnl RTC; route table config | |
e1ac6f1e MM |
71 | * m4_dnl ACCESS_RTE; this instruction needs route |
72 | * m4_dnl ACCESS_EATTRS; this instruction needs extended attributes | |
26bfe59f MM |
73 | * |
74 | * m4_dnl FID_MEMBER( custom instruction member | |
75 | * m4_dnl C type, for storage in structs | |
b40c0f02 | 76 | * m4_dnl name, how the member is named |
30667d50 | 77 | * m4_dnl comparator for same(), if different, this should be TRUE (CAVEAT) |
26bfe59f MM |
78 | * m4_dnl dump format string debug -> format string for bvsnprintf |
79 | * m4_dnl dump format args appropriate args | |
26bfe59f MM |
80 | * m4_dnl ) |
81 | * | |
e1ac6f1e | 82 | * m4_dnl RESULT(type, union-field, value); putting this on value stack |
f74d1976 MM |
83 | * m4_dnl RESULT_VAL(value-struct); pass the struct f_val directly |
84 | * m4_dnl RESULT_VOID; return undef | |
e1ac6f1e MM |
85 | * m4_dnl } |
86 | * | |
0da06b71 MM |
87 | * Also note that the { ... } blocks are not respected by M4 at all. |
88 | * If you get weird unmatched-brace-pair errors, check what it generated and why. | |
89 | * What is really considered as one instruction is not the { ... } block | |
90 | * after m4_dnl INST() but all the code between them. | |
91 | * | |
e1ac6f1e MM |
92 | * Other code is just copied into the interpreter part. |
93 | * | |
0da06b71 MM |
94 | * If you are satisfied with this, you don't need to read the following |
95 | * detailed description of what is really done with the instruction definitions. | |
96 | * | |
97 | * m4_dnl Now let's look under the cover. The code between each INST() | |
98 | * m4_dnl is copied to several places, namely these (numbered by the M4 diversions | |
99 | * m4_dnl used in filter/decl.m4): | |
100 | * | |
101 | * m4_dnl (102) struct f_inst *f_new_inst(FI_EXAMPLE [[ put it here ]]) | |
102 | * m4_dnl { | |
103 | * m4_dnl ... (common code) | |
104 | * m4_dnl (103) [[ put it here ]] | |
105 | * m4_dnl ... | |
106 | * m4_dnl if (all arguments are constant) | |
26194bd6 | 107 | * m4_dnl (108) [[ put it here ]] |
0da06b71 MM |
108 | * m4_dnl } |
109 | * m4_dnl For writing directly to constructor argument list, use FID_NEW_ARGS. | |
110 | * m4_dnl For computing something in constructor (103), use FID_NEW_BODY. | |
111 | * m4_dnl For constant pre-interpretation (108), see below at FID_INTERPRET_BODY. | |
112 | * | |
113 | * m4_dnl struct f_inst { | |
114 | * m4_dnl ... (common fields) | |
115 | * m4_dnl union { | |
116 | * m4_dnl struct { | |
117 | * m4_dnl (101) [[ put it here ]] | |
118 | * m4_dnl } i_FI_EXAMPLE; | |
119 | * m4_dnl ... | |
120 | * m4_dnl }; | |
121 | * m4_dnl }; | |
122 | * m4_dnl This structure is returned from constructor. | |
123 | * m4_dnl For writing directly to this structure, use FID_STRUCT_IN. | |
124 | * | |
125 | * m4_dnl linearize(struct f_line *dest, const struct f_inst *what, uint pos) { | |
126 | * m4_dnl ... | |
127 | * m4_dnl switch (what->fi_code) { | |
128 | * m4_dnl case FI_EXAMPLE: | |
129 | * m4_dnl (105) [[ put it here ]] | |
130 | * m4_dnl break; | |
131 | * m4_dnl } | |
132 | * m4_dnl } | |
133 | * m4_dnl This is called when translating from struct f_inst to struct f_line_item. | |
134 | * m4_dnl For accessing your custom instruction data, use following macros: | |
135 | * m4_dnl whati -> for accessing (struct f_inst).i_FI_EXAMPLE | |
136 | * m4_dnl item -> for accessing (struct f_line)[pos].i_FI_EXAMPLE | |
137 | * m4_dnl For writing directly here, use FID_LINEARIZE_BODY. | |
138 | * | |
139 | * m4_dnl (107) struct f_line_item { | |
140 | * m4_dnl ... (common fields) | |
141 | * m4_dnl union { | |
142 | * m4_dnl struct { | |
143 | * m4_dnl (101) [[ put it here ]] | |
144 | * m4_dnl } i_FI_EXAMPLE; | |
145 | * m4_dnl ... | |
146 | * m4_dnl }; | |
147 | * m4_dnl }; | |
148 | * m4_dnl The same as FID_STRUCT_IN (101) but for the other structure. | |
149 | * m4_dnl This structure is returned from the linearizer (105). | |
150 | * m4_dnl For writing directly to this structure, use FID_LINE_IN. | |
151 | * | |
152 | * m4_dnl f_dump_line_item_FI_EXAMPLE(const struct f_line_item *item, const int indent) | |
153 | * m4_dnl { | |
154 | * m4_dnl (104) [[ put it here ]] | |
155 | * m4_dnl } | |
156 | * m4_dnl This code dumps the instruction on debug. Note that the argument | |
157 | * m4_dnl is the linearized instruction; if the instruction has arguments, | |
158 | * m4_dnl their code has already been linearized and their value is taken | |
159 | * m4_dnl from the value stack. | |
160 | * m4_dnl For writing directly here, use FID_DUMP_BODY. | |
161 | * | |
162 | * m4_dnl f_same(...) | |
163 | * m4_dnl { | |
164 | * m4_dnl switch (f1_->fi_code) { | |
165 | * m4_dnl case FI_EXAMPLE: | |
166 | * m4_dnl (106) [[ put it here ]] | |
167 | * m4_dnl break; | |
168 | * m4_dnl } | |
169 | * m4_dnl } | |
170 | * m4_dnl This code compares the two given instrucions (f1_ and f2_) | |
171 | * m4_dnl on reconfigure. For accessing your custom instruction data, | |
172 | * m4_dnl use macros f1 and f2. | |
173 | * m4_dnl For writing directly here, use FID_SAME_BODY. | |
174 | * | |
d06a875b OZ |
175 | * m4_dnl f_add_lines(...) |
176 | * m4_dnl { | |
177 | * m4_dnl switch (what_->fi_code) { | |
178 | * m4_dnl case FI_EXAMPLE: | |
179 | * m4_dnl (109) [[ put it here ]] | |
180 | * m4_dnl break; | |
181 | * m4_dnl } | |
182 | * m4_dnl } | |
183 | * m4_dnl This code adds new filter lines reachable from the instruction | |
184 | * m4_dnl to the filter iterator line buffer. This is for instructions | |
185 | * m4_dnl that changes conrol flow, like FI_CONDITION or FI_CALL, most | |
186 | * m4_dnl instructions do not need to update it. It is used in generic | |
187 | * m4_dnl filter iteration code (FILTER_ITERATE*). For accessing your | |
188 | * m4_dnl custom instruction data, use macros f1 and f2. For writing | |
189 | * m4_dnl directly here, use FID_ITERATE_BODY. | |
190 | * | |
0da06b71 MM |
191 | * m4_dnl interpret(...) |
192 | * m4_dnl { | |
193 | * m4_dnl switch (what->fi_code) { | |
194 | * m4_dnl case FI_EXAMPLE: | |
195 | * m4_dnl (108) [[ put it here ]] | |
196 | * m4_dnl break; | |
197 | * m4_dnl } | |
198 | * m4_dnl } | |
199 | * m4_dnl This code executes the instruction. Every pre-defined macro | |
200 | * m4_dnl resets the output here. For setting it explicitly, | |
201 | * m4_dnl use FID_INTERPRET_BODY. | |
202 | * m4_dnl This code is put on two places; one is the interpreter, the other | |
203 | * m4_dnl is instruction constructor. If you need to distinguish between | |
204 | * m4_dnl these two, use FID_INTERPRET_EXEC or FID_INTERPRET_NEW respectively. | |
205 | * m4_dnl To address the difference between interpreter and constructor | |
206 | * m4_dnl environments, there are several convenience macros defined: | |
207 | * m4_dnl runtime() -> for spitting out runtime error like division by zero | |
208 | * m4_dnl RESULT(...) -> declare result; may overwrite arguments | |
209 | * m4_dnl v1, v2, v3 -> positional arguments, may be overwritten by RESULT() | |
210 | * m4_dnl falloc(size) -> allocate memory from the appropriate linpool | |
211 | * m4_dnl fpool -> the current linpool | |
212 | * m4_dnl NEVER_CONSTANT-> don't generate pre-interpretation code at all | |
213 | * m4_dnl ACCESS_RTE -> check that route is available, also NEVER_CONSTANT | |
214 | * m4_dnl ACCESS_EATTRS -> pre-cache the eattrs; use only with ACCESS_RTE | |
215 | * m4_dnl f_rta_cow(fs) -> function to call before any change to route should be done | |
216 | * | |
217 | * m4_dnl If you are stymied, see FI_CALL or FI_CONSTANT or just search for | |
218 | * m4_dnl the mentioned macros in this file to see what is happening there in wild. | |
f62a369f JMM |
219 | */ |
220 | ||
221 | /* Binary operators */ | |
4c553c5a MM |
222 | INST(FI_ADD, 2, 1) { |
223 | ARG(1,T_INT); | |
224 | ARG(2,T_INT); | |
f74d1976 | 225 | RESULT(T_INT, i, v1.val.i + v2.val.i); |
4c553c5a MM |
226 | } |
227 | INST(FI_SUBTRACT, 2, 1) { | |
228 | ARG(1,T_INT); | |
229 | ARG(2,T_INT); | |
f74d1976 | 230 | RESULT(T_INT, i, v1.val.i - v2.val.i); |
4c553c5a MM |
231 | } |
232 | INST(FI_MULTIPLY, 2, 1) { | |
233 | ARG(1,T_INT); | |
234 | ARG(2,T_INT); | |
f74d1976 | 235 | RESULT(T_INT, i, v1.val.i * v2.val.i); |
4c553c5a MM |
236 | } |
237 | INST(FI_DIVIDE, 2, 1) { | |
238 | ARG(1,T_INT); | |
239 | ARG(2,T_INT); | |
240 | if (v2.val.i == 0) runtime( "Mother told me not to divide by 0" ); | |
f74d1976 | 241 | RESULT(T_INT, i, v1.val.i / v2.val.i); |
4c553c5a MM |
242 | } |
243 | INST(FI_AND, 1, 1) { | |
244 | ARG(1,T_BOOL); | |
ef8c4574 | 245 | ARG_TYPE_STATIC(2,T_BOOL); |
10c4cd96 OZ |
246 | RESULT_TYPE(T_BOOL); |
247 | ||
f74d1976 | 248 | if (v1.val.i) |
4c553c5a MM |
249 | LINE(2,0); |
250 | else | |
f74d1976 | 251 | RESULT_VAL(v1); |
967b88d9 | 252 | } |
4c553c5a MM |
253 | INST(FI_OR, 1, 1) { |
254 | ARG(1,T_BOOL); | |
ef8c4574 | 255 | ARG_TYPE_STATIC(2,T_BOOL); |
10c4cd96 OZ |
256 | RESULT_TYPE(T_BOOL); |
257 | ||
f74d1976 | 258 | if (!v1.val.i) |
4c553c5a MM |
259 | LINE(2,0); |
260 | else | |
f74d1976 | 261 | RESULT_VAL(v1); |
967b88d9 | 262 | } |
bfa15a64 | 263 | |
4c553c5a | 264 | INST(FI_PAIR_CONSTRUCT, 2, 1) { |
f62a369f JMM |
265 | ARG(1,T_INT); |
266 | ARG(2,T_INT); | |
4c553c5a MM |
267 | uint u1 = v1.val.i; |
268 | uint u2 = v2.val.i; | |
f62a369f JMM |
269 | if ((u1 > 0xFFFF) || (u2 > 0xFFFF)) |
270 | runtime( "Can't operate with value out of bounds in pair constructor" ); | |
4c553c5a | 271 | RESULT(T_PAIR, i, (u1 << 16) | u2); |
967b88d9 | 272 | } |
bfa15a64 | 273 | |
4c553c5a MM |
274 | INST(FI_EC_CONSTRUCT, 2, 1) { |
275 | ARG_ANY(1); | |
276 | ARG(2, T_INT); | |
26bfe59f | 277 | |
f634adc7 | 278 | FID_MEMBER(enum ec_subtype, ecs, f1->ecs != f2->ecs, "ec subtype %s", ec_subtype_str(item->ecs)); |
f62a369f | 279 | |
124d860f | 280 | int ipv4_used; |
4c553c5a | 281 | u32 key, val; |
f62a369f | 282 | |
4c553c5a MM |
283 | if (v1.type == T_INT) { |
284 | ipv4_used = 0; key = v1.val.i; | |
285 | } | |
286 | else if (v1.type == T_QUAD) { | |
287 | ipv4_used = 1; key = v1.val.i; | |
288 | } | |
289 | /* IP->Quad implicit conversion */ | |
290 | else if (val_is_ip4(&v1)) { | |
291 | ipv4_used = 1; key = ipa_to_u32(v1.val.ip); | |
292 | } | |
293 | else | |
bfa15a64 | 294 | runtime("Argument 1 of EC constructor must be integer or IPv4 address, got 0x%02x", v1.type); |
f62a369f | 295 | |
4c553c5a | 296 | val = v2.val.i; |
f62a369f | 297 | |
124d860f MM |
298 | if (ecs == EC_GENERIC) |
299 | RESULT(T_EC, ec, ec_generic(key, val)); | |
300 | else if (ipv4_used) | |
301 | if (val <= 0xFFFF) | |
302 | RESULT(T_EC, ec, ec_ip4(ecs, key, val)); | |
303 | else | |
304 | runtime("4-byte value %u can't be used with IP-address key in extended community", val); | |
305 | else if (key < 0x10000) | |
306 | RESULT(T_EC, ec, ec_as2(ecs, key, val)); | |
307 | else | |
308 | if (val <= 0xFFFF) | |
309 | RESULT(T_EC, ec, ec_as4(ecs, key, val)); | |
310 | else | |
311 | runtime("4-byte value %u can't be used with 4-byte ASN in extended community", val); | |
4c553c5a | 312 | } |
f62a369f | 313 | |
4c553c5a MM |
314 | INST(FI_LC_CONSTRUCT, 3, 1) { |
315 | ARG(1, T_INT); | |
316 | ARG(2, T_INT); | |
317 | ARG(3, T_INT); | |
318 | RESULT(T_LC, lc, [[(lcomm) { v1.val.i, v2.val.i, v3.val.i }]]); | |
319 | } | |
f62a369f | 320 | |
4c553c5a | 321 | INST(FI_PATHMASK_CONSTRUCT, 0, 1) { |
c0999a14 | 322 | VARARG; |
4c553c5a | 323 | |
c0999a14 MM |
324 | struct f_path_mask *pm = falloc(sizeof(struct f_path_mask) + whati->varcount * sizeof(struct f_path_mask_item)); |
325 | pm->len = whati->varcount; | |
b40c0f02 | 326 | |
c0999a14 MM |
327 | for (uint i=0; i<whati->varcount; i++) { |
328 | switch (vv(i).type) { | |
4c553c5a | 329 | case T_PATH_MASK_ITEM: |
ec430a7f OZ |
330 | if (vv(i).val.pmi.kind == PM_LOOP) |
331 | { | |
332 | if (i == 0) | |
333 | runtime("Path mask iterator '+' cannot be first"); | |
334 | ||
335 | /* We want PM_LOOP as prefix operator */ | |
336 | pm->item[i] = pm->item[i - 1]; | |
337 | pm->item[i - 1] = vv(i).val.pmi; | |
338 | break; | |
339 | } | |
340 | ||
c0999a14 | 341 | pm->item[i] = vv(i).val.pmi; |
4c553c5a | 342 | break; |
9f3e0983 | 343 | |
4c553c5a MM |
344 | case T_INT: |
345 | pm->item[i] = (struct f_path_mask_item) { | |
c0999a14 | 346 | .asn = vv(i).val.i, |
4c553c5a MM |
347 | .kind = PM_ASN, |
348 | }; | |
349 | break; | |
9f3e0983 OZ |
350 | |
351 | case T_SET: | |
352 | if (vv(i).val.t->from.type != T_INT) | |
353 | runtime("Only integer sets allowed in path mask"); | |
354 | ||
355 | pm->item[i] = (struct f_path_mask_item) { | |
356 | .set = vv(i).val.t, | |
357 | .kind = PM_ASN_SET, | |
358 | }; | |
359 | break; | |
360 | ||
4c553c5a MM |
361 | default: |
362 | runtime( "Error resolving path mask template: value not an integer" ); | |
363 | } | |
f62a369f | 364 | } |
f62a369f | 365 | |
4c553c5a MM |
366 | RESULT(T_PATH_MASK, path_mask, pm); |
367 | } | |
f62a369f JMM |
368 | |
369 | /* Relational operators */ | |
370 | ||
4c553c5a | 371 | INST(FI_NEQ, 2, 1) { |
c5774939 MM |
372 | ARG_ANY(1); |
373 | ARG_ANY(2); | |
4c553c5a | 374 | RESULT(T_BOOL, i, !val_same(&v1, &v2)); |
967b88d9 | 375 | } |
f62a369f | 376 | |
4c553c5a | 377 | INST(FI_EQ, 2, 1) { |
c5774939 MM |
378 | ARG_ANY(1); |
379 | ARG_ANY(2); | |
4c553c5a | 380 | RESULT(T_BOOL, i, val_same(&v1, &v2)); |
967b88d9 | 381 | } |
c5774939 | 382 | |
4c553c5a | 383 | INST(FI_LT, 2, 1) { |
c5774939 MM |
384 | ARG_ANY(1); |
385 | ARG_ANY(2); | |
10c4cd96 OZ |
386 | ARG_SAME_TYPE(1, 2); |
387 | ||
4c553c5a | 388 | int i = val_compare(&v1, &v2); |
52893045 | 389 | if (i == F_CMP_ERROR) |
c5774939 | 390 | runtime( "Can't compare values of incompatible types" ); |
4c553c5a | 391 | RESULT(T_BOOL, i, (i == -1)); |
967b88d9 | 392 | } |
f62a369f | 393 | |
4c553c5a | 394 | INST(FI_LTE, 2, 1) { |
c5774939 MM |
395 | ARG_ANY(1); |
396 | ARG_ANY(2); | |
10c4cd96 OZ |
397 | ARG_SAME_TYPE(1, 2); |
398 | ||
4c553c5a | 399 | int i = val_compare(&v1, &v2); |
52893045 | 400 | if (i == F_CMP_ERROR) |
c5774939 | 401 | runtime( "Can't compare values of incompatible types" ); |
4c553c5a | 402 | RESULT(T_BOOL, i, (i != 1)); |
967b88d9 | 403 | } |
f62a369f | 404 | |
4c553c5a MM |
405 | INST(FI_NOT, 1, 1) { |
406 | ARG(1,T_BOOL); | |
407 | RESULT(T_BOOL, i, !v1.val.i); | |
967b88d9 | 408 | } |
f62a369f | 409 | |
4c553c5a | 410 | INST(FI_MATCH, 2, 1) { |
f62a369f JMM |
411 | ARG_ANY(1); |
412 | ARG_ANY(2); | |
4c553c5a | 413 | int i = val_in_range(&v1, &v2); |
52893045 | 414 | if (i == F_CMP_ERROR) |
f62a369f | 415 | runtime( "~ applied on unknown type pair" ); |
4c553c5a | 416 | RESULT(T_BOOL, i, !!i); |
967b88d9 | 417 | } |
f62a369f | 418 | |
4c553c5a | 419 | INST(FI_NOT_MATCH, 2, 1) { |
f62a369f JMM |
420 | ARG_ANY(1); |
421 | ARG_ANY(2); | |
4c553c5a | 422 | int i = val_in_range(&v1, &v2); |
fe503c7c | 423 | if (i == F_CMP_ERROR) |
f62a369f | 424 | runtime( "!~ applied on unknown type pair" ); |
4c553c5a | 425 | RESULT(T_BOOL, i, !i); |
967b88d9 | 426 | } |
f62a369f | 427 | |
4c553c5a | 428 | INST(FI_DEFINED, 1, 1) { |
f62a369f | 429 | ARG_ANY(1); |
4c553c5a | 430 | RESULT(T_BOOL, i, (v1.type != T_VOID) && !undef_value(v1)); |
967b88d9 | 431 | } |
4c553c5a MM |
432 | |
433 | INST(FI_TYPE, 1, 1) { | |
f62a369f JMM |
434 | ARG_ANY(1); /* There may be more types supporting this operation */ |
435 | switch (v1.type) | |
436 | { | |
437 | case T_NET: | |
4c553c5a | 438 | RESULT(T_ENUM_NETTYPE, i, v1.val.net->type); |
f62a369f JMM |
439 | break; |
440 | default: | |
441 | runtime( "Can't determine type of this item" ); | |
442 | } | |
967b88d9 | 443 | } |
4c553c5a MM |
444 | |
445 | INST(FI_IS_V4, 1, 1) { | |
f62a369f | 446 | ARG(1, T_IP); |
4c553c5a | 447 | RESULT(T_BOOL, i, ipa_is_ip4(v1.val.ip)); |
967b88d9 | 448 | } |
f62a369f | 449 | |
4c553c5a | 450 | /* Set to indirect value prepared in v1 */ |
96d757c1 | 451 | INST(FI_VAR_SET, 1, 0) { |
b40c0f02 | 452 | NEVER_CONSTANT; |
63f49457 MM |
453 | ARG_ANY(1); |
454 | SYMBOL; | |
26194bd6 | 455 | ARG_TYPE(1, sym->class & 0xff); |
96d757c1 | 456 | |
1757a6fc | 457 | fstk->vstk[curline.vbase + sym->offset] = v1; |
96d757c1 JMM |
458 | } |
459 | ||
460 | INST(FI_VAR_GET, 0, 1) { | |
26bfe59f | 461 | SYMBOL; |
b40c0f02 | 462 | NEVER_CONSTANT; |
6fbcd891 | 463 | RESULT_TYPE(sym->class & 0xff); |
f74d1976 | 464 | RESULT_VAL(fstk->vstk[curline.vbase + sym->offset]); |
967b88d9 | 465 | } |
f62a369f | 466 | |
bfa15a64 | 467 | INST(FI_CONSTANT, 0, 1) { |
30667d50 MM |
468 | FID_MEMBER( |
469 | struct f_val, | |
470 | val, | |
30667d50 | 471 | [[ !val_same(&(f1->val), &(f2->val)) ]], |
f634adc7 | 472 | "value %s", |
30667d50 MM |
473 | val_dump(&(item->val)) |
474 | ); | |
ea4f55e3 | 475 | |
6fbcd891 | 476 | RESULT_TYPE(val.type); |
b40c0f02 | 477 | RESULT_VAL(val); |
967b88d9 | 478 | } |
bfa15a64 | 479 | |
4c553c5a MM |
480 | INST(FI_CONDITION, 1, 0) { |
481 | ARG(1, T_BOOL); | |
b40c0f02 | 482 | if (v1.val.i) |
4c553c5a | 483 | LINE(2,0); |
224b77d4 | 484 | else |
4c553c5a | 485 | LINE(3,1); |
967b88d9 | 486 | } |
9b46748d | 487 | |
0206c070 MM |
488 | INST(FI_PRINT, 0, 0) { |
489 | NEVER_CONSTANT; | |
c0999a14 | 490 | VARARG; |
0206c070 | 491 | |
c0999a14 MM |
492 | if (whati->varcount && !(fs->flags & FF_SILENT)) |
493 | for (uint i=0; i<whati->varcount; i++) | |
494 | val_format(&(vv(i)), &fs->buf); | |
0206c070 MM |
495 | } |
496 | ||
efd7c87b MM |
497 | INST(FI_FLUSH, 0, 0) { |
498 | NEVER_CONSTANT; | |
499 | if (!(fs->flags & FF_SILENT)) | |
500 | /* After log_commit, the buffer is reset */ | |
501 | log_commit(*L_INFO, &fs->buf); | |
502 | } | |
503 | ||
0206c070 MM |
504 | INST(FI_DIE, 0, 0) { |
505 | NEVER_CONSTANT; | |
f634adc7 | 506 | FID_MEMBER(enum filter_return, fret, f1->fret != f2->fret, "%s", filter_return_str(item->fret)); |
4c553c5a | 507 | |
0206c070 | 508 | switch (whati->fret) { |
efd7c87b | 509 | case F_ACCEPT: /* Should take care about turning ACCEPT into MODIFY */ |
f62a369f | 510 | case F_ERROR: |
efd7c87b | 511 | case F_REJECT: /* Maybe print complete route along with reason to reject route? */ |
4c553c5a | 512 | return fret; /* We have to return now, no more processing. */ |
f62a369f JMM |
513 | default: |
514 | bug( "unknown return type: Can't happen"); | |
515 | } | |
967b88d9 | 516 | } |
bfa15a64 OZ |
517 | |
518 | INST(FI_RTA_GET, 0, 1) { | |
f62a369f | 519 | { |
4c553c5a | 520 | STATIC_ATTR; |
f62a369f JMM |
521 | ACCESS_RTE; |
522 | struct rta *rta = (*fs->rte)->attrs; | |
f62a369f | 523 | |
4c553c5a | 524 | switch (sa.sa_code) |
f62a369f | 525 | { |
4c553c5a MM |
526 | case SA_FROM: RESULT(sa.f_type, ip, rta->from); break; |
527 | case SA_GW: RESULT(sa.f_type, ip, rta->nh.gw); break; | |
528 | case SA_NET: RESULT(sa.f_type, net, (*fs->rte)->net->n.addr); break; | |
529 | case SA_PROTO: RESULT(sa.f_type, s, rta->src->proto->name); break; | |
530 | case SA_SOURCE: RESULT(sa.f_type, i, rta->source); break; | |
531 | case SA_SCOPE: RESULT(sa.f_type, i, rta->scope); break; | |
532 | case SA_DEST: RESULT(sa.f_type, i, rta->dest); break; | |
533 | case SA_IFNAME: RESULT(sa.f_type, s, rta->nh.iface ? rta->nh.iface->name : ""); break; | |
534 | case SA_IFINDEX: RESULT(sa.f_type, i, rta->nh.iface ? rta->nh.iface->index : 0); break; | |
8cc5bb09 | 535 | case SA_WEIGHT: RESULT(sa.f_type, i, rta->nh.weight + 1); break; |
e5468d16 | 536 | case SA_GW_MPLS: RESULT(sa.f_type, i, rta->nh.labels ? rta->nh.label[0] : MPLS_NULL); break; |
f62a369f JMM |
537 | |
538 | default: | |
4c553c5a | 539 | bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code); |
f62a369f JMM |
540 | } |
541 | } | |
967b88d9 | 542 | } |
4c553c5a | 543 | |
a84b8b6e MM |
544 | INST(FI_RTA_SET, 1, 0) { |
545 | ACCESS_RTE; | |
546 | ARG_ANY(1); | |
547 | STATIC_ATTR; | |
26194bd6 | 548 | ARG_TYPE(1, sa.f_type); |
a84b8b6e MM |
549 | |
550 | f_rta_cow(fs); | |
551 | { | |
552 | struct rta *rta = (*fs->rte)->attrs; | |
553 | ||
554 | switch (sa.sa_code) | |
555 | { | |
556 | case SA_FROM: | |
557 | rta->from = v1.val.ip; | |
558 | break; | |
559 | ||
560 | case SA_GW: | |
561 | { | |
562 | ip_addr ip = v1.val.ip; | |
94abefc0 OZ |
563 | struct iface *ifa = ipa_is_link_local(ip) ? rta->nh.iface : NULL; |
564 | neighbor *n = neigh_find(rta->src->proto, ip, ifa, 0); | |
a84b8b6e MM |
565 | if (!n || (n->scope == SCOPE_HOST)) |
566 | runtime( "Invalid gw address" ); | |
567 | ||
568 | rta->dest = RTD_UNICAST; | |
569 | rta->nh.gw = ip; | |
570 | rta->nh.iface = n->iface; | |
571 | rta->nh.next = NULL; | |
572 | rta->hostentry = NULL; | |
e5468d16 | 573 | rta->nh.labels = 0; |
a84b8b6e MM |
574 | } |
575 | break; | |
576 | ||
577 | case SA_SCOPE: | |
578 | rta->scope = v1.val.i; | |
579 | break; | |
580 | ||
581 | case SA_DEST: | |
582 | { | |
583 | int i = v1.val.i; | |
584 | if ((i != RTD_BLACKHOLE) && (i != RTD_UNREACHABLE) && (i != RTD_PROHIBIT)) | |
585 | runtime( "Destination can be changed only to blackhole, unreachable or prohibit" ); | |
586 | ||
587 | rta->dest = i; | |
588 | rta->nh.gw = IPA_NONE; | |
589 | rta->nh.iface = NULL; | |
590 | rta->nh.next = NULL; | |
591 | rta->hostentry = NULL; | |
e5468d16 | 592 | rta->nh.labels = 0; |
a84b8b6e MM |
593 | } |
594 | break; | |
595 | ||
596 | case SA_IFNAME: | |
597 | { | |
598 | struct iface *ifa = if_find_by_name(v1.val.s); | |
599 | if (!ifa) | |
600 | runtime( "Invalid iface name" ); | |
601 | ||
602 | rta->dest = RTD_UNICAST; | |
603 | rta->nh.gw = IPA_NONE; | |
604 | rta->nh.iface = ifa; | |
605 | rta->nh.next = NULL; | |
606 | rta->hostentry = NULL; | |
e5468d16 TB |
607 | rta->nh.labels = 0; |
608 | } | |
609 | break; | |
610 | ||
611 | case SA_GW_MPLS: | |
612 | { | |
613 | if (v1.val.i >= 0x100000) | |
614 | runtime( "Invalid MPLS label" ); | |
615 | ||
616 | if (v1.val.i != MPLS_NULL) | |
617 | { | |
618 | rta->nh.label[0] = v1.val.i; | |
619 | rta->nh.labels = 1; | |
620 | } | |
621 | else | |
622 | rta->nh.labels = 0; | |
a84b8b6e MM |
623 | } |
624 | break; | |
625 | ||
8cc5bb09 OZ |
626 | case SA_WEIGHT: |
627 | { | |
628 | int i = v1.val.i; | |
629 | if (i < 1 || i > 256) | |
630 | runtime( "Setting weight value out of bounds" ); | |
631 | if (rta->dest != RTD_UNICAST) | |
632 | runtime( "Setting weight needs regular nexthop " ); | |
633 | ||
634 | /* Set weight on all next hops */ | |
635 | for (struct nexthop *nh = &rta->nh; nh; nh = nh->next) | |
636 | nh->weight = i - 1; | |
637 | } | |
638 | break; | |
639 | ||
a84b8b6e MM |
640 | default: |
641 | bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code); | |
642 | } | |
643 | } | |
644 | } | |
645 | ||
4c553c5a MM |
646 | INST(FI_EA_GET, 0, 1) { /* Access to extended attributes */ |
647 | DYNAMIC_ATTR; | |
f62a369f JMM |
648 | ACCESS_RTE; |
649 | ACCESS_EATTRS; | |
6fbcd891 | 650 | RESULT_TYPE(da.f_type); |
f62a369f | 651 | { |
4c553c5a | 652 | eattr *e = ea_find(*fs->eattrs, da.ea_code); |
f62a369f JMM |
653 | |
654 | if (!e) { | |
655 | /* A special case: undefined as_path looks like empty as_path */ | |
4c553c5a | 656 | if (da.type == EAF_TYPE_AS_PATH) { |
6fbcd891 | 657 | RESULT_(T_PATH, ad, &null_adata); |
f62a369f JMM |
658 | break; |
659 | } | |
660 | ||
661 | /* The same special case for int_set */ | |
4c553c5a | 662 | if (da.type == EAF_TYPE_INT_SET) { |
6fbcd891 | 663 | RESULT_(T_CLIST, ad, &null_adata); |
f62a369f JMM |
664 | break; |
665 | } | |
666 | ||
667 | /* The same special case for ec_set */ | |
4c553c5a | 668 | if (da.type == EAF_TYPE_EC_SET) { |
6fbcd891 | 669 | RESULT_(T_ECLIST, ad, &null_adata); |
f62a369f JMM |
670 | break; |
671 | } | |
672 | ||
673 | /* The same special case for lc_set */ | |
4c553c5a | 674 | if (da.type == EAF_TYPE_LC_SET) { |
6fbcd891 | 675 | RESULT_(T_LCLIST, ad, &null_adata); |
f62a369f JMM |
676 | break; |
677 | } | |
678 | ||
679 | /* Undefined value */ | |
f74d1976 | 680 | RESULT_VOID; |
f62a369f JMM |
681 | break; |
682 | } | |
683 | ||
684 | switch (e->type & EAF_TYPE_MASK) { | |
685 | case EAF_TYPE_INT: | |
6fbcd891 | 686 | RESULT_(da.f_type, i, e->u.data); |
f62a369f JMM |
687 | break; |
688 | case EAF_TYPE_ROUTER_ID: | |
6fbcd891 | 689 | RESULT_(T_QUAD, i, e->u.data); |
f62a369f JMM |
690 | break; |
691 | case EAF_TYPE_OPAQUE: | |
6fbcd891 | 692 | RESULT_(T_ENUM_EMPTY, i, 0); |
f62a369f JMM |
693 | break; |
694 | case EAF_TYPE_IP_ADDRESS: | |
6fbcd891 | 695 | RESULT_(T_IP, ip, *((ip_addr *) e->u.ptr->data)); |
f62a369f JMM |
696 | break; |
697 | case EAF_TYPE_AS_PATH: | |
6fbcd891 | 698 | RESULT_(T_PATH, ad, e->u.ptr); |
f62a369f JMM |
699 | break; |
700 | case EAF_TYPE_BITFIELD: | |
6fbcd891 | 701 | RESULT_(T_BOOL, i, !!(e->u.data & (1u << da.bit))); |
f62a369f JMM |
702 | break; |
703 | case EAF_TYPE_INT_SET: | |
6fbcd891 | 704 | RESULT_(T_CLIST, ad, e->u.ptr); |
f62a369f JMM |
705 | break; |
706 | case EAF_TYPE_EC_SET: | |
6fbcd891 | 707 | RESULT_(T_ECLIST, ad, e->u.ptr); |
f62a369f JMM |
708 | break; |
709 | case EAF_TYPE_LC_SET: | |
6fbcd891 | 710 | RESULT_(T_LCLIST, ad, e->u.ptr); |
f62a369f JMM |
711 | break; |
712 | case EAF_TYPE_UNDEF: | |
f74d1976 | 713 | RESULT_VOID; |
f62a369f JMM |
714 | break; |
715 | default: | |
4c553c5a | 716 | bug("Unknown dynamic attribute type"); |
f62a369f JMM |
717 | } |
718 | } | |
967b88d9 | 719 | } |
4c553c5a | 720 | |
a84b8b6e MM |
721 | INST(FI_EA_SET, 1, 0) { |
722 | ACCESS_RTE; | |
723 | ACCESS_EATTRS; | |
724 | ARG_ANY(1); | |
725 | DYNAMIC_ATTR; | |
26194bd6 | 726 | ARG_TYPE(1, da.f_type); |
a84b8b6e MM |
727 | { |
728 | struct ea_list *l = lp_alloc(fs->pool, sizeof(struct ea_list) + sizeof(eattr)); | |
729 | ||
730 | l->next = NULL; | |
731 | l->flags = EALF_SORTED; | |
732 | l->count = 1; | |
733 | l->attrs[0].id = da.ea_code; | |
734 | l->attrs[0].flags = 0; | |
735 | l->attrs[0].type = da.type | EAF_ORIGINATED | EAF_FRESH; | |
736 | ||
737 | switch (da.type) { | |
738 | case EAF_TYPE_INT: | |
a84b8b6e | 739 | case EAF_TYPE_ROUTER_ID: |
a84b8b6e MM |
740 | l->attrs[0].u.data = v1.val.i; |
741 | break; | |
742 | ||
743 | case EAF_TYPE_OPAQUE: | |
744 | runtime( "Setting opaque attribute is not allowed" ); | |
745 | break; | |
bfa15a64 | 746 | |
26194bd6 | 747 | case EAF_TYPE_IP_ADDRESS:; |
a84b8b6e MM |
748 | int len = sizeof(ip_addr); |
749 | struct adata *ad = lp_alloc(fs->pool, sizeof(struct adata) + len); | |
750 | ad->length = len; | |
751 | (* (ip_addr *) ad->data) = v1.val.ip; | |
752 | l->attrs[0].u.ptr = ad; | |
753 | break; | |
bfa15a64 | 754 | |
a84b8b6e | 755 | case EAF_TYPE_AS_PATH: |
26194bd6 OZ |
756 | case EAF_TYPE_INT_SET: |
757 | case EAF_TYPE_EC_SET: | |
758 | case EAF_TYPE_LC_SET: | |
a84b8b6e MM |
759 | l->attrs[0].u.ptr = v1.val.ad; |
760 | break; | |
bfa15a64 | 761 | |
a84b8b6e | 762 | case EAF_TYPE_BITFIELD: |
a84b8b6e MM |
763 | { |
764 | /* First, we have to find the old value */ | |
765 | eattr *e = ea_find(*fs->eattrs, da.ea_code); | |
766 | u32 data = e ? e->u.data : 0; | |
767 | ||
768 | if (v1.val.i) | |
769 | l->attrs[0].u.data = data | (1u << da.bit); | |
770 | else | |
771 | l->attrs[0].u.data = data & ~(1u << da.bit); | |
772 | } | |
773 | break; | |
bfa15a64 | 774 | |
bfa15a64 OZ |
775 | default: |
776 | bug("Unknown dynamic attribute type"); | |
a84b8b6e MM |
777 | } |
778 | ||
779 | f_rta_cow(fs); | |
780 | l->next = *fs->eattrs; | |
781 | *fs->eattrs = l; | |
782 | } | |
783 | } | |
784 | ||
9b46748d MM |
785 | INST(FI_EA_UNSET, 0, 0) { |
786 | DYNAMIC_ATTR; | |
787 | ACCESS_RTE; | |
788 | ACCESS_EATTRS; | |
789 | ||
790 | { | |
791 | struct ea_list *l = lp_alloc(fs->pool, sizeof(struct ea_list) + sizeof(eattr)); | |
792 | ||
793 | l->next = NULL; | |
794 | l->flags = EALF_SORTED; | |
795 | l->count = 1; | |
796 | l->attrs[0].id = da.ea_code; | |
797 | l->attrs[0].flags = 0; | |
8d65add6 | 798 | l->attrs[0].type = EAF_TYPE_UNDEF | EAF_ORIGINATED | EAF_FRESH; |
9b46748d MM |
799 | l->attrs[0].u.data = 0; |
800 | ||
801 | f_rta_cow(fs); | |
802 | l->next = *fs->eattrs; | |
803 | *fs->eattrs = l; | |
804 | } | |
805 | } | |
806 | ||
4c553c5a | 807 | INST(FI_PREF_GET, 0, 1) { |
f62a369f | 808 | ACCESS_RTE; |
4c553c5a | 809 | RESULT(T_INT, i, (*fs->rte)->pref); |
967b88d9 | 810 | } |
4c553c5a | 811 | |
a84b8b6e MM |
812 | INST(FI_PREF_SET, 1, 0) { |
813 | ACCESS_RTE; | |
814 | ARG(1,T_INT); | |
815 | if (v1.val.i > 0xFFFF) | |
816 | runtime( "Setting preference value out of bounds" ); | |
817 | f_rte_cow(fs); | |
818 | (*fs->rte)->pref = v1.val.i; | |
819 | } | |
820 | ||
4c553c5a | 821 | INST(FI_LENGTH, 1, 1) { /* Get length of */ |
f62a369f | 822 | ARG_ANY(1); |
f62a369f | 823 | switch(v1.type) { |
4c553c5a MM |
824 | case T_NET: RESULT(T_INT, i, net_pxlen(v1.val.net)); break; |
825 | case T_PATH: RESULT(T_INT, i, as_path_getlen(v1.val.ad)); break; | |
826 | case T_CLIST: RESULT(T_INT, i, int_set_get_size(v1.val.ad)); break; | |
827 | case T_ECLIST: RESULT(T_INT, i, ec_set_get_size(v1.val.ad)); break; | |
828 | case T_LCLIST: RESULT(T_INT, i, lc_set_get_size(v1.val.ad)); break; | |
f62a369f JMM |
829 | default: runtime( "Prefix, path, clist or eclist expected" ); |
830 | } | |
967b88d9 | 831 | } |
4c553c5a | 832 | |
ff2ca10c | 833 | INST(FI_NET_SRC, 1, 1) { /* Get src prefix */ |
f62a369f | 834 | ARG(1, T_NET); |
f62a369f | 835 | |
ff2ca10c | 836 | net_addr_union *net = (void *) v1.val.net; |
b40c0f02 | 837 | net_addr *src = falloc(sizeof(net_addr_ip6)); |
ff2ca10c OZ |
838 | const byte *part; |
839 | ||
840 | switch(v1.val.net->type) { | |
841 | case NET_FLOW4: | |
842 | part = flow4_get_part(&net->flow4, FLOW_TYPE_SRC_PREFIX); | |
843 | if (part) | |
844 | net_fill_ip4(src, flow_read_ip4_part(part), flow_read_pxlen(part)); | |
845 | else | |
846 | net_fill_ip4(src, IP4_NONE, 0); | |
847 | break; | |
848 | ||
849 | case NET_FLOW6: | |
850 | part = flow6_get_part(&net->flow6, FLOW_TYPE_SRC_PREFIX); | |
851 | if (part) | |
852 | net_fill_ip6(src, flow_read_ip6_part(part), flow_read_pxlen(part)); | |
853 | else | |
854 | net_fill_ip6(src, IP6_NONE, 0); | |
855 | break; | |
856 | ||
857 | case NET_IP6_SADR: | |
858 | net_fill_ip6(src, net->ip6_sadr.src_prefix, net->ip6_sadr.src_pxlen); | |
859 | break; | |
860 | ||
861 | default: | |
862 | runtime( "Flow or SADR expected" ); | |
863 | } | |
f62a369f | 864 | |
4c553c5a | 865 | RESULT(T_NET, net, src); |
967b88d9 | 866 | } |
4c553c5a | 867 | |
ff2ca10c OZ |
868 | INST(FI_NET_DST, 1, 1) { /* Get dst prefix */ |
869 | ARG(1, T_NET); | |
870 | ||
871 | net_addr_union *net = (void *) v1.val.net; | |
872 | net_addr *dst = falloc(sizeof(net_addr_ip6)); | |
873 | const byte *part; | |
874 | ||
875 | switch(v1.val.net->type) { | |
876 | case NET_FLOW4: | |
877 | part = flow4_get_part(&net->flow4, FLOW_TYPE_DST_PREFIX); | |
878 | if (part) | |
879 | net_fill_ip4(dst, flow_read_ip4_part(part), flow_read_pxlen(part)); | |
880 | else | |
881 | net_fill_ip4(dst, IP4_NONE, 0); | |
882 | break; | |
883 | ||
884 | case NET_FLOW6: | |
885 | part = flow6_get_part(&net->flow6, FLOW_TYPE_DST_PREFIX); | |
886 | if (part) | |
887 | net_fill_ip6(dst, flow_read_ip6_part(part), flow_read_pxlen(part)); | |
888 | else | |
889 | net_fill_ip6(dst, IP6_NONE, 0); | |
890 | break; | |
891 | ||
892 | case NET_IP6_SADR: | |
893 | net_fill_ip6(dst, net->ip6_sadr.dst_prefix, net->ip6_sadr.dst_pxlen); | |
894 | break; | |
895 | ||
896 | default: | |
897 | runtime( "Flow or SADR expected" ); | |
898 | } | |
899 | ||
900 | RESULT(T_NET, net, dst); | |
901 | } | |
902 | ||
4c553c5a | 903 | INST(FI_ROA_MAXLEN, 1, 1) { /* Get ROA max prefix length */ |
f62a369f JMM |
904 | ARG(1, T_NET); |
905 | if (!net_is_roa(v1.val.net)) | |
906 | runtime( "ROA expected" ); | |
907 | ||
4c553c5a | 908 | RESULT(T_INT, i, (v1.val.net->type == NET_ROA4) ? |
f62a369f | 909 | ((net_addr_roa4 *) v1.val.net)->max_pxlen : |
4c553c5a | 910 | ((net_addr_roa6 *) v1.val.net)->max_pxlen); |
967b88d9 | 911 | } |
4c553c5a | 912 | |
a2a268da AZ |
913 | INST(FI_ASN, 1, 1) { /* Get ROA ASN or community ASN part */ |
914 | ARG_ANY(1); | |
915 | RESULT_TYPE(T_INT); | |
916 | switch(v1.type) | |
917 | { | |
918 | case T_NET: | |
919 | if (!net_is_roa(v1.val.net)) | |
920 | runtime( "ROA expected" ); | |
f62a369f | 921 | |
a2a268da AZ |
922 | RESULT_(T_INT, i, (v1.val.net->type == NET_ROA4) ? |
923 | ((net_addr_roa4 *) v1.val.net)->asn : | |
924 | ((net_addr_roa6 *) v1.val.net)->asn); | |
925 | break; | |
926 | ||
927 | case T_PAIR: | |
928 | RESULT_(T_INT, i, v1.val.i >> 16); | |
929 | break; | |
930 | ||
931 | case T_LC: | |
932 | RESULT_(T_INT, i, v1.val.lc.asn); | |
933 | break; | |
934 | ||
935 | default: | |
936 | runtime( "Net, pair or lc expected" ); | |
937 | } | |
967b88d9 | 938 | } |
4c553c5a MM |
939 | |
940 | INST(FI_IP, 1, 1) { /* Convert prefix to ... */ | |
f62a369f | 941 | ARG(1, T_NET); |
4c553c5a | 942 | RESULT(T_IP, ip, net_prefix(v1.val.net)); |
967b88d9 | 943 | } |
4c553c5a MM |
944 | |
945 | INST(FI_ROUTE_DISTINGUISHER, 1, 1) { | |
f62a369f | 946 | ARG(1, T_NET); |
f62a369f JMM |
947 | if (!net_is_vpn(v1.val.net)) |
948 | runtime( "VPN address expected" ); | |
4c553c5a | 949 | RESULT(T_RD, ec, net_rd(v1.val.net)); |
967b88d9 | 950 | } |
f62a369f | 951 | |
4c553c5a MM |
952 | INST(FI_AS_PATH_FIRST, 1, 1) { /* Get first ASN from AS PATH */ |
953 | ARG(1, T_PATH); | |
10c4cd96 | 954 | u32 as = 0; |
f62a369f | 955 | as_path_get_first(v1.val.ad, &as); |
4c553c5a | 956 | RESULT(T_INT, i, as); |
967b88d9 | 957 | } |
f62a369f | 958 | |
bfa15a64 | 959 | INST(FI_AS_PATH_LAST, 1, 1) { /* Get last ASN from AS PATH */ |
4c553c5a | 960 | ARG(1, T_PATH); |
10c4cd96 | 961 | u32 as = 0; |
f62a369f | 962 | as_path_get_last(v1.val.ad, &as); |
4c553c5a | 963 | RESULT(T_INT, i, as); |
967b88d9 | 964 | } |
4c553c5a MM |
965 | |
966 | INST(FI_AS_PATH_LAST_NAG, 1, 1) { /* Get last ASN from non-aggregated part of AS PATH */ | |
f62a369f | 967 | ARG(1, T_PATH); |
4c553c5a MM |
968 | RESULT(T_INT, i, as_path_get_last_nonaggregated(v1.val.ad)); |
969 | } | |
f62a369f | 970 | |
a2a268da AZ |
971 | INST(FI_PAIR_DATA, 1, 1) { /* Get data part from the standard community */ |
972 | ARG(1, T_PAIR); | |
973 | RESULT(T_INT, i, v1.val.i & 0xFFFF); | |
974 | } | |
975 | ||
976 | INST(FI_LC_DATA1, 1, 1) { /* Get data1 part from the large community */ | |
977 | ARG(1, T_LC); | |
978 | RESULT(T_INT, i, v1.val.lc.ldp1); | |
979 | } | |
980 | ||
981 | INST(FI_LC_DATA2, 1, 1) { /* Get data2 part from the large community */ | |
982 | ARG(1, T_LC); | |
983 | RESULT(T_INT, i, v1.val.lc.ldp2); | |
984 | } | |
985 | ||
0e1fd7ea AZ |
986 | INST(FI_MIN, 1, 1) { /* Get minimum element from set */ |
987 | ARG_ANY(1); | |
988 | RESULT_TYPE(f_type_element_type(v1.type)); | |
989 | switch(v1.type) | |
990 | { | |
991 | case T_CLIST: | |
992 | { | |
993 | u32 val = 0; | |
994 | int_set_min(v1.val.ad, &val); | |
995 | RESULT_(T_PAIR, i, val); | |
996 | } | |
997 | break; | |
998 | ||
999 | case T_ECLIST: | |
1000 | { | |
1001 | u64 val = 0; | |
1002 | ec_set_min(v1.val.ad, &val); | |
1003 | RESULT_(T_EC, ec, val); | |
1004 | } | |
1005 | break; | |
1006 | ||
1007 | case T_LCLIST: | |
1008 | { | |
1009 | lcomm val = { 0, 0, 0 }; | |
1010 | lc_set_min(v1.val.ad, &val); | |
1011 | RESULT_(T_LC, lc, val); | |
1012 | } | |
1013 | break; | |
1014 | ||
1015 | default: | |
1016 | runtime( "Clist or lclist expected" ); | |
1017 | } | |
1018 | } | |
1019 | ||
1020 | INST(FI_MAX, 1, 1) { /* Get maximum element from set */ | |
1021 | ARG_ANY(1); | |
1022 | RESULT_TYPE(f_type_element_type(v1.type)); | |
1023 | switch(v1.type) | |
1024 | { | |
1025 | case T_CLIST: | |
1026 | { | |
1027 | u32 val = 0; | |
1028 | int_set_max(v1.val.ad, &val); | |
1029 | RESULT_(T_PAIR, i, val); | |
1030 | } | |
1031 | break; | |
1032 | ||
1033 | case T_ECLIST: | |
1034 | { | |
1035 | u64 val = 0; | |
1036 | ec_set_max(v1.val.ad, &val); | |
1037 | RESULT_(T_EC, ec, val); | |
1038 | } | |
1039 | break; | |
1040 | ||
1041 | case T_LCLIST: | |
1042 | { | |
1043 | lcomm val = { 0, 0, 0 }; | |
1044 | lc_set_max(v1.val.ad, &val); | |
1045 | RESULT_(T_LC, lc, val); | |
1046 | } | |
1047 | break; | |
1048 | ||
1049 | default: | |
1050 | runtime( "Clist or lclist expected" ); | |
1051 | } | |
1052 | } | |
1053 | ||
a84b8b6e | 1054 | INST(FI_RETURN, 1, 1) { |
b40c0f02 | 1055 | NEVER_CONSTANT; |
a84b8b6e MM |
1056 | /* Acquire the return value */ |
1057 | ARG_ANY(1); | |
1058 | uint retpos = fstk->vcnt; | |
1059 | ||
1060 | /* Drop every sub-block including ourselves */ | |
9e263550 OZ |
1061 | do fstk->ecnt--; |
1062 | while ((fstk->ecnt > 0) && !(fstk->estk[fstk->ecnt].emask & FE_RETURN)); | |
a84b8b6e MM |
1063 | |
1064 | /* Now we are at the caller frame; if no such, try to convert to accept/reject. */ | |
1065 | if (!fstk->ecnt) | |
9e263550 | 1066 | { |
a84b8b6e | 1067 | if (fstk->vstk[retpos].type == T_BOOL) |
9e263550 | 1068 | return (fstk->vstk[retpos].val.i) ? F_ACCEPT : F_REJECT; |
a84b8b6e MM |
1069 | else |
1070 | runtime("Can't return non-bool from non-function"); | |
9e263550 | 1071 | } |
a84b8b6e MM |
1072 | |
1073 | /* Set the value stack position, overwriting the former implicit void */ | |
1074 | fstk->vcnt = fstk->estk[fstk->ecnt].ventry - 1; | |
1075 | ||
1076 | /* Copy the return value */ | |
1077 | RESULT_VAL(fstk->vstk[retpos]); | |
1078 | } | |
1079 | ||
4c553c5a | 1080 | INST(FI_CALL, 0, 1) { |
b40c0f02 | 1081 | NEVER_CONSTANT; |
4c0c507b | 1082 | VARARG; |
96d757c1 | 1083 | SYMBOL; |
4f082dfa | 1084 | |
4c0c507b OZ |
1085 | FID_NEW_BODY() |
1086 | ASSERT(sym->class == SYM_FUNCTION); | |
1087 | ||
1088 | if (whati->varcount != sym->function->args) | |
1089 | cf_error("Function '%s' expects %u arguments, got %u arguments", | |
1090 | sym->name, sym->function->args, whati->varcount); | |
1091 | ||
1092 | /* Add implicit void slot for the return value */ | |
1093 | struct f_inst *tmp = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_VOID }); | |
1094 | tmp->next = whati->fvar; | |
1095 | whati->fvar = tmp; | |
1096 | what->size += tmp->size; | |
1097 | ||
3f477ccb | 1098 | FID_SAME_BODY() |
dfb3eb77 | 1099 | if (!(f1->sym->flags & SYM_FLAG_SAME)) |
3f477ccb | 1100 | return 0; |
d06a875b OZ |
1101 | |
1102 | FID_ITERATE_BODY() | |
1103 | BUFFER_PUSH(fit->lines) = whati->sym->function; | |
1104 | ||
3f477ccb MM |
1105 | FID_INTERPRET_BODY() |
1106 | ||
96d757c1 JMM |
1107 | /* Push the body on stack */ |
1108 | LINEX(sym->function); | |
4c0c507b | 1109 | curline.vbase = curline.ventry; |
ea4f55e3 | 1110 | curline.emask |= FE_RETURN; |
bfa15a64 | 1111 | |
4c0c507b OZ |
1112 | /* Arguments on stack */ |
1113 | fstk->vcnt += sym->function->args; | |
96d757c1 JMM |
1114 | |
1115 | /* Storage for local variables */ | |
1757a6fc MM |
1116 | memset(&(fstk->vstk[fstk->vcnt]), 0, sizeof(struct f_val) * sym->function->vars); |
1117 | fstk->vcnt += sym->function->vars; | |
967b88d9 | 1118 | } |
4c553c5a MM |
1119 | |
1120 | INST(FI_DROP_RESULT, 1, 0) { | |
b40c0f02 | 1121 | NEVER_CONSTANT; |
4c553c5a | 1122 | ARG_ANY(1); |
967b88d9 | 1123 | } |
4c553c5a | 1124 | |
4c553c5a | 1125 | INST(FI_SWITCH, 1, 0) { |
f62a369f | 1126 | ARG_ANY(1); |
26bfe59f | 1127 | |
f634adc7 | 1128 | FID_MEMBER(struct f_tree *, tree, [[!same_tree(f1->tree, f2->tree)]], "tree %p", item->tree); |
26bfe59f | 1129 | |
d06a875b OZ |
1130 | FID_ITERATE_BODY() |
1131 | tree_walk(whati->tree, f_add_tree_lines, fit); | |
1132 | ||
1133 | FID_INTERPRET_BODY() | |
32793ab6 MM |
1134 | const struct f_tree *t = find_tree(tree, &v1); |
1135 | if (!t) { | |
4c553c5a | 1136 | v1.type = T_VOID; |
32793ab6 MM |
1137 | t = find_tree(tree, &v1); |
1138 | if (!t) { | |
4c553c5a | 1139 | debug( "No else statement?\n"); |
b40c0f02 | 1140 | FID_HIC(,break,return NULL); |
f62a369f | 1141 | } |
f62a369f | 1142 | } |
4c553c5a MM |
1143 | /* It is actually possible to have t->data NULL */ |
1144 | ||
32793ab6 | 1145 | LINEX(t->data); |
967b88d9 | 1146 | } |
4c553c5a MM |
1147 | |
1148 | INST(FI_IP_MASK, 2, 1) { /* IP.MASK(val) */ | |
f62a369f JMM |
1149 | ARG(1, T_IP); |
1150 | ARG(2, T_INT); | |
4c553c5a | 1151 | RESULT(T_IP, ip, [[ ipa_is_ip4(v1.val.ip) ? |
f62a369f | 1152 | ipa_from_ip4(ip4_and(ipa_to_ip4(v1.val.ip), ip4_mkmask(v2.val.i))) : |
4c553c5a | 1153 | ipa_from_ip6(ip6_and(ipa_to_ip6(v1.val.ip), ip6_mkmask(v2.val.i))) ]]); |
967b88d9 | 1154 | } |
f62a369f | 1155 | |
4c553c5a | 1156 | INST(FI_PATH_PREPEND, 2, 1) { /* Path prepend */ |
f62a369f JMM |
1157 | ARG(1, T_PATH); |
1158 | ARG(2, T_INT); | |
b40c0f02 | 1159 | RESULT(T_PATH, ad, [[ as_path_prepend(fpool, v1.val.ad, v2.val.i) ]]); |
4c553c5a MM |
1160 | } |
1161 | ||
1162 | INST(FI_CLIST_ADD, 2, 1) { /* (Extended) Community list add */ | |
1163 | ARG_ANY(1); | |
1164 | ARG_ANY(2); | |
6fbcd891 OZ |
1165 | RESULT_TYPE(f1->type); |
1166 | ||
4c553c5a MM |
1167 | if (v1.type == T_PATH) |
1168 | runtime("Can't add to path"); | |
1169 | ||
1170 | else if (v1.type == T_CLIST) | |
1171 | { | |
1172 | /* Community (or cluster) list */ | |
1173 | struct f_val dummy; | |
1174 | ||
1175 | if ((v2.type == T_PAIR) || (v2.type == T_QUAD)) | |
6fbcd891 | 1176 | RESULT_(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, v2.val.i) ]]); |
4c553c5a MM |
1177 | /* IP->Quad implicit conversion */ |
1178 | else if (val_is_ip4(&v2)) | |
6fbcd891 | 1179 | RESULT_(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]); |
4c553c5a MM |
1180 | else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy)) |
1181 | runtime("Can't add set"); | |
1182 | else if (v2.type == T_CLIST) | |
6fbcd891 | 1183 | RESULT_(T_CLIST, ad, [[ int_set_union(fpool, v1.val.ad, v2.val.ad) ]]); |
4c553c5a MM |
1184 | else |
1185 | runtime("Can't add non-pair"); | |
1186 | } | |
f62a369f | 1187 | |
4c553c5a MM |
1188 | else if (v1.type == T_ECLIST) |
1189 | { | |
1190 | /* v2.val is either EC or EC-set */ | |
1191 | if ((v2.type == T_SET) && eclist_set_type(v2.val.t)) | |
1192 | runtime("Can't add set"); | |
1193 | else if (v2.type == T_ECLIST) | |
6fbcd891 | 1194 | RESULT_(T_ECLIST, ad, [[ ec_set_union(fpool, v1.val.ad, v2.val.ad) ]]); |
4c553c5a MM |
1195 | else if (v2.type != T_EC) |
1196 | runtime("Can't add non-ec"); | |
1197 | else | |
6fbcd891 | 1198 | RESULT_(T_ECLIST, ad, [[ ec_set_add(fpool, v1.val.ad, v2.val.ec) ]]); |
4c553c5a MM |
1199 | } |
1200 | ||
1201 | else if (v1.type == T_LCLIST) | |
1202 | { | |
1203 | /* v2.val is either LC or LC-set */ | |
1204 | if ((v2.type == T_SET) && lclist_set_type(v2.val.t)) | |
1205 | runtime("Can't add set"); | |
1206 | else if (v2.type == T_LCLIST) | |
6fbcd891 | 1207 | RESULT_(T_LCLIST, ad, [[ lc_set_union(fpool, v1.val.ad, v2.val.ad) ]]); |
4c553c5a MM |
1208 | else if (v2.type != T_LC) |
1209 | runtime("Can't add non-lc"); | |
1210 | else | |
6fbcd891 | 1211 | RESULT_(T_LCLIST, ad, [[ lc_set_add(fpool, v1.val.ad, v2.val.lc) ]]); |
4c553c5a MM |
1212 | |
1213 | } | |
1214 | ||
1215 | else | |
1216 | runtime("Can't add to non-[e|l]clist"); | |
967b88d9 | 1217 | } |
f62a369f | 1218 | |
4c553c5a | 1219 | INST(FI_CLIST_DEL, 2, 1) { /* (Extended) Community list add or delete */ |
f62a369f JMM |
1220 | ARG_ANY(1); |
1221 | ARG_ANY(2); | |
6fbcd891 OZ |
1222 | RESULT_TYPE(f1->type); |
1223 | ||
f62a369f JMM |
1224 | if (v1.type == T_PATH) |
1225 | { | |
4c553c5a | 1226 | const struct f_tree *set = NULL; |
f62a369f | 1227 | u32 key = 0; |
f62a369f JMM |
1228 | |
1229 | if (v2.type == T_INT) | |
1230 | key = v2.val.i; | |
1231 | else if ((v2.type == T_SET) && (v2.val.t->from.type == T_INT)) | |
1232 | set = v2.val.t; | |
1233 | else | |
1234 | runtime("Can't delete non-integer (set)"); | |
1235 | ||
6fbcd891 | 1236 | RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, set, key, 0) ]]); |
f62a369f | 1237 | } |
4c553c5a | 1238 | |
f62a369f JMM |
1239 | else if (v1.type == T_CLIST) |
1240 | { | |
1241 | /* Community (or cluster) list */ | |
1242 | struct f_val dummy; | |
f62a369f JMM |
1243 | |
1244 | if ((v2.type == T_PAIR) || (v2.type == T_QUAD)) | |
6fbcd891 | 1245 | RESULT_(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, v2.val.i) ]]); |
f62a369f | 1246 | /* IP->Quad implicit conversion */ |
4c553c5a | 1247 | else if (val_is_ip4(&v2)) |
6fbcd891 | 1248 | RESULT_(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]); |
4c553c5a | 1249 | else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy) || (v2.type == T_CLIST)) |
6fbcd891 | 1250 | RESULT_(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 0) ]]); |
f62a369f | 1251 | else |
4c553c5a | 1252 | runtime("Can't delete non-pair"); |
f62a369f | 1253 | } |
4c553c5a | 1254 | |
f62a369f JMM |
1255 | else if (v1.type == T_ECLIST) |
1256 | { | |
f62a369f | 1257 | /* v2.val is either EC or EC-set */ |
4c553c5a | 1258 | if ((v2.type == T_SET) && eclist_set_type(v2.val.t) || (v2.type == T_ECLIST)) |
6fbcd891 | 1259 | RESULT_(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 0) ]]); |
f62a369f | 1260 | else if (v2.type != T_EC) |
4c553c5a MM |
1261 | runtime("Can't delete non-ec"); |
1262 | else | |
6fbcd891 | 1263 | RESULT_(T_ECLIST, ad, [[ ec_set_del(fpool, v1.val.ad, v2.val.ec) ]]); |
f62a369f | 1264 | } |
4c553c5a | 1265 | |
f62a369f JMM |
1266 | else if (v1.type == T_LCLIST) |
1267 | { | |
f62a369f | 1268 | /* v2.val is either LC or LC-set */ |
4c553c5a | 1269 | if ((v2.type == T_SET) && lclist_set_type(v2.val.t) || (v2.type == T_LCLIST)) |
6fbcd891 | 1270 | RESULT_(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 0) ]]); |
f62a369f | 1271 | else if (v2.type != T_LC) |
4c553c5a MM |
1272 | runtime("Can't delete non-lc"); |
1273 | else | |
6fbcd891 | 1274 | RESULT_(T_LCLIST, ad, [[ lc_set_del(fpool, v1.val.ad, v2.val.lc) ]]); |
4c553c5a | 1275 | } |
f62a369f | 1276 | |
4c553c5a MM |
1277 | else |
1278 | runtime("Can't delete in non-[e|l]clist"); | |
1279 | } | |
f62a369f | 1280 | |
4c553c5a MM |
1281 | INST(FI_CLIST_FILTER, 2, 1) { /* (Extended) Community list add or delete */ |
1282 | ARG_ANY(1); | |
1283 | ARG_ANY(2); | |
6fbcd891 OZ |
1284 | RESULT_TYPE(f1->type); |
1285 | ||
4c553c5a MM |
1286 | if (v1.type == T_PATH) |
1287 | { | |
1288 | u32 key = 0; | |
f62a369f | 1289 | |
4c553c5a | 1290 | if ((v2.type == T_SET) && (v2.val.t->from.type == T_INT)) |
6fbcd891 | 1291 | RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, v2.val.t, key, 1) ]]); |
4c553c5a MM |
1292 | else |
1293 | runtime("Can't filter integer"); | |
f62a369f | 1294 | } |
f62a369f | 1295 | |
4c553c5a MM |
1296 | else if (v1.type == T_CLIST) |
1297 | { | |
1298 | /* Community (or cluster) list */ | |
1299 | struct f_val dummy; | |
f62a369f | 1300 | |
4c553c5a | 1301 | if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy) || (v2.type == T_CLIST)) |
6fbcd891 | 1302 | RESULT_(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 1) ]]); |
4c553c5a MM |
1303 | else |
1304 | runtime("Can't filter pair"); | |
1305 | } | |
1306 | ||
1307 | else if (v1.type == T_ECLIST) | |
f62a369f | 1308 | { |
4c553c5a MM |
1309 | /* v2.val is either EC or EC-set */ |
1310 | if ((v2.type == T_SET) && eclist_set_type(v2.val.t) || (v2.type == T_ECLIST)) | |
6fbcd891 | 1311 | RESULT_(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 1) ]]); |
4c553c5a MM |
1312 | else |
1313 | runtime("Can't filter ec"); | |
1314 | } | |
f62a369f | 1315 | |
4c553c5a MM |
1316 | else if (v1.type == T_LCLIST) |
1317 | { | |
1318 | /* v2.val is either LC or LC-set */ | |
1319 | if ((v2.type == T_SET) && lclist_set_type(v2.val.t) || (v2.type == T_LCLIST)) | |
6fbcd891 | 1320 | RESULT_(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 1) ]]); |
4c553c5a MM |
1321 | else |
1322 | runtime("Can't filter lc"); | |
f62a369f | 1323 | } |
4c553c5a | 1324 | |
f62a369f | 1325 | else |
4c553c5a MM |
1326 | runtime("Can't filter non-[e|l]clist"); |
1327 | } | |
f62a369f | 1328 | |
4c553c5a | 1329 | INST(FI_ROA_CHECK_IMPLICIT, 0, 1) { /* ROA Check */ |
b40c0f02 | 1330 | NEVER_CONSTANT; |
4c553c5a | 1331 | RTC(1); |
b40c0f02 | 1332 | struct rtable *table = rtc->table; |
4c553c5a MM |
1333 | ACCESS_RTE; |
1334 | ACCESS_EATTRS; | |
1335 | const net_addr *net = (*fs->rte)->net->n.addr; | |
f62a369f | 1336 | |
4c553c5a MM |
1337 | /* We ignore temporary attributes, probably not a problem here */ |
1338 | /* 0x02 is a value of BA_AS_PATH, we don't want to include BGP headers */ | |
1339 | eattr *e = ea_find(*fs->eattrs, EA_CODE(PROTOCOL_BGP, 0x02)); | |
f62a369f | 1340 | |
4c553c5a MM |
1341 | if (!e || ((e->type & EAF_TYPE_MASK) != EAF_TYPE_AS_PATH)) |
1342 | runtime("Missing AS_PATH attribute"); | |
1343 | ||
1344 | u32 as = 0; | |
1345 | as_path_get_last(e->u.ptr, &as); | |
f62a369f | 1346 | |
f62a369f JMM |
1347 | if (!table) |
1348 | runtime("Missing ROA table"); | |
1349 | ||
1350 | if (table->addr_type != NET_ROA4 && table->addr_type != NET_ROA6) | |
1351 | runtime("Table type must be either ROA4 or ROA6"); | |
1352 | ||
4c553c5a MM |
1353 | if (table->addr_type != (net->type == NET_IP4 ? NET_ROA4 : NET_ROA6)) |
1354 | RESULT(T_ENUM_ROA, i, ROA_UNKNOWN); /* Prefix and table type mismatch */ | |
1355 | else | |
1356 | RESULT(T_ENUM_ROA, i, [[ net_roa_check(table, net, as) ]]); | |
1357 | } | |
1358 | ||
1359 | INST(FI_ROA_CHECK_EXPLICIT, 2, 1) { /* ROA Check */ | |
b40c0f02 | 1360 | NEVER_CONSTANT; |
4c553c5a MM |
1361 | ARG(1, T_NET); |
1362 | ARG(2, T_INT); | |
1363 | RTC(3); | |
b40c0f02 | 1364 | struct rtable *table = rtc->table; |
4c553c5a MM |
1365 | |
1366 | u32 as = v2.val.i; | |
1367 | ||
1368 | if (!table) | |
1369 | runtime("Missing ROA table"); | |
1370 | ||
1371 | if (table->addr_type != NET_ROA4 && table->addr_type != NET_ROA6) | |
1372 | runtime("Table type must be either ROA4 or ROA6"); | |
f62a369f JMM |
1373 | |
1374 | if (table->addr_type != (v1.val.net->type == NET_IP4 ? NET_ROA4 : NET_ROA6)) | |
4c553c5a | 1375 | RESULT(T_ENUM_ROA, i, ROA_UNKNOWN); /* Prefix and table type mismatch */ |
f62a369f | 1376 | else |
4c553c5a | 1377 | RESULT(T_ENUM_ROA, i, [[ net_roa_check(table, v1.val.net, as) ]]); |
f62a369f | 1378 | |
967b88d9 | 1379 | } |
f62a369f | 1380 | |
4c553c5a | 1381 | INST(FI_FORMAT, 1, 0) { /* Format */ |
f62a369f | 1382 | ARG_ANY(1); |
b40c0f02 | 1383 | RESULT(T_STRING, s, val_format_str(fpool, &v1)); |
967b88d9 | 1384 | } |
f62a369f | 1385 | |
4c553c5a | 1386 | INST(FI_ASSERT, 1, 0) { /* Birdtest Assert */ |
b40c0f02 | 1387 | NEVER_CONSTANT; |
f62a369f | 1388 | ARG(1, T_BOOL); |
550a6488 | 1389 | |
f634adc7 | 1390 | FID_MEMBER(char *, s, [[strcmp(f1->s, f2->s)]], "string %s", item->s); |
b40c0f02 MM |
1391 | |
1392 | ASSERT(s); | |
26bfe59f | 1393 | |
c0e958e0 MM |
1394 | if (!bt_assert_hook) |
1395 | runtime("No bt_assert hook registered, can't assert"); | |
1396 | ||
b40c0f02 | 1397 | bt_assert_hook(v1.val.i, what); |
967b88d9 | 1398 | } |