Filter: refactoring of instruction constructors

This commit is contained in:
Maria Matejka 2019-01-21 09:17:54 +01:00
parent 4c553c5a5b
commit 9b46748d5b
18 changed files with 749 additions and 432 deletions

View file

@ -107,7 +107,7 @@ struct symbol {
struct sym_scope *scope;
int class;
int aux;
void *aux2;
uint aux2;
void *def;
char name[1];
};

View file

@ -49,6 +49,10 @@ CF_DECLS
struct rtable_config *r;
struct channel_config *cc;
struct f_inst *x;
struct f_inst *xp[2];
struct { struct f_inst *inst; uint count; } xc;
enum filter_return fret;
enum ec_subtype ecs;
struct f_dynamic_attr fda;
struct f_static_attr fsa;
struct filter *f;

View file

@ -1,4 +1,4 @@
src := filter.c f-util.c tree.c trie.c
src := filter.c f-util.c tree.c trie.c f-inst-new.c
obj := $(src-o-files)
$(all-daemon)
$(cf-local)
@ -17,10 +17,16 @@ $(o)f-inst-interpret.c: $(s)interpret.m4 $(s)f-inst.c $(objdir)/.dir-stamp
$(o)f-inst-same.c: $(s)same.m4 $(s)f-inst.c $(objdir)/.dir-stamp
$(M4) $(M4FLAGS_FILTERS) -P $^ >$@
$(o)f-inst-struct.h: $(s)struct.m4 $(s)f-inst.c $(objdir)/.dir-stamp
$(M4) $(M4FLAGS_FILTERS) -P $^ >$@
$(o)f-inst-new.c: $(s)new.m4 $(s)f-inst.c $(objdir)/.dir-stamp
$(M4) $(M4FLAGS_FILTERS) -P $^ >$@
$(o)f-inst-dump.c: $(s)dump.m4 $(s)f-inst.c $(objdir)/.dir-stamp
$(M4) $(M4FLAGS_FILTERS) -P $^ >$@
$(o)filter.o: $(o)f-inst-interpret.c $(o)f-inst-line-size.c $(o)f-inst-postfixify.c $(o)f-inst-same.c $(o)f-inst-dump.c
$(o)filter.o: $(o)f-inst-interpret.c $(o)f-inst-line-size.c $(o)f-inst-postfixify.c $(o)f-inst-same.c $(o)f-inst-dump.c $(o)f-inst-struct.h
tests_src := tree_test.c filter_test.c trie_test.c
tests_targets := $(tests_targets) $(tests-target-files)

View file

@ -16,6 +16,8 @@ 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, da, f_new_inst(fi_code, f_new_inst(FI_EA_GET, da), arg))
/*
* Sets and their items are during parsing handled as lists, linked
@ -158,30 +160,29 @@ f_new_lc_item(u32 f1, u32 t1, u32 f2, u32 t2, u32 f3, u32 t3)
static inline struct f_inst *
f_generate_empty(struct f_dynamic_attr dyn)
{
struct f_inst *e = f_new_inst(FI_CONSTANT);
struct f_val empty;
switch (dyn.type & EAF_TYPE_MASK) {
case EAF_TYPE_AS_PATH:
e->val = f_const_empty_path;
empty = f_const_empty_path;
break;
case EAF_TYPE_INT_SET:
e->val = f_const_empty_clist;
empty = f_const_empty_clist;
break;
case EAF_TYPE_EC_SET:
e->val = f_const_empty_eclist;
empty = f_const_empty_eclist;
break;
case EAF_TYPE_LC_SET:
e->val = f_const_empty_lclist;
empty = f_const_empty_lclist;
break;
default:
cf_error("Can't empty that attribute");
}
struct f_inst *s = f_new_inst_da(FI_EA_SET, dyn);
s->a[0].p = e;
return s;
return f_new_inst(FI_EA_SET, dyn, f_new_inst(FI_CONSTANT, empty));
}
#if 0
static inline struct f_inst *
f_generate_dpair(struct f_inst *t1, struct f_inst *t2)
@ -332,6 +333,8 @@ f_generate_path_mask(struct f_inst *t)
return pmc;
}
#endif
/*
* Remove all new lines and doubled whitespaces
* and convert all tabulators to spaces
@ -380,21 +383,10 @@ assert_copy_expr(const char *start, size_t len)
static struct f_inst *
assert_done(struct f_inst *expr, const char *start, const char *end)
{
struct f_inst *i;
i = f_new_inst(FI_ASSERT);
i->a[0].p = expr;
if (end >= start)
{
i->a[1].p = assert_copy_expr(start, end - start + 1);
}
else
{
/* this is a break of lexer buffer */
i->a[1].p = "???";
}
return i;
return f_new_inst(FI_ASSERT, expr,
(end >= start) ?
assert_copy_expr(start, end - start + 1)
: "???");
}
CF_DECLS
@ -420,17 +412,20 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
%nonassoc THEN
%nonassoc ELSE
%type <x> term block cmds cmds_int cmd function_body constant constructor print_one print_list var_list var_listn function_call symbol bgp_path_expr bgp_path bgp_path_tail
%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 <fda> dynamic_attr
%type <fsa> static_attr
%type <f> filter filter_body where_filter
%type <i> type break_command ec_kind
%type <i> type
%type <ecs> ec_kind
%type <fret> break_command
%type <i32> cnum
%type <e> pair_item ec_item lc_item set_item switch_item set_items switch_items switch_body
%type <trie> fprefix_set
%type <v> set_atom switch_atom fipa
%type <px> fprefix
%type <s> decls declsn one_decl function_params
%type <t> get_cf_position
CF_GRAMMAR
@ -515,8 +510,7 @@ one_decl:
val->type = T_VOID;
$2 = cf_define_symbol($2, SYM_VARIABLE | $1, val);
DBG( "New variable %s type %x\n", $2->name, $1 );
$2->aux2 = NULL;
$$=$2;
$$ = f_new_inst(FI_SET, NULL, $2);
}
;
@ -524,24 +518,29 @@ one_decl:
decls: /* EMPTY */ { $$ = NULL; }
| one_decl ';' decls {
$$ = $1;
$$->aux2 = $3;
f_inst_next($$, $3);
}
;
/* Declarations that have no ';' at the end. */
declsn: one_decl { $$ = $1; }
/* Declarations that have no ';' at the end. Beware; these are reversed. */
declsn: one_decl { $$.inst = $1; $$.count = 1; }
| one_decl ';' declsn {
$$ = $1;
$$->aux2 = $3;
$$ = $3;
$$.count++;
f_inst_next($$.inst, $1);
}
;
filter_body:
function_body {
struct filter *f = cfg_alloc(sizeof(struct filter));
f->name = NULL;
f->root = f_postfixify($1);
$$ = f;
$$ = cfg_alloc(sizeof(struct filter));
$$->name = NULL;
if ($1[0]) {
const struct f_inst *inst[2] = { $1[0], $1[1] };
$$->root = f_postfixify_concat(inst, 2);
}
else
$$->root = f_postfixify($1[1]);
}
;
@ -556,42 +555,19 @@ filter:
where_filter:
WHERE term {
/* Construct 'IF term THEN { ACCEPT; } ELSE { REJECT; }' */
struct filter *f = cfg_alloc(sizeof(struct filter));
struct f_inst acc = {
.fi_code = FI_PRINT_AND_DIE,
.a = { { .p = NULL }, { .i = F_ACCEPT } },
.lineno = ifs->lino,
};
struct f_inst rej = {
.fi_code = FI_PRINT_AND_DIE,
.a = { { .p = NULL }, { .i = F_REJECT } },
.lineno = ifs->lino,
};
struct f_inst i = {
.fi_code = FI_CONDITION,
.a = { { .p = $2 }, { .p = &acc }, { .p = &rej } },
.lineno = ifs->lino,
};
f->name = NULL;
f->root = f_postfixify(&i);
$$ = f;
$$ = f_new_where($2);
}
;
function_params:
'(' declsn ')' { DBG( "Have function parameters\n" ); $$=$2; }
| '(' ')' { $$=NULL; }
'(' declsn ')' { $$ = $2; }
| '(' ')' { $$.inst = NULL; $$.count = 0; }
;
function_body:
decls '{' cmds '}' {
if ($1) {
/* Prepend instruction to clear local variables */
$$ = f_new_inst(FI_CLEAR_LOCAL_VARS);
$$->a[0].p = $1;
$$->next = $3;
} else
$$ = $3;
$$[0] = $1 ? f_clear_local_vars($1) : NULL;
$$[1] = $3;
}
;
@ -601,10 +577,26 @@ function_def:
$2 = cf_define_symbol($2, SYM_FUNCTION, NULL);
cf_push_scope($2);
} function_params function_body {
struct f_inst *vc = f_new_inst(FI_CONSTANT);
vc->val = (struct f_val) { .type = T_VOID };
$2->def = f_postfixify_concat($5, vc, NULL);
$2->aux2 = $4;
const struct f_inst *catlist[4];
uint count = 0;
/* Argument setters */
if ($4.inst)
catlist[count++] = $4.inst;
/* Local var clearers */
if ($5[0])
catlist[count++] = $5[0];
/* Return void if no return is needed */
catlist[count++] = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_VOID });
/* Function body itself */
if ($5[1])
catlist[count++] = $5[1];
$2->def = f_postfixify_concat(catlist, count);
$2->aux2 = $4.count;
DBG("Hmm, we've got one function here - %s\n", $2->name);
cf_pop_scope();
}
@ -612,15 +604,12 @@ function_def:
/* Programs */
/* Hack: $$ of cmds_int is the last node.
$$->next of cmds_int is temporary used for the first node */
cmds: /* EMPTY */ { $$ = NULL; }
| cmds_int { $$ = $1->next; $1->next = NULL; }
| cmds_int { $$ = $1[0]; }
;
cmds_int: cmd { $$ = $1; $1->next = $1; }
| cmds_int cmd { $$ = $2; $2->next = $1->next ; $1->next = $2; }
cmds_int: cmd { $$[0] = $$[1] = $1; }
| cmds_int cmd { $$[1] = $2; f_inst_next($1[1], $2); $$[0] = $1[0]; }
;
block:
@ -784,37 +773,36 @@ bgp_path:
;
bgp_path_tail:
NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT); $$->next = $2; $$->val = (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .asn = $1, .kind = PM_ASN, }, }; }
| NUM DDOT NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT); $$->next = $4; $$->val = (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .from = $1, .to = $3, .kind = PM_ASN_RANGE }, }; }
| '*' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT); $$->next = $2; $$->val = (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_ASTERISK }, }; }
| '?' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT); $$->next = $2; $$->val = (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_QUESTION }, }; }
| bgp_path_expr bgp_path_tail { $$ = $1; $$->next = $2; }
NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .asn = $1, .kind = PM_ASN, }, }); f_inst_next($$, $2); }
| NUM DDOT NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .from = $1, .to = $3, .kind = PM_ASN_RANGE }, }); f_inst_next($$, $4); }
| '*' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_ASTERISK }, }); f_inst_next($$, $2); }
| '?' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_QUESTION }, }); f_inst_next($$, $2); }
| bgp_path_expr bgp_path_tail { $$ = $1; f_inst_next($$, $2); }
| { $$ = NULL; }
;
constant:
NUM { $$ = f_new_inst(FI_CONSTANT); $$->val = (struct f_val) { .type = T_INT, .val.i = $1, }; }
| TRUE { $$ = f_new_inst(FI_CONSTANT); $$->val = (struct f_val) { .type = T_BOOL, .val.i = 1, }; }
| FALSE { $$ = f_new_inst(FI_CONSTANT); $$->val = (struct f_val) { .type = T_BOOL, .val.i = 0, }; }
| TEXT { $$ = f_new_inst(FI_CONSTANT); $$->val = (struct f_val) { .type = T_STRING, .val.s = $1, }; }
| fipa { $$ = f_new_inst(FI_CONSTANT); $$->val = $1; }
| VPN_RD { $$ = f_new_inst(FI_CONSTANT); $$->val = (struct f_val) { .type = T_RD, .val.ec = $1, }; }
| net_ { $$ = f_new_inst(FI_CONSTANT); $$->val = (struct f_val) { .type = T_NET, .val.net = $1, }; }
NUM { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = $1, }); }
| TRUE { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 1, }); }
| FALSE { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 0, }); }
| TEXT { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_STRING, .val.s = $1, }); }
| fipa { $$ = f_new_inst(FI_CONSTANT, $1); }
| VPN_RD { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_RD, .val.ec = $1, }); }
| net_ { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_NET, .val.net = $1, }); }
| '[' set_items ']' {
DBG( "We've got a set here..." );
$$ = f_new_inst(FI_CONSTANT);
$$->val = (struct f_val) { .type = T_SET, .val.t = build_tree($2), };
$$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_SET, .val.t = build_tree($2), });
DBG( "ook\n" );
}
| '[' fprefix_set ']' { $$ = f_new_inst(FI_CONSTANT); $$->val = (struct f_val) { .type = T_PREFIX_SET, .val.ti = $2, }; }
| ENUM { $$ = f_new_inst(FI_CONSTANT); $$->val = (struct f_val) { .type = $1 >> 16, .val.i = $1 & 0xffff, }; }
| '[' fprefix_set ']' { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PREFIX_SET, .val.ti = $2, }); }
| ENUM { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = $1 >> 16, .val.i = $1 & 0xffff, }); }
;
constructor:
'(' term ',' term ')' { $$ = f_generate_dpair($2, $4); }
| '(' ec_kind ',' term ',' term ')' { $$ = f_generate_ec($2, $4, $6); }
| '(' term ',' term ',' term ')' { $$ = f_generate_lc($2, $4, $6); }
| bgp_path { $$ = f_generate_path_mask($1); }
'(' term ',' term ')' { $$ = f_new_inst(FI_PAIR_CONSTRUCT, $2, $4); }
| '(' ec_kind ',' term ',' term ')' { $$ = f_new_inst(FI_EC_CONSTRUCT, $4, $6, $2); }
| '(' term ',' term ',' term ')' { $$ = f_new_inst(FI_LC_CONSTRUCT, $2, $4, $6); }
| bgp_path { $$ = f_new_inst(FI_PATHMASK_CONSTRUCT, $1, 0); }
;
@ -823,23 +811,7 @@ rtadot: /* EMPTY, we are not permitted RTA. prefix */
function_call:
SYM '(' var_list ')' {
struct symbol *sym;
struct f_inst *inst = $3;
if ($1->class != SYM_FUNCTION)
cf_error("You can't call something which is not a function. Really.");
DBG("You are calling function %s\n", $1->name);
$$ = f_new_inst(FI_CALL);
$$->a[0].p = inst;
$$->a[1].p = $1->def;
sym = $1->aux2;
while (sym || inst) {
if (!sym || !inst)
cf_error("Wrong number of arguments for function %s.", $1->name);
DBG( "You should pass parameter called %s\n", sym->name);
inst->a[0].p = sym;
sym = sym->aux2;
inst = inst->next;
}
$$ = f_new_inst(FI_CALL, $1, $3);
}
;
@ -847,15 +819,13 @@ symbol:
SYM {
switch ($1->class & 0xffff) {
case SYM_CONSTANT_RANGE:
$$ = f_new_inst(FI_CONSTANT_INDIRECT);
$$->a[0].p = $1;
$$ = f_new_inst(FI_CONSTANT_INDIRECT, $1->def);
break;
case SYM_VARIABLE_RANGE:
$$ = f_new_inst(FI_VARIABLE);
$$->a[0].p = $1;
$$ = f_new_inst(FI_VARIABLE, $1);
break;
case SYM_ATTRIBUTE:
$$ = f_new_inst_da(FI_EA_GET, *((struct f_dynamic_attr *) $1->def));
$$ = f_new_inst(FI_EA_GET, *((struct f_dynamic_attr *) $1->def));
break;
default:
cf_error("%s: variable expected.", $1->name);
@ -876,22 +846,22 @@ static_attr:
term:
'(' term ')' { $$ = $2; }
| term '+' term { $$ = f_new_inst(FI_ADD); $$->a[0].p = $1; $$->a[1].p = $3; }
| term '-' term { $$ = f_new_inst(FI_SUBTRACT); $$->a[0].p = $1; $$->a[1].p = $3; }
| term '*' term { $$ = f_new_inst(FI_MULTIPLY); $$->a[0].p = $1; $$->a[1].p = $3; }
| term '/' term { $$ = f_new_inst(FI_DIVIDE); $$->a[0].p = $1; $$->a[1].p = $3; }
| term AND term { $$ = f_new_inst(FI_AND); $$->a[0].p = $1; $$->a[1].p = $3; }
| term OR term { $$ = f_new_inst(FI_OR); $$->a[0].p = $1; $$->a[1].p = $3; }
| term '=' term { $$ = f_new_inst(FI_EQ); $$->a[0].p = $1; $$->a[1].p = $3; }
| term NEQ term { $$ = f_new_inst(FI_NEQ); $$->a[0].p = $1; $$->a[1].p = $3; }
| term '<' term { $$ = f_new_inst(FI_LT); $$->a[0].p = $1; $$->a[1].p = $3; }
| term LEQ term { $$ = f_new_inst(FI_LTE); $$->a[0].p = $1; $$->a[1].p = $3; }
| term '>' term { $$ = f_new_inst(FI_LT); $$->a[0].p = $3; $$->a[1].p = $1; }
| term GEQ term { $$ = f_new_inst(FI_LTE); $$->a[0].p = $3; $$->a[1].p = $1; }
| term '~' term { $$ = f_new_inst(FI_MATCH); $$->a[0].p = $1; $$->a[1].p = $3; }
| term NMA term { $$ = f_new_inst(FI_NOT_MATCH);$$->a[0].p = $1; $$->a[1].p = $3; }
| '!' term { $$ = f_new_inst(FI_NOT); $$->a[0].p = $2; }
| DEFINED '(' term ')' { $$ = f_new_inst(FI_DEFINED); $$->a[0].p = $3; }
| term '+' term { $$ = f_new_inst(FI_ADD, $1, $3); }
| term '-' term { $$ = f_new_inst(FI_SUBTRACT, $1, $3); }
| term '*' term { $$ = f_new_inst(FI_MULTIPLY, $1, $3); }
| term '/' term { $$ = f_new_inst(FI_DIVIDE, $1, $3); }
| term AND term { $$ = f_new_inst(FI_AND, $1, $3); }
| term OR term { $$ = f_new_inst(FI_OR, $1, $3); }
| term '=' term { $$ = f_new_inst(FI_EQ, $1, $3); }
| term NEQ term { $$ = f_new_inst(FI_NEQ, $1, $3); }
| term '<' term { $$ = f_new_inst(FI_LT, $1, $3); }
| term LEQ term { $$ = f_new_inst(FI_LTE, $1, $3); }
| term '>' term { $$ = f_new_inst(FI_LT, $3, $1); }
| term GEQ term { $$ = f_new_inst(FI_LTE, $3, $1); }
| term '~' term { $$ = f_new_inst(FI_MATCH, $1, $3); }
| term NMA term { $$ = f_new_inst(FI_NOT_MATCH, $1, $3); }
| '!' term { $$ = f_new_inst(FI_NOT, $2); }
| DEFINED '(' term ')' { $$ = f_new_inst(FI_DEFINED, $3); }
| symbol { $$ = $1; }
| constant { $$ = $1; }
@ -899,22 +869,22 @@ term:
| PREFERENCE { $$ = f_new_inst(FI_PREF_GET); }
| rtadot static_attr { $$ = f_new_inst_sa(FI_RTA_GET, $2); }
| rtadot static_attr { $$ = f_new_inst(FI_RTA_GET, $2); }
| rtadot dynamic_attr { $$ = f_new_inst_da(FI_EA_GET, $2); }
| rtadot dynamic_attr { $$ = f_new_inst(FI_EA_GET, $2); }
| term '.' IS_V4 { $$ = f_new_inst(FI_IS_V4); $$->a[0].p = $1; }
| term '.' TYPE { $$ = f_new_inst(FI_TYPE); $$->a[0].p = $1; }
| term '.' IP { $$ = f_new_inst(FI_IP); $$->a[0].p = $1; $$->aux = T_IP; }
| term '.' RD { $$ = f_new_inst(FI_ROUTE_DISTINGUISHER); $$->a[0].p = $1; $$->aux = T_RD; }
| term '.' LEN { $$ = f_new_inst(FI_LENGTH); $$->a[0].p = $1; }
| term '.' MAXLEN { $$ = f_new_inst(FI_ROA_MAXLEN); $$->a[0].p = $1; }
| term '.' ASN { $$ = f_new_inst(FI_ROA_ASN); $$->a[0].p = $1; }
| term '.' SRC { $$ = f_new_inst(FI_SADR_SRC); $$->a[0].p = $1; }
| term '.' MASK '(' term ')' { $$ = f_new_inst(FI_IP_MASK); $$->a[0].p = $1; $$->a[1].p = $5; }
| term '.' FIRST { $$ = f_new_inst(FI_AS_PATH_FIRST); $$->a[0].p = $1; }
| term '.' LAST { $$ = f_new_inst(FI_AS_PATH_LAST); $$->a[0].p = $1; }
| term '.' LAST_NONAGGREGATED { $$ = f_new_inst(FI_AS_PATH_LAST_NAG); $$->a[0].p = $1; }
| term '.' IS_V4 { $$ = f_new_inst(FI_IS_V4, $1); }
| term '.' TYPE { $$ = f_new_inst(FI_TYPE, $1); }
| term '.' IP { $$ = f_new_inst(FI_IP, $1); }
| term '.' RD { $$ = f_new_inst(FI_ROUTE_DISTINGUISHER, $1); }
| term '.' LEN { $$ = f_new_inst(FI_LENGTH, $1); }
| term '.' MAXLEN { $$ = f_new_inst(FI_ROA_MAXLEN, $1); }
| term '.' ASN { $$ = f_new_inst(FI_ROA_ASN, $1); }
| term '.' SRC { $$ = f_new_inst(FI_SADR_SRC, $1); }
| term '.' MASK '(' term ')' { $$ = f_new_inst(FI_IP_MASK, $1, $5); }
| term '.' FIRST { $$ = f_new_inst(FI_AS_PATH_FIRST, $1); }
| term '.' LAST { $$ = f_new_inst(FI_AS_PATH_LAST, $1); }
| term '.' LAST_NONAGGREGATED { $$ = f_new_inst(FI_AS_PATH_LAST_NAG, $1); }
/* Communities */
/* This causes one shift/reduce conflict
@ -924,19 +894,19 @@ term:
| rtadot dynamic_attr '.' RESET{ }
*/
| '+' EMPTY '+' { $$ = f_new_inst(FI_CONSTANT); $$->val = f_const_empty_path; }
| '-' EMPTY '-' { $$ = f_new_inst(FI_CONSTANT); $$->val = f_const_empty_clist; }
| '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_CONSTANT); $$->val = f_const_empty_eclist; }
| '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_CONSTANT); $$->val = f_const_empty_lclist; }
| PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND); $$->a[0].p = $3; $$->a[1].p = $5; }
| ADD '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD); $$->a[0].p = $3; $$->a[1].p = $5; }
| DELETE '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_DEL); $$->a[0].p = $3; $$->a[1].p = $5; }
| FILTER '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_FILTER); $$->a[0].p = $3; $$->a[1].p = $5; }
| '+' EMPTY '+' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_path); }
| '-' EMPTY '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_clist); }
| '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_eclist); }
| '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_lclist); }
| PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND, $3, $5); }
| ADD '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD, $3, $5); }
| DELETE '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_DEL, $3, $5); }
| FILTER '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_FILTER, $3, $5); }
| ROA_CHECK '(' rtable ')' { $$ = f_new_inst(FI_ROA_CHECK_IMPLICIT); $$->a[0].rtc = $3; }
| ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK_EXPLICIT); $$->a[2].rtc = $3; $$->a[0].p = $5; $$->a[1].p = $7; }
| ROA_CHECK '(' rtable ')' { $$ = f_new_inst(FI_ROA_CHECK_IMPLICIT, $3); }
| ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK_EXPLICIT, $5, $7, $3); }
| FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT); $$->a[0].p = $3; }
| FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT, $3); }
/* | term '.' LEN { $$->code = P('P','l'); } */
@ -953,30 +923,25 @@ break_command:
;
print_one:
term { $$ = f_new_inst(FI_PRINT); $$->a[0].p = $1; $$->a[1].p = NULL; }
term { $$ = f_new_inst(FI_PRINT, $1); }
;
print_list: /* EMPTY */ { $$ = NULL; }
| print_one { $$ = $1; }
| print_one ',' print_list {
if ($1) {
$1->next = $3;
f_inst_next($1, $3);
$$ = $1;
} else $$ = $3;
}
;
var_listn: term {
$$ = f_new_inst(FI_SET);
$$->a[0].p = NULL;
$$->a[1].p = $1;
$$->next = NULL;
$$ = $1;
}
| term ',' var_listn {
$$ = f_new_inst(FI_SET);
$$->a[0].p = NULL;
$$->a[1].p = $1;
$$->next = $3;
$$ = $1;
f_inst_next($$, $3);
}
;
@ -986,58 +951,42 @@ var_list: /* EMPTY */ { $$ = NULL; }
cmd:
IF term THEN block {
$$ = f_new_inst(FI_CONDITION);
$$->a[0].p = $2;
$$->a[1].p = $4;
$$ = f_new_inst(FI_CONDITION, $2, $4, NULL);
}
| IF term THEN block ELSE block {
$$ = f_new_inst(FI_CONDITION);
$$->a[0].p = $2;
$$->a[1].p = $4;
$$->a[2].p = $6;
$$ = 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_da(FI_EA_SET, *((struct f_dynamic_attr *) $1->def));
$$->a[0].p = $3;
$$ = 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);
$$->a[0].p = $1;
$$->a[1].p = $3;
$$ = f_new_inst(FI_SET, $3, $1);
} else
cf_error( "Symbol `%s' is read-only.", $1->name );
}
| RETURN term ';' {
DBG( "Ook, we'll return the value\n" );
$$ = f_new_inst(FI_RETURN);
$$->a[0].p = $2;
$$ = f_new_inst(FI_RETURN, $2);
}
| rtadot dynamic_attr '=' term ';' {
$$ = f_new_inst_da(FI_EA_SET, $2);
$$->a[0].p = $4;
$$ = f_new_inst(FI_EA_SET, $2, $4);
}
| rtadot static_attr '=' term ';' {
$$ = f_new_inst_sa(FI_RTA_SET, $2);
if ($$->sa.readonly)
if ($2.readonly)
cf_error( "This static attribute is read-only.");
$$->a[0].p = $4;
$$ = f_new_inst(FI_RTA_SET, $2, $4);
}
| PREFERENCE '=' term ';' {
$$ = f_new_inst(FI_PREF_SET);
$$->a[0].p = $3;
$$ = f_new_inst(FI_PREF_SET, $3);
}
| UNSET '(' rtadot dynamic_attr ')' ';' {
$$ = f_new_inst_da(FI_EA_SET, $4);
$$->aux = EAF_TYPE_UNDEF | EAF_TEMP;
$$->a[0].p = NULL;
$$ = f_new_inst(FI_EA_UNSET, $4);
}
| break_command print_list ';' { $$ = f_new_inst(FI_PRINT_AND_DIE); $$->a[0].p = $2; $$->a[1].i = $1; }
| function_call ';' { $$ = f_new_inst(FI_DROP_RESULT); $$->a[0].p = $1; }
| break_command print_list ';' { $$ = f_new_inst(FI_PRINT_AND_DIE, $2, $1); }
| function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); }
| CASE term '{' switch_body '}' {
$$ = f_new_inst(FI_SWITCH);
$$->a[0].p = $2;
$$->a[1].p = build_tree( $4 );
$$ = f_new_inst(FI_SWITCH, $2, build_tree($4));
}
| rtadot dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($2); }

View file

@ -17,9 +17,9 @@ m4_divert(-1)')
m4_define(LINEP, `LINE($@)')
m4_define(SYMBOL, `m4_divert(1)debug("%ssymbol %s\n", INDENT, item->sym->name);
m4_divert(-1)')
m4_define(VALI, `m4_divert(1)debug("%svalue %s\n", INDENT, val_dump(item->vp));
m4_define(VALI, `m4_divert(1)debug("%svalue %s\n", INDENT, val_dump(&item->val));
m4_divert(-1)')
m4_define(VALI, `m4_divert(1)debug("%svalue %s\n", INDENT, val_dump(item->vp));
m4_define(VAR, `m4_divert(1)debug("%svar %s: value %s\n", INDENT, item->sym->name, val_dump(item->vp));
m4_divert(-1)')
m4_define(FRET, `m4_divert(1)debug("%sfilter return value %d\n", INDENT, item->fret);
m4_divert(-1)')

View file

@ -108,6 +108,17 @@
INST(FI_PATHMASK_CONSTRUCT, 0, 1) {
ARG_ANY(1);
COUNT(2);
NEW([[]], [[
uint len = 0;
uint dyn = 0;
for (const struct f_inst *tt = f1; tt; tt = tt->next, len++)
if (tt->fi_code != FI_CONSTANT)
dyn++;
WHAT().count = len;
]]);
if (vstk.cnt < what->count) /* TODO: make this check systematic */
runtime("Construction of BGP path mask from %u elements must have at least that number of elements", what->count);
@ -239,13 +250,11 @@
RESULT_OK;
}
INST(FI_VARIABLE, 0, 1) {
VALP(1); // res = * ((struct f_val *) what->a[0].p);
SAME([[if (strcmp(f1->sym->name, f2->sym->name)) return 0; ]]);
VAR;
RESULT_OK;
}
INST(FI_CONSTANT_INDIRECT, 0, 1) {
VALP(1);
SAME([[if (!val_same(f1->vp, f2->vp)) return 0; ]]);
VALP;
RESULT_OK;
}
INST(FI_PRINT, 1, 0) {
@ -261,15 +270,16 @@
}
INST(FI_PRINT_AND_DIE, 0, 0) {
POSTFIXIFY([[
if (what->a[0].p) {
pos = postfixify(dest, what->a[0].p, pos);
dest->items[pos].flags |= FIF_PRINTED;
}
{
uint opos = pos;
]]);
LINE_SIZE([[
if (what->a[0].p) {
cnt += inst_line_size(what->a[0].p);
}
ARG_ANY(1);
POSTFIXIFY([[
if (opos < pos)
dest->items[pos].flags |= FIF_PRINTED;
}
]]);
FRET(2);
@ -544,11 +554,6 @@
runtime( "Setting lclist attribute to non-lclist value" );
l->attrs[0].u.ptr = v1.val.ad;
break;
case EAF_TYPE_UNDEF:
if (v1.type != T_VOID)
runtime( "Setting void attribute to non-void value" );
l->attrs[0].u.data = 0;
break;
default: bug("Unknown type in e,S");
}
@ -558,6 +563,28 @@
}
}
INST(FI_EA_UNSET, 0, 0) {
DYNAMIC_ATTR;
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 = EAF_TYPE_UNDEF | EAF_TEMP | EAF_ORIGINATED | EAF_FRESH;
l->attrs[0].u.data = 0;
f_rta_cow(fs);
l->next = *fs->eattrs;
*fs->eattrs = l;
}
}
INST(FI_PREF_GET, 0, 1) {
ACCESS_RTE;
RESULT(T_INT, i, (*fs->rte)->pref);
@ -660,6 +687,7 @@
if (!estk.cnt)
if (vstk.val[retpos].type == T_BOOL)
if (vstk.val[retpos].val.i)
return F_ACCEPT;
else
return F_REJECT;
@ -674,28 +702,43 @@
}
INST(FI_CALL, 0, 1) {
/* First push the code */
LINEP(2,0);
/* Do not use the symbol on execution */
if (0) {
UNUSED SYMBOL;
}
/* Postfixify extracts the function body from the symbol */
POSTFIXIFY([[
dest->items[pos].lines[0] = what->sym->def;
]]);
/* First push the body on stack */
LINEX(what->lines[0]);
curline.emask |= FE_RETURN;
/* Then push the arguments */
LINE(1,1);
NEW([[]], [[
if (sym->class != SYM_FUNCTION)
cf_error("You can't call something which is not a function. Really.");
uint count = 0;
for (const struct f_inst *inst = f1; inst; inst = inst->next)
count++;
if (count != sym->aux2)
cf_error("Function %s takes %u arguments, got %u.", sym->name, sym->aux2, count);
]]);
}
INST(FI_DROP_RESULT, 1, 0) {
ARG_ANY(1);
}
INST(FI_CLEAR_LOCAL_VARS, 0, 0) { /* Clear local variables */
SYMBOL(1);
for ( ; sym != NULL; sym = sym->aux2)
((struct f_val *) sym->def)->type = T_VOID;
}
INST(FI_SWITCH, 1, 0) {
ARG_ANY(1);
POSTFIXIFY([[
dest->items[pos].tree = what->a[1].p;
]]);
TREE;
const struct f_tree *t = find_tree(what->tree, &v1);
if (!t) {
v1.type = T_VOID;
@ -940,8 +983,6 @@
INST(FI_ASSERT, 1, 0) { /* Birdtest Assert */
ARG(1, T_BOOL);
POSTFIXIFY([[
dest->items[pos].s = what->a[1].p;
]]);
STRING;
CALL(bt_assert_hook, res.val.i, what);
}

124
filter/f-inst.h Normal file
View file

@ -0,0 +1,124 @@
/*
* BIRD Internet Routing Daemon -- Filter instructions
*
* (c) 2018--2019 Maria Matejka <mq@jmq.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
/* Filter instruction words */
#define FI__TWOCHAR(a,b) ((a<<8) | b)
#define FI__LIST \
F(FI_NOP, 0, '0') \
F(FI_ADD, 0, '+') \
F(FI_SUBTRACT, 0, '-') \
F(FI_MULTIPLY, 0, '*') \
F(FI_DIVIDE, 0, '/') \
F(FI_AND, 0, '&') \
F(FI_OR, 0, '|') \
F(FI_PAIR_CONSTRUCT, 'm', 'p') \
F(FI_EC_CONSTRUCT, 'm', 'c') \
F(FI_LC_CONSTRUCT, 'm', 'l') \
F(FI_PATHMASK_CONSTRUCT, 'm', 'P') \
F(FI_NEQ, '!', '=') \
F(FI_EQ, '=', '=') \
F(FI_LT, 0, '<') \
F(FI_LTE, '<', '=') \
F(FI_NOT, 0, '!') \
F(FI_MATCH, 0, '~') \
F(FI_NOT_MATCH, '!', '~') \
F(FI_DEFINED, 'd', 'e') \
F(FI_TYPE, 0, 'T') \
F(FI_IS_V4, 'I', 'i') \
F(FI_SET, 0, 's') \
F(FI_CONSTANT, 0, 'c') \
F(FI_VARIABLE, 0, 'V') \
F(FI_CONSTANT_INDIRECT, 0, 'C') \
F(FI_PRINT, 0, 'p') \
F(FI_CONDITION, 0, '?') \
F(FI_PRINT_AND_DIE, 'p', ',') \
F(FI_RTA_GET, 0, 'a') \
F(FI_RTA_SET, 'a', 'S') \
F(FI_EA_GET, 'e', 'a') \
F(FI_EA_SET, 'e', 'S') \
F(FI_PREF_GET, 0, 'P') \
F(FI_PREF_SET, 'P', 'S') \
F(FI_LENGTH, 0, 'L') \
F(FI_ROA_MAXLEN, 'R', 'M') \
F(FI_ROA_ASN, 'R', 'A') \
F(FI_SADR_SRC, 'n', 's') \
F(FI_IP, 'c', 'p') \
F(FI_ROUTE_DISTINGUISHER, 'R', 'D') \
F(FI_AS_PATH_FIRST, 'a', 'f') \
F(FI_AS_PATH_LAST, 'a', 'l') \
F(FI_AS_PATH_LAST_NAG, 'a', 'L') \
F(FI_RETURN, 0, 'r') \
F(FI_CALL, 'c', 'a') \
F(FI_DROP_RESULT, 'd', 'r') \
F(FI_CLEAR_LOCAL_VARS, 'c', 'V') \
F(FI_SWITCH, 'S', 'W') \
F(FI_IP_MASK, 'i', 'M') \
F(FI_PATH_PREPEND, 'A', 'p') \
F(FI_CLIST_ADD, 'C', 'a') \
F(FI_CLIST_DEL, 'C', 'd') \
F(FI_CLIST_FILTER, 'C', 'f') \
F(FI_ROA_CHECK_IMPLICIT, 'R', 'i') \
F(FI_ROA_CHECK_EXPLICIT, 'R', 'e') \
F(FI_FORMAT, 0, 'F') \
F(FI_ASSERT, 'a', 's')
/* The enum itself */
enum f_instruction_code {
#define F(c,a,b) \
c,
FI__LIST
#undef F
FI__MAX,
} PACKED;
/* Convert the instruction back to the enum name */
const char *f_instruction_name(enum f_instruction_code fi);
/* Instruction structure for config */
struct f_inst {
const struct f_inst *next; /* Next instruction to be executed */
union { /* Instruction content */
struct { /* Instruction code for dispatching purposes */
enum f_instruction_code fi_code;
};
struct {
enum f_instruction_code fi_code_a;
const struct f_inst *p[3]; /* Three arguments at most */
};
struct {
struct {
enum f_instruction_code
enum f_iknst
u16 aux; /* Extension to instruction code, T_*, EA_*, EAF_* */
union {
union f_inst_attr a[3]; /* The three arguments */
struct f_val val; /* The value if FI_CONSTANT */
struct {
union f_inst_attr sa_a[1];
struct f_static_attr sa; /* Static attribute def for FI_RTA_* */
};
struct {
union f_inst_attr da_a[1];
struct f_dynamic_attr da; /* Dynamic attribute def for FI_EA_* */
};
};
int lineno;
};

View file

@ -10,57 +10,15 @@
#include "nest/bird.h"
#include "conf/conf.h"
#include "filter/filter.h"
#include "filter/f-inst-struct.h"
#include "lib/idm.h"
#include "nest/protocol.h"
#include "nest/route.h"
#define P(a,b) ((a<<8) | b)
struct f_inst *
f_new_inst(enum f_instruction_code fi_code)
{
struct f_inst * ret;
ret = cfg_allocz(sizeof(struct f_inst));
ret->fi_code = fi_code;
ret->lineno = ifs->lino;
return ret;
}
struct f_inst *
f_new_inst_da(enum f_instruction_code fi_code, struct f_dynamic_attr da)
{
struct f_inst *ret = f_new_inst(fi_code);
ret->da = da;
return ret;
}
struct f_inst *
f_new_inst_sa(enum f_instruction_code fi_code, struct f_static_attr sa)
{
struct f_inst *ret = f_new_inst(fi_code);
ret->sa = sa;
return ret;
}
/*
* Generate set_dynamic( operation( get_dynamic(), argument ) )
*/
struct f_inst *
f_generate_complex(enum f_instruction_code fi_code, struct f_dynamic_attr da, struct f_inst *argument)
{
struct f_inst *set_dyn = f_new_inst_da(FI_EA_SET, da),
*oper = f_new_inst(fi_code),
*get_dyn = f_new_inst_da(FI_EA_GET, da);
oper->a[0].p = get_dyn;
oper->a[1].p = argument;
set_dyn->a[0].p = oper;
return set_dyn;
}
static const char * const f_instruction_name_str[] = {
#define F(c,a,b) \
#define F(c,...) \
[c] = #c,
FI__LIST
#undef F
@ -88,6 +46,58 @@ filter_name(struct filter *filter)
return filter->name;
}
void f_inst_next(struct f_inst *first, const struct f_inst *append)
{
first->next = append;
}
struct filter *f_new_where(const struct f_inst *where)
{
struct f_inst acc = {
.fi_code = FI_PRINT_AND_DIE,
.lineno = ifs->lino,
.i_FI_PRINT_AND_DIE = { .fret = F_ACCEPT, },
};
struct f_inst rej = {
.fi_code = FI_PRINT_AND_DIE,
.lineno = ifs->lino,
.i_FI_PRINT_AND_DIE = { .fret = F_REJECT, },
};
struct f_inst i = {
.fi_code = FI_CONDITION,
.lineno = ifs->lino,
.i_FI_CONDITION = {
.f1 = where,
.f2 = &acc,
.f3 = &rej,
},
};
struct filter *f = cfg_alloc(sizeof(struct filter));
f->name = NULL;
f->root = f_postfixify(&i);
return f;
}
struct f_inst *f_clear_local_vars(struct f_inst *decls)
{
/* Prepend instructions to clear local variables */
struct f_inst *head = NULL;
for (const struct f_inst *si = decls; si; si = si->next) {
struct f_inst *cur = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_VOID });
if (head)
f_inst_next(cur, head);
else
f_inst_next(cur, si);
head = cur; /* The first FI_CONSTANT put there */
}
return head;
}
#define CA_KEY(n) n->name, n->fda.type
#define CA_NEXT(n) n->next
#define CA_EQ(na,ta,nb,tb) (!strcmp(na,nb) && (ta == tb))

View file

@ -47,6 +47,7 @@
#include "nest/attrs.h"
#include "conf/conf.h"
#include "filter/filter.h"
#include "filter/f-inst-struct.h"
#define CMP_ERROR 999
@ -614,11 +615,11 @@ val_format_str(struct filter_state *fs, struct f_val *v) {
static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS;
static uint
inst_line_size(const struct f_inst *what)
inst_line_size(const struct f_inst *what_)
{
uint cnt = 0;
for ( ; what; what = what->next) {
switch (what->fi_code) {
for ( ; what_; what_ = what_->next) {
switch (what_->fi_code) {
#include "filter/f-inst-line-size.c"
}
}
@ -671,10 +672,10 @@ f_dump_line(const struct f_line *dest, int indent)
#endif
static uint
postfixify(struct f_line *dest, const struct f_inst *what, uint pos)
postfixify(struct f_line *dest, const struct f_inst *what_, uint pos)
{
for ( ; what; what = what->next) {
switch (what->fi_code) {
for ( ; what_; what_ = what_->next) {
switch (what_->fi_code) {
#include "filter/f-inst-postfixify.c"
}
pos++;
@ -683,23 +684,16 @@ postfixify(struct f_line *dest, const struct f_inst *what, uint pos)
}
struct f_line *
f_postfixify_concat(struct f_inst *first, ...)
f_postfixify_concat(const struct f_inst * const inst[], uint count)
{
va_list args;
va_list argd;
va_start(args, first);
va_copy(argd, args);
uint len = 0;
for (struct f_inst *what = first; what; what = va_arg(args, struct f_inst *))
len += inst_line_size(what);
va_end(args);
for (uint i=0; i<count; i++)
len += inst_line_size(inst[i]);
struct f_line *out = cfg_allocz(sizeof(struct f_line) + sizeof(struct f_line_item)*len);
for (struct f_inst *what = first; what; what = va_arg(argd, struct f_inst *))
out->len = postfixify(out, what, out->len);
for (uint i=0; i<count; i++)
out->len = postfixify(out, inst[i], out->len);
f_dump_line(out, 0);
return out;

View file

@ -2,6 +2,7 @@
* BIRD Internet Routing Daemon -- Filters
*
* (c) 1999 Pavel Machek <pavel@ucw.cz>
* (c) 2018--2019 Maria Matejka <mq@jmq.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
@ -11,6 +12,7 @@
#include "lib/resource.h"
#include "lib/ip.h"
#include "lib/macro.h"
#include "nest/route.h"
#include "nest/attrs.h"
@ -115,109 +117,6 @@ struct f_static_attr {
int readonly:1; /* Don't allow writing */
};
/* Filter instruction words */
#define FI__TWOCHAR(a,b) ((a<<8) | b)
#define FI__LIST \
F(FI_NOP, 0, '0') \
F(FI_ADD, 0, '+') \
F(FI_SUBTRACT, 0, '-') \
F(FI_MULTIPLY, 0, '*') \
F(FI_DIVIDE, 0, '/') \
F(FI_AND, 0, '&') \
F(FI_OR, 0, '|') \
F(FI_PAIR_CONSTRUCT, 'm', 'p') \
F(FI_EC_CONSTRUCT, 'm', 'c') \
F(FI_LC_CONSTRUCT, 'm', 'l') \
F(FI_PATHMASK_CONSTRUCT, 'm', 'P') \
F(FI_NEQ, '!', '=') \
F(FI_EQ, '=', '=') \
F(FI_LT, 0, '<') \
F(FI_LTE, '<', '=') \
F(FI_NOT, 0, '!') \
F(FI_MATCH, 0, '~') \
F(FI_NOT_MATCH, '!', '~') \
F(FI_DEFINED, 'd', 'e') \
F(FI_TYPE, 0, 'T') \
F(FI_IS_V4, 'I', 'i') \
F(FI_SET, 0, 's') \
F(FI_CONSTANT, 0, 'c') \
F(FI_VARIABLE, 0, 'V') \
F(FI_CONSTANT_INDIRECT, 0, 'C') \
F(FI_PRINT, 0, 'p') \
F(FI_CONDITION, 0, '?') \
F(FI_PRINT_AND_DIE, 'p', ',') \
F(FI_RTA_GET, 0, 'a') \
F(FI_RTA_SET, 'a', 'S') \
F(FI_EA_GET, 'e', 'a') \
F(FI_EA_SET, 'e', 'S') \
F(FI_PREF_GET, 0, 'P') \
F(FI_PREF_SET, 'P', 'S') \
F(FI_LENGTH, 0, 'L') \
F(FI_ROA_MAXLEN, 'R', 'M') \
F(FI_ROA_ASN, 'R', 'A') \
F(FI_SADR_SRC, 'n', 's') \
F(FI_IP, 'c', 'p') \
F(FI_ROUTE_DISTINGUISHER, 'R', 'D') \
F(FI_AS_PATH_FIRST, 'a', 'f') \
F(FI_AS_PATH_LAST, 'a', 'l') \
F(FI_AS_PATH_LAST_NAG, 'a', 'L') \
F(FI_RETURN, 0, 'r') \
F(FI_CALL, 'c', 'a') \
F(FI_DROP_RESULT, 'd', 'r') \
F(FI_CLEAR_LOCAL_VARS, 'c', 'V') \
F(FI_SWITCH, 'S', 'W') \
F(FI_IP_MASK, 'i', 'M') \
F(FI_PATH_PREPEND, 'A', 'p') \
F(FI_CLIST_ADD, 'C', 'a') \
F(FI_CLIST_DEL, 'C', 'd') \
F(FI_CLIST_FILTER, 'C', 'f') \
F(FI_ROA_CHECK_IMPLICIT, 'R', 'i') \
F(FI_ROA_CHECK_EXPLICIT, 'R', 'e') \
F(FI_FORMAT, 0, 'F') \
F(FI_ASSERT, 'a', 's')
/* The enum itself */
enum f_instruction_code {
#define F(c,a,b) \
c,
FI__LIST
#undef F
FI__MAX,
} PACKED;
/* Convert the instruction back to the enum name */
const char *f_instruction_name(enum f_instruction_code fi);
enum f_instruction_flags {
FIF_PRINTED = 1, /* FI_PRINT_AND_DIE: message put in buffer */
};
union f_inst_attr {
uint i;
void *p;
struct rtable_config *rtc;
};
/* Instruction structure for config */
struct f_inst {
struct f_inst *next; /* Next instruction to be executed */
enum f_instruction_code fi_code; /* The instruction itself */
u16 aux; /* Extension to instruction code, T_*, EA_*, EAF_* */
union {
union f_inst_attr a[3]; /* The three arguments */
struct f_val val; /* The value if FI_CONSTANT */
struct {
union f_inst_attr sa_a[1];
struct f_static_attr sa; /* Static attribute def for FI_RTA_* */
};
struct {
union f_inst_attr da_a[1];
struct f_dynamic_attr da; /* Dynamic attribute def for FI_EA_* */
};
};
int lineno;
};
/* Possible return values of filter execution */
enum filter_return {
F_NOP = 0,
@ -229,6 +128,121 @@ enum filter_return {
F_QUITBIRD,
};
/* Filter instruction declarations */
#define FI__LIST \
F(FI_NOP) \
F(FI_ADD, ARG, ARG) \
F(FI_SUBTRACT, ARG, ARG) \
F(FI_MULTIPLY, ARG, ARG) \
F(FI_DIVIDE, ARG, ARG) \
F(FI_AND, ARG, LINE) \
F(FI_OR, ARG, LINE) \
F(FI_PAIR_CONSTRUCT, ARG, ARG) \
F(FI_EC_CONSTRUCT, ARG, ARG, ECS) \
F(FI_LC_CONSTRUCT, ARG, ARG, ARG) \
F(FI_PATHMASK_CONSTRUCT, ARG, COUNT) \
F(FI_NEQ, ARG, ARG) \
F(FI_EQ, ARG, ARG) \
F(FI_LT, ARG, ARG) \
F(FI_LTE, ARG, ARG) \
F(FI_NOT, ARG) \
F(FI_MATCH, ARG, ARG) \
F(FI_NOT_MATCH, ARG, ARG) \
F(FI_DEFINED, ARG) \
F(FI_TYPE, ARG) \
F(FI_IS_V4, ARG) \
F(FI_SET, ARG, SYMBOL) \
F(FI_CONSTANT, VALI) \
F(FI_VARIABLE, SYMBOL) \
F(FI_CONSTANT_INDIRECT, VALP) \
F(FI_PRINT, ARG) \
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_EA_GET, EA) \
F(FI_EA_SET, EA, ARG) \
F(FI_EA_UNSET, EA) \
F(FI_PREF_GET) \
F(FI_PREF_SET, ARG) \
F(FI_LENGTH, ARG) \
F(FI_ROA_MAXLEN, ARG) \
F(FI_ROA_ASN, ARG) \
F(FI_SADR_SRC, ARG) \
F(FI_IP, ARG) \
F(FI_ROUTE_DISTINGUISHER, ARG) \
F(FI_AS_PATH_FIRST, ARG) \
F(FI_AS_PATH_LAST, ARG) \
F(FI_AS_PATH_LAST_NAG, ARG) \
F(FI_RETURN, ARG) \
F(FI_CALL, SYMBOL, LINE) \
F(FI_DROP_RESULT, ARG) \
F(FI_SWITCH, ARG, TREE) \
F(FI_IP_MASK, ARG, ARG) \
F(FI_PATH_PREPEND, ARG, ARG) \
F(FI_CLIST_ADD, ARG, ARG) \
F(FI_CLIST_DEL, ARG, ARG) \
F(FI_CLIST_FILTER, ARG, ARG) \
F(FI_ROA_CHECK_IMPLICIT, RTC) \
F(FI_ROA_CHECK_EXPLICIT, ARG, ARG, RTC) \
F(FI_FORMAT, ARG) \
F(FI_ASSERT, ARG, STRING)
/* The enum itself */
enum f_instruction_code {
#define F(c, ...) c,
FI__LIST
#undef F
FI__MAX,
} PACKED;
/* Convert the instruction back to the enum name */
const char *f_instruction_name(enum f_instruction_code fi);
struct f_inst;
void f_inst_next(struct f_inst *first, const struct f_inst *append);
struct f_inst *f_clear_local_vars(struct f_inst *decls);
#define FIA(x) , FIA_##x
#define FIA_ARG const struct f_inst *
#define FIA_LINE const struct f_inst *
#define FIA_COUNT uint
#define FIA_SYMBOL const struct symbol *
#define FIA_VALI struct f_val
#define FIA_VALP const struct f_val *
#define FIA_FRET enum filter_return
#define FIA_ECS enum ec_subtype
#define FIA_SA struct f_static_attr
#define FIA_EA struct f_dynamic_attr
#define FIA_RTC const struct rtable_config *
#define FIA_TREE const struct f_tree *
#define FIA_STRING const char *
#define F(c, ...) \
struct f_inst *f_new_inst_##c(enum f_instruction_code MACRO_IFELSE(MACRO_ISLAST(__VA_ARGS__))()(MACRO_FOREACH(FIA, __VA_ARGS__)));
FI__LIST
#undef F
#undef FIA_ARG
#undef FIA_LINE
#undef FIA_LINEP
#undef FIA_COUNT
#undef FIA_SYMBOL
#undef FIA_VALI
#undef FIA_VALP
#undef FIA_FRET
#undef FIA_ECS
#undef FIA_SA
#undef FIA_EA
#undef FIA_RTC
#undef FIA_STRING
#undef FIA
#define f_new_inst(...) MACRO_CONCAT_AFTER(f_new_inst_, MACRO_FIRST(__VA_ARGS__))(__VA_ARGS__)
/* Flags for instructions */
enum f_instruction_flags {
FIF_PRINTED = 1, /* FI_PRINT_AND_DIE: message put in buffer */
};
/* Filter structures for execution */
struct f_line;
@ -242,6 +256,7 @@ struct f_line_item {
const struct f_val *vp;
const struct symbol *sym;
};
struct f_val val;
const struct f_line *lines[2];
enum filter_return fret;
struct f_static_attr sa;
@ -267,9 +282,9 @@ struct filter {
};
/* Convert the f_inst infix tree to the f_line structures */
struct f_line *f_postfixify_concat(struct f_inst *root, ...);
static inline struct f_line *f_postfixify(struct f_inst *root)
{ return f_postfixify_concat(root, NULL); }
struct f_line *f_postfixify_concat(const struct f_inst * const inst[], uint count);
static inline struct f_line *f_postfixify(const struct f_inst *root)
{ return f_postfixify_concat(&root, 1); }
#define F_VAL_STACK_MAX 4096
@ -295,12 +310,9 @@ struct f_exec_stack {
enum f_exception emask; /* Exception mask */
} item[F_EXEC_STACK_MAX];
uint cnt; /* Current stack size; 0 for empty */
};
struct f_inst *f_new_inst(enum f_instruction_code fi_code);
struct f_inst *f_new_inst_da(enum f_instruction_code fi_code, struct f_dynamic_attr da);
struct f_inst *f_new_inst_sa(enum f_instruction_code fi_code, struct f_static_attr sa);
struct filter *f_new_where(const struct f_inst *);
static inline struct f_dynamic_attr f_new_dynamic_attr(u8 type, u8 bit, enum f_type f_type, uint code) /* Type as core knows it, type as filters know it, and code of dynamic attribute */
{ 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)

View file

@ -45,17 +45,22 @@ m4_define(ARG_ANY, `')
m4_define(SYMBOL, `const struct symbol *sym = what->sym')
m4_define(VALI, `res = *what->vp')
m4_define(VALP, `res = *what->vp')
m4_define(VALI, `res = what->val')
m4_define(VALP, `res = what->val')
m4_define(VAR, `res = *what->vp')
m4_define(FRET, `enum filter_return fret = what->fret')
m4_define(ECS, `enum ec_subtype ecs = what->ecs')
m4_define(RTC, `struct rtable *table = what->rtc->table')
m4_define(STATIC_ATTR, `struct f_static_attr sa = what->sa')
m4_define(DYNAMIC_ATTR, `struct f_dynamic_attr da = what->da')
m4_define(TREE, `')
m4_define(STRING, `')
m4_define(COUNT, `')
m4_define(POSTFIXIFY, `')
m4_define(LINE_SIZE, `')
m4_define(SAME, `')
m4_define(COUNT, `')
m4_define(STRUCT, `')
m4_define(NEW, `')
m4_m4wrap(`
m4_divert(0)DNL

View file

@ -10,13 +10,16 @@ m4_divert(-1)m4_dnl
# Common aliases
m4_define(DNL, `m4_dnl')
m4_define(INST, `m4_divert(1)break; case $1: cnt += 1;
m4_define(INST, `m4_divert(1)
#undef what
break; case $1: cnt += 1;
#define what ((const struct f_inst_$1 *) &(what_->i_$1))
m4_divert(-1)')
m4_define(ARG, `m4_divert(1)cnt += inst_line_size(what->a[$1-1].p);
m4_define(ARG, `m4_divert(1)cnt += inst_line_size(what->f$1);
m4_divert(-1)')
m4_define(ARG_T, `m4_divert(1)cnt += inst_line_size(what->a[$1-1].p);
m4_define(ARG_T, `m4_divert(1)cnt += inst_line_size(what->f$1);
m4_divert(-1)')
m4_define(ARG_ANY, `m4_divert(1)cnt += inst_line_size(what->a[$1-1].p);
m4_define(ARG_ANY, `m4_divert(1)cnt += inst_line_size(what->f$1);
m4_divert(-1)')
m4_define(LINE_SIZE, `m4_divert(1)$1m4_divert(-1)')
@ -24,7 +27,8 @@ m4_m4wrap(`
m4_divert(0)DNL
case FI_NOP: bug("This shall not happen");
m4_undivert(1)
break; default: bug( "Unknown instruction %d (%c)", what->fi_code, what->fi_code & 0xff);
#undef what
break; default: bug( "Unknown instruction %d (%c)", what_->fi_code, what_->fi_code & 0xff);
')
m4_changequote([[,]])

84
filter/new.m4 Normal file
View file

@ -0,0 +1,84 @@
m4_divert(-1)m4_dnl
#
# BIRD -- Construction of per-instruction structures
#
# (c) 2018 Maria Matejka <mq@jmq.cz>
#
# Can be freely distributed and used under the terms of the GNU GPL.
#
#
# Diversions:
# 1 for prepared output
# 2 for function arguments
# 3 for function body
# Common aliases
m4_define(DNL, `m4_dnl')
m4_define(FNSTOP, `m4_divert(-1)')
m4_define(FNOUT, `m4_divert(1)')
m4_define(FNARG, `m4_divert(2)')
m4_define(FNBODY, `m4_divert(3)')
m4_define(INST, `m4_define([[INST_NAME]], [[$1]])FNOUT()DNL
m4_undivert(2)DNL
m4_undivert(3)DNL
return what;
}
struct f_inst *f_new_inst_$1(enum f_instruction_code fi_code
FNBODY()) {
struct f_inst *what = cfg_allocz(sizeof(struct f_inst));
what->fi_code = fi_code;
what->lineno = ifs->lino;
FNSTOP()')
m4_define(WHAT, `what->i_[[]]INST_NAME()')
m4_define(FNMETAARG, `FNARG(), $1 $2
FNBODY() WHAT().$2 = $2;
FNSTOP()')
m4_define(ARG, `FNMETAARG(const struct f_inst *, f$1)')
m4_define(ARG_ANY, `FNMETAARG(const struct f_inst *, f$1)')
m4_define(LINE, `FNMETAARG(const struct f_inst *, f$1)')
m4_define(SYMBOL, `FNMETAARG(const struct symbol *, sym)')
m4_define(VALI, `FNMETAARG(struct f_val, vali)')
m4_define(VALP, `FNMETAARG(const struct f_val *, valp)')
m4_define(VAR, `FNARG(), const struct symbol * sym
FNBODY() WHAT().valp = (WHAT().sym = sym)->def;
FNSTOP()')
m4_define(FRET, `FNMETAARG(enum filter_return, fret)')
m4_define(ECS, `FNMETAARG(enum ec_subtype, ecs)')
m4_define(RTC, `FNMETAARG(const struct rtable_config *, rtc)')
m4_define(STATIC_ATTR, `FNMETAARG(struct f_static_attr, sa)')
m4_define(DYNAMIC_ATTR, `FNMETAARG(struct f_dynamic_attr, da)')
m4_define(COUNT, `FNMETAARG(uint, count)')
m4_define(TREE, `FNMETAARG(const struct f_tree *, tree)')
m4_define(STRING, `FNMETAARG(const char *, s)')
m4_define(NEW, `FNARG()$1
FNBODY()$2
FNSTOP()')
m4_m4wrap(`
FNOUT()
m4_undivert(2)
m4_undivert(3)
m4_divert(0)
#include "nest/bird.h"
#include "conf/conf.h"
#include "filter/filter.h"
#include "filter/f-inst-struct.h"
struct f_inst *f_new_inst_FI_NOP(enum f_instruction_code fi_code) {
struct f_inst *what = cfg_allocz(sizeof(struct f_inst));
what->fi_code = fi_code;
what->lineno = ifs->lino;
m4_undivert(1)
return what;
}
')
m4_changequote([[,]])

View file

@ -10,37 +10,45 @@ m4_divert(-1)m4_dnl
# Common aliases
m4_define(DNL, `m4_dnl')
m4_define(POSTFIXIFY_TRAILER, `dest->items[pos].fi_code = what->fi_code;
dest->items[pos].lineno = what->lineno;')
m4_define(POSTFIXIFY_TRAILER, `dest->items[pos].fi_code = what_->fi_code;
dest->items[pos].lineno = what_->lineno;')
m4_define(INST, `m4_divert(1)POSTFIXIFY_TRAILER
break; case $1:
#undef what
break; case $1:
#define what ((const struct f_inst_$1 *) &(what_->i_$1))
m4_divert(-1)'))
m4_define(ARG, `m4_divert(1)pos = postfixify(dest, what->a[$1-1].p, pos);
m4_define(ARG, `m4_divert(1)pos = postfixify(dest, what->f$1, pos);
m4_divert(-1)')
m4_define(ARG_ANY, `m4_divert(1)pos = postfixify(dest, what->a[$1-1].p, pos);
m4_define(ARG_ANY, `m4_divert(1)pos = postfixify(dest, what->f$1, pos);
m4_divert(-1)')
m4_define(LINE, `m4_divert(1)dest->items[pos].lines[$2] = f_postfixify(what->a[$1-1].p);
m4_define(LINE, `m4_divert(1)dest->items[pos].lines[$2] = f_postfixify(what->f$1);
m4_divert(-1)')
m4_define(LINEP, `m4_divert(1)dest->items[pos].lines[$2] = what->a[$1-1].p;
m4_define(LINEP, `m4_divert(1)dest->items[pos].lines[$2] = what->fl$1;
m4_divert(-1)')
m4_define(SYMBOL, `m4_divert(1)dest->items[pos].sym = what->a[$1-1].p;
m4_define(SYMBOL, `m4_divert(1)dest->items[pos].sym = what->sym;
m4_divert(-1)')
m4_define(VALI, `m4_divert(1)dest->items[pos].vp = &(what->val);
m4_define(VALI, `m4_divert(1)dest->items[pos].val = what->vali;
m4_divert(-1)')
m4_define(VALP, `m4_divert(1)dest->items[pos].vp = (dest->items[pos].sym = what->a[$1-1].p)->def;
m4_define(VALP, `m4_divert(1)dest->items[pos].val = *(what->valp);
m4_divert(-1)')
m4_define(FRET, `m4_divert(1)dest->items[pos].fret = what->a[$1-1].i;
m4_define(VAR, `m4_divert(1)dest->items[pos].vp = (dest->items[pos].sym = what->sym)->def;
m4_divert(-1)')
m4_define(ECS, `m4_divert(1)dest->items[pos].ecs = what->aux;
m4_define(FRET, `m4_divert(1)dest->items[pos].fret = what->fret;
m4_divert(-1)')
m4_define(RTC, `m4_divert(1)dest->items[pos].rtc = what->a[$1-1].rtc;
m4_define(ECS, `m4_divert(1)dest->items[pos].ecs = what->ecs;
m4_divert(-1)')
m4_define(RTC, `m4_divert(1)dest->items[pos].rtc = what->rtc;
m4_divert(-1)')
m4_define(STATIC_ATTR, `m4_divert(1)dest->items[pos].sa = what->sa;
m4_divert(-1)')
m4_define(DYNAMIC_ATTR, `m4_divert(1)dest->items[pos].da = what->da;
m4_divert(-1)')
m4_define(COUNT, `m4_divert(1)dest->items[pos].count = what->a[$1-1].i;
m4_define(COUNT, `m4_divert(1)dest->items[pos].count = what->count;
m4_divert(-1)')
m4_define(TREE, `m4_divert(1)dest->items[pos].tree = what->tree;
m4_divert(-1)')
m4_define(STRING, `m4_divert(1)dest->items[pos].s = what->s;
m4_divert(-1)')
m4_define(POSTFIXIFY, `m4_divert(1)$1m4_divert(-1)')
@ -49,7 +57,8 @@ m4_divert(0)DNL
case FI_NOP: bug("This shall not happen");
m4_undivert(1)
POSTFIXIFY_TRAILER
break; default: bug( "Unknown instruction %d (%c)", what->fi_code, what->fi_code & 0xff);
#undef what
break; default: bug( "Unknown instruction %d (%c)", what_->fi_code, what_->fi_code & 0xff);
')
m4_changequote([[,]])

View file

@ -29,7 +29,9 @@ m4_divert(-1)')
m4_define(VALI, `m4_divert(1)if (!val_same(f1->vp, f2->vp)) return 0;
m4_divert(-1)')
m4_define(VALP, `')
m4_define(VALP, `m4_divert(1)if (!val_same(f1->vp, f2->vp)) return 0;
m4_divert(-1)')
m4_define(VAR, `SYMBOL()VALP()')
m4_define(FRET, `m4_divert(1)if (f1->fret != f2->fret) return 0;
m4_divert(-1)')

67
filter/struct.m4 Normal file
View file

@ -0,0 +1,67 @@
m4_divert(-1)m4_dnl
#
# BIRD -- Definition of per-instruction structures
#
# (c) 2018 Maria Matejka <mq@jmq.cz>
#
# Can be freely distributed and used under the terms of the GNU GPL.
#
# Common aliases
m4_define(DNL, `m4_dnl')
m4_define(INST, `m4_divert(2)struct f_inst_$1 i_$1;
m4_divert(1)};
struct f_inst_$1 {
m4_divert(-1)'))
m4_define(ARG, `m4_divert(1)const struct f_inst *f$1;
m4_divert(-1)')
m4_define(ARG_ANY, `m4_divert(1)const struct f_inst *f$1;
m4_divert(-1)')
m4_define(LINE, `m4_divert(1)const struct f_inst *f$1;
m4_divert(-1)')
m4_define(LINEP, `m4_divert(1)const struct f_line *fl$1;
m4_divert(-1)')
m4_define(SYMBOL, `m4_divert(1)const struct symbol *sym;
m4_divert(-1)')
m4_define(VALI, `m4_divert(1)struct f_val vali;
m4_divert(-1)')
m4_define(VALP, `m4_divert(1)const struct f_val *valp;
m4_divert(-1)')
m4_define(VAR, `VALP()SYMBOL()')
m4_define(FRET, `m4_divert(1)enum filter_return fret;
m4_divert(-1)')
m4_define(ECS, `m4_divert(1)enum ec_subtype ecs;
m4_divert(-1)')
m4_define(RTC, `m4_divert(1)const struct rtable_config *rtc;
m4_divert(-1)')
m4_define(STATIC_ATTR, `m4_divert(1)struct f_static_attr sa;
m4_divert(-1)')
m4_define(DYNAMIC_ATTR, `m4_divert(1)struct f_dynamic_attr da;
m4_divert(-1)')
m4_define(COUNT, `m4_divert(1)uint count;
m4_divert(-1)')
m4_define(TREE, `m4_divert(1)const struct f_tree *tree;
m4_divert(-1)')
m4_define(STRING, `m4_divert(1)const char *s;
m4_divert(-1)')
m4_define(STRUCT, `m4_divert(1)$1
m4_divert(-1)')
m4_m4wrap(`
m4_divert(0)DNL
struct f_inst_FI_NOP {
m4_undivert(1)
};
struct f_inst {
const struct f_inst *next; /* Next instruction */
enum f_instruction_code fi_code; /* Instruction code */
int lineno; /* Line number */
union {
m4_undivert(2)
};
};
')
m4_changequote([[,]])

View file

@ -123,7 +123,7 @@ enum ec_subtype {
EC_RT = 0x0002,
EC_RO = 0x0003,
EC_GENERIC = 0xFFFF,
} PACKED;
};
/* Transitive bit (for first u32 half of EC) */
#define EC_TBIT 0x40000000

View file

@ -14,7 +14,7 @@ CF_DEFINES
#define STATIC_CFG ((struct static_config *) this_proto)
static struct static_route *this_srt, *this_snh;
static struct f_inst *this_srt_cmds, **this_srt_last_cmd;
static struct f_inst *this_srt_cmds, *this_srt_last_cmd;
static struct static_route *
static_nexthop_new(void)
@ -111,7 +111,7 @@ stat_route0: ROUTE net_any {
add_tail(&STATIC_CFG->routes, &this_srt->n);
this_srt->net = $2;
this_srt_cmds = NULL;
this_srt_last_cmd = &this_srt_cmds;
this_srt_last_cmd = NULL;
this_srt->mp_next = NULL;
this_snh = NULL;
}
@ -137,7 +137,13 @@ stat_route:
;
stat_route_item:
cmd { *this_srt_last_cmd = $1; this_srt_last_cmd = &($1->next); }
cmd {
if (this_srt_last_cmd)
f_inst_next(this_srt_last_cmd, $1);
else
this_srt_cmds = $1;
this_srt_last_cmd = $1;
}
;
stat_route_opts: