0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2025-01-03 07:31:54 +00:00

Merge commit 'fc9d471b' into thread-next

Conflicts:
	conf/cf-lex.l
	conf/conf.h
	filter/config.Y
	filter/data.c
	filter/data.h
This commit is contained in:
Maria Matejka 2023-10-28 23:42:21 +02:00
commit 1a49a4aea8
17 changed files with 899 additions and 329 deletions

View File

@ -79,7 +79,8 @@ static uint cf_hash(const byte *c);
HASH_DEFINE_REHASH_FN(SYM, struct symbol)
/* 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,
@ -567,7 +568,7 @@ check_eof(void)
static inline void cf_swap_soft_scope(struct config *conf);
static struct symbol *
struct symbol *
cf_new_symbol(struct sym_scope *scope, pool *p, struct linpool *lp, const byte *c)
{
if (scope->readonly)
@ -594,14 +595,20 @@ cf_new_symbol(struct sym_scope *scope, pool *p, struct linpool *lp, const byte *
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);
@ -715,6 +722,8 @@ cf_lex_symbol(const char *data)
cf_lval.i = -val;
return ENUM;
}
case SYM_METHOD:
return sym->method->arg_num ? CF_SYM_METHOD_ARGS : CF_SYM_METHOD_BARE;
case SYM_VOID:
return CF_SYM_UNDEFINED;
default:
@ -753,6 +762,8 @@ ea_class_find_by_name(const char *name)
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
@ -764,10 +775,8 @@ ea_class_find_by_name(const char *name)
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");
for (const struct keyword *k = keyword_list; k->name; k++)
{
struct symbol *s = cf_root_symbol(k->name, &global_root_scope);
@ -777,6 +786,8 @@ cf_lex_init(int is_cli, struct config *c)
global_root_scope.readonly = 1;
global_filter_scope.readonly = 1;
f_type_methods_register();
}
ifs_head = ifs = push_ifs(NULL);

View File

@ -81,6 +81,7 @@ int config_status(void);
btime config_timer_status(void);
void config_init(void);
void cf_error(const char *msg, ...) NORET;
#define cf_warn(msg, args...) log(L_WARN "%s:%d:%d: " msg, ifs->file_name, ifs->lino, ifs->chno - ifs->toklen + 1, ##args)
void config_add_obstacle(struct config *);
void config_del_obstacle(struct config *);
void order_shutdown(int gr);
@ -129,6 +130,7 @@ struct symbol {
struct f_val *val; /* For SYM_CONSTANT */
uint offset; /* For SYM_VARIABLE */
const struct keyword *keyword; /* For SYM_KEYWORD */
const struct f_method *method; /* For SYM_METHOD */
};
char name[0];
@ -150,6 +152,9 @@ struct sym_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[];
@ -166,6 +171,7 @@ struct bytestring {
#define SYM_TABLE 5
#define SYM_ATTRIBUTE 6
#define SYM_KEYWORD 7
#define SYM_METHOD 8
#define SYM_VARIABLE 0x100 /* 0x100-0x1ff are variable types */
#define SYM_VARIABLE_RANGE SYM_VARIABLE ... (SYM_VARIABLE | 0xff)
@ -213,6 +219,10 @@ struct symbol *cf_localize_symbol(struct config *conf, struct symbol *sym);
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

View File

@ -106,7 +106,7 @@ CF_DECLS
%token <ip4> IP4
%token <ip6> IP6
%token <i64> VPN_RD
%token <s> CF_SYM_KNOWN CF_SYM_UNDEFINED
%token <s> CF_SYM_KNOWN CF_SYM_UNDEFINED CF_SYM_METHOD_BARE CF_SYM_METHOD_ARGS
%token <t> TEXT
%token <bs> BYTETEXT
%type <iface> ipa_scope
@ -137,7 +137,7 @@ CF_DECLS
%start config
CF_KEYWORDS(DEFINE, ON, OFF, YES, NO, S, MS, US, PORT, VPN, MPLS, FROM)
CF_KEYWORDS(DEFINE, ON, OFF, YES, NO, S, MS, US, PORT, VPN, MPLS, FROM, MAX, AS)
CF_GRAMMAR

View File

@ -33,6 +33,8 @@ m4_define(CF_iterate, `m4_define([[CF_iter]], m4_defn([[$1]]))CF_itera($2)')
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')
# CLI commands
m4_define(CF_CLI, `m4_define([[CF_cmd]], cmd_[[]]m4_translit($1, [[ ]], _))DNL

View File

@ -551,7 +551,7 @@ include "tablename.conf";;
Define a filter. You can learn more about filters in the following
chapter.
<tag><label id="opt-function">function <m/name/ (<m/parameters/) <m/local variables/ { <m/commands/ }</tag>
<tag><label id="opt-function">function <m/type/ <m/name/ (<m/parameters/) <m/local variables/ { <m/commands/ }</tag>
Define a function. You can learn more about functions in the following chapter.
<tag><label id="opt-protocol">protocol rip|ospf|bgp|<m/.../ [<m/name/ [from <m/name2/]] { <m>protocol options</m> }</tag>
@ -1346,18 +1346,22 @@ block of code conditional.
<p>BIRD supports functions, so that you don't have to repeat the same blocks of
code over and over. Functions can have zero or more parameters and they can have
local variables. Recursion is not allowed. Function definitions look like this:
local variables. You should always specify the function return type and always
return it. No-return functions and multiple-type returning functions are deprecated.
Direct recursion is possible. Function definitions look like this:
<code>
function name ()
function int name ()
{
int local_variable;
int another_variable = 5;
return 42;
}
function with_parameters (int parameter)
function pair with_parameters (int parameter)
{
print parameter;
return (1, 2);
}
</code>
@ -1371,7 +1375,7 @@ may return values using the <cf>return <m/[expr]/</cf> command. Returning a
value exits from current function (this is similar to C).
<p>Filters are defined in a way similar to functions except they cannot have
explicit parameters. They get a route table entry as an implicit parameter, it
explicit parameters and cannot return. They get a route table entry as an implicit parameter, it
is also passed automatically to any functions called. The filter must terminate
with either <cf/accept/ or <cf/reject/ statement. If there is a runtime error in
filter, the route is rejected.
@ -1638,24 +1642,24 @@ in the foot).
<cf><m/P/.len</cf> returns the length of path <m/P/.
<cf><m/P/.empty</cf> makes the path <m/P/ empty.
<cf><m/P/.empty</cf> makes the path <m/P/ empty. Can't be used as a value, always modifies the object.
<cf>prepend(<m/P/,<m/A/)</cf> prepends ASN <m/A/ to path <m/P/ and
<cf><m/P/.prepend(<m/A/)</cf> prepends ASN <m/A/ to path <m/P/ and
returns the result.
<cf>delete(<m/P/,<m/A/)</cf> deletes all instances of ASN <m/A/ from
<cf><m/P/.delete(<m/A/)</cf> deletes all instances of ASN <m/A/ from
from path <m/P/ and returns the result. <m/A/ may also be an integer
set, in that case the operator deletes all ASNs from path <m/P/ that are
also members of set <m/A/.
<cf>filter(<m/P/,<m/A/)</cf> deletes all ASNs from path <m/P/ that are
not members of integer set <m/A/. I.e., <cf/filter/ do the same as
<cf/delete/ with inverted set <m/A/.
<cf><m/P/.filter(<m/A/)</cf> deletes all ASNs from path <m/P/ that are
not members of integer set <m/A/, and returns the result.
I.e., <cf/filter/ do the same as <cf/delete/ with inverted set <m/A/.
Statement <cf><m/P/ = prepend(<m/P/, <m/A/);</cf> can be shortened to
<cf><m/P/.prepend(<m/A/);</cf> if <m/P/ is appropriate route attribute
(for example <cf/bgp_path/) or a local variable.
Similarly for <cf/delete/ and <cf/filter/.
Methods <cf>prepend</cf>, <cf>delete</cf> and <cf>filter</cf> keep the
original object intact as long as you use the result in any way. You can
also write e.g. <cf><m/P/.prepend(<m/A/);</cf> as a standalone statement.
This variant does modify the original object with the result of the operation.
<tag><label id="type-bgpmask">bgpmask</tag>
BGP masks are patterns used for BGP path matching (using <cf>path
@ -1683,29 +1687,29 @@ in the foot).
<cf><m/C/.len</cf> returns the length of clist <m/C/.
<cf><m/C/.empty</cf> makes the list <m/C/ empty.
<cf><m/C/.empty</cf> makes the list <m/C/ empty. Can't be used as a value, always modifies the object.
<cf>add(<m/C/,<m/P/)</cf> adds pair (or quad) <m/P/ to clist <m/C/ and
<cf><m/C/.add(<m/P/)</cf> adds pair (or quad) <m/P/ to clist <m/C/ and
returns the result. If item <m/P/ is already in clist <m/C/, it does
nothing. <m/P/ may also be a clist, in that case all its members are
added; i.e., it works as clist union.
<cf>delete(<m/C/,<m/P/)</cf> deletes pair (or quad) <m/P/ from clist
<cf><m/C/.delete(<m/P/)</cf> deletes pair (or quad) <m/P/ from clist
<m/C/ and returns the result. If clist <m/C/ does not contain item
<m/P/, it does nothing. <m/P/ may also be a pair (or quad) set, in that
case the operator deletes all items from clist <m/C/ that are also
members of set <m/P/. Moreover, <m/P/ may also be a clist, which works
analogously; i.e., it works as clist difference.
<cf>filter(<m/C/,<m/P/)</cf> deletes all items from clist <m/C/ that are
not members of pair (or quad) set <m/P/. I.e., <cf/filter/ do the same
<cf><m/C/.filter(<m/P/)</cf> deletes all items from clist <m/C/ that are
not members of pair (or quad) set <m/P/, and returns the result. I.e., <cf/filter/ do the same
as <cf/delete/ with inverted set <m/P/. <m/P/ may also be a clist, which
works analogously; i.e., it works as clist intersection.
Statement <cf><m/C/ = add(<m/C/, <m/P/);</cf> can be shortened to
<cf><m/C/.add(<m/P/);</cf> if <m/C/ is appropriate route attribute (for
example <cf/bgp_community/) or a local variable.
Similarly for <cf/delete/ and <cf/filter/.
Methods <cf>add</cf>, <cf>delete</cf> and <cf>filter</cf> keep the
original object intact as long as you use the result in any way. You can
also write e.g. <cf><m/P/.add(<m/A/);</cf> as a standalone statement.
This variant does modify the original object with the result of the operation.
<cf><m/C/.min</cf> returns the minimum element of clist <m/C/.

View File

@ -19,25 +19,60 @@ 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; }
static struct symbol *this_function;
static struct f_method_scope {
struct f_inst *object;
struct sym_scope *main;
struct sym_scope scope;
} f_method_scope_stack[32];
static int f_method_scope_pos = -1;
#define FM (f_method_scope_stack[f_method_scope_pos])
static inline void f_push_method_scope(struct f_inst *object)
static inline void f_method_call_start(struct f_inst *object)
{
if (object->type == T_VOID)
cf_error("Can't infer type to properly call a method, please assign the value to a variable");
if (++f_method_scope_pos >= (int) ARRAY_SIZE(f_method_scope_stack))
cf_error("Too many nested method calls");
struct sym_scope *scope = f_type_method_scope(object->type);
if (!scope)
cf_error("No methods defined for type %s", f_type_name(object->type));
FM = (struct f_method_scope) {
.object = object,
.main = new_config->current_scope,
.scope = {
.next = NULL,
.hash = scope->hash,
.active = 1,
.block = 1,
.readonly = 1,
},
};
new_config->current_scope = &FM.scope;
}
static inline void f_pop_method_scope(void)
static inline void f_method_call_args(void)
{
ASSERT_DIE(FM.scope.active);
FM.scope.active = 0;
new_config->current_scope = FM.main;
}
static inline void f_method_call_end(void)
{
ASSERT_DIE(f_method_scope_pos >= 0);
if (FM.scope.active) {
ASSERT_DIE(&FM.scope == new_config->current_scope);
new_config->current_scope = FM.main;
FM.scope.active = 0;
}
f_method_scope_pos--;
}
@ -209,20 +244,6 @@ f_new_lc_item(u32 f1, u32 t1, u32 f2, u32 t2, u32 f3, u32 t3)
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_implicit_roa_check(struct rtable_config *tab)
{
@ -349,35 +370,35 @@ CF_DECLS
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, 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, 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, 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 <fsa> static_attr
%type <f> filter where_filter
%type <fl> filter_body function_body
%type <flv> lvalue
%type <i> type function_vars
%type <i> type maybe_type function_vars
%type <fa> function_argsn function_args
%type <ecs> ec_kind
%type <fret> break_command
@ -403,6 +424,7 @@ filter_def:
$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 };
@ -534,6 +556,7 @@ 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 };
@ -562,30 +585,39 @@ function_body:
;
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));
$2->function = dummy;
this_function->function = dummy;
dummy->return_type = $2;
/* Revert the args */
while ($4) {
struct f_arg *tmp = $4;
$4 = $4->next;
while ($5) {
struct f_arg *tmp = $5;
$5 = $5->next;
tmp->next = dummy->arg_list;
dummy->arg_list = tmp;
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();
}
@ -869,33 +901,18 @@ static_attr:
| GW_MPLS { $$ = f_new_static_attr(T_INT, SA_GW_MPLS, 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:
@ -925,21 +942,42 @@ term:
| static_attr { $$ = f_new_inst(FI_RTA_GET, $1); }
| term '.' {
f_push_method_scope($1);
} method_term {
f_pop_method_scope();
$$ = $4;
}
| term_dot_method
| '+' 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_implicit_roa_check($3); }
| ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK, $5, $7, $3); }
@ -1024,6 +1062,18 @@ cmd:
}
| 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);
}
| static_attr '=' term ';' {
@ -1062,9 +1112,8 @@ cmd:
}
| lvalue '.' {
f_push_method_scope(f_lval_getter(&$1));
} method_cmd ';' {
f_pop_method_scope();
f_method_call_start(f_lval_getter(&$1));
} method_name_cont ';' {
$$ = f_lval_setter(&$1, $4);
}
| BT_ASSERT '(' get_cf_position term get_cf_position ')' ';' { $$ = assert_done($4, $3 + 1, $5 - 1); }

View File

@ -613,7 +613,6 @@ val_dump(const struct f_val *v) {
return val_dump_buffer;
}
struct f_val *
lp_val_copy(struct linpool *lp, const struct f_val *v)
{

View File

@ -13,6 +13,12 @@
#include "nest/bird.h"
#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 {
btype type; /* T_* */
@ -210,8 +216,8 @@ trie_match_next_longest_ip6(net_addr_ip6 *n, ip6_addr *found)
#define F_CMP_ERROR 999
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);

View File

@ -34,6 +34,9 @@ m4_divert(-1)m4_dnl
# 102 constructor arguments
# 110 constructor attributes
# 103 constructor body
# 111 method constructor body
# 112 instruction constructor call from method constructor
# 113 method constructor symbol registrator
# 104 dump line item content
# (there may be nothing in dump-line content and
# it must be handled specially in phase 2)
@ -48,6 +51,8 @@ m4_define(FID_STRUCT_IN, `m4_divert(101)')
m4_define(FID_NEW_ARGS, `m4_divert(102)')
m4_define(FID_NEW_ATTRIBUTES, `m4_divert(110)')
m4_define(FID_NEW_BODY, `m4_divert(103)')
m4_define(FID_NEW_METHOD, `m4_divert(111)')
m4_define(FID_METHOD_CALL, `m4_divert(112)')
m4_define(FID_DUMP_BODY, `m4_divert(104)m4_define([[FID_DUMP_BODY_EXISTS]])')
m4_define(FID_LINEARIZE_BODY, `m4_divert(105)')
m4_define(FID_SAME_BODY, `m4_divert(106)')
@ -120,7 +125,13 @@ FID_IFCONST([[
constargs = 0;
]])
} while (child$1 = child$1->next);
FID_LINEARIZE_BODY
m4_define([[INST_METHOD_NUM_ARGS]],m4_eval($1-1))m4_dnl
m4_ifelse($1,1,,[[FID_NEW_METHOD()m4_dnl
struct f_inst *arg$1 = args;
if (args == NULL) cf_error("Not enough arguments"); /* INST_NAME */
args = args->next;
FID_METHOD_CALL() , arg$1]])
FID_LINEARIZE_BODY()m4_dnl
pos = linearize(dest, whati->f$1, pos);
FID_INTERPRET_BODY()')
@ -170,28 +181,29 @@ FID_HIC(,[[
m4_define(ARG, `ARG_ANY($1) ARG_TYPE($1,$2)')
m4_define(ARG_TYPE, `ARG_TYPE_STATIC($1,$2) ARG_TYPE_DYNAMIC($1,$2)')
m4_define(ARG_TYPE_STATIC, `
m4_define(ARG_TYPE_STATIC, `m4_dnl
m4_ifelse($1,1,[[m4_define([[INST_METHOD_OBJECT_TYPE]],$2)]],)m4_dnl
FID_NEW_BODY()m4_dnl
if (f$1->type && (f$1->type != ($2)) && !f_const_promotion(f$1, ($2)))
cf_error("Argument $1 of %s must be of type %s, got type %s",
f_instruction_name(what->fi_code), f_type_name($2), f_type_name(f$1->type));
FID_INTERPRET_BODY()')
m4_define(ARG_TYPE_DYNAMIC, `
m4_define(ARG_TYPE_DYNAMIC, `m4_dnl
FID_INTERPRET_EXEC()m4_dnl
if (v$1.type != ($2))
runtime("Argument $1 of %s must be of type %s, got type %s",
f_instruction_name(what->fi_code), f_type_name($2), f_type_name(v$1.type));
FID_INTERPRET_BODY()')
m4_define(ARG_SAME_TYPE, `
m4_define(ARG_SAME_TYPE, `m4_dnl
FID_NEW_BODY()m4_dnl
if (f$1->type && f$2->type && (f$1->type != f$2->type) &&
!f_const_promotion(f$2, f$1->type) && !f_const_promotion(f$1, f$2->type))
cf_error("Arguments $1 and $2 of %s must be of the same type", f_instruction_name(what->fi_code));
FID_INTERPRET_BODY()')
m4_define(ARG_PREFER_SAME_TYPE, `
m4_define(ARG_PREFER_SAME_TYPE, `m4_dnl
FID_NEW_BODY()m4_dnl
if (f$1->type && f$2->type && (f$1->type != f$2->type))
(void) (f_const_promotion(f$2, f$1->type) || f_const_promotion(f$1, f$2->type));
@ -267,6 +279,14 @@ m4_define(STATIC_ATTR, `FID_MEMBER(struct f_static_attr, sa, f1->sa.sa_code != f
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
@ -286,6 +306,7 @@ m4_define(ACCESS_RTE, `FID_HIC(,[[do { if (!fs->rte) runtime("No route to access
# 10 iterate
# 1 union in struct f_inst
# 3 constructors + interpreter
# 11 method constructors
#
# These global diversions contain blocks of code that can be directly
# put into the final file, yet it still can't be written out now as
@ -305,6 +326,9 @@ m4_define(FID_DUMP_CALLER, `FID_ZONE(7, Dump line caller)')
m4_define(FID_LINEARIZE, `FID_ZONE(8, Linearize)')
m4_define(FID_SAME, `FID_ZONE(9, Comparison)')
m4_define(FID_ITERATE, `FID_ZONE(10, Iteration)')
m4_define(FID_METHOD, `FID_ZONE(11, Method constructor)')
m4_define(FID_METHOD_SCOPE_INIT, `FID_ZONE(12, Method scope initializator)')
m4_define(FID_METHOD_REGISTER, `FID_ZONE(13, Method registrator)')
# This macro does all the code wrapping. See inline comments.
m4_define(INST_FLUSH, `m4_ifdef([[INST_NAME]], [[
@ -362,6 +386,33 @@ m4_undivert(102)m4_dnl
}
]])
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;
@ -415,6 +466,8 @@ m4_define([[INST_INVAL]], [[$2]])m4_dnl instruction input value count,
m4_define([[INST_OUTVAL]], [[$3]])m4_dnl instruction output value count,
m4_undefine([[INST_NEVER_CONSTANT]])m4_dnl reset NEVER_CONSTANT trigger,
m4_undefine([[INST_RESULT_TYPE]])m4_dnl and reset RESULT_TYPE value.
m4_undefine([[INST_IS_METHOD]])m4_dnl and reset method constructor request.
m4_undefine([[INST_METHOD_OBJECT_TYPE]],)m4_dnl reset method object type,
FID_INTERPRET_BODY()m4_dnl By default, every code is interpreter code.
')
@ -538,6 +591,37 @@ FID_WR_PUT(3)
#undef v3
#undef vv
/* 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[] = " ";

View File

@ -71,6 +71,8 @@
* m4_dnl RTC; route table config
* m4_dnl ACCESS_RTE; this instruction needs route
*
* 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
@ -496,20 +498,15 @@
RESULT(T_BOOL, i, (v1.type != T_VOID) && !undef_value(v1));
}
INST(FI_TYPE, 1, 1) {
ARG_ANY(1); /* There may be more types supporting this operation */
switch (v1.type)
{
case T_NET:
INST(FI_NET_TYPE, 1, 1) {
ARG(1, T_NET);
METHOD_CONSTRUCTOR("type");
RESULT(T_ENUM_NETTYPE, i, v1.val.net->type);
break;
default:
runtime( "Can't determine type of this item" );
}
}
INST(FI_IS_V4, 1, 1) {
ARG(1, T_IP);
METHOD_CONSTRUCTOR("is_v4");
RESULT(T_BOOL, i, ipa_is_ip4(v1.val.ip));
}
@ -533,7 +530,7 @@
/* 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;
}
@ -567,6 +564,30 @@
RESULT_VAL(val);
}
INST(FI_PATH_EMPTY, 1, 1) {
ARG(1, T_PATH);
METHOD_CONSTRUCTOR("empty");
RESULT(T_PATH, ad, &null_adata);
}
INST(FI_CLIST_EMPTY, 1, 1) {
ARG(1, T_CLIST);
METHOD_CONSTRUCTOR("empty");
RESULT(T_CLIST, ad, &null_adata);
}
INST(FI_ECLIST_EMPTY, 1, 1) {
ARG(1, T_ECLIST);
METHOD_CONSTRUCTOR("empty");
RESULT(T_ECLIST, ad, &null_adata);
}
INST(FI_LCLIST_EMPTY, 1, 1) {
ARG(1, T_LCLIST);
METHOD_CONSTRUCTOR("empty");
RESULT(T_LCLIST, ad, &null_adata);
}
INST(FI_FOR_INIT, 1, 0) {
NEVER_CONSTANT;
ARG_ANY(1);
@ -951,20 +972,39 @@
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 */
ARG(1, T_NET);
METHOD_CONSTRUCTOR("src");
net_addr_union *net = (void *) v1.val.net;
net_addr *src = falloc(sizeof(net_addr_ip6));
@ -1000,6 +1040,7 @@
INST(FI_NET_DST, 1, 1) { /* Get dst prefix */
ARG(1, T_NET);
METHOD_CONSTRUCTOR("dst");
net_addr_union *net = (void *) v1.val.net;
net_addr *dst = falloc(sizeof(net_addr_ip6));
@ -1035,6 +1076,7 @@
INST(FI_ROA_MAXLEN, 1, 1) { /* Get ROA max prefix length */
ARG(1, T_NET);
METHOD_CONSTRUCTOR("maxlen")
if (!net_is_roa(v1.val.net))
runtime( "ROA expected" );
@ -1043,40 +1085,38 @@
((net_addr_roa6 *) v1.val.net)->max_pxlen);
}
INST(FI_ASN, 1, 1) { /* Get ROA ASN or community ASN part */
ARG_ANY(1);
RESULT_TYPE(T_INT);
switch(v1.type)
{
case T_NET:
INST(FI_NET_ASN, 1, 1) { /* Get ROA ASN or community ASN part */
ARG(1, T_NET);
METHOD_CONSTRUCTOR("asn");
if (!net_is_roa(v1.val.net))
runtime( "ROA expected" );
RESULT_(T_INT, i, (v1.val.net->type == NET_ROA4) ?
RESULT(T_INT, i, (v1.val.net->type == NET_ROA4) ?
((net_addr_roa4 *) v1.val.net)->asn :
((net_addr_roa6 *) v1.val.net)->asn);
break;
case T_PAIR:
RESULT_(T_INT, i, v1.val.i >> 16);
break;
case T_LC:
RESULT_(T_INT, i, v1.val.lc.asn);
break;
default:
runtime( "Net, pair or lc expected" );
}
}
INST(FI_IP, 1, 1) { /* Convert prefix to ... */
INST(FI_PAIR_ASN, 1, 1) { /* Get ROA ASN or community ASN part */
ARG(1, T_PAIR);
METHOD_CONSTRUCTOR("asn");
RESULT(T_INT, i, v1.val.i >> 16);
}
INST(FI_LC_ASN, 1, 1) { /* Get ROA ASN or community ASN part */
ARG(1, T_LC);
METHOD_CONSTRUCTOR("asn");
RESULT(T_INT, i, v1.val.lc.asn);
}
INST(FI_NET_IP, 1, 1) { /* Convert prefix to ... */
ARG(1, T_NET);
METHOD_CONSTRUCTOR("ip");
RESULT(T_IP, ip, net_prefix(v1.val.net));
}
INST(FI_ROUTE_DISTINGUISHER, 1, 1) {
ARG(1, T_NET);
METHOD_CONSTRUCTOR("rd");
if (!net_is_vpn(v1.val.net))
runtime( "VPN address expected" );
RESULT(T_RD, ec, net_rd(v1.val.net));
@ -1084,6 +1124,7 @@
INST(FI_AS_PATH_FIRST, 1, 1) { /* Get first ASN from AS PATH */
ARG(1, T_PATH);
METHOD_CONSTRUCTOR("first");
u32 as = 0;
as_path_get_first(v1.val.ad, &as);
RESULT(T_INT, i, as);
@ -1091,6 +1132,7 @@
INST(FI_AS_PATH_LAST, 1, 1) { /* Get last ASN from AS PATH */
ARG(1, T_PATH);
METHOD_CONSTRUCTOR("last");
u32 as = 0;
as_path_get_last(v1.val.ad, &as);
RESULT(T_INT, i, as);
@ -1098,90 +1140,74 @@
INST(FI_AS_PATH_LAST_NAG, 1, 1) { /* Get last ASN from non-aggregated part of AS PATH */
ARG(1, T_PATH);
METHOD_CONSTRUCTOR("last_nonaggregated");
RESULT(T_INT, i, as_path_get_last_nonaggregated(v1.val.ad));
}
INST(FI_PAIR_DATA, 1, 1) { /* Get data part from the standard community */
ARG(1, T_PAIR);
METHOD_CONSTRUCTOR("data");
RESULT(T_INT, i, v1.val.i & 0xFFFF);
}
INST(FI_LC_DATA1, 1, 1) { /* Get data1 part from the large community */
ARG(1, T_LC);
METHOD_CONSTRUCTOR("data1");
RESULT(T_INT, i, v1.val.lc.ldp1);
}
INST(FI_LC_DATA2, 1, 1) { /* Get data2 part from the large community */
ARG(1, T_LC);
METHOD_CONSTRUCTOR("data2");
RESULT(T_INT, i, v1.val.lc.ldp2);
}
INST(FI_MIN, 1, 1) { /* Get minimum element from list */
ARG_ANY(1);
RESULT_TYPE(f_type_element_type(v1.type));
switch(v1.type)
{
case T_CLIST:
{
INST(FI_CLIST_MIN, 1, 1) { /* Get minimum element from list */
ARG(1, T_CLIST);
METHOD_CONSTRUCTOR("min");
u32 val = 0;
int_set_min(v1.val.ad, &val);
RESULT_(T_PAIR, i, val);
}
break;
case T_ECLIST:
{
u64 val = 0;
ec_set_min(v1.val.ad, &val);
RESULT_(T_EC, ec, val);
}
break;
case T_LCLIST:
{
lcomm val = { 0, 0, 0 };
lc_set_min(v1.val.ad, &val);
RESULT_(T_LC, lc, val);
}
break;
default:
runtime( "Clist or lclist expected" );
}
RESULT(T_PAIR, i, val);
}
INST(FI_MAX, 1, 1) { /* Get maximum element from list */
ARG_ANY(1);
RESULT_TYPE(f_type_element_type(v1.type));
switch(v1.type)
{
case T_CLIST:
{
INST(FI_CLIST_MAX, 1, 1) { /* Get minimum element from list */
ARG(1, T_CLIST);
METHOD_CONSTRUCTOR("max");
u32 val = 0;
int_set_max(v1.val.ad, &val);
RESULT_(T_PAIR, i, val);
RESULT(T_PAIR, i, val);
}
break;
case T_ECLIST:
{
INST(FI_ECLIST_MIN, 1, 1) { /* Get minimum element from list */
ARG(1, T_ECLIST);
METHOD_CONSTRUCTOR("min");
u64 val = 0;
ec_set_min(v1.val.ad, &val);
RESULT(T_EC, ec, val);
}
INST(FI_ECLIST_MAX, 1, 1) { /* Get minimum element from list */
ARG(1, T_ECLIST);
METHOD_CONSTRUCTOR("max");
u64 val = 0;
ec_set_max(v1.val.ad, &val);
RESULT_(T_EC, ec, val);
RESULT(T_EC, ec, val);
}
break;
case T_LCLIST:
{
lcomm val = { 0, 0, 0 };
INST(FI_LCLIST_MIN, 1, 1) { /* Get minimum element from list */
ARG(1, T_LCLIST);
METHOD_CONSTRUCTOR("min");
lcomm val = {};
lc_set_min(v1.val.ad, &val);
RESULT(T_LC, lc, val);
}
INST(FI_LCLIST_MAX, 1, 1) { /* Get minimum element from list */
ARG(1, T_LCLIST);
METHOD_CONSTRUCTOR("max");
lcomm val = {};
lc_set_max(v1.val.ad, &val);
RESULT_(T_LC, lc, val);
}
break;
default:
runtime( "Clist or lclist expected" );
}
RESULT(T_LC, lc, val);
}
INST(FI_RETURN, 1, 0) {
@ -1216,7 +1242,7 @@
SYMBOL;
/* Fake result type declaration */
RESULT_TYPE(T_VOID);
RESULT_TYPE(sym->function->return_type);
FID_NEW_BODY()
ASSERT(sym->class == SYM_FUNCTION);
@ -1321,6 +1347,7 @@
INST(FI_IP_MASK, 2, 1) { /* IP.MASK(val) */
ARG(1, T_IP);
ARG(2, T_INT);
METHOD_CONSTRUCTOR("mask");
RESULT(T_IP, ip, [[ ipa_is_ip4(v1.val.ip) ?
ipa_from_ip4(ip4_and(ipa_to_ip4(v1.val.ip), ip4_mkmask(v2.val.i))) :
ipa_from_ip6(ip6_and(ipa_to_ip6(v1.val.ip), ip6_mkmask(v2.val.i))) ]]);
@ -1329,167 +1356,158 @@
INST(FI_PATH_PREPEND, 2, 1) { /* Path prepend */
ARG(1, T_PATH);
ARG(2, T_INT);
METHOD_CONSTRUCTOR("prepend");
RESULT(T_PATH, ad, [[ as_path_prepend(fpool, v1.val.ad, v2.val.i) ]]);
}
INST(FI_CLIST_ADD, 2, 1) { /* (Extended) Community list add */
ARG_ANY(1);
ARG(1, T_CLIST);
ARG_ANY(2);
RESULT_TYPE(f1->type);
if (v1.type == T_PATH)
runtime("Can't add to path");
else if (v1.type == T_CLIST)
{
/* Community (or cluster) list */
METHOD_CONSTRUCTOR("add");
struct f_val dummy;
if ((v2.type == T_PAIR) || (v2.type == T_QUAD))
RESULT_(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, v2.val.i) ]]);
RESULT(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, v2.val.i) ]]);
/* IP->Quad implicit conversion */
else if (val_is_ip4(&v2))
RESULT_(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]);
RESULT(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]);
else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy))
runtime("Can't add set");
else if (v2.type == T_CLIST)
RESULT_(T_CLIST, ad, [[ int_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
RESULT(T_CLIST, ad, [[ int_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
else
runtime("Can't add non-pair");
}
else if (v1.type == T_ECLIST)
{
INST(FI_ECLIST_ADD, 2, 1) {
ARG(1, T_ECLIST);
ARG_ANY(2);
METHOD_CONSTRUCTOR("add");
/* v2.val is either EC or EC-set */
if ((v2.type == T_SET) && eclist_set_type(v2.val.t))
runtime("Can't add set");
else if (v2.type == T_ECLIST)
RESULT_(T_ECLIST, ad, [[ ec_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
RESULT(T_ECLIST, ad, [[ ec_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
else if (v2.type != T_EC)
runtime("Can't add non-ec");
else
RESULT_(T_ECLIST, ad, [[ ec_set_add(fpool, v1.val.ad, v2.val.ec) ]]);
RESULT(T_ECLIST, ad, [[ ec_set_add(fpool, v1.val.ad, v2.val.ec) ]]);
}
else if (v1.type == T_LCLIST)
{
INST(FI_LCLIST_ADD, 2, 1) {
ARG(1, T_LCLIST);
ARG_ANY(2);
METHOD_CONSTRUCTOR("add");
/* v2.val is either LC or LC-set */
if ((v2.type == T_SET) && lclist_set_type(v2.val.t))
runtime("Can't add set");
else if (v2.type == T_LCLIST)
RESULT_(T_LCLIST, ad, [[ lc_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
RESULT(T_LCLIST, ad, [[ lc_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
else if (v2.type != T_LC)
runtime("Can't add non-lc");
else
RESULT_(T_LCLIST, ad, [[ lc_set_add(fpool, v1.val.ad, v2.val.lc) ]]);
RESULT(T_LCLIST, ad, [[ lc_set_add(fpool, v1.val.ad, v2.val.lc) ]]);
}
else
runtime("Can't add to non-[e|l]clist");
}
INST(FI_CLIST_DEL, 2, 1) { /* (Extended) Community list add or delete */
ARG_ANY(1);
INST(FI_PATH_DEL, 2, 1) { /* Path delete */
ARG(1, T_PATH);
ARG_ANY(2);
RESULT_TYPE(f1->type);
if (v1.type == T_PATH)
{
METHOD_CONSTRUCTOR("delete");
if ((v2.type == T_SET) && path_set_type(v2.val.t) || (v2.type == T_INT))
RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 0) ]]);
RESULT(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 0) ]]);
else
runtime("Can't delete non-integer (set)");
}
else if (v1.type == T_CLIST)
{
INST(FI_CLIST_DEL, 2, 1) { /* (Extended) Community list add or delete */
ARG(1, T_CLIST);
ARG_ANY(2);
METHOD_CONSTRUCTOR("delete");
/* Community (or cluster) list */
struct f_val dummy;
if ((v2.type == T_PAIR) || (v2.type == T_QUAD))
RESULT_(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, v2.val.i) ]]);
RESULT(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, v2.val.i) ]]);
/* IP->Quad implicit conversion */
else if (val_is_ip4(&v2))
RESULT_(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]);
RESULT(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]);
else 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, 0) ]]);
RESULT(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 0) ]]);
else
runtime("Can't delete non-pair");
}
else if (v1.type == T_ECLIST)
{
INST(FI_ECLIST_DEL, 2, 1) { /* (Extended) Community list add or delete */
ARG(1, T_ECLIST);
ARG_ANY(2);
METHOD_CONSTRUCTOR("delete");
/* 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, 0) ]]);
RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
else if (v2.type != T_EC)
runtime("Can't delete non-ec");
else
RESULT_(T_ECLIST, ad, [[ ec_set_del(fpool, v1.val.ad, v2.val.ec) ]]);
RESULT(T_ECLIST, ad, [[ ec_set_del(fpool, v1.val.ad, v2.val.ec) ]]);
}
else if (v1.type == T_LCLIST)
{
INST(FI_LCLIST_DEL, 2, 1) { /* (Extended) Community list add or delete */
ARG(1, T_LCLIST);
ARG_ANY(2);
METHOD_CONSTRUCTOR("delete");
/* 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, 0) ]]);
RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
else if (v2.type != T_LC)
runtime("Can't delete non-lc");
else
RESULT_(T_LCLIST, ad, [[ lc_set_del(fpool, v1.val.ad, v2.val.lc) ]]);
RESULT(T_LCLIST, ad, [[ lc_set_del(fpool, v1.val.ad, v2.val.lc) ]]);
}
else
runtime("Can't delete in non-[e|l]clist");
}
INST(FI_CLIST_FILTER, 2, 1) { /* (Extended) Community list add or delete */
ARG_ANY(1);
INST(FI_PATH_FILTER, 2, 1) { /* (Extended) Community list add or delete */
ARG(1, T_PATH);
ARG_ANY(2);
RESULT_TYPE(f1->type);
METHOD_CONSTRUCTOR("filter");
if (v1.type == T_PATH)
{
if ((v2.type == T_SET) && path_set_type(v2.val.t))
RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 1) ]]);
RESULT(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 1) ]]);
else
runtime("Can't filter integer");
}
else if (v1.type == T_CLIST)
{
INST(FI_CLIST_FILTER, 2, 1) {
ARG(1, T_CLIST);
ARG_ANY(2);
METHOD_CONSTRUCTOR("filter");
/* Community (or cluster) list */
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, 2, 1) { /* ROA Check */
NEVER_CONSTANT;
ARG(1, T_NET);

View File

@ -48,6 +48,7 @@ struct f_line {
u8 args; /* Function: Args required */
u8 vars;
u8 results; /* Results left on stack: cmd -> 0, term -> 1 */
u8 return_type; /* Type which the function returns */
struct f_arg *arg_list;
struct f_line_item items[0]; /* The items themselves */
};

View File

@ -124,17 +124,17 @@ attribute lclist test_ca_lclist_max21;
define one = 1;
define ten = 10;
function onef(int a)
function int onef(int a)
{
return 1;
}
function twof(int a)
function int twof(int a)
{
return 2;
}
function oneg(int a)
function int oneg(int a)
{
return 1;
}
@ -385,7 +385,7 @@ bt_test_suite(t_bytestring, "Testing bytestrings");
* -------------
*/
function 'mkpair-a'(int a)
function pair 'mkpair-a'(int a)
{
return (1, a);
}
@ -860,14 +860,14 @@ bt_test_suite(t_flowspec, "Testing flowspec routes");
* -------------
*/
function mkpath(int a; int b)
function bgpmask mkpath(int a; int b)
{
return [= a b 3 2 1 =];
}
define set35 = [3 .. 5];
function t_path()
function t_path_old()
bgpmask pm1;
bgppath p2;
int set set12;
@ -946,7 +946,91 @@ int set set12;
bt_assert(x = 18 && y = 50);
}
bt_test_suite(t_path, "Testing paths");
bt_test_suite(t_path_old, "Testing paths (old syntax)");
function t_path_new()
{
bgpmask pm1 = [= 4 3 2 1 =];
int set set12 = [1, 2];
bt_assert(format(pm1) = "[= 4 3 2 1 =]");
bt_assert(+empty+ = +empty+);
bt_assert(10 !~ +empty+);
bgppath p2;
bt_assert(p2 = +empty+);
p2.prepend(1);
p2.prepend(2);
p2.prepend(3);
p2.prepend(4);
bt_assert(p2.empty = +empty+);
bt_assert(format(p2) = "(path 4 3 2 1)");
bt_assert(p2.len = 4);
bt_assert(p2 ~ pm1);
bt_assert(3 ~ p2);
bt_assert(p2 ~ [2, 10..20]);
bt_assert(p2 ~ [4, 10..20]);
bt_assert(p2 !~ []);
p2.prepend(5);
bt_assert(p2 !~ pm1);
bt_assert(10 !~ p2);
bt_assert(p2 !~ [8, ten..(2*ten)]);
bt_assert(p2 ~ [= * 4 3 * 1 =]);
bt_assert(p2 ~ [= (3+2) (2*2) 3 2 1 =]);
bt_assert(p2 ~ [= 5 [2, 4, 6] 3 [1..2] 1 =]);
bt_assert(p2 ~ [= 5 set35 3 set12 set12 =]);
bt_assert(p2 ~ mkpath(5, 4));
bt_assert(p2 ~ [= * [3] * =]);
bt_assert(p2 !~ [= * [] * =]);
bt_assert(p2.len = 5);
bt_assert(p2.first = 5);
bt_assert(p2.last = 1);
bt_assert(p2.len = 5);
bt_assert(p2.delete(3) = +empty+.prepend(1).prepend(2).prepend(4).prepend(5));
bt_assert(p2.filter([1..3]) = +empty+.prepend(1).prepend(2).prepend(3));
bt_assert(p2.delete([]) = p2);
bt_assert(p2.filter([]) = +empty+);
bt_assert(+empty+.prepend(0).prepend(1).delete([]) = +empty+.prepend(0).prepend(1));
bt_assert(+empty+.prepend(0).prepend(1).filter([]) = +empty+);
p2 = +empty+;
p2.prepend(5);
p2.prepend(4);
p2.prepend(3);
p2.prepend(3);
p2.prepend(2);
p2.prepend(1);
bt_assert(p2 !~ [= 1 2 3 4 5 =]);
bt_assert(p2 ~ [= 1 2 * 4 5 =]);
bt_assert(p2 ~ [= 1 2 * 3 4 5 =]);
bt_assert(p2 ~ [= 1 2 3+ 4 5 =]);
bt_assert(p2 ~ [= 1 2 3+ 4+ 5 =]);
bt_assert(p2 !~ [= 1 2 3+ 5+ 4 5 =]);
bt_assert(p2 !~ [= 1 2 3 3 5+ 4 5 =]);
bt_assert(p2.delete(3) = +empty+.prepend(5).prepend(4).prepend(2).prepend(1));
bt_assert(p2.delete([4..5]) = +empty+.prepend(3).prepend(3).prepend(2).prepend(1));
bt_assert(format([= 1 2+ 3 =]) = "[= 1 2 + 3 =]");
# iteration over path
int x = 0;
int y = 0;
for int i in p2 do {
x = x + i;
y = y + x;
}
bt_assert(x = 18 && y = 50);
}
bt_test_suite(t_path_new, "Testing paths (new syntax)");
@ -958,7 +1042,7 @@ bt_test_suite(t_path, "Testing paths");
define p23 = (2, 3);
function t_clist()
function t_clist_old()
clist l;
clist l2;
clist r;
@ -1078,7 +1162,129 @@ clist r;
bt_assert(x = 36);
}
bt_test_suite(t_clist, "Testing lists of communities");
bt_test_suite(t_clist_old, "Testing lists of communities (old syntax)");
function t_clist_new()
{
bt_assert((10, 20).asn = 10);
bt_assert((10, 20).data = 20);
bt_assert(p23.asn = 2);
bt_assert(p23.data = 3);
clist l;
bt_assert(l = -empty-);
bt_assert(l !~ [(*,*)]);
bt_assert((l ~ [(*,*)]) != (l !~ [(*,*)]));
bt_assert(-empty- = -empty-);
l.add( (one,2) );
bt_assert(l ~ [(*,*)]);
l.add( (2,one+2) );
bt_assert(format(l) = "(clist (1,2) (2,3))");
bt_assert(l.empty = -empty-);
bt_assert((2,3) ~ l);
bt_assert(l ~ [(1,*)]);
bt_assert(l ~ [p23]);
bt_assert(l ~ [(2,2..3)]);
bt_assert(l ~ [(1,1..2)]);
bt_assert(l ~ [(1,1)..(1,2)]);
bt_assert(l !~ []);
l.add((2,5));
l.add((5,one));
l.add((6,one));
l.add((one,one));
l.delete([(5,1),(6,one),(one,1)]);
l.delete([(5,one),(6,one)]);
l.filter([(1,*)]);
bt_assert(l = -empty-.add((1,2)));
bt_assert((2,3) !~ l);
bt_assert(l !~ [(2,*)]);
bt_assert(l !~ [(one,3..6)]);
bt_assert(l ~ [(*,*)]);
l.add((3,one));
l.add((one+one+one,one+one));
l.add((3,3));
l.add((3,4));
l.add((3,5));
clist l2 = l.filter([(3,*)]);
l.delete([(3,2..4)]);
bt_assert(l = -empty-.add((1,2)).add((3,1)).add((3,5)));
bt_assert(l.len = 3);
l.add((3,2));
l.add((4,5));
bt_assert(l = -empty-.add((1,2)).add((3,1)).add((3,5)).add((3,2)).add((4,5)));
bt_assert(l.len = 5);
bt_assert(l ~ [(*,2)]);
bt_assert(l ~ [(*,5)]);
bt_assert(l ~ [(*, one)]);
bt_assert(l !~ [(*,3)]);
bt_assert(l !~ [(*,(one+6))]);
bt_assert(l !~ [(*, (one+one+one))]);
bt_assert(l.delete([]) = l);
bt_assert(l.filter([]) = -empty-);
l.delete([(*,(one+onef(3)))]);
l.delete([(*,(4+one))]);
bt_assert(l = -empty-.add((3,1)));
l.delete([(*,(onef(5)))]);
bt_assert(l = -empty-);
l2.add((3,6));
l = l2.filter([(3,1..4)]);
l2.filter([(3,3..6)]);
# clist A (10,20,30)
bt_assert(l = -empty-.add((3,1)).add((3,2)).add((3,3)).add((3,4)));
bt_assert(format(l) = "(clist (3,1) (3,2) (3,3) (3,4))");
# clist B (30,40,50)
bt_assert(l2 = -empty-.add((3,3)).add((3,4)).add((3,5)).add((3,6)));
bt_assert(format(l2) = "(clist (3,3) (3,4) (3,5) (3,6))");
# clist A union B
clist r = l.add(l2);
bt_assert(r = -empty-.add((3,1)).add((3,2)).add((3,3)).add((3,4)).add((3,5)).add((3,6)));
bt_assert(format(r) = "(clist (3,1) (3,2) (3,3) (3,4) (3,5) (3,6))");
# clist A isect B
r = l.filter(l2);
bt_assert(r = -empty-.add((3,3)).add((3,4)));
bt_assert(format(r) = "(clist (3,3) (3,4))");
# clist A \ B
r = l.delete(l2);
bt_assert(r = -empty-.add((3,1)).add((3,2)));
bt_assert(format(r) = "(clist (3,1) (3,2))");
# clist in c set
r = l.filter([(3,1), (*,2)]);
bt_assert(r = -empty-.add((3,1)).add((3,2)));
bt_assert(format(r) = "(clist (3,1) (3,2))");
# minimim & maximum element
r = -empty-.add((2,1)).add((1,3)).add((2,2)).add((3,1)).add((2,3));
bt_assert(format(r) = "(clist (2,1) (1,3) (2,2) (3,1) (2,3))");
bt_assert(r.min = (1,3));
bt_assert(r.max = (3,1));
# iteration over clist
int x = 0;
for pair c in r do
x = x + c.asn * c.asn * c.data;
bt_assert(x = 36);
}
bt_test_suite(t_clist_new, "Testing lists of communities (new syntax)");
@ -1113,11 +1319,12 @@ bt_test_suite(t_ec, "Testing extended communities");
* -------------------------------
*/
function t_eclist()
function t_eclist_old()
eclist el;
eclist el2;
eclist r;
{
# Deprecated syntax
el = -- empty --;
el = add(el, (rt, 10, 20));
el = add(el, (ro, 10.20.30.40, 100));
@ -1200,7 +1407,99 @@ eclist r;
bt_assert(x = 3);
}
bt_test_suite(t_eclist, "Testing lists of extended communities");
bt_test_suite(t_eclist_old, "Testing lists of extended communities");
function t_eclist_new()
{
# New syntax
eclist el;
bt_assert(el = --empty--);
el.add((rt, 10, 20));
el.add((ro, 10.20.30.40, 100));
el.add((ro, 11.21.31.41.mask(16), 200));
bt_assert(--empty-- = --empty--);
bt_assert(((rt, 10, 20)) !~ --empty--);
bt_assert(format(el) = "(eclist (rt, 10, 20) (ro, 10.20.30.40, 100) (ro, 11.21.0.0, 200))");
bt_assert(el.len = 3);
el.delete((rt, 10, 20));
el.delete((rt, 10, 30));
bt_assert(el = (--empty--).add((ro, 10.20.30.40, 100)).add((ro, 11.21.0.0, 200)));
bt_assert(el.empty = --empty--);
el.add((unknown 2, ten, 1));
el.add((unknown 5, ten, 1));
el.add((rt, ten, one+one));
el.add((rt, 10, 3));
el.add((rt, 10, 4));
el.add((rt, 10, 5));
el.add((generic, 0x2000a, 3*ten));
el.delete([(rt, 10, 2..ten)]);
bt_assert(el = (--empty--).add((ro, 10.20.30.40, 100)).add((ro, 11.21.0.0, 200)).add((rt, 10, 1)).add((unknown 5, 10, 1)).add((rt, 10, 30)));
el.filter([(rt, 10, *)]);
bt_assert(el = (--empty--).add((rt, 10, 1)).add((rt, 10, 30)));
bt_assert((rt, 10, 1) ~ el);
bt_assert(el ~ [(rt, 10, ten..40)]);
bt_assert((rt, 10, 20) !~ el);
bt_assert((ro, 10.20.30.40, 100) !~ el);
bt_assert(el !~ [(rt, 10, 35..40)]);
bt_assert(el !~ [(ro, 10, *)]);
bt_assert(el !~ []);
el.add((rt, 10, 40));
eclist el2 = el.filter([(rt, 10, 20..40)] );
el2.add((rt, 10, 50));
bt_assert(el.delete([]) = el);
bt_assert(el.filter([]) = --empty--);
# eclist A (1,30,40)
bt_assert(el = --empty--.add((rt, 10, 1)).add((rt, 10, 30)).add((rt, 10, 40)));
bt_assert(format(el) = "(eclist (rt, 10, 1) (rt, 10, 30) (rt, 10, 40))");
# eclist B (30,40,50)
bt_assert(el2 = --empty--.add((rt, 10, 30)).add((rt, 10, 40)).add((rt, 10, 50)));
bt_assert(format(el2) = "(eclist (rt, 10, 30) (rt, 10, 40) (rt, 10, 50))");
# eclist A union B
eclist r = el2.add(el);
bt_assert(r = --empty--.add((rt, 10, 30)).add((rt, 10, 40)).add((rt, 10, 50)).add((rt, 10, 1)));
bt_assert(format(r) = "(eclist (rt, 10, 30) (rt, 10, 40) (rt, 10, 50) (rt, 10, 1))");
# eclist A isect B
r = el.filter(el2);
bt_assert(r = --empty--.add((rt, 10, 30)).add((rt, 10, 40)));
bt_assert(format(r) = "(eclist (rt, 10, 30) (rt, 10, 40))");
# eclist A \ B
r = el.delete(el2);
bt_assert(r = --empty--.add((rt, 10, 1)));
bt_assert(format(r) = "(eclist (rt, 10, 1))");
# eclist in ec set
r = el.filter([(rt, 10, 1), (rt, 10, 25..30), (ro, 10, 40)]);
bt_assert(r = --empty--.add((rt, 10, 1)).add((rt, 10, 30)));
bt_assert(format(r) = "(eclist (rt, 10, 1) (rt, 10, 30))");
# minimim & maximum element
r = --empty--.add((rt, 2, 1)).add((rt, 1, 3)).add((rt, 2, 2)).add((rt, 3, 1)).add((rt, 2, 3));
bt_assert(format(r) = "(eclist (rt, 2, 1) (rt, 1, 3) (rt, 2, 2) (rt, 3, 1) (rt, 2, 3))");
bt_assert(r.min = (rt, 1, 3));
bt_assert(r.max = (rt, 3, 1));
# iteration over eclist
int x = 0;
for ec c in r do
if c > (rt, 2, 0) && c < (rt, 3, 0) then
x = x + 1;
bt_assert(x = 3);
}
bt_test_suite(t_eclist_new, "Testing lists of extended communities");
@ -1244,12 +1543,12 @@ bt_test_suite(t_ec_set, "Testing sets of extended communities");
* -------------------------
*/
function mktrip(int a)
function lc mktrip(int a)
{
return (a, 2*a, 3*a);
}
function t_lclist()
function t_lclist_old()
lclist ll;
lclist ll2;
lclist r;
@ -1331,7 +1630,92 @@ lclist r;
bt_assert(mx = r.max);
}
bt_test_suite(t_lclist, "Testing lists of large communities");
bt_test_suite(t_lclist_old, "Testing lists of large communities");
function t_lclist_new()
{
bt_assert(---empty--- = ---empty---);
bt_assert((10, 20, 30) !~ ---empty---);
bt_assert((10, 20, 30).asn = 10);
bt_assert((10, 20, 30).data1 = 20);
bt_assert((10, 20, 30).data2 = 30);
lclist ll;
bt_assert(ll = ---empty---);
ll.add((ten, 20, 30));
ll.add((1000, 2000, 3000));
ll.add(mktrip(100000));
bt_assert(ll.empty = ---empty---);
bt_assert(format(ll) = "(lclist (10, 20, 30) (1000, 2000, 3000) (100000, 200000, 300000))");
bt_assert(ll.len = 3);
bt_assert(ll = ---empty---.add((10, 20, 30)).add((1000, 2000, 3000)).add((100000, 200000, 300000)));
bt_assert(mktrip(1000) ~ ll);
bt_assert(mktrip(100) !~ ll);
ll.empty;
ll.add((10, 10, 10));
ll.add((20, 20, 20));
ll.add((30, 30, 30));
lclist ll2;
ll2.add((20, 20, 20));
ll2.add((30, 30, 30));
ll2.add((40, 40, 40));
bt_assert(ll.delete([]) = ll);
bt_assert(ll.filter([]) = ---empty---);
# lclist A (10, 20, 30)
bt_assert(format(ll) = "(lclist (10, 10, 10) (20, 20, 20) (30, 30, 30))");
# lclist B (20, 30, 40)
bt_assert(format(ll2) = "(lclist (20, 20, 20) (30, 30, 30) (40, 40, 40))");
# lclist A union B
lclist r = ll.add(ll2);
bt_assert(r = ---empty---.add((10,10,10)).add((20,20,20)).add((30,30,30)).add((40,40,40)));
bt_assert(format(r) = "(lclist (10, 10, 10) (20, 20, 20) (30, 30, 30) (40, 40, 40))");
# lclist A isect B
r = ll.filter(ll2);
bt_assert(r = ---empty---.add((20, 20, 20)).add((30, 30, 30)));
bt_assert(format(r) = "(lclist (20, 20, 20) (30, 30, 30))");
# lclist A \ B
r = ll.delete(ll2);
bt_assert(r = ---empty---.add((10, 10, 10)));
bt_assert(format(r) = "(lclist (10, 10, 10))");
# lclist in lc set
r = ll.filter([(5..15, *, *), (20, 15..25, *)]);
bt_assert(r = ---empty---.add((10, 10, 10)).add((20, 20, 20)));
bt_assert(format(r) = "(lclist (10, 10, 10) (20, 20, 20))");
# minimim & maximum element
r = ---empty---.add((2, 3, 3)).add((1, 2, 3)).add((2, 3, 1)).add((3, 1, 2)).add((2, 1, 3));
bt_assert(format(r) = "(lclist (2, 3, 3) (1, 2, 3) (2, 3, 1) (3, 1, 2) (2, 1, 3))");
bt_assert(r.min = (1, 2, 3));
bt_assert(r.max = (3, 1, 2));
# iteration over lclist
int x = 0;
int y = 0;
lc mx = (0, 0, 0);
for lc c in r do {
int asn2 = c.asn * c.asn;
x = x + asn2 * c.data1;
y = y + asn2 * c.data2;
if c > mx then mx = c;
}
bt_assert(x = 39 && y = 49);
bt_assert(mx = r.max);
}
bt_test_suite(t_lclist_new, "Testing lists of large communities");
@ -1474,7 +1858,7 @@ bt_test_suite(t_define, "Testing defined() function");
* -------------------------
*/
function callme(int arg1; int arg2)
function int callme(int arg1; int arg2)
int i;
{
case arg1 {
@ -1485,12 +1869,12 @@ int i;
return 0;
}
function callmeagain(int a; int b; int c)
function int callmeagain(int a; int b; int c)
{
return a + b + c;
}
function fifteen()
function int fifteen()
{
return 15;
}
@ -1523,28 +1907,28 @@ function local_vars(int j)
bt_assert(j = 35 && k = 20 && m = 100);
}
function factorial(int x)
function int factorial(int x)
{
if x = 0 then return 0;
if x = 1 then return 1;
else return x * factorial(x - 1);
}
function fibonacci(int x)
function int fibonacci(int x)
{
if x = 0 then return 0;
if x = 1 then return 1;
else return fibonacci(x - 1) + fibonacci(x - 2);
}
function hanoi_init(int a; int b)
function bgppath hanoi_init(int a; int b)
{
if b = 0
then return +empty+;
else return prepend(hanoi_init(a + 1, b - 1), a);
}
function hanoi_solve(int n; bgppath h_src; bgppath h_dst; bgppath h_aux; bool x; bool y)
function bgppath hanoi_solve(int n; bgppath h_src; bgppath h_dst; bgppath h_aux; bool x; bool y)
{
# x -> return src or dst
# y -> print state

View File

@ -97,7 +97,7 @@ bfd_iface_finish:
BFD_IFACE->passwords = get_passwords();
if (!BFD_IFACE->auth_type != !BFD_IFACE->passwords)
log(L_WARN "Authentication and password options should be used together");
cf_warn("Authentication and password options should be used together");
if (BFD_IFACE->passwords)
{

View File

@ -175,7 +175,7 @@ bgp_proto:
| bgp_proto DEFAULT BGP_MED expr ';' { BGP_CFG->default_med = $4; }
| bgp_proto DEFAULT BGP_LOCAL_PREF expr ';' { BGP_CFG->default_local_pref = $4; }
| bgp_proto SOURCE ADDRESS ipa ';' { BGP_CFG->local_ip = $4; }
| bgp_proto START DELAY TIME expr ';' { BGP_CFG->connect_delay_time = $5; log(L_WARN "%s: Start delay time option is deprecated, use connect delay time", this_proto->name); }
| bgp_proto START DELAY TIME expr ';' { BGP_CFG->connect_delay_time = $5; cf_warn("%s: Start delay time option is deprecated, use connect delay time", this_proto->name); }
| bgp_proto CONNECT DELAY TIME expr ';' { BGP_CFG->connect_delay_time = $5; }
| bgp_proto CONNECT RETRY TIME expr ';' { BGP_CFG->connect_retry_time = $5; }
| bgp_proto KEEPALIVE TIME expr ';' { BGP_CFG->keepalive_time = $4; if (($4<1) || ($4>65535)) cf_error("Keepalive time must be in range 1-65535"); }
@ -268,7 +268,7 @@ bgp_channel_item:
| NEXT HOP KEEP bgp_nh { BGP_CC->next_hop_keep = $4; }
| NEXT HOP PREFER GLOBAL { BGP_CC->next_hop_prefer = NHP_GLOBAL; }
| MANDATORY bool { BGP_CC->mandatory = $2; }
| MISSING LLADDR bgp_lladdr { log(L_WARN "%s.%s: Missing lladdr option is deprecated and ignored, remove it", this_proto->name, this_channel->name); }
| MISSING LLADDR bgp_lladdr { cf_warn("%s.%s: Missing lladdr option is deprecated and ignored, remove it", this_proto->name, this_channel->name); }
| GATEWAY DIRECT { BGP_CC->gw_mode = GW_DIRECT; }
| GATEWAY RECURSIVE { BGP_CC->gw_mode = GW_RECURSIVE; }
| SECONDARY bool { BGP_CC->secondary = $2; }

View File

@ -38,10 +38,10 @@ ospf_iface_finish(void)
ip->passwords = get_passwords();
if (ospf_cfg_is_v2() && (ip->autype == OSPF_AUTH_CRYPT) && (ip->helloint < 5))
log(L_WARN "Hello or poll interval less that 5 makes cryptographic authenication prone to replay attacks");
cf_warn("Hello or poll interval less that 5 makes cryptographic authenication prone to replay attacks");
if ((ip->autype == OSPF_AUTH_NONE) && (ip->passwords != NULL))
log(L_WARN "Password option without authentication option does not make sense");
cf_warn("Password option without authentication option does not make sense");
if (ip->passwords)
{
@ -119,7 +119,7 @@ ospf_proto_finish(void)
if (!ic->instance_id_set)
ic->instance_id = base;
else if (ic->instance_id >= 128)
log(L_WARN "Instance ID %d from unassigned/private range", ic->instance_id);
cf_warn("Instance ID %d from unassigned/private range", ic->instance_id);
else if ((ic->instance_id < base) || (ic->instance_id >= (base + 32)))
cf_error("Instance ID %d invalid for given channel type", ic->instance_id);
}

View File

@ -116,7 +116,7 @@ rip_iface_finish:
RIP_IFACE->passwords = get_passwords();
if (!RIP_IFACE->auth_type != !RIP_IFACE->passwords)
log(L_WARN "Authentication and password options should be used together");
cf_warn("Authentication and password options should be used together");
if (RIP_IFACE->passwords)
{

View File

@ -197,12 +197,12 @@ log_commit(log_buffer *buf)
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))
{
log_unlock();
break;
@ -212,7 +212,7 @@ log_commit(log_buffer *buf)
log_unlock();
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
@ -251,6 +251,8 @@ log_prepare(log_buffer *buf, int class)
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;