From bd91338246c1ba40358243f1bdf5a6dbd3a29f35 Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Fri, 31 May 2019 17:33:41 +0200 Subject: [PATCH] Filter: Dropped the setter instructions in favor of direct result storage. This should help filter performance a bit. --- filter/config.Y | 59 +++++++---- filter/data.c | 1 - filter/data.h | 8 ++ filter/decl.m4 | 69 ++++++++++++- filter/f-inst.c | 232 +++---------------------------------------- filter/f-inst.h | 1 - filter/filter.c | 254 +++++++++++++++++++++++++++++++++++++++++++++--- 7 files changed, 364 insertions(+), 260 deletions(-) diff --git a/filter/config.Y b/filter/config.Y index 3898748cb..f57575fc8 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -21,9 +21,13 @@ static inline u32 pair(u32 a, u32 b) { return (a << 16) | b; } static inline u32 pair_a(u32 p) { return p >> 16; } static inline u32 pair_b(u32 p) { return p & 0xFFFF; } -#define f_generate_complex(fi_code, da, arg) \ - f_new_inst(FI_EA_SET, f_new_inst(fi_code, f_new_inst(FI_EA_GET, da), arg), da) - +#define f_generate_complex(fi_code, _da, arg) ({ \ + struct f_inst *fi = f_new_inst(fi_code, f_new_inst(FI_EA_GET, _da), arg); \ + fi->result.type = F_LVAL_EA; \ + fi->result.da = _da; \ + fi; \ +}) + /* * Sets and their items are during parsing handled as lists, linked * through left ptr. The first item in a list also contains a pointer @@ -184,7 +188,10 @@ f_generate_empty(struct f_dynamic_attr dyn) cf_error("Can't empty that attribute"); } - return f_new_inst(FI_EA_SET, f_new_inst(FI_CONSTANT, empty), dyn); + struct f_inst *fi = f_new_inst(FI_CONSTANT, empty); + fi->result.type = F_LVAL_EA; + fi->result.da = dyn; + return fi; } #if 0 @@ -397,31 +404,34 @@ assert_done(struct f_inst *expr, const char *start, const char *end) static struct f_inst * assert_assign(struct f_lval *lval, struct f_inst *expr, const char *start, const char *end) { - struct f_inst *setter, *getter, *checker; + struct f_inst *getter; switch (lval->type) { + case F_LVAL_STACK: + bug("This shall never happen"); + case F_LVAL_EXCEPTION: + bug("This shall never happen"); case F_LVAL_VARIABLE: - setter = f_new_inst(FI_VAR_SET, expr, lval->sym); getter = f_new_inst(FI_VAR_GET, lval->sym); break; case F_LVAL_PREFERENCE: - setter = f_new_inst(FI_PREF_SET, expr); getter = f_new_inst(FI_PREF_GET); break; case F_LVAL_SA: - setter = f_new_inst(FI_RTA_SET, expr, lval->sa); getter = f_new_inst(FI_RTA_GET, lval->sa); break; case F_LVAL_EA: - setter = f_new_inst(FI_EA_SET, expr, lval->da); getter = f_new_inst(FI_EA_GET, lval->da); break; - default: - bug("Unknown lval type"); } - checker = f_new_inst(FI_EQ, expr, getter); + struct f_inst *checker = f_new_inst(FI_EQ, expr, getter); + + struct f_inst *setter = cfg_alloc(sizeof(struct f_inst)); + *setter = *expr; + setter->next = checker; - + setter->result = *lval; + return assert_done(setter, start, end); } @@ -985,29 +995,40 @@ cmd: | CF_SYM_KNOWN '=' term ';' { switch ($1->class) { case SYM_VARIABLE_RANGE: - $$ = f_new_inst(FI_VAR_SET, $3, $1); + $3->result.type = F_LVAL_VARIABLE; + $3->result.sym = $1; break; case SYM_ATTRIBUTE: - $$ = f_new_inst(FI_EA_SET, $3, *$1->attribute); + $3->result.type = F_LVAL_EA; + $3->result.da = *$1->attribute; break; default: cf_error("Can't assign to symbol %s", $1->name); } + $$ = $3; } | RETURN term ';' { DBG( "Ook, we'll return the value\n" ); - $$ = f_new_inst(FI_RETURN, $2); + $2->result.type = F_LVAL_EXCEPTION; + $2->result.exception = FE_RETURN; + $$ = $2; } | dynamic_attr '=' term ';' { - $$ = f_new_inst(FI_EA_SET, $3, $1); + $3->result.type = F_LVAL_EA; + $3->result.da = $1; + $$ = $3; } | static_attr '=' term ';' { if ($1.readonly) cf_error( "This static attribute is read-only."); - $$ = f_new_inst(FI_RTA_SET, $3, $1); + + $3->result.type = F_LVAL_SA; + $3->result.sa = $1; + $$ = $3; } | PREFERENCE '=' term ';' { - $$ = f_new_inst(FI_PREF_SET, $3); + $3->result.type = F_LVAL_PREFERENCE; + $$ = $3; } | UNSET '(' dynamic_attr ')' ';' { $$ = f_new_inst(FI_EA_UNSET, $3); diff --git a/filter/data.c b/filter/data.c index 912e2b00a..cb759a595 100644 --- a/filter/data.c +++ b/filter/data.c @@ -525,4 +525,3 @@ val_dump(const struct f_val *v) { val_format(v, &b); return val_dump_buffer; } - diff --git a/filter/data.h b/filter/data.h index 6973008ff..3718cea4e 100644 --- a/filter/data.h +++ b/filter/data.h @@ -107,8 +107,15 @@ struct f_static_attr { int readonly:1; /* Don't allow writing */ }; +/* Exception bits */ +enum f_exception { + FE_RETURN = 0x1, +}; + /* Filter l-value type */ enum f_lval_type { + F_LVAL_STACK = 0, + F_LVAL_EXCEPTION, F_LVAL_VARIABLE, F_LVAL_PREFERENCE, F_LVAL_SA, @@ -119,6 +126,7 @@ enum f_lval_type { struct f_lval { enum f_lval_type type; union { + enum f_exception exception; const struct symbol *sym; struct f_dynamic_attr da; struct f_static_attr sa; diff --git a/filter/decl.m4 b/filter/decl.m4 index 9934d0bac..a1d625f3b 100644 --- a/filter/decl.m4 +++ b/filter/decl.m4 @@ -119,8 +119,6 @@ case INST_NAME(): { m4_undivert(105) #undef what #undef item - dest->items[pos].fi_code = what_->fi_code; - dest->items[pos].lineno = what_->lineno; break; } m4_undefine([[FID_LINEARIZE_BODY_EXISTS]]) @@ -236,9 +234,29 @@ do { if (whati->fl$1) { } } while(0)m4_dnl FID_ALL()') -m4_define(RESULT_OK, `FID_INTERPRET_BODY()fstk->vcnt++FID_ALL()') -m4_define(RESULT, `RESULT_VAL([[ (struct f_val) { .type = $1, .val.$2 = $3 } ]])') -m4_define(RESULT_VAL, `FID_INTERPRET_BODY()do { res = $1; RESULT_OK; } while (0)FID_ALL()') +m4_define(RESULT_PTR, ` +FID_INTERPRET_BODY + do { + enum filter_return fret = f_lval_set(fs, &(what->result), $1); + if (fret != F_NOP) return fret; + } while (0)m4_dnl +FID_ALL()') + +m4_define(RESULT, ` +FID_INTERPRET_BODY + do { + struct f_val res_ = { .type = $1, .val.$2 = $3 }; + RESULT_PTR(&res_); + } while (0)m4_dnl +FID_ALL()') + +m4_define(RESULT_VOID, ` +FID_INTERPRET_BODY + do { + struct f_val res_ = { .type = T_VOID }; + RESULT_PTR(&res_); + } while (0)m4_dnl +FID_ALL()') m4_define(SYMBOL, `FID_MEMBER(const struct symbol *, sym, sym, [[strcmp(f1->sym->name, f2->sym->name) || (f1->sym->class != f2->sym->class)]], symbol %s, item->sym->name, const struct symbol *sym = whati->sym)') @@ -302,6 +320,16 @@ void f_dump_line(const struct f_line *dest, uint indent) for (uint i=0; ilen; i++) { const struct f_line_item *item = &dest->items[i]; debug("%sInstruction %s at line %u\n", INDENT, f_instruction_name(item->fi_code), item->lineno); + + switch (item->result.type) { + case F_LVAL_STACK: debug("%son stack\n", INDENT); break; + case F_LVAL_EXCEPTION: debug("%s=>exception 0x%x\n", INDENT, item->result.exception); break; + case F_LVAL_VARIABLE: debug("%s=>%s\n", INDENT, item->result.sym->name); break; + case F_LVAL_PREFERENCE: debug("%s=>preference\n", INDENT); break; + case F_LVAL_SA: debug("%s=>sa\n", INDENT); break; + case F_LVAL_EA: debug("%s=>ea\n", INDENT); break; + } + switch (item->fi_code) { FID_WR_PUT(7) default: bug("Unknown instruction %x in f_dump_line", item->fi_code); @@ -318,6 +346,9 @@ linearize(struct f_line *dest, const struct f_inst *what_, uint pos) switch (what_->fi_code) { FID_WR_PUT(8) } + dest->items[pos].fi_code = what_->fi_code; + dest->items[pos].lineno = what_->lineno; + dest->items[pos].result = what_->result; pos++; } return pos; @@ -360,6 +391,32 @@ f_same(const struct f_line *fl1, const struct f_line *fl2) if (f1_->flags != f2_->flags) return 0; + if (f1_->result.type != f2_->result.type) return 0; + switch (f1_->result.type) { + case F_LVAL_STACK: + break; + case F_LVAL_EXCEPTION: + if (f1_->result.exception != f2_->result.exception) + return 0; + break; + case F_LVAL_VARIABLE: + if (strcmp(f1_->result.sym->name, f2_->result.sym->name)) + return 0; + if (f1_->result.sym->class != f2_->result.sym->class) + return 0; + break; + case F_LVAL_PREFERENCE: + break; + case F_LVAL_SA: + if (f1_->result.sa.sa_code != f2_->result.sa.sa_code) + return 0; + break; + case F_LVAL_EA: + if (f1_->result.da.ea_code != f2_->result.da.ea_code) + return 0; + break; + } + switch(f1_->fi_code) { FID_WR_PUT(9) } @@ -382,6 +439,7 @@ struct f_inst { enum f_instruction_code fi_code; /* Instruction code */ int size; /* How many instructions are underneath */ int lineno; /* Line number */ + struct f_lval result; /* Destination */ union { FID_WR_PUT(1) }; @@ -392,6 +450,7 @@ struct f_line_item { enum f_instruction_code fi_code; /* What to do */ enum f_instruction_flags flags; /* Flags, instruction-specific */ uint lineno; /* Where */ + struct f_lval result; /* Destination */ union { FID_WR_PUT(2) }; diff --git a/filter/f-inst.c b/filter/f-inst.c index 749e072ce..3be76d11e 100644 --- a/filter/f-inst.c +++ b/filter/f-inst.c @@ -36,7 +36,7 @@ * m4_dnl ACCESS_RTE; this instruction needs route * m4_dnl ACCESS_EATTRS; this instruction needs extended attributes * m4_dnl RESULT(type, union-field, value); putting this on value stack - * m4_dnl RESULT_OK; legalize what already is on the value stack + * m4_dnl RESULT_PTR(vptr); put what is pointed-to on the value stack * m4_dnl } * * Other code is just copied into the interpreter part. @@ -50,41 +50,37 @@ INST(FI_ADD, 2, 1) { ARG(1,T_INT); ARG(2,T_INT); - res.val.i += v2.val.i; - RESULT_OK; + RESULT(T_INT, i, v1.val.i + v2.val.i); } INST(FI_SUBTRACT, 2, 1) { ARG(1,T_INT); ARG(2,T_INT); - res.val.i -= v2.val.i; - RESULT_OK; + RESULT(T_INT, i, v1.val.i - v2.val.i); } INST(FI_MULTIPLY, 2, 1) { ARG(1,T_INT); ARG(2,T_INT); - res.val.i *= v2.val.i; - RESULT_OK; + RESULT(T_INT, i, v1.val.i * v2.val.i); } 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; + RESULT(T_INT, i, v1.val.i / v2.val.i); } INST(FI_AND, 1, 1) { ARG(1,T_BOOL); - if (res.val.i) + if (v1.val.i) LINE(2,0); else - RESULT_OK; + RESULT_PTR(&(v1)); } INST(FI_OR, 1, 1) { ARG(1,T_BOOL); - if (!res.val.i) + if (!v1.val.i) LINE(2,0); else - RESULT_OK; + RESULT_PTR(&(v1)); } INST(FI_PAIR_CONSTRUCT, 2, 1) { ARG(1,T_INT); @@ -281,8 +277,7 @@ INST(FI_VAR_GET, 0, 1) { SYMBOL(1); - res = fstk->vstk[curline.vbase + sym->offset]; - RESULT_OK; + RESULT_PTR(&(fstk->vstk[curline.vbase + sym->offset])); } /* some constants have value in a[1], some in *a[0].p, strange. */ @@ -303,8 +298,7 @@ debug("%svalue %s\n", INDENT, val_dump(&item->val)); FID_ALL - res = whati->val; - RESULT_OK; + RESULT_PTR(&(whati->val)); } INST(FI_CONSTANT_DEFINED, 0, 1) { FID_STRUCT_IN @@ -324,8 +318,7 @@ debug("%sconstant %s with value %s\n", INDENT, item->sym->name, val_dump(item->valp)); FID_ALL - res = *whati->valp; - RESULT_OK; + RESULT_PTR(whati->valp); } INST(FI_PRINT, 1, 0) { ARG_ANY(1); @@ -398,76 +391,6 @@ } } - INST(FI_RTA_SET, 1, 0) { - ACCESS_RTE; - ARG_ANY(1); - STATIC_ATTR; - 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 (sa.sa_code) - { - case SA_FROM: - rta->from = v1.val.ip; - break; - - case SA_GW: - { - ip_addr ip = v1.val.ip; - neighbor *n = neigh_find(rta->src->proto, ip, NULL, 0); - if (!n || (n->scope == SCOPE_HOST)) - runtime( "Invalid gw address" ); - - rta->dest = RTD_UNICAST; - rta->nh.gw = ip; - rta->nh.iface = n->iface; - rta->nh.next = NULL; - rta->hostentry = NULL; - } - break; - - case SA_SCOPE: - rta->scope = v1.val.i; - break; - - case SA_DEST: - { - 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: - { - struct iface *ifa = if_find_by_name(v1.val.s); - if (!ifa) - runtime( "Invalid iface name" ); - - rta->dest = RTD_UNICAST; - rta->nh.gw = IPA_NONE; - rta->nh.iface = ifa; - rta->nh.next = NULL; - rta->hostentry = NULL; - } - break; - - default: - bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code); - } - } - } - INST(FI_EA_GET, 0, 1) { /* Access to extended attributes */ DYNAMIC_ATTR; ACCESS_RTE; @@ -501,8 +424,7 @@ } /* Undefined value */ - res.type = T_VOID; - RESULT_OK; + RESULT_VOID; break; } @@ -535,8 +457,7 @@ RESULT(T_LCLIST, ad, e->u.ptr); break; case EAF_TYPE_UNDEF: - res.type = T_VOID; - RESULT_OK; + RESULT_VOID; break; default: bug("Unknown dynamic attribute type"); @@ -544,95 +465,6 @@ } } - INST(FI_EA_SET, 1, 0) { - ACCESS_RTE; - ACCESS_EATTRS; - ARG_ANY(1); - DYNAMIC_ATTR; - { - struct ea_list *l = lp_alloc(fs->pool, sizeof(struct ea_list) + sizeof(eattr)); - - l->next = NULL; - l->flags = EALF_SORTED; - l->count = 1; - l->attrs[0].id = da.ea_code; - l->attrs[0].flags = 0; - l->attrs[0].type = da.type | EAF_ORIGINATED | EAF_FRESH; - - switch (da.type) { - case EAF_TYPE_INT: - 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)) { - l->attrs[0].u.data = ipa_to_u32(v1.val.ip); - break; - } - /* T_INT for backward compatibility */ - if ((v1.type != T_QUAD) && (v1.type != T_INT)) - runtime( "Setting quad attribute to non-quad value" ); - l->attrs[0].u.data = v1.val.i; - break; - - case EAF_TYPE_OPAQUE: - runtime( "Setting opaque attribute is not allowed" ); - break; - case EAF_TYPE_IP_ADDRESS: - if (v1.type != T_IP) - runtime( "Setting ip attribute to non-ip value" ); - int len = sizeof(ip_addr); - struct adata *ad = lp_alloc(fs->pool, sizeof(struct adata) + len); - ad->length = len; - (* (ip_addr *) ad->data) = v1.val.ip; - l->attrs[0].u.ptr = ad; - break; - case EAF_TYPE_AS_PATH: - if (v1.type != T_PATH) - runtime( "Setting path attribute to non-path value" ); - l->attrs[0].u.ptr = v1.val.ad; - break; - case EAF_TYPE_BITFIELD: - if (v1.type != T_BOOL) - runtime( "Setting bit in bitfield attribute to non-bool value" ); - { - /* First, we have to find the old value */ - 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 | (1u << da.bit); - else - l->attrs[0].u.data = data & ~(1u << da.bit); - } - break; - case EAF_TYPE_INT_SET: - if (v1.type != T_CLIST) - runtime( "Setting clist attribute to non-clist value" ); - l->attrs[0].u.ptr = v1.val.ad; - break; - case EAF_TYPE_EC_SET: - if (v1.type != T_ECLIST) - runtime( "Setting eclist attribute to non-eclist value" ); - l->attrs[0].u.ptr = v1.val.ad; - break; - case EAF_TYPE_LC_SET: - if (v1.type != T_LCLIST) - runtime( "Setting lclist attribute to non-lclist value" ); - l->attrs[0].u.ptr = v1.val.ad; - break; - default: bug("Unknown type in e,S"); - } - - f_rta_cow(fs); - l->next = *fs->eattrs; - *fs->eattrs = l; - } - } - INST(FI_EA_UNSET, 0, 0) { DYNAMIC_ATTR; ACCESS_RTE; @@ -660,15 +492,6 @@ RESULT(T_INT, i, (*fs->rte)->pref); } - INST(FI_PREF_SET, 1, 0) { - ACCESS_RTE; - ARG(1,T_INT); - if (v1.val.i > 0xFFFF) - runtime( "Setting preference value out of bounds" ); - f_rte_cow(fs); - (*fs->rte)->pref = v1.val.i; - } - INST(FI_LENGTH, 1, 1) { /* Get length of */ ARG_ANY(1); switch(v1.type) { @@ -744,33 +567,6 @@ RESULT(T_INT, i, as_path_get_last_nonaggregated(v1.val.ad)); } - INST(FI_RETURN, 1, 1) { - /* Acquire the return value */ - ARG_ANY(1); - uint retpos = fstk->vcnt; - - /* Drop every sub-block including ourselves */ - while ((fstk->ecnt-- > 0) && !(fstk->estk[fstk->ecnt].emask & FE_RETURN)) - ; - - /* Now we are at the caller frame; if no such, try to convert to accept/reject. */ - if (!fstk->ecnt) - if (fstk->vstk[retpos].type == T_BOOL) - if (fstk->vstk[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, overwriting the former implicit void */ - fstk->vcnt = fstk->estk[fstk->ecnt].ventry - 1; - - /* Copy the return value */ - RESULT_VAL(fstk->vstk[retpos]); - } - INST(FI_CALL, 0, 1) { SYMBOL; diff --git a/filter/f-inst.h b/filter/f-inst.h index f0dcec649..ca685ded8 100644 --- a/filter/f-inst.h +++ b/filter/f-inst.h @@ -84,7 +84,6 @@ static inline struct f_dynamic_attr f_new_dynamic_attr(u8 type, u8 bit, enum f_t { 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_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); /* Hook for call bt_assert() function in configuration */ diff --git a/filter/filter.c b/filter/filter.c index 9b61c7075..98df7bd09 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -51,12 +51,6 @@ #include "filter/data.h" -/* Exception bits */ -enum f_exception { - FE_RETURN = 0x1, -}; - - struct filter_stack { /* Value stack for execution */ #define F_VAL_STACK_MAX 4096 @@ -151,7 +145,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, const struct f_val *v) { buffer b; LOG_BUFFER_INIT(b); val_format(v, &b); @@ -160,6 +154,233 @@ val_format_str(struct filter_state *fs, struct f_val *v) { static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS; +#define runtime(fmt, ...) do { \ + if (!(fs->flags & FF_SILENT)) \ + log_rl(&rl_runtime_err, L_ERR "filters, line %d: " fmt, \ + (fs->stack->estk[fs->stack->ecnt-1].line->items[fs->stack->estk[fs->stack->ecnt-1].pos-1]).lineno, \ + ##__VA_ARGS__); \ + return F_ERROR; \ +} 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) + +static inline enum filter_return +f_rta_set(struct filter_state *fs, struct f_static_attr sa, const struct f_val *val) +{ + ACCESS_RTE; + if (sa.f_type != val->type) + runtime( "Attempt to set static attribute to incompatible type" ); + + f_rta_cow(fs); + { + struct rta *rta = (*fs->rte)->attrs; + + switch (sa.sa_code) + { + case SA_FROM: + rta->from = val->val.ip; + return F_NOP; + + case SA_GW: + { + ip_addr ip = val->val.ip; + neighbor *n = neigh_find(rta->src->proto, ip, NULL, 0); + if (!n || (n->scope == SCOPE_HOST)) + runtime( "Invalid gw address" ); + + rta->dest = RTD_UNICAST; + rta->nh.gw = ip; + rta->nh.iface = n->iface; + rta->nh.next = NULL; + rta->hostentry = NULL; + } + return F_NOP; + + case SA_SCOPE: + rta->scope = val->val.i; + return F_NOP; + + case SA_DEST: + { + int i = val->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; + } + return F_NOP; + + case SA_IFNAME: + { + struct iface *ifa = if_find_by_name(val->val.s); + if (!ifa) + runtime( "Invalid iface name" ); + + rta->dest = RTD_UNICAST; + rta->nh.gw = IPA_NONE; + rta->nh.iface = ifa; + rta->nh.next = NULL; + rta->hostentry = NULL; + } + return F_NOP; + + default: + bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code); + } + } +} + +static inline enum filter_return +f_ea_set(struct filter_state *fs, struct f_dynamic_attr da, const struct f_val *val) +{ + ACCESS_RTE; + ACCESS_EATTRS; + { + struct ea_list *l = lp_alloc(fs->pool, sizeof(struct ea_list) + sizeof(eattr)); + + l->next = NULL; + l->flags = EALF_SORTED; + l->count = 1; + l->attrs[0].id = da.ea_code; + l->attrs[0].flags = 0; + l->attrs[0].type = da.type | EAF_ORIGINATED | EAF_FRESH; + + switch (da.type) { + case EAF_TYPE_INT: + if (val->type != da.f_type) + runtime( "Setting int attribute to non-int value" ); + l->attrs[0].u.data = val->val.i; + break; + + case EAF_TYPE_ROUTER_ID: + /* IP->Quad implicit conversion */ + if (val_is_ip4(val)) { + l->attrs[0].u.data = ipa_to_u32(val->val.ip); + break; + } + /* T_INT for backward compatibility */ + if ((val->type != T_QUAD) && (val->type != T_INT)) + runtime( "Setting quad attribute to non-quad value" ); + l->attrs[0].u.data = val->val.i; + break; + + case EAF_TYPE_OPAQUE: + runtime( "Setting opaque attribute is not allowed" ); + break; + case EAF_TYPE_IP_ADDRESS: + if (val->type != T_IP) + runtime( "Setting ip attribute to non-ip value" ); + int len = sizeof(ip_addr); + struct adata *ad = lp_alloc(fs->pool, sizeof(struct adata) + len); + ad->length = len; + (* (ip_addr *) ad->data) = val->val.ip; + l->attrs[0].u.ptr = ad; + break; + case EAF_TYPE_AS_PATH: + if (val->type != T_PATH) + runtime( "Setting path attribute to non-path value" ); + l->attrs[0].u.ptr = val->val.ad; + break; + case EAF_TYPE_BITFIELD: + if (val->type != T_BOOL) + runtime( "Setting bit in bitfield attribute to non-bool value" ); + { + /* First, we have to find the old value */ + eattr *e = ea_find(*fs->eattrs, da.ea_code); + u32 data = e ? e->u.data : 0; + + if (val->val.i) + l->attrs[0].u.data = data | (1u << da.bit); + else + l->attrs[0].u.data = data & ~(1u << da.bit); + } + break; + case EAF_TYPE_INT_SET: + if (val->type != T_CLIST) + runtime( "Setting clist attribute to non-clist value" ); + l->attrs[0].u.ptr = val->val.ad; + break; + case EAF_TYPE_EC_SET: + if (val->type != T_ECLIST) + runtime( "Setting eclist attribute to non-eclist value" ); + l->attrs[0].u.ptr = val->val.ad; + break; + case EAF_TYPE_LC_SET: + if (val->type != T_LCLIST) + runtime( "Setting lclist attribute to non-lclist value" ); + l->attrs[0].u.ptr = val->val.ad; + break; + default: bug("Unknown type in e,S"); + } + + f_rta_cow(fs); + l->next = *fs->eattrs; + *fs->eattrs = l; + + return F_NOP; + } +} + +static inline enum filter_return +f_lval_set(struct filter_state *fs, const struct f_lval *lv, const struct f_val *val) +{ + switch (lv->type) { + case F_LVAL_STACK: + fs->stack->vstk[fs->stack->vcnt] = *val; + fs->stack->vcnt++; + return F_NOP; + case F_LVAL_EXCEPTION: + { + /* Drop every sub-block including ourselves */ + while ((fs->stack->ecnt-- > 0) && !(fs->stack->estk[fs->stack->ecnt].emask & lv->exception)) + ; + + /* Now we are at the catch frame; if no such, try to convert to accept/reject. */ + if (!fs->stack->ecnt) + if (lv->exception == FE_RETURN) + if (val->type == T_BOOL) + if (val->val.i) + return F_ACCEPT; + else + return F_REJECT; + else + runtime("Can't return non-bool from non-function"); + else + runtime("Unhandled exception 0x%x: %s", lv->exception, val_format_str(fs, val)); + + /* Set the value stack position, overwriting the former implicit void */ + fs->stack->vcnt = fs->stack->estk[fs->stack->ecnt].ventry; + + /* Copy the return value */ + fs->stack->vstk[fs->stack->vcnt - 1] = *val; + return F_NOP; + } + case F_LVAL_VARIABLE: + fs->stack->vstk[fs->stack->estk[fs->stack->ecnt-1].vbase + lv->sym->offset] = *val; + return F_NOP; + case F_LVAL_PREFERENCE: + ACCESS_RTE; + if (val->type != T_INT) + runtime("Preference must be integer, got 0x%02x", val->type); + if (val->val.i > 0xFFFF) + runtime("Preference is at most 65536"); + f_rte_cow(fs); + (*fs->rte)->pref = val->val.i; + return F_NOP; + case F_LVAL_SA: + return f_rta_set(fs, lv->sa, val); + case F_LVAL_EA: + return f_ea_set(fs, lv->da, val); + default: + bug("This shall never happen"); + } +} + /** * interpret * @fs: filter state @@ -209,15 +430,6 @@ interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val) #define v2 fstk->vstk[fstk->vcnt + 1] #define v3 fstk->vstk[fstk->vcnt + 2] -#define runtime(fmt, ...) do { \ - if (!(fs->flags & FF_SILENT)) \ - log_rl(&rl_runtime_err, L_ERR "filters, line %d: " fmt, what->lineno, ##__VA_ARGS__); \ - return F_ERROR; \ -} 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) - #include "filter/inst-interpret.c" #undef res #undef v1 @@ -233,6 +445,16 @@ interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val) fstk->vcnt -= curline.line->vars; fstk->vcnt -= curline.line->args; fstk->ecnt--; + + /* If the caller wants to store the result somewhere, do it. */ + if (fstk->ecnt) { + const struct f_line_item *caller = &(curline.line->items[curline.pos-1]); + if (caller->result.type != F_LVAL_STACK) { + enum filter_return fret = f_lval_set(fs, &(caller->result), &fstk->vstk[--fstk->vcnt]); + if (fret != F_NOP) + return fret; + } + } } if (fstk->vcnt == 0) { -- 2.39.2