]>
Commit | Line | Data |
---|---|---|
f62a369f MM |
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 | * | |
e1ac6f1e MM |
10 | * Filter instructions. You shall define your instruction only here |
11 | * and nowhere else. | |
12 | * | |
13 | * Beware. This file is interpreted by M4 macros. These macros | |
14 | * may be more stupid than you could imagine. If something strange | |
15 | * happens after changing this file, compare the results before and | |
16 | * after your change (see the Makefile to find out where the results are) | |
17 | * and see what really happened. | |
18 | * | |
19 | * This file is not directly a C source code -> it is a generator input | |
20 | * for several C sources; every instruction block gets expanded into many | |
21 | * different places. | |
22 | * | |
23 | * What is the syntax here? | |
24 | * m4_dnl INST(FI_NOP, in, out) { enum value, input args, output args | |
25 | * m4_dnl ARG(num, type); argument, its id (in data fields) and type | |
26 | * m4_dnl ARG_ANY(num); argument with no type check | |
27 | * m4_dnl LINE(num, unused); this argument has to be converted to its own f_line | |
28 | * m4_dnl ECS; extended community subtype | |
29 | * m4_dnl COUNT(unused); simply a uint | |
30 | * m4_dnl SYMBOL(unused); symbol handed from config | |
31 | * m4_dnl FRET(unused); filter return value | |
32 | * m4_dnl STATIC_ATTR; static attribute definition | |
33 | * m4_dnl DYNAMIC_ATTR; dynamic attribute definition | |
34 | * m4_dnl RTC; route table config | |
35 | * m4_dnl TREE; a tree | |
36 | * m4_dnl ACCESS_RTE; this instruction needs route | |
37 | * m4_dnl ACCESS_EATTRS; this instruction needs extended attributes | |
38 | * m4_dnl RESULT(type, union-field, value); putting this on value stack | |
39 | * m4_dnl RESULT_OK; legalize what already is on the value stack | |
40 | * m4_dnl } | |
41 | * | |
42 | * Other code is just copied into the interpreter part. | |
43 | * | |
44 | * If you want to write something really special, see FI_CALL | |
45 | * or FI_CONSTANT or whatever else to see how to use the FID_* | |
46 | * macros. | |
f62a369f MM |
47 | */ |
48 | ||
49 | /* Binary operators */ | |
4c553c5a MM |
50 | INST(FI_ADD, 2, 1) { |
51 | ARG(1,T_INT); | |
52 | ARG(2,T_INT); | |
53 | res.val.i += v2.val.i; | |
54 | RESULT_OK; | |
55 | } | |
56 | INST(FI_SUBTRACT, 2, 1) { | |
57 | ARG(1,T_INT); | |
58 | ARG(2,T_INT); | |
59 | res.val.i -= v2.val.i; | |
60 | RESULT_OK; | |
61 | } | |
62 | INST(FI_MULTIPLY, 2, 1) { | |
63 | ARG(1,T_INT); | |
64 | ARG(2,T_INT); | |
65 | res.val.i *= v2.val.i; | |
66 | RESULT_OK; | |
67 | } | |
68 | INST(FI_DIVIDE, 2, 1) { | |
69 | ARG(1,T_INT); | |
70 | ARG(2,T_INT); | |
71 | if (v2.val.i == 0) runtime( "Mother told me not to divide by 0" ); | |
72 | res.val.i /= v2.val.i; | |
73 | RESULT_OK; | |
74 | } | |
75 | INST(FI_AND, 1, 1) { | |
76 | ARG(1,T_BOOL); | |
8e8b1fe4 | 77 | if (res.val.i) |
4c553c5a MM |
78 | LINE(2,0); |
79 | else | |
80 | RESULT_OK; | |
967b88d9 | 81 | } |
4c553c5a MM |
82 | INST(FI_OR, 1, 1) { |
83 | ARG(1,T_BOOL); | |
8e8b1fe4 | 84 | if (!res.val.i) |
4c553c5a MM |
85 | LINE(2,0); |
86 | else | |
87 | RESULT_OK; | |
967b88d9 | 88 | } |
4c553c5a | 89 | INST(FI_PAIR_CONSTRUCT, 2, 1) { |
f62a369f MM |
90 | ARG(1,T_INT); |
91 | ARG(2,T_INT); | |
4c553c5a MM |
92 | uint u1 = v1.val.i; |
93 | uint u2 = v2.val.i; | |
f62a369f MM |
94 | if ((u1 > 0xFFFF) || (u2 > 0xFFFF)) |
95 | runtime( "Can't operate with value out of bounds in pair constructor" ); | |
4c553c5a | 96 | RESULT(T_PAIR, i, (u1 << 16) | u2); |
967b88d9 | 97 | } |
4c553c5a MM |
98 | INST(FI_EC_CONSTRUCT, 2, 1) { |
99 | ARG_ANY(1); | |
100 | ARG(2, T_INT); | |
101 | ECS; | |
f62a369f | 102 | |
4c553c5a MM |
103 | int check, ipv4_used; |
104 | u32 key, val; | |
f62a369f | 105 | |
4c553c5a MM |
106 | if (v1.type == T_INT) { |
107 | ipv4_used = 0; key = v1.val.i; | |
108 | } | |
109 | else if (v1.type == T_QUAD) { | |
110 | ipv4_used = 1; key = v1.val.i; | |
111 | } | |
112 | /* IP->Quad implicit conversion */ | |
113 | else if (val_is_ip4(&v1)) { | |
114 | ipv4_used = 1; key = ipa_to_u32(v1.val.ip); | |
115 | } | |
116 | else | |
117 | runtime("Argument 1 of instruction FI_EC_CONSTRUCT must be integer or IPv4 address, got 0x%02x"); | |
f62a369f | 118 | |
4c553c5a | 119 | val = v2.val.i; |
f62a369f | 120 | |
4c553c5a MM |
121 | if (ecs == EC_GENERIC) { |
122 | check = 0; RESULT(T_EC, ec, ec_generic(key, val)); | |
123 | } | |
124 | else if (ipv4_used) { | |
125 | check = 1; RESULT(T_EC, ec, ec_ip4(ecs, key, val)); | |
967b88d9 | 126 | } |
4c553c5a MM |
127 | else if (key < 0x10000) { |
128 | check = 0; RESULT(T_EC, ec, ec_as2(ecs, key, val)); | |
129 | } | |
130 | else { | |
131 | check = 1; RESULT(T_EC, ec, ec_as4(ecs, key, val)); | |
f62a369f MM |
132 | } |
133 | ||
4c553c5a MM |
134 | if (check && (val > 0xFFFF)) |
135 | runtime("Value %u > %u out of bounds in EC constructor", val, 0xFFFF); | |
136 | } | |
f62a369f | 137 | |
4c553c5a MM |
138 | INST(FI_LC_CONSTRUCT, 3, 1) { |
139 | ARG(1, T_INT); | |
140 | ARG(2, T_INT); | |
141 | ARG(3, T_INT); | |
142 | RESULT(T_LC, lc, [[(lcomm) { v1.val.i, v2.val.i, v3.val.i }]]); | |
143 | } | |
f62a369f | 144 | |
4c553c5a MM |
145 | INST(FI_PATHMASK_CONSTRUCT, 0, 1) { |
146 | ARG_ANY(1); | |
147 | COUNT(2); | |
9b46748d | 148 | |
4f082dfa MM |
149 | FID_NEW_BODY |
150 | uint len = 0; | |
151 | uint dyn = 0; | |
152 | for (const struct f_inst *tt = f1; tt; tt = tt->next, len++) | |
153 | if (tt->fi_code != FI_CONSTANT) | |
154 | dyn++; | |
9b46748d | 155 | |
4f082dfa | 156 | what->count = len; |
d1039926 | 157 | FID_ALL |
9b46748d | 158 | |
ea4f55e3 MM |
159 | if (vstk.cnt < whati->count) /* TODO: make this check systematic */ |
160 | runtime("Construction of BGP path mask from %u elements must have at least that number of elements", whati->count); | |
4c553c5a | 161 | |
ea4f55e3 MM |
162 | struct f_path_mask *pm = lp_alloc(fs->pool, sizeof(struct f_path_mask) + whati->count * sizeof(struct f_path_mask_item)); |
163 | for (uint i=0; i<whati->count; i++) { | |
164 | #define pv vstk.val[vstk.cnt - whati->count + i] | |
4c553c5a MM |
165 | switch (pv.type) { |
166 | case T_PATH_MASK_ITEM: | |
167 | pm->item[i] = pv.val.pmi; | |
168 | break; | |
169 | case T_INT: | |
170 | pm->item[i] = (struct f_path_mask_item) { | |
171 | .asn = pv.val.i, | |
172 | .kind = PM_ASN, | |
173 | }; | |
174 | break; | |
175 | default: | |
176 | runtime( "Error resolving path mask template: value not an integer" ); | |
177 | } | |
f62a369f MM |
178 | } |
179 | ||
ea4f55e3 MM |
180 | vstk.cnt -= whati->count; |
181 | pm->len = whati->count; | |
f62a369f | 182 | |
4c553c5a MM |
183 | RESULT(T_PATH_MASK, path_mask, pm); |
184 | } | |
f62a369f MM |
185 | |
186 | /* Relational operators */ | |
187 | ||
4c553c5a | 188 | INST(FI_NEQ, 2, 1) { |
c5774939 MM |
189 | ARG_ANY(1); |
190 | ARG_ANY(2); | |
4c553c5a | 191 | RESULT(T_BOOL, i, !val_same(&v1, &v2)); |
967b88d9 | 192 | } |
f62a369f | 193 | |
4c553c5a | 194 | INST(FI_EQ, 2, 1) { |
c5774939 MM |
195 | ARG_ANY(1); |
196 | ARG_ANY(2); | |
4c553c5a | 197 | RESULT(T_BOOL, i, val_same(&v1, &v2)); |
967b88d9 | 198 | } |
c5774939 | 199 | |
4c553c5a | 200 | INST(FI_LT, 2, 1) { |
c5774939 MM |
201 | ARG_ANY(1); |
202 | ARG_ANY(2); | |
4c553c5a | 203 | int i = val_compare(&v1, &v2); |
52893045 | 204 | if (i == F_CMP_ERROR) |
c5774939 | 205 | runtime( "Can't compare values of incompatible types" ); |
4c553c5a | 206 | RESULT(T_BOOL, i, (i == -1)); |
967b88d9 | 207 | } |
f62a369f | 208 | |
4c553c5a | 209 | INST(FI_LTE, 2, 1) { |
c5774939 MM |
210 | ARG_ANY(1); |
211 | ARG_ANY(2); | |
4c553c5a | 212 | int i = val_compare(&v1, &v2); |
52893045 | 213 | if (i == F_CMP_ERROR) |
c5774939 | 214 | runtime( "Can't compare values of incompatible types" ); |
4c553c5a | 215 | RESULT(T_BOOL, i, (i != 1)); |
967b88d9 | 216 | } |
f62a369f | 217 | |
4c553c5a MM |
218 | INST(FI_NOT, 1, 1) { |
219 | ARG(1,T_BOOL); | |
220 | RESULT(T_BOOL, i, !v1.val.i); | |
967b88d9 | 221 | } |
f62a369f | 222 | |
4c553c5a | 223 | INST(FI_MATCH, 2, 1) { |
f62a369f MM |
224 | ARG_ANY(1); |
225 | ARG_ANY(2); | |
4c553c5a | 226 | int i = val_in_range(&v1, &v2); |
52893045 | 227 | if (i == F_CMP_ERROR) |
f62a369f | 228 | runtime( "~ applied on unknown type pair" ); |
4c553c5a | 229 | RESULT(T_BOOL, i, !!i); |
967b88d9 | 230 | } |
f62a369f | 231 | |
4c553c5a | 232 | INST(FI_NOT_MATCH, 2, 1) { |
f62a369f MM |
233 | ARG_ANY(1); |
234 | ARG_ANY(2); | |
4c553c5a | 235 | int i = val_in_range(&v1, &v2); |
fe503c7c | 236 | if (i == F_CMP_ERROR) |
f62a369f | 237 | runtime( "!~ applied on unknown type pair" ); |
4c553c5a | 238 | RESULT(T_BOOL, i, !i); |
967b88d9 | 239 | } |
f62a369f | 240 | |
4c553c5a | 241 | INST(FI_DEFINED, 1, 1) { |
f62a369f | 242 | ARG_ANY(1); |
4c553c5a | 243 | RESULT(T_BOOL, i, (v1.type != T_VOID) && !undef_value(v1)); |
967b88d9 | 244 | } |
4c553c5a MM |
245 | |
246 | INST(FI_TYPE, 1, 1) { | |
f62a369f MM |
247 | ARG_ANY(1); /* There may be more types supporting this operation */ |
248 | switch (v1.type) | |
249 | { | |
250 | case T_NET: | |
4c553c5a | 251 | RESULT(T_ENUM_NETTYPE, i, v1.val.net->type); |
f62a369f MM |
252 | break; |
253 | default: | |
254 | runtime( "Can't determine type of this item" ); | |
255 | } | |
967b88d9 | 256 | } |
4c553c5a MM |
257 | |
258 | INST(FI_IS_V4, 1, 1) { | |
f62a369f | 259 | ARG(1, T_IP); |
4c553c5a | 260 | RESULT(T_BOOL, i, ipa_is_ip4(v1.val.ip)); |
967b88d9 | 261 | } |
f62a369f | 262 | |
4c553c5a | 263 | /* Set to indirect value prepared in v1 */ |
96d757c1 | 264 | INST(FI_VAR_SET, 1, 0) { |
f62a369f | 265 | ARG_ANY(2); |
4c553c5a MM |
266 | SYMBOL(1); |
267 | if ((sym->class != (SYM_VARIABLE | v1.type)) && (v1.type != T_VOID)) | |
f62a369f MM |
268 | { |
269 | /* IP->Quad implicit conversion */ | |
4c553c5a | 270 | if ((sym->class == (SYM_VARIABLE | T_QUAD)) && val_is_ip4(&v1)) |
96d757c1 | 271 | v1 = (struct f_val) { |
4c553c5a MM |
272 | .type = T_QUAD, |
273 | .val.i = ipa_to_u32(v1.val.ip), | |
96d757c1 MM |
274 | }; |
275 | else | |
276 | runtime( "Assigning to variable of incompatible type" ); | |
f62a369f | 277 | } |
96d757c1 MM |
278 | |
279 | vstk.val[curline.vbase + sym->offset] = v1; | |
280 | } | |
281 | ||
282 | INST(FI_VAR_GET, 0, 1) { | |
283 | SYMBOL(1); | |
284 | res = vstk.val[curline.vbase + sym->offset]; | |
285 | RESULT_OK; | |
967b88d9 | 286 | } |
f62a369f | 287 | |
7f0ac737 | 288 | /* some constants have value in a[1], some in *a[0].p, strange. */ |
4c553c5a | 289 | INST(FI_CONSTANT, 0, 1) { /* integer (or simple type) constant, string, set, or prefix_set */ |
ea4f55e3 MM |
290 | FID_LINE_IN |
291 | struct f_val val; | |
292 | FID_STRUCT_IN | |
293 | struct f_val val; | |
294 | FID_NEW_ARGS | |
295 | , const struct f_val val | |
296 | FID_NEW_BODY | |
297 | what->val = val; | |
23e3b1e6 | 298 | FID_LINEARIZE_BODY |
ea4f55e3 MM |
299 | item->val = what->val; |
300 | FID_SAME_BODY | |
301 | if (!val_same(&(f1->val), &(f2->val))) return 0; | |
302 | FID_DUMP_BODY | |
303 | debug("%svalue %s\n", INDENT, val_dump(&item->val)); | |
d1039926 | 304 | FID_ALL |
ea4f55e3 MM |
305 | |
306 | res = whati->val; | |
4c553c5a | 307 | RESULT_OK; |
967b88d9 | 308 | } |
96d757c1 | 309 | INST(FI_CONSTANT_DEFINED, 0, 1) { |
ea4f55e3 MM |
310 | FID_STRUCT_IN |
311 | const struct symbol *sym; | |
312 | FID_LINE_IN | |
313 | const struct symbol *sym; | |
314 | const struct f_val *valp; | |
315 | FID_NEW_ARGS | |
316 | , const struct symbol *sym | |
317 | FID_NEW_BODY | |
318 | what->sym = sym; | |
23e3b1e6 | 319 | FID_LINEARIZE_BODY |
ea4f55e3 MM |
320 | item->valp = (item->sym = what->sym)->val; |
321 | FID_SAME_BODY | |
96d757c1 | 322 | if (strcmp(f1->sym->name, f2->sym->name) || !val_same(f1->sym->val, f2->sym->val)) return 0; |
ea4f55e3 | 323 | FID_DUMP_BODY |
96d757c1 | 324 | debug("%sconstant %s with value %s\n", INDENT, item->sym->name, val_dump(item->valp)); |
d1039926 | 325 | FID_ALL |
ea4f55e3 MM |
326 | |
327 | res = *whati->valp; | |
4c553c5a | 328 | RESULT_OK; |
967b88d9 | 329 | } |
4c553c5a | 330 | INST(FI_PRINT, 1, 0) { |
f62a369f | 331 | ARG_ANY(1); |
4c553c5a | 332 | val_format(&(v1), &fs->buf); |
967b88d9 | 333 | } |
4c553c5a MM |
334 | INST(FI_CONDITION, 1, 0) { |
335 | ARG(1, T_BOOL); | |
224b77d4 | 336 | if (res.val.i) |
4c553c5a | 337 | LINE(2,0); |
224b77d4 | 338 | else |
4c553c5a | 339 | LINE(3,1); |
967b88d9 | 340 | } |
4c553c5a | 341 | INST(FI_PRINT_AND_DIE, 0, 0) { |
23e3b1e6 | 342 | FID_LINEARIZE_BODY |
dd4d4095 MM |
343 | { |
344 | uint opos = pos; | |
d1039926 | 345 | FID_ALL |
9b46748d MM |
346 | |
347 | ARG_ANY(1); | |
348 | ||
23e3b1e6 | 349 | FID_LINEARIZE_BODY |
dd4d4095 MM |
350 | if (opos < pos) |
351 | dest->items[pos].flags |= FIF_PRINTED; | |
352 | } | |
d1039926 | 353 | FID_ALL |
4c553c5a MM |
354 | |
355 | FRET(2); | |
356 | ||
357 | if ((fret == F_NOP || (fret != F_NONL && (what->flags & FIF_PRINTED))) && | |
f62a369f MM |
358 | !(fs->flags & FF_SILENT)) |
359 | log_commit(*L_INFO, &fs->buf); | |
360 | ||
4c553c5a | 361 | switch (fret) { |
f62a369f MM |
362 | case F_QUITBIRD: |
363 | die( "Filter asked me to die" ); | |
364 | case F_ACCEPT: | |
365 | /* Should take care about turning ACCEPT into MODIFY */ | |
366 | case F_ERROR: | |
367 | case F_REJECT: /* FIXME (noncritical) Should print complete route along with reason to reject route */ | |
4c553c5a | 368 | return fret; /* We have to return now, no more processing. */ |
f62a369f MM |
369 | case F_NONL: |
370 | case F_NOP: | |
371 | break; | |
372 | default: | |
373 | bug( "unknown return type: Can't happen"); | |
374 | } | |
967b88d9 | 375 | } |
4c553c5a MM |
376 | |
377 | INST(FI_RTA_GET, 0, 1) { /* rta access */ | |
f62a369f | 378 | { |
4c553c5a | 379 | STATIC_ATTR; |
f62a369f MM |
380 | ACCESS_RTE; |
381 | struct rta *rta = (*fs->rte)->attrs; | |
f62a369f | 382 | |
4c553c5a | 383 | switch (sa.sa_code) |
f62a369f | 384 | { |
4c553c5a MM |
385 | case SA_FROM: RESULT(sa.f_type, ip, rta->from); break; |
386 | case SA_GW: RESULT(sa.f_type, ip, rta->nh.gw); break; | |
387 | case SA_NET: RESULT(sa.f_type, net, (*fs->rte)->net->n.addr); break; | |
388 | case SA_PROTO: RESULT(sa.f_type, s, rta->src->proto->name); break; | |
389 | case SA_SOURCE: RESULT(sa.f_type, i, rta->source); break; | |
390 | case SA_SCOPE: RESULT(sa.f_type, i, rta->scope); break; | |
391 | case SA_DEST: RESULT(sa.f_type, i, rta->dest); break; | |
392 | case SA_IFNAME: RESULT(sa.f_type, s, rta->nh.iface ? rta->nh.iface->name : ""); break; | |
393 | case SA_IFINDEX: RESULT(sa.f_type, i, rta->nh.iface ? rta->nh.iface->index : 0); break; | |
f62a369f MM |
394 | |
395 | default: | |
4c553c5a | 396 | bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code); |
f62a369f MM |
397 | } |
398 | } | |
967b88d9 | 399 | } |
4c553c5a MM |
400 | |
401 | INST(FI_RTA_SET, 1, 0) { | |
f62a369f MM |
402 | ACCESS_RTE; |
403 | ARG_ANY(1); | |
c0e958e0 | 404 | STATIC_ATTR; |
4c553c5a | 405 | if (sa.f_type != v1.type) |
f62a369f MM |
406 | runtime( "Attempt to set static attribute to incompatible type" ); |
407 | ||
408 | f_rta_cow(fs); | |
409 | { | |
410 | struct rta *rta = (*fs->rte)->attrs; | |
411 | ||
4c553c5a | 412 | switch (sa.sa_code) |
f62a369f MM |
413 | { |
414 | case SA_FROM: | |
415 | rta->from = v1.val.ip; | |
416 | break; | |
417 | ||
418 | case SA_GW: | |
419 | { | |
420 | ip_addr ip = v1.val.ip; | |
421 | neighbor *n = neigh_find(rta->src->proto, ip, NULL, 0); | |
422 | if (!n || (n->scope == SCOPE_HOST)) | |
423 | runtime( "Invalid gw address" ); | |
424 | ||
425 | rta->dest = RTD_UNICAST; | |
426 | rta->nh.gw = ip; | |
427 | rta->nh.iface = n->iface; | |
428 | rta->nh.next = NULL; | |
429 | rta->hostentry = NULL; | |
430 | } | |
431 | break; | |
432 | ||
433 | case SA_SCOPE: | |
434 | rta->scope = v1.val.i; | |
435 | break; | |
436 | ||
437 | case SA_DEST: | |
4c553c5a MM |
438 | { |
439 | int i = v1.val.i; | |
440 | if ((i != RTD_BLACKHOLE) && (i != RTD_UNREACHABLE) && (i != RTD_PROHIBIT)) | |
441 | runtime( "Destination can be changed only to blackhole, unreachable or prohibit" ); | |
442 | ||
443 | rta->dest = i; | |
444 | rta->nh.gw = IPA_NONE; | |
445 | rta->nh.iface = NULL; | |
446 | rta->nh.next = NULL; | |
447 | rta->hostentry = NULL; | |
448 | } | |
f62a369f MM |
449 | break; |
450 | ||
451 | case SA_IFNAME: | |
452 | { | |
453 | struct iface *ifa = if_find_by_name(v1.val.s); | |
454 | if (!ifa) | |
455 | runtime( "Invalid iface name" ); | |
456 | ||
457 | rta->dest = RTD_UNICAST; | |
458 | rta->nh.gw = IPA_NONE; | |
459 | rta->nh.iface = ifa; | |
460 | rta->nh.next = NULL; | |
461 | rta->hostentry = NULL; | |
462 | } | |
463 | break; | |
464 | ||
465 | default: | |
4c553c5a | 466 | bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code); |
f62a369f MM |
467 | } |
468 | } | |
967b88d9 | 469 | } |
4c553c5a MM |
470 | |
471 | INST(FI_EA_GET, 0, 1) { /* Access to extended attributes */ | |
472 | DYNAMIC_ATTR; | |
f62a369f MM |
473 | ACCESS_RTE; |
474 | ACCESS_EATTRS; | |
475 | { | |
4c553c5a | 476 | eattr *e = ea_find(*fs->eattrs, da.ea_code); |
f62a369f MM |
477 | |
478 | if (!e) { | |
479 | /* A special case: undefined as_path looks like empty as_path */ | |
4c553c5a MM |
480 | if (da.type == EAF_TYPE_AS_PATH) { |
481 | RESULT(T_PATH, ad, &null_adata); | |
f62a369f MM |
482 | break; |
483 | } | |
484 | ||
485 | /* The same special case for int_set */ | |
4c553c5a MM |
486 | if (da.type == EAF_TYPE_INT_SET) { |
487 | RESULT(T_CLIST, ad, &null_adata); | |
f62a369f MM |
488 | break; |
489 | } | |
490 | ||
491 | /* The same special case for ec_set */ | |
4c553c5a MM |
492 | if (da.type == EAF_TYPE_EC_SET) { |
493 | RESULT(T_ECLIST, ad, &null_adata); | |
f62a369f MM |
494 | break; |
495 | } | |
496 | ||
497 | /* The same special case for lc_set */ | |
4c553c5a MM |
498 | if (da.type == EAF_TYPE_LC_SET) { |
499 | RESULT(T_LCLIST, ad, &null_adata); | |
f62a369f MM |
500 | break; |
501 | } | |
502 | ||
503 | /* Undefined value */ | |
504 | res.type = T_VOID; | |
4c553c5a | 505 | RESULT_OK; |
f62a369f MM |
506 | break; |
507 | } | |
508 | ||
509 | switch (e->type & EAF_TYPE_MASK) { | |
510 | case EAF_TYPE_INT: | |
4c553c5a | 511 | RESULT(da.f_type, i, e->u.data); |
f62a369f MM |
512 | break; |
513 | case EAF_TYPE_ROUTER_ID: | |
4c553c5a | 514 | RESULT(T_QUAD, i, e->u.data); |
f62a369f MM |
515 | break; |
516 | case EAF_TYPE_OPAQUE: | |
4c553c5a | 517 | RESULT(T_ENUM_EMPTY, i, 0); |
f62a369f MM |
518 | break; |
519 | case EAF_TYPE_IP_ADDRESS: | |
4c553c5a | 520 | RESULT(T_IP, ip, *((ip_addr *) e->u.ptr->data)); |
f62a369f MM |
521 | break; |
522 | case EAF_TYPE_AS_PATH: | |
4c553c5a | 523 | RESULT(T_PATH, ad, e->u.ptr); |
f62a369f MM |
524 | break; |
525 | case EAF_TYPE_BITFIELD: | |
4c553c5a | 526 | RESULT(T_BOOL, i, !!(e->u.data & (1u << da.bit))); |
f62a369f MM |
527 | break; |
528 | case EAF_TYPE_INT_SET: | |
4c553c5a | 529 | RESULT(T_CLIST, ad, e->u.ptr); |
f62a369f MM |
530 | break; |
531 | case EAF_TYPE_EC_SET: | |
4c553c5a | 532 | RESULT(T_ECLIST, ad, e->u.ptr); |
f62a369f MM |
533 | break; |
534 | case EAF_TYPE_LC_SET: | |
4c553c5a | 535 | RESULT(T_LCLIST, ad, e->u.ptr); |
f62a369f MM |
536 | break; |
537 | case EAF_TYPE_UNDEF: | |
538 | res.type = T_VOID; | |
4c553c5a | 539 | RESULT_OK; |
f62a369f MM |
540 | break; |
541 | default: | |
4c553c5a | 542 | bug("Unknown dynamic attribute type"); |
f62a369f MM |
543 | } |
544 | } | |
967b88d9 | 545 | } |
4c553c5a MM |
546 | |
547 | INST(FI_EA_SET, 1, 0) { | |
f62a369f MM |
548 | ACCESS_RTE; |
549 | ACCESS_EATTRS; | |
550 | ARG_ANY(1); | |
c0e958e0 | 551 | DYNAMIC_ATTR; |
f62a369f MM |
552 | { |
553 | struct ea_list *l = lp_alloc(fs->pool, sizeof(struct ea_list) + sizeof(eattr)); | |
f62a369f MM |
554 | |
555 | l->next = NULL; | |
556 | l->flags = EALF_SORTED; | |
557 | l->count = 1; | |
4c553c5a | 558 | l->attrs[0].id = da.ea_code; |
f62a369f | 559 | l->attrs[0].flags = 0; |
4c553c5a | 560 | l->attrs[0].type = da.type | EAF_ORIGINATED | EAF_FRESH; |
f62a369f | 561 | |
4c553c5a | 562 | switch (da.type) { |
f62a369f | 563 | case EAF_TYPE_INT: |
4c553c5a | 564 | if (v1.type != da.f_type) |
f62a369f MM |
565 | runtime( "Setting int attribute to non-int value" ); |
566 | l->attrs[0].u.data = v1.val.i; | |
567 | break; | |
568 | ||
569 | case EAF_TYPE_ROUTER_ID: | |
570 | /* IP->Quad implicit conversion */ | |
4c553c5a | 571 | if (val_is_ip4(&v1)) { |
f62a369f MM |
572 | l->attrs[0].u.data = ipa_to_u32(v1.val.ip); |
573 | break; | |
574 | } | |
575 | /* T_INT for backward compatibility */ | |
576 | if ((v1.type != T_QUAD) && (v1.type != T_INT)) | |
577 | runtime( "Setting quad attribute to non-quad value" ); | |
578 | l->attrs[0].u.data = v1.val.i; | |
579 | break; | |
580 | ||
581 | case EAF_TYPE_OPAQUE: | |
582 | runtime( "Setting opaque attribute is not allowed" ); | |
583 | break; | |
584 | case EAF_TYPE_IP_ADDRESS: | |
585 | if (v1.type != T_IP) | |
586 | runtime( "Setting ip attribute to non-ip value" ); | |
587 | int len = sizeof(ip_addr); | |
588 | struct adata *ad = lp_alloc(fs->pool, sizeof(struct adata) + len); | |
589 | ad->length = len; | |
590 | (* (ip_addr *) ad->data) = v1.val.ip; | |
591 | l->attrs[0].u.ptr = ad; | |
592 | break; | |
593 | case EAF_TYPE_AS_PATH: | |
594 | if (v1.type != T_PATH) | |
595 | runtime( "Setting path attribute to non-path value" ); | |
596 | l->attrs[0].u.ptr = v1.val.ad; | |
597 | break; | |
598 | case EAF_TYPE_BITFIELD: | |
599 | if (v1.type != T_BOOL) | |
600 | runtime( "Setting bit in bitfield attribute to non-bool value" ); | |
601 | { | |
602 | /* First, we have to find the old value */ | |
4c553c5a | 603 | eattr *e = ea_find(*fs->eattrs, da.ea_code); |
f62a369f MM |
604 | u32 data = e ? e->u.data : 0; |
605 | ||
606 | if (v1.val.i) | |
4c553c5a | 607 | l->attrs[0].u.data = data | (1u << da.bit); |
f62a369f | 608 | else |
4c553c5a | 609 | l->attrs[0].u.data = data & ~(1u << da.bit); |
f62a369f MM |
610 | } |
611 | break; | |
612 | case EAF_TYPE_INT_SET: | |
613 | if (v1.type != T_CLIST) | |
614 | runtime( "Setting clist attribute to non-clist value" ); | |
615 | l->attrs[0].u.ptr = v1.val.ad; | |
616 | break; | |
617 | case EAF_TYPE_EC_SET: | |
618 | if (v1.type != T_ECLIST) | |
619 | runtime( "Setting eclist attribute to non-eclist value" ); | |
620 | l->attrs[0].u.ptr = v1.val.ad; | |
621 | break; | |
622 | case EAF_TYPE_LC_SET: | |
623 | if (v1.type != T_LCLIST) | |
624 | runtime( "Setting lclist attribute to non-lclist value" ); | |
625 | l->attrs[0].u.ptr = v1.val.ad; | |
626 | break; | |
f62a369f MM |
627 | default: bug("Unknown type in e,S"); |
628 | } | |
629 | ||
630 | f_rta_cow(fs); | |
631 | l->next = *fs->eattrs; | |
632 | *fs->eattrs = l; | |
633 | } | |
967b88d9 | 634 | } |
4c553c5a | 635 | |
9b46748d MM |
636 | INST(FI_EA_UNSET, 0, 0) { |
637 | DYNAMIC_ATTR; | |
638 | ACCESS_RTE; | |
639 | ACCESS_EATTRS; | |
640 | ||
641 | { | |
642 | struct ea_list *l = lp_alloc(fs->pool, sizeof(struct ea_list) + sizeof(eattr)); | |
643 | ||
644 | l->next = NULL; | |
645 | l->flags = EALF_SORTED; | |
646 | l->count = 1; | |
647 | l->attrs[0].id = da.ea_code; | |
648 | l->attrs[0].flags = 0; | |
8d65add6 | 649 | l->attrs[0].type = EAF_TYPE_UNDEF | EAF_ORIGINATED | EAF_FRESH; |
9b46748d MM |
650 | l->attrs[0].u.data = 0; |
651 | ||
652 | f_rta_cow(fs); | |
653 | l->next = *fs->eattrs; | |
654 | *fs->eattrs = l; | |
655 | } | |
656 | } | |
657 | ||
4c553c5a | 658 | INST(FI_PREF_GET, 0, 1) { |
f62a369f | 659 | ACCESS_RTE; |
4c553c5a | 660 | RESULT(T_INT, i, (*fs->rte)->pref); |
967b88d9 | 661 | } |
4c553c5a MM |
662 | |
663 | INST(FI_PREF_SET, 1, 0) { | |
f62a369f MM |
664 | ACCESS_RTE; |
665 | ARG(1,T_INT); | |
666 | if (v1.val.i > 0xFFFF) | |
667 | runtime( "Setting preference value out of bounds" ); | |
668 | f_rte_cow(fs); | |
669 | (*fs->rte)->pref = v1.val.i; | |
967b88d9 | 670 | } |
4c553c5a MM |
671 | |
672 | INST(FI_LENGTH, 1, 1) { /* Get length of */ | |
f62a369f | 673 | ARG_ANY(1); |
f62a369f | 674 | switch(v1.type) { |
4c553c5a MM |
675 | case T_NET: RESULT(T_INT, i, net_pxlen(v1.val.net)); break; |
676 | case T_PATH: RESULT(T_INT, i, as_path_getlen(v1.val.ad)); break; | |
677 | case T_CLIST: RESULT(T_INT, i, int_set_get_size(v1.val.ad)); break; | |
678 | case T_ECLIST: RESULT(T_INT, i, ec_set_get_size(v1.val.ad)); break; | |
679 | case T_LCLIST: RESULT(T_INT, i, lc_set_get_size(v1.val.ad)); break; | |
f62a369f MM |
680 | default: runtime( "Prefix, path, clist or eclist expected" ); |
681 | } | |
967b88d9 | 682 | } |
4c553c5a MM |
683 | |
684 | INST(FI_SADR_SRC, 1, 1) { /* Get SADR src prefix */ | |
f62a369f MM |
685 | ARG(1, T_NET); |
686 | if (!net_is_sadr(v1.val.net)) | |
687 | runtime( "SADR expected" ); | |
688 | ||
4c553c5a MM |
689 | net_addr_ip6_sadr *net = (void *) v1.val.net; |
690 | net_addr *src = lp_alloc(fs->pool, sizeof(net_addr_ip6)); | |
691 | net_fill_ip6(src, net->src_prefix, net->src_pxlen); | |
f62a369f | 692 | |
4c553c5a | 693 | RESULT(T_NET, net, src); |
967b88d9 | 694 | } |
4c553c5a MM |
695 | |
696 | INST(FI_ROA_MAXLEN, 1, 1) { /* Get ROA max prefix length */ | |
f62a369f MM |
697 | ARG(1, T_NET); |
698 | if (!net_is_roa(v1.val.net)) | |
699 | runtime( "ROA expected" ); | |
700 | ||
4c553c5a | 701 | RESULT(T_INT, i, (v1.val.net->type == NET_ROA4) ? |
f62a369f | 702 | ((net_addr_roa4 *) v1.val.net)->max_pxlen : |
4c553c5a | 703 | ((net_addr_roa6 *) v1.val.net)->max_pxlen); |
967b88d9 | 704 | } |
4c553c5a MM |
705 | |
706 | INST(FI_ROA_ASN, 1, 1) { /* Get ROA ASN */ | |
f62a369f MM |
707 | ARG(1, T_NET); |
708 | if (!net_is_roa(v1.val.net)) | |
709 | runtime( "ROA expected" ); | |
710 | ||
4c553c5a | 711 | RESULT(T_INT, i, (v1.val.net->type == NET_ROA4) ? |
f62a369f | 712 | ((net_addr_roa4 *) v1.val.net)->asn : |
4c553c5a | 713 | ((net_addr_roa6 *) v1.val.net)->asn); |
967b88d9 | 714 | } |
4c553c5a MM |
715 | |
716 | INST(FI_IP, 1, 1) { /* Convert prefix to ... */ | |
f62a369f | 717 | ARG(1, T_NET); |
4c553c5a | 718 | RESULT(T_IP, ip, net_prefix(v1.val.net)); |
967b88d9 | 719 | } |
4c553c5a MM |
720 | |
721 | INST(FI_ROUTE_DISTINGUISHER, 1, 1) { | |
f62a369f | 722 | ARG(1, T_NET); |
f62a369f MM |
723 | if (!net_is_vpn(v1.val.net)) |
724 | runtime( "VPN address expected" ); | |
4c553c5a | 725 | RESULT(T_RD, ec, net_rd(v1.val.net)); |
967b88d9 | 726 | } |
f62a369f | 727 | |
4c553c5a MM |
728 | INST(FI_AS_PATH_FIRST, 1, 1) { /* Get first ASN from AS PATH */ |
729 | ARG(1, T_PATH); | |
730 | int as = 0; | |
f62a369f | 731 | as_path_get_first(v1.val.ad, &as); |
4c553c5a | 732 | RESULT(T_INT, i, as); |
967b88d9 | 733 | } |
f62a369f | 734 | |
4c553c5a MM |
735 | INST(FI_AS_PATH_LAST, 1, 1) { /* Get last ASN from AS PATH */ |
736 | ARG(1, T_PATH); | |
737 | int as = 0; | |
f62a369f | 738 | as_path_get_last(v1.val.ad, &as); |
4c553c5a | 739 | RESULT(T_INT, i, as); |
967b88d9 | 740 | } |
4c553c5a MM |
741 | |
742 | INST(FI_AS_PATH_LAST_NAG, 1, 1) { /* Get last ASN from non-aggregated part of AS PATH */ | |
f62a369f | 743 | ARG(1, T_PATH); |
4c553c5a MM |
744 | RESULT(T_INT, i, as_path_get_last_nonaggregated(v1.val.ad)); |
745 | } | |
f62a369f | 746 | |
4c553c5a MM |
747 | INST(FI_RETURN, 1, 1) { |
748 | /* Acquire the return value */ | |
749 | ARG_ANY(1); | |
750 | uint retpos = vstk.cnt; | |
751 | ||
752 | /* Drop every sub-block including ourselves */ | |
753 | while ((estk.cnt-- > 0) && !(estk.item[estk.cnt].emask & FE_RETURN)) | |
754 | ; | |
755 | ||
756 | /* Now we are at the caller frame; if no such, try to convert to accept/reject. */ | |
757 | if (!estk.cnt) | |
758 | if (vstk.val[retpos].type == T_BOOL) | |
759 | if (vstk.val[retpos].val.i) | |
9b46748d | 760 | |
4c553c5a MM |
761 | return F_ACCEPT; |
762 | else | |
763 | return F_REJECT; | |
764 | else | |
765 | runtime("Can't return non-bool from non-function"); | |
766 | ||
96d757c1 MM |
767 | /* Set the value stack position, overwriting the former implicit void */ |
768 | vstk.cnt = estk.item[estk.cnt].ventry - 1; | |
4c553c5a MM |
769 | |
770 | /* Copy the return value */ | |
771 | RESULT_VAL(vstk.val[retpos]); | |
967b88d9 | 772 | } |
4c553c5a MM |
773 | |
774 | INST(FI_CALL, 0, 1) { | |
96d757c1 | 775 | SYMBOL; |
4f082dfa | 776 | |
96d757c1 MM |
777 | /* Push the body on stack */ |
778 | LINEX(sym->function); | |
ea4f55e3 | 779 | curline.emask |= FE_RETURN; |
96d757c1 MM |
780 | |
781 | /* Before this instruction was called, there was the T_VOID | |
782 | * automatic return value pushed on value stack and also | |
783 | * sym->function->args function arguments. Setting the | |
784 | * vbase to point to first argument. */ | |
785 | ASSERT(curline.ventry >= sym->function->args); | |
786 | curline.ventry -= sym->function->args; | |
787 | curline.vbase = curline.ventry; | |
788 | ||
789 | /* Storage for local variables */ | |
790 | memset(&(vstk.val[vstk.cnt]), 0, sizeof(struct f_val) * sym->function->vars); | |
791 | vstk.cnt += sym->function->vars; | |
967b88d9 | 792 | } |
4c553c5a MM |
793 | |
794 | INST(FI_DROP_RESULT, 1, 0) { | |
795 | ARG_ANY(1); | |
967b88d9 | 796 | } |
4c553c5a | 797 | |
4c553c5a | 798 | INST(FI_SWITCH, 1, 0) { |
f62a369f | 799 | ARG_ANY(1); |
9b46748d | 800 | TREE; |
32793ab6 MM |
801 | const struct f_tree *t = find_tree(tree, &v1); |
802 | if (!t) { | |
4c553c5a | 803 | v1.type = T_VOID; |
32793ab6 MM |
804 | t = find_tree(tree, &v1); |
805 | if (!t) { | |
4c553c5a MM |
806 | debug( "No else statement?\n"); |
807 | break; | |
f62a369f | 808 | } |
f62a369f | 809 | } |
4c553c5a MM |
810 | /* It is actually possible to have t->data NULL */ |
811 | ||
32793ab6 | 812 | LINEX(t->data); |
967b88d9 | 813 | } |
4c553c5a MM |
814 | |
815 | INST(FI_IP_MASK, 2, 1) { /* IP.MASK(val) */ | |
f62a369f MM |
816 | ARG(1, T_IP); |
817 | ARG(2, T_INT); | |
4c553c5a | 818 | RESULT(T_IP, ip, [[ ipa_is_ip4(v1.val.ip) ? |
f62a369f | 819 | ipa_from_ip4(ip4_and(ipa_to_ip4(v1.val.ip), ip4_mkmask(v2.val.i))) : |
4c553c5a | 820 | ipa_from_ip6(ip6_and(ipa_to_ip6(v1.val.ip), ip6_mkmask(v2.val.i))) ]]); |
967b88d9 | 821 | } |
f62a369f | 822 | |
4c553c5a | 823 | INST(FI_PATH_PREPEND, 2, 1) { /* Path prepend */ |
f62a369f MM |
824 | ARG(1, T_PATH); |
825 | ARG(2, T_INT); | |
4c553c5a MM |
826 | RESULT(T_PATH, ad, [[ as_path_prepend(fs->pool, v1.val.ad, v2.val.i) ]]); |
827 | } | |
828 | ||
829 | INST(FI_CLIST_ADD, 2, 1) { /* (Extended) Community list add */ | |
830 | ARG_ANY(1); | |
831 | ARG_ANY(2); | |
832 | if (v1.type == T_PATH) | |
833 | runtime("Can't add to path"); | |
834 | ||
835 | else if (v1.type == T_CLIST) | |
836 | { | |
837 | /* Community (or cluster) list */ | |
838 | struct f_val dummy; | |
839 | ||
840 | if ((v2.type == T_PAIR) || (v2.type == T_QUAD)) | |
841 | RESULT(T_CLIST, ad, [[ int_set_add(fs->pool, v1.val.ad, v2.val.i) ]]); | |
842 | /* IP->Quad implicit conversion */ | |
843 | else if (val_is_ip4(&v2)) | |
844 | RESULT(T_CLIST, ad, [[ int_set_add(fs->pool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]); | |
845 | else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy)) | |
846 | runtime("Can't add set"); | |
847 | else if (v2.type == T_CLIST) | |
848 | RESULT(T_CLIST, ad, [[ int_set_union(fs->pool, v1.val.ad, v2.val.ad) ]]); | |
849 | else | |
850 | runtime("Can't add non-pair"); | |
851 | } | |
f62a369f | 852 | |
4c553c5a MM |
853 | else if (v1.type == T_ECLIST) |
854 | { | |
855 | /* v2.val is either EC or EC-set */ | |
856 | if ((v2.type == T_SET) && eclist_set_type(v2.val.t)) | |
857 | runtime("Can't add set"); | |
858 | else if (v2.type == T_ECLIST) | |
859 | RESULT(T_ECLIST, ad, [[ ec_set_union(fs->pool, v1.val.ad, v2.val.ad) ]]); | |
860 | else if (v2.type != T_EC) | |
861 | runtime("Can't add non-ec"); | |
862 | else | |
863 | RESULT(T_ECLIST, ad, [[ ec_set_add(fs->pool, v1.val.ad, v2.val.ec) ]]); | |
864 | } | |
865 | ||
866 | else if (v1.type == T_LCLIST) | |
867 | { | |
868 | /* v2.val is either LC or LC-set */ | |
869 | if ((v2.type == T_SET) && lclist_set_type(v2.val.t)) | |
870 | runtime("Can't add set"); | |
871 | else if (v2.type == T_LCLIST) | |
872 | RESULT(T_LCLIST, ad, [[ lc_set_union(fs->pool, v1.val.ad, v2.val.ad) ]]); | |
873 | else if (v2.type != T_LC) | |
874 | runtime("Can't add non-lc"); | |
875 | else | |
876 | RESULT(T_LCLIST, ad, [[ lc_set_add(fs->pool, v1.val.ad, v2.val.lc) ]]); | |
877 | ||
878 | } | |
879 | ||
880 | else | |
881 | runtime("Can't add to non-[e|l]clist"); | |
967b88d9 | 882 | } |
f62a369f | 883 | |
4c553c5a | 884 | INST(FI_CLIST_DEL, 2, 1) { /* (Extended) Community list add or delete */ |
f62a369f MM |
885 | ARG_ANY(1); |
886 | ARG_ANY(2); | |
887 | if (v1.type == T_PATH) | |
888 | { | |
4c553c5a | 889 | const struct f_tree *set = NULL; |
f62a369f | 890 | u32 key = 0; |
f62a369f MM |
891 | |
892 | if (v2.type == T_INT) | |
893 | key = v2.val.i; | |
894 | else if ((v2.type == T_SET) && (v2.val.t->from.type == T_INT)) | |
895 | set = v2.val.t; | |
896 | else | |
897 | runtime("Can't delete non-integer (set)"); | |
898 | ||
4c553c5a | 899 | RESULT(T_PATH, ad, [[ as_path_filter(fs->pool, v1.val.ad, set, key, 0) ]]); |
f62a369f | 900 | } |
4c553c5a | 901 | |
f62a369f MM |
902 | else if (v1.type == T_CLIST) |
903 | { | |
904 | /* Community (or cluster) list */ | |
905 | struct f_val dummy; | |
f62a369f MM |
906 | |
907 | if ((v2.type == T_PAIR) || (v2.type == T_QUAD)) | |
4c553c5a | 908 | RESULT(T_CLIST, ad, [[ int_set_del(fs->pool, v1.val.ad, v2.val.i) ]]); |
f62a369f | 909 | /* IP->Quad implicit conversion */ |
4c553c5a MM |
910 | else if (val_is_ip4(&v2)) |
911 | RESULT(T_CLIST, ad, [[ int_set_del(fs->pool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]); | |
912 | else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy) || (v2.type == T_CLIST)) | |
913 | RESULT(T_CLIST, ad, [[ clist_filter(fs->pool, v1.val.ad, &v2, 0) ]]); | |
f62a369f | 914 | else |
4c553c5a | 915 | runtime("Can't delete non-pair"); |
f62a369f | 916 | } |
4c553c5a | 917 | |
f62a369f MM |
918 | else if (v1.type == T_ECLIST) |
919 | { | |
f62a369f | 920 | /* v2.val is either EC or EC-set */ |
4c553c5a MM |
921 | if ((v2.type == T_SET) && eclist_set_type(v2.val.t) || (v2.type == T_ECLIST)) |
922 | RESULT(T_ECLIST, ad, [[ eclist_filter(fs->pool, v1.val.ad, &v2, 0) ]]); | |
f62a369f | 923 | else if (v2.type != T_EC) |
4c553c5a MM |
924 | runtime("Can't delete non-ec"); |
925 | else | |
926 | RESULT(T_ECLIST, ad, [[ ec_set_del(fs->pool, v1.val.ad, v2.val.ec) ]]); | |
f62a369f | 927 | } |
4c553c5a | 928 | |
f62a369f MM |
929 | else if (v1.type == T_LCLIST) |
930 | { | |
f62a369f | 931 | /* v2.val is either LC or LC-set */ |
4c553c5a MM |
932 | if ((v2.type == T_SET) && lclist_set_type(v2.val.t) || (v2.type == T_LCLIST)) |
933 | RESULT(T_LCLIST, ad, [[ lclist_filter(fs->pool, v1.val.ad, &v2, 0) ]]); | |
f62a369f | 934 | else if (v2.type != T_LC) |
4c553c5a MM |
935 | runtime("Can't delete non-lc"); |
936 | else | |
937 | RESULT(T_LCLIST, ad, [[ lc_set_del(fs->pool, v1.val.ad, v2.val.lc) ]]); | |
938 | } | |
f62a369f | 939 | |
4c553c5a MM |
940 | else |
941 | runtime("Can't delete in non-[e|l]clist"); | |
942 | } | |
f62a369f | 943 | |
4c553c5a MM |
944 | INST(FI_CLIST_FILTER, 2, 1) { /* (Extended) Community list add or delete */ |
945 | ARG_ANY(1); | |
946 | ARG_ANY(2); | |
947 | if (v1.type == T_PATH) | |
948 | { | |
949 | u32 key = 0; | |
f62a369f | 950 | |
4c553c5a MM |
951 | if ((v2.type == T_SET) && (v2.val.t->from.type == T_INT)) |
952 | RESULT(T_PATH, ad, [[ as_path_filter(fs->pool, v1.val.ad, v2.val.t, key, 1) ]]); | |
953 | else | |
954 | runtime("Can't filter integer"); | |
f62a369f | 955 | } |
f62a369f | 956 | |
4c553c5a MM |
957 | else if (v1.type == T_CLIST) |
958 | { | |
959 | /* Community (or cluster) list */ | |
960 | struct f_val dummy; | |
f62a369f | 961 | |
4c553c5a MM |
962 | if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy) || (v2.type == T_CLIST)) |
963 | RESULT(T_CLIST, ad, [[ clist_filter(fs->pool, v1.val.ad, &v2, 1) ]]); | |
964 | else | |
965 | runtime("Can't filter pair"); | |
966 | } | |
967 | ||
968 | else if (v1.type == T_ECLIST) | |
f62a369f | 969 | { |
4c553c5a MM |
970 | /* v2.val is either EC or EC-set */ |
971 | if ((v2.type == T_SET) && eclist_set_type(v2.val.t) || (v2.type == T_ECLIST)) | |
972 | RESULT(T_ECLIST, ad, [[ eclist_filter(fs->pool, v1.val.ad, &v2, 1) ]]); | |
973 | else | |
974 | runtime("Can't filter ec"); | |
975 | } | |
f62a369f | 976 | |
4c553c5a MM |
977 | else if (v1.type == T_LCLIST) |
978 | { | |
979 | /* v2.val is either LC or LC-set */ | |
980 | if ((v2.type == T_SET) && lclist_set_type(v2.val.t) || (v2.type == T_LCLIST)) | |
981 | RESULT(T_LCLIST, ad, [[ lclist_filter(fs->pool, v1.val.ad, &v2, 1) ]]); | |
982 | else | |
983 | runtime("Can't filter lc"); | |
f62a369f | 984 | } |
4c553c5a | 985 | |
f62a369f | 986 | else |
4c553c5a MM |
987 | runtime("Can't filter non-[e|l]clist"); |
988 | } | |
f62a369f | 989 | |
4c553c5a MM |
990 | INST(FI_ROA_CHECK_IMPLICIT, 0, 1) { /* ROA Check */ |
991 | RTC(1); | |
992 | ACCESS_RTE; | |
993 | ACCESS_EATTRS; | |
994 | const net_addr *net = (*fs->rte)->net->n.addr; | |
f62a369f | 995 | |
4c553c5a MM |
996 | /* We ignore temporary attributes, probably not a problem here */ |
997 | /* 0x02 is a value of BA_AS_PATH, we don't want to include BGP headers */ | |
998 | eattr *e = ea_find(*fs->eattrs, EA_CODE(PROTOCOL_BGP, 0x02)); | |
f62a369f | 999 | |
4c553c5a MM |
1000 | if (!e || ((e->type & EAF_TYPE_MASK) != EAF_TYPE_AS_PATH)) |
1001 | runtime("Missing AS_PATH attribute"); | |
1002 | ||
1003 | u32 as = 0; | |
1004 | as_path_get_last(e->u.ptr, &as); | |
f62a369f | 1005 | |
f62a369f MM |
1006 | if (!table) |
1007 | runtime("Missing ROA table"); | |
1008 | ||
1009 | if (table->addr_type != NET_ROA4 && table->addr_type != NET_ROA6) | |
1010 | runtime("Table type must be either ROA4 or ROA6"); | |
1011 | ||
4c553c5a MM |
1012 | if (table->addr_type != (net->type == NET_IP4 ? NET_ROA4 : NET_ROA6)) |
1013 | RESULT(T_ENUM_ROA, i, ROA_UNKNOWN); /* Prefix and table type mismatch */ | |
1014 | else | |
1015 | RESULT(T_ENUM_ROA, i, [[ net_roa_check(table, net, as) ]]); | |
1016 | } | |
1017 | ||
1018 | INST(FI_ROA_CHECK_EXPLICIT, 2, 1) { /* ROA Check */ | |
1019 | ARG(1, T_NET); | |
1020 | ARG(2, T_INT); | |
1021 | RTC(3); | |
1022 | ||
1023 | u32 as = v2.val.i; | |
1024 | ||
1025 | if (!table) | |
1026 | runtime("Missing ROA table"); | |
1027 | ||
1028 | if (table->addr_type != NET_ROA4 && table->addr_type != NET_ROA6) | |
1029 | runtime("Table type must be either ROA4 or ROA6"); | |
f62a369f MM |
1030 | |
1031 | if (table->addr_type != (v1.val.net->type == NET_IP4 ? NET_ROA4 : NET_ROA6)) | |
4c553c5a | 1032 | RESULT(T_ENUM_ROA, i, ROA_UNKNOWN); /* Prefix and table type mismatch */ |
f62a369f | 1033 | else |
4c553c5a | 1034 | RESULT(T_ENUM_ROA, i, [[ net_roa_check(table, v1.val.net, as) ]]); |
f62a369f | 1035 | |
967b88d9 | 1036 | } |
f62a369f | 1037 | |
4c553c5a | 1038 | INST(FI_FORMAT, 1, 0) { /* Format */ |
f62a369f | 1039 | ARG_ANY(1); |
4c553c5a | 1040 | RESULT(T_STRING, s, val_format_str(fs, &v1)); |
967b88d9 | 1041 | } |
f62a369f | 1042 | |
4c553c5a | 1043 | INST(FI_ASSERT, 1, 0) { /* Birdtest Assert */ |
f62a369f | 1044 | ARG(1, T_BOOL); |
9b46748d | 1045 | STRING; |
c0e958e0 MM |
1046 | if (!bt_assert_hook) |
1047 | runtime("No bt_assert hook registered, can't assert"); | |
1048 | ||
1049 | bt_assert_hook(res.val.i, what); | |
967b88d9 | 1050 | } |