diff --git a/filter/config.Y b/filter/config.Y index 6aa1285f..7b6a7326 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -21,7 +21,7 @@ CF_DECLS CF_KEYWORDS(FUNCTION, PRINT, CONST, ACCEPT, REJECT, ERROR, QUITBIRD, INT, BOOL, IP, PREFIX, PAIR, SET, STRING, - IF, THEN, ELSE, + IF, THEN, ELSE, CASE, TRUE, FALSE, RTA, FROM, GW, NET, LEN, @@ -29,7 +29,7 @@ CF_KEYWORDS(FUNCTION, PRINT, CONST, FILTER ) -%type term block cmds cmd function_body ifthen constant print_one print_list var_list +%type term block cmds cmd function_body ifthen constant print_one print_list var_list switch_body %type filter filter_body %type type break_command %type set_item set_items @@ -118,6 +118,8 @@ function_def: cmds: /* EMPTY */ { $$ = NULL; } | cmd cmds { if ($1) { + if ($1->next) + bug("Command has next already set\n"); $1->next = $2; $$ = $1; } else $$ = $2; @@ -160,7 +162,6 @@ constant: term: term '+' term { $$ = f_new_inst(); $$->code = '+'; $$->a1.p = $1; $$->a2.p = $3; } - | term '=' term { $$ = f_new_inst(); $$->code = '=='; $$->a1.p = $1; $$->a2.p = $3; } | term '!' '=' term { $$ = f_new_inst(); $$->code = '!='; $$->a1.p = $1; $$->a2.p = $4; } | term '<' term { $$ = f_new_inst(); $$->code = '<'; $$->a1.p = $1; $$->a2.p = $3; } @@ -231,6 +232,22 @@ var_list: /* EMPTY */ { $$ = NULL; } } ; +switch_body: /* EMPTY */ { $$ = NULL; } + | term ':' block switch_body { + $$ = f_new_inst(); + $$->code = 'of'; + $$->a1.p = $1; + $$->a2.p = $3; + $$->next = $4; + } + | ELSE ':' block { + $$ = f_new_inst(); + $$->code = 'el'; + $$->a1.p = NULL; + $$->a2.p = $3; + } + ; + cmd: ifthen { $$ = $1; @@ -272,6 +289,12 @@ cmd: inst = inst->next; } } + | CASE term '{' switch_body '}' { + $$ = f_new_inst(); + $$->code = 'sw'; + $$->a1.p = $2; + $$->a2.p = $4; + } ; CF_END diff --git a/filter/filter.c b/filter/filter.c index 41499710..7939ad01 100644 --- a/filter/filter.c +++ b/filter/filter.c @@ -53,6 +53,8 @@ struct f_inst *startup_func = NULL; int val_compare(struct f_val v1, struct f_val v2) { + if (v1.type != v2.type) + return CMP_ERROR; switch (v1.type) { case T_INT: if (v1.val.i == v2.val.i) return 0; @@ -64,6 +66,14 @@ val_compare(struct f_val v1, struct f_val v2) } } +int +val_in_range(struct f_val v1, struct f_val v2) +{ + if (((v1.type == T_INT) || (v1.type == T_IP)) && (v2.type == T_SET)) + return !! find_tree(v2.val.t, v1); + return CMP_ERROR; +} + static void tree_print(struct f_tree *t) { @@ -98,6 +108,42 @@ val_print(struct f_val v) static struct rte **f_rte; +static struct f_val interpret(struct f_inst *what); + +static struct f_val +interpret_switch(struct f_inst *what, struct f_val control) +{ + struct f_val this, res; + int i; + res.type = T_VOID; + + if (!what) + return res; + + switch(what->code) { + case 'el': + return interpret(what->a2.p); + + case 'of': + this = interpret(what->a1.p); + i = val_compare(control, this); + if (!i) + return interpret(what->a2.p); + if (i==CMP_ERROR) { + i = val_in_range(control, this); + if (i==1) + return interpret(what->a2.p); + if (i==CMP_ERROR) + runtime( "incompatible types in case" ); + } + break; + + default: + bug( "This can not happen (%x)\n", what->code ); + } + return interpret_switch(what->next, control); +} + static struct f_val interpret(struct f_inst *what) { @@ -155,11 +201,9 @@ interpret(struct f_inst *what) case '~': TWOARGS; res.type = T_BOOL; - if (((v1.type == T_INT) || (v1.type == T_IP)) && (v2.type == T_SET)) { - res.val.i = !! find_tree(v2.val.t, v1); - break; - } - runtime( "~ applied on unknown type pair" ); + res.val.i = val_in_range(v1, v2); + if (res.val.i == CMP_ERROR) + runtime( "~ applied on unknown type pair" ); break; /* Set to consant, a1 = type, a2 = value */ @@ -258,6 +302,10 @@ interpret(struct f_inst *what) ONEARG; res = interpret(what->a2.p); break; + case 'sw': /* SWITCH alias CASE */ + ONEARG; + interpret_switch(what->a2.p, v1); + break; default: bug( "Unknown instruction %d (%c)", what->code, what->code & 0xff); }