HASH_DEFINE_REHASH_FN(SYM, struct symbol)
-struct sym_scope *global_root_scope;
+/* Global symbol scopes */
- static pool *global_root_scope_pool;
+ pool *global_root_scope_pool;
+ linpool *global_root_scope_linpool;
+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;
linpool *cfg_mem;
return s;
}
- static struct symbol *
++struct symbol *
+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);
++ if (!global_root_scope_pool)
++ {
++ global_root_scope_pool = rp_new(&root_pool, the_bird_domain.the_bird, "Keywords pool");
++ global_root_scope_linpool = lp_new(global_root_scope_pool);
++ }
++
++ struct symbol *s = lp_alloc(global_root_scope_linpool, sizeof(struct symbol) + l + 1);
+ *s = (struct symbol) { .scope = ss, .class = SYM_VOID, };
+ memcpy(s->name, c, l+1);
+
+ if (!ss->hash.data)
+ HASH_INIT(ss->hash, &root_pool, SYM_ORDER);
+
+ HASH_INSERT2(ss->hash, SYM, &root_pool, s);
+ return s;
+}
+
+
/**
* cf_find_symbol_scope - find a symbol by name
* @scope: config scope
}
}
+void
+ea_lex_register(struct ea_class *def)
+{
+ def->sym = cf_root_symbol(def->name, &global_filter_scope);
+ def->sym->class = SYM_ATTRIBUTE;
+ def->sym->attribute = def;
+}
+
+void
+ea_lex_unregister(struct ea_class *def)
+{
+ struct symbol *sym = def->sym;
+ 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;
+}
+
+ void f_type_methods_register(void);
+
/**
* 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 (!global_root_scope_pool)
++ if (!global_root_scope.readonly)
{
- global_root_scope_pool = rp_new(&root_pool, the_bird_domain.the_bird, "Keywords pool");
- global_root_scope_pool = rp_new(&root_pool, "Keywords pool");
- global_root_scope_linpool = lp_new(global_root_scope_pool);
- global_root_scope = lp_allocz(global_root_scope_linpool, sizeof(*global_root_scope));
--
for (const struct keyword *k = keyword_list; k->name; k++)
{
- struct symbol *sym = cf_new_symbol(global_root_scope, global_root_scope_pool, global_root_scope_linpool, k->name);
- sym->class = SYM_KEYWORD;
- sym->keyword = k;
+ struct symbol *s = cf_root_symbol(k->name, &global_root_scope);
+ s->class = SYM_KEYWORD;
+ s->keyword = k;
}
- global_root_scope->readonly = 1;
+ global_root_scope.readonly = 1;
+ global_filter_scope.readonly = 1;
+
+ f_type_methods_register();
}
ifs_head = ifs = push_ifs(NULL);
byte readonly:1; /* Do not add new symbols */
};
-extern struct sym_scope *global_root_scope;
+void cf_enter_filters(void);
+void cf_exit_filters(void);
+
+ extern pool *global_root_scope_pool;
+ extern linpool *global_root_scope_linpool;
+
struct bytestring {
size_t length;
byte data[];
static inline int cf_symbol_is_local(struct config *conf, struct symbol *sym)
{ return (sym->scope == conf->current_scope) && !conf->current_scope->soft_scopes; }
+ /* internal */
+ struct symbol *cf_new_symbol(struct sym_scope *scope, pool *p, struct linpool *lp, const byte *c);
++struct symbol *cf_root_symbol(const byte *, struct sym_scope *);
+
/**
* cf_define_symbol - define meaning of a symbol
* @sym: symbol to be defined
m4_define(CF_itera, `m4_ifelse($#, 1, [[CF_iter($1)]], [[CF_iter($1)[[]]CF_itera(m4_shift($@))]])')
m4_define(CF_iterate, `m4_define([[CF_iter]], m4_defn([[$1]]))CF_itera($2)')
-m4_define(CF_append, `m4_define([[$1]], m4_ifdef([[$1]], m4_defn([[$1]])[[$3]])[[$2]])')
-
-# Keywords act as %token<s>
-m4_define(CF_keywd, `m4_ifdef([[CF_tok_$1]],,[[m4_define([[CF_tok_$1]],1)CF_append([[CF_kw_rule]],$1,[[ | ]])m4_define([[CF_toks]],CF_toks $1)]])')
+# Keywords act as untyped %token
+m4_define(CF_keywd, `m4_ifdef([[CF_tok_$1]],,[[m4_define([[CF_tok_$1]],1)m4_define([[CF_toks]],CF_toks $1)]])')
m4_define(CF_KEYWORDS, `m4_define([[CF_toks]],[[]])CF_iterate([[CF_keywd]], [[$@]])m4_ifelse(CF_toks,,,%token<s>[[]]CF_toks
)DNL')
+ m4_define(CF_METHODS, `m4_define([[CF_toks]],[[]])CF_iterate([[CF_keywd]], [[$@]])m4_ifelse(CF_toks,,,%token<s>[[]]CF_toks
+ )DNL')
-m4_define(CF_keywd2, `m4_ifdef([[CF_tok_$1]],,[[m4_define([[CF_tok_$1]],1)m4_define([[CF_toks]],CF_toks $1)]])')
-m4_define(CF_KEYWORDS_EXCLUSIVE, `m4_define([[CF_toks]],[[]])CF_iterate([[CF_keywd2]], [[$@]])m4_ifelse(CF_toks,,,%token<s>[[]]CF_toks
-)DNL')
-
# CLI commands
m4_define(CF_CLI, `m4_define([[CF_cmd]], cmd_[[]]m4_translit($1, [[ ]], _))DNL
m4_divert(2)CF_KEYWORDS(m4_translit($1, [[ ]], [[,]]))
return t;
}
- static inline struct f_inst *
- f_generate_empty(const struct symbol *sym)
- {
- if (sym->class != SYM_ATTRIBUTE)
- cf_error("Can't empty %s: not an attribute", sym->name);
-
- const struct ea_class *def = sym->attribute;
- const struct f_val empty = f_get_empty(def->type);
- if (empty.type == T_VOID)
- cf_error("Can't empty attribute %s", def->name);
-
- return f_new_inst(FI_EA_SET, f_new_inst(FI_CONSTANT, empty), def);
- }
-
static inline struct f_inst *
-f_const_empty(enum f_type t)
+f_implicit_roa_check(struct rtable_config *tab)
{
- switch (t) {
- case T_PATH:
- case T_CLIST:
- case T_ECLIST:
- case T_LCLIST:
- return f_new_inst(FI_CONSTANT, (struct f_val) {
- .type = t,
- .val.ad = &null_adata,
- });
- default:
- return f_new_inst(FI_CONSTANT, (struct f_val) {});
- }
+ const struct ea_class *def = ea_class_find("bgp_path");
+ if (!def)
+ cf_error("Fatal: Couldn't find BGP path attribute definition.");
+
+ struct f_static_attr fsa = f_new_static_attr(T_NET, SA_NET, 1);
+
+ return f_new_inst(FI_ROA_CHECK,
+ f_new_inst(FI_RTA_GET, fsa),
+ f_new_inst(FI_AS_PATH_LAST, f_new_inst(FI_EA_GET, def)),
+ tab);
}
/*
CF_DECLS
-CF_KEYWORDS_EXCLUSIVE(IN)
CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
ACCEPT, REJECT, ERROR,
- INT, BOOL, IP, TYPE, PREFIX, RD, PAIR, QUAD, EC, LC,
+ INT, BOOL, IP, PREFIX, RD, PAIR, QUAD, EC, LC,
SET, STRING, BYTESTRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
IF, THEN, ELSE, CASE,
- FOR, DO,
+ FOR, IN, DO,
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
- FROM, GW, NET, MASK, PROTO, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS,
- ROA_CHECK, ASN, SRC, DST,
- IS_V4, IS_V6,
- LEN, MAXLEN,
- DATA, DATA1, DATA2,
- FROM, GW, NET, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS, ONLINK,
- PREFERENCE,
++ FROM, GW, NET, PROTO, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS,
+ ROA_CHECK,
DEFINED,
ADD, DELETE, RESET,
- PREPEND, FIRST, LAST, LAST_NONAGGREGATED,
- MIN, MAX,
+ PREPEND,
EMPTY,
FILTER, WHERE, EVAL, ATTRIBUTE,
FROM_HEX,
- BT_ASSERT, BT_TEST_SUITE, BT_CHECK_ASSIGN, BT_TEST_SAME, FORMAT)
+ BT_ASSERT, BT_TEST_SUITE, BT_CHECK_ASSIGN, BT_TEST_SAME, FORMAT, STACKS)
+ CF_METHODS(IS_V4, TYPE, IP, RD, LEN, MAXLEN, ASN, SRC, DST, MASK,
+ FIRST, LAST, LAST_NONAGGREGATED, DATA, DATA1, DATA2, MIN, MAX,
+ EMPTY, PREPEND, ADD, DELETE, FILTER)
+
%nonassoc THEN
%nonassoc ELSE
%type <xp> cmds_int cmd_prep
- %type <x> term term_bs cmd cmd_var cmds cmds_scoped constant constructor print_list var var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail method_cmd method_term
+ %type <x> term term_bs cmd cmd_var cmds cmds_scoped constant constructor print_list var var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail term_dot_method method_name_cont
-%type <fda> dynamic_attr
%type <fsa> static_attr
%type <f> filter where_filter
%type <fl> filter_body function_body
filter_def:
FILTER symbol {
$2 = cf_define_symbol(new_config, $2, SYM_FILTER, filter, NULL);
+ cf_enter_filters();
cf_push_scope( new_config, $2 );
+ this_function = NULL;
} filter_body {
struct filter *f = cfg_alloc(sizeof(struct filter));
*f = (struct filter) { .sym = $2, .root = $4 };
$$ = $1->filter;
}
| {
+ cf_enter_filters();
cf_push_scope(new_config, NULL);
+ this_function = NULL;
} filter_body {
struct filter *f = cfg_alloc(sizeof(struct filter));
*f = (struct filter) { .root = $2 };
;
conf: function_def ;
+ maybe_type:
+ /* EMPTY */ { $$ = T_VOID; }
+ | type { $$ = $1; }
+ ;
+
function_def:
- FUNCTION symbol {
- DBG( "Beginning of function %s\n", $2->name );
- $2 = cf_define_symbol(new_config, $2, SYM_FUNCTION, function, NULL);
+ FUNCTION maybe_type symbol {
+ DBG( "Beginning of function %s\n", $3->name );
+ this_function = cf_define_symbol(new_config, $3, SYM_FUNCTION, function, NULL);
+ /* if ($2 == T_VOID) cf_warn("Support for functions without explicit return type will be removed soon" ); */
+ cf_enter_filters();
- cf_push_scope(new_config, $2);
+ cf_push_scope(new_config, this_function);
} function_args {
/* Make dummy f_line for storing function prototype */
struct f_line *dummy = cfg_allocz(sizeof(struct f_line));
dummy->args++;
}
} function_body {
- $6->args = $2->function->args;
- $6->arg_list = $2->function->arg_list;
- $2->function = $6;
+ $7->args = this_function->function->args;
+ $7->arg_list = this_function->function->arg_list;
+ $7->return_type = this_function->function->return_type;
+ $3->function = $7;
cf_pop_scope(new_config);
+ cf_exit_filters();
}
;
| IFNAME { $$ = f_new_static_attr(T_STRING, SA_IFNAME, 0); }
| IFINDEX { $$ = f_new_static_attr(T_INT, SA_IFINDEX, 1); }
| WEIGHT { $$ = f_new_static_attr(T_INT, SA_WEIGHT, 0); }
- | PREFERENCE { $$ = f_new_static_attr(T_INT, SA_PREF, 0); }
| GW_MPLS { $$ = f_new_static_attr(T_INT, SA_GW_MPLS, 0); }
- | ONLINK { $$ = f_new_static_attr(T_BOOL, SA_ONLINK, 0); }
;
- method_term:
- IS_V4 { $$ = f_new_inst(FI_IS_V4, FM.object); }
- | TYPE { $$ = f_new_inst(FI_TYPE, FM.object); }
- | IP { $$ = f_new_inst(FI_IP, FM.object); }
- | RD { $$ = f_new_inst(FI_ROUTE_DISTINGUISHER, FM.object); }
- | LEN { $$ = f_new_inst(FI_LENGTH, FM.object); }
- | MAXLEN { $$ = f_new_inst(FI_ROA_MAXLEN, FM.object); }
- | ASN { $$ = f_new_inst(FI_ASN, FM.object); }
- | SRC { $$ = f_new_inst(FI_NET_SRC, FM.object); }
- | DST { $$ = f_new_inst(FI_NET_DST, FM.object); }
- | MASK '(' term ')' { $$ = f_new_inst(FI_IP_MASK, FM.object, $3); }
- | FIRST { $$ = f_new_inst(FI_AS_PATH_FIRST, FM.object); }
- | LAST { $$ = f_new_inst(FI_AS_PATH_LAST, FM.object); }
- | LAST_NONAGGREGATED { $$ = f_new_inst(FI_AS_PATH_LAST_NAG, FM.object); }
- | DATA { $$ = f_new_inst(FI_PAIR_DATA, FM.object); }
- | DATA1 { $$ = f_new_inst(FI_LC_DATA1, FM.object); }
- | DATA2 { $$ = f_new_inst(FI_LC_DATA2, FM.object); }
- | MIN { $$ = f_new_inst(FI_MIN, FM.object); }
- | MAX { $$ = f_new_inst(FI_MAX, FM.object); }
- ;
-
- method_cmd:
- EMPTY { $$ = f_new_inst(FI_CONSTANT, f_get_empty(FM.object->i_FI_EA_GET.da->type)); }
- | PREPEND '(' term ')' { $$ = f_new_inst(FI_PATH_PREPEND, FM.object, $3 ); }
- | ADD '(' term ')' { $$ = f_new_inst(FI_CLIST_ADD, FM.object, $3 ); }
- | DELETE '(' term ')' { $$ = f_new_inst(FI_CLIST_DEL, FM.object, $3 ); }
- | FILTER '(' term ')' { $$ = f_new_inst(FI_CLIST_FILTER, FM.object, $3 ); }
+ term_dot_method: term '.' { f_method_call_start($1); } method_name_cont { $$ = $4; };
+ method_name_cont:
+ CF_SYM_METHOD_BARE {
+ $$ = $1->method->new_inst(FM.object, NULL);
+ f_method_call_end();
+ }
+ | CF_SYM_METHOD_ARGS {
+ f_method_call_args();
+ } '(' var_list ')' {
+ $$ = $1->method->new_inst(FM.object, $4);
+ f_method_call_end();
+ }
;
term:
| static_attr { $$ = f_new_inst(FI_RTA_GET, $1); }
- | term '.' {
- f_push_method_scope($1);
- } method_term {
- f_pop_method_scope();
- $$ = $4;
- }
- | dynamic_attr { $$ = f_new_inst(FI_EA_GET, $1); }
-
+ | term_dot_method
- | '+' EMPTY '+' { $$ = f_const_empty(T_PATH); }
- | '-' EMPTY '-' { $$ = f_const_empty(T_CLIST); }
- | '-' '-' EMPTY '-' '-' { $$ = f_const_empty(T_ECLIST); }
- | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_const_empty(T_LCLIST); }
+ | '+' EMPTY '+' { $$ = f_new_inst(FI_CONSTANT, f_get_empty(T_PATH)); }
+ | '-' EMPTY '-' { $$ = f_new_inst(FI_CONSTANT, f_get_empty(T_CLIST)); }
+ | '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_get_empty(T_ECLIST)); }
+ | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_get_empty(T_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); }
+ | ADD '(' term ',' term ')' {
+ switch ($3->type) {
+ case T_CLIST: $$ = f_new_inst(FI_CLIST_ADD, $3, $5); break;
+ case T_ECLIST: $$ = f_new_inst(FI_ECLIST_ADD, $3, $5); break;
+ case T_LCLIST: $$ = f_new_inst(FI_LCLIST_ADD, $3, $5); break;
+ default: cf_error("Can't add to type %s", f_type_name($3->type));
+ }
+ cf_warn("add(x,y) function is deprecated, please use x.add(y)");
+ }
+ | DELETE '(' term ',' term ')' {
+ switch ($3->type) {
+ case T_PATH: $$ = f_new_inst(FI_PATH_DEL, $3, $5); break;
+ case T_CLIST: $$ = f_new_inst(FI_CLIST_DEL, $3, $5); break;
+ case T_ECLIST: $$ = f_new_inst(FI_ECLIST_DEL, $3, $5); break;
+ case T_LCLIST: $$ = f_new_inst(FI_LCLIST_DEL, $3, $5); break;
+ default: cf_error("Can't delete from type %s", f_type_name($3->type));
+ }
+ cf_warn("delete(x,y) function is deprecated, please use x.delete(y)");
+ }
+ | FILTER '(' term ',' term ')' {
+ switch ($3->type) {
+ case T_PATH: $$ = f_new_inst(FI_PATH_FILTER, $3, $5); break;
+ case T_CLIST: $$ = f_new_inst(FI_CLIST_FILTER, $3, $5); break;
+ case T_ECLIST: $$ = f_new_inst(FI_ECLIST_FILTER, $3, $5); break;
+ case T_LCLIST: $$ = f_new_inst(FI_LCLIST_FILTER, $3, $5); break;
+ default: cf_error("Can't filter type %s", f_type_name($3->type));
+ }
+ cf_warn("filter(x,y) function is deprecated, please use x.filter(y)");
+ }
- | 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); }
+ | ROA_CHECK '(' rtable ')' { $$ = f_implicit_roa_check($3); }
+ | ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK, $5, $7, $3); }
| FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT, $3); }
}
| RETURN term ';' {
DBG( "Ook, we'll return the value\n" );
+ if (!this_function)
+ cf_error("Can't return from a non-function, use accept or reject instead.");
+ if (this_function->function->return_type == T_VOID)
+ {
+ if ($2->type != T_VOID)
+ cf_warn("Inferring function %s return type from its return value: %s", this_function->name, f_type_name($2->type));
+ ((struct f_line *) this_function->function)->return_type = $2->type;
+ }
+ else if (this_function->function->return_type != $2->type)
+ cf_error("Can't return type %s from function %s, expected %s",
+ f_type_name($2->type), this_function->name, f_type_name(this_function->function->return_type));
+
$$ = f_new_inst(FI_RETURN, $2);
}
- | dynamic_attr '=' term ';' {
- $$ = f_new_inst(FI_EA_SET, $3, $1);
- }
| static_attr '=' term ';' {
if ($1.readonly)
cf_error( "This static attribute is read-only.");
val_format(v, &b);
return val_dump_buffer;
}
-
+
+struct f_val *
+lp_val_copy(struct linpool *lp, const struct f_val *v)
+{
+ switch (v->type)
+ {
+ case T_VOID:
+ case T_BOOL:
+ case T_INT:
+ case T_IP:
+ case T_PAIR:
+ case T_QUAD:
+ case T_EC:
+ case T_LC:
+ case T_RD:
+ case T_ENUM:
+ case T_PATH_MASK_ITEM:
+ /* These aren't embedded but there is no need to copy them */
+ case T_SET:
+ case T_PREFIX_SET:
+ case T_PATH_MASK:
+ case T_IFACE:
+ {
+ struct f_val *out = lp_alloc(lp, sizeof(*out));
+ *out = *v;
+ return out;
+ }
+
+ case T_NET:
+ {
+ struct {
+ struct f_val val;
+ net_addr net[0];
+ } *out = lp_alloc(lp, sizeof(*out) + v->val.net->length);
+ out->val = *v;
+ out->val.val.net = out->net;
+ net_copy(out->net, v->val.net);
+ return &out->val;
+ }
+
+ case T_STRING:
+ {
+ uint len = strlen(v->val.s);
+ struct {
+ struct f_val val;
+ char buf[0];
+ } *out = lp_alloc(lp, sizeof(*out) + len + 1);
+ out->val = *v;
+ out->val.val.s = out->buf;
+ memcpy(out->buf, v->val.s, len+1);
+ return &out->val;
+ }
+
+ case T_PATH:
+ case T_CLIST:
+ case T_ECLIST:
+ case T_LCLIST:
+ {
+ struct {
+ struct f_val val;
+ struct adata ad;
+ } *out = lp_alloc(lp, sizeof(*out) + v->val.ad->length);
+ out->val = *v;
+ out->val.val.ad = &out->ad;
+ memcpy(&out->ad, v->val.ad, v->val.ad->length);
+ return &out->val;
+ }
+
+ default:
+ bug("Unknown type in value copy: %d", v->type);
+ }
+}
#define _BIRD_FILTER_DATA_H_
#include "nest/bird.h"
-
-/* Type numbers must be in 0..0xff range */
-#define T_MASK 0xff
-
-/* Internal types */
-enum f_type {
-/* Nothing. Simply nothing. */
- T_VOID = 0,
-
-/* User visible types, which fit in int */
- T_INT = 0x10,
- T_BOOL = 0x11,
- T_PAIR = 0x12, /* Notice that pair is stored as integer: first << 16 | second */
- T_QUAD = 0x13,
-
-/* Put enumerational types in 0x30..0x3f range */
- T_ENUM_LO = 0x30,
- T_ENUM_HI = 0x3f,
-
- T_ENUM_RTS = 0x30,
- T_ENUM_BGP_ORIGIN = 0x31,
- T_ENUM_SCOPE = 0x32,
- T_ENUM_RTC = 0x33,
- T_ENUM_RTD = 0x34,
- T_ENUM_ROA = 0x35,
- T_ENUM_NETTYPE = 0x36,
- T_ENUM_RA_PREFERENCE = 0x37,
- T_ENUM_AF = 0x38,
-
-/* new enums go here */
- T_ENUM_EMPTY = 0x3f, /* Special hack for atomic_aggr */
-
-#define T_ENUM T_ENUM_LO ... T_ENUM_HI
-
-/* Bigger ones */
- T_IP = 0x20,
- T_NET = 0x21,
- T_STRING = 0x22,
- T_PATH_MASK = 0x23, /* mask for BGP path */
- T_PATH = 0x24, /* BGP path */
- T_CLIST = 0x25, /* Community list */
- T_EC = 0x26, /* Extended community value, u64 */
- T_ECLIST = 0x27, /* Extended community list */
- T_LC = 0x28, /* Large community value, lcomm */
- T_LCLIST = 0x29, /* Large community list */
- T_RD = 0x2a, /* Route distinguisher for VPN addresses */
- T_PATH_MASK_ITEM = 0x2b, /* Path mask item for path mask constructors */
- T_BYTESTRING = 0x2c,
-
- T_SET = 0x80,
- T_PREFIX_SET = 0x81,
-} PACKED;
+#include "lib/type.h"
+ struct f_method {
+ struct symbol *sym;
+ struct f_inst *(*new_inst)(struct f_inst *obj, struct f_inst *args);
+ uint arg_num;
+ };
+
/* Filter value; size of this affects filter memory consumption */
struct f_val {
- enum f_type type; /* T_* */
- union {
- uint i;
- u64 ec;
- lcomm lc;
- ip_addr ip;
- const net_addr *net;
- const char *s;
- const struct bytestring *bs;
- const struct f_tree *t;
- const struct f_trie *ti;
- const struct adata *ad;
- const struct f_path_mask *path_mask;
- struct f_path_mask_item pmi;
- } val;
+ btype type; /* T_* */
+ union bval_long val;
};
-/* Dynamic attribute definition (eattrs) */
-struct f_dynamic_attr {
- u8 type; /* EA type (EAF_*) */
- u8 bit; /* For bitfield accessors */
- enum f_type f_type; /* Filter type */
- uint ea_code; /* EA code */
-};
+#define fputip(a) ({ ip_addr *ax = falloc(sizeof(*ax)); *ax = (a); ax; })
enum f_sa_code {
- SA_FROM = 1,
- SA_GW,
+ SA_GW = 1,
SA_NET,
SA_PROTO,
- SA_SOURCE,
- SA_SCOPE,
SA_DEST,
SA_IFNAME,
SA_IFINDEX,
#define F_CMP_ERROR 999
-const char *f_type_name(enum f_type t);
-enum f_type f_type_element_type(enum f_type t);
-struct sym_scope *f_type_method_scope(enum f_type t);
+const char *f_type_name(btype t);
-
+enum btype f_type_element_type(btype t);
++struct sym_scope *f_type_method_scope(btype t);
int val_same(const struct f_val *v1, const struct f_val *v2);
int val_compare(const struct f_val *v1, const struct f_val *v2);
m4_define(SYMBOL, `FID_MEMBER(struct symbol *, sym, [[strcmp(f1->sym->name, f2->sym->name) || (f1->sym->class != f2->sym->class)]], "symbol %s", item->sym->name)')
m4_define(RTC, `FID_MEMBER(struct rtable_config *, rtc, [[strcmp(f1->rtc->name, f2->rtc->name)]], "route table %s", item->rtc->name)')
m4_define(STATIC_ATTR, `FID_MEMBER(struct f_static_attr, sa, f1->sa.sa_code != f2->sa.sa_code,,)')
-m4_define(DYNAMIC_ATTR, `FID_MEMBER(struct f_dynamic_attr, da, f1->da.ea_code != f2->da.ea_code,,)')
+m4_define(DYNAMIC_ATTR, `FID_MEMBER(const struct ea_class *, da, f1->da != f2->da,,)')
m4_define(ACCESS_RTE, `FID_HIC(,[[do { if (!fs->rte) runtime("No route to access"); } while (0)]],NEVER_CONSTANT())')
+ # Method constructor block
+ m4_define(METHOD_CONSTRUCTOR, `m4_dnl
+ FID_NEW_METHOD()m4_dnl
+ if (args) cf_error("Too many arguments");
+ m4_define([[INST_IS_METHOD]])
+ m4_define([[INST_METHOD_NAME]],$1)
+ FID_INTERPRET_BODY()')
+
# 2) Code wrapping
# The code produced in 1xx temporary diversions is a raw code without
# any auxiliary commands and syntactical structures around. When the
}
]])
- sym = cf_new_symbol(&f_type_method_scopes[INST_METHOD_OBJECT_TYPE],
- global_root_scope_pool, global_root_scope_linpool,
- INST_METHOD_NAME);
+ m4_ifdef([[INST_IS_METHOD]],m4_dnl
+ FID_METHOD()m4_dnl
+ [[struct f_inst * NONNULL(1)
+ f_new_method_]]INST_NAME()[[(struct f_inst *obj, struct f_inst *args)
+ {
+ /* Unwind the arguments (INST_METHOD_NUM_ARGS) */
+ m4_undivert(111)m4_dnl
+ return f_new_inst(INST_NAME, obj
+ m4_undivert(112)
+ );
+ }
+
+ FID_METHOD_SCOPE_INIT()m4_dnl
+ [INST_METHOD_OBJECT_TYPE] = {},
+ FID_METHOD_REGISTER()m4_dnl
++ sym = cf_root_symbol(INST_METHOD_NAME, &f_type_method_scopes[INST_METHOD_OBJECT_TYPE]);
+ sym->class = SYM_METHOD;
+ sym->method = method = lp_allocz(global_root_scope_linpool, sizeof(struct f_method));
+
+ *method = (struct f_method) {
+ .sym = sym,
+ .new_inst = f_new_method_]]INST_NAME()[[,
+ .arg_num = INST_METHOD_NUM_ARGS,
+ };
+
+ ]])m4_dnl
+
FID_DUMP_CALLER()m4_dnl Case in another big switch used in instruction dumping (debug)
case INST_NAME(): f_dump_line_item_]]INST_NAME()[[(item, indent + 1); break;
#undef v3
#undef vv
-struct sym_scope *f_type_method_scope(enum f_type t)
+ /* Method constructor wrappers */
+ FID_WR_PUT(11)
+
+ #if defined(__GNUC__) && __GNUC__ >= 6
+ #pragma GCC diagnostic push
+ #pragma GCC diagnostic ignored "-Woverride-init"
+ #endif
+
+ static struct sym_scope f_type_method_scopes[] = {
+ FID_WR_PUT(12)
+ };
+
+ #if defined(__GNUC__) && __GNUC__ >= 6
+ #pragma GCC diagnostic pop
+ #endif
+
++struct sym_scope *f_type_method_scope(enum btype t)
+ {
+ return (t < ARRAY_SIZE(f_type_method_scopes)) ? &f_type_method_scopes[t] : NULL;
+ }
+
+ void f_type_methods_register(void)
+ {
+ struct symbol *sym;
+ struct f_method *method;
+ FID_WR_PUT(13)
+
+ for (uint i = 0; i < ARRAY_SIZE(f_type_method_scopes); i++)
+ f_type_method_scopes[i].readonly = 1;
+ }
+
/* Line dumpers */
#define INDENT (((const char *) f_dump_line_indent_str) + sizeof(f_dump_line_indent_str) - (indent) - 1)
static const char f_dump_line_indent_str[] = " ";
* m4_dnl DYNAMIC_ATTR; dynamic attribute definition
* m4_dnl RTC; route table config
* m4_dnl ACCESS_RTE; this instruction needs route
- * m4_dnl ACCESS_EATTRS; this instruction needs extended attributes
*
+ * m4_dnl METHOD_CONSTRUCTOR(name); this instruction is in fact a method of the first argument's type; register it with the given name for that type
+ *
* m4_dnl FID_MEMBER( custom instruction member
* m4_dnl C type, for storage in structs
* m4_dnl name, how the member is named
/* New variable is always the last on stack */
uint pos = curline.vbase + sym->offset;
-- fstk->vstk[pos] = (struct f_val) { };
++ fstk->vstk[pos] = f_get_empty(sym->class & 0xff);
fstk->vcnt = pos + 1;
}
INST(FI_EA_UNSET, 0, 0) {
DYNAMIC_ATTR;
ACCESS_RTE;
- ACCESS_EATTRS;
- f_rta_cow(fs);
- ea_unset_attr(fs->eattrs, fs->pool, 1, da.ea_code);
+ ea_unset_attr(&fs->rte->attrs, 1, da);
+ }
+
+ INST(FI_DEFAULT, 2, 1) {
+ ARG_ANY(1);
+ ARG_ANY(2);
+ RESULT_TYPE(f_type_element_type(v2.type));
+
+ log(L_INFO "Type of arg 1 is: %d", v1.type);
+
+ if (v1.type == T_VOID)
+ RESULT_VAL(v2);
+ else
+ RESULT_VAL(v1);
}
- INST(FI_LENGTH, 1, 1) { /* Get length of */
- ARG_ANY(1);
- switch(v1.type) {
- case T_NET: RESULT(T_INT, i, net_pxlen(v1.val.net)); break;
- case T_PATH: RESULT(T_INT, i, as_path_getlen(v1.val.ad)); break;
- case T_CLIST: RESULT(T_INT, i, int_set_get_size(v1.val.ad)); break;
- case T_ECLIST: RESULT(T_INT, i, ec_set_get_size(v1.val.ad)); break;
- case T_LCLIST: RESULT(T_INT, i, lc_set_get_size(v1.val.ad)); break;
- default: runtime( "Prefix, path, clist or eclist expected" );
- }
+ INST(FI_NET_LENGTH, 1, 1) { /* Get length of */
+ ARG(1, T_NET);
+ METHOD_CONSTRUCTOR("len");
+ RESULT(T_INT, i, net_pxlen(v1.val.net));
+ }
+
+ INST(FI_PATH_LENGTH, 1, 1) { /* Get length of */
+ ARG(1, T_PATH);
+ METHOD_CONSTRUCTOR("len");
+ RESULT(T_INT, i, as_path_getlen(v1.val.ad));
+ }
+
+ INST(FI_CLIST_LENGTH, 1, 1) { /* Get length of */
+ ARG(1, T_CLIST);
+ METHOD_CONSTRUCTOR("len");
+ RESULT(T_INT, i, int_set_get_size(v1.val.ad));
+ }
+
+ INST(FI_ECLIST_LENGTH, 1, 1) { /* Get length of */
+ ARG(1, T_ECLIST);
+ METHOD_CONSTRUCTOR("len");
+ RESULT(T_INT, i, ec_set_get_size(v1.val.ad));
+ }
+
+ INST(FI_LCLIST_LENGTH, 1, 1) { /* Get length of */
+ ARG(1, T_LCLIST);
+ METHOD_CONSTRUCTOR("len");
+ RESULT(T_INT, i, lc_set_get_size(v1.val.ad));
}
INST(FI_NET_SRC, 1, 1) { /* Get src prefix */
struct f_val dummy;
if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy) || (v2.type == T_CLIST))
- RESULT_(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 1) ]]);
+ RESULT(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 1) ]]);
else
runtime("Can't filter pair");
- }
+ }
- else if (v1.type == T_ECLIST)
- {
+ INST(FI_ECLIST_FILTER, 2, 1) {
+ ARG(1, T_ECLIST);
+ ARG_ANY(2);
+ METHOD_CONSTRUCTOR("filter");
/* v2.val is either EC or EC-set */
if ((v2.type == T_SET) && eclist_set_type(v2.val.t) || (v2.type == T_ECLIST))
- RESULT_(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
+ RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
else
runtime("Can't filter ec");
- }
+ }
- else if (v1.type == T_LCLIST)
- {
+ INST(FI_LCLIST_FILTER, 2, 1) {
+ ARG(1, T_LCLIST);
+ ARG_ANY(2);
+ METHOD_CONSTRUCTOR("filter");
/* v2.val is either LC or LC-set */
if ((v2.type == T_SET) && lclist_set_type(v2.val.t) || (v2.type == T_LCLIST))
- RESULT_(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
+ RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
else
runtime("Can't filter lc");
- }
-
- else
- runtime("Can't filter non-[e|l]clist");
}
- INST(FI_ROA_CHECK_IMPLICIT, 0, 1) { /* ROA Check */
- NEVER_CONSTANT;
- RTC(1);
- struct rtable *table = rtc->table;
- ACCESS_RTE;
- ACCESS_EATTRS;
- const net_addr *net = (*fs->rte)->net->n.addr;
-
- /* We ignore temporary attributes, probably not a problem here */
- /* 0x02 is a value of BA_AS_PATH, we don't want to include BGP headers */
- eattr *e = ea_find(*fs->eattrs, EA_CODE(PROTOCOL_BGP, 0x02));
-
- if (!e || ((e->type & EAF_TYPE_MASK) != EAF_TYPE_AS_PATH))
- runtime("Missing AS_PATH attribute");
-
- u32 as = 0;
- as_path_get_last(e->u.ptr, &as);
-
- if (!table)
- runtime("Missing ROA table");
-
- if (table->addr_type != NET_ROA4 && table->addr_type != NET_ROA6)
- runtime("Table type must be either ROA4 or ROA6");
-
- if (table->addr_type != (net->type == NET_IP4 ? NET_ROA4 : NET_ROA6))
- RESULT(T_ENUM_ROA, i, ROA_UNKNOWN); /* Prefix and table type mismatch */
- else
- RESULT(T_ENUM_ROA, i, [[ net_roa_check(table, net, as) ]]);
- }
-
- INST(FI_ROA_CHECK_EXPLICIT, 2, 1) { /* ROA Check */
+ INST(FI_ROA_CHECK, 2, 1) { /* ROA Check */
NEVER_CONSTANT;
ARG(1, T_NET);
ARG(2, T_INT);
* in log(), so it should be written like *L_INFO.
*/
void
-log_commit(int class, buffer *buf)
+log_commit(log_buffer *buf)
{
- struct log_config *l;
-
- if (buf->pos == buf->end)
- strcpy(buf->end - 100, " ... <too long>");
-
- log_lock();
- WALK_LIST(l, *current_log_list)
+ if (buf->buf.pos == buf->buf.end)
+#define TOO_LONG " ... <too long>"
+ memcpy(buf->buf.end - sizeof TOO_LONG, TOO_LONG, sizeof TOO_LONG);
+#undef TOO_LONG
+
+ for (
+ struct log_channel *l = atomic_load_explicit(&global_logs, memory_order_acquire);
+ l;
+ l = atomic_load_explicit(&l->next, memory_order_acquire)
+ )
{
- if (!(l->mask & (1 << class)))
+ uint mask = atomic_load_explicit(&l->mask, memory_order_acquire);
+ if (!(mask & (1 << buf->class)))
continue;
- if (l->fh)
+
+ struct rfile *rf = atomic_load_explicit(&l->rf, memory_order_acquire);
+ if (rf && buf->tm_pos)
{
- if (l->terminal_flag)
- fputs("bird: ", l->fh);
- else
+ *buf->buf.pos = '\n';
+ byte *begin = l->terminal ? buf->buf.start : buf->tm_pos;
+ off_t msg_len = buf->buf.pos - begin + 1;
+ do {
- if (rf_write(rf, buf->tm_pos, msg_len))
++ if (rf_write(rf, begin, msg_len))
+ break;
+
+ log_lock();
+ rf = atomic_load_explicit(&l->rf, memory_order_acquire);
- if (rf_write(rf, buf->tm_pos, msg_len))
++ if (rf_write(rf, begin, msg_len))
{
- byte tbuf[TM_DATETIME_BUFFER_SIZE];
- const char *fmt = config ? config->tf_log.fmt1 : "%F %T.%3f";
- if (!tm_format_real_time(tbuf, sizeof(tbuf), fmt, current_real_time()))
- strcpy(tbuf, "<error>");
-
- if (l->limit)
- {
- off_t msg_len = strlen(tbuf) + strlen(class_names[class]) +
- (buf->pos - buf->start) + 5;
-
- if (l->pos < 0)
- l->pos = log_size(l);
-
- if (l->pos + msg_len > l->limit)
- if (log_rotate(l) < 0)
- continue;
+ log_unlock();
+ break;
+ }
- l->pos += msg_len;
- }
+ log_rotate(l);
+ log_unlock();
- fprintf(l->fh, "%s <%s> ", tbuf, class_names[class]);
- }
- fputs(buf->start, l->fh);
- fputc('\n', l->fh);
- fflush(l->fh);
+ rf = atomic_load_explicit(&l->rf, memory_order_relaxed);
- } while (!rf_write(rf, buf->tm_pos, msg_len));
++ } while (!rf_write(rf, begin, msg_len));
}
#ifdef HAVE_SYSLOG_H
else
int buffer_vprint(buffer *buf, const char *fmt, va_list args);
+void
+log_prepare(log_buffer *buf, int class)
+{
+ buf->buf.start = buf->buf.pos = buf->block;
+ buf->buf.end = buf->block + sizeof buf->block;
+
+ int lf = atomic_load_explicit(&logging_flags, memory_order_acquire);
+ if (lf & LOGGING_TO_TERMINAL)
+ buffer_puts(&buf->buf, "bird: ");
+
+ if (lf & LOGGING_TO_FILE)
+ {
+ const char *fmt = config ? config->tf_log.fmt1 : "%F %T.%3f";
+
+ buf->tm_pos = buf->buf.pos;
+ int t = tm_format_real_time(buf->buf.pos, buf->buf.end - buf->buf.pos, fmt, current_real_time());
+ if (t)
+ buf->buf.pos += t;
+ else
+ buffer_puts(&buf->buf, "<time format error>");
+
+ buffer_print(&buf->buf, " [%04x] <%s> ", THIS_THREAD_ID, class_names[class]);
+ }
++ else
++ buf->tm_pos = NULL;
+
+ buf->msg_pos = buf->buf.pos;
+ buf->class = class;
+}
+
static void
vlog(int class, const char *msg, va_list args)
{