diff --git a/conf/confbase.Y b/conf/confbase.Y index fd94047f..31f19941 100644 --- a/conf/confbase.Y +++ b/conf/confbase.Y @@ -444,7 +444,7 @@ bytestring_text: ; bytestring_expr: - symbol_value + lvalue { $$ = f_lval_getter(&$1); } | term_bs | '(' term ')' { $$ = $2; } ; diff --git a/filter/config.Y b/filter/config.Y index f27d9f58..c2bf6989 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -40,16 +40,19 @@ static inline void f_method_call_start(struct f_inst *object) cf_error("Too many nested method calls"); struct sym_scope *scope = f_type_method_scope(object->type); - if (!scope) + if (!scope && object->type != T_ROUTE) cf_error("No methods defined for type %s", f_type_name(object->type)); + if (!scope) + scope = config->root_scope->next; + /* Replacing the current symbol scope with the appropriate method scope for the given type. */ FM = (struct f_method_scope) { .object = object, .main = new_config->current_scope, .scope = { - .next = NULL, + .next = scope->next, .hash = scope->hash, .block = 1, .readonly = 1, @@ -241,6 +244,7 @@ f_new_lc_item(u32 f1, u32 t1, u32 f2, u32 t2, u32 f3, u32 t3) return t; } + /* * Remove all new lines and doubled whitespaces * and convert all tabulators to spaces @@ -299,13 +303,14 @@ static struct f_inst * f_lval_getter(struct f_lval *lval) { switch (lval->type) { + case F_LVAL_CONSTANT: return f_new_inst(FI_CONSTANT, *(lval->sym->val)); case F_LVAL_VARIABLE: return f_new_inst(FI_VAR_GET, lval->sym); - case F_LVAL_SA: return f_new_inst(FI_RTA_GET, lval->sa); - case F_LVAL_EA: return f_new_inst(FI_EA_GET, lval->da); + case F_LVAL_SA: return f_new_inst(FI_RTA_GET, lval->rte, lval->sa); + case F_LVAL_EA: return f_new_inst(FI_EA_GET, lval->rte, lval->da); case F_LVAL_ATTR_BIT: { struct f_inst *c = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = (1U << lval->fab.bit)}); - return f_new_inst(FI_EQ, c, f_new_inst(FI_BITAND, f_new_inst(FI_EA_GET, lval->fab.class), c)); + return f_new_inst(FI_EQ, c, f_new_inst(FI_BITAND, f_new_inst(FI_EA_GET, lval->rte, lval->fab.class), c)); } default: bug("Unknown lval type"); } @@ -315,20 +320,25 @@ static struct f_inst * f_lval_setter(struct f_lval *lval, struct f_inst *expr) { switch (lval->type) { + case F_LVAL_CONSTANT: cf_error("Constant %s is read-only", lval->sym->name); case F_LVAL_VARIABLE: return f_new_inst(FI_VAR_SET, expr, lval->sym); - case F_LVAL_SA: return f_new_inst(FI_RTA_SET, expr, lval->sa); + case F_LVAL_SA: + if (lval->sa.readonly) + cf_error( "This static attribute is read-only."); + return f_new_inst(FI_RTA_SET, expr, lval->sa); + case F_LVAL_EA: return f_new_inst(FI_EA_SET, expr, lval->da); case F_LVAL_ATTR_BIT: return f_new_inst(FI_CONDITION, expr, f_new_inst(FI_EA_SET, f_new_inst(FI_BITOR, f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = (1U << lval->fab.bit)}), - f_new_inst(FI_EA_GET, lval->fab.class) + f_new_inst(FI_EA_GET, lval->rte, lval->fab.class) ), lval->fab.class), f_new_inst(FI_EA_SET, f_new_inst(FI_BITAND, f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = ~(1U << lval->fab.bit)}), - f_new_inst(FI_EA_GET, lval->fab.class) + f_new_inst(FI_EA_GET, lval->rte, lval->fab.class) ), lval->fab.class) ); @@ -371,7 +381,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, %nonassoc ELSE %type cmds_int cmd_prep -%type term term_bs cmd cmd_var cmds cmds_scoped constant constructor var var_list var_list_r function_call symbol_value bgp_path_expr bgp_path bgp_path_tail term_dot_method method_name_cont +%type term term_bs cmd cmd_var cmds cmds_scoped constant constructor var var_list var_list_r function_call bgp_path_expr bgp_path bgp_path_tail term_dot_method method_name_cont %type static_attr %type filter where_filter %type filter_body function_body @@ -479,6 +489,7 @@ type: | CLIST { $$ = T_CLIST; } | ECLIST { $$ = T_ECLIST; } | LCLIST { $$ = T_LCLIST; } + | ROUTE { $$ = T_ROUTE; } | type SET { switch ($1) { case T_INT: @@ -779,7 +790,7 @@ switch_body: /* EMPTY */ { $$ = NULL; } ; bgp_path_expr: - symbol_value { $$ = $1; } + lvalue { $$ = f_lval_getter(&$1); } | '(' term ')' { $$ = $2; } ; @@ -861,23 +872,6 @@ function_call: } ; -symbol_value: CF_SYM_KNOWN - { - switch ($1->class) { - case SYM_CONSTANT_RANGE: - $$ = f_new_inst(FI_CONSTANT, *($1->val)); - break; - case SYM_VARIABLE_RANGE: - $$ = f_new_inst(FI_VAR_GET, $1); - break; - case SYM_ATTRIBUTE: - $$ = f_new_inst(FI_EA_GET, $1->attribute); - break; - default: - cf_error("Can't get value of symbol %s", $1->name); - } - } - ; static_attr: GW { $$ = f_new_static_attr(T_IP, SA_GW, 0); } @@ -900,6 +894,18 @@ method_name_cont: } '(' var_list ')' { $$ = f_dispatch_method($1, FM.object, $4, 1); } + | static_attr { + if (FM.object->type != T_ROUTE) + cf_error("Getting a route attribute from %s, need a route", f_type_name(FM.object->type)); + $$ = f_new_inst(FI_RTA_GET, FM.object, $1); + } + | CF_SYM_KNOWN { + if ($1->class != SYM_ATTRIBUTE) + cf_error("Not a method of %s: %s", f_type_name(FM.object->type), $1->name); + if (FM.object->type != T_ROUTE) + cf_error("Getting a route attribute from %s, need a route", f_type_name(FM.object->type)); + $$ = f_new_inst(FI_EA_GET, FM.object, $1->attribute); + } ; term: @@ -923,11 +929,10 @@ term: | '!' term { $$ = f_new_inst(FI_NOT, $2); } | DEFINED '(' term ')' { $$ = f_new_inst(FI_DEFINED, $3); } - | symbol_value { $$ = $1; } | constant { $$ = $1; } | constructor { $$ = $1; } - | static_attr { $$ = f_new_inst(FI_RTA_GET, $1); } + | lvalue { $$ = f_lval_getter(&$1); } | term_dot_method @@ -1017,19 +1022,8 @@ cmd: cf_pop_block_scope(new_config); $$ = f_for_cycle($3, $6, $9); } - | CF_SYM_KNOWN '=' term ';' { - switch ($1->class) { - case SYM_VARIABLE_RANGE: - $$ = f_new_inst(FI_VAR_SET, $3, $1); - break; - case SYM_ATTRIBUTE: - if ($1->attribute->readonly) - cf_error("Attribute %s is read-only", $1->attribute->name); - $$ = f_new_inst(FI_EA_SET, $3, $1->attribute); - break; - default: - cf_error("Can't assign to symbol %s", $1->name); - } + | lvalue '=' term ';' { + $$ = f_lval_setter(&$1, $3); } | RETURN term ';' { DBG( "Ook, we'll return the value\n" ); @@ -1047,11 +1041,6 @@ cmd: $$ = f_new_inst(FI_RETURN, $2); } - | static_attr '=' term ';' { - if ($1.readonly) - cf_error( "This static attribute is read-only."); - $$ = f_new_inst(FI_RTA_SET, $3, $1); - } | UNSET '(' CF_SYM_KNOWN ')' ';' { if ($3->class != SYM_ATTRIBUTE) cf_error("Can't unset %s", $3->name); @@ -1091,17 +1080,20 @@ lvalue: CF_SYM_KNOWN { switch ($1->class) { + case SYM_CONSTANT_RANGE: + $$ = (struct f_lval) { .type = F_LVAL_CONSTANT, .sym = $1, }; + break; case SYM_VARIABLE_RANGE: - $$ = (struct f_lval) { .type = F_LVAL_VARIABLE, .sym = $1 }; + $$ = (struct f_lval) { .type = F_LVAL_VARIABLE, .sym = $1, }; break; case SYM_ATTRIBUTE: - $$ = (struct f_lval) { .type = F_LVAL_EA, .da = $1->attribute }; + $$ = (struct f_lval) { .type = F_LVAL_EA, .da = $1->attribute, .rte = f_new_inst(FI_CURRENT_ROUTE), }; break; default: cf_error("Variable name or attribute name required"); } } - | static_attr { $$ = (struct f_lval) { .type = F_LVAL_SA, .sa = $1 }; } + | static_attr { $$ = (struct f_lval) { .type = F_LVAL_SA, .sa = $1, .rte = f_new_inst(FI_CURRENT_ROUTE), }; } ; CF_END diff --git a/filter/data.c b/filter/data.c index d3ed4a62..c55cc7b0 100644 --- a/filter/data.c +++ b/filter/data.c @@ -60,6 +60,8 @@ static const char * const f_type_str[] = { [T_LCLIST] = "lclist", [T_RD] = "rd", + [T_ROUTE] = "route", + [T_SET] = "set", [T_PREFIX_SET] = "prefix set", }; @@ -200,6 +202,7 @@ val_compare(const struct f_val *v1, const struct f_val *v2) return net_compare(v1->val.net, v2->val.net); case T_STRING: return strcmp(v1->val.s, v2->val.s); + case T_ROUTE: default: return F_CMP_ERROR; } @@ -290,6 +293,8 @@ val_same(const struct f_val *v1, const struct f_val *v2) return same_tree(v1->val.t, v2->val.t); case T_PREFIX_SET: return trie_same(v1->val.ti, v2->val.ti); + case T_ROUTE: + return rte_same(v1->val.rte, v2->val.rte); default: bug("Invalid type in val_same(): %x", v1->type); } @@ -563,6 +568,21 @@ val_in_range(const struct f_val *v1, const struct f_val *v2) return F_CMP_ERROR; } +/* + * rte_format - format route information + */ +static void +rte_format(const struct rte *rte, buffer *buf) +{ + if (rte) + buffer_print(buf, "Route [%d] to %N from %s via %s", + rte->src->global_id, rte->net, + rte->sender->req->name, + rte->src->owner->name); + else + buffer_puts(buf, "[No route]"); +} + /* * val_format - format filter value */ @@ -592,6 +612,7 @@ val_format(const struct f_val *v, buffer *buf) 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; + case T_ROUTE: rte_format(v->val.rte, buf); return; default: buffer_print(buf, "[unknown type %x]", v->type); return; } } diff --git a/filter/data.h b/filter/data.h index 9fb300e9..99f122c8 100644 --- a/filter/data.h +++ b/filter/data.h @@ -12,6 +12,7 @@ #include "nest/bird.h" #include "lib/type.h" +#include "nest/iface.h" struct f_method { struct symbol *sym; @@ -56,6 +57,7 @@ struct f_attr_bit { /* Filter l-value type */ enum f_lval_type { + F_LVAL_CONSTANT, F_LVAL_VARIABLE, F_LVAL_SA, F_LVAL_EA, @@ -65,6 +67,7 @@ enum f_lval_type { /* Filter l-value */ struct f_lval { enum f_lval_type type; + struct f_inst *rte; union { struct symbol *sym; const struct ea_class *da; diff --git a/filter/f-inst.c b/filter/f-inst.c index 8bbb0491..b2b449cb 100644 --- a/filter/f-inst.c +++ b/filter/f-inst.c @@ -666,18 +666,27 @@ } } - INST(FI_RTA_GET, 0, 1) { + INST(FI_CURRENT_ROUTE, 0, 1) { + NEVER_CONSTANT; + ACCESS_RTE; + RESULT_TYPE(T_ROUTE); + RESULT_VAL([[(struct f_val) { .type = T_ROUTE, .val.rte = fs->rte, }]]); + } + + INST(FI_RTA_GET, 1, 1) { { + ARG(1, T_ROUTE); STATIC_ATTR; - ACCESS_RTE; + + struct rte *rte = v1.val.rte; switch (sa.sa_code) { - case SA_NET: RESULT(sa.type, net, fs->rte->net); break; - case SA_PROTO: RESULT(sa.type, s, fs->rte->src->owner->name); break; + case SA_NET: RESULT(sa.type, net, rte->net); break; + case SA_PROTO: RESULT(sa.type, s, rte->src->owner->name); break; default: { - struct eattr *nhea = ea_find(fs->rte->attrs, &ea_gen_nexthop); + struct eattr *nhea = ea_find(rte->attrs, &ea_gen_nexthop); struct nexthop_adata *nhad = nhea ? (struct nexthop_adata *) nhea->u.ptr : NULL; struct nexthop *nh = nhad ? &nhad->nh : NULL; @@ -836,13 +845,13 @@ } } - INST(FI_EA_GET, 0, 1) { /* Access to extended attributes */ + INST(FI_EA_GET, 1, 1) { /* Access to extended attributes */ + ARG(1, T_ROUTE); DYNAMIC_ATTR; - ACCESS_RTE; RESULT_TYPE(da->type); { struct f_val empty; - const eattr *e = ea_find(fs->rte->attrs, da->id); + const eattr *e = ea_find(v1.val.rte->attrs, da->id); if (e) { diff --git a/filter/f-util.c b/filter/f-util.c index 15e825d4..84f84967 100644 --- a/filter/f-util.c +++ b/filter/f-util.c @@ -195,7 +195,7 @@ f_implicit_roa_check(struct rtable_config *tab) if (!def) bug("Couldn't find BGP AS Path attribute definition."); - struct f_inst *path_getter = f_new_inst(FI_EA_GET, def); + struct f_inst *path_getter = f_new_inst(FI_EA_GET, f_new_inst(FI_CURRENT_ROUTE), def); struct sym_scope *scope = f_type_method_scope(path_getter->type); struct symbol *ms = scope ? cf_find_symbol_scope(scope, "last") : NULL; @@ -205,7 +205,7 @@ f_implicit_roa_check(struct rtable_config *tab) struct f_static_attr fsa = f_new_static_attr(T_NET, SA_NET, 1); return f_new_inst(FI_ROA_CHECK, - f_new_inst(FI_RTA_GET, fsa), + f_new_inst(FI_RTA_GET, f_new_inst(FI_CURRENT_ROUTE), fsa), ms->method->new_inst(path_getter, NULL), tab); } diff --git a/lib/route.h b/lib/route.h index cb1e3f4e..30e6e88e 100644 --- a/lib/route.h +++ b/lib/route.h @@ -60,6 +60,8 @@ static inline rte rte_init_from(const rte *r) }; } +int rte_same(const rte *, const rte *); + struct rte_src { struct rte_src *next; /* Hash chain */ struct rte_owner *owner; /* Route source owner */ diff --git a/lib/type.h b/lib/type.h index c1f15d83..7260340f 100644 --- a/lib/type.h +++ b/lib/type.h @@ -42,6 +42,7 @@ union bval_long { const struct f_trie *ti; const struct f_path_mask *path_mask; struct f_path_mask_item pmi; + struct rte *rte; }; @@ -54,8 +55,9 @@ enum btype { /* Something but inaccessible. */ T_OPAQUE = 0x02, /* Opaque byte string (not filterable) */ T_IFACE = 0x0c, /* Pointer to an interface (inside adata) */ - T_NEXTHOP_LIST = 0x2c, /* The whole nexthop block */ - T_HOSTENTRY = 0x2e, /* Hostentry with possible MPLS labels */ + T_ROUTE = 0x6a, /* One route pointer */ + T_NEXTHOP_LIST = 0x6c, /* The whole nexthop block */ + T_HOSTENTRY = 0x6e, /* Hostentry with possible MPLS labels */ /* Types shared with eattrs */ T_INT = 0x01, /* 32-bit unsigned integer number */ diff --git a/nest/rt-table.c b/nest/rt-table.c index 6d36d7eb..9474c32a 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -154,7 +154,7 @@ static void rt_delete(void *); static void rt_export_used(struct rt_table_exporter *, const char *, const char *); static void rt_export_cleanup(struct rtable_private *tab); -static int rte_same(const rte *x, const rte *y); +int rte_same(const rte *x, const rte *y); const char *rt_import_state_name_array[TIS_MAX] = { [TIS_DOWN] = "DOWN", @@ -1674,12 +1674,15 @@ rte_validate(struct channel *ch, rte *e) return 1; } -static int +int rte_same(const rte *x, const rte *y) { /* rte.flags / rte.pflags are not checked, as they are internal to rtable */ return - x->attrs == y->attrs && + ( + (x->attrs == y->attrs) || + ((!(x->attrs->flags & EALF_CACHED) || !(y->attrs->flags & EALF_CACHED)) && ea_same(x->attrs, y->attrs)) + ) && x->src == y->src && rte_is_filtered(x) == rte_is_filtered(y); }