summaryrefslogtreecommitdiff
path: root/filter
diff options
context:
space:
mode:
authorMaria Matejka <mq@ucw.cz>2019-01-30 14:03:47 +0100
committerMaria Matejka <mq@ucw.cz>2019-02-20 22:30:54 +0100
commitc0e958e022aac79f69e6aca2652fdb6a529e68e2 (patch)
treeed43827b8f1ade3b7bec7edd9a64b0dfdbd2343a /filter
parent713658798dfafabcd0a74f510c1639f6e3c9c820 (diff)
Filter + Config: Fix bugs, tests and split symbols by type
Diffstat (limited to 'filter')
-rw-r--r--filter/config.Y144
-rw-r--r--filter/f-inst.c13
-rw-r--r--filter/filter.h22
-rw-r--r--filter/filter_test.c35
-rw-r--r--filter/test.conf11
5 files changed, 139 insertions, 86 deletions
diff --git a/filter/config.Y b/filter/config.Y
index 607f534e..1306849f 100644
--- a/filter/config.Y
+++ b/filter/config.Y
@@ -17,7 +17,7 @@ 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, da, f_new_inst(fi_code, f_new_inst(FI_EA_GET, da), arg))
+ f_new_inst(FI_EA_SET, f_new_inst(fi_code, f_new_inst(FI_EA_GET, da), arg), da)
/*
* Sets and their items are during parsing handled as lists, linked
@@ -179,7 +179,7 @@ f_generate_empty(struct f_dynamic_attr dyn)
cf_error("Can't empty that attribute");
}
- return f_new_inst(FI_EA_SET, dyn, f_new_inst(FI_CONSTANT, empty));
+ return f_new_inst(FI_EA_SET, f_new_inst(FI_CONSTANT, empty), dyn);
}
#if 0
@@ -389,6 +389,37 @@ 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;
+ switch (lval->type) {
+ case F_LVAL_VARIABLE:
+ setter = f_new_inst(FI_SET, expr, lval->sym);
+ getter = f_new_inst(FI_VARIABLE, 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);
+ f_inst_next(setter, checker);
+
+ return assert_done(setter, start, end);
+}
+
CF_DECLS
CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
@@ -407,17 +438,18 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
PREPEND, FIRST, LAST, LAST_NONAGGREGATED, MATCH,
EMPTY,
FILTER, WHERE, EVAL, ATTRIBUTE,
- BT_ASSERT, BT_TEST_SUITE, FORMAT)
+ BT_ASSERT, BT_TEST_SUITE, BT_CHECK_ASSIGN, FORMAT)
%nonassoc THEN
%nonassoc ELSE
%type <xc> function_params declsn
%type <xp> cmds_int function_body
-%type <x> term block cmd cmds constant constructor print_one print_list var_list var_listn function_call symbol bgp_path_expr bgp_path bgp_path_tail one_decl decls
+%type <x> term block cmd cmds constant constructor print_one print_list var_list var_listn function_call symbol_value bgp_path_expr bgp_path bgp_path_tail one_decl decls
%type <fda> dynamic_attr
%type <fsa> static_attr
%type <f> filter filter_body where_filter
+%type <flv> lvalue
%type <i> type
%type <ecs> ec_kind
%type <fret> break_command
@@ -432,7 +464,7 @@ CF_GRAMMAR
conf: filter_def ;
filter_def:
- FILTER SYM { $2 = cf_define_symbol($2, SYM_FILTER, NULL); cf_push_scope( $2 ); }
+ FILTER CF_SYM_VOID { $2 = cf_define_symbol($2, SYM_FILTER, NULL); cf_push_scope( $2 ); }
filter_body {
$2->def = $4;
$4->name = $2->name;
@@ -447,16 +479,13 @@ filter_eval:
;
conf: custom_attr ;
-custom_attr: ATTRIBUTE type SYM ';' {
+custom_attr: ATTRIBUTE type CF_SYM_VOID ';' {
cf_define_symbol($3, SYM_ATTRIBUTE, ca_lookup(new_config->pool, $3->name, $2)->fda);
};
conf: bt_test_suite ;
bt_test_suite:
- BT_TEST_SUITE '(' SYM ',' text ')' {
- if (!($3->class & SYM_FUNCTION))
- cf_error("Function expected");
-
+ BT_TEST_SUITE '(' CF_SYM_FUNCTION ',' text ')' {
struct f_bt_test_suite *t = cfg_alloc(sizeof(struct f_bt_test_suite));
t->fn = $3->def;
t->fn_name = $3->name;
@@ -505,7 +534,7 @@ type:
;
one_decl:
- type SYM {
+ type CF_SYM_VOID {
struct f_val * val = cfg_alloc(sizeof(struct f_val));
val->type = T_VOID;
$2 = cf_define_symbol($2, SYM_VARIABLE | $1, val);
@@ -545,8 +574,7 @@ filter_body:
;
filter:
- SYM {
- if ($1->class != SYM_FILTER) cf_error("No such filter.");
+ CF_SYM_FILTER {
$$ = $1->def;
}
| filter_body
@@ -573,7 +601,7 @@ function_body:
conf: function_def ;
function_def:
- FUNCTION SYM { DBG( "Beginning of function %s\n", $2->name );
+ FUNCTION CF_SYM_VOID { DBG( "Beginning of function %s\n", $2->name );
$2 = cf_define_symbol($2, SYM_FUNCTION, NULL);
cf_push_scope($2);
} function_params function_body {
@@ -647,8 +675,7 @@ set_atom:
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 {
- if (!cf_symbol_is_constant($1)) cf_error("%s: constant expected", $1->name);
+ | CF_SYM_CONSTANT {
if (!f_valid_set_type(SYM_TYPE($1))) cf_error("%s: set-incompatible type", $1->name);
$$ = *(struct f_val *)($1->def);
}
@@ -764,7 +791,7 @@ switch_body: /* EMPTY */ { $$ = NULL; }
;
bgp_path_expr:
- symbol { $$ = $1; }
+ symbol_value { $$ = $1; }
| '(' term ')' { $$ = $2; }
;
@@ -806,31 +833,17 @@ constructor:
;
-rtadot: /* EMPTY, we are not permitted RTA. prefix */
- ;
-
function_call:
- SYM '(' var_list ')' {
+ CF_SYM_FUNCTION '(' var_list ')' {
$$ = f_new_inst(FI_CALL, $1, $3);
}
;
-symbol:
- SYM {
- switch ($1->class & 0xffff) {
- case SYM_CONSTANT_RANGE:
- $$ = f_new_inst(FI_CONSTANT_INDIRECT, $1->def);
- break;
- case SYM_VARIABLE_RANGE:
- $$ = f_new_inst(FI_VARIABLE, $1);
- break;
- case SYM_ATTRIBUTE:
- $$ = f_new_inst(FI_EA_GET, *((struct f_dynamic_attr *) $1->def));
- break;
- default:
- cf_error("%s: variable expected.", $1->name);
- }
- }
+symbol_value:
+ CF_SYM_CONSTANT { $$ = f_new_inst(FI_CONSTANT_INDIRECT, $1->def); }
+ | CF_SYM_VARIABLE { $$ = f_new_inst(FI_VARIABLE, $1); }
+ | CF_SYM_ATTRIBUTE { $$ = f_new_inst(FI_EA_GET, *((struct f_dynamic_attr *) $1->def)); }
+ ;
static_attr:
FROM { $$ = f_new_static_attr(T_IP, SA_FROM, 0); }
@@ -863,15 +876,15 @@ term:
| '!' term { $$ = f_new_inst(FI_NOT, $2); }
| DEFINED '(' term ')' { $$ = f_new_inst(FI_DEFINED, $3); }
- | symbol { $$ = $1; }
+ | symbol_value { $$ = $1; }
| constant { $$ = $1; }
| constructor { $$ = $1; }
| PREFERENCE { $$ = f_new_inst(FI_PREF_GET); }
- | rtadot static_attr { $$ = f_new_inst(FI_RTA_GET, $2); }
+ | static_attr { $$ = f_new_inst(FI_RTA_GET, $1); }
- | rtadot dynamic_attr { $$ = f_new_inst(FI_EA_GET, $2); }
+ | dynamic_attr { $$ = f_new_inst(FI_EA_GET, $1); }
| term '.' IS_V4 { $$ = f_new_inst(FI_IS_V4, $1); }
| term '.' TYPE { $$ = f_new_inst(FI_TYPE, $1); }
@@ -888,10 +901,10 @@ term:
/* Communities */
/* This causes one shift/reduce conflict
- | rtadot dynamic_attr '.' ADD '(' term ')' { }
- | rtadot dynamic_attr '.' DELETE '(' term ')' { }
- | rtadot dynamic_attr '.' CONTAINS '(' term ')' { }
- | rtadot dynamic_attr '.' RESET{ }
+ | dynamic_attr '.' ADD '(' term ')' { }
+ | dynamic_attr '.' DELETE '(' term ')' { }
+ | dynamic_attr '.' CONTAINS '(' term ')' { }
+ | dynamic_attr '.' RESET{ }
*/
| '+' EMPTY '+' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_path); }
@@ -956,32 +969,29 @@ cmd:
| IF term THEN block ELSE block {
$$ = f_new_inst(FI_CONDITION, $2, $4, $6);
}
- | SYM '=' term ';' {
- DBG( "Ook, we'll set value\n" );
- if ($1->class == SYM_ATTRIBUTE) {
- $$ = f_new_inst(FI_EA_SET, *((struct f_dynamic_attr *) $1->def), $3);
- } else if (($1->class & ~T_MASK) == SYM_VARIABLE) {
- $$ = f_new_inst(FI_SET, $3, $1);
- } else
- cf_error( "Symbol `%s' is read-only.", $1->name );
+ | CF_SYM_ATTRIBUTE '=' term ';' {
+ $$ = f_new_inst(FI_EA_SET, $3, *((struct f_dynamic_attr *) $1->def));
+ }
+ | CF_SYM_VARIABLE '=' term ';' {
+ $$ = f_new_inst(FI_SET, $3, $1);
}
| RETURN term ';' {
DBG( "Ook, we'll return the value\n" );
$$ = f_new_inst(FI_RETURN, $2);
}
- | rtadot dynamic_attr '=' term ';' {
- $$ = f_new_inst(FI_EA_SET, $2, $4);
+ | dynamic_attr '=' term ';' {
+ $$ = f_new_inst(FI_EA_SET, $3, $1);
}
- | rtadot static_attr '=' term ';' {
- if ($2.readonly)
+ | static_attr '=' term ';' {
+ if ($1.readonly)
cf_error( "This static attribute is read-only.");
- $$ = f_new_inst(FI_RTA_SET, $2, $4);
+ $$ = f_new_inst(FI_RTA_SET, $3, $1);
}
| PREFERENCE '=' term ';' {
$$ = f_new_inst(FI_PREF_SET, $3);
}
- | UNSET '(' rtadot dynamic_attr ')' ';' {
- $$ = f_new_inst(FI_EA_UNSET, $4);
+ | UNSET '(' dynamic_attr ')' ';' {
+ $$ = f_new_inst(FI_EA_UNSET, $3);
}
| break_command print_list ';' { $$ = f_new_inst(FI_PRINT_AND_DIE, $2, $1); }
| function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); }
@@ -989,12 +999,13 @@ cmd:
$$ = f_new_inst(FI_SWITCH, $2, build_tree($4));
}
- | rtadot dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($2); }
- | 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 ); }
+ | dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($1); }
+ | dynamic_attr '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex( FI_PATH_PREPEND, $1, $5 ); }
+ | dynamic_attr '.' ADD '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_ADD, $1, $5 ); }
+ | dynamic_attr '.' DELETE '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_DEL, $1, $5 ); }
+ | dynamic_attr '.' FILTER '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_FILTER, $1, $5 ); }
| BT_ASSERT '(' get_cf_position term get_cf_position ')' ';' { $$ = assert_done($4, $3 + 1, $5 - 1); }
+ | BT_CHECK_ASSIGN '(' get_cf_position lvalue get_cf_position ',' term ')' ';' { $$ = assert_assign(&$4, $7, $3 + 1, $5 - 1); }
;
get_cf_position:
@@ -1002,5 +1013,10 @@ get_cf_position:
$$ = cf_text;
};
+lvalue:
+ CF_SYM_VARIABLE { $$ = (struct f_lval) { .type = F_LVAL_VARIABLE, .sym = $1 }; }
+ | PREFERENCE { $$ = (struct f_lval) { .type = F_LVAL_PREFERENCE }; }
+ | static_attr { $$ = (struct f_lval) { .type = F_LVAL_SA, .sa = $1 }; }
+ | dynamic_attr { $$ = (struct f_lval) { .type = F_LVAL_EA, .da = $1 }; };
CF_END
diff --git a/filter/f-inst.c b/filter/f-inst.c
index ae2b5289..0dd9f9f6 100644
--- a/filter/f-inst.c
+++ b/filter/f-inst.c
@@ -109,7 +109,7 @@
ARG_ANY(1);
COUNT(2);
- NEW([[]], [[
+ NEW(, [[
uint len = 0;
uint dyn = 0;
for (const struct f_inst *tt = f1; tt; tt = tt->next, len++)
@@ -329,9 +329,9 @@
}
INST(FI_RTA_SET, 1, 0) {
- STATIC_ATTR;
ACCESS_RTE;
ARG_ANY(1);
+ STATIC_ATTR;
if (sa.f_type != v1.type)
runtime( "Attempt to set static attribute to incompatible type" );
@@ -475,10 +475,10 @@
}
INST(FI_EA_SET, 1, 0) {
- DYNAMIC_ATTR;
ACCESS_RTE;
ACCESS_EATTRS;
ARG_ANY(1);
+ DYNAMIC_ATTR;
{
struct ea_list *l = lp_alloc(fs->pool, sizeof(struct ea_list) + sizeof(eattr));
@@ -719,7 +719,7 @@
/* Then push the arguments */
LINE(1,1);
- NEW([[]], [[
+ NEW(, [[
if (sym->class != SYM_FUNCTION)
cf_error("You can't call something which is not a function. Really.");
@@ -984,5 +984,8 @@
INST(FI_ASSERT, 1, 0) { /* Birdtest Assert */
ARG(1, T_BOOL);
STRING;
- CALL(bt_assert_hook, res.val.i, what);
+ if (!bt_assert_hook)
+ runtime("No bt_assert hook registered, can't assert");
+
+ bt_assert_hook(res.val.i, what);
}
diff --git a/filter/filter.h b/filter/filter.h
index 39f16e93..15a24fd4 100644
--- a/filter/filter.h
+++ b/filter/filter.h
@@ -128,6 +128,24 @@ enum filter_return {
F_QUITBIRD,
};
+/* Filter l-value type */
+enum f_lval_type {
+ F_LVAL_VARIABLE,
+ F_LVAL_PREFERENCE,
+ F_LVAL_SA,
+ F_LVAL_EA,
+};
+
+/* Filter l-value */
+struct f_lval {
+ enum f_lval_type type;
+ union {
+ const struct symbol *sym;
+ struct f_dynamic_attr da;
+ struct f_static_attr sa;
+ };
+};
+
/* Filter instruction declarations */
#define FI__LIST \
F(FI_NOP) \
@@ -159,9 +177,9 @@ enum filter_return {
F(FI_CONDITION, ARG, LINE, LINE) \
F(FI_PRINT_AND_DIE, ARG, FRET) \
F(FI_RTA_GET, SA) \
- F(FI_RTA_SET, SA, ARG) \
+ F(FI_RTA_SET, ARG, SA) \
F(FI_EA_GET, EA) \
- F(FI_EA_SET, EA, ARG) \
+ F(FI_EA_SET, ARG, EA) \
F(FI_EA_UNSET, EA) \
F(FI_PREF_GET) \
F(FI_PREF_SET, ARG) \
diff --git a/filter/filter_test.c b/filter/filter_test.c
index e19b0a75..af6b590f 100644
--- a/filter/filter_test.c
+++ b/filter/filter_test.c
@@ -22,19 +22,21 @@
#define BT_CONFIG_FILE "filter/test.conf"
-static struct config *
-parse_config_file(const void *filename_void)
-{
- bt_bird_init();
+struct parse_config_file_arg {
+ struct config **cp;
+ const char *filename;
+};
- size_t fn_size = strlen((const char *) filename_void) + 1;
+static int
+parse_config_file(const void *argv)
+{
+ const struct parse_config_file_arg *arg = argv;
+ size_t fn_size = strlen(arg->filename) + 1;
char *filename = alloca(fn_size);
- strncpy(filename, filename_void, fn_size);
-
- struct config *c = bt_config_file_parse(filename);
- bt_bird_cleanup();
-
- return c;
+ strncpy(filename, arg->filename, fn_size);
+
+ *(arg->cp) = bt_config_file_parse(filename);
+ return !!*(arg->cp);
}
static int
@@ -68,13 +70,18 @@ int
main(int argc, char *argv[])
{
bt_init(argc, argv);
+ bt_bird_init();
+
+ bt_assert_hook = bt_assert_filter;
- struct config *c = parse_config_file(BT_CONFIG_FILE);
+ struct config *c = NULL;
+ struct parse_config_file_arg pcfa = { .cp = &c, .filename = BT_CONFIG_FILE };
+ bt_test_suite_base(parse_config_file, "conf", (const void *) &pcfa, 0, 0, "parse config file");
+
+ bt_bird_cleanup();
if (c)
{
- bt_assert_hook = bt_assert_filter;
-
struct f_bt_test_suite *t;
WALK_LIST(t, c->tests)
bt_test_suite_base(run_function, t->fn_name, t->fn, BT_FORKING, BT_TIMEOUT, "%s", t->dsc);
diff --git a/filter/test.conf b/filter/test.conf
index 39b349cc..e6b6ca4e 100644
--- a/filter/test.conf
+++ b/filter/test.conf
@@ -1336,7 +1336,7 @@ bt_test_suite(t_mixed_prefix, "Testing mixed net types");
filter vpn_filter
{
- bt_assert(format(net) = "0:1:2 10.1.10.0/24");
+ bt_assert(format(net) = "1:2 10.1.10.0/24");
bt_assert(net.type = NET_VPN4);
bt_assert(net.type != NET_IP4);
bt_assert(net.type != NET_IP6);
@@ -1347,6 +1347,15 @@ filter vpn_filter
NET_IP6: print "IPV6";
}
+# aoiufahkejtrhaweifdsbhydkfj,ysdnm
+
+ bt_check_assign(from, 10.20.30.40);
+ bt_check_assign(gw, 55.55.55.44);
+
+ bgp_community.add((3,5));
+ bgp_ext_community.add((ro, 135, 999));
+ bgp_large_community.add((6464156, 89646354, 8675643));
+
accept;
}