HASH_DEFINE_REHASH_FN(SYM, struct symbol)
- HASH(struct keyword) kw_hash;
- HASH(struct ea_class) ea_name_hash;
-
-struct sym_scope *conf_this_scope;
-struct sym_scope *global_root_scope;
++/* Global symbol scopes */
+ static pool *global_root_scope_pool;
++static struct sym_scope
++ global_root_scope = {
++ .active = 1,
++ },
++ global_filter_scope = {
++ .active = 0,
++ .next = &global_root_scope,
++ };
++
++/* Local symbol scope: TODO this isn't thread-safe */
+struct sym_scope *conf_this_scope;
- static struct sym_scope global_root_scope__init = { .active = 1, };
- struct sym_scope *global_root_scope = &global_root_scope__init;
-
linpool *cfg_mem;
int (*cf_read_hook)(byte *buf, unsigned int max, int fd);
cf_swap_soft_scope();
- pool *p = new_config->pool;
-
- if (conf_this_scope == global_root_scope)
- s = mb_allocz(p = global_root_scope_pool, sizeof(struct symbol) + l + 1);
- else
- s = cfg_allocz(sizeof(struct symbol) + l + 1);
+ s = cfg_allocz(sizeof(struct symbol) + l + 1);
*s = (struct symbol) { .scope = conf_this_scope, .class = SYM_VOID, };
-- strcpy(s->name, c);
++ memcpy(s->name, c, l+1);
if (!conf_this_scope->hash.data)
- HASH_INIT(conf_this_scope->hash, p, SYM_ORDER);
+ HASH_INIT(conf_this_scope->hash, new_config->pool, SYM_ORDER);
- HASH_INSERT2(conf_this_scope->hash, SYM, p, s);
+ HASH_INSERT2(conf_this_scope->hash, SYM, new_config->pool, s);
if (conf_this_scope == new_config->root_scope)
add_tail(&(new_config->symbols), &(s->n));
return s;
}
-struct symbol *
-cf_symbol_from_keyword(const struct keyword *kw)
-{ return cf_new_symbol(kw->name); }
+static struct symbol *
- cf_root_symbol(const byte *c)
++cf_root_symbol(const byte *c, struct sym_scope *ss)
+{
+ uint l = strlen(c);
+ if (l > SYM_MAX_LEN)
+ bug("Root symbol %s too long", c);
+
+ struct symbol *s = mb_alloc(&root_pool, sizeof(struct symbol) + l + 1);
- *s = (struct symbol) { .scope = global_root_scope, .class = SYM_VOID, };
++ *s = (struct symbol) { .scope = ss, .class = SYM_VOID, };
+ memcpy(s->name, c, l+1);
+
- if (!global_root_scope->hash.data)
- HASH_INIT(global_root_scope->hash, &root_pool, SYM_ORDER);
++ if (!ss->hash.data)
++ HASH_INIT(ss->hash, &root_pool, SYM_ORDER);
+
- HASH_INSERT2(global_root_scope->hash, SYM, &root_pool, s);
++ HASH_INSERT2(ss->hash, SYM, &root_pool, s);
+ return s;
+}
+
/**
* cf_find_symbol_scope - find a symbol by name
/* Find the symbol here or anywhere below */
while (scope)
- if (scope->hash.data && (s = HASH_FIND(scope->hash, SYM, c, 1)))
- if (scope->hash.data && (s = HASH_FIND(scope->hash, SYM, c)))
++ if (scope->active && scope->hash.data && (s = HASH_FIND(scope->hash, SYM, c)))
return s;
else
scope = scope->next;
struct symbol *sym = cf_get_symbol(data);
cf_lval.s = sym;
- /* Is it a keyword? Prefer the keyword. */
- struct keyword *k = HASH_FIND(kw_hash, KW, data);
- if (k)
+ switch (sym->class)
{
- if (k->value > 0)
- return k->value;
- else
- {
- cf_lval.i = -k->value;
- return ENUM;
- }
+ case SYM_KEYWORD:
- {
- int val = sym->keyword->value;
- if (val > 0) return val;
- cf_lval.i = -val;
- return ENUM;
- }
++ if (sym->keyword->value > 0)
++ return sym->keyword->value;
++ else
++ {
++ cf_lval.i = -sym->keyword->value;
++ return ENUM;
++ }
+ case SYM_VOID:
+ return CF_SYM_UNDEFINED;
+ default:
+ return CF_SYM_KNOWN;
}
-
- /* OK, only a symbol. */
- if (sym->class == SYM_VOID)
- return CF_SYM_UNDEFINED;
- else
- return CF_SYM_KNOWN;
- }
-
- static void
- cf_lex_init_kh(void)
- {
- HASH_INIT(kw_hash, config_pool, KW_ORDER);
-
- struct keyword *k;
- for (k=keyword_list; k->name; k++)
- HASH_INSERT(kw_hash, KW, k);
}
- struct symbol *sym = cf_root_symbol(def->name);
- sym->class = SYM_ATTRIBUTE;
- sym->attribute = def;
- def->sym = sym;
+void
+ea_lex_register(struct ea_class *def)
+{
- HASH_REMOVE2(global_root_scope->hash, SYM, &root_pool, sym);
++ def->sym = cf_define_symbol(cf_root_symbol(def->name, &global_filter_scope), SYM_ATTRIBUTE, attribute, def);
+}
+
+void
+ea_lex_unregister(struct ea_class *def)
+{
+ struct symbol *sym = def->sym;
- struct symbol *sym = cf_find_symbol(global_root_scope, name);
++ HASH_REMOVE2(global_filter_scope.hash, SYM, &root_pool, sym);
+ mb_free(sym);
+ def->sym = NULL;
+}
+
+struct ea_class *
+ea_class_find_by_name(const char *name)
+{
++ if (!global_filter_scope.hash.data)
++ return NULL;
++
++ struct symbol *sym = HASH_FIND(global_filter_scope.hash, SYM, name);
++
+ if (!sym || (sym->class != SYM_ATTRIBUTE))
+ return NULL;
+ else
+ return sym->attribute;
+}
+
/**
* cf_lex_init - initialize the lexer
* @is_cli: true if we're going to parse CLI command, false for configuration
void
cf_lex_init(int is_cli, struct config *c)
{
- if (!kw_hash.data)
- cf_lex_init_kh();
+ if (!global_root_scope_pool)
+ {
- global_root_scope_pool = rp_new(&root_pool, "Keywords pool");
- conf_this_scope = global_root_scope = mb_allocz(global_root_scope_pool, sizeof(*global_root_scope));
++ global_root_scope_pool = rp_new(&root_pool, the_bird_domain.the_bird, "Keywords pool");
+
+ for (const struct keyword *k = keyword_list; k->name; k++)
- cf_define_symbol(cf_get_symbol(k->name), SYM_KEYWORD, keyword, k);
++ cf_define_symbol(cf_root_symbol(k->name, &global_root_scope), SYM_KEYWORD, keyword, k);
+ }
ifs_head = ifs = push_ifs(NULL);
if (!is_cli)
if (is_cli)
conf_this_scope->next = config->root_scope;
else
-- conf_this_scope->next = global_root_scope;
++ conf_this_scope->next = &global_filter_scope;
}
/**
}
}
++/**
++ * cf_enter_filters - enable filter / route attributes namespace
++ */
++void
++cf_enter_filters(void)
++{
++ ASSERT_DIE(!global_filter_scope.active);
++ global_filter_scope.active = 1;
++}
++
++/**
++ * cf_exit_filters - disable filter / route attributes namespace
++ */
++void
++cf_exit_filters(void)
++{
++ ASSERT_DIE(global_filter_scope.active);
++ global_filter_scope.active = 0;
++}
++
++
/**
* cf_symbol_class_name - get name of a symbol class
* @sym: symbol
return "routing table";
case SYM_ATTRIBUTE:
return "custom attribute";
++ case SYM_KEYWORD:
++ return "symbol";
case SYM_CONSTANT_RANGE:
return "constant";
case SYM_VARIABLE_RANGE:
const struct f_line *function; /* For SYM_FUNCTION */
const struct filter *filter; /* For SYM_FILTER */
struct rtable_config *table; /* For SYM_TABLE */
- struct f_dynamic_attr *attribute; /* For SYM_ATTRIBUTE */
+ struct ea_class *attribute; /* For SYM_ATTRIBUTE */
struct f_val *val; /* For SYM_CONSTANT */
uint offset; /* For SYM_VARIABLE */
+ const struct keyword *keyword; /* For SYM_KEYWORD */
};
char name[0];
byte soft_scopes; /* Number of soft scopes above */
};
--extern struct sym_scope *global_root_scope;
++void cf_enter_filters(void);
++void cf_exit_filters(void);
struct bytestring {
size_t length;
%type <t> text opttext
%type <bs> bytestring
- %type <s> symbol symbol_known toksym
-%type <s> symbol symbol_known
++%type <s> symbol
%type <v> bytestring_text text_or_ipa
%type <x> bytestring_expr
expr:
NUM
| '(' term ')' { $$ = cf_eval_int($2); }
-- | symbol_known {
++ | CF_SYM_KNOWN {
if ($1->class != (SYM_CONSTANT | T_INT)) cf_error("Number constant expected");
$$ = SYM_VAL($1).i; }
;
| expr US { $$ = $1 US_; }
;
- toksym: FROM | PREFERENCE ;
- symbol: CF_SYM_UNDEFINED | CF_SYM_KNOWN | toksym ;
- symbol_known: CF_SYM_KNOWN | toksym ;
-symbol: CF_SYM_UNDEFINED | CF_SYM_KNOWN | KEYWORD ;
-symbol_known: CF_SYM_KNOWN | KEYWORD ;
++symbol: CF_SYM_UNDEFINED | CF_SYM_KNOWN ;
/* Switches */
m4_define(CF_ENUM, `m4_define([[CF_enum_type]],$1)m4_define([[CF_enum_prefix_ext]],$2)m4_define([[CF_enum_prefix_int]],$2)CF_iterate([[CF_enum]], [[m4_shift(m4_shift($@))]])DNL')
m4_define(CF_ENUM_PX, `m4_define([[CF_enum_type]],$1)m4_define([[CF_enum_prefix_ext]],$2)m4_define([[CF_enum_prefix_int]],$3)CF_iterate([[CF_enum]], [[m4_shift(m4_shift(m4_shift($@)))]])DNL')
--# After all configuration templates end, we generate the
++# After all configuration templates end, we generate the keyword list
m4_m4wrap(`
m4_divert(0)
- static struct keyword keyword_list[] = {
- m4_undivert(1){ NULL, -1, NULL } };
+ static const struct keyword keyword_list[] = {
+ m4_undivert(1){ NULL, -1 } };
')
# As we are processing C source, we must access all M4 primitives via
CF_GRAMMAR
+conf: FILTER STACKS expr expr ';' {
+ new_config->filter_vstk = $3;
+ new_config->filter_estk = $4;
+ }
+ ;
+
conf: filter_def ;
filter_def:
-- FILTER symbol { $2 = cf_define_symbol($2, SYM_FILTER, filter, NULL); cf_push_scope( $2 ); }
-- filter_body {
++ FILTER symbol {
++ $2 = cf_define_symbol($2, SYM_FILTER, filter, NULL);
++ cf_enter_filters();
++ cf_push_scope( $2 );
++ } filter_body {
struct filter *f = cfg_alloc(sizeof(struct filter));
*f = (struct filter) { .sym = $2, .root = $4 };
$2->filter = f;
cf_pop_scope();
++ cf_exit_filters();
}
;
conf: bt_test_suite ;
bt_test_suite:
-- BT_TEST_SUITE '(' symbol_known ',' text ')' {
++ BT_TEST_SUITE '(' CF_SYM_KNOWN ',' text ')' {
cf_assert_symbol($3, SYM_FUNCTION);
struct f_bt_test_suite *t = cfg_allocz(sizeof(struct f_bt_test_suite));
t->fn = $3->function;
conf: bt_test_same ;
bt_test_same:
-- BT_TEST_SAME '(' symbol_known ',' symbol_known ',' NUM ')' {
++ BT_TEST_SAME '(' CF_SYM_KNOWN ',' CF_SYM_KNOWN ',' NUM ')' {
cf_assert_symbol($3, SYM_FUNCTION);
cf_assert_symbol($5, SYM_FUNCTION);
struct f_bt_test_suite *t = cfg_allocz(sizeof(struct f_bt_test_suite));
filter_body: function_body ;
filter:
-- symbol_known {
++ CF_SYM_KNOWN {
cf_assert_symbol($1, SYM_FILTER);
$$ = $1->filter;
}
-- | { cf_push_scope(NULL); } filter_body {
++ | {
++ cf_enter_filters();
++ cf_push_scope(NULL);
++ } filter_body {
struct filter *f = cfg_alloc(sizeof(struct filter));
*f = (struct filter) { .root = $2 };
$$ = f;
cf_pop_scope();
++ cf_exit_filters();
}
;
where_filter:
-- WHERE term {
++ WHERE {
++ cf_enter_filters();
++ } term {
/* Construct 'IF term THEN { ACCEPT; } ELSE { REJECT; }' */
-- $$ = f_new_where($2);
++ $$ = f_new_where($3);
++ cf_exit_filters();
}
;
FUNCTION symbol {
DBG( "Beginning of function %s\n", $2->name );
$2 = cf_define_symbol($2, SYM_FUNCTION, function, NULL);
++ cf_enter_filters();
cf_push_scope($2);
} function_args {
/* Make dummy f_line for storing function prototype */
$6->arg_list = $2->function->arg_list;
$2->function = $6;
cf_pop_scope();
++ cf_exit_filters();
}
;
| VPN_RD { $$.type = T_RD; $$.val.ec = $1; }
| ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
| '(' term ')' {
- $$ = cf_eval($2, T_VOID);
+ $$ = cf_eval_tmp($2, T_VOID);
if (!f_valid_set_type($$.type)) cf_error("Set-incompatible type");
}
-- | symbol_known {
++ | CF_SYM_KNOWN {
cf_assert_symbol($1, SYM_CONSTANT);
if (!f_valid_set_type(SYM_TYPE($1))) cf_error("%s: set-incompatible type", $1->name);
$$ = *$1->val;
| var_list ',' term { $$ = $3; $$->next = $1; }
function_call:
-- symbol_known '(' var_list ')'
++ CF_SYM_KNOWN '(' var_list ')'
{
if ($1->class != SYM_FUNCTION)
cf_error("You can't call something which is not a function. Really.");
}
;
- symbol_value: symbol_known
-symbol_value: symbol_known
++symbol_value: CF_SYM_KNOWN
{
switch ($1->class) {
case SYM_CONSTANT_RANGE:
$$ = f_new_inst(FI_FOR_INIT, $6, $3);
$$->next = f_new_inst(FI_FOR_NEXT, $3, $9);
}
-- | symbol_known '=' term ';' {
++ | CF_SYM_KNOWN '=' term ';' {
switch ($1->class) {
case SYM_VARIABLE_RANGE:
$$ = f_new_inst(FI_VAR_SET, $3, $1);
cf_error( "This static attribute is read-only.");
$$ = f_new_inst(FI_RTA_SET, $3, $1);
}
- | UNSET '(' symbol_known ')' ';' {
- | UNSET '(' dynamic_attr ')' ';' {
- $$ = f_new_inst(FI_EA_UNSET, $3);
++ | UNSET '(' CF_SYM_KNOWN ')' ';' {
+ if ($3->class != SYM_ATTRIBUTE)
+ cf_error("Can't unset %s", $3->name);
+ if ($3->attribute->readonly)
+ cf_error("Attribute %s is read-only", $3->attribute->name);
+ $$ = f_new_inst(FI_EA_UNSET, $3->attribute);
}
| break_command print_list ';' {
struct f_inst *breaker = f_new_inst(FI_DIE, $1);
$$ = f_new_inst(FI_SWITCH, $2, $4);
}
- | symbol_known '.' EMPTY ';' { $$ = f_generate_empty($1); }
- | symbol_known '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex_sym( FI_PATH_PREPEND, $1, $5 ); }
- | symbol_known '.' ADD '(' term ')' ';' { $$ = f_generate_complex_sym( FI_CLIST_ADD, $1, $5 ); }
- | symbol_known '.' DELETE '(' term ')' ';' { $$ = f_generate_complex_sym( FI_CLIST_DEL, $1, $5 ); }
- | symbol_known '.' FILTER '(' term ')' ';' { $$ = f_generate_complex_sym( FI_CLIST_FILTER, $1, $5 ); }
- | dynamic_attr '.' EMPTY ';' { $$ = f_new_inst(FI_EA_SET, f_const_empty($1), $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 ); }
++ | CF_SYM_KNOWN '.' EMPTY ';' { $$ = f_generate_empty($1); }
++ | CF_SYM_KNOWN '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex_sym( FI_PATH_PREPEND, $1, $5 ); }
++ | CF_SYM_KNOWN '.' ADD '(' term ')' ';' { $$ = f_generate_complex_sym( FI_CLIST_ADD, $1, $5 ); }
++ | CF_SYM_KNOWN '.' DELETE '(' term ')' ';' { $$ = f_generate_complex_sym( FI_CLIST_DEL, $1, $5 ); }
++ | CF_SYM_KNOWN '.' FILTER '(' term ')' ';' { $$ = f_generate_complex_sym( 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); }
;
};
lvalue:
- symbol_known {
- CF_SYM_KNOWN { cf_assert_symbol($1, SYM_VARIABLE); $$ = (struct f_lval) { .type = F_LVAL_VARIABLE, .sym = $1 }; }
++ CF_SYM_KNOWN {
+ switch ($1->class) {
+ case SYM_VARIABLE_RANGE:
+ $$ = (struct f_lval) { .type = F_LVAL_VARIABLE, .sym = $1 };
+ break;
+ case SYM_ATTRIBUTE:
+ $$ = (struct f_lval) { .type = F_LVAL_EA, .da = $1->attribute };
+ break;
+ }
+ }
| static_attr { $$ = (struct f_lval) { .type = F_LVAL_SA, .sa = $1 }; }
- | dynamic_attr { $$ = (struct f_lval) { .type = F_LVAL_EA, .da = $1 }; };
+ ;
CF_END
if ($$->addr) cf_error("Only one prefix expected");
if (!net_type_match($3, NB_IP)) cf_error("Only IP networks accepted for 'in' argument");
$$->addr = $3;
- $$->addr_mode = RSD_ADDR_IN;
+ $$->addr_mode = TE_ADDR_IN;
}
--| r_args TABLE symbol_known {
++| r_args TABLE CF_SYM_KNOWN {
cf_assert_symbol($3, SYM_TABLE);
+ if (!$3->table) cf_error("Table %s not configured", $3->name);
$$ = $1;
rt_show_add_table($$, $3->table->table);
$$->tables_defined_by = RSD_TDB_DIRECT;
$$ = $1;
$$->filtered = 1;
}
-- | r_args export_mode symbol_known {
++ | r_args export_mode CF_SYM_KNOWN {
cf_assert_symbol($3, SYM_PROTO);
struct proto_config *c = (struct proto_config *) $3->proto;
$$ = $1;
$$->export_channel = $3;
$$->tables_defined_by = RSD_TDB_INDIRECT;
}
-- | r_args PROTOCOL symbol_known {
++ | r_args PROTOCOL CF_SYM_KNOWN {
cf_assert_symbol($3, SYM_PROTO);
struct proto_config *c = (struct proto_config *) $3->proto;
$$ = $1;
stat_route_opt_list:
/* empty */
-- | '{' stat_route_opts '}'
++ | '{' { cf_enter_filters(); } stat_route_opts '}' { cf_exit_filters(); }
;