Filter: make bgpmask literals real constructors

The bgpmask literals can include expressions. This is OK but they have
to be interpreted as soon as the code is run, not in the time the code
is used as value.

This led to strange behavior like rewriting bgpmasks when they shan't
be rewritten:

	function mask_generator(int as)
	{
		return [= * as * =];
	}

	function another()
	bgpmask m1;
	bgpmask m2;
	{
		m1 = mask_generator(10);
		m2 = mask_generator(20);
		if (m1 == m2) {
			print("strange"); # this would happen
		}
	}

Moreover, sending this to CLI would cause stack overflow and knock down the
whole BIRD, as soon as there is at least one route to execute the given
filter on.

	show route filter bgpmask mmm; bgppath ppp; { ppp = +empty+; mmm = [= (ppp ~ mmm) =]; print(mmm); accept; }

The magic match operator (~) inside the bgpmask literal would try to
resolve mmm, which points to the same bgpmask so it would resolve
itself, call the magic match operator and vice versa.

After this patch, the bgpmask literal will get resolved as soon as it's
assigned to mmm and it also will return a type error as bool is not
convertible to ASN in BIRD.
This commit is contained in:
Jan Maria Matejka 2018-02-28 16:57:50 +01:00
parent 74bfd2f97c
commit e8bc64e308
4 changed files with 52 additions and 14 deletions

View file

@ -317,6 +317,26 @@ f_generate_lc(struct f_inst *t1, struct f_inst *t2, struct f_inst *t3)
return rv; return rv;
} }
static inline struct f_inst *
f_generate_path_mask(struct f_path_mask *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->a1.p = t;
return mrv;
}
}
NEW_F_VAL;
val->type = T_PATH_MASK;
val->val.path_mask = t;
struct f_inst *rv = f_new_inst(FI_CONSTANT_INDIRECT);
rv->a1.p = val;
return rv;
}
CF_DECLS CF_DECLS
@ -708,13 +728,13 @@ constant:
| '[' set_items ']' { DBG( "We've got a set here..." ); $$ = f_new_inst(FI_CONSTANT); $$->aux = T_SET; $$->a2.p = build_tree($2); DBG( "ook\n" ); } | '[' set_items ']' { DBG( "We've got a set here..." ); $$ = f_new_inst(FI_CONSTANT); $$->aux = T_SET; $$->a2.p = build_tree($2); DBG( "ook\n" ); }
| '[' fprefix_set ']' { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_PREFIX_SET; $$->a2.p = $2; } | '[' fprefix_set ']' { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_PREFIX_SET; $$->a2.p = $2; }
| ENUM { $$ = f_new_inst(FI_CONSTANT); $$->aux = $1 >> 16; $$->a2.i = $1 & 0xffff; } | ENUM { $$ = f_new_inst(FI_CONSTANT); $$->aux = $1 >> 16; $$->a2.i = $1 & 0xffff; }
| bgp_path { NEW_F_VAL; $$ = f_new_inst(FI_CONSTANT_INDIRECT); val->type = T_PATH_MASK; val->val.path_mask = $1; $$->a1.p = val; }
; ;
constructor: constructor:
'(' term ',' term ')' { $$ = f_generate_dpair($2, $4); } '(' term ',' term ')' { $$ = f_generate_dpair($2, $4); }
| '(' ec_kind ',' term ',' term ')' { $$ = f_generate_ec($2, $4, $6); } | '(' ec_kind ',' term ',' term ')' { $$ = f_generate_ec($2, $4, $6); }
| '(' term ',' term ',' term ')' { $$ = f_generate_lc($2, $4, $6); } | '(' term ',' term ',' term ')' { $$ = f_generate_lc($2, $4, $6); }
| bgp_path { $$ = f_generate_path_mask($1); }
; ;

View file

@ -82,8 +82,7 @@ pm_format(struct f_path_mask *p, buffer *buf)
break; break;
case PM_ASN_EXPR: case PM_ASN_EXPR:
buffer_print(buf, "%u ", f_eval_asn((struct f_inst *) p->val)); ASSERT(0);
break;
} }
p = p->next; p = p->next;
@ -772,6 +771,32 @@ interpret(struct f_inst *what)
break; break;
} }
case FI_PATHMASK_CONSTRUCT:
{
struct f_path_mask *tt = what->a1.p, *vbegin, **vv = &vbegin;
while (tt) {
*vv = lp_alloc(f_pool, sizeof(struct f_path_mask));
if (tt->kind == PM_ASN_EXPR) {
struct f_val res = interpret((struct f_inst *) tt->val);
(*vv)->kind = PM_ASN;
if (res.type != T_INT) {
runtime( "Error resolving path mask template: value not an integer" );
return (struct f_val) { .type = T_VOID };
}
(*vv)->val = res.val.i;
} else {
**vv = *tt;
}
tt = tt->next;
vv = &((*vv)->next);
}
res = (struct f_val) { .type = T_PATH_MASK, .val.path_mask = vbegin };
break;
}
/* Relational operators */ /* Relational operators */
#define COMPARE(x) \ #define COMPARE(x) \
@ -1550,6 +1575,8 @@ i_same(struct f_inst *f1, struct f_inst *f2)
case FI_LT: case FI_LT:
case FI_LTE: TWOARGS; break; case FI_LTE: TWOARGS; break;
case FI_PATHMASK_CONSTRUCT: if (!pm_same(f1->a1.p, f2->a1.p)) return 0; break;
case FI_NOT: ONEARG; break; case FI_NOT: ONEARG; break;
case FI_NOT_MATCH: case FI_NOT_MATCH:
case FI_MATCH: TWOARGS; break; case FI_MATCH: TWOARGS; break;
@ -1771,14 +1798,6 @@ f_eval_int(struct f_inst *expr)
return res.val.i; return res.val.i;
} }
u32
f_eval_asn(struct f_inst *expr)
{
/* Called as a part of another interpret call, therefore no log_reset() */
struct f_val res = interpret(expr);
return (res.type == T_INT) ? res.val.i : 0;
}
/** /**
* filter_same - compare two filters * filter_same - compare two filters
* @new: first filter to be compared * @new: first filter to be compared

View file

@ -27,6 +27,7 @@
F(FI_PAIR_CONSTRUCT, 'm', 'p') \ F(FI_PAIR_CONSTRUCT, 'm', 'p') \
F(FI_EC_CONSTRUCT, 'm', 'c') \ F(FI_EC_CONSTRUCT, 'm', 'c') \
F(FI_LC_CONSTRUCT, 'm', 'l') \ F(FI_LC_CONSTRUCT, 'm', 'l') \
F(FI_PATHMASK_CONSTRUCT, 'm', 'P') \
F(FI_NEQ, '!', '=') \ F(FI_NEQ, '!', '=') \
F(FI_EQ, '=', '=') \ F(FI_EQ, '=', '=') \
F(FI_LT, 0, '<') \ F(FI_LT, 0, '<') \
@ -196,7 +197,6 @@ int f_run(struct filter *filter, struct rte **rte, struct ea_list **tmp_attrs, s
struct f_val f_eval_rte(struct f_inst *expr, struct rte **rte, struct linpool *tmp_pool); struct f_val f_eval_rte(struct f_inst *expr, struct rte **rte, struct linpool *tmp_pool);
struct f_val f_eval(struct f_inst *expr, struct linpool *tmp_pool); struct f_val f_eval(struct f_inst *expr, struct linpool *tmp_pool);
uint f_eval_int(struct f_inst *expr); uint f_eval_int(struct f_inst *expr);
u32 f_eval_asn(struct f_inst *expr);
char *filter_name(struct filter *filter); char *filter_name(struct filter *filter);
int filter_same(struct filter *new, struct filter *old); int filter_same(struct filter *new, struct filter *old);

View file

@ -535,8 +535,7 @@ as_path_match(struct adata *path, struct f_path_mask *mask)
val2 = val = mask->val; val2 = val = mask->val;
goto step; goto step;
case PM_ASN_EXPR: case PM_ASN_EXPR:
val2 = val = f_eval_asn((struct f_inst *) mask->val); ASSERT(0);
goto step;
case PM_ASN_RANGE: case PM_ASN_RANGE:
val = mask->val; val = mask->val;
val2 = mask->val2; val2 = mask->val2;