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:
commit
1a49a4aea8
@ -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);
|
||||
|
10
conf/conf.h
10
conf/conf.h
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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/.
|
||||
|
||||
|
201
filter/config.Y
201
filter/config.Y
@ -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); }
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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[] = " ";
|
||||
|
344
filter/f-inst.c
344
filter/f-inst.c
@ -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);
|
||||
|
@ -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 */
|
||||
};
|
||||
|
426
filter/test.conf
426
filter/test.conf
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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; }
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user