mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-12-22 09:41:54 +00:00
Merge branch 'thread-next' into HEAD
This commit is contained in:
commit
c6fba7d7e6
@ -60,14 +60,6 @@ stages:
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
build-debian-8-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.nic.cz/labs/bird:debian-8-amd64
|
||||
|
||||
build-debian-8-i386:
|
||||
<<: *build-linux
|
||||
image: registry.nic.cz/labs/bird:debian-8-i386
|
||||
|
||||
build-debian-9-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.nic.cz/labs/bird:debian-9-amd64
|
||||
@ -224,28 +216,6 @@ build-opensuse-15.3-amd64:
|
||||
paths:
|
||||
- pkg/pkgs/*
|
||||
|
||||
# Dpkg error: PATH is not set
|
||||
#pkg-debian-8-amd64:
|
||||
# <<: *pkg-deb
|
||||
# needs: [build-debian-8-amd64]
|
||||
# image: registry.nic.cz/labs/bird:debian-8-amd64
|
||||
|
||||
# Dpkg error: PATH is not set
|
||||
#pkg-debian-8-i386:
|
||||
# <<: *pkg-deb
|
||||
# needs: [build-debian-8-i386]
|
||||
# image: registry.nic.cz/labs/bird:debian-8-i386
|
||||
|
||||
pkg-debian-9-amd64:
|
||||
<<: *pkg-deb
|
||||
needs: [build-debian-9-amd64]
|
||||
image: registry.nic.cz/labs/bird:debian-9-amd64
|
||||
|
||||
pkg-debian-9-i386:
|
||||
<<: *pkg-deb
|
||||
needs: [build-debian-9-i386]
|
||||
image: registry.nic.cz/labs/bird:debian-9-i386
|
||||
|
||||
pkg-debian-10-amd64:
|
||||
<<: *pkg-deb
|
||||
needs: [build-debian-10-amd64]
|
||||
@ -284,7 +254,7 @@ pkg-fedora-33-amd64:
|
||||
pkg-fedora-34-amd64:
|
||||
<<: *pkg-rpm
|
||||
needs: [build-fedora-34-amd64]
|
||||
image: registry.labs.nic.cz/labs/bird:fedora-34-amd64
|
||||
image: registry.nic.cz/labs/bird:fedora-34-amd64
|
||||
|
||||
pkg-centos-8-amd64:
|
||||
<<: *pkg-rpm-wa
|
||||
@ -465,11 +435,11 @@ test-ibgp-loop:
|
||||
variables:
|
||||
TEST_NAME: cf-ibgp-loop
|
||||
|
||||
test-ibgp-loop-big:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-ibgp-loop-big
|
||||
|
||||
#test-ibgp-loop-big:
|
||||
# <<: *test-base
|
||||
# variables:
|
||||
# TEST_NAME: cf-ibgp-loop-big
|
||||
#
|
||||
test-ibgp-flat:
|
||||
<<: *test-base
|
||||
variables:
|
||||
|
26
NEWS
26
NEWS
@ -29,6 +29,32 @@ Version 3.0-alpha0 (2022-02-07)
|
||||
o Lots of refactoring
|
||||
o Bugfixes and improvements as they came along
|
||||
|
||||
Version 2.13.1 (2023-06-23)
|
||||
o BGP: Fix role check when no capability option is present
|
||||
o Filter: Fixed segfault when a case option had an empty block
|
||||
|
||||
This is a bugfix version.
|
||||
|
||||
Version 2.13 (2023-04-21)
|
||||
o Babel: IPv4 via IPv6 extension (RFC 9229)
|
||||
o Babel: Improve authentication on lossy networks
|
||||
o BGP: New 'allow bgp_med' option
|
||||
o BSD: Support for IPv4 routes with IPv6 nexthop on FreeBSD
|
||||
o Experimental BMP protocol implementation
|
||||
o Important bugfixes
|
||||
|
||||
Notes:
|
||||
|
||||
We changed versioning scheme from <epoch>.<major>.<minor> to more common
|
||||
<major>.<minor>.<patch> . From now on, you may expect that BIRD 2.13.x will be
|
||||
strictly only fixing bugs found in 2.13, whereas BIRD 2.14 will also contain
|
||||
new features.
|
||||
|
||||
This BIRD version contains an alpha release of BMP protocol implementation.
|
||||
It is not ready for production usage and therefore it is not compiled by
|
||||
default and have to be enabled during installation.
|
||||
|
||||
|
||||
Version 2.0.12 (2023-01-23)
|
||||
o Filter: New 'onlink' route attribute
|
||||
o Compile-time option to use 4-way tries instead of 16-way ones
|
||||
|
@ -34,6 +34,7 @@ class BIRDFValPrinter(BIRDPrinter):
|
||||
"T_IP": "ip",
|
||||
"T_NET": "net",
|
||||
"T_STRING": "s",
|
||||
"T_BYTESTRING": "bs",
|
||||
"T_PATH_MASK": "path_mask",
|
||||
"T_PATH": "ad",
|
||||
"T_CLIST": "ad",
|
||||
|
285
conf/cf-lex.l
285
conf/cf-lex.l
@ -42,7 +42,7 @@
|
||||
#define PARSER 1
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "filter/filter.h"
|
||||
#include "filter/f-inst.h"
|
||||
@ -51,12 +51,6 @@
|
||||
#include "lib/string.h"
|
||||
#include "lib/hash.h"
|
||||
|
||||
struct keyword {
|
||||
byte *name;
|
||||
int value;
|
||||
struct keyword *next;
|
||||
};
|
||||
|
||||
#include "conf/keywords.h"
|
||||
|
||||
/* Could be defined by Bison in cf-parse.tab.h, inteferes with SYM hash */
|
||||
@ -67,16 +61,10 @@ struct keyword {
|
||||
|
||||
static uint cf_hash(const byte *c);
|
||||
|
||||
#define KW_KEY(n) n->name
|
||||
#define KW_NEXT(n) n->next
|
||||
#define KW_EQ(a,b) !strcmp(a,b)
|
||||
#define KW_FN(k) cf_hash(k)
|
||||
#define KW_ORDER 8 /* Fixed */
|
||||
|
||||
#define SYM_KEY(n) n->name, n->scope->active
|
||||
#define SYM_KEY(n) n->name
|
||||
#define SYM_NEXT(n) n->next
|
||||
#define SYM_EQ(a,s1,b,s2) !strcmp(a,b) && s1 == s2
|
||||
#define SYM_FN(k,s) cf_hash(k)
|
||||
#define SYM_EQ(a,b) !strcmp(a,b)
|
||||
#define SYM_FN(k) cf_hash(k)
|
||||
#define SYM_ORDER 4 /* Initial */
|
||||
|
||||
#define SYM_REHASH sym_rehash
|
||||
@ -85,14 +73,19 @@ static uint cf_hash(const byte *c);
|
||||
|
||||
HASH_DEFINE_REHASH_FN(SYM, struct symbol)
|
||||
|
||||
HASH(struct keyword) kw_hash;
|
||||
HASH(struct ea_class) ea_name_hash;
|
||||
/* Global symbol scopes */
|
||||
pool *global_root_scope_pool;
|
||||
linpool *global_root_scope_linpool;
|
||||
struct sym_scope
|
||||
global_root_scope = {
|
||||
},
|
||||
global_filter_scope = {
|
||||
.next = &global_root_scope,
|
||||
};
|
||||
|
||||
/* Local symbol scope: TODO this isn't thread-safe */
|
||||
struct sym_scope *conf_this_scope;
|
||||
|
||||
static struct sym_scope global_root_scope__init = { .active = 1, };
|
||||
struct sym_scope *global_root_scope = &global_root_scope__init;
|
||||
|
||||
linpool *cfg_mem;
|
||||
|
||||
int (*cf_read_hook)(byte *buf, unsigned int max, int fd);
|
||||
@ -258,35 +251,24 @@ WHITE [ \t]
|
||||
return IP4;
|
||||
}
|
||||
|
||||
{XIGIT}{2}((:{XIGIT}{2}){15,}|({XIGIT}{2}){15,}) {
|
||||
({XIGIT}{2}){16,}|{XIGIT}{2}(:{XIGIT}{2}){15,}|hex:({XIGIT}{2}(:?{XIGIT}{2})*)? {
|
||||
char *s = yytext;
|
||||
size_t len = 0, i;
|
||||
struct bytestring *bytes;
|
||||
byte *b;
|
||||
struct adata *bs;
|
||||
|
||||
while (*s) {
|
||||
len++;
|
||||
s += 2;
|
||||
if (*s == ':')
|
||||
s++;
|
||||
}
|
||||
bytes = cfg_allocz(sizeof(*bytes) + len);
|
||||
/* Skip 'hex:' prefix */
|
||||
if (s[0] == 'h' && s[1] == 'e' && s[2] == 'x' && s[3] == ':')
|
||||
s += 4;
|
||||
|
||||
bytes->length = len;
|
||||
b = &bytes->data[0];
|
||||
s = yytext;
|
||||
errno = 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
*b = bstrtobyte16(s);
|
||||
if (errno == ERANGE)
|
||||
int len = bstrhextobin(s, NULL);
|
||||
if (len < 0)
|
||||
cf_error("Invalid hex string");
|
||||
b++;
|
||||
s += 2;
|
||||
if (*s == ':')
|
||||
s++;
|
||||
}
|
||||
cf_lval.bs = bytes;
|
||||
return BYTESTRING;
|
||||
|
||||
bs = cfg_allocz(sizeof(struct adata) + len);
|
||||
bs->length = bstrhextobin(s, bs->data);
|
||||
ASSERT(bs->length == len);
|
||||
|
||||
cf_lval.bs = bs;
|
||||
return BYTETEXT;
|
||||
}
|
||||
|
||||
({XIGIT}*::|({XIGIT}*:){3,})({XIGIT}*|{DIGIT}+\.{DIGIT}+\.{DIGIT}+\.{DIGIT}+) {
|
||||
@ -402,6 +384,7 @@ else: {
|
||||
\>\= return GEQ;
|
||||
\&\& return AND;
|
||||
\|\| return OR;
|
||||
\-\> return IMP;
|
||||
|
||||
\[\= return PO;
|
||||
\=\] return PC;
|
||||
@ -577,49 +560,56 @@ check_eof(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void cf_swap_soft_scope(void);
|
||||
static inline void cf_swap_soft_scope(struct config *conf);
|
||||
|
||||
static struct symbol *
|
||||
cf_new_symbol(const byte *c)
|
||||
struct symbol *
|
||||
cf_new_symbol(struct sym_scope *scope, pool *p, struct linpool *lp, const byte *c)
|
||||
{
|
||||
if (scope->readonly)
|
||||
cf_error("Unknown symbol %s", c);
|
||||
|
||||
struct symbol *s;
|
||||
|
||||
uint l = strlen(c);
|
||||
if (l > SYM_MAX_LEN)
|
||||
cf_error("Symbol too long");
|
||||
|
||||
cf_swap_soft_scope();
|
||||
s = lp_alloc(lp, sizeof(struct symbol) + l + 1);
|
||||
*s = (struct symbol) { .scope = scope, .class = SYM_VOID, };
|
||||
memcpy(s->name, c, l+1);
|
||||
|
||||
s = cfg_allocz(sizeof(struct symbol) + l + 1);
|
||||
*s = (struct symbol) { .scope = conf_this_scope, .class = SYM_VOID, };
|
||||
strcpy(s->name, c);
|
||||
if (!scope->hash.data)
|
||||
HASH_INIT(scope->hash, p, SYM_ORDER);
|
||||
|
||||
if (!conf_this_scope->hash.data)
|
||||
HASH_INIT(conf_this_scope->hash, new_config->pool, SYM_ORDER);
|
||||
HASH_INSERT2(scope->hash, SYM, p, s);
|
||||
|
||||
HASH_INSERT2(conf_this_scope->hash, SYM, new_config->pool, s);
|
||||
|
||||
if (conf_this_scope == new_config->root_scope)
|
||||
if (new_config && (scope == new_config->root_scope))
|
||||
add_tail(&(new_config->symbols), &(s->n));
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static struct symbol *
|
||||
cf_root_symbol(const byte *c)
|
||||
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);
|
||||
*s = (struct symbol) { .scope = global_root_scope, .class = SYM_VOID, };
|
||||
if (!global_root_scope_pool)
|
||||
{
|
||||
global_root_scope_pool = rp_new(&root_pool, the_bird_domain.the_bird, "Keywords pool");
|
||||
global_root_scope_linpool = lp_new(global_root_scope_pool);
|
||||
}
|
||||
|
||||
struct symbol *s = lp_alloc(global_root_scope_linpool, sizeof(struct symbol) + l + 1);
|
||||
*s = (struct symbol) { .scope = ss, .class = SYM_VOID, };
|
||||
memcpy(s->name, c, l+1);
|
||||
|
||||
if (!global_root_scope->hash.data)
|
||||
HASH_INIT(global_root_scope->hash, &root_pool, SYM_ORDER);
|
||||
if (!ss->hash.data)
|
||||
HASH_INIT(ss->hash, &root_pool, SYM_ORDER);
|
||||
|
||||
HASH_INSERT2(global_root_scope->hash, SYM, &root_pool, s);
|
||||
HASH_INSERT2(ss->hash, SYM, &root_pool, s);
|
||||
return s;
|
||||
}
|
||||
|
||||
@ -642,7 +632,8 @@ cf_find_symbol_scope(const struct sym_scope *scope, const byte *c)
|
||||
|
||||
/* Find the symbol here or anywhere below */
|
||||
while (scope)
|
||||
if (scope->hash.data && (s = HASH_FIND(scope->hash, SYM, c, 1)))
|
||||
if (((scope != &global_filter_scope) || !new_config || new_config->allow_attributes) &&
|
||||
scope->hash.data && (s = HASH_FIND(scope->hash, SYM, c)))
|
||||
return s;
|
||||
else
|
||||
scope = scope->next;
|
||||
@ -660,9 +651,12 @@ cf_find_symbol_scope(const struct sym_scope *scope, const byte *c)
|
||||
* existing symbol is found.
|
||||
*/
|
||||
struct symbol *
|
||||
cf_get_symbol(const byte *c)
|
||||
cf_get_symbol(struct config *conf, const byte *c)
|
||||
{
|
||||
return cf_find_symbol_scope(conf_this_scope, c) ?: cf_new_symbol(c);
|
||||
return cf_find_symbol_scope(conf->current_scope, c) ?: (
|
||||
cf_swap_soft_scope(conf),
|
||||
cf_new_symbol(conf->current_scope, conf->pool, conf->mem, c)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -673,22 +667,23 @@ cf_get_symbol(const byte *c)
|
||||
* for purposes of cf_define_symbol().
|
||||
*/
|
||||
struct symbol *
|
||||
cf_localize_symbol(struct symbol *sym)
|
||||
cf_localize_symbol(struct config *conf, struct symbol *sym)
|
||||
{
|
||||
/* If the symbol type is void, it has been recently allocated just in this scope. */
|
||||
if (!sym->class)
|
||||
return sym;
|
||||
|
||||
/* If the scope is the current, it is already defined in this scope. */
|
||||
if (cf_symbol_is_local(sym))
|
||||
if (cf_symbol_is_local(conf, sym))
|
||||
cf_error("Symbol '%s' already defined", sym->name);
|
||||
|
||||
/* Not allocated here yet, doing it now. */
|
||||
return cf_new_symbol(sym->name);
|
||||
cf_swap_soft_scope(conf);
|
||||
return cf_new_symbol(conf->current_scope, conf->pool, conf->mem, sym->name);
|
||||
}
|
||||
|
||||
struct symbol *
|
||||
cf_default_name(char *template, int *counter)
|
||||
cf_default_name(struct config *conf, char *template, int *counter)
|
||||
{
|
||||
char buf[SYM_MAX_LEN];
|
||||
struct symbol *s;
|
||||
@ -697,7 +692,7 @@ cf_default_name(char *template, int *counter)
|
||||
for(;;)
|
||||
{
|
||||
bsprintf(buf, template, ++(*counter));
|
||||
s = cf_get_symbol(buf);
|
||||
s = cf_get_symbol(conf, buf);
|
||||
if (s->class == SYM_VOID)
|
||||
return s;
|
||||
if (!perc)
|
||||
@ -710,67 +705,56 @@ static enum yytokentype
|
||||
cf_lex_symbol(const char *data)
|
||||
{
|
||||
/* Have we defined such a symbol? */
|
||||
struct symbol *sym = cf_get_symbol(data);
|
||||
struct symbol *sym = cf_get_symbol(new_config, data);
|
||||
cf_lval.s = sym;
|
||||
|
||||
/* Is it a keyword? Prefer the keyword. */
|
||||
struct keyword *k = HASH_FIND(kw_hash, KW, data);
|
||||
if (k)
|
||||
switch (sym->class)
|
||||
{
|
||||
if (k->value > 0)
|
||||
return k->value;
|
||||
else
|
||||
case SYM_KEYWORD:
|
||||
{
|
||||
cf_lval.i = -k->value;
|
||||
int val = sym->keyword->value;
|
||||
if (val > 0) return val;
|
||||
cf_lval.i = -val;
|
||||
return ENUM;
|
||||
}
|
||||
}
|
||||
|
||||
/* OK, only a symbol. */
|
||||
if (sym->class == SYM_VOID)
|
||||
case SYM_METHOD:
|
||||
return (sym->method->arg_num > 1) ? CF_SYM_METHOD_ARGS : CF_SYM_METHOD_BARE;
|
||||
case SYM_VOID:
|
||||
return CF_SYM_UNDEFINED;
|
||||
else
|
||||
default:
|
||||
return CF_SYM_KNOWN;
|
||||
}
|
||||
|
||||
static void
|
||||
cf_lex_init_kh(void)
|
||||
{
|
||||
HASH_INIT(kw_hash, config_pool, KW_ORDER);
|
||||
|
||||
struct keyword *k;
|
||||
for (k=keyword_list; k->name; k++)
|
||||
HASH_INSERT(kw_hash, KW, k);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ea_lex_register(struct ea_class *def)
|
||||
{
|
||||
struct symbol *sym = cf_root_symbol(def->name);
|
||||
sym->class = SYM_ATTRIBUTE;
|
||||
sym->attribute = def;
|
||||
def->sym = sym;
|
||||
def->sym = cf_root_symbol(def->name, &global_filter_scope);
|
||||
def->sym->class = SYM_ATTRIBUTE;
|
||||
def->sym->attribute = def;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* When we start to support complete protocol removal, we may need this function */
|
||||
void
|
||||
ea_lex_unregister(struct ea_class *def)
|
||||
{
|
||||
struct symbol *sym = def->sym;
|
||||
HASH_REMOVE2(global_root_scope->hash, SYM, &root_pool, sym);
|
||||
HASH_REMOVE2(global_filter_scope.hash, SYM, &root_pool, sym);
|
||||
mb_free(sym);
|
||||
def->sym = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct ea_class *
|
||||
ea_class_find_by_name(const char *name)
|
||||
{
|
||||
struct symbol *sym = cf_find_symbol(global_root_scope, name);
|
||||
if (!sym || (sym->class != SYM_ATTRIBUTE))
|
||||
return NULL;
|
||||
else
|
||||
return sym->attribute;
|
||||
struct symbol *sym = cf_find_symbol_scope(config ? config->root_scope : &global_filter_scope, name);
|
||||
return sym && (sym->class == SYM_ATTRIBUTE) ? sym->attribute : NULL;
|
||||
}
|
||||
|
||||
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
|
||||
@ -782,8 +766,20 @@ ea_class_find_by_name(const char *name)
|
||||
void
|
||||
cf_lex_init(int is_cli, struct config *c)
|
||||
{
|
||||
if (!kw_hash.data)
|
||||
cf_lex_init_kh();
|
||||
if (!global_root_scope.readonly)
|
||||
{
|
||||
for (const struct keyword *k = keyword_list; k->name; k++)
|
||||
{
|
||||
struct symbol *s = cf_root_symbol(k->name, &global_root_scope);
|
||||
s->class = SYM_KEYWORD;
|
||||
s->keyword = k;
|
||||
}
|
||||
|
||||
global_root_scope.readonly = 1;
|
||||
global_filter_scope.readonly = 1;
|
||||
|
||||
f_type_methods_register();
|
||||
}
|
||||
|
||||
ifs_head = ifs = push_ifs(NULL);
|
||||
if (!is_cli)
|
||||
@ -801,14 +797,12 @@ cf_lex_init(int is_cli, struct config *c)
|
||||
else
|
||||
BEGIN(INITIAL);
|
||||
|
||||
c->root_scope = cfg_allocz(sizeof(struct sym_scope));
|
||||
conf_this_scope = c->root_scope;
|
||||
conf_this_scope->active = 1;
|
||||
c->root_scope = c->current_scope = cfg_allocz(sizeof(struct sym_scope));
|
||||
|
||||
if (is_cli)
|
||||
conf_this_scope->next = config->root_scope;
|
||||
c->current_scope->next = config->root_scope;
|
||||
else
|
||||
conf_this_scope->next = global_root_scope;
|
||||
c->current_scope->next = &global_filter_scope;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -822,13 +816,12 @@ cf_lex_init(int is_cli, struct config *c)
|
||||
* in all scopes stored on the stack.
|
||||
*/
|
||||
void
|
||||
cf_push_scope(struct symbol *sym)
|
||||
cf_push_scope(struct config *conf, struct symbol *sym)
|
||||
{
|
||||
struct sym_scope *s = cfg_allocz(sizeof(struct sym_scope));
|
||||
|
||||
s->next = conf_this_scope;
|
||||
conf_this_scope = s;
|
||||
s->active = 1;
|
||||
s->next = conf->current_scope;
|
||||
conf->current_scope = s;
|
||||
s->name = sym;
|
||||
s->slots = 0;
|
||||
}
|
||||
@ -841,14 +834,11 @@ cf_push_scope(struct symbol *sym)
|
||||
* invisible to the rest of the config.
|
||||
*/
|
||||
void
|
||||
cf_pop_scope(void)
|
||||
cf_pop_scope(struct config *conf)
|
||||
{
|
||||
ASSERT(!conf_this_scope->soft_scopes);
|
||||
|
||||
conf_this_scope->active = 0;
|
||||
conf_this_scope = conf_this_scope->next;
|
||||
|
||||
ASSERT(conf_this_scope);
|
||||
ASSERT(!conf->current_scope->soft_scopes);
|
||||
conf->current_scope = conf->current_scope->next;
|
||||
ASSERT(conf->current_scope);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -859,12 +849,12 @@ cf_pop_scope(void)
|
||||
* Such scope will be converted to a regular scope on first use.
|
||||
*/
|
||||
void
|
||||
cf_push_soft_scope(void)
|
||||
cf_push_soft_scope(struct config *conf)
|
||||
{
|
||||
if (conf_this_scope->soft_scopes < 0xfe)
|
||||
conf_this_scope->soft_scopes++;
|
||||
if (conf->current_scope->soft_scopes < 0xfe)
|
||||
conf->current_scope->soft_scopes++;
|
||||
else
|
||||
cf_push_block_scope();
|
||||
cf_push_block_scope(conf);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -873,12 +863,12 @@ cf_push_soft_scope(void)
|
||||
* Leave a soft scope entered by cf_push_soft_scope().
|
||||
*/
|
||||
void
|
||||
cf_pop_soft_scope(void)
|
||||
cf_pop_soft_scope(struct config *conf)
|
||||
{
|
||||
if (conf_this_scope->soft_scopes)
|
||||
conf_this_scope->soft_scopes--;
|
||||
if (conf->current_scope->soft_scopes)
|
||||
conf->current_scope->soft_scopes--;
|
||||
else
|
||||
cf_pop_block_scope();
|
||||
cf_pop_block_scope(conf);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -888,15 +878,36 @@ cf_pop_soft_scope(void)
|
||||
* on first use. It is done automatically by cf_new_symbol().
|
||||
*/
|
||||
static inline void
|
||||
cf_swap_soft_scope(void)
|
||||
cf_swap_soft_scope(struct config *conf)
|
||||
{
|
||||
if (conf_this_scope->soft_scopes)
|
||||
if (conf->current_scope->soft_scopes)
|
||||
{
|
||||
conf_this_scope->soft_scopes--;
|
||||
cf_push_block_scope();
|
||||
conf->current_scope->soft_scopes--;
|
||||
cf_push_block_scope(conf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* cf_enter_filters - enable filter / route attributes namespace
|
||||
*/
|
||||
void
|
||||
cf_enter_filters(void)
|
||||
{
|
||||
ASSERT_DIE(!new_config->allow_attributes);
|
||||
new_config->allow_attributes = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* cf_exit_filters - disable filter / route attributes namespace
|
||||
*/
|
||||
void
|
||||
cf_exit_filters(void)
|
||||
{
|
||||
ASSERT_DIE(new_config->allow_attributes);
|
||||
new_config->allow_attributes = 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cf_symbol_class_name - get name of a symbol class
|
||||
* @sym: symbol
|
||||
@ -923,6 +934,8 @@ cf_symbol_class_name(struct symbol *sym)
|
||||
return "routing table";
|
||||
case SYM_ATTRIBUTE:
|
||||
return "custom attribute";
|
||||
case SYM_KEYWORD:
|
||||
return "symbol";
|
||||
case SYM_CONSTANT_RANGE:
|
||||
return "constant";
|
||||
case SYM_VARIABLE_RANGE:
|
||||
|
@ -46,7 +46,7 @@
|
||||
#undef LOCAL_DEBUG
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/iface.h"
|
||||
#include "lib/resource.h"
|
||||
@ -60,7 +60,8 @@
|
||||
|
||||
static jmp_buf conf_jmpbuf;
|
||||
|
||||
struct config *config, *new_config;
|
||||
struct config *config;
|
||||
_Thread_local struct config *new_config;
|
||||
pool *config_pool;
|
||||
|
||||
static struct config *old_config; /* Old configuration */
|
||||
|
64
conf/conf.h
64
conf/conf.h
@ -57,6 +57,8 @@ struct config {
|
||||
int thread_count; /* How many worker threads to prefork */
|
||||
|
||||
struct sym_scope *root_scope; /* Scope for root symbols */
|
||||
struct sym_scope *current_scope; /* Current scope where we are actually in while parsing */
|
||||
int allow_attributes; /* Allow attributes in the current state of configuration parsing */
|
||||
_Atomic int obstacle_count; /* Number of items blocking freeing of this config */
|
||||
event done_event; /* Called when obstacle_count reaches zero */
|
||||
int shutdown; /* This is a pseudo-config for daemon shutdown */
|
||||
@ -66,7 +68,7 @@ struct config {
|
||||
|
||||
/* Please don't use these variables in protocols. Use proto_config->global instead. */
|
||||
extern struct config *config; /* Currently active configuration */
|
||||
extern struct config *new_config; /* Configuration being parsed */
|
||||
extern _Thread_local struct config *new_config; /* Configuration being parsed */
|
||||
|
||||
struct config *config_alloc(const char *name);
|
||||
int config_parse(struct config *);
|
||||
@ -80,6 +82,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);
|
||||
@ -112,6 +115,11 @@ void cfg_copy_list(list *dest, list *src, unsigned node_size);
|
||||
|
||||
extern int (*cf_read_hook)(byte *buf, uint max, int fd);
|
||||
|
||||
struct keyword {
|
||||
byte *name;
|
||||
int value;
|
||||
};
|
||||
|
||||
struct symbol {
|
||||
node n; /* In list of symbols in config */
|
||||
struct symbol *next;
|
||||
@ -127,6 +135,8 @@ struct symbol {
|
||||
struct ea_class *attribute; /* For SYM_ATTRIBUTE */
|
||||
struct f_val *val; /* For SYM_CONSTANT */
|
||||
uint offset; /* For SYM_VARIABLE */
|
||||
const struct keyword *keyword; /* For SYM_KEYWORD */
|
||||
const struct f_method *method; /* For SYM_METHOD */
|
||||
};
|
||||
|
||||
char name[0];
|
||||
@ -139,17 +149,17 @@ struct sym_scope {
|
||||
HASH(struct symbol) hash; /* Local symbol hash */
|
||||
|
||||
uint slots; /* Variable slots */
|
||||
byte active; /* Currently entered */
|
||||
byte block; /* No independent stack frame */
|
||||
byte soft_scopes; /* Number of soft scopes above */
|
||||
byte block:1; /* No independent stack frame */
|
||||
byte readonly:1; /* Do not add new symbols */
|
||||
};
|
||||
|
||||
extern struct sym_scope *global_root_scope;
|
||||
void cf_enter_filters(void);
|
||||
void cf_exit_filters(void);
|
||||
|
||||
extern pool *global_root_scope_pool;
|
||||
extern linpool *global_root_scope_linpool;
|
||||
|
||||
struct bytestring {
|
||||
size_t length;
|
||||
byte data[];
|
||||
};
|
||||
|
||||
#define SYM_MAX_LEN 64
|
||||
|
||||
@ -161,6 +171,8 @@ struct bytestring {
|
||||
#define SYM_FILTER 4
|
||||
#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)
|
||||
@ -188,8 +200,6 @@ struct include_file_stack {
|
||||
|
||||
extern struct include_file_stack *ifs;
|
||||
|
||||
extern struct sym_scope *conf_this_scope;
|
||||
|
||||
int cf_lex(void);
|
||||
void cf_lex_init(int is_cli, struct config *c);
|
||||
void cf_lex_unwind(void);
|
||||
@ -203,12 +213,16 @@ static inline struct symbol *cf_find_symbol_cfg(const struct config *cfg, const
|
||||
struct sym_scope: cf_find_symbol_scope \
|
||||
)((where), (what))
|
||||
|
||||
struct symbol *cf_get_symbol(const byte *c);
|
||||
struct symbol *cf_default_name(char *template, int *counter);
|
||||
struct symbol *cf_localize_symbol(struct symbol *sym);
|
||||
struct symbol *cf_get_symbol(struct config *conf, const byte *c);
|
||||
struct symbol *cf_default_name(struct config *conf, char *template, int *counter);
|
||||
struct symbol *cf_localize_symbol(struct config *conf, struct symbol *sym);
|
||||
|
||||
static inline int cf_symbol_is_local(struct symbol *sym)
|
||||
{ return (sym->scope == conf_this_scope) && !conf_this_scope->soft_scopes; }
|
||||
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
|
||||
@ -225,22 +239,22 @@ static inline int cf_symbol_is_local(struct symbol *sym)
|
||||
* Result: Pointer to the newly defined symbol. If we are in the top-level
|
||||
* scope, it's the same @sym as passed to the function.
|
||||
*/
|
||||
#define cf_define_symbol(osym_, type_, var_, def_) ({ \
|
||||
struct symbol *sym_ = cf_localize_symbol(osym_); \
|
||||
#define cf_define_symbol(conf_, osym_, type_, var_, def_) ({ \
|
||||
struct symbol *sym_ = cf_localize_symbol(conf_, osym_); \
|
||||
sym_->class = type_; \
|
||||
sym_->var_ = def_; \
|
||||
sym_; })
|
||||
|
||||
void cf_push_scope(struct symbol *);
|
||||
void cf_pop_scope(void);
|
||||
void cf_push_soft_scope(void);
|
||||
void cf_pop_soft_scope(void);
|
||||
void cf_push_scope(struct config *, struct symbol *);
|
||||
void cf_pop_scope(struct config *);
|
||||
void cf_push_soft_scope(struct config *);
|
||||
void cf_pop_soft_scope(struct config *);
|
||||
|
||||
static inline void cf_push_block_scope(void)
|
||||
{ cf_push_scope(NULL); conf_this_scope->block = 1; }
|
||||
static inline void cf_push_block_scope(struct config *conf)
|
||||
{ cf_push_scope(conf, NULL); conf->current_scope->block = 1; }
|
||||
|
||||
static inline void cf_pop_block_scope(void)
|
||||
{ ASSERT(conf_this_scope->block); cf_pop_scope(); }
|
||||
static inline void cf_pop_block_scope(struct config *conf)
|
||||
{ ASSERT(conf->current_scope->block); cf_pop_scope(conf); }
|
||||
|
||||
char *cf_symbol_class_name(struct symbol *sym);
|
||||
|
||||
|
@ -19,7 +19,7 @@ CF_HDR
|
||||
#include "lib/string.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/iface.h"
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
#include "nest/bfd.h"
|
||||
#include "nest/cli.h"
|
||||
#include "filter/filter.h"
|
||||
@ -97,19 +97,20 @@ CF_DECLS
|
||||
struct timeformat *tf;
|
||||
struct settle_config settle;
|
||||
struct adata *ad;
|
||||
struct bytestring *bs;
|
||||
const struct adata *bs;
|
||||
struct aggr_item_node *ai;
|
||||
}
|
||||
|
||||
%token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT
|
||||
%token GEQ LEQ NEQ AND OR
|
||||
%token GEQ LEQ NEQ AND OR IMP
|
||||
%token PO PC
|
||||
%token <i> NUM ENUM
|
||||
%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> BYTESTRING
|
||||
%token <bs> BYTETEXT
|
||||
%type <iface> ipa_scope
|
||||
|
||||
%type <i> expr bool pxlen4
|
||||
@ -121,11 +122,15 @@ CF_DECLS
|
||||
%type <ad> label_stack_start label_stack
|
||||
|
||||
%type <t> text opttext
|
||||
%type <s> symbol symbol_known toksym
|
||||
%type <bs> bytestring
|
||||
%type <s> symbol
|
||||
|
||||
%type <v> bytestring_text text_or_ipa
|
||||
%type <x> bytestring_expr
|
||||
|
||||
%nonassoc PREFIX_DUMMY
|
||||
%left AND OR
|
||||
%nonassoc '=' '<' '>' '~' GEQ LEQ NEQ NMA PO PC
|
||||
%nonassoc '=' '<' '>' '~' GEQ LEQ NEQ NMA IMP PO PC
|
||||
%left '|' '&'
|
||||
%left '+' '-'
|
||||
%left '*' '/' '%'
|
||||
@ -134,7 +139,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
|
||||
|
||||
@ -158,16 +163,15 @@ conf: definition ;
|
||||
|
||||
definition:
|
||||
DEFINE symbol '=' term ';' {
|
||||
struct f_val val;
|
||||
if (f_eval(f_linearize($4, 1), &val) > F_RETURN) cf_error("Runtime error");
|
||||
cf_define_symbol($2, SYM_CONSTANT | val.type, val, lp_val_copy(cfg_mem, &val));
|
||||
struct f_val *val = cf_eval($4, T_VOID);
|
||||
cf_define_symbol(new_config, $2, SYM_CONSTANT | val->type, val, val);
|
||||
}
|
||||
;
|
||||
|
||||
expr:
|
||||
NUM
|
||||
| '(' term ')' { $$ = f_eval_int(f_linearize($2, 1)); }
|
||||
| symbol_known {
|
||||
| '(' term ')' { $$ = cf_eval_int($2); }
|
||||
| CF_SYM_KNOWN {
|
||||
if ($1->class != (SYM_CONSTANT | T_INT)) cf_error("Number constant expected");
|
||||
$$ = SYM_VAL($1).i; }
|
||||
;
|
||||
@ -178,9 +182,7 @@ expr_us:
|
||||
| expr US { $$ = $1 US_; }
|
||||
;
|
||||
|
||||
toksym: FROM | PREFERENCE ;
|
||||
symbol: CF_SYM_UNDEFINED | CF_SYM_KNOWN | toksym ;
|
||||
symbol_known: CF_SYM_KNOWN | toksym ;
|
||||
symbol: CF_SYM_UNDEFINED | CF_SYM_KNOWN ;
|
||||
|
||||
/* Switches */
|
||||
|
||||
@ -410,6 +412,45 @@ opttext:
|
||||
| /* empty */ { $$ = NULL; }
|
||||
;
|
||||
|
||||
text_or_ipa:
|
||||
TEXT { $$.type = T_STRING; $$.val.s = $1; }
|
||||
| IP4 { $$.type = T_IP; $$.val.ip = ipa_from_ip4($1); }
|
||||
| IP6 { $$.type = T_IP; $$.val.ip = ipa_from_ip6($1); }
|
||||
| CF_SYM_KNOWN {
|
||||
if (($1->class == (SYM_CONSTANT | T_STRING)) ||
|
||||
($1->class == (SYM_CONSTANT | T_IP)))
|
||||
$$ = *($1->val);
|
||||
else
|
||||
cf_error("String or IP constant expected");
|
||||
}
|
||||
| '(' term ')' {
|
||||
$$ = *cf_eval($2, T_VOID);
|
||||
if (($$.type != T_BYTESTRING) && ($$.type != T_STRING))
|
||||
cf_error("Bytestring or string value expected");
|
||||
}
|
||||
;
|
||||
|
||||
bytestring:
|
||||
BYTETEXT
|
||||
| bytestring_expr { $$ = cf_eval($1, T_BYTESTRING)->val.bs; }
|
||||
;
|
||||
|
||||
bytestring_text:
|
||||
BYTETEXT { $$.type = T_BYTESTRING; $$.val.bs = $1; }
|
||||
| TEXT { $$.type = T_STRING; $$.val.s = $1; }
|
||||
| bytestring_expr {
|
||||
$$ = *cf_eval($1, T_VOID);
|
||||
if (($$.type != T_BYTESTRING) && ($$.type != T_STRING))
|
||||
cf_error("Bytestring or string value expected");
|
||||
}
|
||||
;
|
||||
|
||||
bytestring_expr:
|
||||
lvalue { $$ = f_lval_getter(&$1); }
|
||||
| term_bs
|
||||
| '(' term ')' { $$ = $2; }
|
||||
;
|
||||
|
||||
|
||||
CF_CODE
|
||||
|
||||
|
@ -23,7 +23,7 @@ m4_define(CF_DECLS, `m4_divert(-1)')
|
||||
m4_define(CF_DEFINES, `m4_divert(-1)')
|
||||
|
||||
# Keywords are translated to C initializers
|
||||
m4_define(CF_handle_kw, `m4_divert(1){ "m4_translit($1,[[A-Z]],[[a-z]])", $1, NULL },
|
||||
m4_define(CF_handle_kw, `m4_divert(1){ "m4_translit($1,[[A-Z]],[[a-z]])", $1 },
|
||||
m4_divert(-1)')
|
||||
m4_define(CF_keywd, `m4_ifdef([[CF_tok_$1]],,[[m4_define([[CF_tok_$1]],1)CF_handle_kw($1)]])')
|
||||
m4_define(CF_KEYWORDS, `CF_iterate([[CF_keywd]], [[$@]])DNL')
|
||||
@ -34,16 +34,16 @@ m4_define(CF_CLI, `CF_KEYWORDS(m4_translit($1, [[ ]], [[,]]))
|
||||
|
||||
# Enums are translated to C initializers: use CF_ENUM(typename, prefix, values)
|
||||
# For different prefix: CF_ENUM_PX(typename, external prefix, C prefix, values)
|
||||
m4_define(CF_enum, `m4_divert(1){ "CF_enum_prefix_ext[[]]$1", -((CF_enum_type<<16) | CF_enum_prefix_int[[]]$1), NULL },
|
||||
m4_define(CF_enum, `m4_divert(1){ "CF_enum_prefix_ext[[]]$1", -((CF_enum_type<<16) | CF_enum_prefix_int[[]]$1) },
|
||||
m4_divert(-1)')
|
||||
m4_define(CF_ENUM, `m4_define([[CF_enum_type]],$1)m4_define([[CF_enum_prefix_ext]],$2)m4_define([[CF_enum_prefix_int]],$2)CF_iterate([[CF_enum]], [[m4_shift(m4_shift($@))]])DNL')
|
||||
m4_define(CF_ENUM_PX, `m4_define([[CF_enum_type]],$1)m4_define([[CF_enum_prefix_ext]],$2)m4_define([[CF_enum_prefix_int]],$3)CF_iterate([[CF_enum]], [[m4_shift(m4_shift(m4_shift($@)))]])DNL')
|
||||
|
||||
# After all configuration templates end, we generate the
|
||||
# After all configuration templates end, we generate the keyword list
|
||||
m4_m4wrap(`
|
||||
m4_divert(0)
|
||||
static struct keyword keyword_list[] = {
|
||||
m4_undivert(1){ NULL, -1, NULL } };
|
||||
static const struct keyword keyword_list[] = {
|
||||
m4_undivert(1){ NULL, -1 } };
|
||||
')
|
||||
|
||||
# As we are processing C source, we must access all M4 primitives via
|
||||
|
@ -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
|
||||
|
@ -307,16 +307,18 @@ if test "$enable_mpls_kernel" != no ; then
|
||||
fi
|
||||
|
||||
# temporarily removed "mrt" from all_protocols to speed up 3.0-alpha1 release
|
||||
all_protocols="bfd babel bgp ospf perf pipe radv rip rpki static"
|
||||
all_protocols="aggregator bfd babel bgp ospf perf pipe radv rip rpki static"
|
||||
all_protocols=`echo $all_protocols | sed 's/ /,/g'`
|
||||
|
||||
if test "$with_protocols" = all ; then
|
||||
with_protocols="$all_protocols"
|
||||
fi
|
||||
|
||||
AH_TEMPLATE([CONFIG_AGGREGATOR],[Aggregator protocol])
|
||||
AH_TEMPLATE([CONFIG_BABEL], [Babel protocol])
|
||||
AH_TEMPLATE([CONFIG_BFD], [BFD protocol])
|
||||
AH_TEMPLATE([CONFIG_BGP], [BGP protocol])
|
||||
AH_TEMPLATE([CONFIG_BMP], [BMP protocol])
|
||||
AH_TEMPLATE([CONFIG_MRT], [MRT protocol])
|
||||
AH_TEMPLATE([CONFIG_OSPF], [OSPF protocol])
|
||||
AH_TEMPLATE([CONFIG_PIPE], [Pipe protocol])
|
||||
|
@ -6,3 +6,7 @@ make_archive_script = "tools/make-dev-archive"
|
||||
[upstream]
|
||||
# needed for get-archive
|
||||
archive_url = "https://bird.network.cz/download/bird-{{ version }}.tar.gz"
|
||||
|
||||
[apkg]
|
||||
# apkg compat level
|
||||
compat = 3
|
||||
|
@ -65,11 +65,23 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/
|
||||
<email>&dhemail;</email>
|
||||
</address>
|
||||
</author>
|
||||
<author>
|
||||
<firstname>Jakub</firstname>
|
||||
<surname>Ružička</surname>
|
||||
<contrib>Updated this manpage for birdcl.</contrib>
|
||||
<address>
|
||||
<email>jakub.ruzicka@nic.cz</email>
|
||||
</address>
|
||||
</author>
|
||||
</authorgroup>
|
||||
<copyright>
|
||||
<year>2010</year>
|
||||
<holder>&dhusername;</holder>
|
||||
</copyright>
|
||||
<copyright>
|
||||
<year>2022</year>
|
||||
<holder>Jakub Ružička</holder>
|
||||
</copyright>
|
||||
<legalnotice>
|
||||
<para>This manual page was written for the Debian system
|
||||
(and may be used by others).</para>
|
||||
@ -95,6 +107,10 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/
|
||||
<refname>birdc</refname>
|
||||
<refpurpose>BIRD Internet Routing Daemon remote control</refpurpose>
|
||||
</refnamediv>
|
||||
<refnamediv>
|
||||
<refname>birdcl</refname>
|
||||
<refpurpose>BIRD Internet Routing Daemon remote control light</refpurpose>
|
||||
</refnamediv>
|
||||
|
||||
<refsynopsisdiv>
|
||||
<cmdsynopsis>
|
||||
@ -118,6 +134,13 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/
|
||||
<arg choice="opt"><option>-s <replaceable>control-socket</replaceable></option></arg>
|
||||
<arg choice="opt"><option>-v</option></arg>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>birdcl</command>
|
||||
<arg choice="opt"><option>-l</option></arg>
|
||||
<arg choice="opt"><option>-r</option></arg>
|
||||
<arg choice="opt"><option>-s <replaceable>control-socket</replaceable></option></arg>
|
||||
<arg choice="opt"><option>-v</option></arg>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1 id="description">
|
||||
@ -136,7 +159,10 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/
|
||||
communicate. Once started, <command>bird</command> will give access
|
||||
to an interactive shell: commands can be completed with TAB and help
|
||||
can be requested by pressing the key `?'. More documentation on
|
||||
the available commands can be foung on the website, see below.</para>
|
||||
the available commands can be found on the website, see below.</para>
|
||||
<para><command>birdcl</command> is a light version of <command>birdc</command>
|
||||
remote control for <command>bird</command> without readline/ncurses support.
|
||||
TAB completion isn't available.</para>
|
||||
</refsect1>
|
||||
|
||||
<refsect1 id="options">
|
||||
@ -230,7 +256,7 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
<para>The <command>birdc</command> accepts these options:</para>
|
||||
<para><command>birdc</command> and <command>birdcl</command> accept these options:</para>
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><option>-l</option></term>
|
||||
@ -279,8 +305,7 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/
|
||||
|
||||
<refsect1 id="see_also">
|
||||
<title>SEE ALSO</title>
|
||||
<para>More documentation con be found on the website:
|
||||
<para>More documentation can be found on the website:
|
||||
https://bird.network.cz/.</para>
|
||||
</refsect1>
|
||||
</refentry>
|
||||
|
||||
|
@ -1 +0,0 @@
|
||||
bird2: binary-without-manpage usr/sbin/birdcl
|
@ -1,2 +1,3 @@
|
||||
bird.8
|
||||
birdc.8
|
||||
birdcl.8
|
||||
|
@ -2,4 +2,4 @@ bird2 ({{ version }}-cznic.{{ release }}) unstable; urgency=medium
|
||||
|
||||
* upstream package
|
||||
|
||||
-- Jakub Ružička <jakub.ruzicka@nic.cz> Mon, 29 Mar 2021 14:15:50 +0000
|
||||
-- Jakub Ružička <jakub.ruzicka@nic.cz> {{ now }}
|
||||
|
@ -1 +1 @@
|
||||
9
|
||||
11
|
||||
|
@ -5,18 +5,18 @@ Build-Depends: bison,
|
||||
debhelper,
|
||||
docbook-xsl,
|
||||
flex,
|
||||
libncurses5-dev,
|
||||
libncurses-dev,
|
||||
libreadline-dev | libreadline6-dev | libreadline5-dev,
|
||||
libssh-gcrypt-dev,
|
||||
linuxdoc-tools-latex,
|
||||
m4,
|
||||
opensp,
|
||||
quilt,
|
||||
texlive-latex-extra,
|
||||
xsltproc
|
||||
Build-Depends-Indep: linuxdoc-tools-latex,
|
||||
opensp,
|
||||
texlive-latex-extra
|
||||
Maintainer: Jakub Ružička <jakub.ruzicka@nic.cz>
|
||||
Uploaders: Ondřej Surý <ondrej@debian.org>
|
||||
Standards-Version: 4.3.0
|
||||
Standards-Version: 4.6.2
|
||||
Vcs-Browser: https://salsa.debian.org/debian/bird2
|
||||
Vcs-Git: https://salsa.debian.org/debian/bird2.git
|
||||
Homepage: https://bird.network.cz/
|
||||
@ -26,7 +26,6 @@ Architecture: kfreebsd-any linux-any
|
||||
Pre-Depends: init-system-helpers,
|
||||
${misc:Pre-Depends}
|
||||
Depends: adduser,
|
||||
lsb-base,
|
||||
ucf,
|
||||
${misc:Depends},
|
||||
${shlibs:Depends}
|
||||
|
@ -23,8 +23,7 @@ LDFLAGS += -g -O2 -fno-strict-aliasing -fno-strict-overflow -fPIC -Wl,-z,defs -W
|
||||
override_dh_auto_configure:
|
||||
CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" dh_auto_configure -- $(COMMON_FLAGS) --with-protocols=all
|
||||
|
||||
override_dh_auto_build:
|
||||
dh_auto_build
|
||||
override_dh_auto_build-indep:
|
||||
dh_auto_build -- docs
|
||||
|
||||
override_dh_auto_install:
|
||||
@ -47,10 +46,10 @@ override_dh_installman: bird.8
|
||||
|
||||
override_dh_clean:
|
||||
dh_clean
|
||||
-rm -f bird.8 birdc.8
|
||||
-rm -f bird.8 birdc.8 birdcl.8
|
||||
|
||||
override_dh_missing:
|
||||
dh_missing --fail-missing
|
||||
dh_missing
|
||||
|
||||
override_dh_compress:
|
||||
dh_compress -X.conf
|
||||
|
@ -1,2 +1,2 @@
|
||||
version=3
|
||||
version=4
|
||||
https://bird.network.cz/download/bird-(2\.[\d.]+).tar.gz
|
||||
|
@ -1,17 +1,21 @@
|
||||
%global _hardened_build 1
|
||||
%global _without_doc 1
|
||||
%{!?_rundir:%global _rundir %%{_localstatedir}/run}
|
||||
|
||||
Name: bird
|
||||
Version: {{ version }}
|
||||
Release: cznic.{{ release }}%{?dist}
|
||||
Summary: BIRD Internet Routing Daemon
|
||||
|
||||
Group: System Environment/Daemons
|
||||
License: GPL-2.0-or-later
|
||||
URL: https://bird.network.cz/
|
||||
Source0: https://bird.network.cz/download/bird-%{version}.tar.gz
|
||||
Source1: bird.service
|
||||
Source2: bird.tmpfilesd
|
||||
Source3: bird.sysusersd
|
||||
|
||||
BuildRequires: autoconf
|
||||
BuildRequires: flex
|
||||
BuildRequires: bison
|
||||
BuildRequires: ncurses-devel
|
||||
@ -20,14 +24,13 @@ BuildRequires: sed
|
||||
BuildRequires: gcc
|
||||
BuildRequires: make
|
||||
BuildRequires: libssh-devel
|
||||
%if 0%{?fedora} || (0%{?rhel} && 0%{?rhel} > 7)
|
||||
BuildRequires: systemd-rpm-macros
|
||||
%else
|
||||
BuildRequires: systemd
|
||||
%if 0%{?rhel} && 0%{?rhel} < 8
|
||||
# http://trubka.network.cz/pipermail/bird-users/2019-August/013631.html
|
||||
BuildRequires: devtoolset-8-toolchain
|
||||
%endif
|
||||
|
||||
Obsoletes: bird6 < 2.0.2-1
|
||||
Provides: bird6 = %{version}-%{release}
|
||||
BuildRequires: systemd-rpm-macros
|
||||
%{?systemd_requires}
|
||||
%{?sysusers_requires_compat}
|
||||
|
||||
%description
|
||||
BIRD is a dynamic IP routing daemon supporting both, IPv4 and IPv6, Border
|
||||
@ -41,6 +44,7 @@ powerful language for route filtering.
|
||||
%if 0%{!?_without_doc:1}
|
||||
%package doc
|
||||
Summary: Documentation for BIRD Internet Routing Daemon
|
||||
Group: Documentation
|
||||
BuildRequires: linuxdoc-tools sgml-common perl(FindBin)
|
||||
BuildArch: noarch
|
||||
|
||||
@ -57,9 +61,13 @@ powerful language for route filtering.
|
||||
%endif
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
%setup -q -n bird-%{version}
|
||||
|
||||
%build
|
||||
%if 0%{?rhel} && 0%{?rhel} < 8
|
||||
. /opt/rh/devtoolset-8/enable
|
||||
%endif
|
||||
|
||||
%configure --runstatedir=%{_rundir}/bird
|
||||
%make_build all %{!?_without_doc:docs}
|
||||
|
||||
@ -70,17 +78,18 @@ powerful language for route filtering.
|
||||
install -d %{buildroot}{%{_localstatedir}/lib/bird,%{_rundir}/bird}
|
||||
install -D -p -m 0644 %{SOURCE1} %{buildroot}%{_unitdir}/bird.service
|
||||
install -D -p -m 0644 %{SOURCE2} %{buildroot}%{_tmpfilesdir}/bird.conf
|
||||
install -D -p -m 0644 %{SOURCE3} %{buildroot}%{_sysusersdir}/bird.conf
|
||||
{% endraw %}
|
||||
|
||||
%check
|
||||
%if 0%{?rhel} && 0%{?rhel} < 8
|
||||
. /opt/rh/devtoolset-8/enable
|
||||
%endif
|
||||
|
||||
make test
|
||||
|
||||
%pre
|
||||
getent group bird >/dev/null || groupadd -r bird
|
||||
getent passwd bird >/dev/null || \
|
||||
useradd -r -g bird -d %{_localstatedir}/lib/bird -s /sbin/nologin \
|
||||
-c "BIRD daemon user" bird
|
||||
exit 0
|
||||
%sysusers_create_compat %{SOURCE3}
|
||||
|
||||
%post
|
||||
%systemd_post bird.service
|
||||
@ -95,12 +104,13 @@ exit 0
|
||||
%doc NEWS README
|
||||
%attr(0640,root,bird) %config(noreplace) %{_sysconfdir}/bird.conf
|
||||
%{_unitdir}/bird.service
|
||||
%{_sysusersdir}/bird.conf
|
||||
%{_tmpfilesdir}/bird.conf
|
||||
%{_sbindir}/bird
|
||||
%{_sbindir}/birdc
|
||||
%{_sbindir}/birdcl
|
||||
%dir %attr(0750,bird,bird) %{_localstatedir}/lib/bird
|
||||
%dir %attr(0750,bird,bird) %ghost %{_rundir}/bird
|
||||
%dir %attr(0750,bird,bird) %{_rundir}/bird
|
||||
|
||||
%if 0%{!?_without_doc:1}
|
||||
%files doc
|
||||
|
2
distro/pkg/rpm/bird.sysusersd
Normal file
2
distro/pkg/rpm/bird.sysusersd
Normal file
@ -0,0 +1,2 @@
|
||||
#Type Name ID GECOS Home directory Shell
|
||||
u bird - "BIRD daemon user" /var/lib/bird -
|
3
distro/tests/control
Normal file
3
distro/tests/control
Normal file
@ -0,0 +1,3 @@
|
||||
Tests: test-bird.sh
|
||||
Restrictions: needs-root
|
||||
Depends: bird2, iproute2
|
105
distro/tests/test-bird.sh
Executable file
105
distro/tests/test-bird.sh
Executable file
@ -0,0 +1,105 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
LOCAL=2001:db8:dead::
|
||||
|
||||
EXTERNAL=2001:db8:beef::
|
||||
EXTERNAL_NET=${EXTERNAL}/48
|
||||
EXTERNAL_NH=${LOCAL}beef
|
||||
|
||||
LEARN=2001:db8:feed::
|
||||
LEARN_NET=${LEARN}/48
|
||||
LEARN_NH=${LOCAL}feed
|
||||
|
||||
IFACE=bird-test-dummy
|
||||
IFACE_EXISTS=false
|
||||
|
||||
BIRD_RUNNING=false
|
||||
|
||||
D=$(mktemp -d)
|
||||
pushd ${D} >/dev/null
|
||||
|
||||
stop_bird() {
|
||||
birdc -l down >/dev/null
|
||||
sleep 1
|
||||
grep -q "<FATAL> Shutdown completed" bird.log
|
||||
[ ! -e bird.pid ]
|
||||
[ ! -e bird.ctl ]
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
if ${BIRD_RUNNING}; then
|
||||
stop_bird
|
||||
if [ -e bird.pid ]; then
|
||||
kill -9 $(<bird.pid)
|
||||
fi
|
||||
fi
|
||||
|
||||
if ${IFACE_EXISTS}; then
|
||||
ip link del ${IFACE}
|
||||
fi
|
||||
|
||||
|
||||
popd > /dev/null
|
||||
rm -rf ${D}
|
||||
}
|
||||
|
||||
failed() {
|
||||
cleanup
|
||||
exit 1
|
||||
}
|
||||
|
||||
trap failed ERR
|
||||
trap failed INT
|
||||
trap failed HUP
|
||||
|
||||
ip link add ${IFACE} type dummy
|
||||
IFACE_EXISTS=true
|
||||
|
||||
ip link set ${IFACE} up
|
||||
ip -6 addr add ${LOCAL}/64 dev bird-test-dummy
|
||||
|
||||
ip -6 route add ${LEARN_NET} via ${LEARN_NH}
|
||||
|
||||
cat >bird.conf <<EOF
|
||||
log "bird.log" all;
|
||||
|
||||
protocol device {}
|
||||
|
||||
protocol kernel {
|
||||
ipv6 { import all; export all; };
|
||||
learn;
|
||||
}
|
||||
|
||||
protocol static {
|
||||
ipv6;
|
||||
route ${EXTERNAL_NET} via ${EXTERNAL_NH};
|
||||
}
|
||||
EOF
|
||||
|
||||
bird -l -P bird.pid
|
||||
|
||||
if [ ! -S bird.ctl ] || [ ! -f bird.pid ] || [ ! -f bird.log ]; then
|
||||
failed
|
||||
fi
|
||||
|
||||
BIRD_RUNNING=true
|
||||
|
||||
ROUTE_INSERTED=false
|
||||
for _ in $(seq 10); do
|
||||
if ip -6 route show ${EXTERNAL_NET} | egrep -q "${EXTERNAL_NET} via ${EXTERNAL_NH} dev ${IFACE} proto bird metric [0-9]+ pref medium"; then
|
||||
ROUTE_INSERTED=true
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
$ROUTE_INSERTED || failed
|
||||
|
||||
if birdc -l show route "${LEARN_NET}" | egrep -q "Network not found"; then
|
||||
failed
|
||||
fi
|
||||
|
||||
cleanup
|
||||
exit 0
|
280
doc/bird.sgml
280
doc/bird.sgml
@ -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/name/ (<m/parameters/) [ -> <m/return type/ ] <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>
|
||||
@ -869,7 +869,7 @@ agreement").
|
||||
protocol packets are processed in the local TX queues. This option is
|
||||
Linux specific. Default value is 7 (highest priority, privileged traffic).
|
||||
|
||||
<tag><label id="proto-pass">password "<m/password/" | <m/hex_key/ [ { <m>password options</m> } ] </tag>
|
||||
<tag><label id="proto-pass">password "<m/password/" | <m/bytestring/ [ { <m>password options</m> } ] </tag>
|
||||
Specifies a password that can be used by the protocol as a shared secret
|
||||
key. Password option can be used more times to specify more passwords.
|
||||
If more passwords are specified, it is a protocol-dependent decision
|
||||
@ -877,10 +877,8 @@ agreement").
|
||||
authentication is enabled, authentication can be enabled by separate,
|
||||
protocol-dependent <cf/authentication/ option.
|
||||
|
||||
A password can also be specified as a hexadecimal key. <m/hex_key/ is a
|
||||
sequence of hexadecimal digit pairs, optionally colon-separated. A key
|
||||
specified this way must be at least 16 bytes (32 digits) long (although
|
||||
specific algorithms can impose other restrictions).
|
||||
A password can be specified as a string or as a sequence of hexadecimal
|
||||
digit pairs (<ref id="type-bytestring" name="bytestring">).
|
||||
|
||||
This option is allowed in BFD, OSPF, RIP, and Babel protocols. BGP has
|
||||
also <cf/password/ option, but it is slightly different and described
|
||||
@ -1350,20 +1348,24 @@ can group several statements to a single compound statement by using braces
|
||||
(<cf>{ <M>statements</M> }</cf>) which is useful if you want to make a bigger
|
||||
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:
|
||||
<p>BIRD supports functions, so that you don not have to repeat the same blocks
|
||||
of code over and over. Functions can have zero or more parameters and they can
|
||||
have local variables. If the function returns value, then you should always
|
||||
specify its return type. Direct recursion is possible. Function definitions look
|
||||
like this:
|
||||
|
||||
<code>
|
||||
function name ()
|
||||
function name() -> int
|
||||
{
|
||||
int local_variable;
|
||||
int another_variable = 5;
|
||||
return 42;
|
||||
}
|
||||
|
||||
function with_parameters (int parameter)
|
||||
function with_parameters(int parameter) -> pair
|
||||
{
|
||||
print parameter;
|
||||
return (1, 2);
|
||||
}
|
||||
</code>
|
||||
|
||||
@ -1377,7 +1379,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.
|
||||
@ -1441,6 +1443,27 @@ in the foot).
|
||||
!˜/) operators could be used to match a string value against
|
||||
a shell pattern (represented also as a string).
|
||||
|
||||
<tag><label id="type-bytestring">bytestring</tag>
|
||||
This is a sequences of arbitrary bytes. There are no ways to modify
|
||||
bytestrings in filters. You can pass them between function, assign
|
||||
them to variables of type <cf/bytestring/, print such values,
|
||||
compare bytestings (<cf/=, !=/). Bytestring literals are written
|
||||
in several ways:
|
||||
|
||||
A sequence of hexadecimal digit pairs, optionally colon-separated.
|
||||
A bytestring specified this way must be at least 16 bytes (32 digits)
|
||||
long: <cf/01:23:45:67:89:ab:cd:ef:01:23:45:67:89:ab:cd:ef/ or
|
||||
<cf/0123456789abcdef0123456789abcdef/.
|
||||
|
||||
A sequence of hexadecimal digit pairs of any lengh (including zero)
|
||||
with the <cf/hex:/ prefix. Colon separators can be inserted
|
||||
arbitrarily between any bytes: <cf/hex:/, <cf/hex:1234/,
|
||||
<cf/hex:1234:56:78/.
|
||||
|
||||
A bytestring can be made from a hex string using <cf/from_hex()/
|
||||
function. Source strings can use any number of colons, hyphens and
|
||||
spaces as byte separators: <cf/from_hex(" 1234 56:78 ab-cd-ef ")/.
|
||||
|
||||
<tag><label id="type-ip">ip</tag>
|
||||
This type can hold a single IP address. The IPv4 addresses are stored as
|
||||
IPv4-Mapped IPv6 addresses so one data type for both of them is used.
|
||||
@ -1623,23 +1646,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/). 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
|
||||
@ -1667,28 +1691,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/). 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/.
|
||||
|
||||
@ -1917,6 +1942,70 @@ protocol sections.
|
||||
<chapt>Protocols
|
||||
<label id="protocols">
|
||||
|
||||
<sect>Aggregator
|
||||
<label id="aggregator">
|
||||
|
||||
<sect1>Introduction
|
||||
<label id="aggregator-intro">
|
||||
<p>The Aggregator protocol explicitly merges routes by the given rules. There
|
||||
are four phases of aggregation. First routes are filtered, then sorted into buckets,
|
||||
then buckets are merged and finally the results are filtered once again.
|
||||
Aggregating an already aggregated route is forbidden.
|
||||
|
||||
<p>This is an experimental protocol, use with caution.
|
||||
|
||||
<sect1>Configuration
|
||||
<label id="aggregator-config">
|
||||
<p><descrip>
|
||||
<tag><label id="aggregator-table">table <m/table/</tag>
|
||||
The table from which routes are exported to get aggregated.
|
||||
|
||||
<tag><label id="aggregator-export">export <m/.../</tag>
|
||||
A standard channel's <cf/export/ clause, defining which routes are accepted into aggregation.
|
||||
|
||||
<tag><label id="aggregator-rule">aggregate on <m/expr/ | <m/attribute/ [<m/, .../]</tag>
|
||||
All the given filter expressions and route attributes are evaluated for each route. Then routes
|
||||
are sorted into buckets where <em/all/ values are the same. Note: due to performance reasons,
|
||||
all filter expressions must return a compact type, e.g. integer, a BGP
|
||||
(standard, extended, large) community or an IP address. If you need to compare e.g. modified
|
||||
AS Paths in the aggregation rule, you can define a custom route attribute and set this attribute
|
||||
in the export filter. For now, it's mandatory to say <cf/net/ here, we can't merge prefixes yet.
|
||||
|
||||
<tag><label id="aggregation-merge">merge by { <m/filter code/ }</tag>
|
||||
The given filter code has an extra symbol defined: <cf/routes/. By iterating over <cf/routes/,
|
||||
you get all the routes in the bucket and you can construct your new route. All attributes
|
||||
selected in <cf/aggregate on/ are already set to the common values. For now, it's not possible
|
||||
to use a named filter here. You have to finalize the route by calling <cf/accept/.
|
||||
|
||||
<tag><label id="aggregator-import">import <m/.../</tag>
|
||||
Filter applied to the route after <cf/merge by/. Here you can use a named filter.
|
||||
|
||||
<tag><label id="aggregator-peer-table">peer table <m/table/</tag>
|
||||
The table to which aggregated routes are imported. It may be the same table
|
||||
as <cf/table/.
|
||||
</descrip>
|
||||
|
||||
<sect1>Example
|
||||
<label id="aggregator-example">
|
||||
|
||||
<p><code>
|
||||
protocol aggregator {
|
||||
table master6;
|
||||
export where defined(bgp_path);
|
||||
/* Merge all routes with the same AS Path length */
|
||||
aggregate on net, bgp_path.len;
|
||||
merge by {
|
||||
for route r in routes do {
|
||||
if ! defined(bgp_path) then { bgp_path = r.bgp_path }
|
||||
bgp_community = bgp_community.add(r.bgp_community);
|
||||
}
|
||||
accept;
|
||||
};
|
||||
import all;
|
||||
peer table agr_result;
|
||||
}
|
||||
</code>
|
||||
|
||||
<sect>Babel
|
||||
<label id="babel">
|
||||
|
||||
@ -1955,7 +2044,7 @@ protocol babel [<name>] {
|
||||
ipv6 [sadr] { <channel config> };
|
||||
randomize router id <switch>;
|
||||
interface <interface pattern> {
|
||||
type <wired|wireless>;
|
||||
type <wired|wireless|tunnel>;
|
||||
rxcost <number>;
|
||||
limit <number>;
|
||||
hello interval <time>;
|
||||
@ -1969,6 +2058,11 @@ protocol babel [<name>] {
|
||||
next hop ipv4 <address>;
|
||||
next hop ipv6 <address>;
|
||||
extended next hop <switch>;
|
||||
rtt cost <number>;
|
||||
rtt min <time>;
|
||||
rtt max <time>;
|
||||
rtt decay <number>;
|
||||
send timestamps <switch>;
|
||||
authentication none|mac [permissive];
|
||||
password "<text>";
|
||||
password "<text>" {
|
||||
@ -1999,15 +2093,16 @@ protocol babel [<name>] {
|
||||
router ID every time it starts up, which avoids this problem at the cost
|
||||
of not having stable router IDs in the network. Default: no.
|
||||
|
||||
<tag><label id="babel-type">type wired|wireless </tag>
|
||||
This option specifies the interface type: Wired or wireless. On wired
|
||||
interfaces a neighbor is considered unreachable after a small number of
|
||||
Hello packets are lost, as described by <cf/limit/ option. On wireless
|
||||
<tag><label id="babel-type">type wired|wireless|tunnel </tag>
|
||||
This option specifies the interface type: Wired, wireless or tunnel. On
|
||||
wired interfaces a neighbor is considered unreachable after a small number
|
||||
of Hello packets are lost, as described by <cf/limit/ option. On wireless
|
||||
interfaces the ETX link quality estimation technique is used to compute
|
||||
the metrics of routes discovered over this interface. This technique will
|
||||
gradually degrade the metric of routes when packets are lost rather than
|
||||
the more binary up/down mechanism of wired type links. Default:
|
||||
<cf/wired/.
|
||||
the more binary up/down mechanism of wired type links. A tunnel is like a
|
||||
wired interface, but turns on RTT-based metrics with a default cost of 96.
|
||||
Default: <cf/wired/.
|
||||
|
||||
<tag><label id="babel-rxcost">rxcost <m/num/</tag>
|
||||
This option specifies the nominal RX cost of the interface. The effective
|
||||
@ -2078,6 +2173,37 @@ protocol babel [<name>] {
|
||||
hop when IPv4 addresses are absent from the interface as described in
|
||||
<rfc id="9229">. Default: yes.
|
||||
|
||||
<tag><label id="babel-rtt-cost">rtt cost <m/number/</tag>
|
||||
The RTT-based cost that will be applied to all routes from each neighbour
|
||||
based on the measured RTT to that neighbour. If this value is set,
|
||||
timestamps will be included in generated Babel Hello and IHU messages, and
|
||||
(if the neighbours also have timestamps enabled), the RTT to each
|
||||
neighbour will be computed. An additional cost is added to a neighbour if
|
||||
its RTT is above the <ref id="babel-rtt-min" name="rtt min"> value
|
||||
configured on the interface. The added cost scales linearly from 0 up to
|
||||
the RTT cost configured in this option; the full cost is applied if the
|
||||
neighbour RTT reaches the RTT configured in the <ref id="babel-rtt-max"
|
||||
name="rtt max"> option (and for all RTTs above this value). Default: 0
|
||||
(disabled), except for tunnel interfaces, where it is 96.
|
||||
|
||||
<tag><label id="babel-rtt-min">rtt min <m/time/ s|ms</tag>
|
||||
The minimum RTT above which the RTT cost will start to be applied (scaling
|
||||
linearly from zero up to the full cost). Default: 10 ms
|
||||
|
||||
<tag><label id="babel-rtt-max">rtt max <m/time/ s|ms</tag>
|
||||
The maximum RTT above which the full RTT cost will start be applied.
|
||||
Default: 120 ms
|
||||
|
||||
<tag><label id="babel-rtt-decay">rtt decay <m/number/</tag>
|
||||
The decay factor used for the exponentional moving average of the RTT
|
||||
samples from each neighbour, in units of 1/256. Higher values discards old
|
||||
RTT samples faster. Must be between 1 and 256. Default: 42
|
||||
|
||||
<tag><label id="babel-send-timestamps">send timestamps <m/switch/</tag>
|
||||
Whether to send the timestamps used for RTT calculation on this interface.
|
||||
Sending the timestamps enables peers to calculate an RTT to this node,
|
||||
even if no RTT cost is applied to the route metrics. Default: yes.
|
||||
|
||||
<tag><label id="babel-authentication">authentication none|mac [permissive]</tag>
|
||||
Selects authentication method to be used. <cf/none/ means that packets
|
||||
are not authenticated at all, <cf/mac/ means MAC authentication is
|
||||
@ -2425,7 +2551,6 @@ avoid routing loops.
|
||||
<item> <rfc id="5065"> - AS confederations for BGP
|
||||
<item> <rfc id="5082"> - Generalized TTL Security Mechanism
|
||||
<item> <rfc id="5492"> - Capabilities Advertisement with BGP
|
||||
<item> <rfc id="5549"> - Advertising IPv4 NLRI with an IPv6 Next Hop
|
||||
<item> <rfc id="5575"> - Dissemination of Flow Specification Rules
|
||||
<item> <rfc id="5668"> - 4-Octet AS Specific BGP Extended Community
|
||||
<item> <rfc id="6286"> - AS-Wide Unique BGP Identifier
|
||||
@ -2440,6 +2565,7 @@ avoid routing loops.
|
||||
<item> <rfc id="8203"> - BGP Administrative Shutdown Communication
|
||||
<item> <rfc id="8212"> - Default EBGP Route Propagation Behavior without Policies
|
||||
<item> <rfc id="8654"> - Extended Message Support for BGP
|
||||
<item> <rfc id="8950"> - Advertising IPv4 NLRI with an IPv6 Next Hop
|
||||
<item> <rfc id="9072"> - Extended Optional Parameters Length for BGP OPEN Message
|
||||
<item> <rfc id="9117"> - Revised Validation Procedure for BGP Flow Specifications
|
||||
<item> <rfc id="9234"> - Route Leak Prevention and Detection Using Roles
|
||||
@ -2710,13 +2836,20 @@ using the following configuration parameters:
|
||||
changes its import filter, or if there is suspicion of inconsistency) it
|
||||
is necessary to do a new complete route exchange. BGP protocol extension
|
||||
Route Refresh (<rfc id="2918">) allows BGP speaker to request
|
||||
re-advertisement of all routes from its neighbor. BGP protocol
|
||||
extension Enhanced Route Refresh (<rfc id="7313">) specifies explicit
|
||||
begin and end for such exchanges, therefore the receiver can remove
|
||||
stale routes that were not advertised during the exchange. This option
|
||||
specifies whether BIRD advertises these capabilities and supports
|
||||
re-advertisement of all routes from its neighbor. This option
|
||||
specifies whether BIRD advertises this capability and supports
|
||||
related procedures. Note that even when disabled, BIRD can send route
|
||||
refresh requests. Default: on.
|
||||
refresh requests. Disabling Route Refresh also disables Enhanced Route Refresh.
|
||||
Default: on.
|
||||
|
||||
<tag><label id="bgp-enable-enhanced-route-refresh">enable enhanced route refresh <m/switch/</tag>
|
||||
BGP protocol extension Enhanced Route Refresh (<rfc id="7313">) specifies explicit
|
||||
begin and end for Route Refresh (see previous option),
|
||||
therefore the receiver can remove
|
||||
stale routes that were not advertised during the exchange. This option
|
||||
specifies whether BIRD advertises this capability and supports
|
||||
related procedures.
|
||||
Default: on.
|
||||
|
||||
<tag><label id="bgp-graceful-restart">graceful restart <m/switch/|aware</tag>
|
||||
When a BGP speaker restarts or crashes, neighbors will discard all
|
||||
@ -3093,7 +3226,7 @@ be used in explicit configuration.
|
||||
associated network prefixes. This option provides an extension to use
|
||||
IPv4 next hops with IPv6 prefixes and vice versa. For IPv4 / VPNv4
|
||||
channels, the behavior is controlled by the Extended Next Hop Encoding
|
||||
capability, as described in <rfc id="5549">. For IPv6 / VPNv6 channels,
|
||||
capability, as described in <rfc id="8950">. For IPv6 / VPNv6 channels,
|
||||
just IPv4-mapped IPv6 addresses are used, as described in
|
||||
<rfc id="4798"> and <rfc id="4659">. Default: off.
|
||||
|
||||
@ -3255,6 +3388,13 @@ some of them (marked with `<tt/O/') are optional.
|
||||
name="local Role"> is configured it set automatically.
|
||||
</descrip>
|
||||
|
||||
<p>For attributes unknown by BIRD, the user can assign a name (on top level)
|
||||
to an attribute by its number. This defined name can be used then to both set
|
||||
(by a bytestring literal, transitive) or unset the given attribute even though
|
||||
BIRD knows nothing about it:
|
||||
|
||||
<tt><label id="bgp-attribute-custom">attribute bgp <m/number/ bytestring <m/name/;</tt>
|
||||
|
||||
<sect1>Example
|
||||
<label id="bgp-exam">
|
||||
|
||||
@ -3298,6 +3438,35 @@ protocol bgp {
|
||||
</code>
|
||||
|
||||
|
||||
<sect>BMP
|
||||
<label id="bmp">
|
||||
|
||||
<p>The BGP Monitoring Protocol is used for monitoring BGP sessions and obtaining
|
||||
routing table data. The current implementation in BIRD is a preliminary release
|
||||
with a limited feature set, it will be subject to significant changes in the
|
||||
future. It is not ready for production usage and therefore it is not compiled
|
||||
by default and have to be enabled during installation by the configure option
|
||||
<tt/--with-protocols=/.
|
||||
|
||||
<p>The implementation is limited to monitor protocol state changes and routes
|
||||
in <ref id="bgp-import-table" name="BGP import tables"> (not regular routing
|
||||
tables), therefore import table must be enabled in BGP protocols. All BGP
|
||||
protocols are monitored automatically.
|
||||
|
||||
<sect1>Example
|
||||
<label id="bmp-exam">
|
||||
|
||||
<p><code>
|
||||
protocol bmp {
|
||||
# The monitoring station to connect to
|
||||
station address ip 198.51.100.10 port 1790;
|
||||
|
||||
# required option
|
||||
monitoring rib in pre_policy;
|
||||
}
|
||||
</code>
|
||||
|
||||
|
||||
<sect>Device
|
||||
<label id="device">
|
||||
|
||||
@ -4521,6 +4690,21 @@ definitions, prefix definitions and DNS definitions:
|
||||
options and there is a short variant <cf>dnssl <m/domain/</cf> that just
|
||||
specifies one DNS search domain.
|
||||
|
||||
<tag><label id="radv-custom-option">custom option type <m/number/ value <m/bytestring/</tag>
|
||||
Custom option definitions allow to define an arbitrary option to
|
||||
advertise. You need to specify the option type number and the binary
|
||||
payload of the option. The length field is calculated automatically.
|
||||
Like <cf/rdnss/ above, multiple definitions are cumulative, they can
|
||||
be used also as interface-specific options.
|
||||
|
||||
The following example advertises PREF64 option (<rfc id="8781">) with
|
||||
prefix <cf>2001:db8:a:b::/96</cf> and the lifetime of <cf/1 hour/:
|
||||
|
||||
<label id="radv-custom-option-exam">
|
||||
<p><code>
|
||||
custom option type 38 value hex:0e10:2001:0db8:000a:000b:0000:0000;
|
||||
</code>
|
||||
|
||||
<tag><label id="radv-trigger">trigger <m/prefix/</tag>
|
||||
RAdv protocol could be configured to change its behavior based on
|
||||
availability of routes. When this option is used, the protocol waits in
|
||||
@ -4650,6 +4834,10 @@ definitions, prefix definitions and DNS definitions:
|
||||
<tag><label id="radv-iface-dnssl-local">dnssl local <m/switch/</tag>
|
||||
Use only local DNSSL definitions for this interface. See <cf/rdnss local/
|
||||
option above. Default: no.
|
||||
|
||||
<tag><label id="radv-iface-custom-local">custom option local <m/switch/</tag>
|
||||
Use only local custom option definitions for this interface. See <cf/rdnss local/
|
||||
option above. Default: no.
|
||||
</descrip>
|
||||
|
||||
<p>Prefix specific options
|
||||
@ -5342,8 +5530,8 @@ as the destination becomes adjacent again.
|
||||
(i.e., they can be used multiple times for a route, one time for each nexthop).
|
||||
Syntactically, they are not separate options but just parts of <cf/route/
|
||||
statement after each <cf/via/ statement, not separated by semicolons. E.g.,
|
||||
statement <cf/route 10.0.0.0/8 via 192.0.2.1 bfd weight 1 via 192.0.2.2 weight
|
||||
2;/ describes a route with two nexthops, the first nexthop has two per-nexthop
|
||||
statement <cf>route 10.0.0.0/8 via 192.0.2.1 bfd weight 1 via 192.0.2.2 weight
|
||||
2;</cf> describes a route with two nexthops, the first nexthop has two per-nexthop
|
||||
options (<cf/bfd/ and <cf/weight 1/), the second nexthop has just <cf/weight 2/.
|
||||
|
||||
<descrip>
|
||||
|
547
filter/config.Y
547
filter/config.Y
@ -19,18 +19,59 @@ static inline u32 pair(u32 a, u32 b) { return (a << 16) | b; }
|
||||
static inline u32 pair_a(u32 p) { return p >> 16; }
|
||||
static inline u32 pair_b(u32 p) { return p & 0xFFFF; }
|
||||
|
||||
#define f_generate_complex(fi_code, da, arg) \
|
||||
f_new_inst(FI_EA_SET, f_new_inst(fi_code, f_new_inst(FI_EA_GET, da), arg), da)
|
||||
static struct symbol *this_function;
|
||||
|
||||
#define f_generate_complex_sym(fi_code, sym, arg) ({ \
|
||||
if (sym->class != SYM_ATTRIBUTE) \
|
||||
cf_error("Can't empty %s: not an attribute", sym->name); \
|
||||
f_generate_complex(fi_code, sym->attribute, arg); \
|
||||
})
|
||||
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 f_generate_complex_default(fi_code, da, arg, def) \
|
||||
f_new_inst(FI_EA_SET, f_new_inst(fi_code, f_new_inst(FI_DEFAULT, f_new_inst(FI_EA_GET, da), f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = def })), arg), da)
|
||||
static struct sym_scope *f_for_stored_scope;
|
||||
|
||||
#define FM (f_method_scope_stack[f_method_scope_pos])
|
||||
|
||||
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->hash.count && !scope->next)
|
||||
cf_error("No methods defined for type %s", f_type_name(object->type));
|
||||
|
||||
/* Replacing the current symbol scope with the appropriate method scope
|
||||
for the given type. */
|
||||
FM = (struct f_method_scope) {
|
||||
.object = object,
|
||||
.main = new_config->current_scope,
|
||||
.scope = {
|
||||
.next = scope->next,
|
||||
.hash = scope->hash,
|
||||
.block = 1,
|
||||
.readonly = 1,
|
||||
},
|
||||
};
|
||||
new_config->current_scope = &FM.scope;
|
||||
}
|
||||
|
||||
static inline void f_method_call_args(void)
|
||||
{
|
||||
/* For argument parsing, we need to revert back to the standard symbol scope. */
|
||||
new_config->current_scope = FM.main;
|
||||
}
|
||||
|
||||
static inline void f_method_call_end(void)
|
||||
{
|
||||
ASSERT_DIE(f_method_scope_pos >= 0);
|
||||
if (&FM.scope == new_config->current_scope)
|
||||
new_config->current_scope = FM.main;
|
||||
|
||||
f_method_scope_pos--;
|
||||
}
|
||||
|
||||
static int
|
||||
f_new_var(struct sym_scope *s)
|
||||
@ -62,6 +103,10 @@ f_new_var(struct sym_scope *s)
|
||||
return offset;
|
||||
}
|
||||
|
||||
/* Macro for top-level pre-defined variables. */
|
||||
#define f_predefined_variable(conf_, name_, type_) \
|
||||
cf_define_symbol(conf_, cf_get_symbol(conf_, name_), SYM_VARIABLE | type_, offset, f_new_var(conf_->current_scope))
|
||||
|
||||
/*
|
||||
* Sets and their items are during parsing handled as lists, linked
|
||||
* through left ptr. The first item in a list also contains a pointer
|
||||
@ -200,34 +245,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)
|
||||
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)
|
||||
{
|
||||
const struct ea_class *def = ea_class_find("bgp_path");
|
||||
if (!def)
|
||||
cf_error("Fatal: Couldn't find BGP path attribute definition.");
|
||||
|
||||
struct f_static_attr fsa = f_new_static_attr(T_NET, SA_NET, 1);
|
||||
|
||||
return f_new_inst(FI_ROA_CHECK,
|
||||
f_new_inst(FI_RTA_GET, fsa),
|
||||
f_new_inst(FI_AS_PATH_LAST, f_new_inst(FI_EA_GET, def)),
|
||||
tab);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove all new lines and doubled whitespaces
|
||||
@ -283,28 +300,60 @@ assert_done(struct f_inst *expr, const char *start, const char *end)
|
||||
: "???");
|
||||
}
|
||||
|
||||
static struct f_inst *
|
||||
f_lval_getter(struct f_lval *lval)
|
||||
{
|
||||
switch (lval->type) {
|
||||
case F_LVAL_CONSTANT: return f_new_inst(FI_CONSTANT, *(lval->sym->val));
|
||||
case F_LVAL_VARIABLE: return f_new_inst(FI_VAR_GET, lval->sym);
|
||||
case F_LVAL_SA: return f_new_inst(FI_RTA_GET, lval->rte, lval->sa);
|
||||
case F_LVAL_EA: return f_new_inst(FI_EA_GET, lval->rte, lval->da);
|
||||
case F_LVAL_ATTR_BIT:
|
||||
{
|
||||
struct f_inst *c = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = (1U << lval->fab.bit)});
|
||||
return f_new_inst(FI_EQ, c, f_new_inst(FI_BITAND, f_new_inst(FI_EA_GET, lval->rte, lval->fab.class), c));
|
||||
}
|
||||
default: bug("Unknown lval type");
|
||||
}
|
||||
}
|
||||
|
||||
static struct f_inst *
|
||||
f_lval_setter(struct f_lval *lval, struct f_inst *expr)
|
||||
{
|
||||
switch (lval->type) {
|
||||
case F_LVAL_CONSTANT: cf_error("Constant %s is read-only", lval->sym->name);
|
||||
case F_LVAL_VARIABLE: return f_new_inst(FI_VAR_SET, expr, lval->sym);
|
||||
case F_LVAL_SA:
|
||||
if (lval->sa.readonly)
|
||||
cf_error( "This static attribute is read-only.");
|
||||
return f_new_inst(FI_RTA_SET, expr, lval->sa);
|
||||
|
||||
case F_LVAL_EA: return f_new_inst(FI_EA_SET, expr, lval->da);
|
||||
case F_LVAL_ATTR_BIT: return f_new_inst(FI_CONDITION, expr,
|
||||
f_new_inst(FI_EA_SET,
|
||||
f_new_inst(FI_BITOR,
|
||||
f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = (1U << lval->fab.bit)}),
|
||||
f_new_inst(FI_EA_GET, lval->rte, lval->fab.class)
|
||||
),
|
||||
lval->fab.class),
|
||||
f_new_inst(FI_EA_SET,
|
||||
f_new_inst(FI_BITAND,
|
||||
f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = ~(1U << lval->fab.bit)}),
|
||||
f_new_inst(FI_EA_GET, lval->rte, lval->fab.class)
|
||||
),
|
||||
lval->fab.class)
|
||||
);
|
||||
default: bug("Unknown lval type");
|
||||
}
|
||||
}
|
||||
|
||||
static struct f_inst *
|
||||
assert_assign(struct f_lval *lval, struct f_inst *expr, const char *start, const char *end)
|
||||
{
|
||||
struct f_inst *setter, *getter, *checker;
|
||||
switch (lval->type) {
|
||||
case F_LVAL_VARIABLE:
|
||||
setter = f_new_inst(FI_VAR_SET, expr, lval->sym);
|
||||
getter = f_new_inst(FI_VAR_GET, lval->sym);
|
||||
break;
|
||||
case F_LVAL_SA:
|
||||
setter = f_new_inst(FI_RTA_SET, expr, lval->sa);
|
||||
getter = f_new_inst(FI_RTA_GET, lval->sa);
|
||||
break;
|
||||
case F_LVAL_EA:
|
||||
setter = f_new_inst(FI_EA_SET, expr, lval->da);
|
||||
getter = f_new_inst(FI_EA_GET, lval->da);
|
||||
break;
|
||||
default:
|
||||
bug("Unknown lval type");
|
||||
}
|
||||
struct f_inst *setter = f_lval_setter(lval, expr),
|
||||
*getter = f_lval_getter(lval);
|
||||
|
||||
checker = f_new_inst(FI_EQ, expr, getter);
|
||||
struct f_inst *checker = f_new_inst(FI_EQ, expr, getter);
|
||||
setter->next = checker;
|
||||
|
||||
return assert_done(setter, start, end);
|
||||
@ -314,34 +363,31 @@ CF_DECLS
|
||||
|
||||
CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
||||
ACCEPT, REJECT, ERROR,
|
||||
INT, BOOL, IP, TYPE, PREFIX, RD, PAIR, QUAD, EC, LC,
|
||||
SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
|
||||
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)
|
||||
|
||||
%nonassoc THEN
|
||||
%nonassoc ELSE
|
||||
|
||||
%type <xp> cmds_int cmd_prep
|
||||
%type <x> term cmd cmd_var cmds cmds_scoped constant constructor print_list var var_init var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail
|
||||
%type <x> term term_bs cmd cmd_var cmds cmds_scoped constant constructor var var_list var_list_r function_call 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 function_vars function_type
|
||||
%type <fa> function_argsn function_args
|
||||
%type <ecs> ec_kind
|
||||
%type <fret> break_command
|
||||
@ -363,36 +409,45 @@ conf: FILTER STACKS expr expr ';' {
|
||||
|
||||
conf: filter_def ;
|
||||
filter_def:
|
||||
FILTER symbol { $2 = cf_define_symbol($2, SYM_FILTER, filter, NULL); cf_push_scope( $2 ); }
|
||||
filter_body {
|
||||
FILTER symbol {
|
||||
$2 = cf_define_symbol(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 };
|
||||
$2->filter = f;
|
||||
|
||||
cf_pop_scope();
|
||||
cf_pop_scope(new_config);
|
||||
cf_exit_filters();
|
||||
}
|
||||
;
|
||||
|
||||
conf: filter_eval ;
|
||||
filter_eval:
|
||||
EVAL term { f_eval_int(f_linearize($2, 1)); }
|
||||
EVAL term { cf_eval_int($2); }
|
||||
;
|
||||
|
||||
conf: custom_attr ;
|
||||
custom_attr: ATTRIBUTE type symbol ';' {
|
||||
if (($3->class == SYM_ATTRIBUTE) && ($3->scope == new_config->root_scope))
|
||||
cf_error("Duplicate attribute %s definition", $3->name);
|
||||
|
||||
cf_define_symbol($3, SYM_ATTRIBUTE, attribute,
|
||||
ea_register_alloc(new_config->pool, (struct ea_class) {
|
||||
cf_enter_filters();
|
||||
struct ea_class *ac = ea_class_find_by_name($3->name);
|
||||
cf_exit_filters();
|
||||
if (ac && (ac->type == $2))
|
||||
ea_ref_class(new_config->pool, ac);
|
||||
else
|
||||
ac = ea_register_alloc(new_config->pool, (struct ea_class) {
|
||||
.name = $3->name,
|
||||
.type = $2,
|
||||
})->class);
|
||||
})->class;
|
||||
|
||||
cf_define_symbol(new_config, $3, SYM_ATTRIBUTE, attribute, ac);
|
||||
};
|
||||
|
||||
conf: bt_test_suite ;
|
||||
bt_test_suite:
|
||||
BT_TEST_SUITE '(' symbol_known ',' text ')' {
|
||||
BT_TEST_SUITE '(' CF_SYM_KNOWN ',' text ')' {
|
||||
cf_assert_symbol($3, SYM_FUNCTION);
|
||||
struct f_bt_test_suite *t = cfg_allocz(sizeof(struct f_bt_test_suite));
|
||||
t->fn = $3->function;
|
||||
@ -405,7 +460,7 @@ bt_test_suite:
|
||||
|
||||
conf: bt_test_same ;
|
||||
bt_test_same:
|
||||
BT_TEST_SAME '(' symbol_known ',' symbol_known ',' NUM ')' {
|
||||
BT_TEST_SAME '(' CF_SYM_KNOWN ',' CF_SYM_KNOWN ',' NUM ')' {
|
||||
cf_assert_symbol($3, SYM_FUNCTION);
|
||||
cf_assert_symbol($5, SYM_FUNCTION);
|
||||
struct f_bt_test_suite *t = cfg_allocz(sizeof(struct f_bt_test_suite));
|
||||
@ -429,11 +484,13 @@ type:
|
||||
| EC { $$ = T_EC; }
|
||||
| LC { $$ = T_LC; }
|
||||
| STRING { $$ = T_STRING; }
|
||||
| BYTESTRING { $$ = T_BYTESTRING; }
|
||||
| BGPMASK { $$ = T_PATH_MASK; }
|
||||
| BGPPATH { $$ = T_PATH; }
|
||||
| CLIST { $$ = T_CLIST; }
|
||||
| ECLIST { $$ = T_ECLIST; }
|
||||
| LCLIST { $$ = T_LCLIST; }
|
||||
| ROUTE { $$ = T_ROUTE; }
|
||||
| type SET {
|
||||
switch ($1) {
|
||||
case T_INT:
|
||||
@ -461,7 +518,7 @@ function_argsn:
|
||||
| function_argsn type symbol ';' {
|
||||
if ($3->scope->slots >= 0xfe) cf_error("Too many declarations, at most 255 allowed");
|
||||
$$ = cfg_alloc(sizeof(struct f_arg));
|
||||
$$->arg = cf_define_symbol($3, SYM_VARIABLE | $2, offset, sym_->scope->slots++);
|
||||
$$->arg = cf_define_symbol(new_config, $3, SYM_VARIABLE | $2, offset, sym_->scope->slots++);
|
||||
$$->next = $1;
|
||||
}
|
||||
;
|
||||
@ -470,7 +527,7 @@ function_args:
|
||||
'(' ')' { $$ = NULL; }
|
||||
| '(' function_argsn type symbol ')' {
|
||||
$$ = cfg_alloc(sizeof(struct f_arg));
|
||||
$$->arg = cf_define_symbol($4, SYM_VARIABLE | $3, offset, sym_->scope->slots++);
|
||||
$$->arg = cf_define_symbol(new_config, $4, SYM_VARIABLE | $3, offset, sym_->scope->slots++);
|
||||
$$->next = $2;
|
||||
}
|
||||
;
|
||||
@ -478,31 +535,44 @@ function_args:
|
||||
function_vars:
|
||||
/* EMPTY */ { $$ = 0; }
|
||||
| function_vars type symbol ';' {
|
||||
cf_define_symbol($3, SYM_VARIABLE | $2, offset, f_new_var(sym_->scope));
|
||||
cf_define_symbol(new_config, $3, SYM_VARIABLE | $2, offset, f_new_var(sym_->scope));
|
||||
$$ = $1 + 1;
|
||||
}
|
||||
;
|
||||
|
||||
function_type:
|
||||
/* EMPTY */ { $$ = T_VOID; }
|
||||
| IMP type { $$ = $2; }
|
||||
;
|
||||
|
||||
filter_body: function_body ;
|
||||
|
||||
filter:
|
||||
symbol_known {
|
||||
CF_SYM_KNOWN {
|
||||
cf_assert_symbol($1, SYM_FILTER);
|
||||
$$ = $1->filter;
|
||||
}
|
||||
| { cf_push_scope(NULL); } filter_body {
|
||||
| {
|
||||
cf_enter_filters();
|
||||
cf_push_scope(new_config, NULL);
|
||||
this_function = NULL;
|
||||
} filter_body {
|
||||
struct filter *f = cfg_alloc(sizeof(struct filter));
|
||||
*f = (struct filter) { .root = $2 };
|
||||
$$ = f;
|
||||
|
||||
cf_pop_scope();
|
||||
cf_pop_scope(new_config);
|
||||
cf_exit_filters();
|
||||
}
|
||||
;
|
||||
|
||||
where_filter:
|
||||
WHERE term {
|
||||
WHERE {
|
||||
cf_enter_filters();
|
||||
} term {
|
||||
/* Construct 'IF term THEN { ACCEPT; } ELSE { REJECT; }' */
|
||||
$$ = f_new_where($2);
|
||||
$$ = f_new_where($3);
|
||||
cf_exit_filters();
|
||||
}
|
||||
;
|
||||
|
||||
@ -514,15 +584,19 @@ function_body:
|
||||
;
|
||||
|
||||
conf: function_def ;
|
||||
|
||||
function_def:
|
||||
FUNCTION symbol {
|
||||
DBG( "Beginning of function %s\n", $2->name );
|
||||
$2 = cf_define_symbol($2, SYM_FUNCTION, function, NULL);
|
||||
cf_push_scope($2);
|
||||
} function_args {
|
||||
this_function = cf_define_symbol(new_config, $2, SYM_FUNCTION, function, NULL);
|
||||
cf_enter_filters();
|
||||
cf_push_scope(new_config, this_function);
|
||||
} function_args function_type {
|
||||
/* 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 = $5;
|
||||
|
||||
/* Revert the args */
|
||||
while ($4) {
|
||||
@ -534,10 +608,12 @@ function_def:
|
||||
dummy->args++;
|
||||
}
|
||||
} function_body {
|
||||
$6->args = $2->function->args;
|
||||
$6->arg_list = $2->function->arg_list;
|
||||
$2->function = $6;
|
||||
cf_pop_scope();
|
||||
$7->args = this_function->function->args;
|
||||
$7->arg_list = this_function->function->arg_list;
|
||||
$7->return_type = this_function->function->return_type;
|
||||
$2->function = $7;
|
||||
cf_pop_scope(new_config);
|
||||
cf_exit_filters();
|
||||
}
|
||||
;
|
||||
|
||||
@ -547,7 +623,7 @@ cmds: /* EMPTY */ { $$ = NULL; }
|
||||
| cmds_int { $$ = $1.begin; }
|
||||
;
|
||||
|
||||
cmds_scoped: { cf_push_soft_scope(); } cmds { cf_pop_soft_scope(); $$ = $2; } ;
|
||||
cmds_scoped: { cf_push_soft_scope(new_config); } cmds { cf_pop_soft_scope(new_config); $$ = $2; } ;
|
||||
|
||||
cmd_var: var | cmd ;
|
||||
|
||||
@ -596,10 +672,10 @@ set_atom:
|
||||
| VPN_RD { $$.type = T_RD; $$.val.ec = $1; }
|
||||
| ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
|
||||
| '(' term ')' {
|
||||
if (f_eval(f_linearize($2, 1), &($$)) > F_RETURN) cf_error("Runtime error");
|
||||
$$ = cf_eval_tmp($2, T_VOID);
|
||||
if (!f_valid_set_type($$.type)) cf_error("Set-incompatible type");
|
||||
}
|
||||
| symbol_known {
|
||||
| CF_SYM_KNOWN {
|
||||
cf_assert_symbol($1, SYM_CONSTANT);
|
||||
if (!f_valid_set_type(SYM_TYPE($1))) cf_error("%s: set-incompatible type", $1->name);
|
||||
$$ = *$1->val;
|
||||
@ -608,13 +684,13 @@ set_atom:
|
||||
|
||||
switch_atom:
|
||||
NUM { $$.type = T_INT; $$.val.i = $1; }
|
||||
| '(' term ')' { $$.type = T_INT; $$.val.i = f_eval_int(f_linearize($2, 1)); }
|
||||
| '(' term ')' { $$ = cf_eval_tmp($2, T_INT); }
|
||||
| fipa { $$ = $1; }
|
||||
| ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
|
||||
;
|
||||
|
||||
cnum:
|
||||
term { $$ = f_eval_int(f_linearize($1, 1)); }
|
||||
term { $$ = cf_eval_int($1); }
|
||||
|
||||
pair_item:
|
||||
'(' cnum ',' cnum ')' { $$ = f_new_pair_item($2, $2, $4, $4); }
|
||||
@ -715,7 +791,7 @@ switch_body: /* EMPTY */ { $$ = NULL; }
|
||||
;
|
||||
|
||||
bgp_path_expr:
|
||||
symbol_value { $$ = $1; }
|
||||
lvalue { $$ = f_lval_getter(&$1); }
|
||||
| '(' term ')' { $$ = $2; }
|
||||
;
|
||||
|
||||
@ -743,6 +819,7 @@ constant:
|
||||
| TRUE { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 1, }); }
|
||||
| FALSE { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 0, }); }
|
||||
| TEXT { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_STRING, .val.s = $1, }); }
|
||||
| BYTETEXT { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BYTESTRING, .val.bs = $1, }); }
|
||||
| fipa { $$ = f_new_inst(FI_CONSTANT, $1); }
|
||||
| VPN_RD { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_RD, .val.ec = $1, }); }
|
||||
| net_ { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_NET, .val.net = $1, }); }
|
||||
@ -764,48 +841,38 @@ constructor:
|
||||
;
|
||||
|
||||
|
||||
/* This generates the function_call variable list backwards. */
|
||||
var_list: /* EMPTY */ { $$ = NULL; }
|
||||
/* This generates the function_call variable list backwards */
|
||||
var_list_r:
|
||||
/* EMPTY */ { $$ = NULL; }
|
||||
| term { $$ = $1; }
|
||||
| var_list ',' term { $$ = $3; $$->next = $1; }
|
||||
| var_list_r ',' term { $$ = $3; $$->next = $1; }
|
||||
;
|
||||
|
||||
var_list: var_list_r
|
||||
{
|
||||
$$ = NULL;
|
||||
|
||||
/* Revert the var_list_r */
|
||||
while ($1) {
|
||||
struct f_inst *tmp = $1;
|
||||
$1 = $1->next;
|
||||
|
||||
tmp->next = $$;
|
||||
$$ = tmp;
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
function_call:
|
||||
symbol_known '(' var_list ')'
|
||||
CF_SYM_KNOWN '(' var_list ')'
|
||||
{
|
||||
if ($1->class != SYM_FUNCTION)
|
||||
cf_error("You can't call something which is not a function. Really.");
|
||||
|
||||
/* Revert the var_list */
|
||||
struct f_inst *args = NULL;
|
||||
while ($3) {
|
||||
struct f_inst *tmp = $3;
|
||||
$3 = $3->next;
|
||||
|
||||
tmp->next = args;
|
||||
args = tmp;
|
||||
}
|
||||
|
||||
$$ = f_new_inst(FI_CALL, args, $1);
|
||||
$$ = f_new_inst(FI_CALL, $3, $1);
|
||||
}
|
||||
;
|
||||
|
||||
symbol_value: symbol_known
|
||||
{
|
||||
switch ($1->class) {
|
||||
case SYM_CONSTANT_RANGE:
|
||||
$$ = f_new_inst(FI_CONSTANT, *($1->val));
|
||||
break;
|
||||
case SYM_VARIABLE_RANGE:
|
||||
$$ = f_new_inst(FI_VAR_GET, $1);
|
||||
break;
|
||||
case SYM_ATTRIBUTE:
|
||||
$$ = f_new_inst(FI_EA_GET, $1->attribute);
|
||||
break;
|
||||
default:
|
||||
cf_error("Can't get value of symbol %s", $1->name);
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
static_attr:
|
||||
GW { $$ = f_new_static_attr(T_IP, SA_GW, 0); }
|
||||
@ -818,6 +885,30 @@ static_attr:
|
||||
| GW_MPLS { $$ = f_new_static_attr(T_INT, SA_GW_MPLS, 0); }
|
||||
;
|
||||
|
||||
term_dot_method: term '.' { f_method_call_start($1); } method_name_cont { f_method_call_end(); $$ = $4; };
|
||||
method_name_cont:
|
||||
CF_SYM_METHOD_BARE {
|
||||
$$ = f_dispatch_method($1, FM.object, NULL, 1);
|
||||
}
|
||||
| CF_SYM_METHOD_ARGS {
|
||||
f_method_call_args();
|
||||
} '(' var_list ')' {
|
||||
$$ = f_dispatch_method($1, FM.object, $4, 1);
|
||||
}
|
||||
| static_attr {
|
||||
if (FM.object->type != T_ROUTE)
|
||||
cf_error("Getting a route attribute from %s, need a route", f_type_name(FM.object->type));
|
||||
$$ = f_new_inst(FI_RTA_GET, FM.object, $1);
|
||||
}
|
||||
| CF_SYM_KNOWN {
|
||||
if ($1->class != SYM_ATTRIBUTE)
|
||||
cf_error("Not a method of %s: %s", f_type_name(FM.object->type), $1->name);
|
||||
if (FM.object->type != T_ROUTE)
|
||||
cf_error("Getting a route attribute from %s, need a route", f_type_name(FM.object->type));
|
||||
$$ = f_new_inst(FI_EA_GET, FM.object, $1->attribute);
|
||||
}
|
||||
;
|
||||
|
||||
term:
|
||||
'(' term ')' { $$ = $2; }
|
||||
| term '+' term { $$ = f_new_inst(FI_ADD, $1, $3); }
|
||||
@ -839,85 +930,67 @@ term:
|
||||
| '!' term { $$ = f_new_inst(FI_NOT, $2); }
|
||||
| DEFINED '(' term ')' { $$ = f_new_inst(FI_DEFINED, $3); }
|
||||
|
||||
| symbol_value { $$ = $1; }
|
||||
| constant { $$ = $1; }
|
||||
| constructor { $$ = $1; }
|
||||
|
||||
| static_attr { $$ = f_new_inst(FI_RTA_GET, $1); }
|
||||
| lvalue { $$ = f_lval_getter(&$1); }
|
||||
|
||||
| term '.' IS_V4 { $$ = f_new_inst(FI_IS_V4, $1); }
|
||||
| term '.' TYPE { $$ = f_new_inst(FI_TYPE, $1); }
|
||||
| term '.' IP { $$ = f_new_inst(FI_IP, $1); }
|
||||
| term '.' RD { $$ = f_new_inst(FI_ROUTE_DISTINGUISHER, $1); }
|
||||
| term '.' LEN { $$ = f_new_inst(FI_LENGTH, $1); }
|
||||
| term '.' MAXLEN { $$ = f_new_inst(FI_ROA_MAXLEN, $1); }
|
||||
| term '.' ASN { $$ = f_new_inst(FI_ASN, $1); }
|
||||
| term '.' SRC { $$ = f_new_inst(FI_NET_SRC, $1); }
|
||||
| term '.' DST { $$ = f_new_inst(FI_NET_DST, $1); }
|
||||
| term '.' MASK '(' term ')' { $$ = f_new_inst(FI_IP_MASK, $1, $5); }
|
||||
| term '.' FIRST { $$ = f_new_inst(FI_AS_PATH_FIRST, $1); }
|
||||
| term '.' LAST { $$ = f_new_inst(FI_AS_PATH_LAST, $1); }
|
||||
| term '.' LAST_NONAGGREGATED { $$ = f_new_inst(FI_AS_PATH_LAST_NAG, $1); }
|
||||
| term '.' DATA { $$ = f_new_inst(FI_PAIR_DATA, $1); }
|
||||
| term '.' DATA1 { $$ = f_new_inst(FI_LC_DATA1, $1); }
|
||||
| term '.' DATA2 { $$ = f_new_inst(FI_LC_DATA2, $1); }
|
||||
| term '.' MIN { $$ = f_new_inst(FI_MIN, $1); }
|
||||
| term '.' MAX { $$ = f_new_inst(FI_MAX, $1); }
|
||||
| term_dot_method
|
||||
|
||||
/* Communities */
|
||||
/* This causes one shift/reduce conflict
|
||||
| dynamic_attr '.' ADD '(' term ')' { }
|
||||
| dynamic_attr '.' DELETE '(' term ')' { }
|
||||
| dynamic_attr '.' CONTAINS '(' term ')' { }
|
||||
| dynamic_attr '.' RESET{ }
|
||||
*/
|
||||
| '+' 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)); }
|
||||
|
||||
| '+' EMPTY '+' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_path); }
|
||||
| '-' EMPTY '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_clist); }
|
||||
| '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_eclist); }
|
||||
| '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_lclist); }
|
||||
| PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND, $3, $5); }
|
||||
| ADD '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD, $3, $5); }
|
||||
| DELETE '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_DEL, $3, $5); }
|
||||
| FILTER '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_FILTER, $3, $5); }
|
||||
| PREPEND '(' term ',' term ')' {
|
||||
$$ = f_dispatch_method_x("prepend", $3->type, $3, $5);
|
||||
cf_warn("prepend(x,y) function is deprecated, please use x.prepend(y)");
|
||||
}
|
||||
| ADD '(' term ',' term ')' {
|
||||
$$ = f_dispatch_method_x("add", $3->type, $3, $5);
|
||||
cf_warn("add(x,y) function is deprecated, please use x.add(y)");
|
||||
}
|
||||
| DELETE '(' term ',' term ')' {
|
||||
$$ = f_dispatch_method_x("delete", $3->type, $3, $5);
|
||||
cf_warn("delete(x,y) function is deprecated, please use x.delete(y)");
|
||||
}
|
||||
| FILTER '(' term ',' term ')' {
|
||||
$$ = f_dispatch_method_x("filter", $3->type, $3, $5);
|
||||
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); }
|
||||
|
||||
| FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT, $3); }
|
||||
|
||||
| term_bs
|
||||
| function_call
|
||||
;
|
||||
|
||||
term_bs:
|
||||
FROM_HEX '(' term ')' { $$ = f_new_inst(FI_FROM_HEX, $3); }
|
||||
;
|
||||
|
||||
break_command:
|
||||
ACCEPT { $$ = F_ACCEPT; }
|
||||
| REJECT { $$ = F_REJECT; }
|
||||
| ERROR { $$ = F_ERROR; }
|
||||
;
|
||||
|
||||
print_list: /* EMPTY */ { $$ = NULL; }
|
||||
| term { $$ = $1; }
|
||||
| term ',' print_list {
|
||||
ASSERT($1);
|
||||
ASSERT($1->next == NULL);
|
||||
$1->next = $3;
|
||||
$$ = $1;
|
||||
}
|
||||
;
|
||||
|
||||
var_init:
|
||||
/* empty */ { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { }); }
|
||||
| '=' term { $$ = $2; }
|
||||
;
|
||||
|
||||
var:
|
||||
type symbol var_init ';' {
|
||||
struct symbol *sym = cf_define_symbol($2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope));
|
||||
$$ = f_new_inst(FI_VAR_INIT, $3, sym);
|
||||
type symbol '=' term ';' {
|
||||
struct symbol *sym = cf_define_symbol(new_config, $2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope));
|
||||
$$ = f_new_inst(FI_VAR_INIT, $4, sym);
|
||||
}
|
||||
| type symbol ';' {
|
||||
struct symbol *sym = cf_define_symbol(new_config, $2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope));
|
||||
$$ = f_new_inst(FI_VAR_INIT0, sym);
|
||||
}
|
||||
;
|
||||
|
||||
for_var:
|
||||
type symbol { $$ = cf_define_symbol($2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope)); }
|
||||
type symbol { $$ = cf_define_symbol(new_config, $2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope)); }
|
||||
| CF_SYM_KNOWN { $$ = $1; cf_assert_symbol($1, SYM_VARIABLE); }
|
||||
;
|
||||
|
||||
@ -933,74 +1006,68 @@ cmd:
|
||||
}
|
||||
| FOR {
|
||||
/* Reserve space for walk data on stack */
|
||||
cf_push_block_scope();
|
||||
conf_this_scope->slots += 2;
|
||||
cf_push_block_scope(new_config);
|
||||
new_config->current_scope->slots += 2;
|
||||
} for_var IN
|
||||
/* Parse term in the parent scope */
|
||||
{ conf_this_scope->active = 0; } term { conf_this_scope->active = 1; }
|
||||
{
|
||||
ASSERT_DIE(f_for_stored_scope == NULL);
|
||||
f_for_stored_scope = new_config->current_scope;
|
||||
new_config->current_scope = new_config->current_scope->next;
|
||||
} term {
|
||||
ASSERT_DIE(f_for_stored_scope);
|
||||
new_config->current_scope = f_for_stored_scope;
|
||||
f_for_stored_scope = NULL;
|
||||
}
|
||||
DO cmd {
|
||||
cf_pop_block_scope();
|
||||
$$ = f_new_inst(FI_FOR_INIT, $6, $3);
|
||||
$$->next = f_new_inst(FI_FOR_NEXT, $3, $9);
|
||||
}
|
||||
| symbol_known '=' term ';' {
|
||||
switch ($1->class) {
|
||||
case SYM_VARIABLE_RANGE:
|
||||
$$ = f_new_inst(FI_VAR_SET, $3, $1);
|
||||
break;
|
||||
case SYM_ATTRIBUTE:
|
||||
if ($1->attribute->readonly)
|
||||
cf_error("Attribute %s is read-only", $1->attribute->name);
|
||||
$$ = f_new_inst(FI_EA_SET, $3, $1->attribute);
|
||||
break;
|
||||
default:
|
||||
cf_error("Can't assign to symbol %s", $1->name);
|
||||
cf_pop_block_scope(new_config);
|
||||
$$ = f_for_cycle($3, $6, $9);
|
||||
}
|
||||
| lvalue '=' term ';' {
|
||||
$$ = f_lval_setter(&$1, $3);
|
||||
}
|
||||
| RETURN term ';' {
|
||||
DBG( "Ook, we'll return the value\n" );
|
||||
if (!this_function)
|
||||
cf_error("Can't return from a non-function, use accept or reject instead.");
|
||||
if (this_function->function->return_type == T_VOID)
|
||||
{
|
||||
if ($2->type != T_VOID)
|
||||
cf_warn("Inferring function %s return type from its return value: %s", this_function->name, f_type_name($2->type));
|
||||
((struct f_line *) this_function->function)->return_type = $2->type;
|
||||
}
|
||||
else if (this_function->function->return_type != $2->type)
|
||||
cf_error("Can't return type %s from function %s, expected %s",
|
||||
f_type_name($2->type), this_function->name, f_type_name(this_function->function->return_type));
|
||||
|
||||
$$ = f_new_inst(FI_RETURN, $2);
|
||||
}
|
||||
| static_attr '=' term ';' {
|
||||
if ($1.readonly)
|
||||
cf_error( "This static attribute is read-only.");
|
||||
$$ = f_new_inst(FI_RTA_SET, $3, $1);
|
||||
}
|
||||
| UNSET '(' symbol_known ')' ';' {
|
||||
| UNSET '(' CF_SYM_KNOWN ')' ';' {
|
||||
if ($3->class != SYM_ATTRIBUTE)
|
||||
cf_error("Can't unset %s", $3->name);
|
||||
if ($3->attribute->readonly)
|
||||
cf_error("Attribute %s is read-only", $3->attribute->name);
|
||||
$$ = f_new_inst(FI_EA_UNSET, $3->attribute);
|
||||
}
|
||||
| break_command print_list ';' {
|
||||
struct f_inst *breaker = f_new_inst(FI_DIE, $1);
|
||||
if ($2) {
|
||||
struct f_inst *printer = f_new_inst(FI_PRINT, $2);
|
||||
struct f_inst *flusher = f_new_inst(FI_FLUSH);
|
||||
printer->next = flusher;
|
||||
flusher->next = breaker;
|
||||
$$ = printer;
|
||||
} else
|
||||
$$ = breaker;
|
||||
| break_command var_list_r ';' {
|
||||
$$ = f_print($2, !!$2, $1);
|
||||
}
|
||||
| PRINT print_list ';' {
|
||||
$$ = f_new_inst(FI_PRINT, $2);
|
||||
$$->next = f_new_inst(FI_FLUSH);
|
||||
| PRINT var_list_r ';' {
|
||||
$$ = f_print($2, 1, F_NOP);
|
||||
}
|
||||
| PRINTN print_list ';' {
|
||||
$$ = f_new_inst(FI_PRINT, $2);
|
||||
| PRINTN var_list_r ';' {
|
||||
$$ = f_print($2, 0, F_NOP);
|
||||
}
|
||||
| function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); }
|
||||
| CASE term '{' switch_body '}' {
|
||||
$$ = f_new_inst(FI_SWITCH, $2, $4);
|
||||
}
|
||||
|
||||
| symbol_known '.' EMPTY ';' { $$ = f_generate_empty($1); }
|
||||
| symbol_known '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex_sym( FI_PATH_PREPEND, $1, $5 ); }
|
||||
| symbol_known '.' ADD '(' term ')' ';' { $$ = f_generate_complex_sym( FI_CLIST_ADD, $1, $5 ); }
|
||||
| symbol_known '.' DELETE '(' term ')' ';' { $$ = f_generate_complex_sym( FI_CLIST_DEL, $1, $5 ); }
|
||||
| symbol_known '.' FILTER '(' term ')' ';' { $$ = f_generate_complex_sym( FI_CLIST_FILTER, $1, $5 ); }
|
||||
| lvalue '.' {
|
||||
f_method_call_start(f_lval_getter(&$1));
|
||||
} method_name_cont ';' {
|
||||
f_method_call_end();
|
||||
$$ = f_lval_setter(&$1, $4);
|
||||
}
|
||||
| BT_ASSERT '(' get_cf_position term get_cf_position ')' ';' { $$ = assert_done($4, $3 + 1, $5 - 1); }
|
||||
| BT_CHECK_ASSIGN '(' get_cf_position lvalue get_cf_position ',' term ')' ';' { $$ = assert_assign(&$4, $7, $3 + 1, $5 - 1); }
|
||||
;
|
||||
@ -1011,17 +1078,23 @@ get_cf_position:
|
||||
};
|
||||
|
||||
lvalue:
|
||||
symbol_known {
|
||||
switch ($1->class) {
|
||||
CF_SYM_KNOWN {
|
||||
switch ($1->class)
|
||||
{
|
||||
case SYM_CONSTANT_RANGE:
|
||||
$$ = (struct f_lval) { .type = F_LVAL_CONSTANT, .sym = $1, };
|
||||
break;
|
||||
case SYM_VARIABLE_RANGE:
|
||||
$$ = (struct f_lval) { .type = F_LVAL_VARIABLE, .sym = $1 };
|
||||
$$ = (struct f_lval) { .type = F_LVAL_VARIABLE, .sym = $1, };
|
||||
break;
|
||||
case SYM_ATTRIBUTE:
|
||||
$$ = (struct f_lval) { .type = F_LVAL_EA, .da = $1->attribute };
|
||||
$$ = (struct f_lval) { .type = F_LVAL_EA, .da = $1->attribute, .rte = f_new_inst(FI_CURRENT_ROUTE), };
|
||||
break;
|
||||
default:
|
||||
cf_error("Variable name or attribute name required");
|
||||
}
|
||||
}
|
||||
| static_attr { $$ = (struct f_lval) { .type = F_LVAL_SA, .sa = $1 }; }
|
||||
| static_attr { $$ = (struct f_lval) { .type = F_LVAL_SA, .sa = $1, .rte = f_new_inst(FI_CURRENT_ROUTE), }; }
|
||||
;
|
||||
|
||||
CF_END
|
||||
|
154
filter/data.c
154
filter/data.c
@ -16,7 +16,8 @@
|
||||
#include "lib/unaligned.h"
|
||||
#include "lib/net.h"
|
||||
#include "lib/ip.h"
|
||||
#include "nest/rt.h"
|
||||
#include "lib/hash.h"
|
||||
#include "nest/route.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/iface.h"
|
||||
#include "lib/attrs.h"
|
||||
@ -27,6 +28,8 @@
|
||||
|
||||
static const char * const f_type_str[] = {
|
||||
[T_VOID] = "void",
|
||||
[T_NONE] = "none",
|
||||
|
||||
[T_OPAQUE] = "opaque byte string",
|
||||
[T_IFACE] = "interface",
|
||||
|
||||
@ -47,6 +50,7 @@ static const char * const f_type_str[] = {
|
||||
[T_IP] = "ip",
|
||||
[T_NET] = "prefix",
|
||||
[T_STRING] = "string",
|
||||
[T_BYTESTRING] = "bytestring",
|
||||
[T_PATH_MASK] = "bgpmask",
|
||||
[T_PATH_MASK_ITEM] = "bgpmask item",
|
||||
[T_PATH] = "bgppath",
|
||||
@ -57,14 +61,19 @@ static const char * const f_type_str[] = {
|
||||
[T_LCLIST] = "lclist",
|
||||
[T_RD] = "rd",
|
||||
|
||||
[T_ROUTE] = "route",
|
||||
[T_ROUTES_BLOCK] = "block of routes",
|
||||
|
||||
[T_SET] = "set",
|
||||
[T_PREFIX_SET] = "prefix set",
|
||||
};
|
||||
|
||||
STATIC_ASSERT((1 << (8 * sizeof(btype))) == ARRAY_SIZE(f_type_str));
|
||||
|
||||
const char *
|
||||
f_type_name(btype t)
|
||||
{
|
||||
return (t < ARRAY_SIZE(f_type_str)) ? (f_type_str[t] ?: "?") : "?";
|
||||
return f_type_str[t] ?: "?";
|
||||
}
|
||||
|
||||
btype
|
||||
@ -75,25 +84,13 @@ f_type_element_type(btype t)
|
||||
case T_CLIST: return T_PAIR;
|
||||
case T_ECLIST: return T_EC;
|
||||
case T_LCLIST: return T_LC;
|
||||
case T_ROUTES_BLOCK: return T_ROUTE;
|
||||
default: return T_VOID;
|
||||
};
|
||||
}
|
||||
|
||||
const struct f_trie f_const_empty_trie = { .ipv4 = -1, };
|
||||
|
||||
const struct f_val f_const_empty_path = {
|
||||
.type = T_PATH,
|
||||
.val.ad = &null_adata,
|
||||
}, f_const_empty_clist = {
|
||||
.type = T_CLIST,
|
||||
.val.ad = &null_adata,
|
||||
}, f_const_empty_eclist = {
|
||||
.type = T_ECLIST,
|
||||
.val.ad = &null_adata,
|
||||
}, f_const_empty_lclist = {
|
||||
.type = T_LCLIST,
|
||||
.val.ad = &null_adata,
|
||||
}, f_const_empty_prefix_set = {
|
||||
const struct f_val f_const_empty_prefix_set = {
|
||||
.type = T_PREFIX_SET,
|
||||
.val.ti = &f_const_empty_trie,
|
||||
};
|
||||
@ -208,11 +205,21 @@ val_compare(const struct f_val *v1, const struct f_val *v2)
|
||||
return net_compare(v1->val.net, v2->val.net);
|
||||
case T_STRING:
|
||||
return strcmp(v1->val.s, v2->val.s);
|
||||
case T_PATH:
|
||||
return as_path_compare(v1->val.ad, v2->val.ad);
|
||||
case T_ROUTE:
|
||||
case T_ROUTES_BLOCK:
|
||||
default:
|
||||
return F_CMP_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int
|
||||
bs_same(const struct adata *bs1, const struct adata *bs2)
|
||||
{
|
||||
return (bs1->length == bs2->length) && !memcmp(bs1->data, bs2->data, bs1->length);
|
||||
}
|
||||
|
||||
static inline int
|
||||
pmi_same(const struct f_path_mask_item *mi1, const struct f_path_mask_item *mi2)
|
||||
{
|
||||
@ -277,6 +284,8 @@ val_same(const struct f_val *v1, const struct f_val *v2)
|
||||
return 0;
|
||||
|
||||
switch (v1->type) {
|
||||
case T_BYTESTRING:
|
||||
return bs_same(v1->val.bs, v2->val.bs);
|
||||
case T_PATH_MASK:
|
||||
return pm_same(v1->val.path_mask, v2->val.path_mask);
|
||||
case T_PATH_MASK_ITEM:
|
||||
@ -290,6 +299,15 @@ val_same(const struct f_val *v1, const struct f_val *v2)
|
||||
return same_tree(v1->val.t, v2->val.t);
|
||||
case T_PREFIX_SET:
|
||||
return trie_same(v1->val.ti, v2->val.ti);
|
||||
case T_ROUTE:
|
||||
return rte_same(v1->val.rte, v2->val.rte);
|
||||
case T_ROUTES_BLOCK:
|
||||
if (v1->val.rte_block.len != v2->val.rte_block.len)
|
||||
return 0;
|
||||
for (uint i=0; i < v1->val.rte_block.len; i++)
|
||||
if (!rte_same(v1->val.rte_block.rte[i], v2->val.rte_block.rte[i]))
|
||||
return 0;
|
||||
return 1;
|
||||
default:
|
||||
bug("Invalid type in val_same(): %x", v1->type);
|
||||
}
|
||||
@ -563,6 +581,106 @@ val_in_range(const struct f_val *v1, const struct f_val *v2)
|
||||
return F_CMP_ERROR;
|
||||
}
|
||||
|
||||
uint
|
||||
val_hash(struct f_val *v)
|
||||
{
|
||||
u64 haux;
|
||||
mem_hash_init(&haux);
|
||||
mem_hash_mix_f_val(&haux, v);
|
||||
return mem_hash_value(&haux);
|
||||
}
|
||||
|
||||
void
|
||||
mem_hash_mix_f_val(u64 *h, struct f_val *v)
|
||||
{
|
||||
mem_hash_mix_num(h, v->type);
|
||||
|
||||
#define MX(k) mem_hash_mix(h, &IT(k), sizeof IT(k));
|
||||
#define IT(k) v->val.k
|
||||
|
||||
switch (v->type)
|
||||
{
|
||||
case T_VOID:
|
||||
break;
|
||||
case T_INT:
|
||||
case T_BOOL:
|
||||
case T_PAIR:
|
||||
case T_QUAD:
|
||||
case T_ENUM:
|
||||
MX(i);
|
||||
break;
|
||||
case T_EC:
|
||||
case T_RD:
|
||||
MX(ec);
|
||||
break;
|
||||
case T_LC:
|
||||
MX(lc);
|
||||
break;
|
||||
case T_IP:
|
||||
MX(ip);
|
||||
break;
|
||||
case T_NET:
|
||||
mem_hash_mix_num(h, net_hash(IT(net)));
|
||||
break;
|
||||
case T_STRING:
|
||||
mem_hash_mix_str(h, IT(s));
|
||||
break;
|
||||
case T_PATH_MASK:
|
||||
mem_hash_mix(h, IT(path_mask), sizeof(*IT(path_mask)) + IT(path_mask)->len * sizeof (IT(path_mask)->item));
|
||||
break;
|
||||
case T_PATH:
|
||||
case T_CLIST:
|
||||
case T_ECLIST:
|
||||
case T_LCLIST:
|
||||
case T_BYTESTRING:
|
||||
mem_hash_mix(h, IT(ad)->data, IT(ad)->length);
|
||||
break;
|
||||
case T_SET:
|
||||
MX(t);
|
||||
break;
|
||||
case T_PREFIX_SET:
|
||||
MX(ti);
|
||||
break;
|
||||
|
||||
case T_NONE:
|
||||
case T_PATH_MASK_ITEM:
|
||||
case T_ROUTE:
|
||||
case T_ROUTES_BLOCK:
|
||||
case T_OPAQUE:
|
||||
case T_NEXTHOP_LIST:
|
||||
case T_HOSTENTRY:
|
||||
case T_IFACE:
|
||||
bug("Invalid type %s in f_val hashing", f_type_name(v->type));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* rte_format - format route information
|
||||
*/
|
||||
static void
|
||||
rte_format(const struct rte *rte, buffer *buf)
|
||||
{
|
||||
if (rte)
|
||||
buffer_print(buf, "Route [%d] to %N from %s via %s",
|
||||
rte->src->global_id, rte->net,
|
||||
rte->sender->req->name,
|
||||
rte->src->owner->name);
|
||||
else
|
||||
buffer_puts(buf, "[No route]");
|
||||
}
|
||||
|
||||
static void
|
||||
rte_block_format(const struct rte_block *block, buffer *buf)
|
||||
{
|
||||
buffer_print(buf, "Block of routes:");
|
||||
|
||||
for (uint i = 0; i < block->len; i++)
|
||||
{
|
||||
buffer_print(buf, "%s%d: ", i ? "; " : " ", i);
|
||||
rte_format(block->rte[i], buf);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* val_format - format filter value
|
||||
*/
|
||||
@ -576,6 +694,7 @@ val_format(const struct f_val *v, buffer *buf)
|
||||
case T_BOOL: buffer_puts(buf, v->val.i ? "TRUE" : "FALSE"); return;
|
||||
case T_INT: buffer_print(buf, "%u", v->val.i); return;
|
||||
case T_STRING: buffer_print(buf, "%s", v->val.s); return;
|
||||
case T_BYTESTRING: bstrbintohex(v->val.bs->data, v->val.bs->length, buf2, 1000, ':'); buffer_print(buf, "%s", buf2); return;
|
||||
case T_IP: buffer_print(buf, "%I", v->val.ip); return;
|
||||
case T_NET: buffer_print(buf, "%N", v->val.net); return;
|
||||
case T_PAIR: buffer_print(buf, "(%u,%u)", v->val.i >> 16, v->val.i & 0xffff); return;
|
||||
@ -591,6 +710,8 @@ val_format(const struct f_val *v, buffer *buf)
|
||||
case T_ECLIST: ec_set_format(v->val.ad, -1, buf2, 1000); buffer_print(buf, "(eclist %s)", buf2); return;
|
||||
case T_LCLIST: lc_set_format(v->val.ad, -1, buf2, 1000); buffer_print(buf, "(lclist %s)", buf2); return;
|
||||
case T_PATH_MASK: pm_format(v->val.path_mask, buf); return;
|
||||
case T_ROUTE: rte_format(v->val.rte, buf); return;
|
||||
case T_ROUTES_BLOCK: rte_block_format(&v->val.rte_block, buf); return;
|
||||
default: buffer_print(buf, "[unknown type %x]", v->type); return;
|
||||
}
|
||||
}
|
||||
@ -616,7 +737,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)
|
||||
{
|
||||
|
@ -12,6 +12,15 @@
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "lib/type.h"
|
||||
#include "nest/iface.h"
|
||||
|
||||
struct f_method {
|
||||
struct symbol *sym;
|
||||
struct f_inst *(*new_inst)(struct f_inst *obj, struct f_inst *args);
|
||||
const struct f_method *next;
|
||||
uint arg_num;
|
||||
enum btype args_type[];
|
||||
};
|
||||
|
||||
/* Filter value; size of this affects filter memory consumption */
|
||||
struct f_val {
|
||||
@ -39,20 +48,31 @@ struct f_static_attr {
|
||||
int readonly:1; /* Don't allow writing */
|
||||
};
|
||||
|
||||
struct f_attr_bit {
|
||||
const struct ea_class *class;
|
||||
uint bit;
|
||||
};
|
||||
|
||||
#define f_new_dynamic_attr_bit(_bit, _name) ((struct f_attr_bit) { .bit = _bit, .class = ea_class_find(_name) })
|
||||
|
||||
/* Filter l-value type */
|
||||
enum f_lval_type {
|
||||
F_LVAL_CONSTANT,
|
||||
F_LVAL_VARIABLE,
|
||||
F_LVAL_SA,
|
||||
F_LVAL_EA,
|
||||
F_LVAL_ATTR_BIT,
|
||||
};
|
||||
|
||||
/* Filter l-value */
|
||||
struct f_lval {
|
||||
enum f_lval_type type;
|
||||
struct f_inst *rte;
|
||||
union {
|
||||
struct symbol *sym;
|
||||
const struct ea_class *da;
|
||||
struct f_static_attr sa;
|
||||
struct f_attr_bit fab;
|
||||
};
|
||||
};
|
||||
|
||||
@ -201,8 +221,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);
|
||||
@ -210,6 +230,9 @@ void val_format(const struct f_val *v, buffer *buf);
|
||||
char *val_format_str(struct linpool *lp, const struct f_val *v);
|
||||
const char *val_dump(const struct f_val *v);
|
||||
|
||||
uint val_hash(struct f_val *);
|
||||
void mem_hash_mix_f_val(u64 *, struct f_val *);
|
||||
|
||||
struct f_val *lp_val_copy(struct linpool *lp, const struct f_val *v);
|
||||
|
||||
static inline int val_is_ip4(const struct f_val *v)
|
||||
@ -234,23 +257,29 @@ const struct adata *lclist_filter(struct linpool *pool, const struct adata *list
|
||||
|
||||
|
||||
/* Special undef value for paths and clists */
|
||||
|
||||
static inline int
|
||||
undef_value(struct f_val v)
|
||||
val_is_undefined(struct f_val v)
|
||||
{
|
||||
return ((v.type == T_PATH) || (v.type == T_CLIST) ||
|
||||
(v.type == T_ECLIST) || (v.type == T_LCLIST)) &&
|
||||
(v.val.ad == &null_adata);
|
||||
}
|
||||
|
||||
extern const struct f_val f_const_empty_path, f_const_empty_clist, f_const_empty_eclist, f_const_empty_lclist, f_const_empty_prefix_set;
|
||||
static inline const struct f_val *f_get_empty(btype t)
|
||||
extern const struct f_val f_const_empty_prefix_set;
|
||||
static inline struct f_val f_get_empty(btype t)
|
||||
{
|
||||
switch (t) {
|
||||
case T_PATH: return &f_const_empty_path;
|
||||
case T_CLIST: return &f_const_empty_clist;
|
||||
case T_ECLIST: return &f_const_empty_eclist;
|
||||
case T_LCLIST: return &f_const_empty_lclist;
|
||||
default: return NULL;
|
||||
case T_PATH:
|
||||
case T_CLIST:
|
||||
case T_ECLIST:
|
||||
case T_LCLIST:
|
||||
return (struct f_val) {
|
||||
.type = t,
|
||||
.val.ad = &null_adata,
|
||||
};
|
||||
default:
|
||||
return (struct f_val) { .type = T_VOID };
|
||||
}
|
||||
}
|
||||
|
||||
|
150
filter/decl.m4
150
filter/decl.m4
@ -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,9 @@ 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_TYPE_SIGNATURE, `m4_divert(113)')
|
||||
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 +126,13 @@ FID_IFCONST([[
|
||||
constargs = 0;
|
||||
]])
|
||||
} while (child$1 = child$1->next);
|
||||
FID_LINEARIZE_BODY
|
||||
m4_define([[INST_METHOD_NUM_ARGS]],$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 +182,31 @@ 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_TYPE_SIGNATURE()m4_dnl
|
||||
method->args_type[m4_eval($1-1)] = $2;
|
||||
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));
|
||||
@ -200,7 +215,7 @@ FID_INTERPRET_BODY()')
|
||||
# Executing another filter line. This replaces the recursion
|
||||
# that was needed in the former implementation.
|
||||
m4_define(LINEX, `FID_INTERPRET_EXEC()LINEX_($1)FID_INTERPRET_NEW()return $1 FID_INTERPRET_BODY()')
|
||||
m4_define(LINEX_, `do {
|
||||
m4_define(LINEX_, `do if ($1) {
|
||||
if (fstk->ecnt + 1 >= fstk->elen) runtime("Filter execution stack overflow");
|
||||
fstk->estk[fstk->ecnt].pos = 0;
|
||||
fstk->estk[fstk->ecnt].line = $1;
|
||||
@ -219,6 +234,12 @@ FID_NEW_ARGS()m4_dnl
|
||||
, struct f_inst * f$1
|
||||
FID_NEW_BODY()m4_dnl
|
||||
whati->f$1 = f$1;
|
||||
m4_define([[INST_METHOD_NUM_ARGS]],$1)m4_dnl
|
||||
FID_NEW_METHOD()m4_dnl
|
||||
struct f_inst *arg$1 = args;
|
||||
if (args == NULL) cf_error("Not enough arguments"); /* INST_NAME */
|
||||
args = NULL; /* The rest is the line itself */
|
||||
FID_METHOD_CALL() , arg$1
|
||||
FID_DUMP_BODY()m4_dnl
|
||||
f_dump_line(item->fl$1, indent + 1);
|
||||
FID_LINEARIZE_BODY()m4_dnl
|
||||
@ -228,9 +249,7 @@ if (!f_same(f1->fl$1, f2->fl$1)) return 0;
|
||||
FID_ITERATE_BODY()m4_dnl
|
||||
if (whati->fl$1) BUFFER_PUSH(fit->lines) = whati->fl$1;
|
||||
FID_INTERPRET_EXEC()m4_dnl
|
||||
do { if (whati->fl$1) {
|
||||
LINEX_(whati->fl$1);
|
||||
} } while(0)
|
||||
LINEX_(whati->fl$1)
|
||||
FID_INTERPRET_NEW()m4_dnl
|
||||
return whati->f$1
|
||||
FID_INTERPRET_BODY()')
|
||||
@ -269,6 +288,29 @@ 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()')
|
||||
|
||||
# Short method constructor
|
||||
# $1 = type
|
||||
# $2 = name
|
||||
# $3 = method inputs
|
||||
# method outputs are always 1
|
||||
# $4 = code
|
||||
m4_define(METHOD, `m4_dnl
|
||||
INST([[FI_METHOD__]]$1[[__]]$2, m4_eval($3 + 1), 1) {
|
||||
ARG(1, $1);
|
||||
$4
|
||||
METHOD_CONSTRUCTOR("$2");
|
||||
}')
|
||||
|
||||
m4_define(METHOD_R, `METHOD($1, $2, 0, [[ RESULT($3, $4, $5) ]])')
|
||||
|
||||
# 2) Code wrapping
|
||||
# The code produced in 1xx temporary diversions is a raw code without
|
||||
# any auxiliary commands and syntactical structures around. When the
|
||||
@ -288,6 +330,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
|
||||
@ -307,6 +350,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]], [[
|
||||
@ -334,7 +380,8 @@ m4_undivert(102)m4_dnl
|
||||
[[m4_dnl The one case in The Big Switch inside interpreter
|
||||
case INST_NAME():
|
||||
#define whati (&(what->i_]]INST_NAME()[[))
|
||||
m4_ifelse(m4_eval(INST_INVAL() > 0), 1, [[if (fstk->vcnt < INST_INVAL()) runtime("Stack underflow"); fstk->vcnt -= INST_INVAL(); ]])
|
||||
m4_ifelse(m4_eval(INST_INVAL() > 0), 1, [[if (fstk->vcnt < INST_INVAL()) runtime("Stack underflow");
|
||||
fstk->vcnt -= INST_INVAL();]])
|
||||
m4_undivert(108)m4_dnl
|
||||
#undef whati
|
||||
break;
|
||||
@ -364,6 +411,29 @@ 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
|
||||
method = lp_allocz(global_root_scope_linpool, sizeof(struct f_method) + INST_METHOD_NUM_ARGS * sizeof(enum btype));
|
||||
method->new_inst = f_new_method_]]INST_NAME()[[;
|
||||
method->arg_num = INST_METHOD_NUM_ARGS;
|
||||
m4_undivert(113)
|
||||
f_register_method(INST_METHOD_OBJECT_TYPE, INST_METHOD_NAME, method);
|
||||
|
||||
]])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;
|
||||
|
||||
@ -417,6 +487,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.
|
||||
')
|
||||
|
||||
@ -502,8 +574,8 @@ fi_constant(struct f_inst *what, struct f_val val)
|
||||
return what;
|
||||
}
|
||||
|
||||
static int
|
||||
f_const_promotion(struct f_inst *arg, btype want)
|
||||
int
|
||||
f_const_promotion_(struct f_inst *arg, btype want, int update)
|
||||
{
|
||||
if (arg->fi_code != FI_CONSTANT)
|
||||
return 0;
|
||||
@ -511,6 +583,7 @@ f_const_promotion(struct f_inst *arg, btype want)
|
||||
struct f_val *c = &arg->i_FI_CONSTANT.val;
|
||||
|
||||
if ((c->type == T_IP) && ipa_is_ip4(c->val.ip) && (want == T_QUAD)) {
|
||||
if (update)
|
||||
*c = (struct f_val) {
|
||||
.type = T_QUAD,
|
||||
.val.i = ipa_to_u32(c->val.ip),
|
||||
@ -519,6 +592,7 @@ f_const_promotion(struct f_inst *arg, btype want)
|
||||
}
|
||||
|
||||
else if ((c->type == T_SET) && (!c->val.t) && (want == T_PREFIX_SET)) {
|
||||
if (update)
|
||||
*c = f_const_empty_prefix_set;
|
||||
return 1;
|
||||
}
|
||||
@ -540,6 +614,58 @@ 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;
|
||||
}
|
||||
|
||||
static void
|
||||
f_register_method(enum btype t, const byte *name, struct f_method *dsc)
|
||||
{
|
||||
struct sym_scope *scope = &f_type_method_scopes[t];
|
||||
struct symbol *sym = cf_find_symbol_scope(scope, name);
|
||||
|
||||
if (!sym)
|
||||
{
|
||||
sym = cf_root_symbol(name, scope);
|
||||
sym->class = SYM_METHOD;
|
||||
}
|
||||
|
||||
dsc->sym = sym;
|
||||
dsc->next = sym->method;
|
||||
sym->method = dsc;
|
||||
}
|
||||
|
||||
extern struct sym_scope global_filter_scope;
|
||||
|
||||
void f_type_methods_register(void)
|
||||
{
|
||||
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;
|
||||
|
||||
f_type_method_scopes[T_ROUTE].next = &global_filter_scope;
|
||||
}
|
||||
|
||||
/* 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[] = " ";
|
||||
|
759
filter/f-inst.c
759
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
|
||||
@ -98,6 +100,11 @@
|
||||
*
|
||||
* Other code is just copied into the interpreter part.
|
||||
*
|
||||
* It's also possible to declare type methods in a short way:
|
||||
*
|
||||
* m4_dnl METHOD(type, method name, argument count, code)
|
||||
* m4_dnl METHOD_R(type, method name, argument count, result type, union-field, value)
|
||||
*
|
||||
* The filter language uses a simple type system, where values have types
|
||||
* (constants T_*) and also terms (instructions) are statically typed. Our
|
||||
* static typing is partial (some terms do not declare types of arguments
|
||||
@ -493,26 +500,13 @@
|
||||
|
||||
INST(FI_DEFINED, 1, 1) {
|
||||
ARG_ANY(1);
|
||||
RESULT(T_BOOL, i, (v1.type != T_VOID) && !undef_value(v1));
|
||||
RESULT(T_BOOL, i, (v1.type != T_VOID) && !val_is_undefined(v1));
|
||||
}
|
||||
|
||||
INST(FI_TYPE, 1, 1) {
|
||||
ARG_ANY(1); /* There may be more types supporting this operation */
|
||||
switch (v1.type)
|
||||
{
|
||||
case T_NET:
|
||||
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);
|
||||
RESULT(T_BOOL, i, ipa_is_ip4(v1.val.ip));
|
||||
}
|
||||
METHOD_R(T_NET, type, T_ENUM_NETTYPE, i, v1.val.net->type);
|
||||
METHOD_R(T_IP, is_v4, T_BOOL, i, ipa_is_ip4(v1.val.ip));
|
||||
|
||||
/* Add initialized variable */
|
||||
INST(FI_VAR_INIT, 1, 0) {
|
||||
NEVER_CONSTANT;
|
||||
ARG_ANY(1);
|
||||
@ -525,6 +519,17 @@
|
||||
fstk->vcnt = pos + 1;
|
||||
}
|
||||
|
||||
/* Add uninitialized variable */
|
||||
INST(FI_VAR_INIT0, 0, 0) {
|
||||
NEVER_CONSTANT;
|
||||
SYMBOL;
|
||||
|
||||
/* New variable is always the last on stack */
|
||||
uint pos = curline.vbase + sym->offset;
|
||||
fstk->vstk[pos] = f_get_empty(sym->class & 0xff);
|
||||
fstk->vcnt = pos + 1;
|
||||
}
|
||||
|
||||
/* Set to indirect value prepared in v1 */
|
||||
INST(FI_VAR_SET, 1, 0) {
|
||||
NEVER_CONSTANT;
|
||||
@ -555,92 +560,81 @@
|
||||
RESULT_VAL(val);
|
||||
}
|
||||
|
||||
INST(FI_FOR_INIT, 1, 0) {
|
||||
NEVER_CONSTANT;
|
||||
ARG_ANY(1);
|
||||
SYMBOL;
|
||||
METHOD_R(T_PATH, empty, T_PATH, ad, &null_adata);
|
||||
METHOD_R(T_CLIST, empty, T_CLIST, ad, &null_adata);
|
||||
METHOD_R(T_ECLIST, empty, T_ECLIST, ad, &null_adata);
|
||||
METHOD_R(T_LCLIST, empty, T_LCLIST, ad, &null_adata);
|
||||
|
||||
FID_NEW_BODY()
|
||||
ASSERT((sym->class & ~0xff) == SYM_VARIABLE);
|
||||
|
||||
/* Static type check */
|
||||
if (f1->type)
|
||||
{
|
||||
enum btype t_var = (sym->class & 0xff);
|
||||
enum btype t_arg = f_type_element_type(f1->type);
|
||||
if (!t_arg)
|
||||
cf_error("Value of expression in FOR must be iterable, got %s",
|
||||
f_type_name(f1->type));
|
||||
if (t_var != t_arg)
|
||||
cf_error("Loop variable '%s' in FOR must be %s, is %s",
|
||||
sym->name, f_type_name(t_arg), f_type_name(t_var));
|
||||
}
|
||||
|
||||
FID_INTERPRET_BODY()
|
||||
|
||||
/* Dynamic type check */
|
||||
if ((sym->class & 0xff) != f_type_element_type(v1.type))
|
||||
runtime("Mismatched argument and variable type");
|
||||
|
||||
/* Setup the index */
|
||||
v2 = (struct f_val) { .type = T_INT, .val.i = 0 };
|
||||
|
||||
/* Keep v1 and v2 on the stack */
|
||||
fstk->vcnt += 2;
|
||||
}
|
||||
|
||||
INST(FI_FOR_NEXT, 2, 0) {
|
||||
/* Common loop begin instruction, always created by f_for_cycle() */
|
||||
INST(FI_FOR_LOOP_START, 0, 3) {
|
||||
NEVER_CONSTANT;
|
||||
SYMBOL;
|
||||
|
||||
/* Type checks are done in FI_FOR_INIT */
|
||||
/* Repeat the instruction which called us */
|
||||
ASSERT_DIE(fstk->ecnt > 1);
|
||||
prevline.pos--;
|
||||
|
||||
/* Loop variable */
|
||||
struct f_val *var = &fstk->vstk[curline.vbase + sym->offset];
|
||||
int step = 0;
|
||||
/* There should be exactly three items on the value stack to be taken care of */
|
||||
fstk->vcnt += 3;
|
||||
|
||||
switch(v1.type)
|
||||
{
|
||||
case T_PATH:
|
||||
var->type = T_INT;
|
||||
step = as_path_walk(v1.val.ad, &v2.val.i, &var->val.i);
|
||||
break;
|
||||
/* And these should also stay there after we finish for the caller instruction */
|
||||
curline.ventry += 3;
|
||||
|
||||
case T_CLIST:
|
||||
var->type = T_PAIR;
|
||||
step = int_set_walk(v1.val.ad, &v2.val.i, &var->val.i);
|
||||
break;
|
||||
/* Assert the iterator variable positioning */
|
||||
ASSERT_DIE(curline.vbase + sym->offset == fstk->vcnt - 1);
|
||||
|
||||
case T_ECLIST:
|
||||
var->type = T_EC;
|
||||
step = ec_set_walk(v1.val.ad, &v2.val.i, &var->val.ec);
|
||||
break;
|
||||
|
||||
case T_LCLIST:
|
||||
var->type = T_LC;
|
||||
step = lc_set_walk(v1.val.ad, &v2.val.i, &var->val.lc);
|
||||
break;
|
||||
|
||||
default:
|
||||
runtime( "Clist or lclist expected" );
|
||||
/* The result type declaration makes no sense here but is needed */
|
||||
RESULT_TYPE(T_VOID);
|
||||
}
|
||||
|
||||
if (step)
|
||||
{
|
||||
/* Keep v1 and v2 on the stack */
|
||||
fstk->vcnt += 2;
|
||||
/* Type-specific for_next iterators */
|
||||
INST(FI_PATH_FOR_NEXT, 3, 0) {
|
||||
NEVER_CONSTANT;
|
||||
ARG(1, T_PATH);
|
||||
if (as_path_walk(v1.val.ad, &v2.val.i, &v3.val.i))
|
||||
LINE(2,0);
|
||||
|
||||
/* Repeat this instruction */
|
||||
curline.pos--;
|
||||
|
||||
/* Execute the loop body */
|
||||
LINE(1, 0);
|
||||
|
||||
/* Space for loop variable, may be unused */
|
||||
fstk->vcnt += 1;
|
||||
METHOD_CONSTRUCTOR("!for_next");
|
||||
}
|
||||
else
|
||||
var->type = T_VOID;
|
||||
|
||||
INST(FI_CLIST_FOR_NEXT, 3, 0) {
|
||||
NEVER_CONSTANT;
|
||||
ARG(1, T_CLIST);
|
||||
if (int_set_walk(v1.val.ad, &v2.val.i, &v3.val.i))
|
||||
LINE(2,0);
|
||||
|
||||
METHOD_CONSTRUCTOR("!for_next");
|
||||
}
|
||||
|
||||
INST(FI_ECLIST_FOR_NEXT, 3, 0) {
|
||||
NEVER_CONSTANT;
|
||||
ARG(1, T_ECLIST);
|
||||
if (ec_set_walk(v1.val.ad, &v2.val.i, &v3.val.ec))
|
||||
LINE(2,0);
|
||||
|
||||
METHOD_CONSTRUCTOR("!for_next");
|
||||
}
|
||||
|
||||
INST(FI_LCLIST_FOR_NEXT, 3, 0) {
|
||||
NEVER_CONSTANT;
|
||||
ARG(1, T_LCLIST);
|
||||
if (lc_set_walk(v1.val.ad, &v2.val.i, &v3.val.lc))
|
||||
LINE(2,0);
|
||||
|
||||
METHOD_CONSTRUCTOR("!for_next");
|
||||
}
|
||||
|
||||
INST(FI_ROUTES_BLOCK_FOR_NEXT, 3, 0) {
|
||||
NEVER_CONSTANT;
|
||||
ARG(1, T_ROUTES_BLOCK);
|
||||
|
||||
if (v2.val.i < v1.val.rte_block.len)
|
||||
{
|
||||
v3.val.rte = v1.val.rte_block.rte[v2.val.i++];
|
||||
LINE(2,0);
|
||||
}
|
||||
|
||||
METHOD_CONSTRUCTOR("!for_next");
|
||||
}
|
||||
|
||||
INST(FI_CONDITION, 1, 0) {
|
||||
@ -651,17 +645,16 @@
|
||||
LINE(3,0);
|
||||
}
|
||||
|
||||
INST(FI_PRINT, 0, 0) {
|
||||
INST(FI_PRINT, 1, 0) {
|
||||
NEVER_CONSTANT;
|
||||
VARARG;
|
||||
ARG_ANY(1);
|
||||
|
||||
if (whati->varcount && !(fs->flags & FF_SILENT))
|
||||
if (!(fs->flags & FF_SILENT))
|
||||
{
|
||||
if (!fs->buf.class)
|
||||
log_prepare(&fs->buf, *L_INFO);
|
||||
|
||||
for (uint i=0; i<whati->varcount; i++)
|
||||
val_format(&(vv(i)), &fs->buf.buf);
|
||||
val_format(&v1, &fs->buf.buf);
|
||||
}
|
||||
}
|
||||
|
||||
@ -686,18 +679,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
INST(FI_RTA_GET, 0, 1) {
|
||||
{
|
||||
STATIC_ATTR;
|
||||
INST(FI_CURRENT_ROUTE, 0, 1) {
|
||||
NEVER_CONSTANT;
|
||||
ACCESS_RTE;
|
||||
RESULT_TYPE(T_ROUTE);
|
||||
RESULT_VAL([[(struct f_val) { .type = T_ROUTE, .val.rte = fs->rte, }]]);
|
||||
}
|
||||
|
||||
INST(FI_RTA_GET, 1, 1) {
|
||||
{
|
||||
ARG(1, T_ROUTE);
|
||||
STATIC_ATTR;
|
||||
|
||||
struct rte *rte = v1.val.rte;
|
||||
|
||||
switch (sa.sa_code)
|
||||
{
|
||||
case SA_NET: RESULT(sa.type, net, fs->rte->net); break;
|
||||
case SA_PROTO: RESULT(sa.type, s, fs->rte->src->owner->name); break;
|
||||
case SA_NET: RESULT(sa.type, net, rte->net); break;
|
||||
case SA_PROTO: RESULT(sa.type, s, rte->src->owner->name); break;
|
||||
default:
|
||||
{
|
||||
struct eattr *nhea = ea_find(fs->rte->attrs, &ea_gen_nexthop);
|
||||
struct eattr *nhea = ea_find(rte->attrs, &ea_gen_nexthop);
|
||||
struct nexthop_adata *nhad = nhea ? (struct nexthop_adata *) nhea->u.ptr : NULL;
|
||||
struct nexthop *nh = nhad ? &nhad->nh : NULL;
|
||||
|
||||
@ -856,13 +858,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
INST(FI_EA_GET, 0, 1) { /* Access to extended attributes */
|
||||
INST(FI_EA_GET, 1, 1) { /* Access to extended attributes */
|
||||
ARG(1, T_ROUTE);
|
||||
DYNAMIC_ATTR;
|
||||
ACCESS_RTE;
|
||||
RESULT_TYPE(da->type);
|
||||
{
|
||||
const struct f_val *empty;
|
||||
const eattr *e = ea_find(fs->rte->attrs, da->id);
|
||||
struct f_val empty;
|
||||
const eattr *e = ea_find(v1.val.rte->attrs, da->id);
|
||||
|
||||
if (e)
|
||||
{
|
||||
@ -879,8 +881,8 @@
|
||||
}]]);
|
||||
}
|
||||
}
|
||||
else if (empty = f_get_empty(da->type))
|
||||
RESULT_VAL(*empty);
|
||||
else if ((empty = f_get_empty(da->type)).type != T_VOID)
|
||||
RESULT_VAL(empty);
|
||||
else
|
||||
RESULT_VOID;
|
||||
}
|
||||
@ -898,8 +900,8 @@
|
||||
bug("Unsupported attribute type");
|
||||
|
||||
switch (da->type) {
|
||||
case T_OPAQUE:
|
||||
case T_IFACE:
|
||||
case T_OPAQUE:
|
||||
runtime( "Setting opaque attribute is not allowed" );
|
||||
break;
|
||||
|
||||
@ -939,20 +941,16 @@
|
||||
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" );
|
||||
}
|
||||
}
|
||||
/* Get length of */
|
||||
METHOD_R(T_NET, len, T_INT, i, net_pxlen(v1.val.net));
|
||||
METHOD_R(T_PATH, len, T_INT, i, as_path_getlen(v1.val.ad));
|
||||
METHOD_R(T_CLIST, len, T_INT, i, int_set_get_size(v1.val.ad));
|
||||
METHOD_R(T_ECLIST, len, T_INT, i, ec_set_get_size(v1.val.ad));
|
||||
METHOD_R(T_LCLIST, len, 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));
|
||||
@ -988,6 +986,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));
|
||||
@ -1021,156 +1020,78 @@
|
||||
RESULT(T_NET, net, dst);
|
||||
}
|
||||
|
||||
INST(FI_ROA_MAXLEN, 1, 1) { /* Get ROA max prefix length */
|
||||
ARG(1, T_NET);
|
||||
/* Get ROA max prefix length */
|
||||
METHOD(T_NET, maxlen, 0, [[
|
||||
if (!net_is_roa(v1.val.net))
|
||||
runtime( "ROA expected" );
|
||||
|
||||
RESULT(T_INT, i, (v1.val.net->type == NET_ROA4) ?
|
||||
((net_addr_roa4 *) v1.val.net)->max_pxlen :
|
||||
((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:
|
||||
/* Get ROA ASN */
|
||||
METHOD(T_NET, asn, 0, [[
|
||||
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 ... */
|
||||
ARG(1, T_NET);
|
||||
RESULT(T_IP, ip, net_prefix(v1.val.net));
|
||||
}
|
||||
/* Convert prefix to IP */
|
||||
METHOD_R(T_NET, ip, 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));
|
||||
}
|
||||
|
||||
INST(FI_AS_PATH_FIRST, 1, 1) { /* Get first ASN from AS PATH */
|
||||
ARG(1, T_PATH);
|
||||
u32 as = 0;
|
||||
as_path_get_first(v1.val.ad, &as);
|
||||
RESULT(T_INT, i, as);
|
||||
}
|
||||
/* Get first ASN from AS PATH */
|
||||
METHOD_R(T_PATH, first, T_INT, i, ({ u32 as = 0; as_path_get_first(v1.val.ad, &as); as; }));
|
||||
|
||||
INST(FI_AS_PATH_LAST, 1, 1) { /* Get last ASN from AS PATH */
|
||||
ARG(1, T_PATH);
|
||||
u32 as = 0;
|
||||
as_path_get_last(v1.val.ad, &as);
|
||||
RESULT(T_INT, i, as);
|
||||
}
|
||||
/* Get last ASN from AS PATH */
|
||||
METHOD_R(T_PATH, last, T_INT, i, ({ u32 as = 0; as_path_get_last(v1.val.ad, &as); as; }));
|
||||
|
||||
INST(FI_AS_PATH_LAST_NAG, 1, 1) { /* Get last ASN from non-aggregated part of AS PATH */
|
||||
ARG(1, T_PATH);
|
||||
RESULT(T_INT, i, as_path_get_last_nonaggregated(v1.val.ad));
|
||||
}
|
||||
/* Get last ASN from non-aggregated part of AS PATH */
|
||||
METHOD_R(T_PATH, last_nonaggregated, 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);
|
||||
RESULT(T_INT, i, v1.val.i & 0xFFFF);
|
||||
}
|
||||
/* Get ASN part from the standard community ASN */
|
||||
METHOD_R(T_PAIR, asn, T_INT, i, v1.val.i >> 16);
|
||||
|
||||
INST(FI_LC_DATA1, 1, 1) { /* Get data1 part from the large community */
|
||||
ARG(1, T_LC);
|
||||
RESULT(T_INT, i, v1.val.lc.ldp1);
|
||||
}
|
||||
/* Get data part from the standard community */
|
||||
METHOD_R(T_PAIR, data, T_INT, i, v1.val.i & 0xFFFF);
|
||||
|
||||
INST(FI_LC_DATA2, 1, 1) { /* Get data2 part from the large community */
|
||||
ARG(1, T_LC);
|
||||
RESULT(T_INT, i, v1.val.lc.ldp2);
|
||||
}
|
||||
/* Get ASN part from the large community */
|
||||
METHOD_R(T_LC, asn, T_INT, i, v1.val.lc.asn);
|
||||
|
||||
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:
|
||||
{
|
||||
u32 val = 0;
|
||||
int_set_min(v1.val.ad, &val);
|
||||
RESULT_(T_PAIR, i, val);
|
||||
}
|
||||
break;
|
||||
/* Get data1 part from the large community */
|
||||
METHOD_R(T_LC, data1, T_INT, i, v1.val.lc.ldp1);
|
||||
|
||||
case T_ECLIST:
|
||||
{
|
||||
u64 val = 0;
|
||||
ec_set_min(v1.val.ad, &val);
|
||||
RESULT_(T_EC, ec, val);
|
||||
}
|
||||
break;
|
||||
/* Get data2 part from the large community */
|
||||
METHOD_R(T_LC, data2, T_INT, i, v1.val.lc.ldp2);
|
||||
|
||||
case T_LCLIST:
|
||||
{
|
||||
lcomm val = { 0, 0, 0 };
|
||||
lc_set_min(v1.val.ad, &val);
|
||||
RESULT_(T_LC, lc, val);
|
||||
}
|
||||
break;
|
||||
/* Get minimum element from clist */
|
||||
METHOD_R(T_CLIST, min, T_PAIR, i, ({ u32 val = 0; int_set_min(v1.val.ad, &val); val; }));
|
||||
|
||||
default:
|
||||
runtime( "Clist or lclist expected" );
|
||||
}
|
||||
}
|
||||
/* Get maximum element from clist */
|
||||
METHOD_R(T_CLIST, max, T_PAIR, i, ({ u32 val = 0; int_set_max(v1.val.ad, &val); 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:
|
||||
{
|
||||
u32 val = 0;
|
||||
int_set_max(v1.val.ad, &val);
|
||||
RESULT_(T_PAIR, i, val);
|
||||
}
|
||||
break;
|
||||
/* Get minimum element from eclist */
|
||||
METHOD_R(T_ECLIST, min, T_EC, ec, ({ u64 val = 0; ec_set_min(v1.val.ad, &val); val; }));
|
||||
|
||||
case T_ECLIST:
|
||||
{
|
||||
u64 val = 0;
|
||||
ec_set_max(v1.val.ad, &val);
|
||||
RESULT_(T_EC, ec, val);
|
||||
}
|
||||
break;
|
||||
/* Get maximum element from eclist */
|
||||
METHOD_R(T_ECLIST, max, T_EC, ec, ({ u64 val = 0; ec_set_max(v1.val.ad, &val); val; }));
|
||||
|
||||
case T_LCLIST:
|
||||
{
|
||||
lcomm val = { 0, 0, 0 };
|
||||
lc_set_max(v1.val.ad, &val);
|
||||
RESULT_(T_LC, lc, val);
|
||||
}
|
||||
break;
|
||||
/* Get minimum element from lclist */
|
||||
METHOD_R(T_LCLIST, min, T_LC, lc, ({ lcomm val = {}; lc_set_min(v1.val.ad, &val); val; }));
|
||||
|
||||
default:
|
||||
runtime( "Clist or lclist expected" );
|
||||
}
|
||||
}
|
||||
/* Get maximum element from lclist */
|
||||
METHOD_R(T_LCLIST, max, T_LC, lc, ({ lcomm val = {}; lc_set_max(v1.val.ad, &val); val; }));
|
||||
|
||||
INST(FI_RETURN, 1, 0) {
|
||||
NEVER_CONSTANT;
|
||||
@ -1202,9 +1123,7 @@
|
||||
NEVER_CONSTANT;
|
||||
VARARG;
|
||||
SYMBOL;
|
||||
|
||||
/* Fake result type declaration */
|
||||
RESULT_TYPE(T_VOID);
|
||||
RESULT_TYPE(sym->function->return_type);
|
||||
|
||||
FID_NEW_BODY()
|
||||
ASSERT(sym->class == SYM_FUNCTION);
|
||||
@ -1302,7 +1221,6 @@
|
||||
FID_HIC(,break,return NULL);
|
||||
}
|
||||
}
|
||||
/* It is actually possible to have t->data NULL */
|
||||
|
||||
LINEX(t->data);
|
||||
}
|
||||
@ -1310,6 +1228,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))) ]]);
|
||||
@ -1318,165 +1237,252 @@
|
||||
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_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 */
|
||||
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) ]]);
|
||||
/* 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)) ]]);
|
||||
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) ]]);
|
||||
else
|
||||
runtime("Can't add non-pair");
|
||||
/* Community list add */
|
||||
INST(FI_CLIST_ADD_PAIR, 2, 1) {
|
||||
ARG(1, T_CLIST);
|
||||
ARG(2, T_PAIR);
|
||||
METHOD_CONSTRUCTOR("add");
|
||||
RESULT(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, v2.val.i) ]]);
|
||||
}
|
||||
|
||||
else if (v1.type == T_ECLIST)
|
||||
{
|
||||
/* 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) ]]);
|
||||
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) ]]);
|
||||
INST(FI_CLIST_ADD_IP, 2, 1) {
|
||||
ARG(1, T_CLIST);
|
||||
ARG(2, T_IP);
|
||||
METHOD_CONSTRUCTOR("add");
|
||||
|
||||
FID_NEW_BODY();
|
||||
/* IP->Quad implicit conversion, must be before FI_CLIST_ADD_QUAD */
|
||||
cf_warn("Method add(clist, ip) is deprecated, please use add(clist, quad)");
|
||||
|
||||
FID_INTERPRET_BODY();
|
||||
if (!val_is_ip4(&v2)) runtime("Mismatched IP type");
|
||||
RESULT(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]);
|
||||
}
|
||||
|
||||
else if (v1.type == T_LCLIST)
|
||||
{
|
||||
/* 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) ]]);
|
||||
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) ]]);
|
||||
|
||||
INST(FI_CLIST_ADD_QUAD, 2, 1) {
|
||||
ARG(1, T_CLIST);
|
||||
ARG(2, T_QUAD);
|
||||
METHOD_CONSTRUCTOR("add");
|
||||
RESULT(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, v2.val.i) ]]);
|
||||
}
|
||||
|
||||
else
|
||||
runtime("Can't add to non-[e|l]clist");
|
||||
INST(FI_CLIST_ADD_CLIST, 2, 1) {
|
||||
ARG(1, T_CLIST);
|
||||
ARG(2, T_CLIST);
|
||||
METHOD_CONSTRUCTOR("add");
|
||||
RESULT(T_CLIST, ad, [[ int_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
|
||||
}
|
||||
|
||||
INST(FI_CLIST_DEL, 2, 1) { /* (Extended) Community list add or delete */
|
||||
ARG_ANY(1);
|
||||
ARG_ANY(2);
|
||||
RESULT_TYPE(f1->type);
|
||||
|
||||
if (v1.type == T_PATH)
|
||||
{
|
||||
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) ]]);
|
||||
else
|
||||
runtime("Can't delete non-integer (set)");
|
||||
INST(FI_ECLIST_ADD_EC, 2, 1) {
|
||||
ARG(1, T_ECLIST);
|
||||
ARG(2, T_EC);
|
||||
METHOD_CONSTRUCTOR("add");
|
||||
RESULT(T_ECLIST, ad, [[ ec_set_add(fpool, v1.val.ad, v2.val.ec) ]]);
|
||||
}
|
||||
|
||||
else if (v1.type == T_CLIST)
|
||||
{
|
||||
/* 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) ]]);
|
||||
/* 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)) ]]);
|
||||
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) ]]);
|
||||
else
|
||||
runtime("Can't delete non-pair");
|
||||
INST(FI_ECLIST_ADD_ECLIST, 2, 1) {
|
||||
ARG(1, T_ECLIST);
|
||||
ARG(2, T_ECLIST);
|
||||
METHOD_CONSTRUCTOR("add");
|
||||
RESULT(T_ECLIST, ad, [[ ec_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
|
||||
}
|
||||
|
||||
else if (v1.type == T_ECLIST)
|
||||
{
|
||||
/* 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) ]]);
|
||||
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) ]]);
|
||||
INST(FI_LCLIST_ADD_LC, 2, 1) {
|
||||
ARG(1, T_LCLIST);
|
||||
ARG(2, T_LC);
|
||||
METHOD_CONSTRUCTOR("add");
|
||||
RESULT(T_LCLIST, ad, [[ lc_set_add(fpool, v1.val.ad, v2.val.lc) ]]);
|
||||
}
|
||||
|
||||
else if (v1.type == T_LCLIST)
|
||||
{
|
||||
/* 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) ]]);
|
||||
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) ]]);
|
||||
INST(FI_LCLIST_ADD_LCLIST, 2, 1) {
|
||||
ARG(1, T_LCLIST);
|
||||
ARG(2, T_LCLIST);
|
||||
METHOD_CONSTRUCTOR("add");
|
||||
RESULT(T_LCLIST, ad, [[ lc_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
|
||||
}
|
||||
|
||||
else
|
||||
runtime("Can't delete in non-[e|l]clist");
|
||||
INST(FI_PATH_DELETE_INT, 2, 1) {
|
||||
ARG(1, T_PATH);
|
||||
ARG(2, T_INT);
|
||||
METHOD_CONSTRUCTOR("delete");
|
||||
RESULT(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 0) ]]);
|
||||
}
|
||||
|
||||
INST(FI_CLIST_FILTER, 2, 1) { /* (Extended) Community list add or delete */
|
||||
ARG_ANY(1);
|
||||
ARG_ANY(2);
|
||||
RESULT_TYPE(f1->type);
|
||||
INST(FI_PATH_DELETE_SET, 2, 1) {
|
||||
ARG(1, T_PATH);
|
||||
ARG(2, T_SET);
|
||||
METHOD_CONSTRUCTOR("delete");
|
||||
|
||||
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) ]]);
|
||||
else
|
||||
runtime("Can't filter integer");
|
||||
if (!path_set_type(v2.val.t))
|
||||
runtime("Mismatched set type");
|
||||
|
||||
RESULT(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 0) ]]);
|
||||
}
|
||||
|
||||
else if (v1.type == T_CLIST)
|
||||
{
|
||||
/* 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) ]]);
|
||||
else
|
||||
runtime("Can't filter pair");
|
||||
/* Community list delete */
|
||||
INST(FI_CLIST_DELETE_PAIR, 2, 1) {
|
||||
ARG(1, T_CLIST);
|
||||
ARG(2, T_PAIR);
|
||||
METHOD_CONSTRUCTOR("delete");
|
||||
RESULT(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, v2.val.i) ]]);
|
||||
}
|
||||
|
||||
else if (v1.type == T_ECLIST)
|
||||
{
|
||||
/* 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) ]]);
|
||||
else
|
||||
runtime("Can't filter ec");
|
||||
INST(FI_CLIST_DELETE_IP, 2, 1) {
|
||||
ARG(1, T_CLIST);
|
||||
ARG(2, T_IP);
|
||||
METHOD_CONSTRUCTOR("delete");
|
||||
|
||||
FID_NEW_BODY();
|
||||
/* IP->Quad implicit conversion, must be before FI_CLIST_DELETE_QUAD */
|
||||
cf_warn("Method delete(clist, ip) is deprecated, please use delete(clist, quad)");
|
||||
|
||||
FID_INTERPRET_BODY();
|
||||
if (!val_is_ip4(&v2)) runtime("Mismatched IP type");
|
||||
RESULT(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]);
|
||||
}
|
||||
|
||||
else if (v1.type == T_LCLIST)
|
||||
{
|
||||
/* 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) ]]);
|
||||
else
|
||||
runtime("Can't filter lc");
|
||||
INST(FI_CLIST_DELETE_QUAD, 2, 1) {
|
||||
ARG(1, T_CLIST);
|
||||
ARG(2, T_QUAD);
|
||||
METHOD_CONSTRUCTOR("delete");
|
||||
RESULT(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, v2.val.i) ]]);
|
||||
}
|
||||
|
||||
else
|
||||
runtime("Can't filter non-[e|l]clist");
|
||||
INST(FI_CLIST_DELETE_CLIST, 2, 1) {
|
||||
ARG(1, T_CLIST);
|
||||
ARG(2, T_CLIST);
|
||||
METHOD_CONSTRUCTOR("delete");
|
||||
RESULT(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 0) ]]);
|
||||
}
|
||||
|
||||
INST(FI_CLIST_DELETE_SET, 2, 1) {
|
||||
ARG(1, T_CLIST);
|
||||
ARG(2, T_SET);
|
||||
METHOD_CONSTRUCTOR("delete");
|
||||
|
||||
if (!clist_set_type(v2.val.t, &(struct f_val){}))
|
||||
runtime("Mismatched set type");
|
||||
|
||||
RESULT(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 0) ]]);
|
||||
}
|
||||
|
||||
INST(FI_ECLIST_DELETE_EC, 2, 1) {
|
||||
ARG(1, T_ECLIST);
|
||||
ARG(2, T_EC);
|
||||
METHOD_CONSTRUCTOR("delete");
|
||||
RESULT(T_ECLIST, ad, [[ ec_set_del(fpool, v1.val.ad, v2.val.ec) ]]);
|
||||
}
|
||||
|
||||
INST(FI_ECLIST_DELETE_ECLIST, 2, 1) {
|
||||
ARG(1, T_ECLIST);
|
||||
ARG(2, T_ECLIST);
|
||||
METHOD_CONSTRUCTOR("delete");
|
||||
RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
|
||||
}
|
||||
|
||||
INST(FI_ECLIST_DELETE_SET, 2, 1) {
|
||||
ARG(1, T_ECLIST);
|
||||
ARG(2, T_SET);
|
||||
METHOD_CONSTRUCTOR("delete");
|
||||
|
||||
if (!eclist_set_type(v2.val.t))
|
||||
runtime("Mismatched set type");
|
||||
|
||||
RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
|
||||
}
|
||||
|
||||
INST(FI_LCLIST_DELETE_LC, 2, 1) {
|
||||
ARG(1, T_LCLIST);
|
||||
ARG(2, T_LC);
|
||||
METHOD_CONSTRUCTOR("delete");
|
||||
RESULT(T_LCLIST, ad, [[ lc_set_del(fpool, v1.val.ad, v2.val.lc) ]]);
|
||||
}
|
||||
|
||||
INST(FI_LCLIST_DELETE_LCLIST, 2, 1) {
|
||||
ARG(1, T_LCLIST);
|
||||
ARG(2, T_LCLIST);
|
||||
METHOD_CONSTRUCTOR("delete");
|
||||
RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
|
||||
}
|
||||
|
||||
INST(FI_LCLIST_DELETE_SET, 2, 1) {
|
||||
ARG(1, T_LCLIST);
|
||||
ARG(2, T_SET);
|
||||
METHOD_CONSTRUCTOR("delete");
|
||||
|
||||
if (!lclist_set_type(v2.val.t))
|
||||
runtime("Mismatched set type");
|
||||
|
||||
RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
|
||||
}
|
||||
|
||||
INST(FI_PATH_FILTER_SET, 2, 1) {
|
||||
ARG(1, T_PATH);
|
||||
ARG(2, T_SET);
|
||||
METHOD_CONSTRUCTOR("filter");
|
||||
|
||||
if (!path_set_type(v2.val.t))
|
||||
runtime("Mismatched set type");
|
||||
|
||||
RESULT(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 1) ]]);
|
||||
}
|
||||
|
||||
INST(FI_CLIST_FILTER_CLIST, 2, 1) {
|
||||
ARG(1, T_CLIST);
|
||||
ARG(2, T_CLIST);
|
||||
METHOD_CONSTRUCTOR("filter");
|
||||
RESULT(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 1) ]]);
|
||||
}
|
||||
|
||||
INST(FI_CLIST_FILTER_SET, 2, 1) {
|
||||
ARG(1, T_CLIST);
|
||||
ARG(2, T_SET);
|
||||
METHOD_CONSTRUCTOR("filter");
|
||||
|
||||
if (!clist_set_type(v2.val.t, &(struct f_val){}))
|
||||
runtime("Mismatched set type");
|
||||
|
||||
RESULT(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 1) ]]);
|
||||
}
|
||||
|
||||
INST(FI_ECLIST_FILTER_ECLIST, 2, 1) {
|
||||
ARG(1, T_ECLIST);
|
||||
ARG(2, T_ECLIST);
|
||||
METHOD_CONSTRUCTOR("filter");
|
||||
RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
|
||||
}
|
||||
|
||||
INST(FI_ECLIST_FILTER_SET, 2, 1) {
|
||||
ARG(1, T_ECLIST);
|
||||
ARG(2, T_SET);
|
||||
METHOD_CONSTRUCTOR("filter");
|
||||
|
||||
if (!eclist_set_type(v2.val.t))
|
||||
runtime("Mismatched set type");
|
||||
|
||||
RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
|
||||
}
|
||||
|
||||
INST(FI_LCLIST_FILTER_LCLIST, 2, 1) {
|
||||
ARG(1, T_LCLIST);
|
||||
ARG(2, T_LCLIST);
|
||||
METHOD_CONSTRUCTOR("filter");
|
||||
RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
|
||||
}
|
||||
|
||||
INST(FI_LCLIST_FILTER_SET, 2, 1) {
|
||||
ARG(1, T_LCLIST);
|
||||
ARG(2, T_SET);
|
||||
METHOD_CONSTRUCTOR("filter");
|
||||
|
||||
if (!lclist_set_type(v2.val.t))
|
||||
runtime("Mismatched set type");
|
||||
|
||||
RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
|
||||
}
|
||||
|
||||
INST(FI_ROA_CHECK, 2, 1) { /* ROA Check */
|
||||
@ -1501,6 +1507,21 @@
|
||||
|
||||
}
|
||||
|
||||
INST(FI_FROM_HEX, 1, 1) { /* Convert hex text to bytestring */
|
||||
ARG(1, T_STRING);
|
||||
|
||||
int len = bstrhextobin(v1.val.s, NULL);
|
||||
if (len < 0)
|
||||
runtime("Invalid hex string");
|
||||
|
||||
struct adata *bs;
|
||||
bs = falloc(sizeof(struct adata) + len);
|
||||
bs->length = bstrhextobin(v1.val.s, bs->data);
|
||||
ASSERT(bs->length == (size_t) len);
|
||||
|
||||
RESULT(T_BYTESTRING, bs, bs);
|
||||
}
|
||||
|
||||
INST(FI_FORMAT, 1, 1) { /* Format */
|
||||
ARG_ANY(1);
|
||||
RESULT(T_STRING, s, val_format_str(fpool, &v1));
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "filter/data.h"
|
||||
#include "lib/buffer.h"
|
||||
#include "lib/flowspec.h"
|
||||
#include "lib/string.h"
|
||||
|
||||
/* Flags for instructions */
|
||||
enum f_instruction_flags {
|
||||
@ -35,6 +36,16 @@ const char *f_instruction_name_(enum f_instruction_code fi);
|
||||
static inline const char *f_instruction_name(enum f_instruction_code fi)
|
||||
{ return f_instruction_name_(fi) + 3; }
|
||||
|
||||
|
||||
int f_const_promotion_(struct f_inst *arg, enum btype want, int update);
|
||||
|
||||
static inline int f_const_promotion(struct f_inst *arg, enum btype want)
|
||||
{ return f_const_promotion_(arg, want, 1); }
|
||||
|
||||
static inline int f_try_const_promotion(struct f_inst *arg, enum btype want)
|
||||
{ return f_const_promotion_(arg, want, 0); }
|
||||
|
||||
|
||||
struct f_arg {
|
||||
struct symbol *arg;
|
||||
struct f_arg *next;
|
||||
@ -47,6 +58,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 */
|
||||
};
|
||||
@ -94,16 +106,16 @@ void f_add_lines(const struct f_line_item *what, struct filter_iterator *fit);
|
||||
|
||||
|
||||
struct filter *f_new_where(struct f_inst *);
|
||||
struct f_inst *f_dispatch_method(struct symbol *sym, struct f_inst *obj, struct f_inst *args, int skip);
|
||||
struct f_inst *f_dispatch_method_x(const char *name, enum btype t, struct f_inst *obj, struct f_inst *args);
|
||||
struct f_inst *f_for_cycle(struct symbol *var, struct f_inst *term, struct f_inst *block);
|
||||
struct f_inst *f_implicit_roa_check(struct rtable_config *tab);
|
||||
struct f_inst *f_print(struct f_inst *vars, int flush, enum filter_return fret);
|
||||
|
||||
static inline struct f_static_attr f_new_static_attr(btype type, int code, int readonly)
|
||||
{ return (struct f_static_attr) { .type = type, .sa_code = code, .readonly = readonly }; }
|
||||
struct f_inst *f_generate_roa_check(struct rtable_config *table, struct f_inst *prefix, struct f_inst *asn);
|
||||
|
||||
struct f_attr_bit {
|
||||
const struct ea_class *class;
|
||||
uint bit;
|
||||
};
|
||||
|
||||
#define f_new_dynamic_attr_bit(_bit, _name) ((struct f_attr_bit) { .bit = _bit, .class = ea_class_find(_name) })
|
||||
|
||||
/* Hook for call bt_assert() function in configuration */
|
||||
extern void (*bt_assert_hook)(int result, const struct f_line_item *assert);
|
||||
|
197
filter/f-util.c
197
filter/f-util.c
@ -13,7 +13,7 @@
|
||||
#include "filter/f-inst.h"
|
||||
#include "lib/idm.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
|
||||
#define P(a,b) ((a<<8) | b)
|
||||
|
||||
@ -30,7 +30,8 @@ filter_name(const struct filter *filter)
|
||||
return filter->sym->name;
|
||||
}
|
||||
|
||||
struct filter *f_new_where(struct f_inst *where)
|
||||
struct filter *
|
||||
f_new_where(struct f_inst *where)
|
||||
{
|
||||
struct f_inst *cond = f_new_inst(FI_CONDITION, where,
|
||||
f_new_inst(FI_DIE, F_ACCEPT),
|
||||
@ -40,3 +41,195 @@ struct filter *f_new_where(struct f_inst *where)
|
||||
f->root = f_linearize(cond, 0);
|
||||
return f;
|
||||
}
|
||||
|
||||
static inline int
|
||||
f_match_signature(const struct f_method *dsc, struct f_inst *args)
|
||||
{
|
||||
int i, arg_num = (int) dsc->arg_num;
|
||||
|
||||
for (i = 1; args && (i < arg_num); args = args->next, i++)
|
||||
if (dsc->args_type[i] && (args->type != dsc->args_type[i]) &&
|
||||
!f_try_const_promotion(args, dsc->args_type[i]))
|
||||
return 0;
|
||||
|
||||
return !args && !(i < arg_num);
|
||||
}
|
||||
|
||||
/* Variant of f_match_signature(), optimized for error reporting */
|
||||
static inline void
|
||||
f_match_signature_err(const struct f_method *dsc, struct f_inst *args, int *pos, int *want, int *got)
|
||||
{
|
||||
int i, arg_num = (int) dsc->arg_num;
|
||||
|
||||
for (i = 1; args && (i < arg_num); args = args->next, i++)
|
||||
if (dsc->args_type[i] && (args->type != dsc->args_type[i]) &&
|
||||
!f_try_const_promotion(args, dsc->args_type[i]))
|
||||
break;
|
||||
|
||||
*pos = i;
|
||||
*want = (i < arg_num) ? dsc->args_type[i] : T_NONE;
|
||||
*got = args ? args->type : T_NONE;
|
||||
}
|
||||
|
||||
struct f_inst *
|
||||
f_dispatch_method(struct symbol *sym, struct f_inst *obj, struct f_inst *args, int skip)
|
||||
{
|
||||
/* Find match */
|
||||
for (const struct f_method *dsc = sym->method; dsc; dsc = dsc->next)
|
||||
if (f_match_signature(dsc, args))
|
||||
return dsc->new_inst(obj, args);
|
||||
|
||||
|
||||
/* No valid match - format error message */
|
||||
|
||||
int best_pos = -1; /* Longest argument position with partial match */
|
||||
int best_got = 0; /* Received type at best partial match position */
|
||||
int best_count = 0; /* Number of partial matches at best position */
|
||||
const int best_max = 8; /* Max number of reported types */
|
||||
int best_want[best_max]; /* Expected types at best position */
|
||||
|
||||
for (const struct f_method *dsc = sym->method; dsc; dsc = dsc->next)
|
||||
{
|
||||
int pos, want, got;
|
||||
f_match_signature_err(dsc, args, &pos, &want, &got);
|
||||
|
||||
/* Ignore shorter match */
|
||||
if (pos < best_pos)
|
||||
continue;
|
||||
|
||||
/* Found longer match, reset existing results */
|
||||
if (pos > best_pos)
|
||||
{
|
||||
best_pos = pos;
|
||||
best_got = got;
|
||||
best_count = 0;
|
||||
}
|
||||
|
||||
/* Skip duplicates */
|
||||
for (int i = 0; i < best_count; i++)
|
||||
if (best_want[i] == want)
|
||||
goto next;
|
||||
|
||||
/* Skip if we have enough types */
|
||||
if (best_count >= best_max)
|
||||
continue;
|
||||
|
||||
/* Add new expected type */
|
||||
best_want[best_count] = want;
|
||||
best_count++;
|
||||
next:;
|
||||
}
|
||||
|
||||
/* There is at least one method */
|
||||
ASSERT(best_pos >= 0 && best_count > 0);
|
||||
|
||||
/* Update best_pos for printing */
|
||||
best_pos = best_pos - skip + 1;
|
||||
|
||||
if (!best_got)
|
||||
cf_error("Cannot infer type of argument %d of '%s', please assign it to a variable", best_pos, sym->name);
|
||||
|
||||
/* Format list of expected types */
|
||||
buffer tbuf;
|
||||
STACK_BUFFER_INIT(tbuf, 128);
|
||||
for (int i = 0; i < best_count; i++)
|
||||
buffer_print(&tbuf, " / %s", best_want[i] ? f_type_name(best_want[i]) : "any");
|
||||
char *types = tbuf.start + 3;
|
||||
char *dots = (best_count >= best_max) || (tbuf.pos == tbuf.end) ? " / ..." : "";
|
||||
|
||||
cf_error("Argument %d of '%s' expected %s%s, got %s",
|
||||
best_pos, sym->name, types, dots, f_type_name(best_got));
|
||||
}
|
||||
|
||||
struct f_inst *
|
||||
f_dispatch_method_x(const char *name, enum btype t, struct f_inst *obj, struct f_inst *args)
|
||||
{
|
||||
struct sym_scope *scope = f_type_method_scope(t);
|
||||
struct symbol *sym = cf_find_symbol_scope(scope, name);
|
||||
|
||||
if (!sym)
|
||||
cf_error("Cannot dispatch method '%s'", name);
|
||||
|
||||
return f_dispatch_method(sym, obj, args, 0);
|
||||
}
|
||||
|
||||
|
||||
struct f_inst *
|
||||
f_for_cycle(struct symbol *var, struct f_inst *term, struct f_inst *block)
|
||||
{
|
||||
ASSERT((var->class & ~0xff) == SYM_VARIABLE);
|
||||
ASSERT(term->next == NULL);
|
||||
|
||||
/* Static type check */
|
||||
if (term->type == T_VOID)
|
||||
cf_error("Cannot infer type of FOR expression, please assign it to a variable");
|
||||
|
||||
enum btype el_type = f_type_element_type(term->type);
|
||||
struct sym_scope *scope = el_type ? f_type_method_scope(term->type) : NULL;
|
||||
struct symbol *ms = scope ? cf_find_symbol_scope(scope, "!for_next") : NULL;
|
||||
|
||||
if (!ms)
|
||||
cf_error("Type %s is not iterable, can't be used in FOR", f_type_name(term->type));
|
||||
|
||||
if (var->class != (SYM_VARIABLE | el_type))
|
||||
cf_error("Loop variable '%s' in FOR must be of type %s, got %s",
|
||||
var->name, f_type_name(el_type), f_type_name(var->class & 0xff));
|
||||
|
||||
/* Push the iterator auxiliary value onto stack */
|
||||
struct f_inst *iter = term->next = f_new_inst(FI_CONSTANT, (struct f_val) {});
|
||||
|
||||
/* Initialize the iterator variable */
|
||||
iter->next = f_new_inst(FI_CONSTANT, (struct f_val) { .type = el_type });
|
||||
|
||||
/* Prepend the loop block with loop beginning instruction */
|
||||
struct f_inst *loop_start = f_new_inst(FI_FOR_LOOP_START, var);
|
||||
loop_start->next = block;
|
||||
|
||||
return ms->method->new_inst(term, loop_start);
|
||||
}
|
||||
|
||||
struct f_inst *
|
||||
f_implicit_roa_check(struct rtable_config *tab)
|
||||
{
|
||||
const struct ea_class *def = ea_class_find("bgp_path");
|
||||
if (!def)
|
||||
bug("Couldn't find BGP AS Path attribute definition.");
|
||||
|
||||
struct f_inst *path_getter = f_new_inst(FI_EA_GET, f_new_inst(FI_CURRENT_ROUTE), def);
|
||||
struct sym_scope *scope = f_type_method_scope(path_getter->type);
|
||||
struct symbol *ms = scope ? cf_find_symbol_scope(scope, "last") : NULL;
|
||||
|
||||
if (!ms)
|
||||
bug("Couldn't find the \"last\" method for AS Path.");
|
||||
|
||||
struct f_static_attr fsa = f_new_static_attr(T_NET, SA_NET, 1);
|
||||
|
||||
return f_new_inst(FI_ROA_CHECK,
|
||||
f_new_inst(FI_RTA_GET, f_new_inst(FI_CURRENT_ROUTE), fsa),
|
||||
ms->method->new_inst(path_getter, NULL),
|
||||
tab);
|
||||
}
|
||||
|
||||
struct f_inst *
|
||||
f_print(struct f_inst *vars, int flush, enum filter_return fret)
|
||||
{
|
||||
#define AX(...) do { struct f_inst *_tmp = f_new_inst(__VA_ARGS__); _tmp->next = output; output = _tmp; } while (0)
|
||||
struct f_inst *output = NULL;
|
||||
if (fret != F_NOP)
|
||||
AX(FI_DIE, fret);
|
||||
|
||||
if (flush)
|
||||
AX(FI_FLUSH);
|
||||
|
||||
while (vars)
|
||||
{
|
||||
struct f_inst *tmp = vars;
|
||||
vars = vars->next;
|
||||
tmp->next = NULL;
|
||||
|
||||
AX(FI_PRINT, tmp);
|
||||
}
|
||||
|
||||
return output;
|
||||
#undef AX
|
||||
}
|
||||
|
@ -35,7 +35,7 @@
|
||||
#include "lib/ip.h"
|
||||
#include "lib/net.h"
|
||||
#include "lib/flowspec.h"
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/iface.h"
|
||||
#include "lib/attrs.h"
|
||||
@ -76,6 +76,9 @@ struct filter_state {
|
||||
/* The route we are processing. This may be NULL to indicate no route available. */
|
||||
struct rte *rte;
|
||||
|
||||
/* Additional external values provided to the filter */
|
||||
const struct f_val *val;
|
||||
|
||||
/* Buffer for log output */
|
||||
log_buffer buf;
|
||||
|
||||
@ -109,18 +112,20 @@ static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS;
|
||||
* TWOARGS macro to get both of them evaluated.
|
||||
*/
|
||||
static enum filter_return
|
||||
interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val)
|
||||
interpret(struct filter_state *fs, const struct f_line *line, uint argc, const struct f_val *argv, uint resc, struct f_val *resv)
|
||||
{
|
||||
/* No arguments allowed */
|
||||
ASSERT(line->args == 0);
|
||||
/* Check of appropriate number of arguments */
|
||||
ASSERT(line->args == argc);
|
||||
|
||||
/* Initialize the filter stack */
|
||||
struct filter_stack *fstk = &fs->stack;
|
||||
|
||||
fstk->vcnt = line->vars;
|
||||
memset(fstk->vstk, 0, sizeof(struct f_val) * line->vars);
|
||||
/* Set the arguments and top-level variables */
|
||||
fstk->vcnt = line->vars + line->args;
|
||||
memcpy(fstk->vstk, argv, sizeof(struct f_val) * line->args);
|
||||
memset(fstk->vstk + argc, 0, sizeof(struct f_val) * line->vars);
|
||||
|
||||
/* The same as with the value stack. Not resetting the stack for performance reasons. */
|
||||
/* The same as with the value stack. Not resetting the stack completely for performance reasons. */
|
||||
fstk->ecnt = 1;
|
||||
fstk->estk[0] = (struct filter_exec_stack) {
|
||||
.line = line,
|
||||
@ -128,6 +133,7 @@ interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val)
|
||||
};
|
||||
|
||||
#define curline fstk->estk[fstk->ecnt-1]
|
||||
#define prevline fstk->estk[fstk->ecnt-2]
|
||||
|
||||
#ifdef LOCAL_DEBUG
|
||||
debug("Interpreting line.");
|
||||
@ -172,21 +178,14 @@ interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val)
|
||||
fstk->ecnt--;
|
||||
}
|
||||
|
||||
if (fstk->vcnt == 0) {
|
||||
if (val) {
|
||||
log_rl(&rl_runtime_err, L_ERR "filters: No value left on stack");
|
||||
if (fstk->vcnt != resc)
|
||||
{
|
||||
log_rl(&rl_runtime_err, L_ERR "Filter expected to leave %d values on stack but %d left instead", resc, fstk->vcnt);
|
||||
return F_ERROR;
|
||||
}
|
||||
return F_NOP;
|
||||
}
|
||||
|
||||
if (val && (fstk->vcnt == 1)) {
|
||||
*val = fstk->vstk[0];
|
||||
memcpy(resv, fstk->vstk, sizeof(struct f_val) * resc);
|
||||
return F_NOP;
|
||||
}
|
||||
|
||||
log_rl(&rl_runtime_err, L_ERR "Too many items left on stack: %u", fstk->vcnt);
|
||||
return F_ERROR;
|
||||
}
|
||||
|
||||
|
||||
@ -209,6 +208,12 @@ f_run(const struct filter *filter, struct rte *rte, int flags)
|
||||
if (filter == FILTER_REJECT)
|
||||
return F_REJECT;
|
||||
|
||||
return f_run_args(filter, rte, 0, NULL, flags);
|
||||
}
|
||||
|
||||
enum filter_return
|
||||
f_run_args(const struct filter *filter, struct rte *rte, uint argc, const struct f_val *argv, int flags)
|
||||
{
|
||||
DBG( "Running filter `%s'...", filter->name );
|
||||
|
||||
/* Initialize the filter state */
|
||||
@ -220,7 +225,7 @@ f_run(const struct filter *filter, struct rte *rte, int flags)
|
||||
f_stack_init(filter_state);
|
||||
|
||||
/* Run the interpreter itself */
|
||||
enum filter_return fret = interpret(&filter_state, filter->root, NULL);
|
||||
enum filter_return fret = interpret(&filter_state, filter->root, argc, argv, 0, NULL);
|
||||
|
||||
/* Process the filter output, log it and return */
|
||||
if (fret < F_ACCEPT) {
|
||||
@ -246,7 +251,7 @@ f_run(const struct filter *filter, struct rte *rte, int flags)
|
||||
*/
|
||||
|
||||
enum filter_return
|
||||
f_eval_rte(const struct f_line *expr, struct rte *rte)
|
||||
f_eval_rte(const struct f_line *expr, struct rte *rte, uint argc, const struct f_val *argv, uint resc, struct f_val *resv)
|
||||
{
|
||||
filter_state = (struct filter_state) {
|
||||
.rte = rte,
|
||||
@ -254,16 +259,14 @@ f_eval_rte(const struct f_line *expr, struct rte *rte)
|
||||
|
||||
f_stack_init(filter_state);
|
||||
|
||||
ASSERT(!rta_is_cached(rte->attrs));
|
||||
|
||||
return interpret(&filter_state, expr, NULL);
|
||||
return interpret(&filter_state, expr, argc, argv, resc, resv);
|
||||
}
|
||||
|
||||
/*
|
||||
* f_eval - get a value of a term
|
||||
* @expr: filter line containing the term
|
||||
* @tmp_pool: long data may get allocated from this pool
|
||||
* @pres: here the output will be stored
|
||||
* @pres: here the output will be stored if requested
|
||||
*/
|
||||
enum filter_return
|
||||
f_eval(const struct f_line *expr, struct f_val *pres)
|
||||
@ -272,34 +275,28 @@ f_eval(const struct f_line *expr, struct f_val *pres)
|
||||
|
||||
f_stack_init(filter_state);
|
||||
|
||||
enum filter_return fret = interpret(&filter_state, expr, pres);
|
||||
enum filter_return fret = interpret(&filter_state, expr, 0, NULL, !!pres, pres);
|
||||
return fret;
|
||||
}
|
||||
|
||||
/*
|
||||
* f_eval_int - get an integer value of a term
|
||||
* Called internally from the config parser, uses its internal memory pool
|
||||
* for allocations. Do not call in other cases.
|
||||
* cf_eval_tmp - evaluate a value of a term and check its type
|
||||
*/
|
||||
uint
|
||||
f_eval_int(const struct f_line *expr)
|
||||
struct f_val
|
||||
cf_eval_tmp(const struct f_inst *inst, int type)
|
||||
{
|
||||
/* Called independently in parse-time to eval expressions */
|
||||
filter_state = (struct filter_state) {};
|
||||
|
||||
f_stack_init(filter_state);
|
||||
|
||||
struct f_val val;
|
||||
|
||||
if (interpret(&filter_state, expr, &val) > F_RETURN)
|
||||
if (f_eval(f_linearize(inst, 1), &val) > F_RETURN)
|
||||
cf_error("Runtime error while evaluating expression; see log for details");
|
||||
|
||||
if (val.type != T_INT)
|
||||
cf_error("Integer expression expected");
|
||||
if (type != T_VOID && val.type != type)
|
||||
cf_error("Expression of type %s expected", f_type_name(type));
|
||||
|
||||
return val.val.i;
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* f_eval_buf - get a value of a term and print it to the supplied buffer
|
||||
*/
|
||||
|
@ -13,8 +13,10 @@
|
||||
#include "lib/resource.h"
|
||||
#include "lib/ip.h"
|
||||
#include "lib/macro.h"
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
#include "lib/attrs.h"
|
||||
#include "filter/data.h"
|
||||
#include "conf/conf.h"
|
||||
|
||||
/* Possible return values of filter execution */
|
||||
enum filter_return {
|
||||
@ -40,9 +42,8 @@ static inline const char *filter_return_str(const enum filter_return fret) {
|
||||
}
|
||||
}
|
||||
|
||||
struct f_val;
|
||||
|
||||
/* The filter encapsulating structure to be pointed-to from outside */
|
||||
struct f_inst;
|
||||
struct f_line;
|
||||
struct filter {
|
||||
struct symbol *sym;
|
||||
@ -52,10 +53,19 @@ struct filter {
|
||||
struct rte;
|
||||
|
||||
enum filter_return f_run(const struct filter *filter, struct rte *rte, int flags);
|
||||
enum filter_return f_eval_rte(const struct f_line *expr, struct rte *rte);
|
||||
uint f_eval_int(const struct f_line *expr);
|
||||
enum filter_return f_run_args(const struct filter *filter, struct rte *rte, uint argc, const struct f_val *argv, int flags);
|
||||
enum filter_return f_eval_rte(const struct f_line *expr, struct rte *rte, uint argc, const struct f_val *argv, uint resc, struct f_val *resv);
|
||||
enum filter_return f_eval_buf(const struct f_line *expr, buffer *buf);
|
||||
|
||||
struct f_val cf_eval_tmp(const struct f_inst *inst, int type);
|
||||
static inline struct f_val *cf_eval(const struct f_inst *inst, int type)
|
||||
{
|
||||
struct f_val val = cf_eval_tmp(inst, type);
|
||||
return lp_val_copy(cfg_mem, &val);
|
||||
}
|
||||
|
||||
static inline uint cf_eval_int(const struct f_inst *inst) { return cf_eval_tmp(inst, T_INT).val.i; };
|
||||
|
||||
const char *filter_name(const struct filter *filter);
|
||||
int filter_same(const struct filter *new, const struct filter *old);
|
||||
int f_same(const struct f_line *f1, const struct f_line *f2);
|
||||
|
@ -25,9 +25,9 @@
|
||||
|
||||
|
||||
static int
|
||||
t_reconfig(void)
|
||||
t_reconfig(const void *arg)
|
||||
{
|
||||
if (!bt_config_file_parse(BT_CONFIG_FILE))
|
||||
if (!bt_config_file_parse(arg))
|
||||
return 0;
|
||||
|
||||
struct symbol *s;
|
||||
@ -79,7 +79,9 @@ main(int argc, char *argv[])
|
||||
if (!bt_config_file_parse(BT_CONFIG_FILE))
|
||||
abort();
|
||||
|
||||
bt_test_suite_extra(t_reconfig, 0, BT_TIMEOUT, "Testing reconfiguration");
|
||||
bt_test_suite_arg_extra(t_reconfig, BT_CONFIG_FILE ".overlay", 0, BT_TIMEOUT, "Testing reconfiguration to overlay");
|
||||
bt_test_suite_arg_extra(t_reconfig, BT_CONFIG_FILE, 0, BT_TIMEOUT, "Testing reconfiguration back");
|
||||
bt_test_suite_arg_extra(t_reconfig, BT_CONFIG_FILE, 0, BT_TIMEOUT, "Testing reconfiguration to the same file");
|
||||
|
||||
struct f_bt_test_suite *t;
|
||||
WALK_LIST(t, config->tests)
|
||||
|
521
filter/test.conf
521
filter/test.conf
@ -9,6 +9,9 @@ router id 62.168.0.1;
|
||||
/* We have to setup any protocol */
|
||||
protocol device { }
|
||||
|
||||
attribute bgppath mypath;
|
||||
attribute lclist mylclist;
|
||||
|
||||
/* Setting some custom attributes, enough to force BIRD to reallocate the attribute idmap */
|
||||
attribute int test_ca_int1;
|
||||
attribute int test_ca_int2;
|
||||
@ -121,17 +124,17 @@ attribute lclist test_ca_lclist_max21;
|
||||
define one = 1;
|
||||
define ten = 10;
|
||||
|
||||
function onef(int a)
|
||||
function onef(int a) -> int
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
function twof(int a)
|
||||
function twof(int a) -> int
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
function oneg(int a)
|
||||
function oneg(int a) -> int
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@ -140,6 +143,18 @@ bt_test_same(onef, onef, 1);
|
||||
bt_test_same(onef, oneg, 1);
|
||||
bt_test_same(onef, twof, 0);
|
||||
|
||||
/*
|
||||
* Testing filter corner cases
|
||||
* ---------------------------
|
||||
*/
|
||||
|
||||
function t_nothing() {}
|
||||
bt_test_suite(t_nothing, "Testing nothing");
|
||||
|
||||
function t_metanothing() { t_nothing(); }
|
||||
bt_test_suite(t_metanothing, "Testing meta nothing");
|
||||
|
||||
|
||||
/*
|
||||
* Testing boolean expressions
|
||||
* ---------------------------
|
||||
@ -178,6 +193,14 @@ bt_test_suite(t_bool, "Testing boolean expressions");
|
||||
* ----------------
|
||||
*/
|
||||
|
||||
function aux_t_int(int t; int u)
|
||||
{
|
||||
case t {
|
||||
1: {}
|
||||
else: {}
|
||||
}
|
||||
}
|
||||
|
||||
define four = 4;
|
||||
define xyzzy = (120+10);
|
||||
define '1a-a1' = (xyzzy-100);
|
||||
@ -230,7 +253,10 @@ function t_int()
|
||||
else: bt_assert(false);
|
||||
}
|
||||
|
||||
|
||||
aux_t_int(1, 2);
|
||||
aux_t_int(1, 3);
|
||||
aux_t_int(2, 3);
|
||||
aux_t_int(2, 2);
|
||||
}
|
||||
|
||||
bt_test_suite(t_int, "Testing integers");
|
||||
@ -324,12 +350,42 @@ bt_test_suite(t_string, "Testing string matching");
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Testing bytestings
|
||||
* ------------------
|
||||
*/
|
||||
|
||||
function t_bytestring()
|
||||
{
|
||||
bytestring bs1 = hex:;
|
||||
bytestring bs2 = hex:0112233445566778899aabbccddeeff0;
|
||||
|
||||
bt_assert(format(bs1) = "");
|
||||
bt_assert(format(bs2) = "01:12:23:34:45:56:67:78:89:9a:ab:bc:cd:de:ef:f0");
|
||||
bt_assert(hex:01:12:23:34:45:56:67:78:89:9a:ab:bc:cd:de:ef:f0 = bs2);
|
||||
bt_assert(01:12:23:34:45:56:67:78:89:9a:ab:bc:cd:de:ef:f0 = bs2);
|
||||
bt_assert(0112233445566778899aabbccddeeff0 = bs2);
|
||||
bt_assert(hex:01234567 = hex:01:23:45:67);
|
||||
bt_assert(hex:0123456789abcdef != bs2);
|
||||
bt_assert(hex:0123456789abcdef != hex:0123);
|
||||
bt_assert(format(hex:0123456789abcdef) = "01:23:45:67:89:ab:cd:ef");
|
||||
bt_assert(from_hex(" ") = bs1);
|
||||
bt_assert(from_hex("01:12:23:34:45:56:67:78:89:9a:ab:bc:cd:de:ef:f0") = bs2);
|
||||
bt_assert(from_hex(format(bs2)) = bs2);
|
||||
bt_assert(from_hex(" 0112:23-34455667 78-89 - 9a-ab bc:cd : de:eff0 ") = bs2);
|
||||
}
|
||||
|
||||
bt_test_suite(t_bytestring, "Testing bytestrings");
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Testing pairs
|
||||
* -------------
|
||||
*/
|
||||
|
||||
function 'mkpair-a'(int a)
|
||||
function 'mkpair-a'(int a) -> pair
|
||||
{
|
||||
return (1, a);
|
||||
}
|
||||
@ -652,10 +708,10 @@ prefix set pxs;
|
||||
bt_assert(format([ 0.0.0.0/0 ]) = "[0.0.0.0/0]");
|
||||
bt_assert(format([ 10.10.0.0/32 ]) = "[10.10.0.0/32{0.0.0.1}]");
|
||||
bt_assert(format([ 10.10.0.0/17 ]) = "[10.10.0.0/17{0.0.128.0}]");
|
||||
bt_assert(format([ 10.10.0.0/17{17,19} ]) = "[10.10.0.0/17{0.0.224.0}]"); # 224 = 128+64+32
|
||||
# bt_assert(format([ 10.10.0.0/17{17,19} ]) = "[10.10.0.0/17{0.0.224.0}]"); # 224 = 128+64+32
|
||||
bt_assert(format([ 10.10.128.0/17{18,19} ]) = "[10.10.128.0/18{0.0.96.0}, 10.10.192.0/18{0.0.96.0}]"); # 96 = 64+32
|
||||
bt_assert(format([ 10.10.64.0/18- ]) = "[0.0.0.0/0, 0.0.0.0/1{128.0.0.0}, 0.0.0.0/2{64.0.0.0}, 0.0.0.0/3{32.0.0.0}, 10.10.0.0/16{255.255.0.0}, 10.10.0.0/17{0.0.128.0}, 10.10.64.0/18{0.0.64.0}]");
|
||||
bt_assert(format([ 10.10.64.0/18+ ]) = "[10.10.64.0/18{0.0.96.0}, 10.10.64.0/20{0.0.31.255}, 10.10.80.0/20{0.0.31.255}, 10.10.96.0/20{0.0.31.255}, 10.10.112.0/20{0.0.31.255}]");
|
||||
# bt_assert(format([ 10.10.64.0/18- ]) = "[0.0.0.0/0, 0.0.0.0/1{128.0.0.0}, 0.0.0.0/2{64.0.0.0}, 0.0.0.0/3{32.0.0.0}, 10.10.0.0/16{255.255.0.0}, 10.10.0.0/17{0.0.128.0}, 10.10.64.0/18{0.0.64.0}]");
|
||||
# bt_assert(format([ 10.10.64.0/18+ ]) = "[10.10.64.0/18{0.0.96.0}, 10.10.64.0/20{0.0.31.255}, 10.10.80.0/20{0.0.31.255}, 10.10.96.0/20{0.0.31.255}, 10.10.112.0/20{0.0.31.255}]");
|
||||
|
||||
bt_assert(format([ 10.10.160.0/19 ]) = "[10.10.160.0/19{0.0.32.0}]");
|
||||
bt_assert(format([ 10.10.160.0/19{19,22} ]) = "[10.10.160.0/19{0.0.32.0}, 10.10.160.0/20{0.0.28.0}, 10.10.176.0/20{0.0.28.0}]"); # 28 = 16+8+4
|
||||
@ -667,10 +723,10 @@ prefix set pxs;
|
||||
bt_assert(format([ 11:22:33:44::/64+ ]) = "[11:22:33:44::/64{::1:ffff:ffff:ffff:ffff}]");
|
||||
|
||||
bt_assert(format([ 11:22:33:44::/65 ]) = "[11:22:33:44::/65{::8000:0:0:0}]");
|
||||
bt_assert(format([ 11:22:33:44::/65{65,67} ]) = "[11:22:33:44::/65{::e000:0:0:0}]"); # e = 8+4+2
|
||||
# bt_assert(format([ 11:22:33:44::/65{65,67} ]) = "[11:22:33:44::/65{::e000:0:0:0}]"); # e = 8+4+2
|
||||
bt_assert(format([ 11:22:33:44:8000::/65{66,67} ]) = "[11:22:33:44:8000::/66{::6000:0:0:0}, 11:22:33:44:c000::/66{::6000:0:0:0}]"); # 6 = 4+2
|
||||
bt_assert(format([ 11:22:33:44:4000::/66- ]) = "[::/0, ::/1{8000::}, ::/2{4000::}, ::/3{2000::}, 11:22:33:44::/64{ffff:ffff:ffff:ffff::}, 11:22:33:44::/65{::8000:0:0:0}, 11:22:33:44:4000::/66{::4000:0:0:0}]");
|
||||
bt_assert(format([ 11:22:33:44:4000::/66+ ]) = "[11:22:33:44:4000::/66{::6000:0:0:0}, 11:22:33:44:4000::/68{::1fff:ffff:ffff:ffff}, 11:22:33:44:5000::/68{::1fff:ffff:ffff:ffff}, 11:22:33:44:6000::/68{::1fff:ffff:ffff:ffff}, 11:22:33:44:7000::/68{::1fff:ffff:ffff:ffff}]");
|
||||
# bt_assert(format([ 11:22:33:44:4000::/66- ]) = "[::/0, ::/1{8000::}, ::/2{4000::}, ::/3{2000::}, 11:22:33:44::/64{ffff:ffff:ffff:ffff::}, 11:22:33:44::/65{::8000:0:0:0}, 11:22:33:44:4000::/66{::4000:0:0:0}]");
|
||||
# bt_assert(format([ 11:22:33:44:4000::/66+ ]) = "[11:22:33:44:4000::/66{::6000:0:0:0}, 11:22:33:44:4000::/68{::1fff:ffff:ffff:ffff}, 11:22:33:44:5000::/68{::1fff:ffff:ffff:ffff}, 11:22:33:44:6000::/68{::1fff:ffff:ffff:ffff}, 11:22:33:44:7000::/68{::1fff:ffff:ffff:ffff}]");
|
||||
bt_assert(format([ 11:22:33:44:c000::/67 ]) = "[11:22:33:44:c000::/67{::2000:0:0:0}]");
|
||||
bt_assert(format([ 11:22:33:44:c000::/67{67,71} ]) = "[11:22:33:44:c000::/67{::2000:0:0:0}, 11:22:33:44:c000::/68{::1e00:0:0:0}, 11:22:33:44:d000::/68{::1e00:0:0:0}]");
|
||||
bt_assert(format([ 11:22:33:44:c000::/67+ ]) = "[11:22:33:44:c000::/67{::2000:0:0:0}, 11:22:33:44:c000::/68{::1fff:ffff:ffff:ffff}, 11:22:33:44:d000::/68{::1fff:ffff:ffff:ffff}]");
|
||||
@ -687,8 +743,8 @@ bt_test_suite(t_prefix_set, "Testing prefix sets");
|
||||
*/
|
||||
|
||||
function t_prefix6()
|
||||
prefix px;
|
||||
{
|
||||
prefix px;
|
||||
px = 1020::/18;
|
||||
bt_assert(format(px) = "1020::/18");
|
||||
bt_assert(1020:3040:5060:: ~ 1020:3040:5000::/40);
|
||||
@ -804,14 +860,14 @@ bt_test_suite(t_flowspec, "Testing flowspec routes");
|
||||
* -------------
|
||||
*/
|
||||
|
||||
function mkpath(int a; int b)
|
||||
function mkpath(int a; int b) -> bgpmask
|
||||
{
|
||||
return [= a b 3 2 1 =];
|
||||
}
|
||||
|
||||
define set35 = [3 .. 5];
|
||||
|
||||
function t_path()
|
||||
function t_path_old()
|
||||
bgpmask pm1;
|
||||
bgppath p2;
|
||||
int set set12;
|
||||
@ -890,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)");
|
||||
|
||||
|
||||
|
||||
@ -902,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;
|
||||
@ -981,6 +1121,12 @@ clist r;
|
||||
l = filter(l2, [(3,1..4)]);
|
||||
l2 = filter(l2, [(3,3..6)]);
|
||||
|
||||
quad q = 2.0.1.0;
|
||||
clist ql = add(add(add(-empty-, 1.0.0.1), q), 3.1.0.0);
|
||||
bt_assert(delete(ql, 1.0.0.1) = add(add(-empty-, 2.0.1.0), 3.1.0.0));
|
||||
bt_assert(delete(ql, [2.0.0.0 .. 4.0.0.0]) = add(-empty-, 1.0.0.1));
|
||||
bt_assert(filter(ql, [3.0.0.0 .. 4.0.0.0]) = add(-empty-, 3.1.0.0));
|
||||
|
||||
# clist A (10,20,30)
|
||||
bt_assert(l = add(add(add(add(-empty-, (3,1)), (3,2)), (3,3)), (3,4)));
|
||||
bt_assert(format(l) = "(clist (3,1) (3,2) (3,3) (3,4))");
|
||||
@ -1022,7 +1168,135 @@ 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)]);
|
||||
|
||||
quad q = 2.0.1.0;
|
||||
clist ql = -empty-.add(1.0.0.1).add(q).add(3.1.0.0);
|
||||
bt_assert(delete(ql, 1.0.0.1) = -empty-.add(2.0.1.0).add(3.1.0.0));
|
||||
bt_assert(delete(ql, [2.0.0.0 .. 4.0.0.0]) = -empty-.add(1.0.0.1));
|
||||
bt_assert(filter(ql, [3.0.0.0 .. 4.0.0.0]) = -empty-.add(3.1.0.0));
|
||||
|
||||
# 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)");
|
||||
|
||||
|
||||
|
||||
@ -1033,8 +1307,8 @@ bt_test_suite(t_clist, "Testing lists of communities");
|
||||
*/
|
||||
|
||||
function t_ec()
|
||||
ec cc;
|
||||
{
|
||||
ec cc;
|
||||
cc = (rt, 12345, 200000);
|
||||
bt_assert(format(cc) = "(rt, 12345, 200000)");
|
||||
|
||||
@ -1057,11 +1331,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));
|
||||
@ -1144,7 +1419,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");
|
||||
|
||||
|
||||
|
||||
@ -1188,12 +1555,12 @@ bt_test_suite(t_ec_set, "Testing sets of extended communities");
|
||||
* -------------------------
|
||||
*/
|
||||
|
||||
function mktrip(int a)
|
||||
function mktrip(int a) -> lc
|
||||
{
|
||||
return (a, 2*a, 3*a);
|
||||
}
|
||||
|
||||
function t_lclist()
|
||||
function t_lclist_old()
|
||||
lclist ll;
|
||||
lclist ll2;
|
||||
lclist r;
|
||||
@ -1275,7 +1642,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");
|
||||
|
||||
|
||||
|
||||
@ -1418,7 +1870,7 @@ bt_test_suite(t_define, "Testing defined() function");
|
||||
* -------------------------
|
||||
*/
|
||||
|
||||
function callme(int arg1; int arg2)
|
||||
function callme(int arg1; int arg2) -> int
|
||||
int i;
|
||||
{
|
||||
case arg1 {
|
||||
@ -1429,12 +1881,12 @@ int i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function callmeagain(int a; int b; int c)
|
||||
function callmeagain(int a; int b; int c) -> int
|
||||
{
|
||||
return a + b + c;
|
||||
}
|
||||
|
||||
function fifteen()
|
||||
function fifteen() -> int
|
||||
{
|
||||
return 15;
|
||||
}
|
||||
@ -1467,28 +1919,28 @@ function local_vars(int j)
|
||||
bt_assert(j = 35 && k = 20 && m = 100);
|
||||
}
|
||||
|
||||
function factorial(int x)
|
||||
function factorial(int x) -> int
|
||||
{
|
||||
if x = 0 then return 0;
|
||||
if x = 1 then return 1;
|
||||
else return x * factorial(x - 1);
|
||||
}
|
||||
|
||||
function fibonacci(int x)
|
||||
function fibonacci(int x) -> int
|
||||
{
|
||||
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 hanoi_init(int a; int b) -> bgppath
|
||||
{
|
||||
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 hanoi_solve(int n; bgppath h_src; bgppath h_dst; bgppath h_aux; bool x; bool y) -> bgppath
|
||||
{
|
||||
# x -> return src or dst
|
||||
# y -> print state
|
||||
@ -1798,6 +2250,15 @@ filter vpn_filter
|
||||
bgp_ext_community.add((ro, 135, 999));
|
||||
bgp_large_community.add((6464156, 89646354, 8675643));
|
||||
|
||||
mypath.prepend(65533);
|
||||
mylclist.add((1234, 5678, 90123));
|
||||
|
||||
bgppath locpath;
|
||||
lclist loclclist;
|
||||
|
||||
locpath.prepend(65533);
|
||||
loclclist.add((1234, 5678, 90123));
|
||||
|
||||
accept;
|
||||
}
|
||||
|
||||
|
3
filter/test.conf.overlay
Normal file
3
filter/test.conf.overlay
Normal file
@ -0,0 +1,3 @@
|
||||
attribute int peek_a_boo;
|
||||
|
||||
include "test.conf";
|
@ -28,12 +28,6 @@ struct f_prefix_node {
|
||||
struct f_prefix prefix;
|
||||
};
|
||||
|
||||
static u32
|
||||
xrandom(u32 max)
|
||||
{
|
||||
return (bt_random() % max);
|
||||
}
|
||||
|
||||
static inline uint
|
||||
get_exp_random(void)
|
||||
{
|
||||
@ -95,27 +89,10 @@ is_prefix_included(list *prefixes, const net_addr *needle)
|
||||
return 0; /* FAIL */
|
||||
}
|
||||
|
||||
static void
|
||||
get_random_net(net_addr *net, int v6)
|
||||
{
|
||||
if (!v6)
|
||||
{
|
||||
uint pxlen = xrandom(24)+8;
|
||||
ip4_addr ip4 = ip4_from_u32((u32) bt_random());
|
||||
net_fill_ip4(net, ip4_and(ip4, ip4_mkmask(pxlen)), pxlen);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint pxlen = xrandom(120)+8;
|
||||
ip6_addr ip6 = ip6_build(bt_random(), bt_random(), bt_random(), bt_random());
|
||||
net_fill_ip6(net, ip6_and(ip6, ip6_mkmask(pxlen)), pxlen);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
get_random_prefix(struct f_prefix *px, int v6, int tight)
|
||||
{
|
||||
get_random_net(&px->net, v6);
|
||||
bt_random_net(&px->net, !v6 ? NET_IP4 : NET_IP6);
|
||||
|
||||
if (tight)
|
||||
{
|
||||
@ -379,7 +356,7 @@ select_random_prefix_subset(list *src[], net_addr dst[], int sn, int dn)
|
||||
struct f_prefix_node *px;
|
||||
WALK_LIST(px, *src[i])
|
||||
{
|
||||
if (xrandom(rnd) != 0)
|
||||
if (bt_random_n(rnd) != 0)
|
||||
continue;
|
||||
|
||||
net_copy(&dst[n], &px->prefix.net);
|
||||
@ -395,7 +372,7 @@ done:
|
||||
/* Shuffle networks */
|
||||
for (int i = 0; i < dn; i++)
|
||||
{
|
||||
int j = xrandom(dn);
|
||||
int j = bt_random_n(dn);
|
||||
|
||||
if (i == j)
|
||||
continue;
|
||||
@ -441,7 +418,7 @@ t_match_random_net(void)
|
||||
for (int i = 0; i < PREFIX_TESTS_NUM; i++)
|
||||
{
|
||||
net_addr net;
|
||||
get_random_net(&net, v6);
|
||||
bt_random_net(&net, !v6 ? NET_IP4 : NET_IP6);
|
||||
test_match_net(prefixes, trie, &net);
|
||||
}
|
||||
|
||||
@ -795,7 +772,7 @@ t_trie_walk_to_root(void)
|
||||
for (i = 0; i < (PREFIX_TESTS_NUM / 10); i++)
|
||||
{
|
||||
net_addr from;
|
||||
get_random_net(&from, v6);
|
||||
bt_random_net(&from, !v6 ? NET_IP4 : NET_IP6);
|
||||
|
||||
net_addr found[129];
|
||||
int found_num = find_covering_nets(pxset, num, &from, found);
|
||||
|
25
lib/a-path.c
25
lib/a-path.c
@ -8,7 +8,7 @@
|
||||
*/
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
#include "lib/attrs.h"
|
||||
#include "lib/resource.h"
|
||||
#include "lib/unaligned.h"
|
||||
@ -669,6 +669,29 @@ as_path_filter(struct linpool *pool, const struct adata *path, const struct f_va
|
||||
return res;
|
||||
}
|
||||
|
||||
int
|
||||
as_path_compare(const struct adata *path1, const struct adata *path2)
|
||||
{
|
||||
uint pos1 = 0;
|
||||
uint pos2 = 0;
|
||||
uint val1 = 0;
|
||||
uint val2 = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int res1 = as_path_walk(path1, &pos1, &val1);
|
||||
int res2 = as_path_walk(path2, &pos2, &val2);
|
||||
|
||||
if (res1 == 0 && res2 == 0)
|
||||
return 0;
|
||||
|
||||
if (val1 == val2)
|
||||
continue;
|
||||
|
||||
return val1 < val2 ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
as_path_walk(const struct adata *path, uint *pos, uint *val)
|
||||
{
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "test/birdtest.h"
|
||||
#include "test/bt-utils.h"
|
||||
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
#include "lib/attrs.h"
|
||||
#include "lib/resource.h"
|
||||
#include "filter/data.h"
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
#include "lib/attrs.h"
|
||||
#include "lib/resource.h"
|
||||
#include "lib/string.h"
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include "test/bt-utils.h"
|
||||
|
||||
#include "lib/net.h"
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
#include "lib/attrs.h"
|
||||
#include "lib/resource.h"
|
||||
|
||||
|
@ -83,6 +83,7 @@ u32 as_path_get_last_nonaggregated(const struct adata *path);
|
||||
int as_path_contains(const struct adata *path, u32 as, int min);
|
||||
int as_path_match_set(const struct adata *path, const struct f_tree *set);
|
||||
const struct adata *as_path_filter(struct linpool *pool, const struct adata *path, const struct f_val *set, int pos);
|
||||
int as_path_compare(const struct adata *path1, const struct adata *path2);
|
||||
int as_path_walk(const struct adata *path, uint *pos, uint *val);
|
||||
|
||||
static inline struct adata *as_path_prepend(struct linpool *pool, const struct adata *path, u32 as)
|
||||
|
@ -239,10 +239,36 @@ asm(
|
||||
);
|
||||
#endif
|
||||
|
||||
|
||||
/* Pseudorandom numbers */
|
||||
|
||||
u32 random_u32(void);
|
||||
void random_init(void);
|
||||
void random_bytes(void *buf, size_t size);
|
||||
|
||||
|
||||
/* Hashing */
|
||||
|
||||
/* Constant parameter for non-parametrized hashes */
|
||||
#define HASH_PARAM 2902958171u
|
||||
|
||||
/* Precomputed powers of HASH_PARAM */
|
||||
#define HASH_PARAM1 ((u64) HASH_PARAM)
|
||||
#define HASH_PARAM2 (HASH_PARAM1 * HASH_PARAM)
|
||||
#define HASH_PARAM3 (HASH_PARAM2 * HASH_PARAM)
|
||||
#define HASH_PARAM4 (HASH_PARAM3 * HASH_PARAM)
|
||||
|
||||
/* Reduce intermediate 64-bit value to final 32-bit value */
|
||||
static inline u32 hash_value(u64 a)
|
||||
{ return ((u32) a) ^ ((u32) (a >> 32)); }
|
||||
|
||||
static inline u64 u32_hash0(u32 v, u32 p, u64 acc)
|
||||
{ return (acc + v) * p; }
|
||||
|
||||
static inline u64 u64_hash0(u64 v, u32 p, u64 acc)
|
||||
{ return u32_hash0(v >> 32, p, u32_hash0(v, p, acc)); }
|
||||
|
||||
static inline u32 u64_hash(u64 v)
|
||||
{ return hash_value(u64_hash0(v, HASH_PARAM, 0)); }
|
||||
|
||||
#endif
|
||||
|
214
lib/bitmap.c
214
lib/bitmap.c
@ -197,3 +197,217 @@ hmap_check(struct hmap *b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Indirect bitmap for MPLS labels (20 bit range)
|
||||
*/
|
||||
|
||||
void
|
||||
lmap_init(struct lmap *b, pool *p)
|
||||
{
|
||||
b->slab = sl_new(p, 128);
|
||||
b->size = 8;
|
||||
b->data = mb_allocz(p, b->size * sizeof(u32 *));
|
||||
b->root = sl_allocz(b->slab);
|
||||
}
|
||||
|
||||
static void
|
||||
lmap_grow(struct lmap *b, uint need)
|
||||
{
|
||||
uint old_size = b->size;
|
||||
|
||||
while (b->size < need)
|
||||
b->size *= 2;
|
||||
|
||||
b->data = mb_realloc(b->data, b->size * sizeof(u32 *));
|
||||
|
||||
memset(b->data + old_size, 0, (b->size - old_size) * sizeof(u32 *));
|
||||
}
|
||||
|
||||
void
|
||||
lmap_free(struct lmap *b)
|
||||
{
|
||||
rfree(b->slab);
|
||||
mb_free(b->data);
|
||||
memset(b, 0, sizeof(struct lmap));
|
||||
}
|
||||
|
||||
static inline int
|
||||
b1024_and(u32 *p)
|
||||
{
|
||||
for (int i = 0; i < 32; i++)
|
||||
if (~p[i])
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline int
|
||||
b1024_or(u32 *p)
|
||||
{
|
||||
for (int i = 0; i < 32; i++)
|
||||
if (p[i])
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
lmap_test(struct lmap *b, uint n)
|
||||
{
|
||||
uint n0 = n >> 10;
|
||||
uint n1 = n & 0x3ff;
|
||||
|
||||
return (n0 < b->size) && b->data[n0] && BIT32_TEST(b->data[n0], n1);
|
||||
}
|
||||
|
||||
void
|
||||
lmap_set(struct lmap *b, uint n)
|
||||
{
|
||||
uint n0 = n >> 10;
|
||||
uint n1 = n & 0x3ff;
|
||||
|
||||
if (n0 >= b->size)
|
||||
lmap_grow(b, n0 + 1);
|
||||
|
||||
if (! b->data[n0])
|
||||
b->data[n0] = sl_allocz(b->slab);
|
||||
|
||||
BIT32_SET(b->data[n0], n1);
|
||||
|
||||
if (b1024_and(b->data[n0]))
|
||||
BIT32_SET(b->root, n0);
|
||||
}
|
||||
|
||||
void
|
||||
lmap_clear(struct lmap *b, uint n)
|
||||
{
|
||||
uint n0 = n >> 10;
|
||||
uint n1 = n & 0x3ff;
|
||||
|
||||
if (n0 >= b->size)
|
||||
return;
|
||||
|
||||
if (! b->data[n0])
|
||||
return;
|
||||
|
||||
BIT32_CLR(b->data[n0], n1);
|
||||
BIT32_CLR(b->root, n0);
|
||||
|
||||
if (!b1024_or(b->data[n0]))
|
||||
{
|
||||
sl_free(b->data[n0]);
|
||||
b->data[n0] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int
|
||||
b1024_first_zero(u32 *p)
|
||||
{
|
||||
for (int i = 0; i < 32; i++)
|
||||
if (~p[i])
|
||||
return 32*i + u32_ctz(~p[i]);
|
||||
|
||||
return 1024;
|
||||
}
|
||||
|
||||
uint
|
||||
lmap_first_zero(struct lmap *b)
|
||||
{
|
||||
uint n0 = b1024_first_zero(b->root);
|
||||
uint n1 = ((n0 < b->size) && b->data[n0]) ?
|
||||
b1024_first_zero(b->data[n0]) : 0;
|
||||
|
||||
return (n0 << 10) + n1;
|
||||
}
|
||||
|
||||
static uint
|
||||
b1024_first_zero_in_range(u32 *p, uint lo, uint hi)
|
||||
{
|
||||
uint lo0 = lo >> 5;
|
||||
uint lo1 = lo & 0x1f;
|
||||
uint hi0 = hi >> 5;
|
||||
uint hi1 = hi & 0x1f;
|
||||
u32 mask = (1 << lo1) - 1;
|
||||
u32 val;
|
||||
|
||||
for (uint i = lo0; i < hi0; i++)
|
||||
{
|
||||
val = p[i] | mask;
|
||||
mask = 0;
|
||||
|
||||
if (~val)
|
||||
return 32*i + u32_ctz(~val);
|
||||
}
|
||||
|
||||
if (hi1)
|
||||
{
|
||||
mask |= ~((1u << hi1) - 1);
|
||||
val = p[hi0] | mask;
|
||||
|
||||
if (~val)
|
||||
return 32*hi0 + u32_ctz(~val);
|
||||
}
|
||||
|
||||
return hi;
|
||||
}
|
||||
|
||||
uint
|
||||
lmap_first_zero_in_range(struct lmap *b, uint lo, uint hi)
|
||||
{
|
||||
uint lo0 = lo >> 10;
|
||||
uint lo1 = lo & 0x3ff;
|
||||
uint hi0 = hi >> 10;
|
||||
uint hi1 = hi & 0x3ff;
|
||||
|
||||
if (lo1)
|
||||
{
|
||||
uint max = (lo0 == hi0) ? hi1 : 1024;
|
||||
uint n0 = lo0;
|
||||
uint n1 = ((n0 < b->size) && b->data[n0]) ?
|
||||
b1024_first_zero_in_range(b->data[n0], lo1, max) : lo1;
|
||||
|
||||
if (n1 < 1024)
|
||||
return (n0 << 10) + n1;
|
||||
|
||||
lo0++;
|
||||
lo1 = 0;
|
||||
}
|
||||
|
||||
if (lo0 < hi0)
|
||||
{
|
||||
uint n0 = b1024_first_zero_in_range(b->root, lo0, hi0);
|
||||
|
||||
if (n0 < hi0)
|
||||
{
|
||||
uint n1 = ((n0 < b->size) && b->data[n0]) ?
|
||||
b1024_first_zero(b->data[n0]) : 0;
|
||||
|
||||
return (n0 << 10) + n1;
|
||||
}
|
||||
}
|
||||
|
||||
if (hi1)
|
||||
{
|
||||
uint n0 = hi0;
|
||||
uint n1 = ((n0 < b->size) && b->data[n0]) ?
|
||||
b1024_first_zero_in_range(b->data[n0], 0, hi1) : 0;
|
||||
|
||||
return (n0 << 10) + n1;
|
||||
}
|
||||
|
||||
return hi;
|
||||
}
|
||||
|
||||
void
|
||||
lmap_check(struct lmap *b)
|
||||
{
|
||||
for (int i = 0; i < (int) b->size; i++)
|
||||
{
|
||||
int x = b->data[i] && b1024_and(b->data[i]);
|
||||
int y = !!BIT32_TEST(b->root, i);
|
||||
if (x != y)
|
||||
bug("Inconsistent data on %d (%d vs %d)", i, x, y);
|
||||
}
|
||||
}
|
||||
|
21
lib/bitmap.h
21
lib/bitmap.h
@ -60,4 +60,25 @@ void hmap_clear(struct hmap *b, uint n);
|
||||
u32 hmap_first_zero(struct hmap *b);
|
||||
void hmap_check(struct hmap *b);
|
||||
|
||||
|
||||
struct lmap
|
||||
{
|
||||
slab *slab;
|
||||
uint size;
|
||||
u32 **data;
|
||||
u32 *root;
|
||||
};
|
||||
|
||||
static inline uint lmap_max(struct lmap *b)
|
||||
{ return b->size << 10; }
|
||||
|
||||
void lmap_init(struct lmap *b, pool *p);
|
||||
void lmap_free(struct lmap *b);
|
||||
int lmap_test(struct lmap *b, uint n);
|
||||
void lmap_set(struct lmap *b, uint n);
|
||||
void lmap_clear(struct lmap *b, uint n);
|
||||
uint lmap_first_zero(struct lmap *b);
|
||||
uint lmap_first_zero_in_range(struct lmap *b, uint lo, uint hi);
|
||||
void lmap_check(struct lmap *b);
|
||||
|
||||
#endif
|
||||
|
@ -170,6 +170,70 @@ t_hmap_set_clear_fill(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
t_lmap_set_clear_fill(void)
|
||||
{
|
||||
struct lmap b;
|
||||
|
||||
lmap_init(&b, &root_pool);
|
||||
|
||||
char expected[MAX_NUM] = {};
|
||||
uint i, j, n;
|
||||
|
||||
for (i = 0; i < STEP_NUM; i++)
|
||||
{
|
||||
uint last = 0;
|
||||
uint lo = bt_random() % (1 << 19);
|
||||
uint hi = lo + 2 * STEP_SET;
|
||||
uint step_set = bt_random() % STEP_SET;
|
||||
uint step_clr = bt_random() % STEP_CLR;
|
||||
|
||||
for (j = 0; j < step_set; j++)
|
||||
{
|
||||
n = lmap_first_zero_in_range(&b, lo, hi);
|
||||
bt_assert(n >= lo);
|
||||
bt_assert(n <= hi);
|
||||
|
||||
for (last = lo; last < n; last++)
|
||||
bt_assert(expected[last]);
|
||||
|
||||
if (n >= hi)
|
||||
break;
|
||||
|
||||
bt_assert(!expected[n]);
|
||||
|
||||
lmap_set(&b, n);
|
||||
expected[n] = 1;
|
||||
}
|
||||
|
||||
for (j = 0; j < step_clr; j++)
|
||||
{
|
||||
n = lo + bt_random() % (step_set + 1);
|
||||
|
||||
if (!expected[n])
|
||||
continue;
|
||||
|
||||
lmap_clear(&b, n);
|
||||
expected[n] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint cnt = 0;
|
||||
for (i = 0; i < MAX_NUM; i++)
|
||||
{
|
||||
if (lmap_test(&b, i) != expected[i])
|
||||
bt_abort_msg("Bitmap mismatch on %d (should be %d %d)", i, lmap_test(&b, i), expected[i]);
|
||||
|
||||
if (expected[i])
|
||||
cnt++;
|
||||
}
|
||||
// bt_log("Total %u", cnt);
|
||||
|
||||
lmap_check(&b);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
@ -178,6 +242,7 @@ main(int argc, char *argv[])
|
||||
bt_test_suite(t_bmap_set_clear_random, "BMap - random sequence of sets / clears");
|
||||
bt_test_suite(t_hmap_set_clear_random, "HMap - random sequence of sets / clears");
|
||||
bt_test_suite(t_hmap_set_clear_fill, "HMap - linear sets and random clears");
|
||||
bt_test_suite(t_lmap_set_clear_fill, "LMap - linear sets and random clears");
|
||||
|
||||
return bt_exit_value();
|
||||
}
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include "nest/locks.h"
|
||||
#include "sysdep/unix/unix.h"
|
||||
#include "nest/iface.h"
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
|
||||
#define MAX_NUM 4
|
||||
|
||||
|
@ -235,6 +235,14 @@ mem_hash_mix(u64 *h, const void *p, uint s)
|
||||
*h = *h * multiplier + pp[i];
|
||||
}
|
||||
|
||||
static inline void
|
||||
mem_hash_mix_str(u64 *h, const char *s)
|
||||
{
|
||||
const u64 multiplier = 0xb38bc09a61202731ULL;
|
||||
while (s)
|
||||
*h = *h * multiplier + *s++;
|
||||
}
|
||||
|
||||
static inline void
|
||||
mem_hash_mix_num(u64 *h, u64 val)
|
||||
{
|
||||
|
31
lib/ip.h
31
lib/ip.h
@ -194,14 +194,37 @@ static inline int ipa_nonzero2(ip_addr a)
|
||||
* Hash and compare functions
|
||||
*/
|
||||
|
||||
static inline u64 ip4_hash0(ip4_addr a, u32 p, u64 acc)
|
||||
{ return (acc + _I(a)) * p; }
|
||||
|
||||
static inline u32 ip4_hash(ip4_addr a)
|
||||
{ return u32_hash(_I(a)); }
|
||||
{
|
||||
// return hash_value(ip4_hash0(a, HASH_PARAM, 0));
|
||||
|
||||
/* For some reason, the old hash works slightly better */
|
||||
return u32_hash(_I(a));
|
||||
}
|
||||
|
||||
static inline u64 ip6_hash0(ip6_addr a, u32 p, u64 acc)
|
||||
{
|
||||
acc += _I0(a); acc *= p;
|
||||
acc += _I1(a); acc *= p;
|
||||
acc += _I2(a); acc *= p;
|
||||
acc += _I3(a); acc *= p;
|
||||
return acc;
|
||||
}
|
||||
|
||||
static inline u32 ip6_hash(ip6_addr a)
|
||||
{
|
||||
/* Returns a 32-bit hash key, although low-order bits are not mixed */
|
||||
u32 x = _I0(a) ^ _I1(a) ^ _I2(a) ^ _I3(a);
|
||||
return x ^ (x << 16) ^ (x << 24);
|
||||
// return hash_value(ip6_hash0(a, HASH_PARAM, 0));
|
||||
|
||||
/* Just use the expanded form */
|
||||
u64 acc =
|
||||
_I0(a) * HASH_PARAM4 +
|
||||
_I1(a) * HASH_PARAM3 +
|
||||
_I2(a) * HASH_PARAM2 +
|
||||
_I3(a) * HASH_PARAM1;
|
||||
return hash_value(acc);
|
||||
}
|
||||
|
||||
static inline int ip4_compare(ip4_addr a, ip4_addr b)
|
||||
|
34
lib/net.h
34
lib/net.h
@ -479,39 +479,47 @@ static inline void net_copy_mpls(net_addr_mpls *dst, const net_addr_mpls *src)
|
||||
{ memcpy(dst, src, sizeof(net_addr_mpls)); }
|
||||
|
||||
|
||||
/* XXXX */
|
||||
static inline u32 u64_hash(u64 a)
|
||||
{ return u32_hash(a); }
|
||||
static inline u32 px4_hash(ip4_addr prefix, u32 pxlen)
|
||||
{ return ip4_hash(prefix) ^ (pxlen << 26); }
|
||||
|
||||
static inline u32 px6_hash(ip6_addr prefix, u32 pxlen)
|
||||
{ return ip6_hash(prefix) ^ (pxlen << 26); }
|
||||
|
||||
static inline u32 net_hash_ip4(const net_addr_ip4 *n)
|
||||
{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
|
||||
{ return px4_hash(n->prefix, n->pxlen); }
|
||||
|
||||
static inline u32 net_hash_ip6(const net_addr_ip6 *n)
|
||||
{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
|
||||
{ return px6_hash(n->prefix, n->pxlen); }
|
||||
|
||||
static inline u32 net_hash_vpn4(const net_addr_vpn4 *n)
|
||||
{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26) ^ u64_hash(n->rd); }
|
||||
{
|
||||
u64 acc = ip4_hash0(n->prefix, HASH_PARAM, 0) ^ (n->pxlen << 26);
|
||||
return hash_value(u64_hash0(n->rd, HASH_PARAM, acc));
|
||||
}
|
||||
|
||||
static inline u32 net_hash_vpn6(const net_addr_vpn6 *n)
|
||||
{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26) ^ u64_hash(n->rd); }
|
||||
{
|
||||
u64 acc = ip6_hash0(n->prefix, HASH_PARAM, 0) ^ (n->pxlen << 26);
|
||||
return hash_value(u64_hash0(n->rd, HASH_PARAM, acc));
|
||||
}
|
||||
|
||||
static inline u32 net_hash_roa4(const net_addr_roa4 *n)
|
||||
{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
|
||||
{ return px4_hash(n->prefix, n->pxlen); }
|
||||
|
||||
static inline u32 net_hash_roa6(const net_addr_roa6 *n)
|
||||
{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
|
||||
{ return px6_hash(n->prefix, n->pxlen); }
|
||||
|
||||
static inline u32 net_hash_flow4(const net_addr_flow4 *n)
|
||||
{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
|
||||
{ return px4_hash(n->prefix, n->pxlen); }
|
||||
|
||||
static inline u32 net_hash_flow6(const net_addr_flow6 *n)
|
||||
{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
|
||||
{ return px6_hash(n->prefix, n->pxlen); }
|
||||
|
||||
static inline u32 net_hash_ip6_sadr(const net_addr_ip6_sadr *n)
|
||||
{ return net_hash_ip6((net_addr_ip6 *) n); }
|
||||
{ return px6_hash(n->dst_prefix, n->dst_pxlen); }
|
||||
|
||||
static inline u32 net_hash_mpls(const net_addr_mpls *n)
|
||||
{ return n->label; }
|
||||
{ return u32_hash(n->label); }
|
||||
|
||||
u32 net_hash(const net_addr *a);
|
||||
|
||||
|
@ -336,6 +336,7 @@ resource_init(void)
|
||||
rcu_init();
|
||||
resource_sys_init();
|
||||
|
||||
root_pool.r.class = &pool_class;
|
||||
rp_init(&root_pool, the_bird_domain.the_bird, "Root");
|
||||
tmp_init(&root_pool, the_bird_domain.the_bird);
|
||||
}
|
||||
|
10
lib/route.h
10
lib/route.h
@ -60,10 +60,12 @@ static inline rte rte_init_from(const rte *r)
|
||||
};
|
||||
}
|
||||
|
||||
int rte_same(const rte *, const rte *);
|
||||
|
||||
struct rte_src {
|
||||
struct rte_src *next; /* Hash chain */
|
||||
struct rte_owner *owner; /* Route source owner */
|
||||
u32 private_id; /* Private ID, assigned by the protocol */
|
||||
u64 private_id; /* Private ID, assigned by the protocol */
|
||||
u32 global_id; /* Globally unique ID of the source */
|
||||
_Atomic u64 uc; /* Use count */
|
||||
};
|
||||
@ -171,6 +173,8 @@ static inline void rt_unlock_source(struct rte_src *src)
|
||||
void rt_init_sources(struct rte_owner *, const char *name, event_list *list);
|
||||
void rt_destroy_sources(struct rte_owner *, event *);
|
||||
|
||||
void rt_dump_sources(struct rte_owner *);
|
||||
|
||||
/*
|
||||
* Route Attributes
|
||||
*
|
||||
@ -221,7 +225,8 @@ struct nexthop_adata {
|
||||
#define RTS_BABEL 13 /* Babel route */
|
||||
#define RTS_RPKI 14 /* Route Origin Authorization */
|
||||
#define RTS_PERF 15 /* Perf checker */
|
||||
#define RTS_MAX 16
|
||||
#define RTS_AGGREGATED 16 /* Aggregated route */
|
||||
#define RTS_MAX 17
|
||||
|
||||
#define RTD_NONE 0 /* Undefined next hop */
|
||||
#define RTD_UNICAST 1 /* A standard next hop */
|
||||
@ -305,6 +310,7 @@ struct ea_class_ref {
|
||||
|
||||
void ea_register_init(struct ea_class *);
|
||||
struct ea_class_ref *ea_register_alloc(pool *, struct ea_class);
|
||||
struct ea_class_ref *ea_ref_class(pool *, struct ea_class *); /* Reference for an attribute alias */
|
||||
|
||||
#define EA_REGISTER_ALL_HELPER(x) ea_register_init(x);
|
||||
#define EA_REGISTER_ALL(...) MACRO_FOREACH(EA_REGISTER_ALL_HELPER, __VA_ARGS__)
|
||||
|
@ -33,6 +33,9 @@ u64 bstrtoul10(const char *str, char **end);
|
||||
u64 bstrtoul16(const char *str, char **end);
|
||||
byte bstrtobyte16(const char *str);
|
||||
|
||||
int bstrhextobin(const char *s, byte *b);
|
||||
int bstrbintohex(const byte *b, size_t len, char *buf, size_t size, char delim);
|
||||
|
||||
int patmatch(const byte *pat, const byte *str);
|
||||
|
||||
static inline char *xbasename(const char *str)
|
||||
|
@ -60,29 +60,91 @@ bstrtoul16(const char *str, char **end)
|
||||
return UINT64_MAX;
|
||||
}
|
||||
|
||||
byte
|
||||
bstrtobyte16(const char *str)
|
||||
static int
|
||||
fromxdigit(char c)
|
||||
{
|
||||
byte out = 0;
|
||||
for (int i=0; i<2; i++) {
|
||||
switch (str[i]) {
|
||||
switch (c)
|
||||
{
|
||||
case '0' ... '9':
|
||||
out *= 16;
|
||||
out += str[i] - '0';
|
||||
break;
|
||||
return c - '0';
|
||||
case 'a' ... 'f':
|
||||
out *= 16;
|
||||
out += str[i] + 10 - 'a';
|
||||
break;
|
||||
return c + 10 - 'a';
|
||||
case 'A' ... 'F':
|
||||
out *= 16;
|
||||
out += str[i] + 10 - 'A';
|
||||
break;
|
||||
return c + 10 - 'A';
|
||||
default:
|
||||
errno = ERANGE;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
bstrhextobin(const char *s, byte *b)
|
||||
{
|
||||
int len = 0;
|
||||
int hi = 0;
|
||||
|
||||
for (; *s; s++)
|
||||
{
|
||||
int v = fromxdigit(*s);
|
||||
if (v < 0)
|
||||
{
|
||||
if (strchr(" :-", *s) && !hi)
|
||||
continue;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
return out;
|
||||
if (len == INT32_MAX)
|
||||
return -1;
|
||||
|
||||
if (b)
|
||||
{
|
||||
if (!hi)
|
||||
b[len] = (v << 4);
|
||||
else
|
||||
b[len] |= v;
|
||||
}
|
||||
|
||||
len += hi;
|
||||
hi = !hi;
|
||||
}
|
||||
|
||||
return !hi ? len : -1;
|
||||
}
|
||||
|
||||
static char
|
||||
toxdigit(uint b)
|
||||
{
|
||||
if (b < 10)
|
||||
return ('0' + b);
|
||||
else if (b < 16)
|
||||
return ('a' + b - 10);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
bstrbintohex(const byte *b, size_t len, char *buf, size_t size, char delim)
|
||||
{
|
||||
ASSERT(size >= 6);
|
||||
char *bound = buf + size - 3;
|
||||
|
||||
size_t i;
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
if (buf > bound)
|
||||
{
|
||||
strcpy(buf - 4, "...");
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint x = b[i];
|
||||
buf[0] = toxdigit(x >> 4);
|
||||
buf[1] = toxdigit(x & 0xF);
|
||||
buf[2] = delim;
|
||||
buf += 3;
|
||||
}
|
||||
|
||||
buf[i ? -1 : 0] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -55,6 +55,9 @@ static inline timer *timers_first(struct timeloop *loop)
|
||||
#define current_time() atomic_load_explicit(&last_time, memory_order_acquire)
|
||||
#define current_real_time() atomic_load_explicit(&real_time, memory_order_acquire)
|
||||
|
||||
/* In sysdep code */
|
||||
btime current_time_now(void);
|
||||
|
||||
//#define now (current_time() TO_S)
|
||||
//#define now_real (current_real_time() TO_S)
|
||||
extern btime boot_time;
|
||||
|
14
lib/type.h
14
lib/type.h
@ -37,10 +37,16 @@ union bval_long {
|
||||
ip_addr ip;
|
||||
const net_addr *net;
|
||||
const char *s;
|
||||
const struct adata *bs;
|
||||
const struct f_tree *t;
|
||||
const struct f_trie *ti;
|
||||
const struct f_path_mask *path_mask;
|
||||
struct f_path_mask_item pmi;
|
||||
struct rte *rte;
|
||||
struct rte_block {
|
||||
struct rte **rte;
|
||||
uint len;
|
||||
} rte_block;
|
||||
};
|
||||
|
||||
|
||||
@ -48,12 +54,15 @@ union bval_long {
|
||||
enum btype {
|
||||
/* Nothing. Simply nothing. */
|
||||
T_VOID = 0,
|
||||
T_NONE = 0xff,
|
||||
|
||||
/* Something but inaccessible. */
|
||||
T_OPAQUE = 0x02, /* Opaque byte string (not filterable) */
|
||||
T_IFACE = 0x0c, /* Pointer to an interface (inside adata) */
|
||||
T_NEXTHOP_LIST = 0x2c, /* The whole nexthop block */
|
||||
T_HOSTENTRY = 0x2e, /* Hostentry with possible MPLS labels */
|
||||
T_ROUTES_BLOCK = 0x68, /* Block of route pointers */
|
||||
T_ROUTE = 0x6a, /* One route pointer */
|
||||
T_NEXTHOP_LIST = 0x6c, /* The whole nexthop block */
|
||||
T_HOSTENTRY = 0x6e, /* Hostentry with possible MPLS labels */
|
||||
|
||||
/* Types shared with eattrs */
|
||||
T_INT = 0x01, /* 32-bit unsigned integer number */
|
||||
@ -99,6 +108,7 @@ enum btype {
|
||||
T_LC = 0xc0, /* Large community value, lcomm */
|
||||
T_RD = 0xc4, /* Route distinguisher for VPN addresses */
|
||||
T_PATH_MASK_ITEM = 0xc8, /* Path mask item for path mask constructors */
|
||||
T_BYTESTRING = 0xcc,
|
||||
|
||||
T_SET = 0x80,
|
||||
T_PREFIX_SET = 0x84,
|
||||
|
@ -13,6 +13,6 @@ $(o)proto-build.c: Makefile $(lastword $(MAKEFILE_LIST)) $(objdir)/.dir-stamp
|
||||
|
||||
prepare: $(o)proto-build.c
|
||||
|
||||
tests_src :=
|
||||
tests_src := rt-fib_test.c
|
||||
tests_targets := $(tests_targets) $(tests-target-files)
|
||||
tests_objs := $(tests_objs) $(src-o-files)
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
#include "nest/cli.h"
|
||||
#include "conf/conf.h"
|
||||
#include "nest/cmds.h"
|
||||
@ -54,9 +54,6 @@ cmd_show_symbols(struct sym_show_data *sd)
|
||||
for (const struct sym_scope *scope = config->root_scope; scope; scope = scope->next)
|
||||
HASH_WALK(scope->hash, next, sym)
|
||||
{
|
||||
if (!sym->scope->active)
|
||||
continue;
|
||||
|
||||
if (sd->type && (sym->class != sd->type))
|
||||
continue;
|
||||
|
||||
|
@ -204,7 +204,7 @@ rtrid:
|
||||
|
||||
idval:
|
||||
NUM { $$ = $1; }
|
||||
| '(' term ')' { $$ = f_eval_int(f_linearize($2, 1)); }
|
||||
| '(' term ')' { $$ = cf_eval_int($2); }
|
||||
| IP4 { $$ = ip4_to_u32($1); }
|
||||
| CF_SYM_KNOWN {
|
||||
if ($1->class == (SYM_CONSTANT | T_INT) || $1->class == (SYM_CONSTANT | T_QUAD))
|
||||
@ -299,19 +299,19 @@ proto_start:
|
||||
|
||||
proto_name:
|
||||
/* EMPTY */ {
|
||||
struct symbol *s = cf_default_name(this_proto->protocol->template, &this_proto->protocol->name_counter);
|
||||
struct symbol *s = cf_default_name(new_config, this_proto->protocol->template, &this_proto->protocol->name_counter);
|
||||
s->class = this_proto->class;
|
||||
s->proto = this_proto;
|
||||
this_proto->name = s->name;
|
||||
}
|
||||
| symbol {
|
||||
cf_define_symbol($1, this_proto->class, proto, this_proto);
|
||||
cf_define_symbol(new_config, $1, this_proto->class, proto, this_proto);
|
||||
this_proto->name = $1->name;
|
||||
}
|
||||
| FROM CF_SYM_KNOWN {
|
||||
if (($2->class != SYM_TEMPLATE) && ($2->class != SYM_PROTO)) cf_error("Template or protocol name expected");
|
||||
|
||||
struct symbol *s = cf_default_name(this_proto->protocol->template, &this_proto->protocol->name_counter);
|
||||
struct symbol *s = cf_default_name(new_config, this_proto->protocol->template, &this_proto->protocol->name_counter);
|
||||
s->class = this_proto->class;
|
||||
s->proto = this_proto;
|
||||
this_proto->name = s->name;
|
||||
@ -321,7 +321,7 @@ proto_name:
|
||||
| symbol FROM CF_SYM_KNOWN {
|
||||
if (($3->class != SYM_TEMPLATE) && ($3->class != SYM_PROTO)) cf_error("Template or protocol name expected");
|
||||
|
||||
cf_define_symbol($1, this_proto->class, proto, this_proto);
|
||||
cf_define_symbol(new_config, $1, this_proto->class, proto, this_proto);
|
||||
this_proto->name = $1->name;
|
||||
|
||||
proto_copy_config(this_proto, $3->proto);
|
||||
@ -608,10 +608,15 @@ password_item:
|
||||
|
||||
pass_key: PASSWORD | KEY;
|
||||
|
||||
password_item_begin:
|
||||
pass_key text { init_password_list(); init_password($2, strlen($2), password_id++); }
|
||||
| pass_key BYTESTRING { init_password_list(); init_password($2->data, $2->length, password_id++); }
|
||||
;
|
||||
password_item_begin: pass_key bytestring_text
|
||||
{
|
||||
init_password_list();
|
||||
if ($2.type == T_BYTESTRING)
|
||||
init_password($2.val.bs->data, $2.val.bs->length, password_id++);
|
||||
else if ($2.type == T_STRING)
|
||||
init_password($2.val.s, strlen($2.val.s), password_id++);
|
||||
else bug("Bad bytestring_text");
|
||||
};
|
||||
|
||||
password_item_params:
|
||||
/* empty */ { }
|
||||
@ -726,7 +731,7 @@ r_args:
|
||||
$$->addr = $3;
|
||||
$$->addr_mode = TE_ADDR_IN;
|
||||
}
|
||||
| r_args TABLE symbol_known {
|
||||
| r_args TABLE CF_SYM_KNOWN {
|
||||
cf_assert_symbol($3, SYM_TABLE);
|
||||
if (!$3->table) cf_error("Table %s not configured", $3->name);
|
||||
$$ = $1;
|
||||
@ -773,7 +778,7 @@ r_args:
|
||||
$$ = $1;
|
||||
$$->filtered = 1;
|
||||
}
|
||||
| r_args export_mode symbol_known {
|
||||
| r_args export_mode CF_SYM_KNOWN {
|
||||
cf_assert_symbol($3, SYM_PROTO);
|
||||
struct proto_config *c = (struct proto_config *) $3->proto;
|
||||
$$ = $1;
|
||||
@ -790,7 +795,7 @@ r_args:
|
||||
$$->export_channel = $3;
|
||||
$$->tables_defined_by = RSD_TDB_INDIRECT;
|
||||
}
|
||||
| r_args PROTOCOL symbol_known {
|
||||
| r_args PROTOCOL CF_SYM_KNOWN {
|
||||
cf_assert_symbol($3, SYM_PROTO);
|
||||
struct proto_config *c = (struct proto_config *) $3->proto;
|
||||
$$ = $1;
|
||||
@ -902,7 +907,7 @@ sym_args:
|
||||
| sym_args FILTER { $$ = $1; $$->type = SYM_FILTER; }
|
||||
| sym_args PROTOCOL { $$ = $1; $$->type = SYM_PROTO; }
|
||||
| sym_args TEMPLATE { $$ = $1; $$->type = SYM_TEMPLATE; }
|
||||
| sym_args symbol { $$ = $1; $$->sym = $2; }
|
||||
| sym_args CF_SYM_KNOWN { $$ = $1; $$->sym = $2; }
|
||||
;
|
||||
|
||||
|
||||
|
@ -288,7 +288,7 @@ ifa_send_notify(struct iface_subscription *s, unsigned c, struct ifa *a)
|
||||
|
||||
if (s->ifa_notify &&
|
||||
(p->proto_state != PS_DOWN) &&
|
||||
(!p->vrf || p->vrf == a->iface->master))
|
||||
(!p->vrf || if_in_vrf(a->iface, p->vrf)))
|
||||
{
|
||||
if (p->debug & D_IFACES)
|
||||
log(L_TRACE "%s < address %N on interface %s %s",
|
||||
@ -330,7 +330,7 @@ if_send_notify(struct iface_subscription *s, unsigned c, struct iface *i)
|
||||
|
||||
if (s->if_notify &&
|
||||
(p->proto_state != PS_DOWN) &&
|
||||
(!p->vrf || p->vrf == i->master))
|
||||
(!p->vrf || if_in_vrf(i, p->vrf)))
|
||||
{
|
||||
if (p->debug & D_IFACES)
|
||||
log(L_TRACE "%s < interface %s %s", p->name, i->name,
|
||||
|
@ -58,6 +58,7 @@ struct iface {
|
||||
#define IF_IGNORE 0x40 /* Not to be used by routing protocols (loopbacks etc.) */
|
||||
#define IF_ADMIN_UP 0x80 /* Administrative up (e.g. IFF_UP in Linux) */
|
||||
#define IF_LINK_UP 0x100 /* Link available (e.g. IFF_LOWER_UP in Linux) */
|
||||
#define IF_VRF 0x200 /* Iface is VRF master */
|
||||
|
||||
#define IA_PRIMARY 0x10000 /* This address is primary */
|
||||
#define IA_SECONDARY 0x20000 /* This address has been reported as secondary by the kernel */
|
||||
@ -124,6 +125,9 @@ struct iface *if_find_by_name(const char *);
|
||||
struct iface *if_get_by_name(const char *);
|
||||
void if_recalc_all_preferred_addresses(void);
|
||||
|
||||
static inline int if_in_vrf(struct iface *i, struct iface *vrf)
|
||||
{ return (i->flags & IF_VRF) ? (i == vrf) : (i->master == vrf); }
|
||||
|
||||
struct iface *if_walk_first(void);
|
||||
struct iface *if_walk_next(struct iface *);
|
||||
void if_walk_done(void);
|
||||
|
@ -166,7 +166,7 @@ if_connected_any(ip_addr a, struct iface *vrf, struct iface **iface, struct ifa
|
||||
|
||||
/* Prefer SCOPE_HOST or longer prefix */
|
||||
WALK_LIST(i, global_iface_list)
|
||||
if ((!vrf || vrf == i->master) && ((s = if_connected(a, i, &b, flags)) >= 0))
|
||||
if ((!vrf || if_in_vrf(i,vrf)) && ((s = if_connected(a, i, &b, flags)) >= 0))
|
||||
if (scope_better(s, scope) || (scope_remote(s, scope) && ifa_better(b, *addr)))
|
||||
{
|
||||
*iface = i;
|
||||
@ -432,7 +432,7 @@ neigh_update(neighbor *n, struct iface *iface)
|
||||
return;
|
||||
|
||||
/* VRF-bound neighbors ignore changes in other VRFs */
|
||||
if (p->vrf && (p->vrf != iface->master))
|
||||
if (p->vrf && !if_in_vrf(iface, p->vrf))
|
||||
return;
|
||||
|
||||
scope = if_connected(n->addr, iface, &ifa, n->flags);
|
||||
|
40
nest/proto.c
40
nest/proto.c
@ -16,7 +16,7 @@
|
||||
#include "lib/timer.h"
|
||||
#include "lib/string.h"
|
||||
#include "conf/conf.h"
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
#include "nest/iface.h"
|
||||
#include "nest/cli.h"
|
||||
#include "filter/filter.h"
|
||||
@ -184,7 +184,6 @@ proto_log_state_change(struct proto *p)
|
||||
p->last_state_name_announced = NULL;
|
||||
}
|
||||
|
||||
|
||||
struct channel_config *
|
||||
proto_cf_find_channel(struct proto_config *pc, uint net_type)
|
||||
{
|
||||
@ -687,10 +686,12 @@ channel_start_export(struct channel *c)
|
||||
|
||||
ASSERT(c->channel_state == CS_UP);
|
||||
|
||||
pool *p = rp_newf(c->proto->pool, c->proto->pool->domain, "Channel %s.%s export", c->proto->name, c->name);
|
||||
|
||||
c->out_req = (struct rt_export_request) {
|
||||
.name = mb_sprintf(c->proto->pool, "%s.%s", c->proto->name, c->name),
|
||||
.name = mb_sprintf(p, "%s.%s", c->proto->name, c->name),
|
||||
.list = proto_work_list(c->proto),
|
||||
.pool = c->proto->pool,
|
||||
.pool = p,
|
||||
.feed_block_size = c->feed_block_size,
|
||||
.prefilter = {
|
||||
.mode = c->out_subprefix ? TE_ADDR_IN : TE_ADDR_NONE,
|
||||
@ -702,8 +703,8 @@ channel_start_export(struct channel *c)
|
||||
.mark_seen = channel_rpe_mark_seen_export,
|
||||
};
|
||||
|
||||
bmap_init(&c->export_map, c->proto->pool, 16);
|
||||
bmap_init(&c->export_reject_map, c->proto->pool, 16);
|
||||
bmap_init(&c->export_map, p, 16);
|
||||
bmap_init(&c->export_reject_map, p, 16);
|
||||
|
||||
channel_reset_limit(c, &c->out_limit, PLD_OUT);
|
||||
|
||||
@ -728,7 +729,7 @@ channel_start_export(struct channel *c)
|
||||
}
|
||||
|
||||
c->refeed_req = c->out_req;
|
||||
c->refeed_req.name = mb_sprintf(c->proto->pool, "%s.%s.refeed", c->proto->name, c->name);
|
||||
c->refeed_req.name = mb_sprintf(p, "%s.%s.refeed", c->proto->name, c->name);
|
||||
c->refeed_req.dump_req = channel_dump_refeed_req;
|
||||
c->refeed_req.log_state_change = channel_refeed_log_state_change;
|
||||
c->refeed_req.mark_seen = channel_rpe_mark_seen_refeed;
|
||||
@ -795,12 +796,12 @@ channel_export_stopped(struct rt_export_request *req)
|
||||
return;
|
||||
}
|
||||
|
||||
mb_free(c->out_req.name);
|
||||
c->out_req.name = NULL;
|
||||
|
||||
bmap_free(&c->export_map);
|
||||
bmap_free(&c->export_reject_map);
|
||||
|
||||
c->out_req.name = NULL;
|
||||
rfree(c->out_req.pool);
|
||||
|
||||
channel_check_stopped(c);
|
||||
}
|
||||
|
||||
@ -1545,10 +1546,10 @@ proto_event(void *ptr)
|
||||
p->do_stop = 0;
|
||||
}
|
||||
|
||||
if (proto_is_done(p) && p->pool_fragile) /* perusing pool_fragile to do this once only */
|
||||
if (proto_is_done(p) && p->pool_inloop) /* perusing pool_inloop to do this once only */
|
||||
{
|
||||
rp_free(p->pool_fragile);
|
||||
p->pool_fragile = NULL;
|
||||
rp_free(p->pool_inloop);
|
||||
p->pool_inloop = NULL;
|
||||
if (p->loop != &main_birdloop)
|
||||
birdloop_stop_self(p->loop, proto_loop_stopped, p);
|
||||
else
|
||||
@ -1630,7 +1631,8 @@ proto_start(struct proto *p)
|
||||
|
||||
PROTO_LOCKED_FROM_MAIN(p)
|
||||
{
|
||||
p->pool_fragile = rp_newf(p->pool, birdloop_domain(p->loop), "Protocol %s fragile objects", p->cf->name);
|
||||
p->pool_inloop = rp_newf(p->pool, birdloop_domain(p->loop), "Protocol %s early cleanup objects", p->cf->name);
|
||||
p->pool_up = rp_newf(p->pool, birdloop_domain(p->loop), "Protocol %s stop-free objects", p->cf->name);
|
||||
proto_notify_state(p, (p->proto->start ? p->proto->start(p) : PS_UP));
|
||||
}
|
||||
}
|
||||
@ -1893,8 +1895,8 @@ protos_do_commit(struct config *new, struct config *old, int force_reconfig, int
|
||||
/* This is hack, we would like to share config, but we need to copy it now */
|
||||
new_config = new;
|
||||
cfg_mem = new->mem;
|
||||
conf_this_scope = new->root_scope;
|
||||
sym = cf_get_symbol(oc->name);
|
||||
new->current_scope = new->root_scope;
|
||||
sym = cf_get_symbol(new, oc->name);
|
||||
proto_clone_config(sym, parsym->proto);
|
||||
new_config = NULL;
|
||||
cfg_mem = NULL;
|
||||
@ -2273,6 +2275,9 @@ protos_dump_all(void)
|
||||
c->out_req.hook ? rt_export_state_name(rt_export_get_state(c->out_req.hook)) : "-");
|
||||
}
|
||||
|
||||
debug("\tSOURCES\n");
|
||||
rt_dump_sources(&p->sources);
|
||||
|
||||
if (p->proto->dump && (p->proto_state != PS_DOWN))
|
||||
p->proto->dump(p);
|
||||
}
|
||||
@ -2563,6 +2568,9 @@ proto_do_stop(struct proto *p)
|
||||
p->main_source = NULL;
|
||||
}
|
||||
|
||||
rp_free(p->pool_up);
|
||||
p->pool_up = NULL;
|
||||
|
||||
proto_stop_channels(p);
|
||||
rt_destroy_sources(&p->sources, p->event);
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include "lib/event.h"
|
||||
#include "nest/iface.h"
|
||||
#include "lib/settle.h"
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
#include "nest/limit.h"
|
||||
#include "conf/conf.h"
|
||||
#include "filter/data.h"
|
||||
@ -93,8 +93,8 @@ void protos_dump_all(void);
|
||||
|
||||
extern struct protocol
|
||||
proto_device, proto_radv, proto_rip, proto_static, proto_mrt,
|
||||
proto_ospf, proto_perf,
|
||||
proto_pipe, proto_bgp, proto_bfd, proto_babel, proto_rpki;
|
||||
proto_ospf, proto_perf, proto_aggregator,
|
||||
proto_pipe, proto_bgp, proto_bmp, proto_bfd, proto_babel, proto_rpki;
|
||||
|
||||
/*
|
||||
* Routing Protocol Instance
|
||||
@ -145,7 +145,9 @@ struct proto {
|
||||
struct proto_config *cf; /* Configuration data */
|
||||
struct proto_config *cf_new; /* Configuration we want to switch to after shutdown (NULL=delete) */
|
||||
pool *pool; /* Pool containing local objects */
|
||||
pool *pool_fragile; /* Pool containing fragile local objects which need to be freed
|
||||
pool *pool_up; /* Pool containing local objects which should be dropped as soon
|
||||
as the protocol enters the STOP / DOWN state */
|
||||
pool *pool_inloop; /* Pool containing local objects which need to be freed
|
||||
before the protocol's birdloop actually stops, like olocks */
|
||||
event *event; /* Protocol event */
|
||||
timer *restart_timer; /* Timer to restart the protocol from limits */
|
||||
@ -209,14 +211,10 @@ struct proto {
|
||||
*
|
||||
* rte_recalculate Called at the beginning of the best route selection
|
||||
* rte_mergable Compare two rte's and decide whether they could be merged (1=yes, 0=no).
|
||||
* rte_insert Called whenever a rte is inserted to a routing table.
|
||||
* rte_remove Called whenever a rte is removed from the routing table.
|
||||
*/
|
||||
|
||||
int (*rte_recalculate)(struct rtable_private *, struct network *, struct rte *, struct rte *, struct rte *);
|
||||
int (*rte_mergable)(struct rte *, struct rte *);
|
||||
void (*rte_insert)(struct network *, struct rte *);
|
||||
void (*rte_remove)(struct network *, struct rte *);
|
||||
u32 (*rte_igp_metric)(const struct rte *);
|
||||
|
||||
/* Hic sunt protocol-specific data */
|
||||
@ -506,7 +504,8 @@ struct channel_class {
|
||||
#endif
|
||||
};
|
||||
|
||||
extern struct channel_class channel_bgp;
|
||||
extern const struct channel_class channel_basic;
|
||||
extern const struct channel_class channel_bgp;
|
||||
|
||||
struct channel_config {
|
||||
node n;
|
||||
@ -682,6 +681,7 @@ static inline struct channel_config *proto_cf_main_channel(struct proto_config *
|
||||
struct channel *proto_find_channel_by_table(struct proto *p, rtable *t);
|
||||
struct channel *proto_find_channel_by_name(struct proto *p, const char *n);
|
||||
struct channel *proto_add_channel(struct proto *p, struct channel_config *cf);
|
||||
void proto_remove_channel(struct proto *p, struct channel *c);
|
||||
int proto_configure_channel(struct proto *p, struct channel **c, struct channel_config *cf);
|
||||
|
||||
void channel_set_state(struct channel *c, uint state);
|
||||
|
@ -319,7 +319,7 @@ struct rt_pending_export {
|
||||
|
||||
struct rt_export_request {
|
||||
struct rt_export_hook *hook; /* Table part of the export */
|
||||
char *name; /* Network prefilter address */
|
||||
char *name;
|
||||
u8 trace_routes;
|
||||
uint feed_block_size; /* How many routes to feed at once */
|
||||
struct rt_prefilter prefilter;
|
@ -45,7 +45,7 @@
|
||||
*/
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/iface.h"
|
||||
#include "nest/cli.h"
|
||||
@ -91,6 +91,8 @@ const char * const rta_src_names[RTS_MAX] = {
|
||||
[RTS_PIPE] = "pipe",
|
||||
[RTS_BABEL] = "Babel",
|
||||
[RTS_RPKI] = "RPKI",
|
||||
[RTS_PERF] = "Perf",
|
||||
[RTS_AGGREGATED] = "aggregated",
|
||||
};
|
||||
|
||||
static void
|
||||
@ -196,7 +198,7 @@ static struct idm src_ids;
|
||||
#define RSH_KEY(n) n->private_id
|
||||
#define RSH_NEXT(n) n->next
|
||||
#define RSH_EQ(n1,n2) n1 == n2
|
||||
#define RSH_FN(n) u32_hash(n)
|
||||
#define RSH_FN(n) u64_hash(n)
|
||||
|
||||
#define RSH_REHASH rte_src_rehash
|
||||
#define RSH_PARAMS /2, *2, 1, 1, 8, 20
|
||||
@ -255,7 +257,7 @@ rt_get_source_o(struct rte_owner *p, u32 id)
|
||||
|
||||
HASH_INSERT2(p->hash, RSH, rta_pool, src);
|
||||
if (config->table_debug)
|
||||
log(L_TRACE "Allocated new rte_src for %s, ID %uL %uG, have %u sources now",
|
||||
log(L_TRACE "Allocated new rte_src for %s, ID %luL %uG, have %u sources now",
|
||||
p->name, src->private_id, src->global_id, p->uc);
|
||||
|
||||
uint gm = atomic_load_explicit(&rte_src_global_max, memory_order_relaxed);
|
||||
@ -364,6 +366,30 @@ rt_prune_sources(void *data)
|
||||
RTA_UNLOCK;
|
||||
}
|
||||
|
||||
void
|
||||
rt_dump_sources(struct rte_owner *o)
|
||||
{
|
||||
debug("\t%s: hord=%u, uc=%u, cnt=%u prune=%p, stop=%p\n",
|
||||
o->name, o->hash.order, o->uc, o->hash.count, o->prune, o->stop);
|
||||
debug("\tget_route_info=%p, better=%p, mergable=%p, igp_metric=%p, recalculate=%p",
|
||||
o->class->get_route_info, o->class->rte_better, o->class->rte_mergable,
|
||||
o->class->rte_igp_metric, o->rte_recalculate);
|
||||
|
||||
int splitting = 0;
|
||||
HASH_WALK(o->hash, next, src)
|
||||
{
|
||||
debug("%c%c%uL %uG %luU",
|
||||
(splitting % 8) ? ',' : '\n',
|
||||
(splitting % 8) ? ' ' : '\t',
|
||||
src->private_id, src->global_id,
|
||||
atomic_load_explicit(&src->uc, memory_order_relaxed));
|
||||
|
||||
splitting++;
|
||||
}
|
||||
HASH_WALK_END;
|
||||
debug("\n");
|
||||
}
|
||||
|
||||
void
|
||||
rt_init_sources(struct rte_owner *o, const char *name, event_list *list)
|
||||
{
|
||||
@ -573,16 +599,20 @@ static struct idm ea_class_idm;
|
||||
|
||||
/* Config parser lex register function */
|
||||
void ea_lex_register(struct ea_class *def);
|
||||
void ea_lex_unregister(struct ea_class *def);
|
||||
|
||||
static void
|
||||
ea_class_free(struct ea_class *cl)
|
||||
{
|
||||
RTA_LOCK;
|
||||
|
||||
/* No more ea class references. Unregister the attribute. */
|
||||
idm_free(&ea_class_idm, cl->id);
|
||||
ea_class_global[cl->id] = NULL;
|
||||
if (!cl->hidden)
|
||||
ea_lex_unregister(cl);
|
||||
|
||||
/* When we start supporting full protocol removal, we may need to call
|
||||
* ea_lex_unregister(cl), see where ea_lex_register() is called. */
|
||||
|
||||
RTA_UNLOCK;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -619,7 +649,7 @@ ea_class_init(void)
|
||||
sizeof(*ea_class_global) * (ea_class_max = EA_CLASS_INITIAL_MAX));
|
||||
}
|
||||
|
||||
static struct ea_class_ref *
|
||||
struct ea_class_ref *
|
||||
ea_ref_class(pool *p, struct ea_class *def)
|
||||
{
|
||||
def->uc++;
|
||||
@ -640,9 +670,6 @@ ea_register(pool *p, struct ea_class *def)
|
||||
ASSERT_DIE(def->id < ea_class_max);
|
||||
ea_class_global[def->id] = def;
|
||||
|
||||
if (!def->hidden)
|
||||
ea_lex_register(def);
|
||||
|
||||
return ea_ref_class(p, def);
|
||||
}
|
||||
|
||||
@ -680,7 +707,12 @@ ea_register_init(struct ea_class *clp)
|
||||
{
|
||||
RTA_LOCK;
|
||||
ASSERT_DIE(!ea_class_find_by_name(clp->name));
|
||||
ea_register(&root_pool, clp);
|
||||
|
||||
struct ea_class *def = ea_register(&root_pool, clp)->class;
|
||||
|
||||
if (!clp->hidden)
|
||||
ea_lex_register(def);
|
||||
|
||||
RTA_UNLOCK;
|
||||
}
|
||||
|
||||
@ -1340,7 +1372,15 @@ nexthop_dump(const struct adata *ad)
|
||||
|
||||
debug(":");
|
||||
|
||||
NEXTHOP_WALK(nh, nhad)
|
||||
if (!NEXTHOP_IS_REACHABLE(nhad))
|
||||
{
|
||||
const char *name = rta_dest_name(nhad->dest);
|
||||
if (name)
|
||||
debug(" %s", name);
|
||||
else
|
||||
debug(" D%d", nhad->dest);
|
||||
}
|
||||
else NEXTHOP_WALK(nh, nhad)
|
||||
{
|
||||
if (ipa_nonzero(nh->gw)) debug(" ->%I", nh->gw);
|
||||
if (nh->labels) debug(" L %d", nh->label[0]);
|
||||
|
@ -18,7 +18,7 @@
|
||||
#include "nest/bird.h"
|
||||
#include "nest/iface.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
#include "nest/rt-dev.h"
|
||||
#include "conf/conf.h"
|
||||
#include "lib/resource.h"
|
||||
|
@ -55,7 +55,7 @@
|
||||
#undef LOCAL_DEBUG
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
#include "lib/string.h"
|
||||
|
||||
/*
|
||||
|
240
nest/rt-fib_test.c
Normal file
240
nest/rt-fib_test.c
Normal file
@ -0,0 +1,240 @@
|
||||
/*
|
||||
* BIRD -- Forwarding Information Base -- Tests
|
||||
*
|
||||
* (c) 2023 CZ.NIC z.s.p.o.
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#include "test/birdtest.h"
|
||||
#include "test/bt-utils.h"
|
||||
|
||||
#include "nest/route.h"
|
||||
|
||||
|
||||
#define TESTS_NUM 10
|
||||
#define PREFIXES_NUM 400000
|
||||
#define PREFIX_TESTS_NUM 200000
|
||||
#define PREFIX_BENCH_MAX 1000000
|
||||
#define PREFIX_BENCH_NUM 10000000
|
||||
|
||||
struct test_node
|
||||
{
|
||||
int pos;
|
||||
struct fib_node n;
|
||||
};
|
||||
|
||||
static inline int net_match(struct test_node *tn, net_addr *query, net_addr *data)
|
||||
{ return (tn->pos < PREFIXES_NUM) && net_equal(query, &data[tn->pos]); }
|
||||
|
||||
static int
|
||||
t_match_random_net(void)
|
||||
{
|
||||
bt_config_parse(BT_CONFIG_SIMPLE);
|
||||
|
||||
for (int round = 0; round < TESTS_NUM; round++)
|
||||
{
|
||||
int type = !(round & 1) ? NET_IP4 : NET_IP6;
|
||||
|
||||
pool *p = rp_new(&root_pool, the_bird_domain.the_bird, "FIB pool");
|
||||
net_addr *nets = bt_random_nets(type, PREFIXES_NUM);
|
||||
|
||||
/* Make FIB structure */
|
||||
struct fib f;
|
||||
fib_init(&f, &root_pool, type, sizeof(struct test_node), OFFSETOF(struct test_node, n), 4, NULL);
|
||||
|
||||
for (int i = 0; i < PREFIXES_NUM; i++)
|
||||
{
|
||||
struct test_node *tn = fib_get(&f, &nets[i]);
|
||||
bt_assert(!tn->pos || net_match(tn, &nets[i], nets));
|
||||
tn->pos = i;
|
||||
}
|
||||
|
||||
/* Test (mostly) negative matches */
|
||||
for (int i = 0; i < PREFIX_TESTS_NUM; i++)
|
||||
{
|
||||
net_addr net;
|
||||
bt_random_net(&net, type);
|
||||
|
||||
struct test_node *tn = fib_find(&f, &net);
|
||||
bt_assert(!tn || net_match(tn, &net, nets));
|
||||
}
|
||||
|
||||
/* Test positive matches */
|
||||
for (int i = 0; i < PREFIX_TESTS_NUM; i++)
|
||||
{
|
||||
int j = bt_random_n(PREFIXES_NUM);
|
||||
|
||||
struct test_node *tn = fib_find(&f, &nets[j]);
|
||||
bt_assert(tn && net_match(tn, &nets[j], nets));
|
||||
}
|
||||
|
||||
rfree(p);
|
||||
tmp_flush();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
t_fib_walk(void)
|
||||
{
|
||||
bt_config_parse(BT_CONFIG_SIMPLE);
|
||||
|
||||
for (int round = 0; round < TESTS_NUM; round++)
|
||||
{
|
||||
int type = !(round & 1) ? NET_IP4 : NET_IP6;
|
||||
|
||||
pool *p = rp_new(&root_pool, the_bird_domain.the_bird, "FIB pool");
|
||||
net_addr *nets = bt_random_nets(type, PREFIXES_NUM);
|
||||
byte *marks = tmp_allocz(PREFIXES_NUM);
|
||||
|
||||
/* Make FIB structure */
|
||||
struct fib f;
|
||||
fib_init(&f, p, type, sizeof(struct test_node), OFFSETOF(struct test_node, n), 4, NULL);
|
||||
|
||||
for (int i = 1; i < PREFIXES_NUM; i++)
|
||||
{
|
||||
struct test_node *tn = fib_get(&f, &nets[i]);
|
||||
bt_assert(!tn->pos || net_match(tn, &nets[i], nets));
|
||||
if (tn->pos)
|
||||
{
|
||||
/* Mark dupicate nets */
|
||||
bt_assert(!marks[tn->pos]);
|
||||
marks[tn->pos] = 1;
|
||||
}
|
||||
tn->pos = i;
|
||||
}
|
||||
|
||||
/* Walk FIB and mark nets */
|
||||
FIB_WALK(&f, struct test_node, tn)
|
||||
{
|
||||
bt_assert(!marks[tn->pos]);
|
||||
marks[tn->pos] = 1;
|
||||
}
|
||||
FIB_WALK_END;
|
||||
|
||||
/* Check in all nets are marked */
|
||||
for (int i = 1; i < PREFIXES_NUM; i++)
|
||||
bt_assert(marks[i]);
|
||||
|
||||
rfree(p);
|
||||
tmp_flush();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
benchmark_fib_dataset(const char *filename, int type)
|
||||
{
|
||||
net_addr *nets, *test_r, *test_s;
|
||||
uint n = PREFIX_BENCH_MAX;
|
||||
int tn = PREFIX_BENCH_NUM;
|
||||
int match;
|
||||
|
||||
bt_reset_suite_case_timer();
|
||||
bt_log_suite_case_result(1, "Reading %s", filename, n);
|
||||
nets = bt_read_net_file(filename, type, &n);
|
||||
bt_log_suite_case_result(1, "Read net data, %u nets", n);
|
||||
bt_reset_suite_case_timer();
|
||||
|
||||
pool *p = rp_new(&root_pool, the_bird_domain.the_bird, "FIB pool");
|
||||
|
||||
/* Make FIB structure */
|
||||
struct fib f;
|
||||
fib_init(&f, p, type, sizeof(struct test_node), OFFSETOF(struct test_node, n), 0, NULL);
|
||||
|
||||
for (int i = 0; i < (int) n; i++)
|
||||
{
|
||||
struct test_node *tn = fib_get(&f, &nets[i]);
|
||||
tn->pos = i;
|
||||
}
|
||||
|
||||
bt_log_suite_case_result(1, "Fill FIB structure, %u nets, order %u", n, f.hash_order);
|
||||
bt_reset_suite_case_timer();
|
||||
|
||||
/* Compute FIB size */
|
||||
size_t fib_size = rmemsize(p).effective * 1000 / (1024*1024);
|
||||
bt_log_suite_case_result(1, "FIB size: %u.%03u MB", (uint) (fib_size / 1000), (uint) (fib_size % 1000));
|
||||
|
||||
/* Compute FIB histogram */
|
||||
uint hist[16] = {};
|
||||
uint sum = 0;
|
||||
for (uint i = 0; i < f.hash_size; i++)
|
||||
{
|
||||
int len = 0;
|
||||
for (struct fib_node *fn = f.hash_table[i]; fn; fn = fn->next)
|
||||
len++;
|
||||
|
||||
sum += len;
|
||||
len = MIN(len, 15);
|
||||
hist[len]++;
|
||||
}
|
||||
bt_log_suite_case_result(1, "FIB histogram:");
|
||||
for (uint i = 0; i < 16; i++)
|
||||
if (hist[i])
|
||||
bt_log_suite_case_result(1, "%02u: %8u", i, hist[i]);
|
||||
|
||||
uint avg = (sum * 1000) / (f.hash_size - hist[0]);
|
||||
bt_log_suite_case_result(1, "FIB chain length: %u.%03u", (uint) (avg / 1000), (uint) (avg % 1000));
|
||||
bt_reset_suite_case_timer();
|
||||
|
||||
/* Make test data */
|
||||
test_r = bt_random_nets(type, tn);
|
||||
test_s = bt_random_net_subset(nets, n, tn);
|
||||
|
||||
bt_log_suite_case_result(1, "Make test data, 2x %u nets", tn);
|
||||
bt_reset_suite_case_timer();
|
||||
|
||||
/* Test (mostly negative) random matches */
|
||||
match = 0;
|
||||
for (int i = 0; i < tn; i++)
|
||||
if (fib_find(&f, &test_r[i]))
|
||||
match++;
|
||||
|
||||
bt_log_suite_case_result(1, "Random match, %d / %d matches", match, tn);
|
||||
bt_reset_suite_case_timer();
|
||||
|
||||
/* Test (positive) subset matches */
|
||||
match = 0;
|
||||
for (int i = 0; i < tn; i++)
|
||||
if (fib_find(&f, &test_s[i]))
|
||||
match++;
|
||||
|
||||
bt_log_suite_case_result(1, "Subset match, %d / %d matches", match, tn);
|
||||
bt_log_suite_case_result(1, "");
|
||||
bt_reset_suite_case_timer();
|
||||
|
||||
rfree(p);
|
||||
tmp_flush();
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int UNUSED
|
||||
t_bench_fib_datasets(void)
|
||||
{
|
||||
bt_config_parse(BT_CONFIG_SIMPLE);
|
||||
|
||||
/* Specific datasets, not included */
|
||||
benchmark_fib_dataset("fib-data-bgp-v4-1", NET_IP4);
|
||||
benchmark_fib_dataset("fib-data-bgp-v4-10", NET_IP4);
|
||||
benchmark_fib_dataset("fib-data-bgp-v6-1", NET_IP6);
|
||||
benchmark_fib_dataset("fib-data-bgp-v6-10", NET_IP6);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
bt_init(argc, argv);
|
||||
bt_bird_init();
|
||||
|
||||
bt_test_suite(t_match_random_net, "Testing random prefix matching");
|
||||
bt_test_suite(t_fib_walk, "Testing FIB_WALK() on random FIB");
|
||||
|
||||
// bt_test_suite(t_bench_fib_datasets, "Benchmark FIB from datasets by random subset of nets");
|
||||
|
||||
return bt_exit_value();
|
||||
}
|
@ -10,7 +10,7 @@
|
||||
#undef LOCAL_DEBUG
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/cli.h"
|
||||
#include "nest/iface.h"
|
||||
@ -79,7 +79,7 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary
|
||||
if (d->verbose)
|
||||
{
|
||||
ea_show_list(c, a);
|
||||
cli_printf(c, -1008, "\tInternal route handling values: %uL %uG %uS id %u",
|
||||
cli_printf(c, -1008, "\tInternal route handling values: %luL %uG %uS id %u",
|
||||
e->src->private_id, e->src->global_id, e->stale_cycle, e->id);
|
||||
}
|
||||
else if (dest == RTD_UNICAST)
|
||||
|
@ -93,7 +93,7 @@
|
||||
#undef LOCAL_DEBUG
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/iface.h"
|
||||
#include "lib/resource.h"
|
||||
@ -154,7 +154,7 @@ static void rt_delete(void *);
|
||||
static void rt_export_used(struct rt_table_exporter *, const char *, const char *);
|
||||
static void rt_export_cleanup(struct rtable_private *tab);
|
||||
|
||||
static int rte_same(const rte *x, const rte *y);
|
||||
int rte_same(const rte *x, const rte *y);
|
||||
|
||||
const char *rt_import_state_name_array[TIS_MAX] = {
|
||||
[TIS_DOWN] = "DOWN",
|
||||
@ -735,7 +735,7 @@ rte_mergable(const rte *pri, const rte *sec)
|
||||
static void
|
||||
rte_trace(const char *name, const rte *e, int dir, const char *msg)
|
||||
{
|
||||
log(L_TRACE "%s %c %s %N src %uL %uG %uS id %u %s",
|
||||
log(L_TRACE "%s %c %s %N src %luL %uG %uS id %u %s",
|
||||
name, dir, msg, e->net,
|
||||
e->src->private_id, e->src->global_id, e->stale_cycle, e->id,
|
||||
rta_dest_name(rte_dest(e)));
|
||||
@ -1089,7 +1089,6 @@ rt_notify_merged(struct rt_export_request *req, const net_addr *n,
|
||||
const rte **feed, uint count)
|
||||
{
|
||||
struct channel *c = channel_from_export_request(req);
|
||||
|
||||
// struct proto *p = c->proto;
|
||||
|
||||
#if 0 /* TODO: Find whether this check is possible when processing multiple changes at once. */
|
||||
@ -1659,12 +1658,15 @@ rte_validate(struct channel *ch, rte *e)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
int
|
||||
rte_same(const rte *x, const rte *y)
|
||||
{
|
||||
/* rte.flags / rte.pflags are not checked, as they are internal to rtable */
|
||||
return
|
||||
x->attrs == y->attrs &&
|
||||
(x == y) || (
|
||||
(x->attrs == y->attrs) ||
|
||||
((!(x->attrs->flags & EALF_CACHED) || !(y->attrs->flags & EALF_CACHED)) && ea_same(x->attrs, y->attrs))
|
||||
) &&
|
||||
x->src == y->src &&
|
||||
rte_is_filtered(x) == rte_is_filtered(y);
|
||||
}
|
||||
@ -2492,6 +2494,7 @@ rte_dump(struct rte_storage *e)
|
||||
{
|
||||
debug("%-1N ", e->rte.net);
|
||||
debug("PF=%02x ", e->rte.pflags);
|
||||
debug("SRC=%uG ", e->rte.src->global_id);
|
||||
ea_dump(e->rte.attrs);
|
||||
debug("\n");
|
||||
}
|
||||
@ -3434,8 +3437,8 @@ rt_preconfig(struct config *c)
|
||||
{
|
||||
init_list(&c->tables);
|
||||
|
||||
c->def_tables[NET_IP4] = cf_define_symbol(cf_get_symbol("master4"), SYM_TABLE, table, NULL);
|
||||
c->def_tables[NET_IP6] = cf_define_symbol(cf_get_symbol("master6"), SYM_TABLE, table, NULL);
|
||||
c->def_tables[NET_IP4] = cf_define_symbol(c, cf_get_symbol(c, "master4"), SYM_TABLE, table, NULL);
|
||||
c->def_tables[NET_IP6] = cf_define_symbol(c, cf_get_symbol(c, "master6"), SYM_TABLE, table, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
@ -4072,7 +4075,7 @@ rt_new_table(struct symbol *s, uint addr_type)
|
||||
if (s == new_config->def_tables[addr_type])
|
||||
s->table = c;
|
||||
else
|
||||
cf_define_symbol(s, SYM_TABLE, table, c);
|
||||
cf_define_symbol(new_config, s, SYM_TABLE, table, c);
|
||||
|
||||
c->name = s->name;
|
||||
c->addr_type = addr_type;
|
||||
|
1
proto/aggregator/Doc
Normal file
1
proto/aggregator/Doc
Normal file
@ -0,0 +1 @@
|
||||
S aggregator.c
|
6
proto/aggregator/Makefile
Normal file
6
proto/aggregator/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
src := aggregator.c
|
||||
obj := $(src-o-files)
|
||||
$(all-daemon)
|
||||
$(cf-local)
|
||||
|
||||
tests_objs := $(tests_objs) $(src-o-files)
|
473
proto/aggregator/aggregator.c
Normal file
473
proto/aggregator/aggregator.c
Normal file
@ -0,0 +1,473 @@
|
||||
/*
|
||||
* BIRD Internet Routing Daemon -- Route aggregation
|
||||
*
|
||||
* (c) 2023--2023 Igor Putovny <igor.putovny@nic.cz>
|
||||
* (c) 2023 CZ.NIC, z.s.p.o.
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
/**
|
||||
* DOC: Route aggregation
|
||||
*
|
||||
* This is an implementation of route aggregation functionality.
|
||||
* It enables user to specify a set of route attributes in the configuarion file
|
||||
* and then, for a given destination (net), aggregate routes with the same
|
||||
* values of these attributes into a single multi-path route.
|
||||
*
|
||||
* Structure &channel contains pointer to aggregation list which is represented
|
||||
* by &aggr_list_linearized. In rt_notify_aggregated(), attributes from this
|
||||
* list are evaluated for every route of a given net and results are stored
|
||||
* in &rte_val_list which contains pointer to this route and array of &f_val.
|
||||
* Array of pointers to &rte_val_list entries is sorted using
|
||||
* sort_rte_val_list(). For comparison of &f_val structures, val_compare()
|
||||
* is used. Comparator function is written so that sorting is stable. If all
|
||||
* attributes have the same values, routes are compared by their global IDs.
|
||||
*
|
||||
* After sorting, &rte_val_list entries containing equivalent routes will be
|
||||
* adjacent to each other. Function process_rte_list() iterates through these
|
||||
* entries to identify sequences of equivalent routes. New route will be
|
||||
* created for each such sequence, even if only from a single route.
|
||||
* Only attributes from the aggreagation list will be set for the new route.
|
||||
* New &rta is created and prepare_rta() is used to copy static and dynamic
|
||||
* attributes to new &rta from &rta of the original route. New route is created
|
||||
* by create_merged_rte() from new &rta and exported to the routing table.
|
||||
*/
|
||||
|
||||
#undef LOCAL_DEBUG
|
||||
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "nest/iface.h"
|
||||
#include "filter/filter.h"
|
||||
#include "aggregator.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
* Compare list of &f_val entries.
|
||||
* @count: number of &f_val entries
|
||||
*/
|
||||
static int
|
||||
same_val_list(const struct f_val *v1, const struct f_val *v2, uint len)
|
||||
{
|
||||
for (uint i = 0; i < len; i++)
|
||||
if (!val_same(&v1[i], &v2[i]))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create and export new merged route.
|
||||
* @old: first route in a sequence of equivalent routes that are to be merged
|
||||
* @rte_val: first element in a sequence of equivalent rte_val_list entries
|
||||
* @length: number of equivalent routes that are to be merged (at least 1)
|
||||
* @ail: aggregation list
|
||||
*/
|
||||
static void
|
||||
aggregator_bucket_update(struct aggregator_proto *p, struct aggregator_bucket *bucket, const net_addr *net)
|
||||
{
|
||||
/* Empty bucket */
|
||||
if (!bucket->rte)
|
||||
{
|
||||
rte_update(p->dst, net, NULL, bucket->last_src);
|
||||
rt_unlock_source(bucket->last_src);
|
||||
bucket->last_src = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Store TMP linpool state */
|
||||
struct lp_state *tmp_state = lp_save(tmp_linpool);
|
||||
|
||||
/* Allocate route */
|
||||
struct rte new = { .net = net, .src = bucket->rte->rte.src };
|
||||
ea_set_attr(&new.attrs, EA_LITERAL_EMBEDDED(&ea_gen_source, 0, RTS_AGGREGATED));
|
||||
|
||||
if (net_type_match(net, NB_DEST))
|
||||
ea_set_dest(&new.attrs, 0, RTD_UNREACHABLE);
|
||||
|
||||
/* Seed the attributes from aggregator rule */
|
||||
f_eval_rte(p->premerge, &new, p->aggr_on_count, bucket->aggr_data, 0, NULL);
|
||||
|
||||
/*
|
||||
log("=============== CREATE MERGED ROUTE ===============");
|
||||
log("New route created: id = %d, protocol: %s", new->src->global_id, new->src->proto->name);
|
||||
log("===================================================");
|
||||
*/
|
||||
|
||||
/* merge filter needs one argument called "routes" */
|
||||
struct f_val val = {
|
||||
.type = T_ROUTES_BLOCK,
|
||||
.val.rte_block = {},
|
||||
};
|
||||
|
||||
for (struct aggregator_route *rte = bucket->rte; rte; rte = rte->next_rte)
|
||||
val.val.rte_block.len++;
|
||||
|
||||
val.val.rte_block.rte = tmp_alloc(sizeof(struct rte *) * val.val.rte_block.len);
|
||||
{
|
||||
uint i = 0;
|
||||
for (struct aggregator_route *rte = bucket->rte; rte; rte = rte->next_rte)
|
||||
val.val.rte_block.rte[i++] = &rte->rte;
|
||||
ASSERT_DIE(i == val.val.rte_block.len);
|
||||
}
|
||||
|
||||
/* Actually run the merge rule */
|
||||
enum filter_return fret = f_eval_rte(p->merge_by, &new, 1, &val, 0, NULL);
|
||||
|
||||
/* Finally import the route */
|
||||
switch (fret)
|
||||
{
|
||||
/* Pass the route to the protocol */
|
||||
case F_ACCEPT:
|
||||
rte_update(p->dst, net, &new, bucket->last_src ?: new.src);
|
||||
break;
|
||||
|
||||
/* Something bad happened */
|
||||
default:
|
||||
ASSERT_DIE(fret == F_ERROR);
|
||||
/* fall through */
|
||||
|
||||
/* We actually don't want this route */
|
||||
case F_REJECT:
|
||||
if (bucket->last_src)
|
||||
rte_update(p->dst, net, NULL, bucket->last_src);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Switch source lock for bucket->last_src */
|
||||
if (bucket->last_src != new.src)
|
||||
{
|
||||
if (new.src)
|
||||
rt_lock_source(new.src);
|
||||
if (bucket->last_src)
|
||||
rt_unlock_source(bucket->last_src);
|
||||
|
||||
bucket->last_src = new.src;
|
||||
}
|
||||
|
||||
lp_restore(tmp_linpool, tmp_state);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reload all the buckets on reconfiguration if merge filter has changed.
|
||||
* TODO: make this splitted
|
||||
*/
|
||||
static void
|
||||
aggregator_reload_buckets(void *data)
|
||||
{
|
||||
struct aggregator_proto *p = data;
|
||||
|
||||
HASH_WALK(p->buckets, next_hash, b)
|
||||
if (b->rte)
|
||||
aggregator_bucket_update(p, b, b->rte->rte.net);
|
||||
HASH_WALK_END;
|
||||
}
|
||||
|
||||
static inline u32 aggr_route_hash(const rte *e)
|
||||
{
|
||||
struct {
|
||||
const net_addr *net; /* the net_addr pointer is stable as long as any route exists for it in the source table */
|
||||
struct rte_src *src;
|
||||
} obj = {
|
||||
.net = e->net,
|
||||
.src = e->src,
|
||||
};
|
||||
|
||||
return mem_hash(&obj, sizeof obj);
|
||||
}
|
||||
|
||||
#define AGGR_RTE_KEY(n) (&(n)->rte)
|
||||
#define AGGR_RTE_NEXT(n) ((n)->next_hash)
|
||||
#define AGGR_RTE_EQ(a,b) (((a)->src == (b)->src) && ((a)->net == (b)->net))
|
||||
#define AGGR_RTE_FN(_n) aggr_route_hash(_n)
|
||||
#define AGGR_RTE_ORDER 4 /* Initial */
|
||||
|
||||
#define AGGR_RTE_REHASH aggr_rte_rehash
|
||||
#define AGGR_RTE_PARAMS /8, *2, 2, 2, 4, 24
|
||||
|
||||
HASH_DEFINE_REHASH_FN(AGGR_RTE, struct aggregator_route);
|
||||
|
||||
|
||||
#define AGGR_BUCK_KEY(n) (n)
|
||||
#define AGGR_BUCK_NEXT(n) ((n)->next_hash)
|
||||
#define AGGR_BUCK_EQ(a,b) (((a)->hash == (b)->hash) && (same_val_list((a)->aggr_data, (b)->aggr_data, p->aggr_on_count)))
|
||||
#define AGGR_BUCK_FN(n) ((n)->hash)
|
||||
#define AGGR_BUCK_ORDER 4 /* Initial */
|
||||
|
||||
#define AGGR_BUCK_REHASH aggr_buck_rehash
|
||||
#define AGGR_BUCK_PARAMS /8, *2, 2, 2, 4, 24
|
||||
|
||||
HASH_DEFINE_REHASH_FN(AGGR_BUCK, struct aggregator_bucket);
|
||||
|
||||
|
||||
#define AGGR_DATA_MEMSIZE (sizeof(struct f_val) * p->aggr_on_count)
|
||||
|
||||
static void
|
||||
aggregator_rt_notify(struct proto *P, struct channel *src_ch, const net_addr *net, rte *new, const rte *old)
|
||||
{
|
||||
struct aggregator_proto *p = SKIP_BACK(struct aggregator_proto, p, P);
|
||||
ASSERT_DIE(src_ch == p->src);
|
||||
struct aggregator_bucket *new_bucket = NULL, *old_bucket = NULL;
|
||||
struct aggregator_route *old_route = NULL;
|
||||
|
||||
/* Find the objects for the old route */
|
||||
if (old)
|
||||
old_route = HASH_FIND(p->routes, AGGR_RTE, old);
|
||||
|
||||
if (old_route)
|
||||
old_bucket = old_route->bucket;
|
||||
|
||||
/* Find the bucket for the new route */
|
||||
if (new)
|
||||
{
|
||||
/* Routes are identical, do nothing */
|
||||
if (old_route && rte_same(&old_route->rte, new))
|
||||
return;
|
||||
|
||||
/* Evaluate route attributes. */
|
||||
struct aggregator_bucket *tmp_bucket = sl_allocz(p->bucket_slab);
|
||||
struct lp_state *tmp_state = lp_save(tmp_linpool);
|
||||
|
||||
struct ea_list *oa = new->attrs;
|
||||
enum filter_return fret = f_eval_rte(p->aggr_on, new, 0, NULL, p->aggr_on_count, tmp_bucket->aggr_data);
|
||||
|
||||
if (new->attrs != oa)
|
||||
log(L_WARN "Aggregator rule modifies the route");
|
||||
|
||||
/* Check filter return value */
|
||||
if (fret > F_RETURN)
|
||||
{
|
||||
sl_free(tmp_bucket);
|
||||
lp_restore(tmp_linpool, tmp_state);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Compute the hash */
|
||||
u64 haux;
|
||||
mem_hash_init(&haux);
|
||||
for (uint i = 0; i < p->aggr_on_count; i++)
|
||||
mem_hash_mix_f_val(&haux, &tmp_bucket->aggr_data[i]);
|
||||
tmp_bucket->hash = mem_hash_value(&haux);
|
||||
|
||||
/* Find the existing bucket */
|
||||
if (new_bucket = HASH_FIND(p->buckets, AGGR_BUCK, tmp_bucket))
|
||||
sl_free(tmp_bucket);
|
||||
else
|
||||
{
|
||||
new_bucket = tmp_bucket;
|
||||
HASH_INSERT2(p->buckets, AGGR_BUCK, p->p.pool, new_bucket);
|
||||
}
|
||||
|
||||
/* Store the route attributes */
|
||||
if (rta_is_cached(new->attrs))
|
||||
rta_clone(new->attrs);
|
||||
else
|
||||
new->attrs = rta_lookup(new->attrs, 0);
|
||||
|
||||
/* Insert the new route into the bucket */
|
||||
struct aggregator_route *arte = sl_alloc(p->route_slab);
|
||||
*arte = (struct aggregator_route) {
|
||||
.bucket = new_bucket,
|
||||
.rte = *new,
|
||||
.next_rte = new_bucket->rte,
|
||||
};
|
||||
new_bucket->rte = arte;
|
||||
new_bucket->count++;
|
||||
HASH_INSERT2(p->routes, AGGR_RTE, p->p.pool, arte);
|
||||
|
||||
lp_restore(tmp_linpool, tmp_state);
|
||||
}
|
||||
|
||||
/* Remove the old route from its bucket */
|
||||
if (old_bucket)
|
||||
{
|
||||
for (struct aggregator_route **k = &old_bucket->rte; *k; k = &(*k)->next_rte)
|
||||
if (*k == old_route)
|
||||
{
|
||||
*k = (*k)->next_rte;
|
||||
break;
|
||||
}
|
||||
|
||||
old_bucket->count--;
|
||||
HASH_REMOVE2(p->routes, AGGR_RTE, p->p.pool, old_route);
|
||||
rta_free(old_route->rte.attrs);
|
||||
sl_free(old_route);
|
||||
}
|
||||
|
||||
/* Announce changes */
|
||||
if (old_bucket)
|
||||
aggregator_bucket_update(p, old_bucket, net);
|
||||
|
||||
if (new_bucket && (new_bucket != old_bucket))
|
||||
aggregator_bucket_update(p, new_bucket, net);
|
||||
|
||||
/* Cleanup the old bucket if empty */
|
||||
if (old_bucket && (!old_bucket->rte || !old_bucket->count))
|
||||
{
|
||||
ASSERT_DIE(!old_bucket->rte && !old_bucket->count);
|
||||
HASH_REMOVE2(p->buckets, AGGR_BUCK, p->p.pool, old_bucket);
|
||||
sl_free(old_bucket);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
aggregator_preexport(struct channel *C, struct rte *new)
|
||||
{
|
||||
struct aggregator_proto *p = SKIP_BACK(struct aggregator_proto, p, C->proto);
|
||||
/* Reject our own routes */
|
||||
if (new->sender == p->dst->in_req.hook)
|
||||
return -1;
|
||||
|
||||
/* Disallow aggregating already aggregated routes */
|
||||
if (ea_get_int(new->attrs, &ea_gen_source, 0) == RTS_AGGREGATED)
|
||||
{
|
||||
log(L_ERR "Multiple aggregations of the same route not supported.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
aggregator_postconfig(struct proto_config *CF)
|
||||
{
|
||||
struct aggregator_config *cf = SKIP_BACK(struct aggregator_config, c, CF);
|
||||
|
||||
if (!cf->dst->table)
|
||||
cf_error("Source table not specified");
|
||||
|
||||
if (!cf->src->table)
|
||||
cf_error("Destination table not specified");
|
||||
|
||||
if (cf->dst->table->addr_type != cf->src->table->addr_type)
|
||||
cf_error("Both tables must be of the same type");
|
||||
|
||||
cf->dst->in_filter = cf->src->in_filter;
|
||||
|
||||
cf->src->in_filter = FILTER_REJECT;
|
||||
cf->dst->out_filter = FILTER_REJECT;
|
||||
|
||||
cf->dst->debug = cf->src->debug;
|
||||
}
|
||||
|
||||
static struct proto *
|
||||
aggregator_init(struct proto_config *CF)
|
||||
{
|
||||
struct proto *P = proto_new(CF);
|
||||
struct aggregator_proto *p = SKIP_BACK(struct aggregator_proto, p, P);
|
||||
struct aggregator_config *cf = SKIP_BACK(struct aggregator_config, c, CF);
|
||||
|
||||
proto_configure_channel(P, &p->src, cf->src);
|
||||
proto_configure_channel(P, &p->dst, cf->dst);
|
||||
|
||||
p->aggr_on_count = cf->aggr_on_count;
|
||||
p->aggr_on = cf->aggr_on;
|
||||
p->premerge = cf->premerge;
|
||||
p->merge_by = cf->merge_by;
|
||||
|
||||
P->rt_notify = aggregator_rt_notify;
|
||||
P->preexport = aggregator_preexport;
|
||||
|
||||
return P;
|
||||
}
|
||||
|
||||
static int
|
||||
aggregator_start(struct proto *P)
|
||||
{
|
||||
struct aggregator_proto *p = SKIP_BACK(struct aggregator_proto, p, P);
|
||||
|
||||
p->bucket_slab = sl_new(P->pool, sizeof(struct aggregator_bucket) + AGGR_DATA_MEMSIZE);
|
||||
HASH_INIT(p->buckets, P->pool, AGGR_BUCK_ORDER);
|
||||
|
||||
p->route_slab = sl_new(P->pool, sizeof(struct aggregator_route));
|
||||
HASH_INIT(p->routes, P->pool, AGGR_RTE_ORDER);
|
||||
|
||||
p->reload_buckets = (event) {
|
||||
.hook = aggregator_reload_buckets,
|
||||
.data = p,
|
||||
};
|
||||
|
||||
return PS_UP;
|
||||
}
|
||||
|
||||
static int
|
||||
aggregator_shutdown(struct proto *P)
|
||||
{
|
||||
struct aggregator_proto *p = SKIP_BACK(struct aggregator_proto, p, P);
|
||||
|
||||
HASH_WALK_DELSAFE(p->buckets, next_hash, b)
|
||||
{
|
||||
for (struct aggregator_route *arte; arte = b->rte; )
|
||||
{
|
||||
b->rte = arte->next_rte;
|
||||
b->count--;
|
||||
HASH_REMOVE(p->routes, AGGR_RTE, arte);
|
||||
rta_free(arte->rte.attrs);
|
||||
sl_free(arte);
|
||||
}
|
||||
|
||||
if (b->last_src)
|
||||
rt_unlock_source(b->last_src);
|
||||
|
||||
ASSERT_DIE(b->count == 0);
|
||||
HASH_REMOVE(p->buckets, AGGR_BUCK, b);
|
||||
sl_free(b);
|
||||
}
|
||||
HASH_WALK_END;
|
||||
|
||||
return PS_DOWN;
|
||||
}
|
||||
|
||||
static int
|
||||
aggregator_reconfigure(struct proto *P, struct proto_config *CF)
|
||||
{
|
||||
struct aggregator_proto *p = SKIP_BACK(struct aggregator_proto, p, P);
|
||||
struct aggregator_config *cf = SKIP_BACK(struct aggregator_config, c, CF);
|
||||
|
||||
TRACE(D_EVENTS, "Reconfiguring");
|
||||
|
||||
/* Compare numeric values (shortcut) */
|
||||
if (cf->aggr_on_count != p->aggr_on_count)
|
||||
return 0;
|
||||
|
||||
/* Compare aggregator rule */
|
||||
if (!f_same(cf->aggr_on, p->aggr_on) || !f_same(cf->premerge, p->premerge))
|
||||
return 0;
|
||||
|
||||
/* Compare merge filter */
|
||||
if (!f_same(cf->merge_by, p->merge_by))
|
||||
ev_schedule(&p->reload_buckets);
|
||||
|
||||
p->aggr_on = cf->aggr_on;
|
||||
p->premerge = cf->premerge;
|
||||
p->merge_by = cf->merge_by;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct protocol proto_aggregator = {
|
||||
.name = "Aggregator",
|
||||
.template = "aggregator%d",
|
||||
.preference = 1,
|
||||
.channel_mask = NB_ANY,
|
||||
.proto_size = sizeof(struct aggregator_proto),
|
||||
.config_size = sizeof(struct aggregator_config),
|
||||
.startup = PROTOCOL_STARTUP_CONNECTOR,
|
||||
.postconfig = aggregator_postconfig,
|
||||
.init = aggregator_init,
|
||||
.start = aggregator_start,
|
||||
.shutdown = aggregator_shutdown,
|
||||
.reconfigure = aggregator_reconfigure,
|
||||
};
|
||||
|
||||
void
|
||||
aggregator_build(void)
|
||||
{
|
||||
proto_build(&proto_aggregator);
|
||||
}
|
69
proto/aggregator/aggregator.h
Normal file
69
proto/aggregator/aggregator.h
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* BIRD -- Aggregator Pseudoprotocol
|
||||
*
|
||||
* (c) 2023 Igor Putovny <igor.putovny@nic.cz>
|
||||
* (c) 2023 Maria Matejka <mq@ucw.cz>
|
||||
* (c) 2023 CZ.NIC z.s.p.o.
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*
|
||||
* This file contains the data structures used by Babel.
|
||||
*/
|
||||
|
||||
#ifndef _BIRD_AGGREGATOR_H_
|
||||
#define _BIRD_AGGREGATOR_H_
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "lib/hash.h"
|
||||
|
||||
struct aggregator_config {
|
||||
struct proto_config c;
|
||||
struct channel_config *src, *dst;
|
||||
const struct f_line *aggr_on;
|
||||
const struct f_line *premerge;
|
||||
const struct f_line *merge_by;
|
||||
uint aggr_on_count;
|
||||
u8 aggr_on_net;
|
||||
};
|
||||
|
||||
struct aggregator_route {
|
||||
struct aggregator_route *next_hash;
|
||||
struct aggregator_route *next_rte;
|
||||
struct aggregator_bucket *bucket;
|
||||
struct rte rte;
|
||||
};
|
||||
|
||||
struct aggregator_bucket {
|
||||
struct aggregator_bucket *next_hash;
|
||||
struct aggregator_route *rte; /* Pointer to struct aggregator_route.rte */
|
||||
struct rte_src *last_src; /* Which src we announced the bucket last with */
|
||||
u32 count;
|
||||
u32 hash;
|
||||
struct f_val aggr_data[0];
|
||||
};
|
||||
|
||||
struct aggregator_proto {
|
||||
struct proto p;
|
||||
struct channel *src, *dst;
|
||||
|
||||
/* Buckets by aggregator rule */
|
||||
HASH(struct aggregator_bucket) buckets;
|
||||
slab *bucket_slab;
|
||||
|
||||
/* Routes by net and src */
|
||||
HASH(struct aggregator_route) routes;
|
||||
slab *route_slab;
|
||||
|
||||
/* Aggregator rule */
|
||||
const struct f_line *aggr_on;
|
||||
uint aggr_on_count;
|
||||
u8 aggr_on_net;
|
||||
|
||||
/* Merge filter */
|
||||
const struct f_line *premerge;
|
||||
const struct f_line *merge_by;
|
||||
event reload_buckets;
|
||||
};
|
||||
|
||||
#endif
|
151
proto/aggregator/config.Y
Normal file
151
proto/aggregator/config.Y
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* BIRD -- Aggregator configuration
|
||||
*
|
||||
* (c) 2023 Igor Putovny <igor.putovny@nic.cz>
|
||||
* (c) 2023 Maria Matejka <mq@ucw.cz>
|
||||
* (c) 2023 CZ.NIC z.s.p.o.
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
CF_HDR
|
||||
|
||||
#include "proto/aggregator/aggregator.h"
|
||||
|
||||
CF_DEFINES
|
||||
|
||||
#define AGGREGATOR_CFG ((struct aggregator_config *) this_proto)
|
||||
#define AGGR_ITEM_ALLOC ((struct aggr_item_node *) cfg_allocz(sizeof(struct aggr_item_node)))
|
||||
|
||||
CF_DECLS
|
||||
|
||||
CF_KEYWORDS(AGGREGATOR, AGGREGATE, ON, MERGE, BY)
|
||||
|
||||
%type <xp> aggr_item aggr_list
|
||||
|
||||
CF_GRAMMAR
|
||||
|
||||
proto: aggregator_proto ;
|
||||
|
||||
aggregator_proto_start: proto_start AGGREGATOR
|
||||
{
|
||||
this_proto = proto_config_new(&proto_aggregator, $1);
|
||||
this_channel = AGGREGATOR_CFG->src = channel_config_new(NULL, "source", 0, this_proto);
|
||||
AGGREGATOR_CFG->dst = channel_config_new(NULL, "destination", 0, this_proto);
|
||||
|
||||
AGGREGATOR_CFG->src->ra_mode = AGGREGATOR_CFG->dst->ra_mode = RA_ANY;
|
||||
};
|
||||
|
||||
aggregator_proto_item:
|
||||
proto_item
|
||||
| channel_item_
|
||||
| PEER TABLE rtable { AGGREGATOR_CFG->dst->table = $3; }
|
||||
| AGGREGATE ON {
|
||||
if (AGGREGATOR_CFG->aggr_on)
|
||||
cf_error("Only one aggregate on clause allowed");
|
||||
|
||||
cf_enter_filters();
|
||||
cf_push_block_scope(new_config);
|
||||
} aggr_list {
|
||||
int count = new_config->current_scope->slots;
|
||||
cf_pop_block_scope(new_config);
|
||||
cf_exit_filters();
|
||||
|
||||
if (!AGGREGATOR_CFG->aggr_on_net)
|
||||
cf_error("aggregate on must be always include 'net'.");
|
||||
|
||||
struct f_inst *rot = NULL;
|
||||
while ($4.begin)
|
||||
{
|
||||
struct f_inst *tmp = $4.begin->next;
|
||||
$4.begin->next = rot;
|
||||
rot = $4.begin;
|
||||
$4.begin = tmp;
|
||||
}
|
||||
|
||||
AGGREGATOR_CFG->aggr_on_count = count;
|
||||
AGGREGATOR_CFG->aggr_on = f_linearize(rot, count);
|
||||
|
||||
struct f_line *premerge = f_linearize($4.end, 0);
|
||||
premerge->args = count;
|
||||
AGGREGATOR_CFG->premerge = premerge;
|
||||
}
|
||||
| MERGE BY {
|
||||
cf_enter_filters();
|
||||
cf_push_block_scope(new_config);
|
||||
f_predefined_variable(new_config, "routes", T_ROUTES_BLOCK);
|
||||
} function_body {
|
||||
cf_pop_block_scope(new_config);
|
||||
cf_exit_filters();
|
||||
$4->args++;
|
||||
AGGREGATOR_CFG->merge_by = $4;
|
||||
}
|
||||
;
|
||||
|
||||
aggregator_proto_opts: /* empty */ | aggregator_proto_opts aggregator_proto_item ';' ;
|
||||
aggregator_proto: aggregator_proto_start proto_name '{' aggregator_proto_opts '}' ;
|
||||
|
||||
|
||||
aggr_list:
|
||||
aggr_item
|
||||
| aggr_list ',' aggr_item {
|
||||
if ($$.begin = $3.begin)
|
||||
$$.begin->next = $1.begin;
|
||||
else
|
||||
$$.begin = $1.begin;
|
||||
|
||||
if ($$.end = $3.end)
|
||||
$$.end->next = $1.end;
|
||||
else
|
||||
$$.end = $1.end;
|
||||
}
|
||||
;
|
||||
|
||||
aggr_item:
|
||||
'(' term ')' {
|
||||
switch ($2->type) {
|
||||
case T_INT:
|
||||
case T_BOOL:
|
||||
case T_PAIR:
|
||||
case T_QUAD:
|
||||
case T_ENUM:
|
||||
case T_IP:
|
||||
case T_EC:
|
||||
case T_LC:
|
||||
case T_RD:
|
||||
/* Fits, OK */
|
||||
break;
|
||||
|
||||
default:
|
||||
cf_error("Expression evaluated to type %s unsupported by aggregator. Store this value as a custom attribute instead", f_type_name($2->type));
|
||||
}
|
||||
|
||||
$$.begin = $2;
|
||||
$$.end = NULL;
|
||||
f_new_var(new_config->current_scope);
|
||||
}
|
||||
| lvalue {
|
||||
$$.begin = f_lval_getter(&$1);
|
||||
int vari = f_new_var(new_config->current_scope);
|
||||
|
||||
if ($1.type == F_LVAL_SA && $1.sa.sa_code == SA_NET)
|
||||
AGGREGATOR_CFG->aggr_on_net = 1;
|
||||
if (($1.type == F_LVAL_CONSTANT) ||
|
||||
($1.type == F_LVAL_SA && $1.sa.readonly))
|
||||
$$.end = NULL;
|
||||
else
|
||||
{
|
||||
char varname[12];
|
||||
bsnprintf(varname, 12, "!aggr%d", vari);
|
||||
$$.end = f_lval_setter(&$1,
|
||||
f_new_inst(FI_VAR_GET, cf_define_symbol(
|
||||
new_config, cf_get_symbol(new_config, varname),
|
||||
SYM_VARIABLE | $$.begin->type, offset, vari
|
||||
)));
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
CF_CODE
|
||||
|
||||
CF_END
|
132
proto/aggregator/test.conf
Normal file
132
proto/aggregator/test.conf
Normal file
@ -0,0 +1,132 @@
|
||||
log "bird.log" all;
|
||||
|
||||
attribute int foo;
|
||||
|
||||
protocol device {}
|
||||
|
||||
protocol static {
|
||||
ipv6 {
|
||||
import filter {
|
||||
preference = 111;
|
||||
foo = 666;
|
||||
accept;
|
||||
};
|
||||
};
|
||||
route 2001:db8:0::/48 unreachable { bgp_path.prepend(65432); bgp_path.prepend(4200000000); };
|
||||
route 2001:db8:1::/48 unreachable;
|
||||
route 2001:db8:2::/48 unreachable;
|
||||
route 2001:db8:3::/48 unreachable;
|
||||
route 2001:db8:4::/48 unreachable;
|
||||
route 2001:db8:5::/48 unreachable;
|
||||
route 2001:db8:6::/48 unreachable;
|
||||
route 2001:db8:7::/48 unreachable;
|
||||
route 2001:db8:8::/48 unreachable;
|
||||
route 2001:db8:9::/48 unreachable;
|
||||
route 2001:db8:a::/48 unreachable;
|
||||
route 2001:db8:b::/48 unreachable;
|
||||
route 2001:db8:c::/48 unreachable;
|
||||
route 2001:db8:d::/48 unreachable;
|
||||
route 2001:db8:e::/48 unreachable;
|
||||
route 2001:db8:f::/48 unreachable;
|
||||
}
|
||||
|
||||
protocol static {
|
||||
ipv6 {
|
||||
import filter {
|
||||
preference = 222;
|
||||
foo = 666;
|
||||
bgp_med = 1;
|
||||
bgp_community = -empty-.add((65533,1)).add((65500,0xe));
|
||||
accept;
|
||||
};
|
||||
};
|
||||
route 2001:db8:1::/48 unreachable;
|
||||
route 2001:db8:3::/48 unreachable;
|
||||
route 2001:db8:5::/48 unreachable;
|
||||
route 2001:db8:7::/48 unreachable;
|
||||
route 2001:db8:9::/48 unreachable;
|
||||
route 2001:db8:b::/48 unreachable;
|
||||
route 2001:db8:d::/48 unreachable;
|
||||
route 2001:db8:f::/48 unreachable;
|
||||
}
|
||||
|
||||
protocol static {
|
||||
ipv6 {
|
||||
import filter {
|
||||
preference = 222;
|
||||
foo = 666;
|
||||
bgp_med = 2;
|
||||
bgp_community = -empty-.add((65533,2)).add((65500,0xd));
|
||||
accept;
|
||||
};
|
||||
};
|
||||
route 2001:db8:2::/48 unreachable;
|
||||
route 2001:db8:3::/48 unreachable;
|
||||
route 2001:db8:6::/48 unreachable;
|
||||
route 2001:db8:7::/48 unreachable;
|
||||
route 2001:db8:a::/48 unreachable;
|
||||
route 2001:db8:b::/48 unreachable;
|
||||
route 2001:db8:e::/48 unreachable;
|
||||
route 2001:db8:f::/48 unreachable;
|
||||
}
|
||||
|
||||
protocol static {
|
||||
ipv6 {
|
||||
import filter {
|
||||
preference = 222;
|
||||
foo = 666;
|
||||
bgp_med = 4;
|
||||
bgp_community = -empty-.add((65533,4)).add((65500,0xb));
|
||||
accept;
|
||||
};
|
||||
};
|
||||
route 2001:db8:4::/48 unreachable;
|
||||
route 2001:db8:5::/48 unreachable;
|
||||
route 2001:db8:6::/48 unreachable;
|
||||
route 2001:db8:7::/48 unreachable;
|
||||
route 2001:db8:c::/48 unreachable;
|
||||
route 2001:db8:d::/48 unreachable;
|
||||
route 2001:db8:e::/48 unreachable;
|
||||
route 2001:db8:f::/48 unreachable;
|
||||
}
|
||||
|
||||
protocol static {
|
||||
ipv6 {
|
||||
import filter {
|
||||
preference = 222;
|
||||
foo = 666;
|
||||
bgp_med = 8;
|
||||
bgp_community = -empty-.add((65533,8)).add((65500,0x7));
|
||||
accept;
|
||||
};
|
||||
};
|
||||
route 2001:db8:8::/48 unreachable;
|
||||
route 2001:db8:9::/48 unreachable;
|
||||
route 2001:db8:a::/48 unreachable;
|
||||
route 2001:db8:b::/48 unreachable;
|
||||
route 2001:db8:c::/48 unreachable;
|
||||
route 2001:db8:d::/48 unreachable;
|
||||
route 2001:db8:e::/48 unreachable;
|
||||
route 2001:db8:f::/48 unreachable;
|
||||
}
|
||||
|
||||
ipv6 table agr_result;
|
||||
|
||||
protocol aggregator {
|
||||
table master6;
|
||||
peer table agr_result;
|
||||
export all;
|
||||
aggregate on net,(defined(bgp_med)), (1 + 3 + 5 + 7), preference, dest;
|
||||
merge by {
|
||||
print "Merging all these: ", routes;
|
||||
bgp_med = 0;
|
||||
for route r in routes do {
|
||||
if ! defined(r.bgp_med) then { unset(bgp_med); accept; }
|
||||
|
||||
print r, " bgp_med: ", r.bgp_med;
|
||||
bgp_med = bgp_med + r.bgp_med;
|
||||
bgp_community = bgp_community.add(r.bgp_community);
|
||||
}
|
||||
accept;
|
||||
};
|
||||
}
|
@ -599,6 +599,7 @@ babel_update_cost(struct babel_neighbor *nbr)
|
||||
switch (cf->type)
|
||||
{
|
||||
case BABEL_IFACE_TYPE_WIRED:
|
||||
case BABEL_IFACE_TYPE_TUNNEL:
|
||||
/* k-out-of-j selection - Appendix 2.1 in the RFC. */
|
||||
|
||||
/* Link is bad if less than cf->limit/16 of expected hellos were received */
|
||||
@ -627,6 +628,24 @@ babel_update_cost(struct babel_neighbor *nbr)
|
||||
break;
|
||||
}
|
||||
|
||||
if (cf->rtt_cost && nbr->srtt > cf->rtt_min)
|
||||
{
|
||||
uint rtt_cost = cf->rtt_cost;
|
||||
|
||||
if (nbr->srtt < cf->rtt_max)
|
||||
{
|
||||
uint rtt_interval = cf->rtt_max TO_US - cf->rtt_min TO_US;
|
||||
uint rtt_diff = (nbr->srtt TO_US - cf->rtt_min TO_US);
|
||||
|
||||
rtt_cost = (rtt_cost * rtt_diff) / rtt_interval;
|
||||
}
|
||||
|
||||
txcost = MIN(txcost + rtt_cost, BABEL_INFINITY);
|
||||
|
||||
TRACE(D_EVENTS, "Added RTT cost %u to nbr %I on %s with srtt %t ms",
|
||||
rtt_cost, nbr->addr, nbr->ifa->iface->name, nbr->srtt * 1000);
|
||||
}
|
||||
|
||||
done:
|
||||
/* If RX cost changed, send IHU with next Hello */
|
||||
if (rxcost != nbr->rxcost)
|
||||
@ -853,6 +872,12 @@ babel_build_ihu(union babel_msg *msg, struct babel_iface *ifa, struct babel_neig
|
||||
msg->ihu.rxcost = n->rxcost;
|
||||
msg->ihu.interval = ifa->cf->ihu_interval;
|
||||
|
||||
if (n->last_tstamp_rcvd && ifa->cf->rtt_send)
|
||||
{
|
||||
msg->ihu.tstamp = n->last_tstamp;
|
||||
msg->ihu.tstamp_rcvd = n->last_tstamp_rcvd TO_US;
|
||||
}
|
||||
|
||||
TRACE(D_PACKETS, "Sending IHU for %I with rxcost %d interval %t",
|
||||
msg->ihu.addr, msg->ihu.rxcost, (btime) msg->ihu.interval);
|
||||
}
|
||||
@ -892,6 +917,9 @@ babel_send_hello(struct babel_iface *ifa, uint interval)
|
||||
msg.hello.seqno = ifa->hello_seqno++;
|
||||
msg.hello.interval = interval ?: ifa->cf->hello_interval;
|
||||
|
||||
if (ifa->cf->rtt_send)
|
||||
msg.hello.tstamp = 1; /* real timestamp will be set on TLV write */
|
||||
|
||||
TRACE(D_PACKETS, "Sending hello on %s with seqno %d interval %t",
|
||||
ifa->ifname, msg.hello.seqno, (btime) msg.hello.interval);
|
||||
|
||||
@ -1198,14 +1226,26 @@ babel_handle_hello(union babel_msg *m, struct babel_iface *ifa)
|
||||
msg->seqno, (btime) msg->interval);
|
||||
|
||||
struct babel_neighbor *n = babel_get_neighbor(ifa, msg->sender);
|
||||
struct babel_iface_config *cf = n->ifa->cf;
|
||||
int first_hello = !n->hello_cnt;
|
||||
|
||||
if (msg->tstamp)
|
||||
{
|
||||
n->last_tstamp = msg->tstamp;
|
||||
n->last_tstamp_rcvd = msg->pkt_received;
|
||||
}
|
||||
babel_update_hello_history(n, msg->seqno, msg->interval);
|
||||
babel_update_cost(n);
|
||||
|
||||
/* Speed up session establishment by sending IHU immediately */
|
||||
if (first_hello)
|
||||
{
|
||||
/* if using RTT, all IHUs must be paired with hellos */
|
||||
if(cf->rtt_send)
|
||||
babel_send_hello(ifa, 0);
|
||||
else
|
||||
babel_send_ihu(ifa, n);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -1224,6 +1264,39 @@ babel_handle_ihu(union babel_msg *m, struct babel_iface *ifa)
|
||||
struct babel_neighbor *n = babel_get_neighbor(ifa, msg->sender);
|
||||
n->txcost = msg->rxcost;
|
||||
n->ihu_expiry = current_time() + BABEL_IHU_EXPIRY_FACTOR(msg->interval);
|
||||
|
||||
if (msg->tstamp)
|
||||
{
|
||||
u32 rtt_sample = 0, pkt_received = msg->pkt_received TO_US;
|
||||
int remote_time, full_time;
|
||||
|
||||
/* processing time reported by peer */
|
||||
remote_time = (n->last_tstamp - msg->tstamp_rcvd);
|
||||
/* time since we sent the last timestamp - RTT including remote time */
|
||||
full_time = (pkt_received - msg->tstamp);
|
||||
|
||||
/* sanity checks */
|
||||
if (remote_time < 0 || full_time < 0 ||
|
||||
remote_time US_ > BABEL_RTT_MAX_VALUE || full_time US_ > BABEL_RTT_MAX_VALUE)
|
||||
goto out;
|
||||
|
||||
if (remote_time < full_time)
|
||||
rtt_sample = full_time - remote_time;
|
||||
|
||||
if (n->srtt)
|
||||
{
|
||||
uint decay = n->ifa->cf->rtt_decay;
|
||||
|
||||
n->srtt = (decay * rtt_sample + (256 - decay) * n->srtt) / 256;
|
||||
}
|
||||
else
|
||||
n->srtt = rtt_sample;
|
||||
|
||||
TRACE(D_EVENTS, "RTT sample for neighbour %I on %s: %u us (srtt %t ms)",
|
||||
n->addr, ifa->ifname, rtt_sample, n->srtt * 1000);
|
||||
}
|
||||
|
||||
out:
|
||||
babel_update_cost(n);
|
||||
}
|
||||
|
||||
@ -1989,7 +2062,7 @@ babel_reconfigure_ifaces(struct babel_proto *p, struct babel_config *cf)
|
||||
{
|
||||
IFACE_WALK(iface)
|
||||
{
|
||||
if (p->p.vrf && p->p.vrf != iface->master)
|
||||
if (p->p.vrf && !if_in_vrf(iface, p->p.vrf))
|
||||
continue;
|
||||
|
||||
if (!(iface->flags & IF_UP))
|
||||
@ -2203,8 +2276,8 @@ babel_show_neighbors(struct proto *P, const char *iff)
|
||||
}
|
||||
|
||||
cli_msg(-1024, "%s:", p->p.name);
|
||||
cli_msg(-1024, "%-25s %-10s %6s %6s %6s %7s %4s",
|
||||
"IP address", "Interface", "Metric", "Routes", "Hellos", "Expires", "Auth");
|
||||
cli_msg(-1024, "%-25s %-10s %6s %6s %6s %7s %4s %9s",
|
||||
"IP address", "Interface", "Metric", "Routes", "Hellos", "Expires", "Auth", "RTT (ms)");
|
||||
|
||||
WALK_LIST(ifa, p->interfaces)
|
||||
{
|
||||
@ -2219,9 +2292,10 @@ babel_show_neighbors(struct proto *P, const char *iff)
|
||||
|
||||
uint hellos = u32_popcount(n->hello_map);
|
||||
btime timer = (n->hello_expiry ?: n->init_expiry) - current_time();
|
||||
cli_msg(-1024, "%-25I %-10s %6u %6u %6u %7t %-4s",
|
||||
cli_msg(-1024, "%-25I %-10s %6u %6u %6u %7t %-4s %9t",
|
||||
n->addr, ifa->iface->name, n->cost, rts, hellos, MAX(timer, 0),
|
||||
n->auth_passed ? "Yes" : "No");
|
||||
n->auth_passed ? "Yes" : "No",
|
||||
n->srtt * 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include "nest/bird.h"
|
||||
#include "nest/cli.h"
|
||||
#include "nest/iface.h"
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/locks.h"
|
||||
#include "nest/password.h"
|
||||
@ -49,10 +49,16 @@
|
||||
#define BABEL_GARBAGE_INTERVAL (300 S_)
|
||||
#define BABEL_RXCOST_WIRED 96
|
||||
#define BABEL_RXCOST_WIRELESS 256
|
||||
#define BABEL_RXCOST_RTT 96
|
||||
#define BABEL_INITIAL_HOP_COUNT 255
|
||||
#define BABEL_MAX_SEND_INTERVAL 5 /* Unused ? */
|
||||
#define BABEL_INITIAL_NEIGHBOR_TIMEOUT (60 S_)
|
||||
|
||||
#define BABEL_RTT_MAX_VALUE (600 S_)
|
||||
#define BABEL_RTT_MIN (10 MS_)
|
||||
#define BABEL_RTT_MAX (120 MS_)
|
||||
#define BABEL_RTT_DECAY 42
|
||||
|
||||
/* Max interval that will not overflow when carried as 16-bit centiseconds */
|
||||
#define BABEL_TIME_UNITS 10000 /* On-wire times are counted in centiseconds */
|
||||
#define BABEL_MIN_INTERVAL (0x0001 * BABEL_TIME_UNITS)
|
||||
@ -92,6 +98,8 @@ enum babel_tlv_type {
|
||||
enum babel_subtlv_type {
|
||||
BABEL_SUBTLV_PAD1 = 0,
|
||||
BABEL_SUBTLV_PADN = 1,
|
||||
BABEL_SUBTLV_DIVERSITY = 2, /* we don't support this */
|
||||
BABEL_SUBTLV_TIMESTAMP = 3,
|
||||
|
||||
/* Mandatory subtlvs */
|
||||
BABEL_SUBTLV_SOURCE_PREFIX = 128,
|
||||
@ -102,6 +110,7 @@ enum babel_iface_type {
|
||||
BABEL_IFACE_TYPE_UNDEF = 0,
|
||||
BABEL_IFACE_TYPE_WIRED = 1,
|
||||
BABEL_IFACE_TYPE_WIRELESS = 2,
|
||||
BABEL_IFACE_TYPE_TUNNEL = 3,
|
||||
BABEL_IFACE_TYPE_MAX
|
||||
};
|
||||
|
||||
@ -137,6 +146,12 @@ struct babel_iface_config {
|
||||
uint ihu_interval; /* IHU interval, in us */
|
||||
uint update_interval; /* Update interval, in us */
|
||||
|
||||
btime rtt_min; /* rtt above which to start penalising metric */
|
||||
btime rtt_max; /* max rtt metric penalty applied above this */
|
||||
u16 rtt_cost; /* metric penalty to apply at rtt_max */
|
||||
u16 rtt_decay; /* decay of neighbour RTT (units of 1/256) */
|
||||
u8 rtt_send; /* whether to send timestamps on this interface */
|
||||
|
||||
u16 rx_buffer; /* RX buffer size, 0 for MTU */
|
||||
u16 tx_length; /* TX packet length limit (including headers), 0 for MTU */
|
||||
int tx_tos;
|
||||
@ -225,6 +240,10 @@ struct babel_neighbor {
|
||||
u16 next_hello_seqno;
|
||||
uint last_hello_int;
|
||||
|
||||
u32 last_tstamp;
|
||||
btime last_tstamp_rcvd;
|
||||
btime srtt;
|
||||
|
||||
u32 auth_pc_unicast;
|
||||
u32 auth_pc_multicast;
|
||||
u8 auth_passed;
|
||||
@ -323,6 +342,8 @@ struct babel_msg_hello {
|
||||
u16 seqno;
|
||||
uint interval;
|
||||
ip_addr sender;
|
||||
u32 tstamp;
|
||||
btime pkt_received;
|
||||
};
|
||||
|
||||
struct babel_msg_ihu {
|
||||
@ -332,6 +353,9 @@ struct babel_msg_ihu {
|
||||
uint interval;
|
||||
ip_addr addr;
|
||||
ip_addr sender;
|
||||
u32 tstamp;
|
||||
u32 tstamp_rcvd;
|
||||
btime pkt_received;
|
||||
};
|
||||
|
||||
struct babel_msg_update {
|
||||
|
@ -26,7 +26,7 @@ CF_KEYWORDS(BABEL, INTERFACE, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT,
|
||||
TYPE, WIRED, WIRELESS, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK, LINK,
|
||||
NEXT, HOP, IPV4, IPV6, SHOW, INTERFACES, NEIGHBORS,
|
||||
ENTRIES, RANDOMIZE, ROUTER, ID, AUTHENTICATION, NONE, MAC, PERMISSIVE,
|
||||
EXTENDED)
|
||||
EXTENDED, TUNNEL, RTT, MIN, MAX, DECAY, SEND, TIMESTAMPS)
|
||||
|
||||
CF_GRAMMAR
|
||||
|
||||
@ -67,6 +67,10 @@ babel_iface_start:
|
||||
BABEL_IFACE->limit = BABEL_HELLO_LIMIT;
|
||||
BABEL_IFACE->tx_tos = IP_PREC_INTERNET_CONTROL;
|
||||
BABEL_IFACE->tx_priority = sk_priority_control;
|
||||
BABEL_IFACE->rtt_min = BABEL_RTT_MIN;
|
||||
BABEL_IFACE->rtt_max = BABEL_RTT_MAX;
|
||||
BABEL_IFACE->rtt_decay = BABEL_RTT_DECAY;
|
||||
BABEL_IFACE->rtt_send = 1;
|
||||
BABEL_IFACE->check_link = 1;
|
||||
BABEL_IFACE->ext_next_hop = 1;
|
||||
};
|
||||
@ -87,8 +91,16 @@ babel_iface_finish:
|
||||
BABEL_IFACE->hello_interval = BABEL_HELLO_INTERVAL_WIRED;
|
||||
if (!BABEL_IFACE->rxcost)
|
||||
BABEL_IFACE->rxcost = BABEL_RXCOST_WIRED;
|
||||
if (BABEL_IFACE->type == BABEL_IFACE_TYPE_TUNNEL && !BABEL_IFACE->rtt_cost)
|
||||
BABEL_IFACE->rtt_cost = BABEL_RXCOST_RTT;
|
||||
}
|
||||
|
||||
if (BABEL_IFACE->rtt_cost && !BABEL_IFACE->rtt_send)
|
||||
cf_error("Can't set RTT cost when sending timestamps is disabled");
|
||||
|
||||
if (BABEL_IFACE->rtt_min >= BABEL_IFACE->rtt_max)
|
||||
cf_error("Min RTT must be smaller than max RTT");
|
||||
|
||||
/* Make sure we do not overflow the 16-bit centisec fields */
|
||||
if (!BABEL_IFACE->update_interval)
|
||||
BABEL_IFACE->update_interval = MIN_(BABEL_IFACE->hello_interval*BABEL_UPDATE_INTERVAL_FACTOR, BABEL_MAX_INTERVAL);
|
||||
@ -136,6 +148,7 @@ babel_iface_item:
|
||||
| LIMIT expr { BABEL_IFACE->limit = $2; if (($2<1) || ($2>16)) cf_error("Limit must be in range 1-16"); }
|
||||
| TYPE WIRED { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRED; }
|
||||
| TYPE WIRELESS { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRELESS; }
|
||||
| TYPE TUNNEL { BABEL_IFACE->type = BABEL_IFACE_TYPE_TUNNEL; }
|
||||
| HELLO INTERVAL expr_us { BABEL_IFACE->hello_interval = $3; if (($3<BABEL_MIN_INTERVAL) || ($3>BABEL_MAX_INTERVAL)) cf_error("Hello interval must be in range 10 ms - 655 s"); }
|
||||
| UPDATE INTERVAL expr_us { BABEL_IFACE->update_interval = $3; if (($3<BABEL_MIN_INTERVAL) || ($3>BABEL_MAX_INTERVAL)) cf_error("Update interval must be in range 10 ms - 655 s"); }
|
||||
| RX BUFFER expr { BABEL_IFACE->rx_buffer = $3; if (($3<256) || ($3>65535)) cf_error("RX buffer must be in range 256-65535"); }
|
||||
@ -149,6 +162,11 @@ babel_iface_item:
|
||||
| AUTHENTICATION NONE { BABEL_IFACE->auth_type = BABEL_AUTH_NONE; }
|
||||
| AUTHENTICATION MAC { BABEL_IFACE->auth_type = BABEL_AUTH_MAC; BABEL_IFACE->auth_permissive = 0; }
|
||||
| AUTHENTICATION MAC PERMISSIVE { BABEL_IFACE->auth_type = BABEL_AUTH_MAC; BABEL_IFACE->auth_permissive = 1; }
|
||||
| RTT MIN expr_us { BABEL_IFACE->rtt_min = $3; }
|
||||
| RTT MAX expr_us { BABEL_IFACE->rtt_max = $3; }
|
||||
| RTT COST expr { BABEL_IFACE->rtt_cost = $3; if ($3 >= BABEL_INFINITY) cf_error("RTT cost must be < 65535"); }
|
||||
| RTT DECAY expr { BABEL_IFACE->rtt_decay = $3; if (($3 < 1) || ($3 > 256)) cf_error("RTT decay must be between 1-256"); }
|
||||
| SEND TIMESTAMPS bool { BABEL_IFACE->rtt_send = $3; }
|
||||
| password_list
|
||||
;
|
||||
|
||||
|
@ -58,6 +58,13 @@ struct babel_tlv_ihu {
|
||||
u8 addr[0];
|
||||
} PACKED;
|
||||
|
||||
struct babel_subtlv_timestamp {
|
||||
u8 type;
|
||||
u8 length;
|
||||
u32 tstamp;
|
||||
u32 tstamp_rcvd; /* only used in IHU */
|
||||
} PACKED;
|
||||
|
||||
struct babel_tlv_router_id {
|
||||
u8 type;
|
||||
u8 length;
|
||||
@ -161,6 +168,7 @@ struct babel_parse_state {
|
||||
const struct babel_tlv_data* (*get_subtlv_data)(u8 type);
|
||||
struct babel_proto *proto;
|
||||
struct babel_iface *ifa;
|
||||
btime received_time;
|
||||
ip_addr saddr;
|
||||
ip_addr next_hop_ip4;
|
||||
ip_addr next_hop_ip6;
|
||||
@ -172,6 +180,7 @@ struct babel_parse_state {
|
||||
u8 def_ip6_prefix_seen; /* def_ip6_prefix is valid */
|
||||
u8 def_ip4_prefix_seen; /* def_ip4_prefix is valid */
|
||||
u8 def_ip4_via_ip6_prefix_seen; /* def_ip4_via_ip6_prefix is valid */
|
||||
u8 hello_tstamp_seen; /* pkt contains a hello timestamp */
|
||||
u8 current_tlv_endpos; /* End of self-terminating TLVs (offset from start) */
|
||||
u8 sadr_enabled;
|
||||
u8 is_unicast;
|
||||
@ -336,6 +345,7 @@ static int babel_read_update(struct babel_tlv *hdr, union babel_msg *msg, struct
|
||||
static int babel_read_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
|
||||
static int babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
|
||||
static int babel_read_source_prefix(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
|
||||
static int babel_read_timestamp(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
|
||||
|
||||
static uint babel_write_ack(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
|
||||
static uint babel_write_hello(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
|
||||
@ -344,6 +354,7 @@ static uint babel_write_update(struct babel_tlv *hdr, union babel_msg *msg, stru
|
||||
static uint babel_write_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
|
||||
static uint babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
|
||||
static int babel_write_source_prefix(struct babel_tlv *hdr, net_addr *net, uint max_len);
|
||||
static int babel_write_timestamp(struct babel_tlv *hdr, u32 tstamp, u32 tstamp_rcvd, uint max_len);
|
||||
|
||||
static const struct babel_tlv_data tlv_data[BABEL_TLV_MAX] = {
|
||||
[BABEL_TLV_ACK_REQ] = {
|
||||
@ -419,6 +430,13 @@ static const struct babel_tlv_data *get_packet_tlv_data(u8 type)
|
||||
return type < sizeof(tlv_data) / sizeof(*tlv_data) ? &tlv_data[type] : NULL;
|
||||
}
|
||||
|
||||
static const struct babel_tlv_data timestamp_tlv_data = {
|
||||
sizeof(struct babel_subtlv_timestamp),
|
||||
babel_read_timestamp,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct babel_tlv_data source_prefix_tlv_data = {
|
||||
sizeof(struct babel_subtlv_source_prefix),
|
||||
babel_read_source_prefix,
|
||||
@ -430,6 +448,8 @@ static const struct babel_tlv_data *get_packet_subtlv_data(u8 type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case BABEL_SUBTLV_TIMESTAMP:
|
||||
return ×tamp_tlv_data;
|
||||
case BABEL_SUBTLV_SOURCE_PREFIX:
|
||||
return &source_prefix_tlv_data;
|
||||
|
||||
@ -491,16 +511,34 @@ babel_read_hello(struct babel_tlv *hdr, union babel_msg *m,
|
||||
|
||||
static uint
|
||||
babel_write_hello(struct babel_tlv *hdr, union babel_msg *m,
|
||||
struct babel_write_state *state UNUSED, uint max_len UNUSED)
|
||||
struct babel_write_state *state UNUSED, uint max_len)
|
||||
{
|
||||
struct babel_tlv_hello *tlv = (void *) hdr;
|
||||
struct babel_msg_hello *msg = &m->hello;
|
||||
uint len = sizeof(struct babel_tlv_hello);
|
||||
|
||||
TLV_HDR0(tlv, BABEL_TLV_HELLO);
|
||||
put_u16(&tlv->seqno, msg->seqno);
|
||||
put_time16(&tlv->interval, msg->interval);
|
||||
|
||||
return sizeof(struct babel_tlv_hello);
|
||||
if (msg->tstamp)
|
||||
{
|
||||
/*
|
||||
* There can be a substantial delay between when the babel_msg was created
|
||||
* and when it is serialised. We don't want this included in the RTT
|
||||
* measurement, so replace the timestamp with the current time to get as
|
||||
* close as possible to on-wire time for the packet.
|
||||
*/
|
||||
u32 tstamp = current_time_now() TO_US;
|
||||
|
||||
int l = babel_write_timestamp(hdr, tstamp, 0, max_len);
|
||||
if (l < 0)
|
||||
return 0;
|
||||
|
||||
len += l;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -565,6 +603,7 @@ babel_write_ihu(struct babel_tlv *hdr, union babel_msg *m,
|
||||
{
|
||||
struct babel_tlv_ihu *tlv = (void *) hdr;
|
||||
struct babel_msg_ihu *msg = &m->ihu;
|
||||
uint len = sizeof(*tlv);
|
||||
|
||||
if (ipa_is_link_local(msg->addr) && max_len < sizeof(struct babel_tlv_ihu) + 8)
|
||||
return 0;
|
||||
@ -576,12 +615,24 @@ babel_write_ihu(struct babel_tlv *hdr, union babel_msg *m,
|
||||
if (!ipa_is_link_local(msg->addr))
|
||||
{
|
||||
tlv->ae = BABEL_AE_WILDCARD;
|
||||
return sizeof(struct babel_tlv_ihu);
|
||||
goto out;
|
||||
}
|
||||
put_ip6_ll(&tlv->addr, msg->addr);
|
||||
tlv->ae = BABEL_AE_IP6_LL;
|
||||
hdr->length += 8;
|
||||
return sizeof(struct babel_tlv_ihu) + 8;
|
||||
len += 8;
|
||||
|
||||
out:
|
||||
if (msg->tstamp)
|
||||
{
|
||||
int l = babel_write_timestamp(hdr, msg->tstamp, msg->tstamp_rcvd, max_len);
|
||||
if (l < 0)
|
||||
return 0;
|
||||
|
||||
len += l;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -1249,6 +1300,66 @@ babel_write_source_prefix(struct babel_tlv *hdr, net_addr *n, uint max_len)
|
||||
return len;
|
||||
}
|
||||
|
||||
static int
|
||||
babel_read_timestamp(struct babel_tlv *hdr, union babel_msg *msg,
|
||||
struct babel_parse_state *state)
|
||||
{
|
||||
struct babel_subtlv_timestamp *tlv = (void *) hdr;
|
||||
|
||||
switch (msg->type)
|
||||
{
|
||||
case BABEL_TLV_HELLO:
|
||||
if (tlv->length < 4)
|
||||
return PARSE_ERROR;
|
||||
|
||||
msg->hello.tstamp = get_u32(&tlv->tstamp);
|
||||
msg->hello.pkt_received = state->received_time;
|
||||
state->hello_tstamp_seen = 1;
|
||||
break;
|
||||
|
||||
case BABEL_TLV_IHU:
|
||||
if (tlv->length < 8)
|
||||
return PARSE_ERROR;
|
||||
|
||||
/* RTT calculation relies on a Hello always being present with an IHU */
|
||||
if (!state->hello_tstamp_seen)
|
||||
break;
|
||||
|
||||
msg->ihu.tstamp = get_u32(&tlv->tstamp);
|
||||
msg->ihu.tstamp_rcvd = get_u32(&tlv->tstamp_rcvd);
|
||||
msg->ihu.pkt_received = state->received_time;
|
||||
break;
|
||||
|
||||
default:
|
||||
return PARSE_ERROR;
|
||||
}
|
||||
|
||||
return PARSE_SUCCESS;
|
||||
}
|
||||
|
||||
static int
|
||||
babel_write_timestamp(struct babel_tlv *hdr, u32 tstamp, u32 tstamp_rcvd, uint max_len)
|
||||
{
|
||||
struct babel_subtlv_timestamp *tlv = (void *) NEXT_TLV(hdr);
|
||||
uint len = sizeof(*tlv);
|
||||
|
||||
if (hdr->type == BABEL_TLV_HELLO)
|
||||
len -= 4;
|
||||
|
||||
if (len > max_len)
|
||||
return -1;
|
||||
|
||||
TLV_HDR(tlv, BABEL_SUBTLV_TIMESTAMP, len);
|
||||
hdr->length += len;
|
||||
|
||||
put_u32(&tlv->tstamp, tstamp);
|
||||
|
||||
if (hdr->type == BABEL_TLV_IHU)
|
||||
put_u32(&tlv->tstamp_rcvd, tstamp_rcvd);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static inline int
|
||||
babel_read_subtlvs(struct babel_tlv *hdr,
|
||||
union babel_msg *msg,
|
||||
@ -1518,6 +1629,13 @@ babel_process_packet(struct babel_iface *ifa,
|
||||
.saddr = saddr,
|
||||
.next_hop_ip6 = saddr,
|
||||
.sadr_enabled = babel_sadr_enabled(p),
|
||||
|
||||
/*
|
||||
* The core updates current_time() after returning from poll(), so this is
|
||||
* actually the time the packet was received, even though there may have
|
||||
* been a bit of delay before we got to process it
|
||||
*/
|
||||
.received_time = current_time(),
|
||||
};
|
||||
|
||||
if ((pkt->magic != BABEL_MAGIC) || (pkt->version != BABEL_VERSION))
|
||||
|
@ -635,7 +635,7 @@ bfd_reconfigure_iface(struct bfd_proto *p UNUSED, struct bfd_iface *ifa, struct
|
||||
*/
|
||||
|
||||
static void
|
||||
bfd_request_notify(struct bfd_request *req, u8 state, u8 diag)
|
||||
bfd_request_notify(struct bfd_request *req, u8 state, u8 remote, u8 diag)
|
||||
{
|
||||
u8 old_state = req->state;
|
||||
|
||||
@ -645,7 +645,7 @@ bfd_request_notify(struct bfd_request *req, u8 state, u8 diag)
|
||||
req->state = state;
|
||||
req->diag = diag;
|
||||
req->old_state = old_state;
|
||||
req->down = (old_state == BFD_STATE_UP) && (state == BFD_STATE_DOWN);
|
||||
req->down = (old_state == BFD_STATE_UP) && (state == BFD_STATE_DOWN) && (remote != BFD_STATE_ADMIN_DOWN);
|
||||
|
||||
if (req->hook)
|
||||
{
|
||||
@ -780,7 +780,7 @@ bfd_pickup_requests(void *_data UNUSED)
|
||||
|
||||
node *n;
|
||||
WALK_LIST(n, tmp_list)
|
||||
bfd_request_notify(SKIP_BACK(struct bfd_request, n, n), BFD_STATE_ADMIN_DOWN, 0);
|
||||
bfd_request_notify(SKIP_BACK(struct bfd_request, n, n), BFD_STATE_ADMIN_DOWN, BFD_STATE_ADMIN_DOWN, 0);
|
||||
|
||||
BFD_LOCK;
|
||||
add_tail_list(&bfd_global.wait_list, &tmp_list);
|
||||
@ -1026,7 +1026,7 @@ bfd_notify_hook(void *data)
|
||||
struct bfd_proto *p = data;
|
||||
struct bfd_session *s;
|
||||
list tmp_list;
|
||||
u8 state, diag;
|
||||
u8 loc_state, rem_state, diag;
|
||||
node *n, *nn;
|
||||
|
||||
bfd_lock_sessions(p);
|
||||
@ -1039,12 +1039,13 @@ bfd_notify_hook(void *data)
|
||||
{
|
||||
bfd_lock_sessions(p);
|
||||
rem_node(&s->n);
|
||||
state = s->loc_state;
|
||||
loc_state = s->loc_state;
|
||||
rem_state = s->rem_state;
|
||||
diag = s->loc_diag;
|
||||
bfd_unlock_sessions(p);
|
||||
|
||||
WALK_LIST_DELSAFE(n, nn, s->request_list)
|
||||
bfd_request_notify(SKIP_BACK(struct bfd_request, n, n), state, diag);
|
||||
bfd_request_notify(SKIP_BACK(struct bfd_request, n, n), loc_state, rem_state, diag);
|
||||
|
||||
/* Remove the session if all requests were removed in notify hooks */
|
||||
if (EMPTY_LIST(s->request_list))
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include "nest/cli.h"
|
||||
#include "nest/iface.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
#include "nest/password.h"
|
||||
#include "conf/conf.h"
|
||||
#include "lib/hash.h"
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include "nest/bird.h"
|
||||
#include "nest/iface.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
#include "lib/attrs.h"
|
||||
#include "conf/conf.h"
|
||||
#include "lib/resource.h"
|
||||
@ -1223,8 +1223,8 @@ bgp_register_attrs(void)
|
||||
if (!bgp_attr_table[i].name)
|
||||
bgp_attr_table[i] = (union bgp_attr_desc) {
|
||||
.name = mb_sprintf(&root_pool, "bgp_unknown_0x%02x", i),
|
||||
.type = T_OPAQUE,
|
||||
.flags = BAF_OPTIONAL,
|
||||
.type = T_BYTESTRING,
|
||||
.flags = BAF_OPTIONAL | BAF_TRANSITIVE,
|
||||
.readonly = 1,
|
||||
.export = bgp_export_unknown,
|
||||
.encode = bgp_encode_raw,
|
||||
@ -1236,6 +1236,13 @@ bgp_register_attrs(void)
|
||||
}
|
||||
}
|
||||
|
||||
struct ea_class *
|
||||
bgp_find_ea_class_by_id(uint id)
|
||||
{
|
||||
return (id < ARRAY_SIZE(bgp_attr_table)) ? &bgp_attr_table[id].class : NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Attribute export
|
||||
*/
|
||||
@ -1657,6 +1664,9 @@ bgp_defer_bucket(struct bgp_channel *bc, struct bgp_bucket *b)
|
||||
void
|
||||
bgp_withdraw_bucket(struct bgp_channel *bc, struct bgp_bucket *b)
|
||||
{
|
||||
if (b->bmp)
|
||||
return;
|
||||
|
||||
struct bgp_proto *p = (void *) bc->c.proto;
|
||||
struct bgp_pending_tx *c = bc->ptx;
|
||||
struct bgp_bucket *wb = bgp_get_withdraw_bucket(c);
|
||||
@ -1807,6 +1817,10 @@ bgp_free_prefix(struct bgp_pending_tx *c, struct bgp_prefix *px)
|
||||
void
|
||||
bgp_done_prefix(struct bgp_channel *c, struct bgp_prefix *px, struct bgp_bucket *buck)
|
||||
{
|
||||
/* BMP hack */
|
||||
if (buck->bmp)
|
||||
return;
|
||||
|
||||
/* Cleanup: We're called from bucket senders. */
|
||||
ASSERT_DIE(px->cur == buck);
|
||||
rem_node(&px->buck_node_xx);
|
||||
|
@ -86,7 +86,6 @@
|
||||
* RFC 5065 - AS confederations for BGP
|
||||
* RFC 5082 - Generalized TTL Security Mechanism
|
||||
* RFC 5492 - Capabilities Advertisement with BGP
|
||||
* RFC 5549 - Advertising IPv4 NLRI with an IPv6 Next Hop
|
||||
* RFC 5575 - Dissemination of Flow Specification Rules
|
||||
* RFC 5668 - 4-Octet AS Specific BGP Extended Community
|
||||
* RFC 6286 - AS-Wide Unique BGP Identifier
|
||||
@ -101,6 +100,7 @@
|
||||
* RFC 8203 - BGP Administrative Shutdown Communication
|
||||
* RFC 8212 - Default EBGP Route Propagation Behavior without Policies
|
||||
* RFC 8654 - Extended Message Support for BGP
|
||||
* RFC 8950 - Advertising IPv4 NLRI with an IPv6 Next Hop
|
||||
* RFC 9072 - Extended Optional Parameters Length for BGP OPEN Message
|
||||
* RFC 9117 - Revised Validation Procedure for BGP Flow Specifications
|
||||
* RFC 9234 - Route Leak Prevention and Detection Using Roles
|
||||
@ -115,7 +115,7 @@
|
||||
#include "nest/bird.h"
|
||||
#include "nest/iface.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
#include "nest/cli.h"
|
||||
#include "nest/locks.h"
|
||||
#include "conf/conf.h"
|
||||
@ -125,6 +125,9 @@
|
||||
#include "lib/string.h"
|
||||
|
||||
#include "bgp.h"
|
||||
#ifdef CONFIG_BMP
|
||||
#include "proto/bmp/bmp.h"
|
||||
#endif
|
||||
|
||||
static void bgp_listen_create(void *);
|
||||
|
||||
@ -476,10 +479,20 @@ bgp_close_conn(struct bgp_conn *conn)
|
||||
sk_close(conn->sk);
|
||||
conn->sk = NULL;
|
||||
|
||||
mb_free(conn->local_open_msg);
|
||||
conn->local_open_msg = NULL;
|
||||
mb_free(conn->remote_open_msg);
|
||||
conn->remote_open_msg = NULL;
|
||||
conn->local_open_length = 0;
|
||||
conn->remote_open_length = 0;
|
||||
|
||||
mb_free(conn->local_caps);
|
||||
conn->local_caps = NULL;
|
||||
mb_free(conn->remote_caps);
|
||||
conn->remote_caps = NULL;
|
||||
|
||||
conn->notify_data = NULL;
|
||||
conn->notify_size = 0;
|
||||
}
|
||||
|
||||
|
||||
@ -593,8 +606,8 @@ bgp_spawn(struct bgp_proto *pp, ip_addr remote_ip)
|
||||
/* This is hack, we would like to share config, but we need to copy it now */
|
||||
new_config = config;
|
||||
cfg_mem = config->mem;
|
||||
conf_this_scope = config->root_scope;
|
||||
sym = cf_default_name(fmt, &(pp->dynamic_name_counter));
|
||||
config->current_scope = config->root_scope;
|
||||
sym = cf_default_name(config, fmt, &(pp->dynamic_name_counter));
|
||||
proto_clone_config(sym, pp->p.cf);
|
||||
new_config = NULL;
|
||||
cfg_mem = NULL;
|
||||
@ -785,10 +798,15 @@ bgp_conn_enter_established_state(struct bgp_conn *conn)
|
||||
|
||||
bgp_conn_set_state(conn, BS_ESTABLISHED);
|
||||
proto_notify_state(&p->p, PS_UP);
|
||||
|
||||
#ifdef CONFIG_BMP
|
||||
bmp_peer_up(p, conn->local_open_msg, conn->local_open_length,
|
||||
conn->remote_open_msg, conn->remote_open_length);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
bgp_conn_leave_established_state(struct bgp_proto *p)
|
||||
bgp_conn_leave_established_state(struct bgp_conn *conn, struct bgp_proto *p)
|
||||
{
|
||||
BGP_TRACE(D_EVENTS, "BGP session closed");
|
||||
p->last_established = current_time();
|
||||
@ -796,6 +814,12 @@ bgp_conn_leave_established_state(struct bgp_proto *p)
|
||||
|
||||
if (p->p.proto_state == PS_UP)
|
||||
bgp_stop(p, 0, NULL, 0);
|
||||
|
||||
#ifdef CONFIG_BMP
|
||||
bmp_peer_down(p, p->last_error_class,
|
||||
conn->notify_code, conn->notify_subcode,
|
||||
conn->notify_data, conn->notify_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@ -812,7 +836,7 @@ bgp_conn_enter_close_state(struct bgp_conn *conn)
|
||||
bgp_start_timer(p, conn->hold_timer, 10);
|
||||
|
||||
if (os == BS_ESTABLISHED)
|
||||
bgp_conn_leave_established_state(p);
|
||||
bgp_conn_leave_established_state(conn, p);
|
||||
}
|
||||
|
||||
void
|
||||
@ -826,7 +850,7 @@ bgp_conn_enter_idle_state(struct bgp_conn *conn)
|
||||
proto_send_event(&p->p, p->event);
|
||||
|
||||
if (os == BS_ESTABLISHED)
|
||||
bgp_conn_leave_established_state(p);
|
||||
bgp_conn_leave_established_state(conn, p);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1769,7 +1793,7 @@ bgp_start(struct proto *P)
|
||||
* so that we are the only instance attempting to talk with that neighbor.
|
||||
*/
|
||||
struct object_lock *lock;
|
||||
lock = p->lock = olock_new(P->pool_fragile);
|
||||
lock = p->lock = olock_new(P->pool_inloop);
|
||||
lock->addr = p->remote_ip;
|
||||
lock->port = p->cf->remote_port;
|
||||
lock->iface = p->cf->iface;
|
||||
@ -2453,12 +2477,13 @@ bgp_error(struct bgp_conn *c, uint code, uint subcode, byte *data, int len)
|
||||
|
||||
bgp_log_error(p, BE_BGP_TX, "Error", code, subcode, data, ABS(len));
|
||||
bgp_store_error(p, c, BE_BGP_TX, (code << 16) | subcode);
|
||||
bgp_conn_enter_close_state(c);
|
||||
|
||||
c->notify_code = code;
|
||||
c->notify_subcode = subcode;
|
||||
c->notify_data = data;
|
||||
c->notify_size = (len > 0) ? len : 0;
|
||||
|
||||
bgp_conn_enter_close_state(c);
|
||||
bgp_schedule_packet(c, NULL, PKT_NOTIFICATION);
|
||||
|
||||
if (code != 6)
|
||||
@ -2845,7 +2870,7 @@ bgp_show_proto_info(struct proto *P)
|
||||
}
|
||||
}
|
||||
|
||||
struct channel_class channel_bgp = {
|
||||
const struct channel_class channel_bgp = {
|
||||
.channel_size = sizeof(struct bgp_channel),
|
||||
.config_size = sizeof(struct bgp_channel_config),
|
||||
.init = bgp_channel_init,
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include <stdint.h>
|
||||
#include <setjmp.h>
|
||||
#include "nest/bird.h"
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
#include "nest/bfd.h"
|
||||
//#include "lib/lists.h"
|
||||
#include "lib/hash.h"
|
||||
@ -96,6 +96,7 @@ struct bgp_config {
|
||||
u32 default_med; /* Default value for MULTI_EXIT_DISC attribute */
|
||||
int capabilities; /* Enable capability handshake [RFC 5492] */
|
||||
int enable_refresh; /* Enable local support for route refresh [RFC 2918] */
|
||||
int enable_enhanced_refresh; /* Enable local support for enhanced route refresh [RFC 7313] */
|
||||
int enable_as4; /* Enable local support for 4B AS numbers [RFC 6793] */
|
||||
int enable_extended_messages; /* Enable local support for extended messages [RFC 8654] */
|
||||
int enable_hostname; /* Enable local support for hostname [draft] */
|
||||
@ -230,7 +231,7 @@ struct bgp_af_caps {
|
||||
u8 llgr_able; /* Long-lived GR, RFC draft */
|
||||
u32 llgr_time; /* Long-lived GR stale time */
|
||||
u8 llgr_flags; /* Long-lived GR per-AF flags */
|
||||
u8 ext_next_hop; /* Extended IPv6 next hop, RFC 5549 */
|
||||
u8 ext_next_hop; /* Extended IPv6 next hop, RFC 8950 */
|
||||
u8 add_path; /* Multiple paths support, RFC 7911 */
|
||||
};
|
||||
|
||||
@ -285,6 +286,11 @@ struct bgp_conn {
|
||||
u8 ext_messages; /* Session uses extended message length */
|
||||
u32 received_as; /* ASN received in OPEN message */
|
||||
|
||||
byte *local_open_msg; /* Saved OPEN messages (no header) */
|
||||
byte *remote_open_msg;
|
||||
uint local_open_length;
|
||||
uint remote_open_length;
|
||||
|
||||
struct bgp_caps *local_caps;
|
||||
struct bgp_caps *remote_caps;
|
||||
timer *connect_timer;
|
||||
@ -419,7 +425,8 @@ struct bgp_bucket {
|
||||
struct bgp_bucket *next; /* Node in bucket hash table */
|
||||
list prefixes; /* Prefixes to send in this bucket (struct bgp_prefix) */
|
||||
u32 hash; /* Hash over extended attributes */
|
||||
u32 px_uc; /* How many prefixes are linking this bucket */
|
||||
u32 px_uc:31; /* How many prefixes are linking this bucket */
|
||||
u32 bmp:1; /* Temporary bucket for BMP encoding */
|
||||
ea_list eattrs[0]; /* Per-bucket extended attributes */
|
||||
};
|
||||
|
||||
@ -509,6 +516,7 @@ struct bgp_parse_state {
|
||||
#define BGP_PORT 179
|
||||
#define BGP_VERSION 4
|
||||
#define BGP_HEADER_LENGTH 19
|
||||
#define BGP_HDR_MARKER_LENGTH 16
|
||||
#define BGP_MAX_MESSAGE_LENGTH 4096
|
||||
#define BGP_MAX_EXT_MSG_LENGTH 65535
|
||||
#define BGP_RX_BUFFER_SIZE 4096
|
||||
@ -519,6 +527,13 @@ struct bgp_parse_state {
|
||||
#define BGP_CF_WALK_CHANNELS(P,C) WALK_LIST(C, P->c.channels) if (C->c.class == &channel_bgp)
|
||||
#define BGP_WALK_CHANNELS(P,C) WALK_LIST(C, P->p.channels) if (C->c.class == &channel_bgp)
|
||||
|
||||
#define BGP_MSG_HDR_MARKER_SIZE 16
|
||||
#define BGP_MSG_HDR_MARKER_POS 0
|
||||
#define BGP_MSG_HDR_LENGTH_SIZE 2
|
||||
#define BGP_MSG_HDR_LENGTH_POS BGP_MSG_HDR_MARKER_SIZE
|
||||
#define BGP_MSG_HDR_TYPE_SIZE 1
|
||||
#define BGP_MSG_HDR_TYPE_POS (BGP_MSG_HDR_MARKER_SIZE + BGP_MSG_HDR_LENGTH_SIZE)
|
||||
|
||||
static inline int bgp_channel_is_ipv4(struct bgp_channel *c)
|
||||
{ return BGP_AFI(c->afi) == BGP_AFI_IPV4; }
|
||||
|
||||
@ -632,6 +647,8 @@ static inline struct bgp_proto *bgp_rte_proto(const rte *rte)
|
||||
SKIP_BACK(struct bgp_proto, p.sources, rte->src->owner) : NULL;
|
||||
}
|
||||
|
||||
byte * bgp_bmp_encode_rte(struct bgp_channel *c, byte *buf, const net_addr *n, const struct rte *new, const struct rte_src *src);
|
||||
|
||||
#define BGP_AIGP_METRIC 1
|
||||
#define BGP_AIGP_MAX U64(0xffffffffffffffff)
|
||||
|
||||
@ -646,6 +663,7 @@ bgp_total_aigp_metric(const rte *e)
|
||||
}
|
||||
|
||||
void bgp_register_attrs(void);
|
||||
struct ea_class *bgp_find_ea_class_by_id(uint id);
|
||||
|
||||
|
||||
/* packets.c */
|
||||
@ -663,6 +681,7 @@ const char * bgp_error_dsc(unsigned code, unsigned subcode);
|
||||
void bgp_log_error(struct bgp_proto *p, u8 class, char *msg, unsigned code, unsigned subcode, byte *data, unsigned len);
|
||||
|
||||
void bgp_update_next_hop(struct bgp_export_state *s, eattr *a, ea_list **to);
|
||||
byte *bgp_create_end_mark_(struct bgp_channel *c, byte *buf);
|
||||
|
||||
|
||||
/* Packet types */
|
||||
|
@ -23,7 +23,7 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
|
||||
BGP_LOCAL_PREF, BGP_MED,
|
||||
SOURCE, ADDRESS, PASSWORD, RR, RS, CLIENT, CLUSTER, ID, AS4, ADVERTISE,
|
||||
IPV4, CAPABILITIES, LIMIT, PASSIVE, PREFER, OLDER, MISSING, LLADDR,
|
||||
DROP, IGNORE, ROUTE, REFRESH, INTERPRET, COMMUNITIES,
|
||||
DROP, IGNORE, ENHANCED, ROUTE, REFRESH, INTERPRET, COMMUNITIES,
|
||||
IGP, TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL,
|
||||
SECURITY, DETERMINISTIC, SECONDARY, ALLOW, BFD, ADD, PATHS, RX, TX,
|
||||
GRACEFUL, RESTART, AWARE, CHECK, LINK, PORT, EXTENDED, MESSAGES, SETKEY,
|
||||
@ -44,10 +44,6 @@ CF_KEYWORDS(CEASE, PREFIX, LIMIT, HIT, ADMINISTRATIVE, SHUTDOWN, RESET, PEER,
|
||||
|
||||
CF_GRAMMAR
|
||||
|
||||
/* Workaround for collisions between keywords and symbols */
|
||||
toksym: ROLE | PEER | PROVIDER | CUSTOMER | RS_SERVER | RS_CLIENT ;
|
||||
toksym: BGP_MED | BGP_LOCAL_PREF | SOURCE ;
|
||||
|
||||
proto: bgp_proto '}' ;
|
||||
|
||||
bgp_proto_start: proto_start BGP {
|
||||
@ -66,6 +62,7 @@ bgp_proto_start: proto_start BGP {
|
||||
BGP_CFG->error_delay_time_min = 60;
|
||||
BGP_CFG->error_delay_time_max = 300;
|
||||
BGP_CFG->enable_refresh = 1;
|
||||
BGP_CFG->enable_enhanced_refresh = 1;
|
||||
BGP_CFG->enable_as4 = 1;
|
||||
BGP_CFG->enable_hostname = 0;
|
||||
BGP_CFG->capabilities = 2;
|
||||
@ -179,7 +176,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"); }
|
||||
@ -189,6 +186,7 @@ bgp_proto:
|
||||
| bgp_proto DISABLE AFTER ERROR bool ';' { BGP_CFG->disable_after_error = $5; }
|
||||
| bgp_proto DISABLE AFTER CEASE bgp_cease_mask ';' { BGP_CFG->disable_after_cease = $5; }
|
||||
| bgp_proto ENABLE ROUTE REFRESH bool ';' { BGP_CFG->enable_refresh = $5; }
|
||||
| bgp_proto ENABLE ENHANCED ROUTE REFRESH bool ';' { BGP_CFG->enable_enhanced_refresh = $6; }
|
||||
| bgp_proto ENABLE AS4 bool ';' { BGP_CFG->enable_as4 = $4; }
|
||||
| bgp_proto ENABLE EXTENDED MESSAGES bool ';' { BGP_CFG->enable_extended_messages = $5; }
|
||||
| bgp_proto ADVERTISE HOSTNAME bool ';' { BGP_CFG->enable_hostname = $4; }
|
||||
@ -272,7 +270,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; }
|
||||
@ -339,6 +337,19 @@ bgp_channel_end:
|
||||
|
||||
bgp_proto_channel: bgp_channel_start bgp_channel_opt_list bgp_channel_end;
|
||||
|
||||
custom_attr: ATTRIBUTE BGP NUM type symbol ';' {
|
||||
if ($3 > 255 || $3 < 1)
|
||||
cf_error("Invalid attribute number. (Given %i, must be 1-255.)", $3);
|
||||
|
||||
struct ea_class *ac = bgp_find_ea_class_by_id($3);
|
||||
ASSERT_DIE(ac);
|
||||
if ($4 != ac->type)
|
||||
cf_error("Attribute %d type must be %s, not %s.", $3, f_type_name(ac->type), f_type_name($4));
|
||||
|
||||
ea_ref_class(new_config->pool, ac);
|
||||
cf_define_symbol(new_config, $5, SYM_ATTRIBUTE, attribute, ac);
|
||||
};
|
||||
|
||||
CF_ENUM(T_ENUM_BGP_ORIGIN, ORIGIN_, IGP, EGP, INCOMPLETE)
|
||||
|
||||
CF_CODE
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include "nest/bird.h"
|
||||
#include "nest/iface.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/rt.h"
|
||||
#include "nest/route.h"
|
||||
#include "lib/attrs.h"
|
||||
#include "proto/mrt/mrt.h"
|
||||
#include "conf/conf.h"
|
||||
@ -26,6 +26,9 @@
|
||||
#include "nest/cli.h"
|
||||
|
||||
#include "bgp.h"
|
||||
#ifdef CONFIG_BMP
|
||||
#include "proto/bmp/bmp.h"
|
||||
#endif
|
||||
|
||||
|
||||
#define BGP_RR_REQUEST 0
|
||||
@ -213,6 +216,13 @@ bgp_af_caps_cmp(const void *X, const void *Y)
|
||||
return (x->afi < y->afi) ? -1 : (x->afi > y->afi) ? 1 : 0;
|
||||
}
|
||||
|
||||
struct bgp_caps *
|
||||
bgp_alloc_capabilities(struct bgp_proto *p, int n)
|
||||
{
|
||||
struct bgp_caps *caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps) + n * sizeof(struct bgp_af_caps));
|
||||
caps->role = BGP_ROLE_UNDEFINED;
|
||||
return caps;
|
||||
}
|
||||
|
||||
void
|
||||
bgp_prepare_capabilities(struct bgp_conn *conn)
|
||||
@ -225,19 +235,19 @@ bgp_prepare_capabilities(struct bgp_conn *conn)
|
||||
if (!p->cf->capabilities)
|
||||
{
|
||||
/* Just prepare empty local_caps */
|
||||
conn->local_caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps));
|
||||
conn->local_caps = bgp_alloc_capabilities(p, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Prepare bgp_caps structure */
|
||||
int n = list_length(&p->p.channels);
|
||||
caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps) + n * sizeof(struct bgp_af_caps));
|
||||
caps = bgp_alloc_capabilities(p, n);
|
||||
conn->local_caps = caps;
|
||||
|
||||
caps->as4_support = p->cf->enable_as4;
|
||||
caps->ext_messages = p->cf->enable_extended_messages;
|
||||
caps->route_refresh = p->cf->enable_refresh;
|
||||
caps->enhanced_refresh = p->cf->enable_refresh;
|
||||
caps->enhanced_refresh = p->cf->enable_refresh && p->cf->enable_enhanced_refresh;
|
||||
caps->role = p->cf->local_role;
|
||||
|
||||
if (caps->as4_support)
|
||||
@ -462,10 +472,7 @@ bgp_read_capabilities(struct bgp_conn *conn, byte *pos, int len)
|
||||
u32 af;
|
||||
|
||||
if (!conn->remote_caps)
|
||||
{
|
||||
caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps) + sizeof(struct bgp_af_caps));
|
||||
caps->role = BGP_ROLE_UNDEFINED;
|
||||
}
|
||||
caps = bgp_alloc_capabilities(p, 1);
|
||||
else
|
||||
{
|
||||
caps = conn->remote_caps;
|
||||
@ -501,7 +508,7 @@ bgp_read_capabilities(struct bgp_conn *conn, byte *pos, int len)
|
||||
caps->route_refresh = 1;
|
||||
break;
|
||||
|
||||
case 5: /* Extended next hop encoding capability, RFC 5549 */
|
||||
case 5: /* Extended next hop encoding capability, RFC 8950 */
|
||||
if (cl % 6)
|
||||
goto err;
|
||||
|
||||
@ -761,7 +768,7 @@ bgp_read_options(struct bgp_conn *conn, byte *pos, uint len, uint rest)
|
||||
|
||||
/* Prepare empty caps if no capability option was announced */
|
||||
if (!conn->remote_caps)
|
||||
conn->remote_caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps));
|
||||
conn->remote_caps = bgp_alloc_capabilities(p, 0);
|
||||
|
||||
return 0;
|
||||
|
||||
@ -770,6 +777,14 @@ err:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static byte *
|
||||
bgp_copy_open(struct bgp_proto *p, const byte *pkt, uint len)
|
||||
{
|
||||
char *buf = mb_alloc(p->p.pool, len - BGP_HEADER_LENGTH);
|
||||
memcpy(buf, pkt + BGP_HEADER_LENGTH, len - BGP_HEADER_LENGTH);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static byte *
|
||||
bgp_create_open(struct bgp_conn *conn, byte *buf)
|
||||
{
|
||||
@ -844,6 +859,9 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
|
||||
id = get_u32(pkt+24);
|
||||
BGP_TRACE(D_PACKETS, "Got OPEN(as=%d,hold=%d,id=%R)", asn, hold, id);
|
||||
|
||||
conn->remote_open_msg = bgp_copy_open(p, pkt, len);
|
||||
conn->remote_open_length = len - BGP_HEADER_LENGTH;
|
||||
|
||||
if (bgp_read_options(conn, pkt+29, pkt[28], len-29) < 0)
|
||||
return;
|
||||
|
||||
@ -1171,7 +1189,7 @@ bgp_use_gateway(struct bgp_export_state *s)
|
||||
return NULL;
|
||||
|
||||
/* Do not use gateway from different VRF */
|
||||
if (p->p.vrf && nhad->nh.iface && (p->p.vrf != nhad->nh.iface->master))
|
||||
if (p->p.vrf && nhad->nh.iface && !if_in_vrf(nhad->nh.iface, p->p.vrf))
|
||||
return 0;
|
||||
|
||||
/* Use it when exported to internal peers */
|
||||
@ -1261,7 +1279,7 @@ bgp_encode_next_hop_ip(struct bgp_write_state *s, eattr *a, byte *buf, uint size
|
||||
|
||||
/*
|
||||
* Both IPv4 and IPv6 next hops can be used (with ext_next_hop enabled). This
|
||||
* is specified in RFC 5549 for IPv4 and in RFC 4798 for IPv6. The difference
|
||||
* is specified in RFC 8950 for IPv4 and in RFC 4798 for IPv6. The difference
|
||||
* is that IPv4 address is directly encoded with IPv4 NLRI, but as IPv4-mapped
|
||||
* IPv6 address with IPv6 NLRI.
|
||||
*/
|
||||
@ -1336,7 +1354,7 @@ bgp_encode_next_hop_vpn(struct bgp_write_state *s, eattr *a, byte *buf, uint siz
|
||||
|
||||
/*
|
||||
* Both IPv4 and IPv6 next hops can be used (with ext_next_hop enabled). This
|
||||
* is specified in RFC 5549 for VPNv4 and in RFC 4659 for VPNv6. The difference
|
||||
* is specified in RFC 8950 for VPNv4 and in RFC 4659 for VPNv6. The difference
|
||||
* is that IPv4 address is directly encoded with VPNv4 NLRI, but as IPv4-mapped
|
||||
* IPv6 address with VPNv6 NLRI.
|
||||
*/
|
||||
@ -2380,6 +2398,97 @@ bgp_create_mp_unreach(struct bgp_write_state *s, struct bgp_bucket *buck, byte *
|
||||
return buf+11+len;
|
||||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_BMP
|
||||
|
||||
static byte *
|
||||
bgp_create_update_bmp(struct bgp_channel *c, byte *buf, struct bgp_bucket *buck, bool update)
|
||||
{
|
||||
struct bgp_proto *p = (void *) c->c.proto;
|
||||
byte *end = buf + (BGP_MAX_EXT_MSG_LENGTH - BGP_HEADER_LENGTH);
|
||||
byte *res = NULL;
|
||||
/* FIXME: must be a bit shorter */
|
||||
|
||||
struct bgp_caps *peer = p->conn->remote_caps;
|
||||
const struct bgp_af_caps *rem = bgp_find_af_caps(peer, c->afi);
|
||||
|
||||
struct bgp_write_state s = {
|
||||
.proto = p,
|
||||
.channel = c,
|
||||
.pool = tmp_linpool,
|
||||
.mp_reach = (c->afi != BGP_AF_IPV4) || (rem && rem->ext_next_hop),
|
||||
.as4_session = 1,
|
||||
.add_path = c->add_path_rx,
|
||||
.mpls = c->desc->mpls,
|
||||
};
|
||||
|
||||
if (!update)
|
||||
{
|
||||
res = !s.mp_reach ?
|
||||
bgp_create_ip_unreach(&s, buck, buf, end):
|
||||
bgp_create_mp_unreach(&s, buck, buf, end);
|
||||
}
|
||||
else
|
||||
{
|
||||
res = !s.mp_reach ?
|
||||
bgp_create_ip_reach(&s, buck, buf, end):
|
||||
bgp_create_mp_reach(&s, buck, buf, end);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static byte *
|
||||
bgp_bmp_prepare_bgp_hdr(byte *buf, const u16 msg_size, const u8 msg_type)
|
||||
{
|
||||
memset(buf + BGP_MSG_HDR_MARKER_POS, 0xff, BGP_MSG_HDR_MARKER_SIZE);
|
||||
put_u16(buf + BGP_MSG_HDR_LENGTH_POS, msg_size);
|
||||
put_u8(buf + BGP_MSG_HDR_TYPE_POS, msg_type);
|
||||
|
||||
return buf + BGP_MSG_HDR_TYPE_POS + BGP_MSG_HDR_TYPE_SIZE;
|
||||
}
|
||||
|
||||
byte *
|
||||
bgp_bmp_encode_rte(struct bgp_channel *c, byte *buf, const net_addr *n,
|
||||
const struct rte *new, const struct rte_src *src)
|
||||
{
|
||||
// struct bgp_proto *p = (void *) c->c.proto;
|
||||
byte *pkt = buf + BGP_HEADER_LENGTH;
|
||||
|
||||
ea_list *attrs = new ? new->attrs : NULL;
|
||||
uint ea_size = new ? (sizeof(ea_list) + attrs->count * sizeof(eattr)) : 0;
|
||||
uint bucket_size = sizeof(struct bgp_bucket) + ea_size;
|
||||
uint prefix_size = sizeof(struct bgp_prefix) + n->length;
|
||||
|
||||
struct lp_state *tmpp = lp_save(tmp_linpool);
|
||||
|
||||
/* Temporary bucket */
|
||||
struct bgp_bucket *b = tmp_allocz(bucket_size);
|
||||
b->bmp = 1;
|
||||
init_list(&b->prefixes);
|
||||
|
||||
if (attrs)
|
||||
memcpy(b->eattrs, attrs, ea_size);
|
||||
|
||||
/* Temporary prefix */
|
||||
struct bgp_prefix *px = tmp_allocz(prefix_size);
|
||||
px->path_id = (u32) src->private_id;
|
||||
net_copy(px->net, n);
|
||||
add_tail(&b->prefixes, &px->buck_node_xx);
|
||||
|
||||
byte *end = bgp_create_update_bmp(c, pkt, b, !!new);
|
||||
|
||||
if (end)
|
||||
bgp_bmp_prepare_bgp_hdr(buf, end - buf, PKT_UPDATE);
|
||||
|
||||
lp_restore(tmp_linpool, tmpp);
|
||||
|
||||
return end;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_BMP */
|
||||
|
||||
|
||||
static byte *
|
||||
bgp_create_update(struct bgp_channel *c, byte *buf)
|
||||
{
|
||||
@ -2473,6 +2582,14 @@ bgp_create_mp_end_mark(struct bgp_channel *c, byte *buf)
|
||||
return buf+10;
|
||||
}
|
||||
|
||||
byte *
|
||||
bgp_create_end_mark_(struct bgp_channel *c, byte *buf)
|
||||
{
|
||||
return (c->afi == BGP_AF_IPV4) ?
|
||||
bgp_create_ip_end_mark(c, buf):
|
||||
bgp_create_mp_end_mark(c, buf);
|
||||
}
|
||||
|
||||
static byte *
|
||||
bgp_create_end_mark(struct bgp_channel *c, byte *buf)
|
||||
{
|
||||
@ -2481,9 +2598,7 @@ bgp_create_end_mark(struct bgp_channel *c, byte *buf)
|
||||
BGP_TRACE(D_PACKETS, "Sending END-OF-RIB");
|
||||
p->stats.tx_updates++;
|
||||
|
||||
return (c->afi == BGP_AF_IPV4) ?
|
||||
bgp_create_ip_end_mark(c, buf):
|
||||
bgp_create_mp_end_mark(c, buf);
|
||||
return bgp_create_end_mark_(c, buf);
|
||||
}
|
||||
|
||||
static inline void
|
||||
@ -2624,7 +2739,6 @@ bgp_rx_update(struct bgp_conn *conn, byte *pkt, uint len)
|
||||
s.ip_reach_len = len - pos;
|
||||
s.ip_reach_nlri = pkt + pos;
|
||||
|
||||
|
||||
if (s.attr_len)
|
||||
ea = bgp_decode_attrs(&s, s.attrs, s.attr_len);
|
||||
else
|
||||
@ -2863,7 +2977,7 @@ bgp_send(struct bgp_conn *conn, uint type, uint len)
|
||||
conn->bgp->stats.tx_messages++;
|
||||
conn->bgp->stats.tx_bytes += len;
|
||||
|
||||
memset(buf, 0xff, 16); /* Marker */
|
||||
memset(buf, 0xff, BGP_HDR_MARKER_LENGTH);
|
||||
put_u16(buf+16, len);
|
||||
buf[18] = type;
|
||||
|
||||
@ -2911,6 +3025,10 @@ bgp_fire_tx(struct bgp_conn *conn)
|
||||
{
|
||||
conn->packets_to_send &= ~(1 << PKT_OPEN);
|
||||
end = bgp_create_open(conn, pkt);
|
||||
|
||||
conn->local_open_msg = bgp_copy_open(p, buf, end - buf);
|
||||
conn->local_open_length = end - buf - BGP_HEADER_LENGTH;
|
||||
|
||||
return bgp_send(conn, PKT_OPEN, end - buf);
|
||||
}
|
||||
else if (s & (1 << PKT_KEEPALIVE))
|
||||
@ -3197,6 +3315,11 @@ bgp_rx_notification(struct bgp_conn *conn, byte *pkt, uint len)
|
||||
bgp_log_error(p, BE_BGP_RX, "Received", code, subcode, pkt+21, len-21);
|
||||
bgp_store_error(p, conn, BE_BGP_RX, (code << 16) | subcode);
|
||||
|
||||
conn->notify_code = code;
|
||||
conn->notify_subcode = subcode;
|
||||
conn->notify_data = pkt+21;
|
||||
conn->notify_size = len-21;
|
||||
|
||||
bgp_conn_enter_close_state(conn);
|
||||
bgp_schedule_packet(conn, NULL, PKT_SCHEDULE_CLOSE);
|
||||
|
||||
|
1
proto/bmp/Doc
Normal file
1
proto/bmp/Doc
Normal file
@ -0,0 +1 @@
|
||||
S bmp.c
|
2
proto/bmp/LICENSE
Normal file
2
proto/bmp/LICENSE
Normal file
@ -0,0 +1,2 @@
|
||||
The patch is provided under the terms of the GNU General Public License, either
|
||||
version 2, or any later version.
|
6
proto/bmp/Makefile
Normal file
6
proto/bmp/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
src := bmp.c buffer.c map.c
|
||||
obj := $(src-o-files)
|
||||
$(all-daemon)
|
||||
$(cf-local)
|
||||
|
||||
tests_objs := $(tests_objs) $(src-o-files)
|
6
proto/bmp/README.txt
Normal file
6
proto/bmp/README.txt
Normal file
@ -0,0 +1,6 @@
|
||||
ABOUT
|
||||
This package |proto/bmp/*| provide implementation of BGP Monitoring Protocol (BMP).
|
||||
It has been started by Akamai Technologies, Inc. as a pilot program for support BMP in BIRD.
|
||||
It provides only basic features of BMP specification which are needed by Akamai evaluation of
|
||||
feasible BMP protocol.
|
||||
Content of this package has been provided as a patch for BIRD release v2.0.7.
|
1367
proto/bmp/bmp.c
Normal file
1367
proto/bmp/bmp.c
Normal file
File diff suppressed because it is too large
Load Diff
129
proto/bmp/bmp.h
Normal file
129
proto/bmp/bmp.h
Normal file
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* BIRD -- The BGP Monitoring Protocol (BMP)
|
||||
*
|
||||
* (c) 2020 Akamai Technologies, Inc. (Pawel Maslanka, pmaslank@akamai.com)
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#ifndef _BIRD_BMP_H_
|
||||
#define _BIRD_BMP_H_
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "lib/lists.h"
|
||||
#include "nest/route.h"
|
||||
#include "lib/event.h"
|
||||
#include "lib/hash.h"
|
||||
#include "lib/socket.h"
|
||||
#include "proto/bmp/map.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
// Max length of MIB-II description object
|
||||
#define MIB_II_STR_LEN 255
|
||||
|
||||
// The following fields of this structure controls whether there will be put
|
||||
// specific routes into Route Monitoring message and send to BMP collector
|
||||
struct monitoring_rib {
|
||||
bool in_pre_policy; // Monitoring pre-policy Adj-Rib-In
|
||||
bool in_post_policy; // Monitoring post-policy Adj-Rib-In
|
||||
bool local; // Monitoring Local Rib
|
||||
};
|
||||
|
||||
struct bmp_config {
|
||||
struct proto_config c;
|
||||
const char *sys_descr; // sysDescr MIB-II [RFC1213] object
|
||||
const char *sys_name; // sysName MIB-II [RFC1213] object
|
||||
ip_addr local_addr; // Local IP address
|
||||
ip_addr station_ip; // Monitoring station address
|
||||
u16 station_port; // Monitoring station TCP port
|
||||
bool monitoring_rib_in_pre_policy; // Route monitoring pre-policy Adj-Rib-In
|
||||
bool monitoring_rib_in_post_policy; // Route monitoring post-policy Adj-Rib-In
|
||||
};
|
||||
|
||||
/* Forward declarations */
|
||||
struct bgp_proto;
|
||||
struct bmp_proto;
|
||||
|
||||
struct bmp_proto {
|
||||
struct proto p; // Parent proto
|
||||
const struct bmp_config *cf; // Shortcut to BMP configuration
|
||||
node bmp_node; // Node in bmp_proto_list
|
||||
|
||||
HASH(struct bmp_peer) peer_map;
|
||||
HASH(struct bmp_stream) stream_map;
|
||||
HASH(struct bmp_table) table_map;
|
||||
|
||||
sock *sk; // TCP connection
|
||||
event *tx_ev; // TX event
|
||||
event *update_ev; // Update event
|
||||
char sys_descr[MIB_II_STR_LEN]; // sysDescr MIB-II [RFC1213] object
|
||||
char sys_name[MIB_II_STR_LEN]; // sysName MIB-II [RFC1213] object
|
||||
ip_addr local_addr; // Source local IP address
|
||||
ip_addr station_ip; // Monitoring station IP address
|
||||
u16 station_port; // Monitoring station TCP port
|
||||
struct monitoring_rib monitoring_rib;
|
||||
// Below fields are for internal use
|
||||
// struct bmp_peer_map bgp_peers; // Stores 'bgp_proto' structure per BGP peer
|
||||
pool *buffer_mpool; // Memory pool used for BMP buffer allocations
|
||||
pool *map_mem_pool; // Memory pool used for BMP map allocations
|
||||
pool *tx_mem_pool; // Memory pool used for packet allocations designated to BMP collector
|
||||
pool *update_msg_mem_pool; // Memory pool used for BPG UPDATE MSG allocations
|
||||
list tx_queue; // Stores queued packets going to be sent
|
||||
timer *connect_retry_timer; // Timer for retrying connection to the BMP collector
|
||||
list update_msg_queue; // Stores all composed BGP UPDATE MSGs
|
||||
bool started; // Flag that stores running status of BMP instance
|
||||
int sock_err; // Last socket error code
|
||||
};
|
||||
|
||||
struct bmp_peer {
|
||||
struct bgp_proto *bgp;
|
||||
struct bmp_peer *next;
|
||||
list streams;
|
||||
};
|
||||
|
||||
struct bmp_stream {
|
||||
node n;
|
||||
struct bgp_proto *bgp;
|
||||
u32 key;
|
||||
bool sync;
|
||||
struct bmp_stream *next;
|
||||
struct bmp_table *table;
|
||||
struct bgp_channel *sender;
|
||||
};
|
||||
|
||||
struct bmp_table {
|
||||
struct rtable *table;
|
||||
struct bmp_table *next;
|
||||
struct channel *channel;
|
||||
u32 uc;
|
||||
};
|
||||
|
||||
|
||||
#ifdef CONFIG_BMP
|
||||
|
||||
/**
|
||||
* bmp_peer_up - send notification that BGP peer connection is established
|
||||
*/
|
||||
void
|
||||
bmp_peer_up(struct bgp_proto *bgp,
|
||||
const byte *tx_open_msg, uint tx_open_length,
|
||||
const byte *rx_open_msg, uint rx_open_length);
|
||||
|
||||
/**
|
||||
* bmp_peer_down - send notification that BGP peer connection is not in
|
||||
* established state
|
||||
*/
|
||||
void
|
||||
bmp_peer_down(const struct bgp_proto *bgp, int err_class, int code, int subcode, const byte *data, int length);
|
||||
|
||||
|
||||
#else /* BMP build disabled */
|
||||
|
||||
static inline void bmp_peer_up(struct bgp_proto *bgp UNUSED, const byte *tx_open_msg UNUSED, uint tx_open_length UNUSED, const byte *rx_open_msg UNUSED, uint rx_open_length UNUSED) { }
|
||||
static inline void bmp_peer_down(const struct bgp_proto *bgp UNUSED, const int err_class UNUSED, int code UNUSED, int subcode UNUSED, const byte *data UNUSED, int length UNUSED) { }
|
||||
|
||||
#endif /* CONFIG_BMP */
|
||||
|
||||
#endif /* _BIRD_BMP_H_ */
|
65
proto/bmp/buffer.c
Normal file
65
proto/bmp/buffer.c
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* BIRD -- The BGP Monitoring Protocol (BMP)
|
||||
*
|
||||
* (c) 2020 Akamai Technologies, Inc. (Pawel Maslanka, pmaslank@akamai.com)
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#include "proto/bmp/buffer.h"
|
||||
|
||||
buffer
|
||||
bmp_buffer_alloc(pool *ppool, const size_t n)
|
||||
{
|
||||
buffer buf;
|
||||
buf.start = mb_alloc(ppool, n);
|
||||
buf.pos = buf.start;
|
||||
buf.end = buf.start + n;
|
||||
return buf;
|
||||
}
|
||||
|
||||
void
|
||||
bmp_buffer_free(buffer *buf)
|
||||
{
|
||||
mb_free(buf->start);
|
||||
buf->start = buf->pos = buf->end = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief bmp_buffer_grow
|
||||
* @param buf - buffer to grow
|
||||
* @param n - required amount of available space
|
||||
* Resize buffer in a way that there is at least @n bytes of available space.
|
||||
*/
|
||||
static void
|
||||
bmp_buffer_grow(buffer *buf, const size_t n)
|
||||
{
|
||||
size_t pos = bmp_buffer_pos(buf);
|
||||
size_t size = bmp_buffer_size(buf);
|
||||
size_t req = pos + n;
|
||||
|
||||
while (size < req)
|
||||
size = size * 3 / 2;
|
||||
|
||||
buf->start = mb_realloc(buf->start, size);
|
||||
buf->pos = buf->start + pos;
|
||||
buf->end = buf->start + size;
|
||||
}
|
||||
|
||||
void
|
||||
bmp_buffer_need(buffer *buf, const size_t n)
|
||||
{
|
||||
if (bmp_buffer_avail(buf) < n)
|
||||
bmp_buffer_grow(buf, n);
|
||||
}
|
||||
|
||||
void
|
||||
bmp_put_data(buffer *buf, const void *src, const size_t n)
|
||||
{
|
||||
if (!n)
|
||||
return;
|
||||
|
||||
bmp_buffer_need(buf, n);
|
||||
memcpy(buf->pos, src, n);
|
||||
buf->pos += n;
|
||||
}
|
77
proto/bmp/buffer.h
Normal file
77
proto/bmp/buffer.h
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* BIRD -- The BGP Monitoring Protocol (BMP)
|
||||
*
|
||||
* (c) 2020 Akamai Technologies, Inc. (Pawel Maslanka, pmaslank@akamai.com)
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#ifndef _BIRD_BMP_BUFFER_H_
|
||||
#define _BIRD_BMP_BUFFER_H_
|
||||
|
||||
#include "proto/bmp/bmp.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lib/resource.h"
|
||||
|
||||
buffer
|
||||
bmp_buffer_alloc(pool *ppool, const size_t n);
|
||||
|
||||
void
|
||||
bmp_buffer_free(buffer *buf);
|
||||
|
||||
static inline void
|
||||
bmp_buffer_flush(buffer *buf)
|
||||
{
|
||||
buf->pos = buf->start;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
bmp_buffer_size(const buffer *buf)
|
||||
{
|
||||
return buf->end - buf->start;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
bmp_buffer_avail(const buffer *buf)
|
||||
{
|
||||
return buf->end - buf->pos;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
bmp_buffer_pos(const buffer *buf)
|
||||
{
|
||||
return buf->pos - buf->start;
|
||||
}
|
||||
|
||||
static inline byte *
|
||||
bmp_buffer_data(const buffer *buf)
|
||||
{
|
||||
return buf->start;
|
||||
}
|
||||
|
||||
void
|
||||
bmp_buffer_need(buffer *buf, const size_t n);
|
||||
|
||||
// Idea for following macros has been taken from |proto/mrt/mrt.c|
|
||||
#define BMP_DEFINE_PUT_FUNC(S, T) \
|
||||
static inline void \
|
||||
bmp_put_##S(buffer *b, const T x) \
|
||||
{ \
|
||||
bmp_buffer_need(b, sizeof(T)); \
|
||||
put_##S(b->pos, x); \
|
||||
b->pos += sizeof(T); \
|
||||
}
|
||||
|
||||
BMP_DEFINE_PUT_FUNC(u8, u8)
|
||||
BMP_DEFINE_PUT_FUNC(u16, u16)
|
||||
BMP_DEFINE_PUT_FUNC(u32, u32)
|
||||
BMP_DEFINE_PUT_FUNC(u64, u64)
|
||||
BMP_DEFINE_PUT_FUNC(ip4, ip4_addr)
|
||||
BMP_DEFINE_PUT_FUNC(ip6, ip6_addr)
|
||||
|
||||
void
|
||||
bmp_put_data(buffer *buf, const void *src, const size_t n);
|
||||
|
||||
#endif /* _BIRD_BMP_BUFFER_H_ */
|
78
proto/bmp/config.Y
Normal file
78
proto/bmp/config.Y
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* BIRD -- The BGP Monitoring Protocol (BMP)
|
||||
*
|
||||
* (c) 2020 Akamai Technologies, Inc. (Pawel Maslanka, pmaslank@akamai.com)
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
CF_HDR
|
||||
|
||||
#include "proto/bmp/bmp.h"
|
||||
|
||||
CF_DEFINES
|
||||
|
||||
#define BMP_CFG ((struct bmp_config *) this_proto)
|
||||
|
||||
CF_DECLS
|
||||
|
||||
CF_KEYWORDS(BMP, DESCRIPTION, ENABLED, IN, IP, MONITORING, NAME, PORT,
|
||||
PRE_POLICY, POST_POLICY, RIB, STATION, SYSTEM)
|
||||
|
||||
CF_GRAMMAR
|
||||
|
||||
proto: bmp_proto '}' ;
|
||||
|
||||
bmp_proto_start: proto_start BMP {
|
||||
this_proto = proto_config_new(&proto_bmp, $1);
|
||||
BMP_CFG->sys_descr = "Not defined";
|
||||
BMP_CFG->sys_name = "Not defined";
|
||||
}
|
||||
;
|
||||
|
||||
bmp_station_address:
|
||||
/* empty */
|
||||
| bmp_station_address IP ipa {
|
||||
if (ipa_zero($3))
|
||||
cf_error("Invalid BMP monitoring station IP address");
|
||||
BMP_CFG->station_ip = $3;
|
||||
}
|
||||
| bmp_station_address PORT expr {
|
||||
if (($3 < 1) || ($3 > 65535))
|
||||
cf_error("Invalid BMP monitoring station port number");
|
||||
BMP_CFG->station_port = $3;
|
||||
}
|
||||
;
|
||||
|
||||
bmp_proto:
|
||||
bmp_proto_start proto_name '{'
|
||||
| bmp_proto proto_item ';'
|
||||
| bmp_proto LOCAL ADDRESS ipa ';' {
|
||||
BMP_CFG->local_addr = $4;
|
||||
}
|
||||
| bmp_proto STATION ADDRESS bmp_station_address ';'
|
||||
| bmp_proto SYSTEM DESCRIPTION text ';' {
|
||||
if (!$4 || (strlen($4) == 0))
|
||||
cf_error("String is empty");
|
||||
else if (strlen($4) > 255)
|
||||
cf_error("Invalid string length");
|
||||
BMP_CFG->sys_descr = $4;
|
||||
}
|
||||
| bmp_proto SYSTEM NAME text ';' {
|
||||
if (!$4 || (strlen($4) == 0))
|
||||
cf_error("String is empty");
|
||||
else if (strlen($4) > 255)
|
||||
cf_error("Invalid string length");
|
||||
BMP_CFG->sys_name = $4;
|
||||
}
|
||||
| bmp_proto MONITORING RIB IN PRE_POLICY bool ';' {
|
||||
BMP_CFG->monitoring_rib_in_pre_policy = $6;
|
||||
}
|
||||
| bmp_proto MONITORING RIB IN POST_POLICY bool ';' {
|
||||
BMP_CFG->monitoring_rib_in_post_policy = $6;
|
||||
}
|
||||
;
|
||||
|
||||
CF_CODE
|
||||
|
||||
CF_END
|
119
proto/bmp/map.c
Normal file
119
proto/bmp/map.c
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* BIRD -- The BGP Monitoring Protocol (BMP)
|
||||
*
|
||||
* (c) 2020 Akamai Technologies, Inc. (Pawel Maslanka, pmaslank@akamai.com)
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#include "proto/bmp/map.h"
|
||||
|
||||
/* Peer Index Table */
|
||||
#define PEER_KEY(n) (n)->peer_as, (n)->peer_ip
|
||||
#define PEER_NEXT(n) (n)->next
|
||||
#define PEER_EQ(as1,ip1,as2,ip2) \
|
||||
(as1) == (as2) && ipa_equal(ip1, ip2)
|
||||
#define PEER_FN(as,ip) ipa_hash(ip)
|
||||
|
||||
#define PEER_REHASH bmp_peer_rehash
|
||||
#define PEER_PARAMS /8, *2, 2, 2, 6, 20
|
||||
|
||||
HASH_DEFINE_REHASH_FN(PEER, struct bmp_peer_map_key)
|
||||
|
||||
#define PEER_INIT_ORDER 6
|
||||
|
||||
void
|
||||
bmp_peer_map_init(struct bmp_peer_map *map, pool *mpool)
|
||||
{
|
||||
map->mpool = mpool;
|
||||
HASH_INIT(map->peer_hash, map->mpool, PEER_INIT_ORDER);
|
||||
}
|
||||
|
||||
struct bmp_peer_map_key
|
||||
bmp_peer_map_key_create(const ip_addr peer_ip, const u32 peer_as)
|
||||
{
|
||||
struct bmp_peer_map_key key;
|
||||
key.next = NULL;
|
||||
key.peer_ip = peer_ip;
|
||||
key.peer_as = peer_as;
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
void
|
||||
bmp_peer_map_flush(struct bmp_peer_map *map)
|
||||
{
|
||||
struct bmp_peer_map_entry *entry;
|
||||
HASH_WALK_DELSAFE(map->peer_hash, next, e)
|
||||
{
|
||||
entry = (struct bmp_peer_map_entry *) e;
|
||||
mb_free(entry->data.buf);
|
||||
HASH_DELETE(map->peer_hash, PEER, PEER_KEY(&entry->key));
|
||||
mb_free(entry);
|
||||
}
|
||||
HASH_WALK_DELSAFE_END;
|
||||
|
||||
HASH_MAY_RESIZE_DOWN(map->peer_hash, PEER, map->mpool);
|
||||
}
|
||||
|
||||
void
|
||||
bmp_peer_map_free(struct bmp_peer_map *map)
|
||||
{
|
||||
bmp_peer_map_flush(map);
|
||||
HASH_FREE(map->peer_hash);
|
||||
}
|
||||
|
||||
void
|
||||
bmp_peer_map_insert(struct bmp_peer_map *map, const struct bmp_peer_map_key key,
|
||||
const byte *data, const size_t data_size)
|
||||
{
|
||||
struct bmp_peer_map_entry *entry
|
||||
= (void *) HASH_FIND(map->peer_hash, PEER, PEER_KEY(&key));
|
||||
|
||||
if (entry)
|
||||
{
|
||||
mb_free(entry->data.buf);
|
||||
entry->data.buf = mb_alloc(map->mpool, data_size);
|
||||
memcpy(entry->data.buf, data, data_size);
|
||||
entry->data.buf_size = data_size;
|
||||
return;
|
||||
}
|
||||
|
||||
entry = mb_alloc(map->mpool, sizeof (struct bmp_peer_map_entry));
|
||||
entry->data.buf = mb_alloc(map->mpool, data_size);
|
||||
memcpy(entry->data.buf, data, data_size);
|
||||
entry->data.buf_size = data_size;
|
||||
entry->key = key;
|
||||
HASH_INSERT2(map->peer_hash, PEER, map->mpool, &entry->key);
|
||||
}
|
||||
|
||||
void
|
||||
bmp_peer_map_remove(struct bmp_peer_map *map, const struct bmp_peer_map_key key)
|
||||
{
|
||||
struct bmp_peer_map_entry *entry
|
||||
= (void *) HASH_DELETE(map->peer_hash, PEER, PEER_KEY(&key));
|
||||
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
mb_free(entry->data.buf);
|
||||
mb_free(entry);
|
||||
}
|
||||
|
||||
const struct bmp_peer_map_entry *
|
||||
bmp_peer_map_get(struct bmp_peer_map *map, const struct bmp_peer_map_key key)
|
||||
{
|
||||
return (struct bmp_peer_map_entry *) HASH_FIND(map->peer_hash, PEER, PEER_KEY(&key));
|
||||
}
|
||||
|
||||
void
|
||||
bmp_peer_map_walk(const struct bmp_peer_map *map, bmp_peer_map_walk_action action, void *arg)
|
||||
{
|
||||
struct bmp_peer_map_entry *entry;
|
||||
HASH_WALK_FILTER(map->peer_hash, next, e, _)
|
||||
{
|
||||
entry = (struct bmp_peer_map_entry *) e;
|
||||
action(entry->key, entry->data.buf, entry->data.buf_size, arg);
|
||||
}
|
||||
HASH_WALK_FILTER_END;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user