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