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 * 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 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 * 58 * All the arguments are processed literally; if you need an argument including comma, 59 * you have to quote it by [[ ... ]] 60 * 61 * What is the syntax here? 62 * m4_dnl INST(FI_NOP, in, out) { enum value, input args, output args 63 * m4_dnl ARG(num, type); argument, its id (in data fields) and type accessible by v1, v2, v3 64 * m4_dnl ARG_ANY(num); argument with no type check accessible by v1, v2, v3 65 * m4_dnl VARARG; variable-length argument list; accessible by vv(i) and whati->varcount 66 * m4_dnl LINE(num, unused); this argument has to be converted to its own f_line 67 * m4_dnl SYMBOL; symbol handed from config 68 * m4_dnl STATIC_ATTR; static attribute definition 69 * m4_dnl DYNAMIC_ATTR; dynamic attribute definition 70 * m4_dnl RTC; route table config 71 * m4_dnl ACCESS_RTE; this instruction needs route 72 * m4_dnl ACCESS_EATTRS; this instruction needs extended attributes 73 * 74 * m4_dnl FID_MEMBER( custom instruction member 75 * m4_dnl C type, for storage in structs 76 * m4_dnl name, how the member is named 77 * m4_dnl comparator for same(), if different, this should be TRUE (CAVEAT) 78 * m4_dnl dump format string debug -> format string for bvsnprintf 79 * m4_dnl dump format args appropriate args 80 * m4_dnl ) 81 * 82 * m4_dnl RESULT(type, union-field, value); putting this on value stack 83 * m4_dnl RESULT_VAL(value-struct); pass the struct f_val directly 84 * m4_dnl RESULT_VOID; return undef 85 * m4_dnl } 86 * 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 * 92 * Other code is just copied into the interpreter part. 93 * 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) 107 * m4_dnl (108) [[ put it here ]] 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 * 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 * 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. 219 */ 220 221 /* Binary operators */ 222 INST(FI_ADD, 2, 1) { 223 ARG(1,T_INT); 224 ARG(2,T_INT); 225 RESULT(T_INT, i, v1.val.i + v2.val.i); 226 } 227 INST(FI_SUBTRACT, 2, 1) { 228 ARG(1,T_INT); 229 ARG(2,T_INT); 230 RESULT(T_INT, i, v1.val.i - v2.val.i); 231 } 232 INST(FI_MULTIPLY, 2, 1) { 233 ARG(1,T_INT); 234 ARG(2,T_INT); 235 RESULT(T_INT, i, v1.val.i * v2.val.i); 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" ); 241 RESULT(T_INT, i, v1.val.i / v2.val.i); 242 } 243 INST(FI_AND, 1, 1) { 244 ARG(1,T_BOOL); 245 ARG_TYPE_STATIC(2,T_BOOL); 246 RESULT_TYPE(T_BOOL); 247 248 if (v1.val.i) 249 LINE(2,0); 250 else 251 RESULT_VAL(v1); 252 } 253 INST(FI_OR, 1, 1) { 254 ARG(1,T_BOOL); 255 ARG_TYPE_STATIC(2,T_BOOL); 256 RESULT_TYPE(T_BOOL); 257 258 if (!v1.val.i) 259 LINE(2,0); 260 else 261 RESULT_VAL(v1); 262 } 263 264 INST(FI_PAIR_CONSTRUCT, 2, 1) { 265 ARG(1,T_INT); 266 ARG(2,T_INT); 267 uint u1 = v1.val.i; 268 uint u2 = v2.val.i; 269 if ((u1 > 0xFFFF) || (u2 > 0xFFFF)) 270 runtime( "Can't operate with value out of bounds in pair constructor" ); 271 RESULT(T_PAIR, i, (u1 << 16) | u2); 272 } 273 274 INST(FI_EC_CONSTRUCT, 2, 1) { 275 ARG_ANY(1); 276 ARG(2, T_INT); 277 278 FID_MEMBER(enum ec_subtype, ecs, f1->ecs != f2->ecs, "ec subtype %s", ec_subtype_str(item->ecs)); 279 280 int ipv4_used; 281 u32 key, val; 282 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 294 runtime("Argument 1 of EC constructor must be integer or IPv4 address, got 0x%02x", v1.type); 295 296 val = v2.val.i; 297 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); 312 } 313 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 } 320 321 INST(FI_PATHMASK_CONSTRUCT, 0, 1) { 322 VARARG; 323 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; 326 327 for (uint i=0; i<whati->varcount; i++) { 328 switch (vv(i).type) { 329 case T_PATH_MASK_ITEM: 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 341 pm->item[i] = vv(i).val.pmi; 342 break; 343 344 case T_INT: 345 pm->item[i] = (struct f_path_mask_item) { 346 .asn = vv(i).val.i, 347 .kind = PM_ASN, 348 }; 349 break; 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 361 default: 362 runtime( "Error resolving path mask template: value not an integer" ); 363 } 364 } 365 366 RESULT(T_PATH_MASK, path_mask, pm); 367 } 368 369 /* Relational operators */ 370 371 INST(FI_NEQ, 2, 1) { 372 ARG_ANY(1); 373 ARG_ANY(2); 374 RESULT(T_BOOL, i, !val_same(&v1, &v2)); 375 } 376 377 INST(FI_EQ, 2, 1) { 378 ARG_ANY(1); 379 ARG_ANY(2); 380 RESULT(T_BOOL, i, val_same(&v1, &v2)); 381 } 382 383 INST(FI_LT, 2, 1) { 384 ARG_ANY(1); 385 ARG_ANY(2); 386 ARG_SAME_TYPE(1, 2); 387 388 int i = val_compare(&v1, &v2); 389 if (i == F_CMP_ERROR) 390 runtime( "Can't compare values of incompatible types" ); 391 RESULT(T_BOOL, i, (i == -1)); 392 } 393 394 INST(FI_LTE, 2, 1) { 395 ARG_ANY(1); 396 ARG_ANY(2); 397 ARG_SAME_TYPE(1, 2); 398 399 int i = val_compare(&v1, &v2); 400 if (i == F_CMP_ERROR) 401 runtime( "Can't compare values of incompatible types" ); 402 RESULT(T_BOOL, i, (i != 1)); 403 } 404 405 INST(FI_NOT, 1, 1) { 406 ARG(1,T_BOOL); 407 RESULT(T_BOOL, i, !v1.val.i); 408 } 409 410 INST(FI_MATCH, 2, 1) { 411 ARG_ANY(1); 412 ARG_ANY(2); 413 int i = val_in_range(&v1, &v2); 414 if (i == F_CMP_ERROR) 415 runtime( "~ applied on unknown type pair" ); 416 RESULT(T_BOOL, i, !!i); 417 } 418 419 INST(FI_NOT_MATCH, 2, 1) { 420 ARG_ANY(1); 421 ARG_ANY(2); 422 int i = val_in_range(&v1, &v2); 423 if (i == F_CMP_ERROR) 424 runtime( "!~ applied on unknown type pair" ); 425 RESULT(T_BOOL, i, !i); 426 } 427 428 INST(FI_DEFINED, 1, 1) { 429 ARG_ANY(1); 430 RESULT(T_BOOL, i, (v1.type != T_VOID) && !undef_value(v1)); 431 } 432 433 INST(FI_TYPE, 1, 1) { 434 ARG_ANY(1); /* There may be more types supporting this operation */ 435 switch (v1.type) 436 { 437 case T_NET: 438 RESULT(T_ENUM_NETTYPE, i, v1.val.net->type); 439 break; 440 default: 441 runtime( "Can't determine type of this item" ); 442 } 443 } 444 445 INST(FI_IS_V4, 1, 1) { 446 ARG(1, T_IP); 447 RESULT(T_BOOL, i, ipa_is_ip4(v1.val.ip)); 448 } 449 450 /* Set to indirect value prepared in v1 */ 451 INST(FI_VAR_SET, 1, 0) { 452 NEVER_CONSTANT; 453 ARG_ANY(1); 454 SYMBOL; 455 ARG_TYPE(1, sym->class & 0xff); 456 457 fstk->vstk[curline.vbase + sym->offset] = v1; 458 } 459 460 INST(FI_VAR_GET, 0, 1) { 461 SYMBOL; 462 NEVER_CONSTANT; 463 RESULT_TYPE(sym->class & 0xff); 464 RESULT_VAL(fstk->vstk[curline.vbase + sym->offset]); 465 } 466 467 INST(FI_CONSTANT, 0, 1) { 468 FID_MEMBER( 469 struct f_val, 470 val, 471 [[ !val_same(&(f1->val), &(f2->val)) ]], 472 "value %s", 473 val_dump(&(item->val)) 474 ); 475 476 RESULT_TYPE(val.type); 477 RESULT_VAL(val); 478 } 479 480 INST(FI_CONDITION, 1, 0) { 481 ARG(1, T_BOOL); 482 if (v1.val.i) 483 LINE(2,0); 484 else 485 LINE(3,1); 486 } 487 488 INST(FI_PRINT, 0, 0) { 489 NEVER_CONSTANT; 490 VARARG; 491 492 if (whati->varcount && !(fs->flags & FF_SILENT)) 493 for (uint i=0; i<whati->varcount; i++) 494 val_format(&(vv(i)), &fs->buf); 495 } 496 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 504 INST(FI_DIE, 0, 0) { 505 NEVER_CONSTANT; 506 FID_MEMBER(enum filter_return, fret, f1->fret != f2->fret, "%s", filter_return_str(item->fret)); 507 508 switch (whati->fret) { 509 case F_ACCEPT: /* Should take care about turning ACCEPT into MODIFY */ 510 case F_ERROR: 511 case F_REJECT: /* Maybe print complete route along with reason to reject route? */ 512 return fret; /* We have to return now, no more processing. */ 513 default: 514 bug( "unknown return type: Can't happen"); 515 } 516 } 517 518 INST(FI_RTA_GET, 0, 1) { 519 { 520 STATIC_ATTR; 521 ACCESS_RTE; 522 struct rta *rta = (*fs->rte)->attrs; 523 524 switch (sa.sa_code) 525 { 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; 535 case SA_WEIGHT: RESULT(sa.f_type, i, rta->nh.weight + 1); break; 536 537 default: 538 bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code); 539 } 540 } 541 } 542 543 INST(FI_RTA_SET, 1, 0) { 544 ACCESS_RTE; 545 ARG_ANY(1); 546 STATIC_ATTR; 547 ARG_TYPE(1, sa.f_type); 548 549 f_rta_cow(fs); 550 { 551 struct rta *rta = (*fs->rte)->attrs; 552 553 switch (sa.sa_code) 554 { 555 case SA_FROM: 556 rta->from = v1.val.ip; 557 break; 558 559 case SA_GW: 560 { 561 ip_addr ip = v1.val.ip; 562 struct iface *ifa = ipa_is_link_local(ip) ? rta->nh.iface : NULL; 563 neighbor *n = neigh_find(rta->src->proto, ip, ifa, 0); 564 if (!n || (n->scope == SCOPE_HOST)) 565 runtime( "Invalid gw address" ); 566 567 rta->dest = RTD_UNICAST; 568 rta->nh.gw = ip; 569 rta->nh.iface = n->iface; 570 rta->nh.next = NULL; 571 rta->hostentry = NULL; 572 } 573 break; 574 575 case SA_SCOPE: 576 rta->scope = v1.val.i; 577 break; 578 579 case SA_DEST: 580 { 581 int i = v1.val.i; 582 if ((i != RTD_BLACKHOLE) && (i != RTD_UNREACHABLE) && (i != RTD_PROHIBIT)) 583 runtime( "Destination can be changed only to blackhole, unreachable or prohibit" ); 584 585 rta->dest = i; 586 rta->nh.gw = IPA_NONE; 587 rta->nh.iface = NULL; 588 rta->nh.next = NULL; 589 rta->hostentry = NULL; 590 } 591 break; 592 593 case SA_IFNAME: 594 { 595 struct iface *ifa = if_find_by_name(v1.val.s); 596 if (!ifa) 597 runtime( "Invalid iface name" ); 598 599 rta->dest = RTD_UNICAST; 600 rta->nh.gw = IPA_NONE; 601 rta->nh.iface = ifa; 602 rta->nh.next = NULL; 603 rta->hostentry = NULL; 604 } 605 break; 606 607 case SA_WEIGHT: 608 { 609 int i = v1.val.i; 610 if (i < 1 || i > 256) 611 runtime( "Setting weight value out of bounds" ); 612 if (rta->dest != RTD_UNICAST) 613 runtime( "Setting weight needs regular nexthop " ); 614 615 /* Set weight on all next hops */ 616 for (struct nexthop *nh = &rta->nh; nh; nh = nh->next) 617 nh->weight = i - 1; 618 } 619 break; 620 621 default: 622 bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code); 623 } 624 } 625 } 626 627 INST(FI_EA_GET, 0, 1) { /* Access to extended attributes */ 628 DYNAMIC_ATTR; 629 ACCESS_RTE; 630 ACCESS_EATTRS; 631 RESULT_TYPE(da.f_type); 632 { 633 eattr *e = ea_find(*fs->eattrs, da.ea_code); 634 635 if (!e) { 636 /* A special case: undefined as_path looks like empty as_path */ 637 if (da.type == EAF_TYPE_AS_PATH) { 638 RESULT_(T_PATH, ad, &null_adata); 639 break; 640 } 641 642 /* The same special case for int_set */ 643 if (da.type == EAF_TYPE_INT_SET) { 644 RESULT_(T_CLIST, ad, &null_adata); 645 break; 646 } 647 648 /* The same special case for ec_set */ 649 if (da.type == EAF_TYPE_EC_SET) { 650 RESULT_(T_ECLIST, ad, &null_adata); 651 break; 652 } 653 654 /* The same special case for lc_set */ 655 if (da.type == EAF_TYPE_LC_SET) { 656 RESULT_(T_LCLIST, ad, &null_adata); 657 break; 658 } 659 660 /* Undefined value */ 661 RESULT_VOID; 662 break; 663 } 664 665 switch (e->type & EAF_TYPE_MASK) { 666 case EAF_TYPE_INT: 667 RESULT_(da.f_type, i, e->u.data); 668 break; 669 case EAF_TYPE_ROUTER_ID: 670 RESULT_(T_QUAD, i, e->u.data); 671 break; 672 case EAF_TYPE_OPAQUE: 673 RESULT_(T_ENUM_EMPTY, i, 0); 674 break; 675 case EAF_TYPE_IP_ADDRESS: 676 RESULT_(T_IP, ip, *((ip_addr *) e->u.ptr->data)); 677 break; 678 case EAF_TYPE_AS_PATH: 679 RESULT_(T_PATH, ad, e->u.ptr); 680 break; 681 case EAF_TYPE_BITFIELD: 682 RESULT_(T_BOOL, i, !!(e->u.data & (1u << da.bit))); 683 break; 684 case EAF_TYPE_INT_SET: 685 RESULT_(T_CLIST, ad, e->u.ptr); 686 break; 687 case EAF_TYPE_EC_SET: 688 RESULT_(T_ECLIST, ad, e->u.ptr); 689 break; 690 case EAF_TYPE_LC_SET: 691 RESULT_(T_LCLIST, ad, e->u.ptr); 692 break; 693 case EAF_TYPE_UNDEF: 694 RESULT_VOID; 695 break; 696 default: 697 bug("Unknown dynamic attribute type"); 698 } 699 } 700 } 701 702 INST(FI_EA_SET, 1, 0) { 703 ACCESS_RTE; 704 ACCESS_EATTRS; 705 ARG_ANY(1); 706 DYNAMIC_ATTR; 707 ARG_TYPE(1, da.f_type); 708 { 709 struct ea_list *l = lp_alloc(fs->pool, sizeof(struct ea_list) + sizeof(eattr)); 710 711 l->next = NULL; 712 l->flags = EALF_SORTED; 713 l->count = 1; 714 l->attrs[0].id = da.ea_code; 715 l->attrs[0].flags = 0; 716 l->attrs[0].type = da.type | EAF_ORIGINATED | EAF_FRESH; 717 718 switch (da.type) { 719 case EAF_TYPE_INT: 720 case EAF_TYPE_ROUTER_ID: 721 l->attrs[0].u.data = v1.val.i; 722 break; 723 724 case EAF_TYPE_OPAQUE: 725 runtime( "Setting opaque attribute is not allowed" ); 726 break; 727 728 case EAF_TYPE_IP_ADDRESS:; 729 int len = sizeof(ip_addr); 730 struct adata *ad = lp_alloc(fs->pool, sizeof(struct adata) + len); 731 ad->length = len; 732 (* (ip_addr *) ad->data) = v1.val.ip; 733 l->attrs[0].u.ptr = ad; 734 break; 735 736 case EAF_TYPE_AS_PATH: 737 case EAF_TYPE_INT_SET: 738 case EAF_TYPE_EC_SET: 739 case EAF_TYPE_LC_SET: 740 l->attrs[0].u.ptr = v1.val.ad; 741 break; 742 743 case EAF_TYPE_BITFIELD: 744 { 745 /* First, we have to find the old value */ 746 eattr *e = ea_find(*fs->eattrs, da.ea_code); 747 u32 data = e ? e->u.data : 0; 748 749 if (v1.val.i) 750 l->attrs[0].u.data = data | (1u << da.bit); 751 else 752 l->attrs[0].u.data = data & ~(1u << da.bit); 753 } 754 break; 755 756 default: 757 bug("Unknown dynamic attribute type"); 758 } 759 760 f_rta_cow(fs); 761 l->next = *fs->eattrs; 762 *fs->eattrs = l; 763 } 764 } 765 766 INST(FI_EA_UNSET, 0, 0) { 767 DYNAMIC_ATTR; 768 ACCESS_RTE; 769 ACCESS_EATTRS; 770 771 { 772 struct ea_list *l = lp_alloc(fs->pool, sizeof(struct ea_list) + sizeof(eattr)); 773 774 l->next = NULL; 775 l->flags = EALF_SORTED; 776 l->count = 1; 777 l->attrs[0].id = da.ea_code; 778 l->attrs[0].flags = 0; 779 l->attrs[0].type = EAF_TYPE_UNDEF | EAF_ORIGINATED | EAF_FRESH; 780 l->attrs[0].u.data = 0; 781 782 f_rta_cow(fs); 783 l->next = *fs->eattrs; 784 *fs->eattrs = l; 785 } 786 } 787 788 INST(FI_PREF_GET, 0, 1) { 789 ACCESS_RTE; 790 RESULT(T_INT, i, (*fs->rte)->pref); 791 } 792 793 INST(FI_PREF_SET, 1, 0) { 794 ACCESS_RTE; 795 ARG(1,T_INT); 796 if (v1.val.i > 0xFFFF) 797 runtime( "Setting preference value out of bounds" ); 798 f_rte_cow(fs); 799 (*fs->rte)->pref = v1.val.i; 800 } 801 802 INST(FI_LENGTH, 1, 1) { /* Get length of */ 803 ARG_ANY(1); 804 switch(v1.type) { 805 case T_NET: RESULT(T_INT, i, net_pxlen(v1.val.net)); break; 806 case T_PATH: RESULT(T_INT, i, as_path_getlen(v1.val.ad)); break; 807 case T_CLIST: RESULT(T_INT, i, int_set_get_size(v1.val.ad)); break; 808 case T_ECLIST: RESULT(T_INT, i, ec_set_get_size(v1.val.ad)); break; 809 case T_LCLIST: RESULT(T_INT, i, lc_set_get_size(v1.val.ad)); break; 810 default: runtime( "Prefix, path, clist or eclist expected" ); 811 } 812 } 813 814 INST(FI_NET_SRC, 1, 1) { /* Get src prefix */ 815 ARG(1, T_NET); 816 817 net_addr_union *net = (void *) v1.val.net; 818 net_addr *src = falloc(sizeof(net_addr_ip6)); 819 const byte *part; 820 821 switch(v1.val.net->type) { 822 case NET_FLOW4: 823 part = flow4_get_part(&net->flow4, FLOW_TYPE_SRC_PREFIX); 824 if (part) 825 net_fill_ip4(src, flow_read_ip4_part(part), flow_read_pxlen(part)); 826 else 827 net_fill_ip4(src, IP4_NONE, 0); 828 break; 829 830 case NET_FLOW6: 831 part = flow6_get_part(&net->flow6, FLOW_TYPE_SRC_PREFIX); 832 if (part) 833 net_fill_ip6(src, flow_read_ip6_part(part), flow_read_pxlen(part)); 834 else 835 net_fill_ip6(src, IP6_NONE, 0); 836 break; 837 838 case NET_IP6_SADR: 839 net_fill_ip6(src, net->ip6_sadr.src_prefix, net->ip6_sadr.src_pxlen); 840 break; 841 842 default: 843 runtime( "Flow or SADR expected" ); 844 } 845 846 RESULT(T_NET, net, src); 847 } 848 849 INST(FI_NET_DST, 1, 1) { /* Get dst prefix */ 850 ARG(1, T_NET); 851 852 net_addr_union *net = (void *) v1.val.net; 853 net_addr *dst = falloc(sizeof(net_addr_ip6)); 854 const byte *part; 855 856 switch(v1.val.net->type) { 857 case NET_FLOW4: 858 part = flow4_get_part(&net->flow4, FLOW_TYPE_DST_PREFIX); 859 if (part) 860 net_fill_ip4(dst, flow_read_ip4_part(part), flow_read_pxlen(part)); 861 else 862 net_fill_ip4(dst, IP4_NONE, 0); 863 break; 864 865 case NET_FLOW6: 866 part = flow6_get_part(&net->flow6, FLOW_TYPE_DST_PREFIX); 867 if (part) 868 net_fill_ip6(dst, flow_read_ip6_part(part), flow_read_pxlen(part)); 869 else 870 net_fill_ip6(dst, IP6_NONE, 0); 871 break; 872 873 case NET_IP6_SADR: 874 net_fill_ip6(dst, net->ip6_sadr.dst_prefix, net->ip6_sadr.dst_pxlen); 875 break; 876 877 default: 878 runtime( "Flow or SADR expected" ); 879 } 880 881 RESULT(T_NET, net, dst); 882 } 883 884 INST(FI_ROA_MAXLEN, 1, 1) { /* Get ROA max prefix length */ 885 ARG(1, T_NET); 886 if (!net_is_roa(v1.val.net)) 887 runtime( "ROA expected" ); 888 889 RESULT(T_INT, i, (v1.val.net->type == NET_ROA4) ? 890 ((net_addr_roa4 *) v1.val.net)->max_pxlen : 891 ((net_addr_roa6 *) v1.val.net)->max_pxlen); 892 } 893 894 INST(FI_ROA_ASN, 1, 1) { /* Get ROA ASN */ 895 ARG(1, T_NET); 896 if (!net_is_roa(v1.val.net)) 897 runtime( "ROA expected" ); 898 899 RESULT(T_INT, i, (v1.val.net->type == NET_ROA4) ? 900 ((net_addr_roa4 *) v1.val.net)->asn : 901 ((net_addr_roa6 *) v1.val.net)->asn); 902 } 903 904 INST(FI_IP, 1, 1) { /* Convert prefix to ... */ 905 ARG(1, T_NET); 906 RESULT(T_IP, ip, net_prefix(v1.val.net)); 907 } 908 909 INST(FI_ROUTE_DISTINGUISHER, 1, 1) { 910 ARG(1, T_NET); 911 if (!net_is_vpn(v1.val.net)) 912 runtime( "VPN address expected" ); 913 RESULT(T_RD, ec, net_rd(v1.val.net)); 914 } 915 916 INST(FI_AS_PATH_FIRST, 1, 1) { /* Get first ASN from AS PATH */ 917 ARG(1, T_PATH); 918 u32 as = 0; 919 as_path_get_first(v1.val.ad, &as); 920 RESULT(T_INT, i, as); 921 } 922 923 INST(FI_AS_PATH_LAST, 1, 1) { /* Get last ASN from AS PATH */ 924 ARG(1, T_PATH); 925 u32 as = 0; 926 as_path_get_last(v1.val.ad, &as); 927 RESULT(T_INT, i, as); 928 } 929 930 INST(FI_AS_PATH_LAST_NAG, 1, 1) { /* Get last ASN from non-aggregated part of AS PATH */ 931 ARG(1, T_PATH); 932 RESULT(T_INT, i, as_path_get_last_nonaggregated(v1.val.ad)); 933 } 934 935 INST(FI_RETURN, 1, 1) { 936 NEVER_CONSTANT; 937 /* Acquire the return value */ 938 ARG_ANY(1); 939 uint retpos = fstk->vcnt; 940 941 /* Drop every sub-block including ourselves */ 942 do fstk->ecnt--; 943 while ((fstk->ecnt > 0) && !(fstk->estk[fstk->ecnt].emask & FE_RETURN)); 944 945 /* Now we are at the caller frame; if no such, try to convert to accept/reject. */ 946 if (!fstk->ecnt) 947 { 948 if (fstk->vstk[retpos].type == T_BOOL) 949 return (fstk->vstk[retpos].val.i) ? F_ACCEPT : F_REJECT; 950 else 951 runtime("Can't return non-bool from non-function"); 952 } 953 954 /* Set the value stack position, overwriting the former implicit void */ 955 fstk->vcnt = fstk->estk[fstk->ecnt].ventry - 1; 956 957 /* Copy the return value */ 958 RESULT_VAL(fstk->vstk[retpos]); 959 } 960 961 INST(FI_CALL, 0, 1) { 962 NEVER_CONSTANT; 963 SYMBOL; 964 965 FID_SAME_BODY() 966 if (!(f1->sym->flags & SYM_FLAG_SAME)) 967 return 0; 968 969 FID_ITERATE_BODY() 970 BUFFER_PUSH(fit->lines) = whati->sym->function; 971 972 FID_INTERPRET_BODY() 973 974 /* Push the body on stack */ 975 LINEX(sym->function); 976 curline.emask |= FE_RETURN; 977 978 /* Before this instruction was called, there was the T_VOID 979 * automatic return value pushed on value stack and also 980 * sym->function->args function arguments. Setting the 981 * vbase to point to first argument. */ 982 ASSERT(curline.ventry >= sym->function->args); 983 curline.ventry -= sym->function->args; 984 curline.vbase = curline.ventry; 985 986 /* Storage for local variables */ 987 memset(&(fstk->vstk[fstk->vcnt]), 0, sizeof(struct f_val) * sym->function->vars); 988 fstk->vcnt += sym->function->vars; 989 } 990 991 INST(FI_DROP_RESULT, 1, 0) { 992 NEVER_CONSTANT; 993 ARG_ANY(1); 994 } 995 996 INST(FI_SWITCH, 1, 0) { 997 ARG_ANY(1); 998 999 FID_MEMBER(struct f_tree *, tree, [[!same_tree(f1->tree, f2->tree)]], "tree %p", item->tree); 1000 1001 FID_ITERATE_BODY() 1002 tree_walk(whati->tree, f_add_tree_lines, fit); 1003 1004 FID_INTERPRET_BODY() 1005 const struct f_tree *t = find_tree(tree, &v1); 1006 if (!t) { 1007 v1.type = T_VOID; 1008 t = find_tree(tree, &v1); 1009 if (!t) { 1010 debug( "No else statement?\n"); 1011 FID_HIC(,break,return NULL); 1012 } 1013 } 1014 /* It is actually possible to have t->data NULL */ 1015 1016 LINEX(t->data); 1017 } 1018 1019 INST(FI_IP_MASK, 2, 1) { /* IP.MASK(val) */ 1020 ARG(1, T_IP); 1021 ARG(2, T_INT); 1022 RESULT(T_IP, ip, [[ ipa_is_ip4(v1.val.ip) ? 1023 ipa_from_ip4(ip4_and(ipa_to_ip4(v1.val.ip), ip4_mkmask(v2.val.i))) : 1024 ipa_from_ip6(ip6_and(ipa_to_ip6(v1.val.ip), ip6_mkmask(v2.val.i))) ]]); 1025 } 1026 1027 INST(FI_PATH_PREPEND, 2, 1) { /* Path prepend */ 1028 ARG(1, T_PATH); 1029 ARG(2, T_INT); 1030 RESULT(T_PATH, ad, [[ as_path_prepend(fpool, v1.val.ad, v2.val.i) ]]); 1031 } 1032 1033 INST(FI_CLIST_ADD, 2, 1) { /* (Extended) Community list add */ 1034 ARG_ANY(1); 1035 ARG_ANY(2); 1036 RESULT_TYPE(f1->type); 1037 1038 if (v1.type == T_PATH) 1039 runtime("Can't add to path"); 1040 1041 else if (v1.type == T_CLIST) 1042 { 1043 /* Community (or cluster) list */ 1044 struct f_val dummy; 1045 1046 if ((v2.type == T_PAIR) || (v2.type == T_QUAD)) 1047 RESULT_(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, v2.val.i) ]]); 1048 /* IP->Quad implicit conversion */ 1049 else if (val_is_ip4(&v2)) 1050 RESULT_(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]); 1051 else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy)) 1052 runtime("Can't add set"); 1053 else if (v2.type == T_CLIST) 1054 RESULT_(T_CLIST, ad, [[ int_set_union(fpool, v1.val.ad, v2.val.ad) ]]); 1055 else 1056 runtime("Can't add non-pair"); 1057 } 1058 1059 else if (v1.type == T_ECLIST) 1060 { 1061 /* v2.val is either EC or EC-set */ 1062 if ((v2.type == T_SET) && eclist_set_type(v2.val.t)) 1063 runtime("Can't add set"); 1064 else if (v2.type == T_ECLIST) 1065 RESULT_(T_ECLIST, ad, [[ ec_set_union(fpool, v1.val.ad, v2.val.ad) ]]); 1066 else if (v2.type != T_EC) 1067 runtime("Can't add non-ec"); 1068 else 1069 RESULT_(T_ECLIST, ad, [[ ec_set_add(fpool, v1.val.ad, v2.val.ec) ]]); 1070 } 1071 1072 else if (v1.type == T_LCLIST) 1073 { 1074 /* v2.val is either LC or LC-set */ 1075 if ((v2.type == T_SET) && lclist_set_type(v2.val.t)) 1076 runtime("Can't add set"); 1077 else if (v2.type == T_LCLIST) 1078 RESULT_(T_LCLIST, ad, [[ lc_set_union(fpool, v1.val.ad, v2.val.ad) ]]); 1079 else if (v2.type != T_LC) 1080 runtime("Can't add non-lc"); 1081 else 1082 RESULT_(T_LCLIST, ad, [[ lc_set_add(fpool, v1.val.ad, v2.val.lc) ]]); 1083 1084 } 1085 1086 else 1087 runtime("Can't add to non-[e|l]clist"); 1088 } 1089 1090 INST(FI_CLIST_DEL, 2, 1) { /* (Extended) Community list add or delete */ 1091 ARG_ANY(1); 1092 ARG_ANY(2); 1093 RESULT_TYPE(f1->type); 1094 1095 if (v1.type == T_PATH) 1096 { 1097 const struct f_tree *set = NULL; 1098 u32 key = 0; 1099 1100 if (v2.type == T_INT) 1101 key = v2.val.i; 1102 else if ((v2.type == T_SET) && (v2.val.t->from.type == T_INT)) 1103 set = v2.val.t; 1104 else 1105 runtime("Can't delete non-integer (set)"); 1106 1107 RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, set, key, 0) ]]); 1108 } 1109 1110 else if (v1.type == T_CLIST) 1111 { 1112 /* Community (or cluster) list */ 1113 struct f_val dummy; 1114 1115 if ((v2.type == T_PAIR) || (v2.type == T_QUAD)) 1116 RESULT_(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, v2.val.i) ]]); 1117 /* IP->Quad implicit conversion */ 1118 else if (val_is_ip4(&v2)) 1119 RESULT_(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]); 1120 else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy) || (v2.type == T_CLIST)) 1121 RESULT_(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 0) ]]); 1122 else 1123 runtime("Can't delete non-pair"); 1124 } 1125 1126 else if (v1.type == T_ECLIST) 1127 { 1128 /* v2.val is either EC or EC-set */ 1129 if ((v2.type == T_SET) && eclist_set_type(v2.val.t) || (v2.type == T_ECLIST)) 1130 RESULT_(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 0) ]]); 1131 else if (v2.type != T_EC) 1132 runtime("Can't delete non-ec"); 1133 else 1134 RESULT_(T_ECLIST, ad, [[ ec_set_del(fpool, v1.val.ad, v2.val.ec) ]]); 1135 } 1136 1137 else if (v1.type == T_LCLIST) 1138 { 1139 /* v2.val is either LC or LC-set */ 1140 if ((v2.type == T_SET) && lclist_set_type(v2.val.t) || (v2.type == T_LCLIST)) 1141 RESULT_(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 0) ]]); 1142 else if (v2.type != T_LC) 1143 runtime("Can't delete non-lc"); 1144 else 1145 RESULT_(T_LCLIST, ad, [[ lc_set_del(fpool, v1.val.ad, v2.val.lc) ]]); 1146 } 1147 1148 else 1149 runtime("Can't delete in non-[e|l]clist"); 1150 } 1151 1152 INST(FI_CLIST_FILTER, 2, 1) { /* (Extended) Community list add or delete */ 1153 ARG_ANY(1); 1154 ARG_ANY(2); 1155 RESULT_TYPE(f1->type); 1156 1157 if (v1.type == T_PATH) 1158 { 1159 u32 key = 0; 1160 1161 if ((v2.type == T_SET) && (v2.val.t->from.type == T_INT)) 1162 RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, v2.val.t, key, 1) ]]); 1163 else 1164 runtime("Can't filter integer"); 1165 } 1166 1167 else if (v1.type == T_CLIST) 1168 { 1169 /* Community (or cluster) list */ 1170 struct f_val dummy; 1171 1172 if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy) || (v2.type == T_CLIST)) 1173 RESULT_(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 1) ]]); 1174 else 1175 runtime("Can't filter pair"); 1176 } 1177 1178 else if (v1.type == T_ECLIST) 1179 { 1180 /* v2.val is either EC or EC-set */ 1181 if ((v2.type == T_SET) && eclist_set_type(v2.val.t) || (v2.type == T_ECLIST)) 1182 RESULT_(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 1) ]]); 1183 else 1184 runtime("Can't filter ec"); 1185 } 1186 1187 else if (v1.type == T_LCLIST) 1188 { 1189 /* v2.val is either LC or LC-set */ 1190 if ((v2.type == T_SET) && lclist_set_type(v2.val.t) || (v2.type == T_LCLIST)) 1191 RESULT_(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 1) ]]); 1192 else 1193 runtime("Can't filter lc"); 1194 } 1195 1196 else 1197 runtime("Can't filter non-[e|l]clist"); 1198 } 1199 1200 INST(FI_ROA_CHECK_IMPLICIT, 0, 1) { /* ROA Check */ 1201 NEVER_CONSTANT; 1202 RTC(1); 1203 struct rtable *table = rtc->table; 1204 ACCESS_RTE; 1205 ACCESS_EATTRS; 1206 const net_addr *net = (*fs->rte)->net->n.addr; 1207 1208 /* We ignore temporary attributes, probably not a problem here */ 1209 /* 0x02 is a value of BA_AS_PATH, we don't want to include BGP headers */ 1210 eattr *e = ea_find(*fs->eattrs, EA_CODE(PROTOCOL_BGP, 0x02)); 1211 1212 if (!e || ((e->type & EAF_TYPE_MASK) != EAF_TYPE_AS_PATH)) 1213 runtime("Missing AS_PATH attribute"); 1214 1215 u32 as = 0; 1216 as_path_get_last(e->u.ptr, &as); 1217 1218 if (!table) 1219 runtime("Missing ROA table"); 1220 1221 if (table->addr_type != NET_ROA4 && table->addr_type != NET_ROA6) 1222 runtime("Table type must be either ROA4 or ROA6"); 1223 1224 if (table->addr_type != (net->type == NET_IP4 ? NET_ROA4 : NET_ROA6)) 1225 RESULT(T_ENUM_ROA, i, ROA_UNKNOWN); /* Prefix and table type mismatch */ 1226 else 1227 RESULT(T_ENUM_ROA, i, [[ net_roa_check(table, net, as) ]]); 1228 } 1229 1230 INST(FI_ROA_CHECK_EXPLICIT, 2, 1) { /* ROA Check */ 1231 NEVER_CONSTANT; 1232 ARG(1, T_NET); 1233 ARG(2, T_INT); 1234 RTC(3); 1235 struct rtable *table = rtc->table; 1236 1237 u32 as = v2.val.i; 1238 1239 if (!table) 1240 runtime("Missing ROA table"); 1241 1242 if (table->addr_type != NET_ROA4 && table->addr_type != NET_ROA6) 1243 runtime("Table type must be either ROA4 or ROA6"); 1244 1245 if (table->addr_type != (v1.val.net->type == NET_IP4 ? NET_ROA4 : NET_ROA6)) 1246 RESULT(T_ENUM_ROA, i, ROA_UNKNOWN); /* Prefix and table type mismatch */ 1247 else 1248 RESULT(T_ENUM_ROA, i, [[ net_roa_check(table, v1.val.net, as) ]]); 1249 1250 } 1251 1252 INST(FI_FORMAT, 1, 0) { /* Format */ 1253 ARG_ANY(1); 1254 RESULT(T_STRING, s, val_format_str(fpool, &v1)); 1255 } 1256 1257 INST(FI_ASSERT, 1, 0) { /* Birdtest Assert */ 1258 NEVER_CONSTANT; 1259 ARG(1, T_BOOL); 1260 1261 FID_MEMBER(char *, s, [[strcmp(f1->s, f2->s)]], "string %s", item->s); 1262 1263 ASSERT(s); 1264 1265 if (!bt_assert_hook) 1266 runtime("No bt_assert hook registered, can't assert"); 1267 1268 bt_assert_hook(v1.val.i, what); 1269 } 1270