diff options
Diffstat (limited to 'filter')
-rw-r--r-- | filter/Makefile | 16 | ||||
-rw-r--r-- | filter/config.Y | 185 | ||||
-rw-r--r-- | filter/dump.m4 | 43 | ||||
-rw-r--r-- | filter/f-inst.c | 983 | ||||
-rw-r--r-- | filter/f-util.c | 29 | ||||
-rw-r--r-- | filter/filter.c | 544 | ||||
-rw-r--r-- | filter/filter.h | 325 | ||||
-rw-r--r-- | filter/filter_test.c | 7 | ||||
-rw-r--r-- | filter/interpret.m4 | 57 | ||||
-rw-r--r-- | filter/line-size.m4 | 30 | ||||
-rw-r--r-- | filter/postfixify.m4 | 55 | ||||
-rw-r--r-- | filter/same.m4 | 56 | ||||
-rw-r--r-- | filter/tree.c | 30 | ||||
-rw-r--r-- | filter/tree_test.c | 10 | ||||
-rw-r--r-- | filter/trie.c | 14 |
15 files changed, 1402 insertions, 982 deletions
diff --git a/filter/Makefile b/filter/Makefile index 0979a34f..c74ba2f3 100644 --- a/filter/Makefile +++ b/filter/Makefile @@ -5,10 +5,22 @@ $(cf-local) M4FLAGS_FILTERS=$(filter-out -s,$(M4FLAGS)) -$(o)f-inst-interpret.c: $(s)interpret.m4 $(s)f-inst.c +$(o)f-inst-line-size.c: $(s)line-size.m4 $(s)f-inst.c $(objdir)/.dir-stamp $(M4) $(M4FLAGS_FILTERS) -P $^ >$@ -$(o)filter.o: $(o)f-inst-interpret.c +$(o)f-inst-postfixify.c: $(s)postfixify.m4 $(s)f-inst.c $(objdir)/.dir-stamp + $(M4) $(M4FLAGS_FILTERS) -P $^ >$@ + +$(o)f-inst-interpret.c: $(s)interpret.m4 $(s)f-inst.c $(objdir)/.dir-stamp + $(M4) $(M4FLAGS_FILTERS) -P $^ >$@ + +$(o)f-inst-same.c: $(s)same.m4 $(s)f-inst.c $(objdir)/.dir-stamp + $(M4) $(M4FLAGS_FILTERS) -P $^ >$@ + +$(o)f-inst-dump.c: $(s)dump.m4 $(s)f-inst.c $(objdir)/.dir-stamp + $(M4) $(M4FLAGS_FILTERS) -P $^ >$@ + +$(o)filter.o: $(o)f-inst-interpret.c $(o)f-inst-line-size.c $(o)f-inst-postfixify.c $(o)f-inst-same.c $(o)f-inst-dump.c tests_src := tree_test.c filter_test.c trie_test.c tests_targets := $(tests_targets) $(tests-target-files) diff --git a/filter/config.Y b/filter/config.Y index e1f3fec8..a79b1582 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -158,20 +158,20 @@ f_new_lc_item(u32 f1, u32 t1, u32 f2, u32 t2, u32 f3, u32 t3) static inline struct f_inst * f_generate_empty(struct f_dynamic_attr dyn) { - struct f_inst *e = f_new_inst(FI_EMPTY); + struct f_inst *e = f_new_inst(FI_CONSTANT); switch (dyn.type & EAF_TYPE_MASK) { case EAF_TYPE_AS_PATH: - e->aux = T_PATH; + e->val = f_const_empty_path; break; case EAF_TYPE_INT_SET: - e->aux = T_CLIST; + e->val = f_const_empty_clist; break; case EAF_TYPE_EC_SET: - e->aux = T_ECLIST; + e->val = f_const_empty_eclist; break; case EAF_TYPE_LC_SET: - e->aux = T_LCLIST; + e->val = f_const_empty_lclist; break; default: cf_error("Can't empty that attribute"); @@ -302,23 +302,34 @@ f_generate_lc(struct f_inst *t1, struct f_inst *t2, struct f_inst *t3) } static inline struct f_inst * -f_generate_path_mask(struct f_path_mask *t) +f_generate_path_mask(struct f_inst *t) { - for (struct f_path_mask *tt = t; tt; tt = tt->next) { - if (tt->kind == PM_ASN_EXPR) { - struct f_inst *mrv = f_new_inst(FI_PATHMASK_CONSTRUCT); - mrv->a[0].p = t; - return mrv; - } + uint len = 0; + uint dyn = 0; + for (const struct f_inst *tt = t; tt; tt = tt->next) { + if (tt->fi_code != FI_CONSTANT) + dyn++; + len++; + } + + if (dyn) { + struct f_inst *pmc = f_new_inst(FI_PATHMASK_CONSTRUCT); + pmc->a[0].p = t; + pmc->a[1].i = len; + return pmc; } - struct f_inst *rv = f_new_inst(FI_CONSTANT); - rv->val = (struct f_val) { - .type = T_PATH_MASK, - .val.path_mask = t, - }; + struct f_path_mask *pm = cfg_allocz(sizeof(struct f_path_mask) + len * sizeof(struct f_path_mask_item)); - return rv; + uint i = 0; + for (const struct f_inst *tt = t; tt; tt = tt->next) + pm->item[i++] = tt->val.val.pmi; + + pm->len = i; + struct f_inst *pmc = f_new_inst(FI_CONSTANT); + pmc->val = (struct f_val) { .type = T_PATH_MASK, .val.path_mask = pm, }; + + return pmc; } /* @@ -409,7 +420,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, %nonassoc THEN %nonassoc ELSE -%type <x> term block cmds cmds_int cmd function_body constant constructor print_one print_list var_list var_listn function_call symbol bgp_path_expr +%type <x> term block cmds cmds_int cmd function_body constant constructor print_one print_list var_list var_listn function_call symbol bgp_path_expr bgp_path bgp_path_tail %type <fda> dynamic_attr %type <fsa> static_attr %type <f> filter filter_body where_filter @@ -420,7 +431,6 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, %type <v> set_atom switch_atom fipa %type <px> fprefix %type <s> decls declsn one_decl function_params -%type <h> bgp_path bgp_path_tail %type <t> get_cf_position CF_GRAMMAR @@ -438,7 +448,7 @@ filter_def: conf: filter_eval ; filter_eval: - EVAL term { f_eval_int($2); } + EVAL term { f_eval_int(f_postfixify($2)); } ; conf: custom_attr ; @@ -530,7 +540,7 @@ filter_body: function_body { struct filter *f = cfg_alloc(sizeof(struct filter)); f->name = NULL; - f->root = $1; + f->root = f_postfixify($1); $$ = f; } ; @@ -547,21 +557,25 @@ where_filter: WHERE term { /* Construct 'IF term THEN { ACCEPT; } ELSE { REJECT; }' */ struct filter *f = cfg_alloc(sizeof(struct filter)); - struct f_inst *i, *acc, *rej; - acc = f_new_inst(FI_PRINT_AND_DIE); /* ACCEPT */ - acc->a[0].p = NULL; - acc->a[1].i = F_ACCEPT; - rej = f_new_inst(FI_PRINT_AND_DIE); /* REJECT */ - rej->a[0].p = NULL; - rej->a[1].i = F_REJECT; - i = f_new_inst(FI_CONDITION); /* IF */ - i->a[0].p = $2; - i->a[1].p = acc; - i->a[2].p = rej; + struct f_inst acc = { + .fi_code = FI_PRINT_AND_DIE, + .a = { { .p = NULL }, { .i = F_ACCEPT } }, + .lineno = ifs->lino, + }; + struct f_inst rej = { + .fi_code = FI_PRINT_AND_DIE, + .a = { { .p = NULL }, { .i = F_REJECT } }, + .lineno = ifs->lino, + }; + struct f_inst i = { + .fi_code = FI_CONDITION, + .a = { { .p = $2 }, { .p = &acc }, { .p = &rej } }, + .lineno = ifs->lino, + }; f->name = NULL; - f->root = i; + f->root = f_postfixify(&i); $$ = f; - } + } ; function_params: @@ -587,7 +601,9 @@ function_def: $2 = cf_define_symbol($2, SYM_FUNCTION, NULL); cf_push_scope($2); } function_params function_body { - $2->def = $5; + struct f_inst *vc = f_new_inst(FI_CONSTANT); + vc->val = (struct f_val) { .type = T_VOID }; + $2->def = f_postfixify_concat($5, vc, NULL); $2->aux2 = $4; DBG("Hmm, we've got one function here - %s\n", $2->name); cf_pop_scope(); @@ -639,7 +655,7 @@ set_atom: | VPN_RD { $$.type = T_RD; $$.val.ec = $1; } | ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); } | '(' term ')' { - if (f_eval($2, cfg_mem, &($$)) > F_RETURN) cf_error("Runtime error"); + if (f_eval(f_postfixify($2), cfg_mem, &($$)) > F_RETURN) cf_error("Runtime error"); if (!f_valid_set_type($$.type)) cf_error("Set-incompatible type"); } | SYM { @@ -651,13 +667,13 @@ set_atom: switch_atom: NUM { $$.type = T_INT; $$.val.i = $1; } - | '(' term ')' { $$.type = T_INT; $$.val.i = f_eval_int($2); } + | '(' term ')' { $$.type = T_INT; $$.val.i = f_eval_int(f_postfixify($2)); } | fipa { $$ = $1; } | ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); } ; cnum: - term { $$ = f_eval_int($1); } + term { $$ = f_eval_int(f_postfixify($1)); } pair_item: '(' cnum ',' cnum ')' { $$ = f_new_pair_item($2, $2, $4, $4); } @@ -744,15 +760,16 @@ switch_body: /* EMPTY */ { $$ = NULL; } | switch_body switch_items ':' cmds { /* Fill data fields */ struct f_tree *t; + struct f_line *line = f_postfixify($4); for (t = $2; t; t = t->left) - t->data = $4; + t->data = line; $$ = f_merge_items($1, $2); } | switch_body ELSECOL cmds { struct f_tree *t = f_new_tree(); t->from.type = t->to.type = T_VOID; t->right = t; - t->data = $3; + t->data = f_postfixify($3); $$ = f_merge_items($1, t); } ; @@ -767,11 +784,11 @@ bgp_path: ; bgp_path_tail: - NUM bgp_path_tail { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN; $$->val = $1; } - | NUM DDOT NUM bgp_path_tail { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $4; $$->kind = PM_ASN_RANGE; $$->val = $1; $$->val2 = $3; } - | '*' bgp_path_tail { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; } - | '?' bgp_path_tail { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_QUESTION; } - | bgp_path_expr bgp_path_tail { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN_EXPR; $$->val = (uintptr_t) $1; } + NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT); $$->next = $2; $$->val = (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .asn = $1, .kind = PM_ASN, }, }; } + | NUM DDOT NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT); $$->next = $4; $$->val = (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .from = $1, .to = $3, .kind = PM_ASN_RANGE }, }; } + | '*' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT); $$->next = $2; $$->val = (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_ASTERISK }, }; } + | '?' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT); $$->next = $2; $$->val = (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_QUESTION }, }; } + | bgp_path_expr bgp_path_tail { $$ = $1; $$->next = $2; } | { $$ = NULL; } ; @@ -831,12 +848,11 @@ symbol: switch ($1->class & 0xffff) { case SYM_CONSTANT_RANGE: $$ = f_new_inst(FI_CONSTANT_INDIRECT); - goto cv_common; + $$->a[0].p = $1; + break; case SYM_VARIABLE_RANGE: $$ = f_new_inst(FI_VARIABLE); - cv_common: - $$->a[0].p = $1->def; - $$->a[1].p = $1->name; + $$->a[0].p = $1; break; case SYM_ATTRIBUTE: $$ = f_new_inst_da(FI_EA_GET, *((struct f_dynamic_attr *) $1->def)); @@ -847,15 +863,15 @@ symbol: } static_attr: - FROM { $$ = f_new_static_attr(T_IP, SA_FROM, 1); } - | GW { $$ = f_new_static_attr(T_IP, SA_GW, 1); } - | NET { $$ = f_new_static_attr(T_NET, SA_NET, 0); } - | PROTO { $$ = f_new_static_attr(T_STRING, SA_PROTO, 0); } - | SOURCE { $$ = f_new_static_attr(T_ENUM_RTS, SA_SOURCE, 0); } - | SCOPE { $$ = f_new_static_attr(T_ENUM_SCOPE, SA_SCOPE, 1); } - | DEST { $$ = f_new_static_attr(T_ENUM_RTD, SA_DEST, 1); } - | IFNAME { $$ = f_new_static_attr(T_STRING, SA_IFNAME, 1); } - | IFINDEX { $$ = f_new_static_attr(T_INT, SA_IFINDEX, 0); } + FROM { $$ = f_new_static_attr(T_IP, SA_FROM, 0); } + | GW { $$ = f_new_static_attr(T_IP, SA_GW, 0); } + | NET { $$ = f_new_static_attr(T_NET, SA_NET, 1); } + | PROTO { $$ = f_new_static_attr(T_STRING, SA_PROTO, 1); } + | SOURCE { $$ = f_new_static_attr(T_ENUM_RTS, SA_SOURCE, 1); } + | SCOPE { $$ = f_new_static_attr(T_ENUM_SCOPE, SA_SCOPE, 0); } + | DEST { $$ = f_new_static_attr(T_ENUM_RTD, SA_DEST, 0); } + | IFNAME { $$ = f_new_static_attr(T_STRING, SA_IFNAME, 0); } + | IFINDEX { $$ = f_new_static_attr(T_INT, SA_IFINDEX, 1); } ; term: @@ -908,42 +924,23 @@ term: | rtadot dynamic_attr '.' RESET{ } */ - | '+' EMPTY '+' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_PATH; } - | '-' EMPTY '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_CLIST; } - | '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_ECLIST; } - | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_LCLIST; } + | '+' EMPTY '+' { $$ = f_new_inst(FI_CONSTANT); $$->val = f_const_empty_path; } + | '-' EMPTY '-' { $$ = f_new_inst(FI_CONSTANT); $$->val = f_const_empty_clist; } + | '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_CONSTANT); $$->val = f_const_empty_eclist; } + | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_CONSTANT); $$->val = f_const_empty_lclist; } | PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND); $$->a[0].p = $3; $$->a[1].p = $5; } - | ADD '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD_DEL); $$->a[0].p = $3; $$->a[1].p = $5; $$->aux = 'a'; } - | DELETE '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD_DEL); $$->a[0].p = $3; $$->a[1].p = $5; $$->aux = 'd'; } - | FILTER '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD_DEL); $$->a[0].p = $3; $$->a[1].p = $5; $$->aux = 'f'; } + | ADD '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD); $$->a[0].p = $3; $$->a[1].p = $5; } + | DELETE '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_DEL); $$->a[0].p = $3; $$->a[1].p = $5; } + | FILTER '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_FILTER); $$->a[0].p = $3; $$->a[1].p = $5; } - | ROA_CHECK '(' rtable ')' { $$ = f_generate_roa_check($3, NULL, NULL); } - | ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_generate_roa_check($3, $5, $7); } + | ROA_CHECK '(' rtable ')' { $$ = f_new_inst(FI_ROA_CHECK_IMPLICIT); $$->a[0].rtc = $3; } + | ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK_EXPLICIT); $$->a[2].rtc = $3; $$->a[0].p = $5; $$->a[1].p = $7; } | FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT); $$->a[0].p = $3; } /* | term '.' LEN { $$->code = P('P','l'); } */ -/* function_call is inlined here */ - | SYM '(' var_list ')' { - struct symbol *sym; - struct f_inst *inst = $3; - if ($1->class != SYM_FUNCTION) - cf_error("You can't call something which is not a function. Really."); - DBG("You are calling function %s\n", $1->name); - $$ = f_new_inst(FI_CALL); - $$->a[0].p = inst; - $$->a[1].p = $1->def; - sym = $1->aux2; - while (sym || inst) { - if (!sym || !inst) - cf_error("Wrong number of arguments for function %s.", $1->name); - DBG( "You should pass parameter called %s\n", sym->name); - inst->a[0].p = sym; - sym = sym->aux2; - inst = inst->next; - } - } + | function_call ; break_command: @@ -1022,7 +1019,7 @@ cmd: } | rtadot static_attr '=' term ';' { $$ = f_new_inst_sa(FI_RTA_SET, $2); - if (!$$->a[0].i) + if ($$->sa.readonly) cf_error( "This static attribute is read-only."); $$->a[0].p = $4; } @@ -1036,7 +1033,7 @@ cmd: $$->a[0].p = NULL; } | break_command print_list ';' { $$ = f_new_inst(FI_PRINT_AND_DIE); $$->a[0].p = $2; $$->a[1].i = $1; } - | function_call ';' { $$ = $1; } + | function_call ';' { $$ = f_new_inst(FI_DROP_RESULT); $$->a[0].p = $1; } | CASE term '{' switch_body '}' { $$ = f_new_inst(FI_SWITCH); $$->a[0].p = $2; @@ -1044,10 +1041,10 @@ cmd: } | rtadot dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($2); } - | rtadot dynamic_attr '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex( FI_PATH_PREPEND, 'x', $2, $6 ); } - | rtadot dynamic_attr '.' ADD '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_ADD_DEL, 'a', $2, $6 ); } - | rtadot dynamic_attr '.' DELETE '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_ADD_DEL, 'd', $2, $6 ); } - | rtadot dynamic_attr '.' FILTER '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_ADD_DEL, 'f', $2, $6 ); } + | rtadot dynamic_attr '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex( FI_PATH_PREPEND, $2, $6 ); } + | rtadot dynamic_attr '.' ADD '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_ADD, $2, $6 ); } + | rtadot dynamic_attr '.' DELETE '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_DEL, $2, $6 ); } + | rtadot dynamic_attr '.' FILTER '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_FILTER, $2, $6 ); } | BT_ASSERT '(' get_cf_position term get_cf_position ')' ';' { $$ = assert_done($4, $3 + 1, $5 - 1); } ; diff --git a/filter/dump.m4 b/filter/dump.m4 new file mode 100644 index 00000000..913a7652 --- /dev/null +++ b/filter/dump.m4 @@ -0,0 +1,43 @@ +m4_divert(-1)m4_dnl +# +# BIRD -- Dumping instruction lines +# +# (c) 2018 Maria Matejka <mq@jmq.cz> +# +# Can be freely distributed and used under the terms of the GNU GPL. +# + +# Common aliases +m4_define(DNL, `m4_dnl') + +m4_define(INST, `m4_divert(1)break; case $1: +m4_divert(-1)')) +m4_define(LINE, `m4_divert(1)f_dump_line(item->lines[$2], indent + 1); +m4_divert(-1)') +m4_define(LINEP, `LINE($@)') +m4_define(SYMBOL, `m4_divert(1)debug("%ssymbol %s\n", INDENT, item->sym->name); +m4_divert(-1)') +m4_define(VALI, `m4_divert(1)debug("%svalue %s\n", INDENT, val_dump(item->vp)); +m4_divert(-1)') +m4_define(VALI, `m4_divert(1)debug("%svalue %s\n", INDENT, val_dump(item->vp)); +m4_divert(-1)') +m4_define(FRET, `m4_divert(1)debug("%sfilter return value %d\n", INDENT, item->fret); +m4_divert(-1)') +m4_define(ECS, `m4_divert(1)debug("%sec subtype %d\n", INDENT, item->ecs); +m4_divert(-1)') +m4_define(RTC, `m4_divert(1)debug("%sroute table %s\n", INDENT, item->rtc->name); +m4_divert(-1)') +m4_define(STATIC_ATTR, `m4_divert(1)debug("%sstatic attribute %u/%u/%u\n", INDENT, item->sa.f_type, item->sa.sa_code, item->sa.readonly); +m4_divert(-1)') +m4_define(DYNAMIC_ATTR, `m4_divert(1)debug("%sdynamic attribute %u/%u/%u/%u\n", INDENT, item->da.type, item->da.bit, item->da.f_type, item->da.ea_code); +m4_divert(-1)') +m4_define(DUMP, `m4_divert(1)$1m4_divert(-1)') + +m4_m4wrap(` +m4_divert(0)DNL +case FI_NOP: bug("This shall not happen"); +m4_undivert(1) +break; default: bug( "Unknown instruction %d (%c)", item->fi_code, item->fi_code & 0xff); +') + +m4_changequote([[,]]) diff --git a/filter/f-inst.c b/filter/f-inst.c index ef5d4f06..02c88409 100644 --- a/filter/f-inst.c +++ b/filter/f-inst.c @@ -10,269 +10,282 @@ */ /* Binary operators */ - INST(FI_ADD) { - ARG_T(1,0,T_INT); - ARG_T(2,1,T_INT); - res.val.i += v1.val.i; - } - INST(FI_SUBTRACT) { - ARG_T(1,0,T_INT); - ARG_T(2,1,T_INT); - res.val.i -= v1.val.i; - } - INST(FI_MULTIPLY) { - ARG_T(1,0,T_INT); - ARG_T(2,1,T_INT); - res.val.i *= v1.val.i; - } - INST(FI_DIVIDE) { - ARG_T(1,0,T_INT); - ARG_T(2,1,T_INT); - if (v1.val.i == 0) runtime( "Mother told me not to divide by 0" ); - res.val.i /= v1.val.i; - } - INST(FI_AND) { - ARG_T(1,0,T_BOOL); + INST(FI_ADD, 2, 1) { + ARG(1,T_INT); + ARG(2,T_INT); + res.val.i += v2.val.i; + RESULT_OK; + } + INST(FI_SUBTRACT, 2, 1) { + ARG(1,T_INT); + ARG(2,T_INT); + res.val.i -= v2.val.i; + RESULT_OK; + } + INST(FI_MULTIPLY, 2, 1) { + ARG(1,T_INT); + ARG(2,T_INT); + res.val.i *= v2.val.i; + RESULT_OK; + } + INST(FI_DIVIDE, 2, 1) { + ARG(1,T_INT); + ARG(2,T_INT); + if (v2.val.i == 0) runtime( "Mother told me not to divide by 0" ); + res.val.i /= v2.val.i; + RESULT_OK; + } + INST(FI_AND, 1, 1) { + ARG(1,T_BOOL); if (res.val.i) - ARG_T(2,0,T_BOOL); + LINE(2,0); + else + RESULT_OK; } - INST(FI_OR) { - ARG_T(1,0,T_BOOL); + INST(FI_OR, 1, 1) { + ARG(1,T_BOOL); if (!res.val.i) - ARG_T(2,0,T_BOOL); + LINE(2,0); + else + RESULT_OK; } - INST(FI_PAIR_CONSTRUCT) { + INST(FI_PAIR_CONSTRUCT, 2, 1) { ARG(1,T_INT); ARG(2,T_INT); - u1 = v1.val.i; - u2 = v2.val.i; + uint u1 = v1.val.i; + uint u2 = v2.val.i; if ((u1 > 0xFFFF) || (u2 > 0xFFFF)) runtime( "Can't operate with value out of bounds in pair constructor" ); - res.val.i = (u1 << 16) | u2; - res.type = T_PAIR; + RESULT(T_PAIR, i, (u1 << 16) | u2); } + INST(FI_EC_CONSTRUCT, 2, 1) { + ARG_ANY(1); + ARG(2, T_INT); + ECS; - INST(FI_EC_CONSTRUCT) { - { - ARG_ANY(1); - ARG(2, T_INT); - - int check, ipv4_used; - u32 key, val; - - if (v1.type == T_INT) { - ipv4_used = 0; key = v1.val.i; - } - else if (v1.type == T_QUAD) { - ipv4_used = 1; key = v1.val.i; - } - /* IP->Quad implicit conversion */ - else if (val_is_ip4(v1)) { - ipv4_used = 1; key = ipa_to_u32(v1.val.ip); - } - else - runtime("Can't operate with key of non-integer/IPv4 type in EC constructor"); - - val = v2.val.i; - - /* XXXX */ - res.type = T_EC; + int check, ipv4_used; + u32 key, val; - if (what->aux == EC_GENERIC) { - check = 0; res.val.ec = ec_generic(key, val); - } - else if (ipv4_used) { - check = 1; res.val.ec = ec_ip4(what->aux, key, val); - } - else if (key < 0x10000) { - check = 0; res.val.ec = ec_as2(what->aux, key, val); - } - else { - check = 1; res.val.ec = ec_as4(what->aux, key, val); - } + if (v1.type == T_INT) { + ipv4_used = 0; key = v1.val.i; + } + else if (v1.type == T_QUAD) { + ipv4_used = 1; key = v1.val.i; + } + /* IP->Quad implicit conversion */ + else if (val_is_ip4(&v1)) { + ipv4_used = 1; key = ipa_to_u32(v1.val.ip); + } + else + runtime("Argument 1 of instruction FI_EC_CONSTRUCT must be integer or IPv4 address, got 0x%02x"); - if (check && (val > 0xFFFF)) - runtime("Can't operate with value out of bounds in EC constructor"); + val = v2.val.i; + if (ecs == EC_GENERIC) { + check = 0; RESULT(T_EC, ec, ec_generic(key, val)); + } + else if (ipv4_used) { + check = 1; RESULT(T_EC, ec, ec_ip4(ecs, key, val)); } + else if (key < 0x10000) { + check = 0; RESULT(T_EC, ec, ec_as2(ecs, key, val)); + } + else { + check = 1; RESULT(T_EC, ec, ec_as4(ecs, key, val)); } - INST(FI_LC_CONSTRUCT) { - { - ARG(1, T_INT); - ARG(2, T_INT); - ARG(3, T_INT); + if (check && (val > 0xFFFF)) + runtime("Value %u > %u out of bounds in EC constructor", val, 0xFFFF); + } - res.type = T_LC; - res.val.lc = (lcomm) { v1.val.i, v2.val.i, v3.val.i }; + INST(FI_LC_CONSTRUCT, 3, 1) { + ARG(1, T_INT); + ARG(2, T_INT); + ARG(3, T_INT); + RESULT(T_LC, lc, [[(lcomm) { v1.val.i, v2.val.i, v3.val.i }]]); + } - } + INST(FI_PATHMASK_CONSTRUCT, 0, 1) { + ARG_ANY(1); + COUNT(2); + if (vstk.cnt < what->count) /* TODO: make this check systematic */ + runtime("Construction of BGP path mask from %u elements must have at least that number of elements", what->count); + + struct f_path_mask *pm = lp_alloc(fs->pool, sizeof(struct f_path_mask) + what->count * sizeof(struct f_path_mask_item)); + for (uint i=0; i<what->count; i++) { +//#define pv vstk.val[vstk.cnt-i-1] +#define pv vstk.val[vstk.cnt - what->count + i] + switch (pv.type) { + case T_PATH_MASK_ITEM: + pm->item[i] = pv.val.pmi; + break; + case T_INT: + pm->item[i] = (struct f_path_mask_item) { + .asn = pv.val.i, + .kind = PM_ASN, + }; + break; + default: + runtime( "Error resolving path mask template: value not an integer" ); + } } - INST(FI_PATHMASK_CONSTRUCT) { - { - struct f_path_mask *tt = what->a[0].p, *vbegin, **vv = &vbegin; - - while (tt) { - *vv = lp_alloc(fs->pool, sizeof(struct f_path_mask)); - if (tt->kind == PM_ASN_EXPR) { - INTERPRET((struct f_inst *) tt->val, 0); - (*vv)->kind = PM_ASN; - if (res.type != T_INT) { - runtime( "Error resolving path mask template: value not an integer" ); - return F_ERROR; - } - - (*vv)->val = res.val.i; - } else { - **vv = *tt; - } - tt = tt->next; - vv = &((*vv)->next); - } + vstk.cnt -= what->count; + pm->len = what->count; - res = (struct f_val) { .type = T_PATH_MASK, .val.path_mask = vbegin }; - } - } + RESULT(T_PATH_MASK, path_mask, pm); + } /* Relational operators */ - INST(FI_NEQ) { + INST(FI_NEQ, 2, 1) { ARG_ANY(1); ARG_ANY(2); - res.type = T_BOOL; - res.val.i = !val_same(v1, v2); + RESULT(T_BOOL, i, !val_same(&v1, &v2)); } - INST(FI_EQ) { + INST(FI_EQ, 2, 1) { ARG_ANY(1); ARG_ANY(2); - res.type = T_BOOL; - res.val.i = val_same(v1, v2); + RESULT(T_BOOL, i, val_same(&v1, &v2)); } - INST(FI_LT) { + INST(FI_LT, 2, 1) { ARG_ANY(1); ARG_ANY(2); - i = val_compare(v1, v2); - if (i==CMP_ERROR) + int i = val_compare(&v1, &v2); + if (i == CMP_ERROR) runtime( "Can't compare values of incompatible types" ); - res.type = T_BOOL; - res.val.i = (i == -1); + RESULT(T_BOOL, i, (i == -1)); } - INST(FI_LTE) { + INST(FI_LTE, 2, 1) { ARG_ANY(1); ARG_ANY(2); - i = val_compare(v1, v2); - if (i==CMP_ERROR) + int i = val_compare(&v1, &v2); + if (i == CMP_ERROR) runtime( "Can't compare values of incompatible types" ); - res.type = T_BOOL; - res.val.i = (i != 1); + RESULT(T_BOOL, i, (i != 1)); } - INST(FI_NOT) { - ARG_T(1,0,T_BOOL); - res.val.i = !res.val.i; + INST(FI_NOT, 1, 1) { + ARG(1,T_BOOL); + RESULT(T_BOOL, i, !v1.val.i); } - INST(FI_MATCH) { + INST(FI_MATCH, 2, 1) { ARG_ANY(1); ARG_ANY(2); - res.type = T_BOOL; - res.val.i = val_in_range(v1, v2); - if (res.val.i == CMP_ERROR) + int i = val_in_range(&v1, &v2); + if (i == CMP_ERROR) runtime( "~ applied on unknown type pair" ); - res.val.i = !!res.val.i; + RESULT(T_BOOL, i, !!i); } - INST(FI_NOT_MATCH) { + INST(FI_NOT_MATCH, 2, 1) { ARG_ANY(1); ARG_ANY(2); - res.type = T_BOOL; - res.val.i = val_in_range(v1, v2); + int i = val_in_range(&v1, &v2); if (res.val.i == CMP_ERROR) runtime( "!~ applied on unknown type pair" ); - res.val.i = !res.val.i; + RESULT(T_BOOL, i, !i); } - INST(FI_DEFINED) { + INST(FI_DEFINED, 1, 1) { ARG_ANY(1); - res.type = T_BOOL; - res.val.i = (v1.type != T_VOID) && !undef_value(v1); + RESULT(T_BOOL, i, (v1.type != T_VOID) && !undef_value(v1)); } - INST(FI_TYPE) { + + INST(FI_TYPE, 1, 1) { ARG_ANY(1); /* There may be more types supporting this operation */ switch (v1.type) { case T_NET: - res.type = T_ENUM_NETTYPE; - res.val.i = v1.val.net->type; + RESULT(T_ENUM_NETTYPE, i, v1.val.net->type); break; default: runtime( "Can't determine type of this item" ); } } - INST(FI_IS_V4) { + + INST(FI_IS_V4, 1, 1) { ARG(1, T_IP); - res.type = T_BOOL; - res.val.i = ipa_is_ip4(v1.val.ip); + RESULT(T_BOOL, i, ipa_is_ip4(v1.val.ip)); } - /* Set to indirect value, a[0] = variable, a[1] = value */ - INST(FI_SET) { + /* Set to indirect value prepared in v1 */ + INST(FI_SET, 1, 0) { ARG_ANY(2); - sym = what->a[0].p; - vp = sym->def; - if ((sym->class != (SYM_VARIABLE | v2.type)) && (v2.type != T_VOID)) + SYMBOL(1); + if ((sym->class != (SYM_VARIABLE | v1.type)) && (v1.type != T_VOID)) { /* IP->Quad implicit conversion */ - if ((sym->class == (SYM_VARIABLE | T_QUAD)) && val_is_ip4(v2)) + if ((sym->class == (SYM_VARIABLE | T_QUAD)) && val_is_ip4(&v1)) { - vp->type = T_QUAD; - vp->val.i = ipa_to_u32(v2.val.ip); + *((struct f_val *) sym->def) = (struct f_val) { + .type = T_QUAD, + .val.i = ipa_to_u32(v1.val.ip), + }; break; } runtime( "Assigning to variable of incompatible type" ); } - *vp = v2; + *((struct f_val *) sym->def) = v1; } /* some constants have value in a[1], some in *a[0].p, strange. */ - INST(FI_CONSTANT) { /* integer (or simple type) constant, string, set, or prefix_set */ - res = what->val; + INST(FI_CONSTANT, 0, 1) { /* integer (or simple type) constant, string, set, or prefix_set */ + VALI; // res = what->val; + RESULT_OK; } - INST(FI_VARIABLE) { - res = * ((struct f_val *) what->a[0].p); + INST(FI_VARIABLE, 0, 1) { + VALP(1); // res = * ((struct f_val *) what->a[0].p); + SAME([[if (strcmp(f1->sym->name, f2->sym->name)) return 0; ]]); + RESULT_OK; } - INST(FI_CONSTANT_INDIRECT) { - res = * ((struct f_val *) what->a[0].p); + INST(FI_CONSTANT_INDIRECT, 0, 1) { + VALP(1); + SAME([[if (!val_same(f1->vp, f2->vp)) return 0; ]]); + RESULT_OK; } - INST(FI_PRINT) { + INST(FI_PRINT, 1, 0) { ARG_ANY(1); - val_format(v1, &fs->buf); + val_format(&(v1), &fs->buf); } - INST(FI_CONDITION) { - ARG_T(1, 0, T_BOOL); + INST(FI_CONDITION, 1, 0) { + ARG(1, T_BOOL); if (res.val.i) - ARG_ANY_T(2,0); + LINE(2,0); else - ARG_ANY_T(3,0); + LINE(3,1); } - INST(FI_PRINT_AND_DIE) { - ARG_ANY(1); - if ((what->a[1].i == F_NOP || (what->a[1].i != F_NONL && what->a[0].p)) && + INST(FI_PRINT_AND_DIE, 0, 0) { + POSTFIXIFY([[ + if (what->a[0].p) { + pos = postfixify(dest, what->a[0].p, pos); + dest->items[pos].flags |= FIF_PRINTED; + } + ]]); + LINE_SIZE([[ + if (what->a[0].p) { + cnt += inst_line_size(what->a[0].p); + } + ]]); + + FRET(2); + + if ((fret == F_NOP || (fret != F_NONL && (what->flags & FIF_PRINTED))) && !(fs->flags & FF_SILENT)) log_commit(*L_INFO, &fs->buf); - switch (what->a[1].i) { + switch (fret) { case F_QUITBIRD: die( "Filter asked me to die" ); case F_ACCEPT: /* Should take care about turning ACCEPT into MODIFY */ case F_ERROR: case F_REJECT: /* FIXME (noncritical) Should print complete route along with reason to reject route */ - return what->a[1].i; /* We have to return now, no more processing. */ + return fret; /* We have to return now, no more processing. */ case F_NONL: case F_NOP: break; @@ -280,40 +293,43 @@ bug( "unknown return type: Can't happen"); } } - INST(FI_RTA_GET) { /* rta access */ + + INST(FI_RTA_GET, 0, 1) { /* rta access */ { + STATIC_ATTR; ACCESS_RTE; struct rta *rta = (*fs->rte)->attrs; - res.type = what->aux; - switch (what->a[1].i) + switch (sa.sa_code) { - case SA_FROM: res.val.ip = rta->from; break; - case SA_GW: res.val.ip = rta->nh.gw; break; - case SA_NET: res.val.net = (*fs->rte)->net->n.addr; break; - case SA_PROTO: res.val.s = rta->src->proto->name; break; - case SA_SOURCE: res.val.i = rta->source; break; - case SA_SCOPE: res.val.i = rta->scope; break; - case SA_DEST: res.val.i = rta->dest; break; - case SA_IFNAME: res.val.s = rta->nh.iface ? rta->nh.iface->name : ""; break; - case SA_IFINDEX: res.val.i = rta->nh.iface ? rta->nh.iface->index : 0; break; + case SA_FROM: RESULT(sa.f_type, ip, rta->from); break; + case SA_GW: RESULT(sa.f_type, ip, rta->nh.gw); break; + case SA_NET: RESULT(sa.f_type, net, (*fs->rte)->net->n.addr); break; + case SA_PROTO: RESULT(sa.f_type, s, rta->src->proto->name); break; + case SA_SOURCE: RESULT(sa.f_type, i, rta->source); break; + case SA_SCOPE: RESULT(sa.f_type, i, rta->scope); break; + case SA_DEST: RESULT(sa.f_type, i, rta->dest); break; + case SA_IFNAME: RESULT(sa.f_type, s, rta->nh.iface ? rta->nh.iface->name : ""); break; + case SA_IFINDEX: RESULT(sa.f_type, i, rta->nh.iface ? rta->nh.iface->index : 0); break; default: - bug("Invalid static attribute access (%x)", res.type); + bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code); } } } - INST(FI_RTA_SET) { + + INST(FI_RTA_SET, 1, 0) { + STATIC_ATTR; ACCESS_RTE; ARG_ANY(1); - if (what->aux != v1.type) + if (sa.f_type != v1.type) runtime( "Attempt to set static attribute to incompatible type" ); f_rta_cow(fs); { struct rta *rta = (*fs->rte)->attrs; - switch (what->a[1].i) + switch (sa.sa_code) { case SA_FROM: rta->from = v1.val.ip; @@ -339,15 +355,17 @@ break; case SA_DEST: - i = v1.val.i; - if ((i != RTD_BLACKHOLE) && (i != RTD_UNREACHABLE) && (i != RTD_PROHIBIT)) - runtime( "Destination can be changed only to blackhole, unreachable or prohibit" ); - - rta->dest = i; - rta->nh.gw = IPA_NONE; - rta->nh.iface = NULL; - rta->nh.next = NULL; - rta->hostentry = NULL; + { + int i = v1.val.i; + if ((i != RTD_BLACKHOLE) && (i != RTD_UNREACHABLE) && (i != RTD_PROHIBIT)) + runtime( "Destination can be changed only to blackhole, unreachable or prohibit" ); + + rta->dest = i; + rta->nh.gw = IPA_NONE; + rta->nh.iface = NULL; + rta->nh.next = NULL; + rta->hostentry = NULL; + } break; case SA_IFNAME: @@ -365,124 +383,112 @@ break; default: - bug("Invalid static attribute access (%x)", res.type); + bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code); } } } - INST(FI_EA_GET) { /* Access to extended attributes */ + + INST(FI_EA_GET, 0, 1) { /* Access to extended attributes */ + DYNAMIC_ATTR; ACCESS_RTE; ACCESS_EATTRS; { - u16 code = what->a[1].i; - int f_type = what->aux >> 8; - eattr *e = ea_find(*fs->eattrs, code); + eattr *e = ea_find(*fs->eattrs, da.ea_code); if (!e) { /* A special case: undefined as_path looks like empty as_path */ - if ((what->aux & EAF_TYPE_MASK) == EAF_TYPE_AS_PATH) { - res.type = T_PATH; - res.val.ad = &undef_adata; + if (da.type == EAF_TYPE_AS_PATH) { + RESULT(T_PATH, ad, &null_adata); break; } /* The same special case for int_set */ - if ((what->aux & EAF_TYPE_MASK) == EAF_TYPE_INT_SET) { - res.type = T_CLIST; - res.val.ad = &undef_adata; + if (da.type == EAF_TYPE_INT_SET) { + RESULT(T_CLIST, ad, &null_adata); break; } /* The same special case for ec_set */ - if ((what->aux & EAF_TYPE_MASK) == EAF_TYPE_EC_SET) { - res.type = T_ECLIST; - res.val.ad = &undef_adata; + if (da.type == EAF_TYPE_EC_SET) { + RESULT(T_ECLIST, ad, &null_adata); break; } /* The same special case for lc_set */ - if ((what->aux & EAF_TYPE_MASK) == EAF_TYPE_LC_SET) { - res.type = T_LCLIST; - res.val.ad = &undef_adata; + if (da.type == EAF_TYPE_LC_SET) { + RESULT(T_LCLIST, ad, &null_adata); break; } /* Undefined value */ res.type = T_VOID; + RESULT_OK; break; } switch (e->type & EAF_TYPE_MASK) { case EAF_TYPE_INT: - res.type = f_type; - res.val.i = e->u.data; + RESULT(da.f_type, i, e->u.data); break; case EAF_TYPE_ROUTER_ID: - res.type = T_QUAD; - res.val.i = e->u.data; + RESULT(T_QUAD, i, e->u.data); break; case EAF_TYPE_OPAQUE: - res.type = T_ENUM_EMPTY; - res.val.i = 0; + RESULT(T_ENUM_EMPTY, i, 0); break; case EAF_TYPE_IP_ADDRESS: - res.type = T_IP; - struct adata * ad = e->u.ptr; - res.val.ip = * (ip_addr *) ad->data; + RESULT(T_IP, ip, *((ip_addr *) e->u.ptr->data)); break; case EAF_TYPE_AS_PATH: - res.type = T_PATH; - res.val.ad = e->u.ptr; + RESULT(T_PATH, ad, e->u.ptr); break; case EAF_TYPE_BITFIELD: - res.type = T_BOOL; - res.val.i = !!(e->u.data & BITFIELD_MASK(what)); + RESULT(T_BOOL, i, !!(e->u.data & (1u << da.bit))); break; case EAF_TYPE_INT_SET: - res.type = T_CLIST; - res.val.ad = e->u.ptr; + RESULT(T_CLIST, ad, e->u.ptr); break; case EAF_TYPE_EC_SET: - res.type = T_ECLIST; - res.val.ad = e->u.ptr; + RESULT(T_ECLIST, ad, e->u.ptr); break; case EAF_TYPE_LC_SET: - res.type = T_LCLIST; - res.val.ad = e->u.ptr; + RESULT(T_LCLIST, ad, e->u.ptr); break; case EAF_TYPE_UNDEF: res.type = T_VOID; + RESULT_OK; break; default: - bug("Unknown type in e,a"); + bug("Unknown dynamic attribute type"); } } } - INST(FI_EA_SET) { + + INST(FI_EA_SET, 1, 0) { + DYNAMIC_ATTR; ACCESS_RTE; ACCESS_EATTRS; ARG_ANY(1); { struct ea_list *l = lp_alloc(fs->pool, sizeof(struct ea_list) + sizeof(eattr)); - u16 code = what->a[1].i; - int f_type = what->aux >> 8; l->next = NULL; l->flags = EALF_SORTED; l->count = 1; - l->attrs[0].id = code; + l->attrs[0].id = da.ea_code; l->attrs[0].flags = 0; - l->attrs[0].type = (what->aux & 0xff) | EAF_ORIGINATED | EAF_FRESH; + l->attrs[0].type = da.type | EAF_ORIGINATED | EAF_FRESH; - switch (what->aux & EAF_TYPE_MASK) { + switch (da.type) { case EAF_TYPE_INT: - if (v1.type != f_type) + if (v1.type != da.f_type) runtime( "Setting int attribute to non-int value" ); l->attrs[0].u.data = v1.val.i; break; case EAF_TYPE_ROUTER_ID: /* IP->Quad implicit conversion */ - if (val_is_ip4(v1)) { + if (val_is_ip4(&v1)) { l->attrs[0].u.data = ipa_to_u32(v1.val.ip); break; } @@ -514,13 +520,13 @@ runtime( "Setting bit in bitfield attribute to non-bool value" ); { /* First, we have to find the old value */ - eattr *e = ea_find(*fs->eattrs, code); + eattr *e = ea_find(*fs->eattrs, da.ea_code); u32 data = e ? e->u.data : 0; if (v1.val.i) - l->attrs[0].u.data = data | BITFIELD_MASK(what); + l->attrs[0].u.data = data | (1u << da.bit); else - l->attrs[0].u.data = data & ~BITFIELD_MASK(what);; + l->attrs[0].u.data = data & ~(1u << da.bit); } break; case EAF_TYPE_INT_SET: @@ -551,12 +557,13 @@ *fs->eattrs = l; } } - INST(FI_PREF_GET) { + + INST(FI_PREF_GET, 0, 1) { ACCESS_RTE; - res.type = T_INT; - res.val.i = (*fs->rte)->pref; + RESULT(T_INT, i, (*fs->rte)->pref); } - INST(FI_PREF_SET) { + + INST(FI_PREF_SET, 1, 0) { ACCESS_RTE; ARG(1,T_INT); if (v1.val.i > 0xFFFF) @@ -564,149 +571,221 @@ f_rte_cow(fs); (*fs->rte)->pref = v1.val.i; } - INST(FI_LENGTH) { /* Get length of */ + + INST(FI_LENGTH, 1, 1) { /* Get length of */ ARG_ANY(1); - res.type = T_INT; switch(v1.type) { - case T_NET: res.val.i = net_pxlen(v1.val.net); break; - case T_PATH: res.val.i = as_path_getlen(v1.val.ad); break; - case T_CLIST: res.val.i = int_set_get_size(v1.val.ad); break; - case T_ECLIST: res.val.i = ec_set_get_size(v1.val.ad); break; - case T_LCLIST: res.val.i = lc_set_get_size(v1.val.ad); break; + case T_NET: RESULT(T_INT, i, net_pxlen(v1.val.net)); break; + case T_PATH: RESULT(T_INT, i, as_path_getlen(v1.val.ad)); break; + case T_CLIST: RESULT(T_INT, i, int_set_get_size(v1.val.ad)); break; + case T_ECLIST: RESULT(T_INT, i, ec_set_get_size(v1.val.ad)); break; + case T_LCLIST: RESULT(T_INT, i, lc_set_get_size(v1.val.ad)); break; default: runtime( "Prefix, path, clist or eclist expected" ); } } - INST(FI_SADR_SRC) { /* Get SADR src prefix */ + + INST(FI_SADR_SRC, 1, 1) { /* Get SADR src prefix */ ARG(1, T_NET); if (!net_is_sadr(v1.val.net)) runtime( "SADR expected" ); - { - net_addr_ip6_sadr *net = (void *) v1.val.net; - net_addr *src = lp_alloc(fs->pool, sizeof(net_addr_ip6)); - net_fill_ip6(src, net->src_prefix, net->src_pxlen); + net_addr_ip6_sadr *net = (void *) v1.val.net; + net_addr *src = lp_alloc(fs->pool, sizeof(net_addr_ip6)); + net_fill_ip6(src, net->src_prefix, net->src_pxlen); - res.type = T_NET; - res.val.net = src; - } + RESULT(T_NET, net, src); } - INST(FI_ROA_MAXLEN) { /* Get ROA max prefix length */ + + INST(FI_ROA_MAXLEN, 1, 1) { /* Get ROA max prefix length */ ARG(1, T_NET); if (!net_is_roa(v1.val.net)) runtime( "ROA expected" ); - res.type = T_INT; - res.val.i = (v1.val.net->type == NET_ROA4) ? + RESULT(T_INT, i, (v1.val.net->type == NET_ROA4) ? ((net_addr_roa4 *) v1.val.net)->max_pxlen : - ((net_addr_roa6 *) v1.val.net)->max_pxlen; + ((net_addr_roa6 *) v1.val.net)->max_pxlen); } - INST(FI_ROA_ASN) { /* Get ROA ASN */ + + INST(FI_ROA_ASN, 1, 1) { /* Get ROA ASN */ ARG(1, T_NET); if (!net_is_roa(v1.val.net)) runtime( "ROA expected" ); - res.type = T_INT; - res.val.i = (v1.val.net->type == NET_ROA4) ? + RESULT(T_INT, i, (v1.val.net->type == NET_ROA4) ? ((net_addr_roa4 *) v1.val.net)->asn : - ((net_addr_roa6 *) v1.val.net)->asn; + ((net_addr_roa6 *) v1.val.net)->asn); } - INST(FI_IP) { /* Convert prefix to ... */ + + INST(FI_IP, 1, 1) { /* Convert prefix to ... */ ARG(1, T_NET); - res.type = T_IP; - res.val.ip = net_prefix(v1.val.net); + RESULT(T_IP, ip, net_prefix(v1.val.net)); } - INST(FI_ROUTE_DISTINGUISHER) { + + INST(FI_ROUTE_DISTINGUISHER, 1, 1) { ARG(1, T_NET); if (!net_is_vpn(v1.val.net)) runtime( "VPN address expected" ); - res.type = T_RD; - res.val.ec = net_rd(v1.val.net); + RESULT(T_RD, ec, net_rd(v1.val.net)); } - INST(FI_AS_PATH_FIRST) { /* Get first ASN from AS PATH */ - ARG(1, T_PATH); - as = 0; + INST(FI_AS_PATH_FIRST, 1, 1) { /* Get first ASN from AS PATH */ + ARG(1, T_PATH); + int as = 0; as_path_get_first(v1.val.ad, &as); - res.type = T_INT; - res.val.i = as; + RESULT(T_INT, i, as); } - INST(FI_AS_PATH_LAST) { /* Get last ASN from AS PATH */ - ARG(1, T_PATH); - as = 0; + INST(FI_AS_PATH_LAST, 1, 1) { /* Get last ASN from AS PATH */ + ARG(1, T_PATH); + int as = 0; as_path_get_last(v1.val.ad, &as); - res.type = T_INT; - res.val.i = as; + RESULT(T_INT, i, as); } - INST(FI_AS_PATH_LAST_NAG) { /* Get last ASN from non-aggregated part of AS PATH */ + + INST(FI_AS_PATH_LAST_NAG, 1, 1) { /* Get last ASN from non-aggregated part of AS PATH */ ARG(1, T_PATH); + RESULT(T_INT, i, as_path_get_last_nonaggregated(v1.val.ad)); + } - res.type = T_INT; - res.val.i = as_path_get_last_nonaggregated(v1.val.ad); + INST(FI_RETURN, 1, 1) { + /* Acquire the return value */ + ARG_ANY(1); + uint retpos = vstk.cnt; + + /* Drop every sub-block including ourselves */ + while ((estk.cnt-- > 0) && !(estk.item[estk.cnt].emask & FE_RETURN)) + ; + + /* Now we are at the caller frame; if no such, try to convert to accept/reject. */ + if (!estk.cnt) + if (vstk.val[retpos].type == T_BOOL) + if (vstk.val[retpos].val.i) + return F_ACCEPT; + else + return F_REJECT; + else + runtime("Can't return non-bool from non-function"); + + /* Set the value stack position */ + vstk.cnt = estk.item[estk.cnt].ventry; + + /* Copy the return value */ + RESULT_VAL(vstk.val[retpos]); } - INST(FI_RETURN) { - ARG_ANY_T(1,0); - return F_RETURN; + + INST(FI_CALL, 0, 1) { + /* First push the code */ + LINEP(2,0); + curline.emask |= FE_RETURN; + + /* Then push the arguments */ + LINE(1,1); } - INST(FI_CALL) { - ARG_ANY_T(1,0); - fret = interpret(fs, what->a[1].p); - if (fret > F_RETURN) - return fret; + + INST(FI_DROP_RESULT, 1, 0) { + ARG_ANY(1); } - INST(FI_CLEAR_LOCAL_VARS) { /* Clear local variables */ - for (sym = what->a[0].p; sym != NULL; sym = sym->aux2) + + INST(FI_CLEAR_LOCAL_VARS, 0, 0) { /* Clear local variables */ + SYMBOL(1); + for ( ; sym != NULL; sym = sym->aux2) ((struct f_val *) sym->def)->type = T_VOID; } - INST(FI_SWITCH) { + INST(FI_SWITCH, 1, 0) { ARG_ANY(1); - { - struct f_tree *t = find_tree(what->a[1].p, v1); + POSTFIXIFY([[ + dest->items[pos].tree = what->a[1].p; + ]]); + const struct f_tree *t = find_tree(what->tree, &v1); + if (!t) { + v1.type = T_VOID; + t = find_tree(what->tree, &v1); if (!t) { - v1.type = T_VOID; - t = find_tree(what->a[1].p, v1); - if (!t) { - debug( "No else statement?\n"); - break; - } + debug( "No else statement?\n"); + break; } - /* It is actually possible to have t->data NULL */ - - fret = interpret(fs, t->data); - if (fret >= F_RETURN) - return fret; } + /* It is actually possible to have t->data NULL */ + + LINEX(t->data); } - INST(FI_IP_MASK) { /* IP.MASK(val) */ + + INST(FI_IP_MASK, 2, 1) { /* IP.MASK(val) */ ARG(1, T_IP); ARG(2, T_INT); - - res.type = T_IP; - res.val.ip = ipa_is_ip4(v1.val.ip) ? + RESULT(T_IP, ip, [[ ipa_is_ip4(v1.val.ip) ? ipa_from_ip4(ip4_and(ipa_to_ip4(v1.val.ip), ip4_mkmask(v2.val.i))) : - ipa_from_ip6(ip6_and(ipa_to_ip6(v1.val.ip), ip6_mkmask(v2.val.i))); + ipa_from_ip6(ip6_and(ipa_to_ip6(v1.val.ip), ip6_mkmask(v2.val.i))) ]]); } - INST(FI_EMPTY) { /* Create empty attribute */ - res.type = what->aux; - res.val.ad = adata_empty(fs->pool, 0); - } - INST(FI_PATH_PREPEND) { /* Path prepend */ + INST(FI_PATH_PREPEND, 2, 1) { /* Path prepend */ ARG(1, T_PATH); ARG(2, T_INT); + RESULT(T_PATH, ad, [[ as_path_prepend(fs->pool, v1.val.ad, v2.val.i) ]]); + } + + INST(FI_CLIST_ADD, 2, 1) { /* (Extended) Community list add */ + ARG_ANY(1); + ARG_ANY(2); + if (v1.type == T_PATH) + runtime("Can't add to path"); + + else if (v1.type == T_CLIST) + { + /* Community (or cluster) list */ + struct f_val dummy; + + if ((v2.type == T_PAIR) || (v2.type == T_QUAD)) + RESULT(T_CLIST, ad, [[ int_set_add(fs->pool, v1.val.ad, v2.val.i) ]]); + /* IP->Quad implicit conversion */ + else if (val_is_ip4(&v2)) + RESULT(T_CLIST, ad, [[ int_set_add(fs->pool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]); + else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy)) + runtime("Can't add set"); + else if (v2.type == T_CLIST) + RESULT(T_CLIST, ad, [[ int_set_union(fs->pool, v1.val.ad, v2.val.ad) ]]); + else + runtime("Can't add non-pair"); + } - res.type = T_PATH; - res.val.ad = as_path_prepend(fs->pool, v1.val.ad, v2.val.i); + else if (v1.type == T_ECLIST) + { + /* v2.val is either EC or EC-set */ + if ((v2.type == T_SET) && eclist_set_type(v2.val.t)) + runtime("Can't add set"); + else if (v2.type == T_ECLIST) + RESULT(T_ECLIST, ad, [[ ec_set_union(fs->pool, v1.val.ad, v2.val.ad) ]]); + else if (v2.type != T_EC) + runtime("Can't add non-ec"); + else + RESULT(T_ECLIST, ad, [[ ec_set_add(fs->pool, v1.val.ad, v2.val.ec) ]]); + } + + else if (v1.type == T_LCLIST) + { + /* v2.val is either LC or LC-set */ + if ((v2.type == T_SET) && lclist_set_type(v2.val.t)) + runtime("Can't add set"); + else if (v2.type == T_LCLIST) + RESULT(T_LCLIST, ad, [[ lc_set_union(fs->pool, v1.val.ad, v2.val.ad) ]]); + else if (v2.type != T_LC) + runtime("Can't add non-lc"); + else + RESULT(T_LCLIST, ad, [[ lc_set_add(fs->pool, v1.val.ad, v2.val.lc) ]]); + + } + + else + runtime("Can't add to non-[e|l]clist"); } - INST(FI_CLIST_ADD_DEL) { /* (Extended) Community list add or delete */ + INST(FI_CLIST_DEL, 2, 1) { /* (Extended) Community list add or delete */ ARG_ANY(1); ARG_ANY(2); if (v1.type == T_PATH) { - struct f_tree *set = NULL; + const struct f_tree *set = NULL; u32 key = 0; - int pos; if (v2.type == T_INT) key = v2.val.i; @@ -715,210 +794,154 @@ else runtime("Can't delete non-integer (set)"); - switch (what->aux) - { - case 'a': runtime("Can't add to path"); - case 'd': pos = 0; break; - case 'f': pos = 1; break; - default: bug("unknown Ca operation"); - } - - if (pos && !set) - runtime("Can't filter integer"); - - res.type = T_PATH; - res.val.ad = as_path_filter(fs->pool, v1.val.ad, set, key, pos); + RESULT(T_PATH, ad, [[ as_path_filter(fs->pool, v1.val.ad, set, key, 0) ]]); } + else if (v1.type == T_CLIST) { /* Community (or cluster) list */ struct f_val dummy; - int arg_set = 0; - uint n = 0; if ((v2.type == T_PAIR) || (v2.type == T_QUAD)) - n = v2.val.i; + RESULT(T_CLIST, ad, [[ int_set_del(fs->pool, v1.val.ad, v2.val.i) ]]); /* IP->Quad implicit conversion */ - else if (val_is_ip4(v2)) - n = ipa_to_u32(v2.val.ip); - else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy)) - arg_set = 1; - else if (v2.type == T_CLIST) - arg_set = 2; + else if (val_is_ip4(&v2)) + RESULT(T_CLIST, ad, [[ int_set_del(fs->pool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]); + else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy) || (v2.type == T_CLIST)) + RESULT(T_CLIST, ad, [[ clist_filter(fs->pool, v1.val.ad, &v2, 0) ]]); else - runtime("Can't add/delete non-pair"); - - res.type = T_CLIST; - switch (what->aux) - { - case 'a': - if (arg_set == 1) - runtime("Can't add set"); - else if (!arg_set) - res.val.ad = int_set_add(fs->pool, v1.val.ad, n); - else - res.val.ad = int_set_union(fs->pool, v1.val.ad, v2.val.ad); - break; - - case 'd': - if (!arg_set) - res.val.ad = int_set_del(fs->pool, v1.val.ad, n); - else - res.val.ad = clist_filter(fs->pool, v1.val.ad, v2, 0); - break; - - case 'f': - if (!arg_set) - runtime("Can't filter pair"); - res.val.ad = clist_filter(fs->pool, v1.val.ad, v2, 1); - break; - - default: - bug("unknown Ca operation"); - } + runtime("Can't delete non-pair"); } + else if (v1.type == T_ECLIST) { - /* Extended community list */ - int arg_set = 0; - /* v2.val is either EC or EC-set */ - if ((v2.type == T_SET) && eclist_set_type(v2.val.t)) - arg_set = 1; - else if (v2.type == T_ECLIST) - arg_set = 2; + if ((v2.type == T_SET) && eclist_set_type(v2.val.t) || (v2.type == T_ECLIST)) + RESULT(T_ECLIST, ad, [[ eclist_filter(fs->pool, v1.val.ad, &v2, 0) ]]); else if (v2.type != T_EC) - runtime("Can't add/delete non-ec"); - - res.type = T_ECLIST; - switch (what->aux) - { - case 'a': - if (arg_set == 1) - runtime("Can't add set"); - else if (!arg_set) - res.val.ad = ec_set_add(fs->pool, v1.val.ad, v2.val.ec); - else - res.val.ad = ec_set_union(fs->pool, v1.val.ad, v2.val.ad); - break; - - case 'd': - if (!arg_set) - res.val.ad = ec_set_del(fs->pool, v1.val.ad, v2.val.ec); - else - res.val.ad = eclist_filter(fs->pool, v1.val.ad, v2, 0); - break; - - case 'f': - if (!arg_set) - runtime("Can't filter ec"); - res.val.ad = eclist_filter(fs->pool, v1.val.ad, v2, 1); - break; - - default: - bug("unknown Ca operation"); - } + runtime("Can't delete non-ec"); + else + RESULT(T_ECLIST, ad, [[ ec_set_del(fs->pool, v1.val.ad, v2.val.ec) ]]); } + else if (v1.type == T_LCLIST) { - /* Large community list */ - int arg_set = 0; - /* v2.val is either LC or LC-set */ - if ((v2.type == T_SET) && lclist_set_type(v2.val.t)) - arg_set = 1; - else if (v2.type == T_LCLIST) - arg_set = 2; + if ((v2.type == T_SET) && lclist_set_type(v2.val.t) || (v2.type == T_LCLIST)) + RESULT(T_LCLIST, ad, [[ lclist_filter(fs->pool, v1.val.ad, &v2, 0) ]]); else if (v2.type != T_LC) - runtime("Can't add/delete non-lc"); - - res.type = T_LCLIST; - switch (what->aux) - { - case 'a': - if (arg_set == 1) - runtime("Can't add set"); - else if (!arg_set) - res.val.ad = lc_set_add(fs->pool, v1.val.ad, v2.val.lc); - else - res.val.ad = lc_set_union(fs->pool, v1.val.ad, v2.val.ad); - break; + runtime("Can't delete non-lc"); + else + RESULT(T_LCLIST, ad, [[ lc_set_del(fs->pool, v1.val.ad, v2.val.lc) ]]); + } - case 'd': - if (!arg_set) - res.val.ad = lc_set_del(fs->pool, v1.val.ad, v2.val.lc); - else - res.val.ad = lclist_filter(fs->pool, v1.val.ad, v2, 0); - break; + else + runtime("Can't delete in non-[e|l]clist"); + } - case 'f': - if (!arg_set) - runtime("Can't filter lc"); - res.val.ad = lclist_filter(fs->pool, v1.val.ad, v2, 1); - break; + INST(FI_CLIST_FILTER, 2, 1) { /* (Extended) Community list add or delete */ + ARG_ANY(1); + ARG_ANY(2); + if (v1.type == T_PATH) + { + u32 key = 0; - default: - bug("unknown Ca operation"); - } + if ((v2.type == T_SET) && (v2.val.t->from.type == T_INT)) + RESULT(T_PATH, ad, [[ as_path_filter(fs->pool, v1.val.ad, v2.val.t, key, 1) ]]); + else + runtime("Can't filter integer"); } - else - runtime("Can't add/delete to non-[e|l]clist"); - } + else if (v1.type == T_CLIST) + { + /* Community (or cluster) list */ + struct f_val dummy; - INST(FI_ROA_CHECK) { /* ROA Check */ - if (what->arg1) + if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy) || (v2.type == T_CLIST)) + RESULT(T_CLIST, ad, [[ clist_filter(fs->pool, v1.val.ad, &v2, 1) ]]); + else + runtime("Can't filter pair"); + } + + else if (v1.type == T_ECLIST) { - ARG(1, T_NET); - ARG(2, T_INT); + /* v2.val is either EC or EC-set */ + if ((v2.type == T_SET) && eclist_set_type(v2.val.t) || (v2.type == T_ECLIST)) + RESULT(T_ECLIST, ad, [[ eclist_filter(fs->pool, v1.val.ad, &v2, 1) ]]); + else + runtime("Can't filter ec"); + } - as = v2.val.i; + else if (v1.type == T_LCLIST) + { + /* v2.val is either LC or LC-set */ + if ((v2.type == T_SET) && lclist_set_type(v2.val.t) || (v2.type == T_LCLIST)) + RESULT(T_LCLIST, ad, [[ lclist_filter(fs->pool, v1.val.ad, &v2, 1) ]]); + else + runtime("Can't filter lc"); } + else - { - ACCESS_RTE; - ACCESS_EATTRS; - v1.val.net = (*fs->rte)->net->n.addr; + runtime("Can't filter non-[e|l]clist"); + } - /* We ignore temporary attributes, probably not a problem here */ - /* 0x02 is a value of BA_AS_PATH, we don't want to include BGP headers */ - eattr *e = ea_find(*fs->eattrs, EA_CODE(PROTOCOL_BGP, 0x02)); + INST(FI_ROA_CHECK_IMPLICIT, 0, 1) { /* ROA Check */ + RTC(1); + ACCESS_RTE; + ACCESS_EATTRS; + const net_addr *net = (*fs->rte)->net->n.addr; - if (!e || ((e->type & EAF_TYPE_MASK) != EAF_TYPE_AS_PATH)) - runtime("Missing AS_PATH attribute"); + /* We ignore temporary attributes, probably not a problem here */ + /* 0x02 is a value of BA_AS_PATH, we don't want to include BGP headers */ + eattr *e = ea_find(*fs->eattrs, EA_CODE(PROTOCOL_BGP, 0x02)); - as_path_get_last(e->u.ptr, &as); - } + if (!e || ((e->type & EAF_TYPE_MASK) != EAF_TYPE_AS_PATH)) + runtime("Missing AS_PATH attribute"); + + u32 as = 0; + as_path_get_last(e->u.ptr, &as); - struct rtable *table = what->a[2].rtc->table; if (!table) runtime("Missing ROA table"); if (table->addr_type != NET_ROA4 && table->addr_type != NET_ROA6) runtime("Table type must be either ROA4 or ROA6"); - res.type = T_ENUM_ROA; + if (table->addr_type != (net->type == NET_IP4 ? NET_ROA4 : NET_ROA6)) + RESULT(T_ENUM_ROA, i, ROA_UNKNOWN); /* Prefix and table type mismatch */ + else + RESULT(T_ENUM_ROA, i, [[ net_roa_check(table, net, as) ]]); + } + + INST(FI_ROA_CHECK_EXPLICIT, 2, 1) { /* ROA Check */ + ARG(1, T_NET); + ARG(2, T_INT); + RTC(3); + + u32 as = v2.val.i; + + if (!table) + runtime("Missing ROA table"); + + if (table->addr_type != NET_ROA4 && table->addr_type != NET_ROA6) + runtime("Table type must be either ROA4 or ROA6"); if (table->addr_type != (v1.val.net->type == NET_IP4 ? NET_ROA4 : NET_ROA6)) - res.val.i = ROA_UNKNOWN; /* Prefix and table type mismatch */ + RESULT(T_ENUM_ROA, i, ROA_UNKNOWN); /* Prefix and table type mismatch */ else - res.val.i = net_roa_check(table, v1.val.net, as); + RESULT(T_ENUM_ROA, i, [[ net_roa_check(table, v1.val.net, as) ]]); } - INST(FI_FORMAT) { /* Format */ + INST(FI_FORMAT, 1, 0) { /* Format */ ARG_ANY(1); - - res.type = T_STRING; - res.val.s = val_format_str(fs, v1); + RESULT(T_STRING, s, val_format_str(fs, &v1)); } - INST(FI_ASSERT) { /* Birdtest Assert */ + INST(FI_ASSERT, 1, 0) { /* Birdtest Assert */ ARG(1, T_BOOL); - - res.type = v1.type; - res.val = v1.val; - + POSTFIXIFY([[ + dest->items[pos].s = what->a[1].p; + ]]); CALL(bt_assert_hook, res.val.i, what); } - diff --git a/filter/f-util.c b/filter/f-util.c index 11a5e97e..4f11a6d9 100644 --- a/filter/f-util.c +++ b/filter/f-util.c @@ -30,8 +30,7 @@ struct f_inst * f_new_inst_da(enum f_instruction_code fi_code, struct f_dynamic_attr da) { struct f_inst *ret = f_new_inst(fi_code); - ret->aux = (da.f_type << 8) | da.type; - ret->a[1].i = da.ea_code; + ret->da = da; return ret; } @@ -39,9 +38,7 @@ struct f_inst * f_new_inst_sa(enum f_instruction_code fi_code, struct f_static_attr sa) { struct f_inst *ret = f_new_inst(fi_code); - ret->aux = sa.f_type; - ret->a[1].i = sa.sa_code; - ret->a[0].i = sa.readonly; + ret->sa = sa; return ret; } @@ -49,13 +46,12 @@ f_new_inst_sa(enum f_instruction_code fi_code, struct f_static_attr sa) * Generate set_dynamic( operation( get_dynamic(), argument ) ) */ struct f_inst * -f_generate_complex(int operation, int operation_aux, struct f_dynamic_attr da, struct f_inst *argument) +f_generate_complex(enum f_instruction_code fi_code, struct f_dynamic_attr da, struct f_inst *argument) { struct f_inst *set_dyn = f_new_inst_da(FI_EA_SET, da), - *oper = f_new_inst(operation), + *oper = f_new_inst(fi_code), *get_dyn = f_new_inst_da(FI_EA_GET, da); - oper->aux = operation_aux; oper->a[0].p = get_dyn; oper->a[1].p = argument; @@ -63,21 +59,6 @@ f_generate_complex(int operation, int operation_aux, struct f_dynamic_attr da, s return set_dyn; } -struct f_inst * -f_generate_roa_check(struct rtable_config *table, struct f_inst *prefix, struct f_inst *asn) -{ - struct f_inst *ret = f_new_inst(FI_ROA_CHECK); - ret->arg1 = prefix; - ret->arg2 = asn; - /* prefix == NULL <-> asn == NULL */ - - if (table->addr_type != NET_ROA4 && table->addr_type != NET_ROA6) - cf_error("%s is not a ROA table", table->name); - ret->arg3 = table; - - return ret; -} - static const char * const f_instruction_name_str[] = { #define F(c,a,b) \ [c] = #c, @@ -219,7 +200,7 @@ ca_lookup(pool *p, const char *name, int f_type) } cas = mb_allocz(&root_pool, sizeof(struct ca_storage) + strlen(name) + 1); - cas->fda = f_new_dynamic_attr(ea_type, f_type, EA_CUSTOM(id)); + cas->fda = f_new_dynamic_attr(ea_type, 0, f_type, EA_CUSTOM(id)); cas->uc = 1; strcpy(cas->name, name); diff --git a/filter/filter.c b/filter/filter.c index 308792b9..858d5fc5 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -50,15 +50,6 @@ #define CMP_ERROR 999 -#define FILTER_STACK_DEPTH 16384 - -/* Filter interpreter stack. Make this thread local after going parallel. */ -struct filter_stack { - struct f_val val; -}; - -static struct filter_stack filter_stack[FILTER_STACK_DEPTH]; - /* Internal filter state, to be allocated on stack when executing filters */ struct filter_state { struct rte **rte; @@ -66,14 +57,10 @@ struct filter_state { struct ea_list **eattrs; struct linpool *pool; struct buffer buf; - struct filter_stack *stack; - int stack_ptr; int flags; }; -void (*bt_assert_hook)(int result, struct f_inst *assert); - -static struct adata undef_adata; /* adata of length 0 used for undefined */ +void (*bt_assert_hook)(int result, const struct f_line_item *assert); /* Special undef value for paths and clists */ static inline int @@ -81,9 +68,23 @@ undef_value(struct f_val v) { return ((v.type == T_PATH) || (v.type == T_CLIST) || (v.type == T_ECLIST) || (v.type == T_LCLIST)) && - (v.val.ad == &undef_adata); + (v.val.ad == &null_adata); } +const struct f_val f_const_empty_path = { + .type = T_PATH, + .val.ad = &null_adata, +}, f_const_empty_clist = { + .type = T_CLIST, + .val.ad = &null_adata, +}, f_const_empty_eclist = { + .type = T_ECLIST, + .val.ad = &null_adata, +}, f_const_empty_lclist = { + .type = T_LCLIST, + .val.ad = &null_adata, +}; + static struct adata * adata_empty(struct linpool *pool, int l) { @@ -93,16 +94,16 @@ adata_empty(struct linpool *pool, int l) } static void -pm_format(struct f_path_mask *p, buffer *buf) +pm_format(const struct f_path_mask *p, buffer *buf) { buffer_puts(buf, "[= "); - while (p) + for (uint i=0; i<p->len; i++) { - switch(p->kind) + switch(p->item[i].kind) { case PM_ASN: - buffer_print(buf, "%u ", p->val); + buffer_print(buf, "%u ", p->item[i].asn); break; case PM_QUESTION: @@ -114,21 +115,20 @@ pm_format(struct f_path_mask *p, buffer *buf) break; case PM_ASN_RANGE: - buffer_print(buf, "%u..%u ", p->val, p->val2); + buffer_print(buf, "%u..%u ", p->item[i].from, p->item[i].to); break; case PM_ASN_EXPR: ASSERT(0); } - p = p->next; } buffer_puts(buf, "=]"); } -static inline int val_is_ip4(const struct f_val v) -{ return (v.type == T_IP) && ipa_is_ip4(v.val.ip); } +static inline int val_is_ip4(const struct f_val *v) +{ return (v->type == T_IP) && ipa_is_ip4(v->val.ip); } static inline int lcomm_cmp(lcomm v1, lcomm v2) @@ -152,25 +152,25 @@ lcomm_cmp(lcomm v1, lcomm v2) * that it can be used for building balanced trees. */ int -val_compare(struct f_val v1, struct f_val v2) +val_compare(const struct f_val *v1, const struct f_val *v2) { - if (v1.type != v2.type) { - if (v1.type == T_VOID) /* Hack for else */ + if (v1->type != v2->type) { + if (v1->type == T_VOID) /* Hack for else */ return -1; - if (v2.type == T_VOID) + if (v2->type == T_VOID) return 1; /* IP->Quad implicit conversion */ - if ((v1.type == T_QUAD) && val_is_ip4(v2)) - return uint_cmp(v1.val.i, ipa_to_u32(v2.val.ip)); - if (val_is_ip4(v1) && (v2.type == T_QUAD)) - return uint_cmp(ipa_to_u32(v1.val.ip), v2.val.i); + if ((v1->type == T_QUAD) && val_is_ip4(v2)) + return uint_cmp(v1->val.i, ipa_to_u32(v2->val.ip)); + if (val_is_ip4(v1) && (v2->type == T_QUAD)) + return uint_cmp(ipa_to_u32(v1->val.ip), v2->val.i); debug( "Types do not match in val_compare\n" ); return CMP_ERROR; } - switch (v1.type) { + switch (v1->type) { case T_VOID: return 0; case T_ENUM: @@ -178,47 +178,52 @@ val_compare(struct f_val v1, struct f_val v2) case T_BOOL: case T_PAIR: case T_QUAD: - return uint_cmp(v1.val.i, v2.val.i); + return uint_cmp(v1->val.i, v2->val.i); case T_EC: case T_RD: - return u64_cmp(v1.val.ec, v2.val.ec); + return u64_cmp(v1->val.ec, v2->val.ec); case T_LC: - return lcomm_cmp(v1.val.lc, v2.val.lc); + return lcomm_cmp(v1->val.lc, v2->val.lc); case T_IP: - return ipa_compare(v1.val.ip, v2.val.ip); + return ipa_compare(v1->val.ip, v2->val.ip); case T_NET: - return net_compare(v1.val.net, v2.val.net); + return net_compare(v1->val.net, v2->val.net); case T_STRING: - return strcmp(v1.val.s, v2.val.s); + return strcmp(v1->val.s, v2->val.s); default: return CMP_ERROR; } } static int -pm_same(struct f_path_mask *m1, struct f_path_mask *m2) +pm_same(const struct f_path_mask *m1, const struct f_path_mask *m2) { - while (m1 && m2) + if (m1->len != m2->len) + + for (uint i=0; i<m1->len; i++) { - if (m1->kind != m2->kind) + if (m1->item[i].kind != m2->item[i].kind) return 0; - if (m1->kind == PM_ASN_EXPR) - { - if (!i_same((struct f_inst *) m1->val, (struct f_inst *) m2->val)) - return 0; - } - else - { - if ((m1->val != m2->val) || (m1->val2 != m2->val2)) - return 0; + switch (m1->item[i].kind) { + case PM_ASN: + if (m1->item[i].asn != m2->item[i].asn) + return 0; + break; + case PM_ASN_EXPR: + if (!f_same(m1->item[i].expr, m2->item[i].expr)) + return 0; + break; + case PM_ASN_RANGE: + if (m1->item[i].from != m2->item[i].from) + return 0; + if (m1->item[i].to != m2->item[i].to) + return 0; + break; } - - m1 = m1->next; - m2 = m2->next; } - return !m1 && !m2; + return 1; } /** @@ -230,7 +235,7 @@ pm_same(struct f_path_mask *m1, struct f_path_mask *m2) * Comparison of values of different types is valid and returns 0. */ int -val_same(struct f_val v1, struct f_val v2) +val_same(const struct f_val *v1, const struct f_val *v2) { int rc; @@ -238,28 +243,28 @@ val_same(struct f_val v1, struct f_val v2) if (rc != CMP_ERROR) return !rc; - if (v1.type != v2.type) + if (v1->type != v2->type) return 0; - switch (v1.type) { + switch (v1->type) { case T_PATH_MASK: - return pm_same(v1.val.path_mask, v2.val.path_mask); + return pm_same(v1->val.path_mask, v2->val.path_mask); case T_PATH: case T_CLIST: case T_ECLIST: case T_LCLIST: - return adata_same(v1.val.ad, v2.val.ad); + return adata_same(v1->val.ad, v2->val.ad); case T_SET: - return same_tree(v1.val.t, v2.val.t); + return same_tree(v1->val.t, v2->val.t); case T_PREFIX_SET: - return trie_same(v1.val.ti, v2.val.ti); + return trie_same(v1->val.ti, v2->val.ti); default: - bug("Invalid type in val_same(): %x", v1.type); + bug("Invalid type in val_same(): %x", v1->type); } } static int -clist_set_type(struct f_tree *set, struct f_val *v) +clist_set_type(const struct f_tree *set, struct f_val *v) { switch (set->from.type) { @@ -272,7 +277,7 @@ clist_set_type(struct f_tree *set, struct f_val *v) return 1; case T_IP: - if (val_is_ip4(set->from) && val_is_ip4(set->to)) + if (val_is_ip4(&(set->from)) && val_is_ip4(&(set->to))) { v->type = T_QUAD; return 1; @@ -285,15 +290,15 @@ clist_set_type(struct f_tree *set, struct f_val *v) } static inline int -eclist_set_type(struct f_tree *set) +eclist_set_type(const struct f_tree *set) { return set->from.type == T_EC; } static inline int -lclist_set_type(struct f_tree *set) +lclist_set_type(const struct f_tree *set) { return set->from.type == T_LC; } static int -clist_match_set(struct adata *clist, struct f_tree *set) +clist_match_set(const struct adata *clist, const struct f_tree *set) { if (!clist) return 0; @@ -307,14 +312,14 @@ clist_match_set(struct adata *clist, struct f_tree *set) while (l < end) { v.val.i = *l++; - if (find_tree(set, v)) + if (find_tree(set, &v)) return 1; } return 0; } static int -eclist_match_set(struct adata *list, struct f_tree *set) +eclist_match_set(const struct adata *list, const struct f_tree *set) { if (!list) return 0; @@ -330,7 +335,7 @@ eclist_match_set(struct adata *list, struct f_tree *set) v.type = T_EC; for (i = 0; i < len; i += 2) { v.val.ec = ec_get(l, i); - if (find_tree(set, v)) + if (find_tree(set, &v)) return 1; } @@ -338,7 +343,7 @@ eclist_match_set(struct adata *list, struct f_tree *set) } static int -lclist_match_set(struct adata *list, struct f_tree *set) +lclist_match_set(const struct adata *list, const struct f_tree *set) { if (!list) return 0; @@ -354,23 +359,23 @@ lclist_match_set(struct adata *list, struct f_tree *set) v.type = T_LC; for (i = 0; i < len; i += 3) { v.val.lc = lc_get(l, i); - if (find_tree(set, v)) + if (find_tree(set, &v)) return 1; } return 0; } -static struct adata * -clist_filter(struct linpool *pool, struct adata *list, struct f_val set, int pos) +static const struct adata * +clist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos) { if (!list) return NULL; - int tree = (set.type == T_SET); /* 1 -> set is T_SET, 0 -> set is T_CLIST */ + int tree = (set->type == T_SET); /* 1 -> set is T_SET, 0 -> set is T_CLIST */ struct f_val v; if (tree) - clist_set_type(set.val.t, &v); + clist_set_type(set->val.t, &v); else v.type = T_PAIR; @@ -383,7 +388,7 @@ clist_filter(struct linpool *pool, struct adata *list, struct f_val set, int pos while (l < end) { v.val.i = *l++; /* pos && member(val, set) || !pos && !member(val, set), member() depends on tree */ - if ((tree ? !!find_tree(set.val.t, v) : int_set_contains(set.val.ad, v.val.i)) == pos) + if ((tree ? !!find_tree(set->val.t, &v) : int_set_contains(set->val.ad, v.val.i)) == pos) *k++ = v.val.i; } @@ -396,13 +401,13 @@ clist_filter(struct linpool *pool, struct adata *list, struct f_val set, int pos return res; } -static struct adata * -eclist_filter(struct linpool *pool, struct adata *list, struct f_val set, int pos) +static const struct adata * +eclist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos) { if (!list) return NULL; - int tree = (set.type == T_SET); /* 1 -> set is T_SET, 0 -> set is T_CLIST */ + int tree = (set->type == T_SET); /* 1 -> set is T_SET, 0 -> set is T_CLIST */ struct f_val v; int len = int_set_get_size(list); @@ -415,7 +420,7 @@ eclist_filter(struct linpool *pool, struct adata *list, struct f_val set, int po for (i = 0; i < len; i += 2) { v.val.ec = ec_get(l, i); /* pos && member(val, set) || !pos && !member(val, set), member() depends on tree */ - if ((tree ? !!find_tree(set.val.t, v) : ec_set_contains(set.val.ad, v.val.ec)) == pos) { + if ((tree ? !!find_tree(set->val.t, &v) : ec_set_contains(set->val.ad, v.val.ec)) == pos) { *k++ = l[i]; *k++ = l[i+1]; } @@ -430,13 +435,13 @@ eclist_filter(struct linpool *pool, struct adata *list, struct f_val set, int po return res; } -static struct adata * -lclist_filter(struct linpool *pool, struct adata *list, struct f_val set, int pos) +static const struct adata * +lclist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos) { if (!list) return NULL; - int tree = (set.type == T_SET); /* 1 -> set is T_SET, 0 -> set is T_CLIST */ + int tree = (set->type == T_SET); /* 1 -> set is T_SET, 0 -> set is T_CLIST */ struct f_val v; int len = int_set_get_size(list); @@ -449,7 +454,7 @@ lclist_filter(struct linpool *pool, struct adata *list, struct f_val set, int po for (i = 0; i < len; i += 3) { v.val.lc = lc_get(l, i); /* pos && member(val, set) || !pos && !member(val, set), member() depends on tree */ - if ((tree ? !!find_tree(set.val.t, v) : lc_set_contains(set.val.ad, v.val.lc)) == pos) + if ((tree ? !!find_tree(set->val.t, &v) : lc_set_contains(set->val.ad, v.val.lc)) == pos) k = lc_copy(k, l+i); } @@ -470,57 +475,57 @@ lclist_filter(struct linpool *pool, struct adata *list, struct f_val set, int po * Checks if @v1 is element (|~| operator) of @v2. */ static int -val_in_range(struct f_val v1, struct f_val v2) +val_in_range(const struct f_val *v1, const struct f_val *v2) { - if ((v1.type == T_PATH) && (v2.type == T_PATH_MASK)) - return as_path_match(v1.val.ad, v2.val.path_mask); + if ((v1->type == T_PATH) && (v2->type == T_PATH_MASK)) + return as_path_match(v1->val.ad, v2->val.path_mask); - if ((v1.type == T_INT) && (v2.type == T_PATH)) - return as_path_contains(v2.val.ad, v1.val.i, 1); + if ((v1->type == T_INT) && (v2->type == T_PATH)) + return as_path_contains(v2->val.ad, v1->val.i, 1); - if (((v1.type == T_PAIR) || (v1.type == T_QUAD)) && (v2.type == T_CLIST)) - return int_set_contains(v2.val.ad, v1.val.i); + if (((v1->type == T_PAIR) || (v1->type == T_QUAD)) && (v2->type == T_CLIST)) + return int_set_contains(v2->val.ad, v1->val.i); /* IP->Quad implicit conversion */ - if (val_is_ip4(v1) && (v2.type == T_CLIST)) - return int_set_contains(v2.val.ad, ipa_to_u32(v1.val.ip)); + if (val_is_ip4(v1) && (v2->type == T_CLIST)) + return int_set_contains(v2->val.ad, ipa_to_u32(v1->val.ip)); - if ((v1.type == T_EC) && (v2.type == T_ECLIST)) - return ec_set_contains(v2.val.ad, v1.val.ec); + if ((v1->type == T_EC) && (v2->type == T_ECLIST)) + return ec_set_contains(v2->val.ad, v1->val.ec); - if ((v1.type == T_LC) && (v2.type == T_LCLIST)) - return lc_set_contains(v2.val.ad, v1.val.lc); + if ((v1->type == T_LC) && (v2->type == T_LCLIST)) + return lc_set_contains(v2->val.ad, v1->val.lc); - if ((v1.type == T_STRING) && (v2.type == T_STRING)) - return patmatch(v2.val.s, v1.val.s); + if ((v1->type == T_STRING) && (v2->type == T_STRING)) + return patmatch(v2->val.s, v1->val.s); - if ((v1.type == T_IP) && (v2.type == T_NET)) - return ipa_in_netX(v1.val.ip, v2.val.net); + if ((v1->type == T_IP) && (v2->type == T_NET)) + return ipa_in_netX(v1->val.ip, v2->val.net); - if ((v1.type == T_NET) && (v2.type == T_NET)) - return net_in_netX(v1.val.net, v2.val.net); + if ((v1->type == T_NET) && (v2->type == T_NET)) + return net_in_netX(v1->val.net, v2->val.net); - if ((v1.type == T_NET) && (v2.type == T_PREFIX_SET)) - return trie_match_net(v2.val.ti, v1.val.net); + if ((v1->type == T_NET) && (v2->type == T_PREFIX_SET)) + return trie_match_net(v2->val.ti, v1->val.net); - if (v2.type != T_SET) + if (v2->type != T_SET) return CMP_ERROR; /* With integrated Quad<->IP implicit conversion */ - if ((v1.type == v2.val.t->from.type) || - ((v1.type == T_QUAD) && val_is_ip4(v2.val.t->from) && val_is_ip4(v2.val.t->to))) - return !!find_tree(v2.val.t, v1); + if ((v1->type == v2->val.t->from.type) || + ((v1->type == T_QUAD) && val_is_ip4(&(v2->val.t->from)) && val_is_ip4(&(v2->val.t->to)))) + return !!find_tree(v2->val.t, v1); - if (v1.type == T_CLIST) - return clist_match_set(v1.val.ad, v2.val.t); + if (v1->type == T_CLIST) + return clist_match_set(v1->val.ad, v2->val.t); - if (v1.type == T_ECLIST) - return eclist_match_set(v1.val.ad, v2.val.t); + if (v1->type == T_ECLIST) + return eclist_match_set(v1->val.ad, v2->val.t); - if (v1.type == T_LCLIST) - return lclist_match_set(v1.val.ad, v2.val.t); + if (v1->type == T_LCLIST) + return lclist_match_set(v1->val.ad, v2->val.t); - if (v1.type == T_PATH) - return as_path_match_set(v1.val.ad, v2.val.t); + if (v1->type == T_PATH) + return as_path_match_set(v1->val.ad, v2->val.t); return CMP_ERROR; } @@ -529,31 +534,31 @@ val_in_range(struct f_val v1, struct f_val v2) * val_format - format filter value */ void -val_format(struct f_val v, buffer *buf) +val_format(const struct f_val *v, buffer *buf) { char buf2[1024]; - switch (v.type) + switch (v->type) { case T_VOID: buffer_puts(buf, "(void)"); return; - case T_BOOL: buffer_puts(buf, v.val.i ? "TRUE" : "FALSE"); return; - case T_INT: buffer_print(buf, "%u", v.val.i); return; - case T_STRING: buffer_print(buf, "%s", v.val.s); return; - case T_IP: buffer_print(buf, "%I", v.val.ip); return; - case T_NET: buffer_print(buf, "%N", v.val.net); return; - case T_PAIR: buffer_print(buf, "(%u,%u)", v.val.i >> 16, v.val.i & 0xffff); return; - case T_QUAD: buffer_print(buf, "%R", v.val.i); return; - case T_EC: ec_format(buf2, v.val.ec); buffer_print(buf, "%s", buf2); return; - case T_LC: lc_format(buf2, v.val.lc); buffer_print(buf, "%s", buf2); return; - case T_RD: rd_format(v.val.ec, buf2, 1024); buffer_print(buf, "%s", buf2); return; - case T_PREFIX_SET: trie_format(v.val.ti, buf); return; - case T_SET: tree_format(v.val.t, buf); return; - case T_ENUM: buffer_print(buf, "(enum %x)%u", v.type, v.val.i); return; - case T_PATH: as_path_format(v.val.ad, buf2, 1000); buffer_print(buf, "(path %s)", buf2); return; - case T_CLIST: int_set_format(v.val.ad, 1, -1, buf2, 1000); buffer_print(buf, "(clist %s)", buf2); return; - case T_ECLIST: ec_set_format(v.val.ad, -1, buf2, 1000); buffer_print(buf, "(eclist %s)", buf2); return; - case T_LCLIST: lc_set_format(v.val.ad, -1, buf2, 1000); buffer_print(buf, "(lclist %s)", buf2); return; - case T_PATH_MASK: pm_format(v.val.path_mask, buf); return; - default: buffer_print(buf, "[unknown type %x]", v.type); return; + case T_BOOL: buffer_puts(buf, v->val.i ? "TRUE" : "FALSE"); return; + case T_INT: buffer_print(buf, "%u", v->val.i); return; + case T_STRING: buffer_print(buf, "%s", v->val.s); return; + case T_IP: buffer_print(buf, "%I", v->val.ip); return; + case T_NET: buffer_print(buf, "%N", v->val.net); return; + case T_PAIR: buffer_print(buf, "(%u,%u)", v->val.i >> 16, v->val.i & 0xffff); return; + case T_QUAD: buffer_print(buf, "%R", v->val.i); return; + case T_EC: ec_format(buf2, v->val.ec); buffer_print(buf, "%s", buf2); return; + case T_LC: lc_format(buf2, v->val.lc); buffer_print(buf, "%s", buf2); return; + case T_RD: rd_format(v->val.ec, buf2, 1024); buffer_print(buf, "%s", buf2); return; + case T_PREFIX_SET: trie_format(v->val.ti, buf); return; + case T_SET: tree_format(v->val.t, buf); return; + case T_ENUM: buffer_print(buf, "(enum %x)%u", v->type, v->val.i); return; + case T_PATH: as_path_format(v->val.ad, buf2, 1000); buffer_print(buf, "(path %s)", buf2); return; + case T_CLIST: int_set_format(v->val.ad, 1, -1, buf2, 1000); buffer_print(buf, "(clist %s)", buf2); return; + case T_ECLIST: ec_set_format(v->val.ad, -1, buf2, 1000); buffer_print(buf, "(eclist %s)", buf2); return; + case T_LCLIST: lc_set_format(v->val.ad, -1, buf2, 1000); buffer_print(buf, "(lclist %s)", buf2); return; + case T_PATH_MASK: pm_format(v->val.path_mask, buf); return; + default: buffer_print(buf, "[unknown type %x]", v->type); return; } } @@ -599,7 +604,7 @@ f_rta_cow(struct filter_state *fs) } static char * -val_format_str(struct filter_state *fs, struct f_val v) { +val_format_str(struct filter_state *fs, struct f_val *v) { buffer b; LOG_BUFFER_INIT(b); val_format(v, &b); @@ -608,6 +613,98 @@ val_format_str(struct filter_state *fs, struct f_val v) { static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS; +static uint +inst_line_size(const struct f_inst *what) +{ + uint cnt = 0; + for ( ; what; what = what->next) { + switch (what->fi_code) { +#include "filter/f-inst-line-size.c" + } + } + return cnt; +} + +#if DEBUGGING +#define INDENT (((const char *) f_dump_line_indent_str) + sizeof(f_dump_line_indent_str) - (indent) - 1) +static const char f_dump_line_indent_str[] = " "; + +static char val_dump_buffer[1024]; + +static const char * +val_dump(const struct f_val *v) { + static buffer b = { + .start = val_dump_buffer, + .end = val_dump_buffer + 1024, + }; + b.pos = b.start; + val_format(v, &b); + return val_dump_buffer; +} + +static void f_dump_line(const struct f_line *dest, int indent); + +static void +f_dump_line_item(const struct f_line_item *item, int indent) +{ + debug("%sInstruction %s at line %u\n", INDENT, f_instruction_name(item->fi_code), item->lineno); + switch (item->fi_code) { +#include "filter/f-inst-dump.c" + } +} + +static void +f_dump_line(const struct f_line *dest, int indent) +{ + if (!dest) { + debug("%sNo filter line (NULL)\n", INDENT); + return; + } + debug("%sFilter line %p (len=%u)\n", INDENT, dest, dest->len); + for (uint i=0; i<dest->len; i++) + f_dump_line_item(&dest->items[i], indent+1); + debug("%sFilter line %p dump done\n", INDENT, dest); +#undef INDENT +} +#else +#define f_dump_line(...) +#endif + +static uint +postfixify(struct f_line *dest, const struct f_inst *what, uint pos) +{ + for ( ; what; what = what->next) { + switch (what->fi_code) { +#include "filter/f-inst-postfixify.c" + } + pos++; + } + return pos; +} + +struct f_line * +f_postfixify_concat(struct f_inst *first, ...) +{ + va_list args; + va_list argd; + va_start(args, first); + va_copy(argd, args); + + uint len = 0; + for (struct f_inst *what = first; what; what = va_arg(args, struct f_inst *)) + len += inst_line_size(what); + + va_end(args); + + struct f_line *out = cfg_allocz(sizeof(struct f_line) + sizeof(struct f_line_item)*len); + + for (struct f_inst *what = first; what; what = va_arg(argd, struct f_inst *)) + out->len = postfixify(out, what, out->len); + + f_dump_line(out, 0); + return out; +} + /** * interpret * @fs: filter state @@ -624,26 +721,28 @@ static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS; * TWOARGS macro to get both of them evaluated. */ static enum filter_return -interpret(struct filter_state *fs, struct f_inst *what) +interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val) { - struct symbol *sym; - struct f_val *vp; - unsigned u1, u2; - enum filter_return fret; - int i; - u32 as; + struct f_val_stack vstk; + vstk.cnt = 0; -#define res fs->stack[fs->stack_ptr].val -#define v0 res -#define v1 fs->stack[fs->stack_ptr + 1].val -#define v2 fs->stack[fs->stack_ptr + 2].val -#define v3 fs->stack[fs->stack_ptr + 3].val + struct f_exec_stack estk; + estk.cnt = 1; + estk.item[0].line = line; + estk.item[0].pos = 0; - res = (struct f_val) { .type = T_VOID }; +#define curline estk.item[estk.cnt-1] + + while (estk.cnt > 0) { + while (curline.pos < curline.line->len) { + const struct f_line_item *what = &(curline.line->items[curline.pos++]); - for ( ; what; what = what->next) { - res = (struct f_val) { .type = T_VOID }; - switch (what->fi_code) { + + switch (what->fi_code) { +#define res vstk.val[vstk.cnt] +#define v1 vstk.val[vstk.cnt] +#define v2 vstk.val[vstk.cnt + 1] +#define v3 vstk.val[vstk.cnt + 2] #define runtime(fmt, ...) do { \ if (!(fs->flags & FF_SILENT)) \ @@ -651,84 +750,70 @@ interpret(struct filter_state *fs, struct f_inst *what) return F_ERROR; \ } while(0) -#define ARG_ANY_T(n, tt) INTERPRET(what->a[n-1].p, tt) -#define ARG_ANY(n) ARG_ANY_T(n, n) - -#define ARG_T(n,tt,t) do { \ - ARG_ANY_T(n,tt); \ - if (v##tt.type != t) \ - runtime("Argument %d of instruction %s must be of type %02x, got %02x", \ - n, f_instruction_name(what->fi_code), t, v##tt.type); \ -} while (0) - -#define ARG(n,t) ARG_T(n,n,t) - -#define INTERPRET(what_, n) do { \ - fs->stack_ptr += n; \ - fret = interpret(fs, what_); \ - fs->stack_ptr -= n; \ - if (fret == F_RETURN) \ - bug("This shall not happen"); \ - if (fret > F_RETURN) \ - return fret; \ -} while (0) - #define ACCESS_RTE do { if (!fs->rte) runtime("No route to access"); } while (0) - #define ACCESS_EATTRS do { if (!fs->eattrs) f_cache_eattrs(fs); } while (0) -#define BITFIELD_MASK(what_) (1u << EA_BIT_GET(what_->a[1].i)) - - case FI_NOP: - bug("This shall not happen"); - #include "filter/f-inst-interpret.c" - - break; - default: - bug( "Unknown instruction %d (%c)", what->fi_code, what->fi_code & 0xff); - #undef res +#undef v1 +#undef v2 +#undef v3 #undef runtime -#undef ARG_ANY -#undef ARG -#undef INTERPRET #undef ACCESS_RTE #undef ACCESS_EATTRS + } } + estk.cnt--; } - return F_NOP; -} + switch (vstk.cnt) { + case 0: + if (val) { + log_rl(&rl_runtime_err, L_ERR "filters: No value left on stack"); + return F_ERROR; + } + return F_NOP; + case 1: + if (val) { + *val = vstk.val[0]; + return F_NOP; + } + /* fallthrough */ + default: + log_rl(&rl_runtime_err, L_ERR "Too many items left on stack: %u", vstk.cnt); + return F_ERROR; + } +} -#define ARG(n) \ - if (!i_same(f1->a[n-1].p, f2->a[n-1].p)) \ - return 0; - -#define ONEARG ARG(1); -#define TWOARGS ONEARG; ARG(2); -#define THREEARGS TWOARGS; ARG(3); - -#define A2_SAME if (f1->a[1].i != f2->a[1].i) return 0; /* - * i_same - function that does real comparing of instruction trees, you should call filter_same from outside + * f_same - function that does real comparing of instruction trees, you should call filter_same from outside */ int -i_same(struct f_inst *f1, struct f_inst *f2) +f_same(const struct f_line *fl1, const struct f_line *fl2) { - if ((!!f1) != (!!f2)) - return 0; - if (!f1) + if ((!fl1) && (!fl2)) return 1; - if (f1->aux != f2->aux) + if ((!fl1) || (!fl2)) return 0; - if (f1->fi_code != f2->fi_code) + if (fl1->len != fl2->len) return 0; - if (f1 == f2) /* It looks strange, but it is possible with call rewriting trickery */ - return 1; + for (uint i=0; i<fl1->len; i++) { +#define f1 (&(fl1->items[i])) +#define f2 (&(fl2->items[i])) + if (f1->fi_code != f2->fi_code) + return 0; + if (f1->flags != f2->flags) + return 0; - switch(f1->fi_code) { + switch(f1->fi_code) { +#include "filter/f-inst-same.c" + } + } + return 1; +} + +#if 0 case FI_ADD: /* fall through */ case FI_SUBTRACT: case FI_MULTIPLY: @@ -842,9 +927,7 @@ i_same(struct f_inst *f1, struct f_inst *f2) case FI_ASSERT: ONEARG; break; default: bug( "Unknown instruction %d in same (%c)", f1->fi_code, f1->fi_code & 0xff); - } - return i_same(f1->next, f2->next); -} +#endif /** * f_run - run a filter for a route @@ -871,7 +954,7 @@ i_same(struct f_inst *f1, struct f_inst *f2) * modified in place, old cached rta is possibly freed. */ enum filter_return -f_run(struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int flags) +f_run(const struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int flags) { if (filter == FILTER_ACCEPT) return F_ACCEPT; @@ -886,12 +969,11 @@ f_run(struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int fla .rte = rte, .pool = tmp_pool, .flags = flags, - .stack = filter_stack, }; LOG_BUFFER_INIT(fs.buf); - enum filter_return fret = interpret(&fs, filter->root); + enum filter_return fret = interpret(&fs, filter->root, NULL); if (fs.old_rta) { /* @@ -925,54 +1007,52 @@ f_run(struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int fla /* TODO: perhaps we could integrate f_eval(), f_eval_rte() and f_run() */ enum filter_return -f_eval_rte(struct f_inst *expr, struct rte **rte, struct linpool *tmp_pool) +f_eval_rte(const struct f_line *expr, struct rte **rte, struct linpool *tmp_pool) { struct filter_state fs = { .rte = rte, .pool = tmp_pool, - .stack = filter_stack, }; LOG_BUFFER_INIT(fs.buf); /* Note that in this function we assume that rte->attrs is private / uncached */ - return interpret(&fs, expr); + return interpret(&fs, expr, NULL); } enum filter_return -f_eval(struct f_inst *expr, struct linpool *tmp_pool, struct f_val *pres) +f_eval(const struct f_line *expr, struct linpool *tmp_pool, struct f_val *pres) { struct filter_state fs = { .pool = tmp_pool, - .stack = filter_stack, }; LOG_BUFFER_INIT(fs.buf); - enum filter_return fret = interpret(&fs, expr); - *pres = filter_stack[0].val; + enum filter_return fret = interpret(&fs, expr, pres); return fret; } uint -f_eval_int(struct f_inst *expr) +f_eval_int(const struct f_line *expr) { /* Called independently in parse-time to eval expressions */ struct filter_state fs = { .pool = cfg_mem, - .stack = filter_stack, }; + struct f_val val; + LOG_BUFFER_INIT(fs.buf); - if (interpret(&fs, expr) > F_RETURN) + if (interpret(&fs, expr, &val) > F_RETURN) cf_error("Runtime error while evaluating expression"); - if (filter_stack[0].val.type != T_INT) + if (val.type != T_INT) cf_error("Integer expression expected"); - return filter_stack[0].val.val.i; + return val.val.i; } /** @@ -993,5 +1073,5 @@ filter_same(struct filter *new, struct filter *old) if (old == FILTER_ACCEPT || old == FILTER_REJECT || new == FILTER_ACCEPT || new == FILTER_REJECT) return 0; - return i_same(new->root, old->root); + return f_same(new->root, old->root); } diff --git a/filter/filter.h b/filter/filter.h index 594c9511..87bd2c36 100644 --- a/filter/filter.h +++ b/filter/filter.h @@ -14,13 +14,65 @@ #include "nest/route.h" #include "nest/attrs.h" +/* IP prefix range structure */ struct f_prefix { - net_addr net; - u8 lo, hi; + net_addr net; /* The matching prefix must match this net */ + u8 lo, hi; /* And its length must fit between lo and hi */ }; +/* Type numbers must be in 0..0xff range */ +#define T_MASK 0xff + +/* Internal types */ +enum f_type { +/* Do not use type of zero, that way we'll see errors easier. */ + T_VOID = 1, + +/* User visible types, which fit in int */ + T_INT = 0x10, + T_BOOL = 0x11, + T_PAIR = 0x12, /* Notice that pair is stored as integer: first << 16 | second */ + T_QUAD = 0x13, + +/* Put enumerational types in 0x30..0x3f range */ + T_ENUM_LO = 0x30, + T_ENUM_HI = 0x3f, + + T_ENUM_RTS = 0x30, + T_ENUM_BGP_ORIGIN = 0x31, + T_ENUM_SCOPE = 0x32, + T_ENUM_RTC = 0x33, + T_ENUM_RTD = 0x34, + T_ENUM_ROA = 0x35, + T_ENUM_NETTYPE = 0x36, + T_ENUM_RA_PREFERENCE = 0x37, + +/* new enums go here */ + T_ENUM_EMPTY = 0x3f, /* Special hack for atomic_aggr */ + +#define T_ENUM T_ENUM_LO ... T_ENUM_HI + +/* Bigger ones */ + T_IP = 0x20, + T_NET = 0x21, + T_STRING = 0x22, + T_PATH_MASK = 0x23, /* mask for BGP path */ + T_PATH = 0x24, /* BGP path */ + T_CLIST = 0x25, /* Community list */ + T_EC = 0x26, /* Extended community value, u64 */ + T_ECLIST = 0x27, /* Extended community list */ + T_LC = 0x28, /* Large community value, lcomm */ + T_LCLIST = 0x29, /* Large community list */ + T_RD = 0x2a, /* Route distinguisher for VPN addresses */ + T_PATH_MASK_ITEM = 0x2b, /* Path mask item for path mask constructors */ + + T_SET = 0x80, + T_PREFIX_SET = 0x81, +} PACKED; + +/* Filter value; size of this affects filter memory consumption */ struct f_val { - int type; /* T_* */ + enum f_type type; /* T_* */ union { uint i; u64 ec; @@ -28,27 +80,42 @@ struct f_val { ip_addr ip; const net_addr *net; char *s; - struct f_tree *t; - struct f_trie *ti; - struct adata *ad; - struct f_path_mask *path_mask; + const struct f_tree *t; + const struct f_trie *ti; + const struct adata *ad; + const struct f_path_mask *path_mask; + struct f_path_mask_item pmi; } val; }; +/* Dynamic attribute definition (eattrs) */ struct f_dynamic_attr { - int type; - int f_type; - int ea_code; + u8 type; /* EA type (EAF_*) */ + u8 bit; /* For bitfield accessors */ + enum f_type f_type; /* Filter type */ + uint ea_code; /* EA code */ }; +enum f_sa_code { + SA_FROM = 1, + SA_GW, + SA_NET, + SA_PROTO, + SA_SOURCE, + SA_SCOPE, + SA_DEST, + SA_IFNAME, + SA_IFINDEX, +} PACKED; + +/* Static attribute definition (members of struct rta) */ struct f_static_attr { - int f_type; - int sa_code; - int readonly; + enum f_type f_type; /* Filter type */ + enum f_sa_code sa_code; /* Static attribute id */ + int readonly:1; /* Don't allow writing */ }; -/* Filter instruction types */ - +/* Filter instruction words */ #define FI__TWOCHAR(a,b) ((a<<8) | b) #define FI__LIST \ F(FI_NOP, 0, '0') \ @@ -96,16 +163,20 @@ struct f_static_attr { F(FI_AS_PATH_LAST_NAG, 'a', 'L') \ F(FI_RETURN, 0, 'r') \ F(FI_CALL, 'c', 'a') \ + F(FI_DROP_RESULT, 'd', 'r') \ F(FI_CLEAR_LOCAL_VARS, 'c', 'V') \ F(FI_SWITCH, 'S', 'W') \ F(FI_IP_MASK, 'i', 'M') \ - F(FI_EMPTY, 0, 'E') \ F(FI_PATH_PREPEND, 'A', 'p') \ - F(FI_CLIST_ADD_DEL, 'C', 'a') \ - F(FI_ROA_CHECK, 'R', 'C') \ + F(FI_CLIST_ADD, 'C', 'a') \ + F(FI_CLIST_DEL, 'C', 'd') \ + F(FI_CLIST_FILTER, 'C', 'f') \ + F(FI_ROA_CHECK_IMPLICIT, 'R', 'i') \ + F(FI_ROA_CHECK_EXPLICIT, 'R', 'e') \ F(FI_FORMAT, 0, 'F') \ F(FI_ASSERT, 'a', 's') +/* The enum itself */ enum f_instruction_code { #define F(c,a,b) \ c, @@ -114,145 +185,165 @@ FI__LIST FI__MAX, } PACKED; +/* Convert the instruction back to the enum name */ const char *f_instruction_name(enum f_instruction_code fi); -struct f_inst { /* Instruction */ - struct f_inst *next; /* Structure is 16 bytes, anyway */ - enum f_instruction_code fi_code; +enum f_instruction_flags { + FIF_PRINTED = 1, /* FI_PRINT_AND_DIE: message put in buffer */ +}; + +union f_inst_attr { + uint i; + void *p; + struct rtable_config *rtc; +}; + +/* Instruction structure for config */ +struct f_inst { + struct f_inst *next; /* Next instruction to be executed */ + enum f_instruction_code fi_code; /* The instruction itself */ u16 aux; /* Extension to instruction code, T_*, EA_*, EAF_* */ union { - union { - uint i; - void *p; - struct rtable_config *rtc; - } a[3]; /* The three arguments */ + union f_inst_attr a[3]; /* The three arguments */ struct f_val val; /* The value if FI_CONSTANT */ + struct { + union f_inst_attr sa_a[1]; + struct f_static_attr sa; /* Static attribute def for FI_RTA_* */ + }; + struct { + union f_inst_attr da_a[1]; + struct f_dynamic_attr da; /* Dynamic attribute def for FI_EA_* */ + }; }; int lineno; }; -#define arg1 a[0].p -#define arg2 a[1].p -#define arg3 a[2].p +/* Possible return values of filter execution */ +enum filter_return { + F_NOP = 0, + F_NONL, + F_RETURN, + F_ACCEPT, /* Need to preserve ordering: accepts < rejects! */ + F_REJECT, + F_ERROR, + F_QUITBIRD, +}; + +/* Filter structures for execution */ +struct f_line; +/* The single instruction item */ +struct f_line_item { + enum f_instruction_code fi_code; /* What to do */ + enum f_instruction_flags flags; /* Flags, instruction-specific */ + uint lineno; /* Where */ + union { + struct { + const struct f_val *vp; + const struct symbol *sym; + }; + const struct f_line *lines[2]; + enum filter_return fret; + struct f_static_attr sa; + struct f_dynamic_attr da; + enum ec_subtype ecs; + const char *s; + const struct f_tree *tree; + const struct rtable_config *rtc; + uint count; + }; /* Additional instruction data */ +}; + +/* Line of instructions to be unconditionally executed one after another */ +struct f_line { + uint len; /* Line length */ + struct f_line_item items[0]; /* The items themselves */ +}; + +/* The filter encapsulating structure to be pointed-to from outside */ struct filter { char *name; - struct f_inst *root; + struct f_line *root; +}; + +/* Convert the f_inst infix tree to the f_line structures */ +struct f_line *f_postfixify_concat(struct f_inst *root, ...); +static inline struct f_line *f_postfixify(struct f_inst *root) +{ return f_postfixify_concat(root, NULL); } + +#define F_VAL_STACK_MAX 4096 + +/* Value stack for execution */ +struct f_val_stack { + uint cnt; /* Current stack size; 0 for empty */ + struct f_val val[F_VAL_STACK_MAX]; /* The stack itself */ +}; + +#define F_EXEC_STACK_MAX 4096 + +/* Exception bits */ +enum f_exception { + FE_RETURN = 0x1, +}; + +/* Instruction stack for execution */ +struct f_exec_stack { + struct { + const struct f_line *line; /* The line that is being executed */ + uint pos; /* Instruction index in the line */ + uint ventry; /* Value stack depth on entry */ + enum f_exception emask; /* Exception mask */ + } item[F_EXEC_STACK_MAX]; + uint cnt; /* Current stack size; 0 for empty */ + }; struct f_inst *f_new_inst(enum f_instruction_code fi_code); struct f_inst *f_new_inst_da(enum f_instruction_code fi_code, struct f_dynamic_attr da); struct f_inst *f_new_inst_sa(enum f_instruction_code fi_code, struct f_static_attr sa); -static inline struct f_dynamic_attr f_new_dynamic_attr(int type, int f_type, int code) /* Type as core knows it, type as filters know it, and code of dynamic attribute */ -{ return (struct f_dynamic_attr) { .type = type, .f_type = f_type, .ea_code = code }; } /* f_type currently unused; will be handy for static type checking */ +static inline struct f_dynamic_attr f_new_dynamic_attr(u8 type, u8 bit, enum f_type f_type, uint code) /* Type as core knows it, type as filters know it, and code of dynamic attribute */ +{ return (struct f_dynamic_attr) { .type = type, .bit = bit, .f_type = f_type, .ea_code = code }; } /* f_type currently unused; will be handy for static type checking */ static inline struct f_static_attr f_new_static_attr(int f_type, int code, int readonly) { return (struct f_static_attr) { .f_type = f_type, .sa_code = code, .readonly = readonly }; } struct f_tree *f_new_tree(void); -struct f_inst *f_generate_complex(int operation, int operation_aux, struct f_dynamic_attr da, struct f_inst *argument); +struct f_inst *f_generate_complex(enum f_instruction_code fi_code, struct f_dynamic_attr da, struct f_inst *argument); struct f_inst *f_generate_roa_check(struct rtable_config *table, struct f_inst *prefix, struct f_inst *asn); struct f_tree *build_tree(struct f_tree *); -struct f_tree *find_tree(struct f_tree *t, struct f_val val); -int same_tree(struct f_tree *t1, struct f_tree *t2); -void tree_format(struct f_tree *t, buffer *buf); +const struct f_tree *find_tree(const struct f_tree *t, const struct f_val *val); +int same_tree(const struct f_tree *t1, const struct f_tree *t2); +void tree_format(const struct f_tree *t, buffer *buf); struct f_trie *f_new_trie(linpool *lp, uint node_size); void *trie_add_prefix(struct f_trie *t, const net_addr *n, uint l, uint h); -int trie_match_net(struct f_trie *t, const net_addr *n); -int trie_same(struct f_trie *t1, struct f_trie *t2); -void trie_format(struct f_trie *t, buffer *buf); +int trie_match_net(const struct f_trie *t, const net_addr *n); +int trie_same(const struct f_trie *t1, const struct f_trie *t2); +void trie_format(const struct f_trie *t, buffer *buf); struct ea_list; struct rte; -enum filter_return { - F_NOP = 0, - F_NONL, - F_RETURN, - F_ACCEPT, /* Need to preserve ordering: accepts < rejects! */ - F_REJECT, - F_ERROR, - F_QUITBIRD, -}; - -enum filter_return f_run(struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int flags); -enum filter_return f_eval_rte(struct f_inst *expr, struct rte **rte, struct linpool *tmp_pool); -enum filter_return f_eval(struct f_inst *expr, struct linpool *tmp_pool, struct f_val *pres); -uint f_eval_int(struct f_inst *expr); +enum filter_return f_run(const struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int flags); +enum filter_return f_eval_rte(const struct f_line *expr, struct rte **rte, struct linpool *tmp_pool); +enum filter_return f_eval(const struct f_line *expr, struct linpool *tmp_pool, struct f_val *pres); +uint f_eval_int(const struct f_line *expr); char *filter_name(struct filter *filter); int filter_same(struct filter *new, struct filter *old); -int i_same(struct f_inst *f1, struct f_inst *f2); +int f_same(const struct f_line *f1, const struct f_line *f2); + +int val_compare(const struct f_val *v1, const struct f_val *v2); -int val_compare(struct f_val v1, struct f_val v2); -int val_same(struct f_val v1, struct f_val v2); +void val_format(const struct f_val *v, buffer *buf); -void val_format(struct f_val v, buffer *buf); +extern const struct f_val f_const_empty_path, f_const_empty_clist, f_const_empty_eclist, f_const_empty_lclist; #define FILTER_ACCEPT NULL #define FILTER_REJECT ((void *) 1) #define FILTER_UNDEF ((void *) 2) /* Used in BGP */ -/* Type numbers must be in 0..0xff range */ -#define T_MASK 0xff - -/* Internal types */ -/* Do not use type of zero, that way we'll see errors easier. */ -#define T_VOID 1 - -/* User visible types, which fit in int */ -#define T_INT 0x10 -#define T_BOOL 0x11 -#define T_PAIR 0x12 /* Notice that pair is stored as integer: first << 16 | second */ -#define T_QUAD 0x13 - -/* Put enumerational types in 0x30..0x3f range */ -#define T_ENUM_LO 0x30 -#define T_ENUM_HI 0x3f - -#define T_ENUM_RTS 0x30 -#define T_ENUM_BGP_ORIGIN 0x31 -#define T_ENUM_SCOPE 0x32 -#define T_ENUM_RTC 0x33 -#define T_ENUM_RTD 0x34 -#define T_ENUM_ROA 0x35 -#define T_ENUM_NETTYPE 0x36 -#define T_ENUM_RA_PREFERENCE 0x37 - -/* new enums go here */ -#define T_ENUM_EMPTY 0x3f /* Special hack for atomic_aggr */ - -#define T_ENUM T_ENUM_LO ... T_ENUM_HI - -/* Bigger ones */ -#define T_IP 0x20 -#define T_NET 0x21 -#define T_STRING 0x22 -#define T_PATH_MASK 0x23 /* mask for BGP path */ -#define T_PATH 0x24 /* BGP path */ -#define T_CLIST 0x25 /* Community list */ -#define T_EC 0x26 /* Extended community value, u64 */ -#define T_ECLIST 0x27 /* Extended community list */ -#define T_LC 0x28 /* Large community value, lcomm */ -#define T_LCLIST 0x29 /* Large community list */ -#define T_RD 0x2a /* Route distinguisher for VPN addresses */ - -#define T_SET 0x80 -#define T_PREFIX_SET 0x81 - - -#define SA_FROM 1 -#define SA_GW 2 -#define SA_NET 3 -#define SA_PROTO 4 -#define SA_SOURCE 5 -#define SA_SCOPE 6 -#define SA_DEST 7 -#define SA_IFNAME 8 -#define SA_IFINDEX 9 - struct f_tree { struct f_tree *left, *right; @@ -291,12 +382,12 @@ struct custom_attribute *ca_lookup(pool *p, const char *name, int ea_type); /* Bird Tests */ struct f_bt_test_suite { node n; /* Node in config->tests */ - struct f_inst *fn; /* Root of function */ + struct f_line *fn; /* Root of function */ const char *fn_name; /* Name of test */ const char *dsc; /* Description */ }; /* Hook for call bt_assert() function in configuration */ -extern void (*bt_assert_hook)(int result, struct f_inst *assert); +extern void (*bt_assert_hook)(int result, const struct f_line_item *assert); #endif diff --git a/filter/filter_test.c b/filter/filter_test.c index 32555d82..e19b0a75 100644 --- a/filter/filter_test.c +++ b/filter/filter_test.c @@ -40,8 +40,7 @@ parse_config_file(const void *filename_void) static int run_function(const void *parsed_fn_def) { - /* XXX: const -> non-const */ - struct f_inst *f = (struct f_inst *) parsed_fn_def; + const struct f_line *f = (const struct f_line *) parsed_fn_def; linpool *tmp = lp_new_default(&root_pool); struct f_val res; @@ -52,7 +51,7 @@ run_function(const void *parsed_fn_def) } static void -bt_assert_filter(int result, struct f_inst *assert) +bt_assert_filter(int result, const struct f_line_item *assert) { int bt_suit_case_result = 1; if (!result) @@ -62,7 +61,7 @@ bt_assert_filter(int result, struct f_inst *assert) bt_suit_case_result = 0; } - bt_log_suite_case_result(bt_suit_case_result, "Assertion at line %d (%s)", assert->lineno, (char *) assert->a[1].p); + bt_log_suite_case_result(bt_suit_case_result, "Assertion at line %d (%s)", assert->lineno, assert->s); } int diff --git a/filter/interpret.m4 b/filter/interpret.m4 index d1c83389..829b48f6 100644 --- a/filter/interpret.m4 +++ b/filter/interpret.m4 @@ -10,7 +10,60 @@ m4_divert(-1)m4_dnl # Common aliases m4_define(DNL, `m4_dnl') -m4_define(INST, `break; case $1:') +m4_define(INST, `break; case $1: +m4_ifelse(eval($2 > 0), `if (vstk.cnt < $2) runtime("Stack underflow");', `') +vstk.cnt -= $2; +') +m4_define(ARG, `if (v$1.type != $2) runtime("Argument $1 of instruction %s must be of type $2, got 0x%02x", f_instruction_name(what->fi_code), v$1.type)') + +m4_define(RESULT_OK, `vstk.cnt++') +m4_define(RESULT, `RESULT_VAL([[ (struct f_val) { .type = $1, .val.$2 = $3 } ]])') +m4_define(RESULT_VAL, `do { res = $1; RESULT_OK; } while (0)') + +m4_define(LINEX, `do { + estk.item[estk.cnt].pos = 0; + estk.item[estk.cnt].line = $1; + estk.item[estk.cnt].ventry = vstk.cnt; + estk.item[estk.cnt].emask = 0; + estk.cnt++; +} while (0)') + +m4_define(LINE, `do { + if (what->lines[$2]) { + estk.item[estk.cnt].pos = 0; + estk.item[estk.cnt].line = what->lines[$2]; + estk.item[estk.cnt].ventry = vstk.cnt; + estk.item[estk.cnt].emask = 0; + estk.cnt++; + } +} while (0)') + +m4_define(LINEP, LINE($1, $2)) + +m4_define(ARG_ANY, `') + +m4_define(SYMBOL, `const struct symbol *sym = what->sym') + +m4_define(VALI, `res = *what->vp') +m4_define(VALP, `res = *what->vp') +m4_define(FRET, `enum filter_return fret = what->fret') +m4_define(ECS, `enum ec_subtype ecs = what->ecs') +m4_define(RTC, `struct rtable *table = what->rtc->table') +m4_define(STATIC_ATTR, `struct f_static_attr sa = what->sa') +m4_define(DYNAMIC_ATTR, `struct f_dynamic_attr da = what->da') +m4_define(POSTFIXIFY, `') +m4_define(LINE_SIZE, `') +m4_define(SAME, `') +m4_define(COUNT, `') + +m4_m4wrap(` +m4_divert(0)DNL +case FI_NOP: bug("This shall not happen"); +m4_undivert(1) +break; default: bug( "Unknown instruction %d (%c)", what->fi_code, what->fi_code & 0xff); +') + +m4_divert(1) m4_changequote([[,]]) -m4_divert(0) + diff --git a/filter/line-size.m4 b/filter/line-size.m4 new file mode 100644 index 00000000..0c005fa1 --- /dev/null +++ b/filter/line-size.m4 @@ -0,0 +1,30 @@ +m4_divert(-1)m4_dnl +# +# BIRD -- Line size counting +# +# (c) 2018 Maria Matejka <mq@jmq.cz> +# +# Can be freely distributed and used under the terms of the GNU GPL. +# + +# Common aliases +m4_define(DNL, `m4_dnl') + +m4_define(INST, `m4_divert(1)break; case $1: cnt += 1; +m4_divert(-1)') +m4_define(ARG, `m4_divert(1)cnt += inst_line_size(what->a[$1-1].p); +m4_divert(-1)') +m4_define(ARG_T, `m4_divert(1)cnt += inst_line_size(what->a[$1-1].p); +m4_divert(-1)') +m4_define(ARG_ANY, `m4_divert(1)cnt += inst_line_size(what->a[$1-1].p); +m4_divert(-1)') +m4_define(LINE_SIZE, `m4_divert(1)$1m4_divert(-1)') + +m4_m4wrap(` +m4_divert(0)DNL +case FI_NOP: bug("This shall not happen"); +m4_undivert(1) +break; default: bug( "Unknown instruction %d (%c)", what->fi_code, what->fi_code & 0xff); +') + +m4_changequote([[,]]) diff --git a/filter/postfixify.m4 b/filter/postfixify.m4 new file mode 100644 index 00000000..8c96ba64 --- /dev/null +++ b/filter/postfixify.m4 @@ -0,0 +1,55 @@ +m4_divert(-1)m4_dnl +# +# BIRD -- Converting instructions trees to instruction lines +# +# (c) 2018 Maria Matejka <mq@jmq.cz> +# +# Can be freely distributed and used under the terms of the GNU GPL. +# + +# Common aliases +m4_define(DNL, `m4_dnl') + +m4_define(POSTFIXIFY_TRAILER, `dest->items[pos].fi_code = what->fi_code; +dest->items[pos].lineno = what->lineno;') + +m4_define(INST, `m4_divert(1)POSTFIXIFY_TRAILER +break; case $1: +m4_divert(-1)')) +m4_define(ARG, `m4_divert(1)pos = postfixify(dest, what->a[$1-1].p, pos); +m4_divert(-1)') +m4_define(ARG_ANY, `m4_divert(1)pos = postfixify(dest, what->a[$1-1].p, pos); +m4_divert(-1)') +m4_define(LINE, `m4_divert(1)dest->items[pos].lines[$2] = f_postfixify(what->a[$1-1].p); +m4_divert(-1)') +m4_define(LINEP, `m4_divert(1)dest->items[pos].lines[$2] = what->a[$1-1].p; +m4_divert(-1)') +m4_define(SYMBOL, `m4_divert(1)dest->items[pos].sym = what->a[$1-1].p; +m4_divert(-1)') +m4_define(VALI, `m4_divert(1)dest->items[pos].vp = &(what->val); +m4_divert(-1)') +m4_define(VALP, `m4_divert(1)dest->items[pos].vp = (dest->items[pos].sym = what->a[$1-1].p)->def; +m4_divert(-1)') +m4_define(FRET, `m4_divert(1)dest->items[pos].fret = what->a[$1-1].i; +m4_divert(-1)') +m4_define(ECS, `m4_divert(1)dest->items[pos].ecs = what->aux; +m4_divert(-1)') +m4_define(RTC, `m4_divert(1)dest->items[pos].rtc = what->a[$1-1].rtc; +m4_divert(-1)') +m4_define(STATIC_ATTR, `m4_divert(1)dest->items[pos].sa = what->sa; +m4_divert(-1)') +m4_define(DYNAMIC_ATTR, `m4_divert(1)dest->items[pos].da = what->da; +m4_divert(-1)') +m4_define(COUNT, `m4_divert(1)dest->items[pos].count = what->a[$1-1].i; +m4_divert(-1)') +m4_define(POSTFIXIFY, `m4_divert(1)$1m4_divert(-1)') + +m4_m4wrap(` +m4_divert(0)DNL +case FI_NOP: bug("This shall not happen"); +m4_undivert(1) +POSTFIXIFY_TRAILER +break; default: bug( "Unknown instruction %d (%c)", what->fi_code, what->fi_code & 0xff); +') + +m4_changequote([[,]]) diff --git a/filter/same.m4 b/filter/same.m4 new file mode 100644 index 00000000..73f2d1c3 --- /dev/null +++ b/filter/same.m4 @@ -0,0 +1,56 @@ +m4_divert(-1)m4_dnl +# +# BIRD -- Filter Comparator Generator +# +# (c) 2018 Maria Matejka <mq@jmq.cz> +# +# Can be freely distributed and used under the terms of the GNU GPL. +# + +# Common aliases +m4_define(DNL, `m4_dnl') + +m4_define(INST, `m4_divert(1)break; case $1: +m4_divert(-1)') + +m4_define(ARG, `') +m4_define(ARG_ANY, `') + +m4_define(LINE, `m4_divert(1)if (!f_same(f1->lines[$2], f2->lines[$2])) return 0; +m4_divert(-1)') +m4_define(LINEP, LINE) + +m4_define(SYMBOL, `m4_divert(1){ + const struct symbol *s1 = f1->sym, *s2 = f2->sym; + if (strcmp(s1->name, s2->name)) return 0; + if (s1->class != s2->class) return 0; +} +m4_divert(-1)') + +m4_define(VALI, `m4_divert(1)if (!val_same(f1->vp, f2->vp)) return 0; +m4_divert(-1)') +m4_define(VALP, `') + +m4_define(FRET, `m4_divert(1)if (f1->fret != f2->fret) return 0; +m4_divert(-1)') +m4_define(ECS, `m4_divert(1)if (f1->ecs != f2->ecs) return 0; +m4_divert(-1)') +m4_define(RTC, `m4_divert(1)if (strcmp(f1->rtc->name, f2->rtc->name)) return 0; +m4_divert(-1)') +m4_define(STATIC_ATTR, `m4_divert(1)if (f1->sa.sa_code != f2->sa.sa_code) return 0; +m4_divert(-1)') +m4_define(DYNAMIC_ATTR, `m4_divert(1)if (f1->da.ea_code != f2->da.ea_code) return 0; +m4_divert(-1)') + +m4_define(SAME, `m4_divert(1)$1m4_divert(-1)') + +m4_m4wrap(` +m4_divert(0)DNL +case FI_NOP: bug("This shall not happen"); +m4_undivert(1) +break; default: bug( "Unknown instruction %d (%c)", f1->fi_code, f1->fi_code & 0xff); +') + +m4_divert(1) +m4_changequote([[,]]) + diff --git a/filter/tree.c b/filter/tree.c index f8379fa8..80e1d395 100644 --- a/filter/tree.c +++ b/filter/tree.c @@ -23,15 +23,15 @@ * Both set matching and |switch() { }| construction is implemented using this function, * thus both are as fast as they can be. */ -struct f_tree * -find_tree(struct f_tree *t, struct f_val val) +const struct f_tree * +find_tree(const struct f_tree *t, const struct f_val *val) { if (!t) return NULL; - if ((val_compare(t->from, val) != 1) && - (val_compare(t->to, val) != -1)) + if ((val_compare(&(t->from), val) != 1) && + (val_compare(&(t->to), val) != -1)) return t; - if (val_compare(t->from, val) == -1) + if (val_compare(&(t->from), val) == -1) return find_tree(t->right, val); else return find_tree(t->left, val); @@ -56,7 +56,7 @@ build_tree_rec(struct f_tree **buf, int l, int h) static int tree_compare(const void *p1, const void *p2) { - return val_compare((* (struct f_tree **) p1)->from, (* (struct f_tree **) p2)->from); + return val_compare(&((* (struct f_tree **) p1)->from), &((* (struct f_tree **) p2)->from)); } /** @@ -119,39 +119,39 @@ f_new_tree(void) * Compares two trees and returns 1 if they are same */ int -same_tree(struct f_tree *t1, struct f_tree *t2) +same_tree(const struct f_tree *t1, const struct f_tree *t2) { if ((!!t1) != (!!t2)) return 0; if (!t1) return 1; - if (val_compare(t1->from, t2->from)) + if (val_compare(&(t1->from), &(t2->from))) return 0; - if (val_compare(t1->to, t2->to)) + if (val_compare(&(t1->to), &(t2->to))) return 0; if (!same_tree(t1->left, t2->left)) return 0; if (!same_tree(t1->right, t2->right)) return 0; - if (!i_same(t1->data, t2->data)) + if (!f_same(t1->data, t2->data)) return 0; return 1; } static void -tree_node_format(struct f_tree *t, buffer *buf) +tree_node_format(const struct f_tree *t, buffer *buf) { if (t == NULL) return; tree_node_format(t->left, buf); - val_format(t->from, buf); - if (val_compare(t->from, t->to) != 0) + val_format(&(t->from), buf); + if (val_compare(&(t->from), &(t->to)) != 0) { buffer_puts(buf, ".."); - val_format(t->to, buf); + val_format(&(t->to), buf); } buffer_puts(buf, ", "); @@ -159,7 +159,7 @@ tree_node_format(struct f_tree *t, buffer *buf) } void -tree_format(struct f_tree *t, buffer *buf) +tree_format(const struct f_tree *t, buffer *buf) { buffer_puts(buf, "["); diff --git a/filter/tree_test.c b/filter/tree_test.c index 5b22a9fe..f3e8ce49 100644 --- a/filter/tree_test.c +++ b/filter/tree_test.c @@ -226,8 +226,8 @@ t_find(void) }; for(looking_up_value.val.i = 0; looking_up_value.val.i < nodes_count; looking_up_value.val.i++) { - struct f_tree *found_tree = find_tree(tree, looking_up_value); - bt_assert((val_compare(looking_up_value, found_tree->from) == 0) && (val_compare(looking_up_value, found_tree->to) == 0)); + const struct f_tree *found_tree = find_tree(tree, &looking_up_value); + bt_assert((val_compare(&looking_up_value, &(found_tree->from)) == 0) && (val_compare(&looking_up_value, &(found_tree->to)) == 0)); } } @@ -278,11 +278,11 @@ t_find_ranges(void) for(*i = 0; *i <= max_value; *i += (uint)bt_random()/nodes_count) { - struct f_tree *found_tree = find_tree(tree, needle); + const struct f_tree *found_tree = find_tree(tree, &needle); bt_debug("searching: %u \n", *i); bt_assert( - (val_compare(needle, found_tree->from) == 0) || (val_compare(needle, found_tree->to) == 0) || - ((val_compare(needle, found_tree->from) == 1) && (val_compare(needle, found_tree->to) == -1)) + (val_compare(&needle, &(found_tree->from)) == 0) || (val_compare(&needle, &(found_tree->to)) == 0) || + ((val_compare(&needle, &(found_tree->from)) == 1) && (val_compare(&needle, &(found_tree->to)) == -1)) ); } } diff --git a/filter/trie.c b/filter/trie.c index adcfcdf3..a279e38c 100644 --- a/filter/trie.c +++ b/filter/trie.c @@ -220,7 +220,7 @@ trie_add_prefix(struct f_trie *t, const net_addr *net, uint l, uint h) } static int -trie_match_prefix(struct f_trie *t, ip_addr px, uint plen) +trie_match_prefix(const struct f_trie *t, ip_addr px, uint plen) { ip_addr pmask = ipa_mkmask(plen); ip_addr paddr = ipa_and(px, pmask); @@ -229,7 +229,7 @@ trie_match_prefix(struct f_trie *t, ip_addr px, uint plen) return t->zero; int plentest = plen - 1; - struct f_trie_node *n = t->root; + const struct f_trie_node *n = t->root; while(n) { @@ -264,7 +264,7 @@ trie_match_prefix(struct f_trie *t, ip_addr px, uint plen) * is such prefix pattern in the trie. */ int -trie_match_net(struct f_trie *t, const net_addr *n) +trie_match_net(const struct f_trie *t, const net_addr *n) { uint add = 0; @@ -279,7 +279,7 @@ trie_match_net(struct f_trie *t, const net_addr *n) } static int -trie_node_same(struct f_trie_node *t1, struct f_trie_node *t2) +trie_node_same(const struct f_trie_node *t1, const struct f_trie_node *t2) { if ((t1 == NULL) && (t2 == NULL)) return 1; @@ -303,13 +303,13 @@ trie_node_same(struct f_trie_node *t1, struct f_trie_node *t2) * Compares two tries and returns 1 if they are same */ int -trie_same(struct f_trie *t1, struct f_trie *t2) +trie_same(const struct f_trie *t1, const struct f_trie *t2) { return (t1->zero == t2->zero) && trie_node_same(t1->root, t2->root); } static void -trie_node_format(struct f_trie_node *t, buffer *buf) +trie_node_format(const struct f_trie_node *t, buffer *buf) { if (t == NULL) return; @@ -329,7 +329,7 @@ trie_node_format(struct f_trie_node *t, buffer *buf) * Prints the trie to the supplied buffer. */ void -trie_format(struct f_trie *t, buffer *buf) +trie_format(const struct f_trie *t, buffer *buf) { buffer_puts(buf, "["); |