0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-09-19 20:05:21 +00:00

Merge commit 'cae5979871ee7aa341334f8b1af6bafc60ee9692' into tmp-bad-learn

This commit is contained in:
Maria Matejka 2022-09-27 12:39:07 +02:00
commit 32a67c93eb
150 changed files with 10304 additions and 5234 deletions

View File

@ -3,7 +3,7 @@ variables:
LC_ALL: C.UTF-8
GIT_STRATEGY: fetch
DOCKER_CMD: docker --config="$HOME/.docker/$CI_JOB_ID/"
IMG_BASE: registry.labs.nic.cz/labs/bird
IMG_BASE: registry.nic.cz/labs/bird
TOOLS_DIR: /var/lib/gitlab-runner/bird-tools
stages:
@ -16,7 +16,7 @@ stages:
stage: image
allow_failure: true
script:
- $DOCKER_CMD login -u gitlab-ci-token -p $CI_BUILD_TOKEN registry.labs.nic.cz
- $DOCKER_CMD login -u gitlab-ci-token -p $CI_BUILD_TOKEN registry.nic.cz
# Make sure we refresh the base image if it updates (eg. security updates, etc)
# If we do just the build, cache is always reused and the freshness of the
# base image is never checked. However, pull always asks and updates the
@ -164,9 +164,9 @@ docker_ubuntu-20_04-amd64:
IMG_NAME: "ubuntu-20.04-amd64"
<<: *docker_build
docker_ubuntu-20_10-amd64:
docker_ubuntu-21_10-amd64:
variables:
IMG_NAME: "ubuntu-20.10-amd64"
IMG_NAME: "ubuntu-21.10-amd64"
<<: *docker_build
# GPG error
@ -234,143 +234,143 @@ docker_opensuse-15.3-amd64:
build-debian-8-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:debian-8-amd64
image: registry.nic.cz/labs/bird:debian-8-amd64
build-debian-8-i386:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:debian-8-i386
image: registry.nic.cz/labs/bird:debian-8-i386
build-debian-9-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:debian-9-amd64
image: registry.nic.cz/labs/bird:debian-9-amd64
build-debian-9-i386:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:debian-9-i386
image: registry.nic.cz/labs/bird:debian-9-i386
build-debian-10-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:debian-10-amd64
image: registry.nic.cz/labs/bird:debian-10-amd64
build-debian-10-i386:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:debian-10-i386
image: registry.nic.cz/labs/bird:debian-10-i386
build-debian-11-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:debian-11-amd64
image: registry.nic.cz/labs/bird:debian-11-amd64
#build-debian-11-i386:
# <<: *build-linux
# image: registry.labs.nic.cz/labs/bird:debian-11-i386
# image: registry.nic.cz/labs/bird:debian-11-i386
build-debian-testing-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:debian-testing-amd64
image: registry.nic.cz/labs/bird:debian-testing-amd64
#build-debian-testing-i386:
# <<: *build-linux
# image: registry.labs.nic.cz/labs/bird:debian-testing-i386
# image: registry.nic.cz/labs/bird:debian-testing-i386
build-fedora-25-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:fedora-25-amd64
image: registry.nic.cz/labs/bird:fedora-25-amd64
build-fedora-26-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:fedora-26-amd64
image: registry.nic.cz/labs/bird:fedora-26-amd64
build-fedora-27-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:fedora-27-amd64
image: registry.nic.cz/labs/bird:fedora-27-amd64
build-fedora-28-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:fedora-28-amd64
image: registry.nic.cz/labs/bird:fedora-28-amd64
build-fedora-29-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:fedora-29-amd64
image: registry.nic.cz/labs/bird:fedora-29-amd64
build-fedora-30-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:fedora-30-amd64
image: registry.nic.cz/labs/bird:fedora-30-amd64
build-fedora-31-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:fedora-31-amd64
image: registry.nic.cz/labs/bird:fedora-31-amd64
build-fedora-32-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:fedora-32-amd64
image: registry.nic.cz/labs/bird:fedora-32-amd64
build-fedora-33-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:fedora-33-amd64
image: registry.nic.cz/labs/bird:fedora-33-amd64
build-fedora-34-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:fedora-33-amd64
image: registry.nic.cz/labs/bird:fedora-33-amd64
build-centos-7-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:centos-7-amd64
image: registry.nic.cz/labs/bird:centos-7-amd64
build-centos-8-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:centos-8-amd64
image: registry.nic.cz/labs/bird:centos-8-amd64
build-ubuntu-14_04-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:ubuntu-14.04-amd64
image: registry.nic.cz/labs/bird:ubuntu-14.04-amd64
build-ubuntu-16_04-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:ubuntu-16.04-amd64
image: registry.nic.cz/labs/bird:ubuntu-16.04-amd64
build-ubuntu-18_04-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:ubuntu-18.04-amd64
image: registry.nic.cz/labs/bird:ubuntu-18.04-amd64
build-ubuntu-20_04-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:ubuntu-20.04-amd64
image: registry.nic.cz/labs/bird:ubuntu-20.04-amd64
build-ubuntu-20_10-amd64:
build-ubuntu-21_10-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:ubuntu-20.10-amd64
image: registry.nic.cz/labs/bird:ubuntu-21.10-amd64
#build-ubuntu-21_04-amd64:
# <<: *build-linux
# image: registry.labs.nic.cz/labs/bird:ubuntu-21.04-amd64
# image: registry.nic.cz/labs/bird:ubuntu-21.04-amd64
build-opensuse-15.0-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:opensuse-15.0-amd64
image: registry.nic.cz/labs/bird:opensuse-15.0-amd64
build-opensuse-15.1-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:opensuse-15.1-amd64
image: registry.nic.cz/labs/bird:opensuse-15.1-amd64
build-opensuse-15.2-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:opensuse-15.2-amd64
image: registry.nic.cz/labs/bird:opensuse-15.2-amd64
build-opensuse-15.3-amd64:
<<: *build-linux
image: registry.labs.nic.cz/labs/bird:opensuse-15.3-amd64
image: registry.nic.cz/labs/bird:opensuse-15.3-amd64
build-freebsd-11-amd64:
<<: *build-base
tags:
- freebsd
- amd64
#build-freebsd-11-amd64:
# <<: *build-base
# tags:
# - freebsd
# - amd64
build-freebsd-11-i386:
<<: *build-base
tags:
- freebsd
- i386
#build-freebsd-11-i386:
# <<: *build-base
# tags:
# - freebsd
# - i386
.pkg-deb: &pkg-deb
@ -408,112 +408,113 @@ build-freebsd-11-i386:
#pkg-debian-8-amd64:
# <<: *pkg-deb
# needs: [build-debian-8-amd64]
# image: registry.labs.nic.cz/labs/bird: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.labs.nic.cz/labs/bird:debian-8-i386
# image: registry.nic.cz/labs/bird:debian-8-i386
# Dpkg error: PATH is not set
pkg-debian-9-amd64:
<<: *pkg-deb
needs: [build-debian-9-amd64]
image: registry.labs.nic.cz/labs/bird:debian-9-amd64
image: registry.nic.cz/labs/bird:debian-9-amd64
# Dpkg error: PATH is not set
pkg-debian-9-i386:
<<: *pkg-deb
needs: [build-debian-9-i386]
image: registry.labs.nic.cz/labs/bird:debian-9-i386
image: registry.nic.cz/labs/bird:debian-9-i386
pkg-debian-10-amd64:
<<: *pkg-deb
needs: [build-debian-10-amd64]
image: registry.labs.nic.cz/labs/bird:debian-10-amd64
image: registry.nic.cz/labs/bird:debian-10-amd64
pkg-debian-10-i386:
<<: *pkg-deb
needs: [build-debian-10-i386]
image: registry.labs.nic.cz/labs/bird:debian-10-i386
image: registry.nic.cz/labs/bird:debian-10-i386
pkg-debian-11-amd64:
<<: *pkg-deb
needs: [build-debian-11-amd64]
image: registry.labs.nic.cz/labs/bird:debian-11-amd64
image: registry.nic.cz/labs/bird:debian-11-amd64
pkg-fedora-30-amd64:
<<: *pkg-rpm-wa
needs: [build-fedora-30-amd64]
image: registry.labs.nic.cz/labs/bird:fedora-30-amd64
image: registry.nic.cz/labs/bird:fedora-30-amd64
pkg-fedora-31-amd64:
<<: *pkg-rpm-wa
needs: [build-fedora-31-amd64]
image: registry.labs.nic.cz/labs/bird:fedora-31-amd64
image: registry.nic.cz/labs/bird:fedora-31-amd64
pkg-fedora-32-amd64:
<<: *pkg-rpm-wa
needs: [build-fedora-32-amd64]
image: registry.labs.nic.cz/labs/bird:fedora-32-amd64
image: registry.nic.cz/labs/bird:fedora-32-amd64
pkg-fedora-33-amd64:
<<: *pkg-rpm-wa
needs: [build-fedora-33-amd64]
image: registry.labs.nic.cz/labs/bird:fedora-33-amd64
image: registry.nic.cz/labs/bird: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-7-amd64:
<<: *pkg-rpm-wa
variables:
LC_ALL: en_US.UTF-8
needs: [build-centos-7-amd64]
image: registry.labs.nic.cz/labs/bird:centos-7-amd64
image: registry.nic.cz/labs/bird:centos-7-amd64
pkg-centos-8-amd64:
<<: *pkg-rpm-wa
needs: [build-centos-8-amd64]
image: registry.labs.nic.cz/labs/bird:centos-8-amd64
image: registry.nic.cz/labs/bird:centos-8-amd64
pkg-ubuntu-18.04-amd64:
<<: *pkg-deb
needs: [build-ubuntu-18_04-amd64]
image: registry.labs.nic.cz/labs/bird:ubuntu-18.04-amd64
image: registry.nic.cz/labs/bird:ubuntu-18.04-amd64
pkg-ubuntu-20.04-amd64:
<<: *pkg-deb
needs: [build-ubuntu-20_04-amd64]
image: registry.labs.nic.cz/labs/bird:ubuntu-20.04-amd64
image: registry.nic.cz/labs/bird:ubuntu-20.04-amd64
pkg-ubuntu-20.10-amd64:
pkg-ubuntu-21.10-amd64:
<<: *pkg-deb
needs: [build-ubuntu-20_10-amd64]
image: registry.labs.nic.cz/labs/bird:ubuntu-20.10-amd64
needs: [build-ubuntu-21_10-amd64]
image: registry.nic.cz/labs/bird:ubuntu-21.10-amd64
#pkg-ubuntu-21.04-amd64:
# <<: *pkg-deb
# needs: [build-ubuntu-21_04-amd64]
# image: registry.labs.nic.cz/labs/bird:ubuntu-21.04-amd64
# image: registry.nic.cz/labs/bird:ubuntu-21.04-amd64
pkg-opensuse-15.1-amd64:
<<: *pkg-rpm-wa
needs: [build-opensuse-15.1-amd64]
image: registry.labs.nic.cz/labs/bird:opensuse-15.1-amd64
image: registry.nic.cz/labs/bird:opensuse-15.1-amd64
pkg-opensuse-15.2-amd64:
<<: *pkg-rpm-wa
needs: [build-opensuse-15.2-amd64]
image: registry.labs.nic.cz/labs/bird:opensuse-15.2-amd64
image: registry.nic.cz/labs/bird:opensuse-15.2-amd64
pkg-opensuse-15.3-amd64:
<<: *pkg-rpm-wa
needs: [build-opensuse-15.3-amd64]
image: registry.labs.nic.cz/labs/bird:opensuse-15.3-amd64
image: registry.nic.cz/labs/bird:opensuse-15.3-amd64
build-birdlab:
@ -582,6 +583,11 @@ test-ospf-custom:
variables:
TEST_NAME: cf-ospf-custom
test-ospf-area:
<<: *test-base
variables:
TEST_NAME: cf-ospf-area
test-ospf-vrf:
<<: *test-base
variables:
@ -636,3 +642,8 @@ test-babel-auth:
<<: *test-base
variables:
TEST_NAME: cf-babel-auth
test-rip-base:
<<: *test-base
variables:
TEST_NAME: cf-rip-base

View File

@ -82,6 +82,9 @@ conf-lex-targets := $(addprefix $(objdir)/conf/,cf-lex.o)
conf-y-targets := $(addprefix $(objdir)/conf/,cf-parse.y keywords.h commands.h)
cf-local = $(conf-y-targets): $(s)config.Y
# nest/Makefile declarations needed for all other modules
proto-build-c := $(addprefix $(objdir)/nest/,proto-build.c)
src-o-files = $(patsubst %.c,$(o)%.o,$(src))
tests-target-files = $(patsubst %.c,$(o)%,$(tests_src))
@ -95,6 +98,13 @@ else
o = $(patsubst $(srcdir)%,$(objdir)%,$(s))
endif
define proto-build_in =
PROTO_BUILD += $(1)
$(proto-build-c): $(lastword $(MAKEFILE_LIST))
endef
proto-build = $(eval $(call proto-build_in,$(1)))
define clean_in =
clean::
rm -f $(addprefix $(o),$(1))

35
NEWS
View File

@ -1,3 +1,38 @@
Version 2.0.9 (2022-02-09)
o BGP: Flowspec validation procedure
o Babel: MAC authentication support
o Routing table configuration blocks
o Optional prefix trie in routing table for faster LPM/interval queries
o CLI: New 'show route in <prefix>' command
o Filter: Faster (16-way) prefix sets
o Filter: MPLS label route attribute
o Filter: Operators to pick community components
o Filter: Operators to find minimum and maximum element of lists
o BGP: New 'free bind' option
o BGP: Log route updates that were changed to withdraws
o BGP: Improved 'invalid next hop' error reporting
o OSPF: Allow ifaces with host address as unnumbered PtP or PtMP ifaces
o OSPF: All packets on PtP networks should be sent to AllSPFRouters address
o Scripts for apkg-powered upstream packaging for deb and rpm
o Support for Blake2s and Blake2b hash functions
o Security keys / passwords can be entered in hexadecimal digits
o Memory statistics split into Effective and Overhead
o Linux: New option 'netlink rx buffer' to specify netlink socket buffer size
o BSD: Assume onlink flag on ifaces with only host addresses
o Many bugfixes
Notes:
For OSPF on PtP network, BIRD now sends all packets to multicast AllSPFRouters
address (as required in RFC 2328 8.1). This likely breaks setups with multiple
neighbors on a network configured as PtP, which worked in previous versions.
Such links should be configured as PtMP.
Since Linux 5.3, netlink socket can be flooded by route cache entries during
route table scan. This version mitigates that issue by using strict netlink
filtering.
Version 2.0.8 (2021-03-18)
o Automatic channel reloads based on RPKI changes
o Multiple static routes with the same network

26
aclocal.m4 vendored
View File

@ -1,6 +1,32 @@
dnl ** Additional Autoconf tests for BIRD configure script
dnl ** (c) 1999 Martin Mares <mj@ucw.cz>
AC_DEFUN([BIRD_CHECK_POINTER_ALIGNMENT],
[
AC_CACHE_CHECK(
[how pointers are aligned],
[bird_cv_pointer_alignment],
AC_COMPILE_IFELSE([
AC_LANG_PROGRAM(
[
_Static_assert(_Alignof(void *) == 8, "bad");
], []
)
],
[bird_cv_pointer_alignment=8],
AC_COMPILE_IFELSE([
AC_LANG_PROGRAM(
[
_Static_assert(_Alignof(void *) == 4, "bad");
], []
)
],
[bird_cv_pointer_alignment=4],
[bird_cv_pointer_alignment=unknown]
))
)
])
AC_DEFUN([BIRD_CHECK_THREAD_LOCAL],
[
AC_CACHE_CHECK(

View File

@ -25,7 +25,6 @@ class BIRDFValPrinter(BIRDPrinter):
"T_ENUM_RTS": "i",
"T_ENUM_BGP_ORIGIN": "i",
"T_ENUM_SCOPE": "i",
"T_ENUM_RTC": "i",
"T_ENUM_RTD": "i",
"T_ENUM_ROA": "i",
"T_ENUM_NETTYPE": "i",

View File

@ -50,6 +50,7 @@ static byte *server_read_pos = server_read_buf;
int init = 1; /* During intial sequence */
int busy = 1; /* Executing BIRD command */
int interactive; /* Whether stdin is terminal */
int last_code; /* Last return code */
static int num_lines, skip_input;
int term_lns, term_cls;
@ -196,7 +197,7 @@ init_commands(void)
{
/* Initial command is finished and we want to exit */
cleanup();
exit(0);
exit((last_code < 8000) ? 0 : 1);
}
input_init();
@ -283,6 +284,8 @@ server_got_reply(char *x)
if (code)
PRINTF(len, "%s\n", verbose ? x : x+5);
last_code = code;
if (x[4] == ' ')
{
busy = 0;

View File

@ -42,7 +42,7 @@
#define PARSER 1
#include "nest/bird.h"
#include "nest/route.h"
#include "nest/rt.h"
#include "nest/protocol.h"
#include "filter/filter.h"
#include "filter/f-inst.h"
@ -77,19 +77,22 @@ static uint cf_hash(const byte *c);
#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_ORDER 6 /* Initial */
#define SYM_ORDER 4 /* Initial */
#define SYM_REHASH sym_rehash
#define SYM_PARAMS /8, *1, 2, 2, 6, 20
#define SYM_PARAMS /8, *1, 2, 2, 4, 20
HASH_DEFINE_REHASH_FN(SYM, struct symbol)
HASH(struct keyword) kw_hash;
HASH(struct ea_class) ea_name_hash;
struct sym_scope *conf_this_scope;
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);
@ -255,7 +258,7 @@ WHITE [ \t]
return IP4;
}
{XIGIT}{2}(:{XIGIT}{2}|{XIGIT}{2}){15,} {
{XIGIT}{2}((:{XIGIT}{2}){15,}|({XIGIT}{2}){15,}) {
char *s = yytext;
size_t len = 0, i;
struct bytestring *bytes;
@ -347,7 +350,7 @@ else: {
return DDOT;
}
[={}:;,.()+*/%<>~\[\]?!\|-] {
[={}:;,.()+*/%<>~\[\]?!\|&-] {
return yytext[0];
}
@ -587,41 +590,58 @@ cf_new_symbol(const byte *c)
*s = (struct symbol) { .scope = conf_this_scope, .class = SYM_VOID, };
strcpy(s->name, c);
if (!new_config->sym_hash.data)
HASH_INIT(new_config->sym_hash, new_config->pool, SYM_ORDER);
if (!conf_this_scope->hash.data)
HASH_INIT(conf_this_scope->hash, new_config->pool, SYM_ORDER);
HASH_INSERT2(new_config->sym_hash, SYM, new_config->pool, s);
HASH_INSERT2(conf_this_scope->hash, SYM, new_config->pool, s);
add_tail(&(new_config->symbols), &(s->n));
if (conf_this_scope == new_config->root_scope)
add_tail(&(new_config->symbols), &(s->n));
return s;
}
static struct symbol *
cf_root_symbol(const byte *c)
{
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, };
memcpy(s->name, c, l+1);
if (!global_root_scope->hash.data)
HASH_INIT(global_root_scope->hash, &root_pool, SYM_ORDER);
HASH_INSERT2(global_root_scope->hash, SYM, &root_pool, s);
return s;
}
/**
* cf_find_symbol - find a symbol by name
* @cfg: specificed config
* cf_find_symbol_scope - find a symbol by name
* @scope: config scope
* @c: symbol name
*
* This functions searches the symbol table in the config @cfg for a symbol of
* given name. First it examines the current scope, then the second recent one
* This functions searches the symbol table in the scope @scope for a symbol of
* given name. First it examines the current scope, then the underlying one
* and so on until it either finds the symbol and returns a pointer to its
* &symbol structure or reaches the end of the scope chain and returns %NULL to
* signify no match.
*/
struct symbol *
cf_find_symbol(const struct config *cfg, const byte *c)
cf_find_symbol_scope(const struct sym_scope *scope, const byte *c)
{
struct symbol *s;
if (cfg->sym_hash.data &&
(s = HASH_FIND(cfg->sym_hash, SYM, c, 1)))
return s;
/* In CLI command parsing, fallback points to the current config, otherwise it is NULL. */
if (cfg->fallback &&
cfg->fallback->sym_hash.data &&
(s = HASH_FIND(cfg->fallback->sym_hash, SYM, c, 1)))
return s;
/* Find the symbol here or anywhere below */
while (scope)
if (scope->hash.data && (s = HASH_FIND(scope->hash, SYM, c, 1)))
return s;
else
scope = scope->next;
return NULL;
}
@ -638,7 +658,7 @@ cf_find_symbol(const struct config *cfg, const byte *c)
struct symbol *
cf_get_symbol(const byte *c)
{
return cf_find_symbol(new_config, c) ?: cf_new_symbol(c);
return cf_find_symbol_scope(conf_this_scope, c) ?: cf_new_symbol(c);
}
/**
@ -654,7 +674,7 @@ cf_localize_symbol(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 (sym->scope == conf_this_scope)
cf_error("Symbol already defined");
@ -689,10 +709,7 @@ cf_lex_symbol(const char *data)
struct symbol *sym = cf_get_symbol(data);
cf_lval.s = sym;
if (sym->class != SYM_VOID)
return CF_SYM_KNOWN;
/* Is it a keyword? */
/* Is it a keyword? Prefer the keyword. */
struct keyword *k = HASH_FIND(kw_hash, KW, data);
if (k)
{
@ -705,9 +722,11 @@ cf_lex_symbol(const char *data)
}
}
/* OK, undefined symbol */
cf_lval.s = sym;
return CF_SYM_UNDEFINED;
/* OK, only a symbol. */
if (sym->class == SYM_VOID)
return CF_SYM_UNDEFINED;
else
return CF_SYM_KNOWN;
}
static void
@ -720,6 +739,34 @@ cf_lex_init_kh(void)
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;
}
void
ea_lex_unregister(struct ea_class *def)
{
struct symbol *sym = def->sym;
HASH_REMOVE2(global_root_scope->hash, SYM, &root_pool, sym);
mb_free(sym);
def->sym = NULL;
}
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;
}
/**
* cf_lex_init - initialize the lexer
* @is_cli: true if we're going to parse CLI command, false for configuration
@ -753,6 +800,11 @@ cf_lex_init(int is_cli, struct config *c)
c->root_scope = cfg_allocz(sizeof(struct sym_scope));
conf_this_scope = c->root_scope;
conf_this_scope->active = 1;
if (is_cli)
conf_this_scope->next = config->root_scope;
else
conf_this_scope->next = global_root_scope;
}
/**

View File

@ -46,7 +46,7 @@
#undef LOCAL_DEBUG
#include "nest/bird.h"
#include "nest/route.h"
#include "nest/rt.h"
#include "nest/protocol.h"
#include "nest/iface.h"
#include "lib/resource.h"
@ -168,7 +168,6 @@ int
cli_parse(struct config *c)
{
int done = 0;
c->fallback = config;
new_config = c;
cfg_mem = c->mem;
if (setjmp(conf_jmpbuf))
@ -179,7 +178,6 @@ cli_parse(struct config *c)
done = 1;
cleanup:
c->fallback = NULL;
new_config = NULL;
cfg_mem = NULL;
return done;
@ -520,6 +518,8 @@ order_shutdown(int gr)
memcpy(c, config, sizeof(struct config));
init_list(&c->protos);
init_list(&c->tables);
init_list(&c->symbols);
memset(c->def_tables, 0, sizeof(c->def_tables));
c->shutdown = 1;
c->gr_down = gr;

View File

@ -16,7 +16,6 @@
#include "lib/timer.h"
/* Configuration structure */
struct config {
pool *pool; /* Pool the configuration is stored in */
linpool *mem; /* Linear pool containing configuration data */
@ -45,6 +44,7 @@ struct config {
int cli_debug; /* Tracing of CLI connections and commands */
int latency_debug; /* I/O loop tracks duration of each event */
int pipe_debug; /* Track route propagation through pipes */
u32 latency_limit; /* Events with longer duration are logged (us) */
u32 watchdog_warning; /* I/O loop watchdog limit for warning (us) */
u32 watchdog_timeout; /* Watchdog timeout (in seconds, 0 = disabled) */
@ -54,8 +54,7 @@ struct config {
char *err_file_name; /* File name containing error */
char *file_name; /* Name of main configuration file */
int file_fd; /* File descriptor of main configuration file */
HASH(struct symbol) sym_hash; /* Lexer: symbol hash table */
struct config *fallback; /* Link to regular config for CLI parsing */
struct sym_scope *root_scope; /* Scope for root symbols */
int obstacle_count; /* Number of items blocking freeing of this config */
int shutdown; /* This is a pseudo-config for daemon shutdown */
@ -122,7 +121,7 @@ struct symbol {
const struct f_line *function; /* For SYM_FUNCTION */
const struct filter *filter; /* For SYM_FILTER */
struct rtable_config *table; /* For SYM_TABLE */
struct f_dynamic_attr *attribute; /* For SYM_ATTRIBUTE */
struct ea_class *attribute; /* For SYM_ATTRIBUTE */
struct f_val *val; /* For SYM_CONSTANT */
uint offset; /* For SYM_VARIABLE */
};
@ -133,10 +132,15 @@ struct symbol {
struct sym_scope {
struct sym_scope *next; /* Next on scope stack */
struct symbol *name; /* Name of this scope */
HASH(struct symbol) hash; /* Local symbol hash */
uint slots; /* Variable slots */
int active; /* Currently entered */
};
extern struct sym_scope *global_root_scope;
struct bytestring {
size_t length;
byte data[];
@ -185,7 +189,14 @@ int cf_lex(void);
void cf_lex_init(int is_cli, struct config *c);
void cf_lex_unwind(void);
struct symbol *cf_find_symbol(const struct config *cfg, const byte *c);
struct symbol *cf_find_symbol_scope(const struct sym_scope *scope, const byte *c);
static inline struct symbol *cf_find_symbol_cfg(const struct config *cfg, const byte *c)
{ return cf_find_symbol_scope(cfg->root_scope, c); }
#define cf_find_symbol(where, what) _Generic(*(where), \
struct config: cf_find_symbol_cfg, \
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);

View File

@ -18,7 +18,7 @@ CF_HDR
#include "lib/string.h"
#include "nest/protocol.h"
#include "nest/iface.h"
#include "nest/route.h"
#include "nest/rt.h"
#include "nest/bfd.h"
#include "nest/cli.h"
#include "filter/filter.h"
@ -71,8 +71,9 @@ CF_DECLS
} xp;
enum filter_return fret;
enum ec_subtype ecs;
struct f_dynamic_attr fda;
struct ea_class *ea_class;
struct f_static_attr fsa;
struct f_attr_bit fab;
struct f_lval flv;
struct f_line *fl;
const struct filter *f;
@ -91,7 +92,7 @@ CF_DECLS
struct proto_spec ps;
struct channel_limit cl;
struct timeformat *tf;
mpls_label_stack *mls;
struct adata *ad;
struct bytestring *bs;
}
@ -112,14 +113,15 @@ CF_DECLS
%type <a> ipa
%type <net> net_ip4_ net_ip6_ net_ip6 net_ip_ net_ip net_or_ipa
%type <net_ptr> net_ net_any net_vpn4_ net_vpn6_ net_vpn_ net_roa4_ net_roa6_ net_roa_ net_ip6_sadr_ net_mpls_
%type <mls> label_stack_start label_stack
%type <ad> label_stack_start label_stack
%type <t> text opttext
%type <s> symbol
%type <s> symbol symbol_known toksym
%nonassoc PREFIX_DUMMY
%left AND OR
%nonassoc '=' '<' '>' '~' GEQ LEQ NEQ NMA PO PC
%left '|' '&'
%left '+' '-'
%left '*' '/' '%'
%left '!'
@ -151,16 +153,16 @@ conf: definition ;
definition:
DEFINE symbol '=' term ';' {
struct f_val *val = cfg_allocz(sizeof(struct f_val));
if (f_eval(f_linearize($4), cfg_mem, val) > F_RETURN) cf_error("Runtime error");
cf_define_symbol($2, SYM_CONSTANT | val->type, val, val);
struct f_val val;
if (f_eval(f_linearize($4), &val) > F_RETURN) cf_error("Runtime error");
cf_define_symbol($2, SYM_CONSTANT | val.type, val, lp_val_copy(cfg_mem, &val));
}
;
expr:
NUM
| '(' term ')' { $$ = f_eval_int(f_linearize($2)); }
| CF_SYM_KNOWN {
| symbol_known {
if ($1->class != (SYM_CONSTANT | T_INT)) cf_error("Number constant expected");
$$ = SYM_VAL($1).i; }
;
@ -171,7 +173,9 @@ expr_us:
| expr US { $$ = $1 US_; }
;
symbol: CF_SYM_UNDEFINED | CF_SYM_KNOWN ;
toksym: FROM ;
symbol: CF_SYM_UNDEFINED | CF_SYM_KNOWN | toksym ;
symbol_known: CF_SYM_KNOWN | toksym ;
/* Switches */
@ -347,17 +351,19 @@ net_or_ipa:
label_stack_start: NUM
{
$$ = cfg_allocz(sizeof(mpls_label_stack));
$$->len = 1;
$$->stack[0] = $1;
$$ = cfg_allocz(ADATA_SIZE(MPLS_MAX_LABEL_STACK * sizeof(u32)));
$$->length = sizeof(u32);
*((u32 *)$$->data) = $1;
};
label_stack:
label_stack_start
| label_stack '/' NUM {
if ($1->len >= MPLS_MAX_LABEL_STACK)
if ($1->length >= MPLS_MAX_LABEL_STACK * sizeof(u32))
cf_error("Too many labels in stack");
$1->stack[$1->len++] = $3;
*((u32 *)($$->data + $1->length)) = $3;
$1->length += sizeof(u32);
$$ = $1;
}
;

View File

@ -26,8 +26,7 @@ m4_define(CF_DEFINES, `m4_divert(-1)')
m4_define(CF_handle_kw, `m4_divert(1){ "m4_translit($1,[[A-Z]],[[a-z]])", $1, NULL },
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, `m4_define([[CF_toks]],[[]])CF_iterate([[CF_keywd]], [[$@]])m4_ifelse(CF_toks,,,%token[[]]CF_toks
)DNL')
m4_define(CF_KEYWORDS, `CF_iterate([[CF_keywd]], [[$@]])DNL')
# CLI commands generate keywords as well
m4_define(CF_CLI, `CF_KEYWORDS(m4_translit($1, [[ ]], [[,]]))

View File

@ -31,7 +31,7 @@ m4_define(CF_iterate, `m4_define([[CF_iter]], m4_defn([[$1]]))CF_itera($2)')
# Keywords act as untyped %token
m4_define(CF_keywd, `m4_ifdef([[CF_tok_$1]],,[[m4_define([[CF_tok_$1]],1)m4_define([[CF_toks]],CF_toks $1)]])')
m4_define(CF_KEYWORDS, `m4_define([[CF_toks]],[[]])CF_iterate([[CF_keywd]], [[$@]])m4_ifelse(CF_toks,,,%token[[]]CF_toks
m4_define(CF_KEYWORDS, `m4_define([[CF_toks]],[[]])CF_iterate([[CF_keywd]], [[$@]])m4_ifelse(CF_toks,,,%token<s>[[]]CF_toks
)DNL')
# CLI commands

View File

@ -360,6 +360,13 @@ AC_C_BIGENDIAN(
[AC_MSG_ERROR([Cannot determine CPU endianity.])]
)
BIRD_CHECK_POINTER_ALIGNMENT
if test "$bird_cv_pointer_alignment" = "unknown" ; then
AC_MSG_ERROR([Couldn't determine pointer alignment])
else
AC_DEFINE_UNQUOTED([CPU_POINTER_ALIGNMENT], [$bird_cv_pointer_alignment], [Pointer alignment for macro usage])
fi
BIRD_CHECK_ANDROID_GLOB
if test "$bird_cv_lib_glob" = no ; then
AC_MSG_ERROR([glob.h not found.])

View File

@ -5,8 +5,9 @@ After=network.target
[Service]
Type=simple
ExecStartPre=/usr/sbin/bird -p
ExecStart=/usr/sbin/bird -f -u bird -g bird
ExecReload=/bin/kill -HUP $MAINPID
ExecReload=/usr/sbin/birdc configure
Restart=on-failure
[Install]

View File

@ -14,7 +14,7 @@ configuration - something in config which is not keyword.
(set-fill-column 80)
Copyright 1999,2000 Pavel Machek <pavel@ucw.cz>, distribute under GPL version 2 or later.
Copyright 1999 - 2022 CZ.NIC, z.s.p.o , distribute under GPL version 2 or later.
-->
@ -23,7 +23,6 @@ configuration - something in config which is not keyword.
<title>BIRD 2.0 User's Guide
<author>
Ondrej Filip <it/&lt;feela@network.cz&gt;/,
Pavel Machek <it/&lt;pavel@ucw.cz&gt;/,
Martin Mares <it/&lt;mj@ucw.cz&gt;/,
Maria Matejka <it/&lt;mq@jmq.cz&gt;/,
Ondrej Zajicek <it/&lt;santiago@crfreenet.org&gt;/
@ -145,13 +144,6 @@ BIRD executable by configuring out routing protocols you don't use, and
<p>You can pass several command-line options to bird:
<descrip>
<tag><label id="argv-block">-B <m/exp/</tag>
allocate memory using 2^<cf/exp/ byte sized blocks;
if you're expecting high memory load, raise this to
reduce number of allocated memory pages. For a million routes
in one table, the recommended setting is 18.
Default is your system page size, typically 12 for 4096 bytes.
<tag><label id="argv-config">-c <m/config name/</tag>
use given configuration file instead of <it/prefix/<file>/etc/bird.conf</file>.
@ -259,16 +251,9 @@ The global best route selection algorithm is (roughly) as follows:
</itemize>
<p><label id="dsc-table-sorted">Usually, a routing table just chooses a selected
route from a list of entries for one network. But if the <cf/sorted/ option is
activated, these lists of entries are kept completely sorted (according to
preference or some protocol-dependent metric). This is needed for some features
of some protocols (e.g. <cf/secondary/ option of BGP protocol, which allows to
accept not just a selected route, but the first route (in the sorted list) that
is accepted by filters), but it is incompatible with some other features (e.g.
<cf/deterministic med/ option of BGP protocol, which activates a way of choosing
selected route that cannot be described using comparison and ordering). Minor
advantage is that routes are shown sorted in <cf/show route/, minor disadvantage
is that it is slightly more computationally expensive.
route from a list of entries for one network. Optionally, these lists of entries
are kept completely sorted (according to preference or some protocol-dependent
metric). See <ref id="rtable-sorted" name="sorted"> table option for details.
<sect>Routes and network types
<label id="routes">
@ -635,18 +620,73 @@ include "tablename.conf";;
<cf/protocol/ times, and the <cf/iso long ms/ format for <cf/base/ and
<cf/log/ times.
<tag><label id="opt-table"><m/nettype/ table <m/name/ [sorted]</tag>
Create a new routing table. The default routing tables <cf/master4/ and
<cf/master6/ are created implicitly, other routing tables have to be
added by this command. Option <cf/sorted/ can be used to enable sorting
of routes, see <ref id="dsc-table-sorted" name="sorted table">
description for details.
<tag><label id="opt-table"><m/nettype/ table <m/name/ [ { <m/option/; [<m/.../] } ]</tag>
Define a new routing table. The default routing tables <cf/master4/ and
<cf/master6/ are defined implicitly, other routing tables have to be
defined by this option. See the <ref id="rtable-opts"
name="routing table configuration section"> for routing table options.
<tag><label id="opt-eval">eval <m/expr/</tag>
Evaluates given filter expression. It is used by the developers for testing of filters.
</descrip>
<sect>Routing table options
<label id="rtable-opts">
<p>Most routing tables do not need any options and are defined without an option
block, but there are still some options to tweak routing table behavior. Note
that implicit tables (<cf/master4/ and <cf/master6/) can be redefined in order
to set options.
<descrip>
<tag><label id="rtable-sorted">sorted <m/switch/</tag>
Usually, a routing table just chooses the selected (best) route from a
list of routes for each network, while keeping remaining routes unsorted.
If enabled, these lists of routes are kept completely sorted (according
to preference or some protocol-dependent metric).
This is needed for some protocol features (e.g. <cf/secondary/ option of
BGP protocol, which allows to accept not just a selected route, but the
first route (in the sorted list) that is accepted by filters), but it is
incompatible with some other features (e.g. <cf/deterministic med/
option of BGP protocol, which activates a way of choosing selected route
that cannot be described using comparison and ordering). Minor advantage
is that routes are shown sorted in <cf/show route/, minor disadvantage
is that it is slightly more computationally expensive. Default: off.
<tag><label id="rtable-trie">trie <m/switch/</tag>
BIRD routing tables are implemented with hash tables, which is efficient
for exact-match lookups, but inconvenient for longest-match lookups or
interval lookups (finding superprefix or subprefixes). This option
activates additional trie structure that is used to accelerate these
lookups, while using the hash table for exact-match lookups.
This has advantage for <ref id="rpki" name="RPKI"> (on ROA tables),
for <ref id="bgp-gateway" name="recursive next-hops"> (on IGP tables),
and is required for <ref id="bgp-validate" name="flowspec validation">
(on base IP tables). Another advantage is that interval results (like
from <cf/show route in .../ command) are lexicographically sorted. The
disadvantage is that trie-enabled routing tables require more memory,
which may be an issue especially in multi-table setups. Default: off.
<tag><label id="rtable-min-settle-time">min settle time <m/time/</tag>
Specify a minimum value of the settle time. When a ROA table changes,
automatic <ref id="proto-rpki-reload" name="RPKI reload"> may be
triggered, after a short settle time. Minimum settle time is a delay
from the last ROA table change to wait for more updates. Default: 1 s.
<tag><label id="rtable-max-settle-time">max settle time <m/time/</tag>
Specify a maximum value of the settle time. When a ROA table changes,
automatic <ref id="proto-rpki-reload" name="RPKI reload"> may be
triggered, after a short settle time. Maximum settle time is an upper
limit to the settle time from the initial ROA table change even if
there are consecutive updates gradually renewing the settle time.
Default: 20 s.
</descrip>
<sect>Protocol options
<label id="protocol-opts">
@ -1299,6 +1339,9 @@ in the foot).
The same syntax can also be used to construct a pair from two arbitrary
integer expressions (for example <cf/(1+2,a)/).
Operators <cf/.asn/ and <cf/.data/ can be used to extract corresponding
components of a pair: <cf>(<m/asn/, <m/data/)</cf>.
<tag><label id="type-quad">quad</tag>
This is a dotted quad of numbers used to represent router IDs (and
others). Each component can have a value from 0 to 255. Literals of
@ -1389,6 +1432,10 @@ in the foot).
pairs, LCs can be constructed using expressions for its parts, (e.g.
<cf/(myas, 10+20, 3*10)/, where <cf/myas/ is an integer variable).
Operators <cf/.asn/, <cf/.data1/, and <cf/.data2/ can be used
to extract corresponding components of LCs:
<cf>(<m/asn/, <m/data1/, <m/data2/)</cf>.
<tag><label id="type-set">int|pair|quad|ip|prefix|ec|lc|enum set</tag>
Filters recognize four types of sets. Sets are similar to strings: you
can pass them around but you can't modify them. Literals of type <cf>int
@ -1532,7 +1579,7 @@ in the foot).
Clist is similar to a set, except that unlike other sets, it can be
modified. The type is used for community list (a set of pairs) and for
cluster list (a set of quads). There exist no literals of this type.
There are three special operators on clists:
There are special operators on clists:
<cf><m/C/.len</cf> returns the length of clist <m/C/.
@ -1559,6 +1606,15 @@ in the foot).
<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/.
<cf><m/C/.min</cf> returns the minimum element of clist <m/C/.
<cf><m/C/.max</cf> returns the maximum element of clist <m/C/.
Operators <cf/.min/, <cf/.max/ can be used together with <cf/filter/
to extract the community from the specific subset of communities
(e.g. localpref or prepend) without the need to check every possible
value (e.g. <cf/filter(bgp_community, [(23456, 1000..1099)]).min/).
<tag><label id="type-eclist">eclist</tag>
Eclist is a data type used for BGP extended community lists. Eclists
are very similar to clists, but they are sets of ECs instead of pairs.
@ -1667,17 +1723,8 @@ Common route attributes are:
primary key of the routing table. Read-only. (See the <ref id="routes"
name="chapter about routes">.)
<tag><label id="rta-scope"><m/enum/ scope</tag>
The scope of the route. Possible values: <cf/SCOPE_HOST/ for routes
local to this host, <cf/SCOPE_LINK/ for those specific for a physical
link, <cf/SCOPE_SITE/ and <cf/SCOPE_ORGANIZATION/ for private routes and
<cf/SCOPE_UNIVERSE/ for globally visible routes. This attribute is not
interpreted by BIRD and can be used to mark routes in filters. The
default value for new routes is <cf/SCOPE_UNIVERSE/.
<tag><label id="rta-preference"><m/int/ preference</tag>
Preference of the route. Valid values are 0-65535. (See the chapter
about routing tables.)
Preference of the route.
<tag><label id="rta-from"><m/ip/ from</tag>
The router which the route has originated from.
@ -2097,6 +2144,13 @@ protocol bfd [&lt;name&gt;] {
to configure separate BFD protocol instances for IPv4 and for IPv6
sessions.
<tag><label id="bfd-strict-bind">strict bind <m/switch/</tag>
Specify whether each BFD interface should use a separate listening
socket bound to its local address, or just use a shared listening socket
accepting all addresses. Binding to a specific address could be useful
in cases like running multiple BIRD instances on a machine, each
handling a different set of interfaces. Default: disabled.
<tag><label id="bfd-iface">interface <m/pattern/ [, <m/.../] { <m/options/ }</tag>
Interface definitions allow to specify options for sessions associated
with such interfaces and also may contain interface specific options.
@ -2281,6 +2335,7 @@ avoid routing loops.
<item> <rfc id="8092"> - BGP Large Communities Attribute
<item> <rfc id="8203"> - BGP Administrative Shutdown Communication
<item> <rfc id="8212"> - Default EBGP Route Propagation Behavior without Policies
<item> <rfc id="9117"> - Revised Validation Procedure for BGP Flow Specifications
</itemize>
<sect1>Route selection rules
@ -2403,6 +2458,12 @@ using the following configuration parameters:
same address family and using the same local port) should have set
<cf/strict bind/, or none of them. Default: disabled.
<tag><label id="bgp-free-bind">free bind <m/switch/</tag>
Use IP_FREEBIND socket option for the listening socket, which allows
binding to an IP address not (yet) assigned to an interface. Note that
all BGP instances that share a listening socket should have the same
value of the <cf/freebind/ option. Default: disabled.
<tag><label id="bgp-check-link">check link <M>switch</M></tag>
BGP could use hardware link state into consideration. If enabled,
BIRD tracks the link state of the associated interface and when link
@ -2614,13 +2675,6 @@ using the following configuration parameters:
disabled. Default: on, with automatic fallback to off when received
capability-related error.
<tag><label id="bgp-advertise-ipv4">advertise ipv4 <m/switch/</tag>
Advertise IPv4 multiprotocol capability. This is not a correct behavior
according to the strict interpretation of <rfc id="4760">, but it is
widespread and required by some BGP implementations (Cisco and Quagga).
This option is relevant to IPv4 mode with enabled capability
advertisement only. Default: on.
<tag><label id="bgp-advertise-hostname">advertise hostname <m/switch/</tag>
Advertise hostname capability along with the hostname. Default: off.
@ -2666,7 +2720,7 @@ using the following configuration parameters:
<tag><label id="bgp-error-wait-time">error wait time <m/number/,<m/number/</tag>
Minimum and maximum delay in seconds between a protocol failure (either
local or reported by the peer) and automatic restart. Doesn't apply
local or reported by the peer) and automatic restart. Doesn not apply
when <cf/disable after error/ is configured. If consecutive errors
happen, the delay is increased exponentially until it reaches the
maximum. Default: 60, 300.
@ -2844,6 +2898,31 @@ be used in explicit configuration.
explicitly (to conserve memory). This option requires that the connected
routing table is <ref id="dsc-table-sorted" name="sorted">. Default: off.
<tag><label id="bgp-validate">validate <m/switch/</tag>
Apply flowspec validation procedure as described in <rfc id="8955">
section 6 and <rfc id="9117">. The Validation procedure enforces that
only routers in the forwarding path for a network can originate flowspec
rules for that network. The validation procedure should be used for EBGP
to prevent injection of malicious flowspec rules from outside, but it
should also be used for IBGP to ensure that selected flowspec rules are
consistent with selected IP routes. The validation procedure uses an IP
routing table (<ref id="bgp-base-table" name="base table">, see below)
against which flowspec rules are validated. This option is limited to
flowspec channels. Default: off (for compatibility reasons).
Note that currently the flowspec validation does not work reliably
together with <ref id="bgp-import-table" name="import table"> option
enabled on flowspec channels.
<tag><label id="bgp-base-table">base table <m/name/</tag>
Specifies an IP table used for the flowspec validation procedure. The
table must have enabled <cf/trie/ option, otherwise the validation
procedure would not work. The type of the table must be <cf/ipv4/ for
<cf/flow4/ channels and <cf/ipv6/ for <cf/flow6/ channels. This option
is limited to flowspec channels. Default: the main table of the
<cf/ipv4/ / <cf/ipv6/ channel of the same BGP instance, or the
<cf/master4/ / <cf/master6/ table if there is no such channel.
<tag><label id="bgp-extended-next-hop">extended next hop <m/switch/</tag>
BGP expects that announced next hops have the same address family as
associated network prefixes. This option provides an extension to use
@ -3240,6 +3319,12 @@ channels.
allows to specify a limit on maximal number of nexthops in one route. By
default, multipath merging is disabled. If enabled, default value of the
limit is 16.
<tag><label id="krt-netlink-rx-buffer">netlink rx buffer <m/number/</tag> (Linux)
Set kernel receive buffer size (in bytes) for the netlink socket. The default
value is OS-dependent (from the <file>/proc/sys/net/core/rmem_default</file>
file), If you get some "Kernel dropped some netlink message ..." warnings,
you may increase this value.
</descrip>
<sect1>Attributes
@ -4124,6 +4209,14 @@ include standard channel config options; see the example below.
<tag><label id="pipe-peer-table">peer table <m/table/</tag>
Defines secondary routing table to connect to. The primary one is
selected by the <cf/table/ keyword.
<tag><label id="pipe-max-generation">max generation <m/expr/</tag>
Sets maximal generation of route that may pass through this pipe.
The generation value is increased by one by each pipe on its path.
Not meeting this requirement causes an error message complaining about
an overpiped route. If you have long chains of pipes, you probably want
to raise this value; anyway the default of 16 should be enough for even
most strange uses. Maximum is 254.
</descrip>
<sect1>Attributes
@ -5157,7 +5250,7 @@ Note that for negated matches, value must be either zero or equal to bitmask
<cf>port 1..1023,1194,3306</cf>).
<tag><label id="flow-dport">dport <m/numbers-match/</tag>
Set a mating destination port numbers (e.g. <cf>dport 49151</cf>).
Set a matching destination port numbers (e.g. <cf>dport 49151</cf>).
<tag><label id="flow-sport">sport <m/numbers-match/</tag>
Set a matching source port numbers (e.g. <cf>sport = 0</cf>).
@ -5348,15 +5441,15 @@ name="atrey.karlin.mff.cuni.cz:/pub/rfc">).
</book>
<!--
LocalWords: GPL IPv GateD BGPv RIPv OSPFv Linux sgml html dvi sgmltools Pavel
LocalWords: GPL IPv GateD BGPv RIPv OSPFv Linux sgml html dvi sgmltools
LocalWords: linuxdoc dtd descrip config conf syslog stderr auth ospf bgp Mbps
LocalWords: router's eval expr num birdc ctl UNIX if's enums bool int ip GCC
LocalWords: len ipaddress pxlen netmask enum bgppath bgpmask clist gw md eth
LocalWords: RTS printn quitbird iBGP AS'es eBGP RFC multiprotocol IGP Machek
LocalWords: RTS printn quitbird iBGP AS'es eBGP RFC multiprotocol IGP
LocalWords: EGP misconfigurations keepalive pref aggr aggregator BIRD's RTC
LocalWords: OS'es AS's multicast nolisten misconfigured UID blackhole MRTD MTU
LocalWords: uninstalls ethernets IP binutils ANYCAST anycast dest RTD ICMP rfc
LocalWords: compat multicasts nonbroadcast pointopoint loopback sym stats
LocalWords: Perl SIGHUP dd mm yy HH MM SS EXT IA UNICAST multihop Discriminator txt
LocalWords: proto wildcard Ondrej Filip
LocalWords: proto wildcard
-->

View File

@ -12,9 +12,9 @@
<title>BIRD Programmer's Documentation
<author>
Ondrej Filip <it/&lt;feela@network.cz&gt;/,
Pavel Machek <it/&lt;pavel@ucw.cz&gt;/,
Martin Mares <it/&lt;mj@ucw.cz&gt;/,
Ondrej Zajicek <it/&lt;santiago@crfreenet.org&gt;/
Maria Matejka <it/&lt;mq@jmq.cz&gt;/,
</author>
<abstract>

View File

@ -22,6 +22,13 @@ 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)
#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); \
})
/*
* Sets and their items are during parsing handled as lists, linked
* through left ptr. The first item in a list also contains a pointer
@ -161,28 +168,32 @@ f_new_lc_item(u32 f1, u32 t1, u32 f2, u32 t2, u32 f3, u32 t3)
}
static inline struct f_inst *
f_generate_empty(struct f_dynamic_attr dyn)
f_generate_empty(const struct symbol *sym)
{
struct f_val empty;
if (sym->class != SYM_ATTRIBUTE)
cf_error("Can't empty %s: not an attribute", sym->name);
switch (dyn.type & EAF_TYPE_MASK) {
case EAF_TYPE_AS_PATH:
empty = f_const_empty_path;
break;
case EAF_TYPE_INT_SET:
empty = f_const_empty_clist;
break;
case EAF_TYPE_EC_SET:
empty = f_const_empty_eclist;
break;
case EAF_TYPE_LC_SET:
empty = f_const_empty_lclist;
break;
default:
cf_error("Can't empty that attribute");
}
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), dyn);
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);
}
/*
@ -274,14 +285,15 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
IF, THEN, ELSE, CASE,
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS,
PREFERENCE,
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,
DEFINED,
ADD, DELETE, CONTAINS, RESET,
PREPEND, FIRST, LAST, LAST_NONAGGREGATED, MATCH,
ADD, DELETE, RESET,
PREPEND, FIRST, LAST, LAST_NONAGGREGATED,
MIN, MAX,
EMPTY,
FILTER, WHERE, EVAL, ATTRIBUTE,
BT_ASSERT, BT_TEST_SUITE, BT_CHECK_ASSIGN, BT_TEST_SAME, FORMAT, STACKS)
@ -291,8 +303,8 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
%type <xp> cmds_int cmd_prep
%type <x> term block cmd cmds constant constructor print_list var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail
%type <fda> dynamic_attr
%type <fsa> static_attr
%type <fab> attr_bit
%type <f> filter where_filter
%type <fl> filter_body function_body
%type <flv> lvalue
@ -333,12 +345,19 @@ filter_eval:
conf: custom_attr ;
custom_attr: ATTRIBUTE type symbol ';' {
cf_define_symbol($3, SYM_ATTRIBUTE, attribute, ca_lookup(new_config->pool, $3->name, $2)->fda);
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) {
.name = $3->name,
.type = $2,
})->class);
};
conf: bt_test_suite ;
bt_test_suite:
BT_TEST_SUITE '(' CF_SYM_KNOWN ',' text ')' {
BT_TEST_SUITE '(' symbol_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;
@ -351,7 +370,7 @@ bt_test_suite:
conf: bt_test_same ;
bt_test_same:
BT_TEST_SAME '(' CF_SYM_KNOWN ',' CF_SYM_KNOWN ',' NUM ')' {
BT_TEST_SAME '(' symbol_known ',' symbol_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,7 +448,7 @@ function_vars:
filter_body: function_body ;
filter:
CF_SYM_KNOWN {
symbol_known {
cf_assert_symbol($1, SYM_FILTER);
$$ = $1->filter;
}
@ -527,10 +546,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), cfg_mem, &($$)) > F_RETURN) cf_error("Runtime error");
if (f_eval(f_linearize($2), &($$)) > F_RETURN) cf_error("Runtime error");
if (!f_valid_set_type($$.type)) cf_error("Set-incompatible type");
}
| CF_SYM_KNOWN {
| symbol_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;
@ -700,7 +719,7 @@ var_list: /* EMPTY */ { $$ = NULL; }
| var_list ',' term { $$ = $3; $$->next = $1; }
function_call:
CF_SYM_KNOWN '(' var_list ')' {
symbol_known '(' var_list ')' {
if ($1->class != SYM_FUNCTION)
cf_error("You can't call something which is not a function. Really.");
@ -724,7 +743,7 @@ function_call:
}
;
symbol_value: CF_SYM_KNOWN
symbol_value: symbol_known
{
switch ($1->class) {
case SYM_CONSTANT_RANGE:
@ -734,7 +753,7 @@ symbol_value: CF_SYM_KNOWN
$$ = f_new_inst(FI_VAR_GET, $1);
break;
case SYM_ATTRIBUTE:
$$ = f_new_inst(FI_EA_GET, *$1->attribute);
$$ = f_new_inst(FI_EA_GET, $1->attribute);
break;
default:
cf_error("Can't get value of symbol %s", $1->name);
@ -743,17 +762,13 @@ symbol_value: CF_SYM_KNOWN
;
static_attr:
FROM { $$ = f_new_static_attr(T_IP, SA_FROM, 0); }
| GW { $$ = f_new_static_attr(T_IP, SA_GW, 0); }
GW { $$ = f_new_static_attr(T_IP, SA_GW, 0); }
| NET { $$ = f_new_static_attr(T_NET, SA_NET, 1); }
| PROTO { $$ = f_new_static_attr(T_STRING, SA_PROTO, 1); }
| SOURCE { $$ = f_new_static_attr(T_ENUM_RTS, SA_SOURCE, 1); }
| SCOPE { $$ = f_new_static_attr(T_ENUM_SCOPE, SA_SCOPE, 0); }
| DEST { $$ = f_new_static_attr(T_ENUM_RTD, SA_DEST, 0); }
| IFNAME { $$ = f_new_static_attr(T_STRING, SA_IFNAME, 0); }
| IFINDEX { $$ = f_new_static_attr(T_INT, SA_IFINDEX, 1); }
| WEIGHT { $$ = f_new_static_attr(T_INT, SA_WEIGHT, 0); }
| PREFERENCE { $$ = f_new_static_attr(T_INT, SA_PREF, 0); }
| GW_MPLS { $$ = f_new_static_attr(T_INT, SA_GW_MPLS, 0); }
;
@ -763,6 +778,8 @@ term:
| term '-' term { $$ = f_new_inst(FI_SUBTRACT, $1, $3); }
| term '*' term { $$ = f_new_inst(FI_MULTIPLY, $1, $3); }
| term '/' term { $$ = f_new_inst(FI_DIVIDE, $1, $3); }
| term '&' term { $$ = f_new_inst(FI_BITAND, $1, $3); }
| term '|' term { $$ = f_new_inst(FI_BITOR, $1, $3); }
| term AND term { $$ = f_new_inst(FI_AND, $1, $3); }
| term OR term { $$ = f_new_inst(FI_OR, $1, $3); }
| term '=' term { $$ = f_new_inst(FI_EQ, $1, $3); }
@ -781,8 +798,10 @@ term:
| constructor { $$ = $1; }
| static_attr { $$ = f_new_inst(FI_RTA_GET, $1); }
| dynamic_attr { $$ = f_new_inst(FI_EA_GET, $1); }
| attr_bit {
struct f_inst *c = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = (1U << $1.bit)});
$$ = f_new_inst(FI_EQ, c, f_new_inst(FI_BITAND, f_new_inst(FI_EA_GET, $1.class), c));
}
| term '.' IS_V4 { $$ = f_new_inst(FI_IS_V4, $1); }
| term '.' TYPE { $$ = f_new_inst(FI_TYPE, $1); }
@ -790,13 +809,18 @@ term:
| 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_ROA_ASN, $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); }
/* Communities */
/* This causes one shift/reduce conflict
@ -815,13 +839,11 @@ term:
| DELETE '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_DEL, $3, $5); }
| FILTER '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_FILTER, $3, $5); }
| ROA_CHECK '(' rtable ')' { $$ = f_new_inst(FI_ROA_CHECK_IMPLICIT, $3); }
| ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK_EXPLICIT, $5, $7, $3); }
| ROA_CHECK '(' rtable ')' { $$ = f_implicit_roa_check($3); }
| ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK, $5, $7, $3); }
| FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT, $3); }
/* | term '.' LEN { $$->code = P('P','l'); } */
| function_call
;
@ -848,13 +870,15 @@ cmd:
| IF term THEN block ELSE block {
$$ = f_new_inst(FI_CONDITION, $2, $4, $6);
}
| CF_SYM_KNOWN '=' term ';' {
| symbol_known '=' term ';' {
switch ($1->class) {
case SYM_VARIABLE_RANGE:
$$ = f_new_inst(FI_VAR_SET, $3, $1);
break;
case SYM_ATTRIBUTE:
$$ = f_new_inst(FI_EA_SET, $3, *$1->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);
@ -864,16 +888,25 @@ cmd:
DBG( "Ook, we'll return the value\n" );
$$ = f_new_inst(FI_RETURN, $2);
}
| dynamic_attr '=' term ';' {
$$ = f_new_inst(FI_EA_SET, $3, $1);
}
| static_attr '=' term ';' {
if ($1.readonly)
cf_error( "This static attribute is read-only.");
$$ = f_new_inst(FI_RTA_SET, $3, $1);
}
| UNSET '(' dynamic_attr ')' ';' {
$$ = f_new_inst(FI_EA_UNSET, $3);
| UNSET '(' symbol_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);
}
| attr_bit '=' term ';' {
$$ = f_new_inst(FI_CONDITION, $3,
f_generate_complex(FI_BITOR, $1.class,
f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = (1U << $1.bit)})),
f_generate_complex(FI_BITAND, $1.class,
f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = ~(1U << $1.bit)}))
);
}
| break_command print_list ';' {
struct f_inst *breaker = f_new_inst(FI_DIE, $1);
@ -898,11 +931,11 @@ cmd:
$$ = f_new_inst(FI_SWITCH, $2, build_tree($4));
}
| dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($1); }
| dynamic_attr '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex( FI_PATH_PREPEND, $1, $5 ); }
| dynamic_attr '.' ADD '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_ADD, $1, $5 ); }
| dynamic_attr '.' DELETE '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_DEL, $1, $5 ); }
| dynamic_attr '.' FILTER '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_FILTER, $1, $5 ); }
| 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 ); }
| 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); }
;
@ -913,8 +946,17 @@ get_cf_position:
};
lvalue:
CF_SYM_KNOWN { cf_assert_symbol($1, SYM_VARIABLE); $$ = (struct f_lval) { .type = F_LVAL_VARIABLE, .sym = $1 }; }
symbol_known {
switch ($1->class) {
case SYM_VARIABLE_RANGE:
$$ = (struct f_lval) { .type = F_LVAL_VARIABLE, .sym = $1 };
break;
case SYM_ATTRIBUTE:
$$ = (struct f_lval) { .type = F_LVAL_EA, .da = $1->attribute };
break;
}
}
| static_attr { $$ = (struct f_lval) { .type = F_LVAL_SA, .sa = $1 }; }
| dynamic_attr { $$ = (struct f_lval) { .type = F_LVAL_EA, .da = $1 }; };
;
CF_END

View File

@ -16,10 +16,10 @@
#include "lib/unaligned.h"
#include "lib/net.h"
#include "lib/ip.h"
#include "nest/route.h"
#include "nest/rt.h"
#include "nest/protocol.h"
#include "nest/iface.h"
#include "nest/attrs.h"
#include "lib/attrs.h"
#include "conf/conf.h"
#include "filter/filter.h"
#include "filter/f-inst.h"
@ -27,6 +27,8 @@
static const char * const f_type_str[] = {
[T_VOID] = "void",
[T_OPAQUE] = "opaque byte string",
[T_IFACE] = "interface",
[T_INT] = "int",
[T_BOOL] = "bool",
@ -36,7 +38,6 @@ static const char * const f_type_str[] = {
[T_ENUM_RTS] = "enum rts",
[T_ENUM_BGP_ORIGIN] = "enum bgp_origin",
[T_ENUM_SCOPE] = "enum scope",
[T_ENUM_RTC] = "enum rtc",
[T_ENUM_RTD] = "enum rtd",
[T_ENUM_ROA] = "enum roa",
[T_ENUM_NETTYPE] = "enum nettype",
@ -47,6 +48,7 @@ static const char * const f_type_str[] = {
[T_NET] = "prefix",
[T_STRING] = "string",
[T_PATH_MASK] = "bgpmask",
[T_PATH_MASK_ITEM] = "bgpmask item",
[T_PATH] = "bgppath",
[T_CLIST] = "clist",
[T_EC] = "ec",
@ -54,18 +56,26 @@ static const char * const f_type_str[] = {
[T_LC] = "lc",
[T_LCLIST] = "lclist",
[T_RD] = "rd",
[T_SET] = "set",
[T_PREFIX_SET] = "prefix set",
};
const char *
f_type_name(enum f_type t)
f_type_name(btype t)
{
if (t < ARRAY_SIZE(f_type_str))
return f_type_str[t] ?: "?";
return (t < ARRAY_SIZE(f_type_str)) ? (f_type_str[t] ?: "?") : "?";
}
if ((t == T_SET) || (t == T_PREFIX_SET))
return "set";
return "?";
btype
f_type_element_type(btype t)
{
switch(t) {
case T_CLIST: return T_PAIR;
case T_ECLIST: return T_EC;
case T_LCLIST: return T_LC;
default: return T_VOID;
};
}
const struct f_val f_const_empty_path = {
@ -82,14 +92,6 @@ const struct f_val f_const_empty_path = {
.val.ad = &null_adata,
};
static struct adata *
adata_empty(struct linpool *pool, int l)
{
struct adata *res = lp_alloc(pool, sizeof(struct adata) + l);
res->length = l;
return res;
}
static void
pm_format(const struct f_path_mask *p, buffer *buf)
{
@ -412,7 +414,7 @@ clist_filter(struct linpool *pool, const struct adata *list, const struct f_val
if (nl == list->length)
return list;
struct adata *res = adata_empty(pool, nl);
struct adata *res = lp_alloc_adata(pool, nl);
memcpy(res->data, tmp, nl);
return res;
}
@ -446,7 +448,7 @@ eclist_filter(struct linpool *pool, const struct adata *list, const struct f_val
if (nl == list->length)
return list;
struct adata *res = adata_empty(pool, nl);
struct adata *res = lp_alloc_adata(pool, nl);
memcpy(res->data, tmp, nl);
return res;
}
@ -478,7 +480,7 @@ lclist_filter(struct linpool *pool, const struct adata *list, const struct f_val
if (nl == list->length)
return list;
struct adata *res = adata_empty(pool, nl);
struct adata *res = lp_alloc_adata(pool, nl);
memcpy(res->data, tmp, nl);
return res;
}
@ -599,3 +601,75 @@ 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)
{
switch (v->type)
{
case T_VOID:
case T_BOOL:
case T_INT:
case T_IP:
case T_PAIR:
case T_QUAD:
case T_EC:
case T_LC:
case T_RD:
case T_ENUM:
case T_PATH_MASK_ITEM:
/* These aren't embedded but there is no need to copy them */
case T_SET:
case T_PREFIX_SET:
case T_PATH_MASK:
case T_IFACE:
{
struct f_val *out = lp_alloc(lp, sizeof(*out));
*out = *v;
return out;
}
case T_NET:
{
struct {
struct f_val val;
net_addr net[0];
} *out = lp_alloc(lp, sizeof(*out) + v->val.net->length);
out->val = *v;
out->val.val.net = out->net;
net_copy(out->net, v->val.net);
return &out->val;
}
case T_STRING:
{
uint len = strlen(v->val.s);
struct {
struct f_val val;
char buf[0];
} *out = lp_alloc(lp, sizeof(*out) + len + 1);
out->val = *v;
out->val.val.s = out->buf;
memcpy(out->buf, v->val.s, len+1);
return &out->val;
}
case T_PATH:
case T_CLIST:
case T_ECLIST:
case T_LCLIST:
{
struct {
struct f_val val;
struct adata ad;
} *out = lp_alloc(lp, sizeof(*out) + v->val.ad->length);
out->val = *v;
out->val.val.ad = &out->ad;
memcpy(&out->ad, v->val.ad, v->val.ad->length);
return &out->val;
}
default:
bug("Unknown type in value copy: %d", v->type);
}
}

View File

@ -11,110 +11,37 @@
#define _BIRD_FILTER_DATA_H_
#include "nest/bird.h"
/* Type numbers must be in 0..0xff range */
#define T_MASK 0xff
/* Internal types */
enum f_type {
/* Nothing. Simply nothing. */
T_VOID = 0,
/* User visible types, which fit in int */
T_INT = 0x10,
T_BOOL = 0x11,
T_PAIR = 0x12, /* Notice that pair is stored as integer: first << 16 | second */
T_QUAD = 0x13,
/* Put enumerational types in 0x30..0x3f range */
T_ENUM_LO = 0x30,
T_ENUM_HI = 0x3f,
T_ENUM_RTS = 0x30,
T_ENUM_BGP_ORIGIN = 0x31,
T_ENUM_SCOPE = 0x32,
T_ENUM_RTC = 0x33,
T_ENUM_RTD = 0x34,
T_ENUM_ROA = 0x35,
T_ENUM_NETTYPE = 0x36,
T_ENUM_RA_PREFERENCE = 0x37,
T_ENUM_AF = 0x38,
/* new enums go here */
T_ENUM_EMPTY = 0x3f, /* Special hack for atomic_aggr */
#define T_ENUM T_ENUM_LO ... T_ENUM_HI
/* Bigger ones */
T_IP = 0x20,
T_NET = 0x21,
T_STRING = 0x22,
T_PATH_MASK = 0x23, /* mask for BGP path */
T_PATH = 0x24, /* BGP path */
T_CLIST = 0x25, /* Community list */
T_EC = 0x26, /* Extended community value, u64 */
T_ECLIST = 0x27, /* Extended community list */
T_LC = 0x28, /* Large community value, lcomm */
T_LCLIST = 0x29, /* Large community list */
T_RD = 0x2a, /* Route distinguisher for VPN addresses */
T_PATH_MASK_ITEM = 0x2b, /* Path mask item for path mask constructors */
T_SET = 0x80,
T_PREFIX_SET = 0x81,
} PACKED;
#include "lib/type.h"
/* Filter value; size of this affects filter memory consumption */
struct f_val {
enum f_type type; /* T_* */
union {
uint i;
u64 ec;
lcomm lc;
ip_addr ip;
const net_addr *net;
const char *s;
const struct f_tree *t;
const struct f_trie *ti;
const struct adata *ad;
const struct f_path_mask *path_mask;
struct f_path_mask_item pmi;
} val;
btype type; /* T_* */
union bval_long val;
};
/* Dynamic attribute definition (eattrs) */
struct f_dynamic_attr {
u8 type; /* EA type (EAF_*) */
u8 bit; /* For bitfield accessors */
enum f_type f_type; /* Filter type */
uint ea_code; /* EA code */
};
#define fputip(a) ({ ip_addr *ax = falloc(sizeof(*ax)); *ax = (a); ax; })
enum f_sa_code {
SA_FROM = 1,
SA_GW,
SA_GW = 1,
SA_NET,
SA_PROTO,
SA_SOURCE,
SA_SCOPE,
SA_DEST,
SA_IFNAME,
SA_IFINDEX,
SA_WEIGHT,
SA_PREF,
SA_GW_MPLS,
} PACKED;
/* Static attribute definition (members of struct rta) */
struct f_static_attr {
enum f_type f_type; /* Filter type */
btype type; /* Data type */
enum f_sa_code sa_code; /* Static attribute id */
int readonly:1; /* Don't allow writing */
int readonly:1; /* Don't allow writing */
};
/* Filter l-value type */
enum f_lval_type {
F_LVAL_VARIABLE,
F_LVAL_PREFERENCE,
F_LVAL_SA,
F_LVAL_EA,
};
@ -124,7 +51,7 @@ struct f_lval {
enum f_lval_type type;
union {
struct symbol *sym;
struct f_dynamic_attr da;
const struct ea_class *da;
struct f_static_attr sa;
};
};
@ -141,18 +68,23 @@ struct f_tree {
void *data;
};
#define TRIE_STEP 4
#define TRIE_STACK_LENGTH 33
struct f_trie_node4
{
ip4_addr addr, mask, accept;
uint plen;
struct f_trie_node4 *c[2];
u16 plen;
u16 local;
struct f_trie_node4 *c[1 << TRIE_STEP];
};
struct f_trie_node6
{
ip6_addr addr, mask, accept;
uint plen;
struct f_trie_node6 *c[2];
u16 plen;
u16 local;
struct f_trie_node6 *c[1 << TRIE_STEP];
};
struct f_trie_node
@ -169,9 +101,20 @@ struct f_trie
u8 zero;
s8 ipv4; /* -1 for undefined / empty */
u16 data_size; /* Additional data for each trie node */
u32 prefix_count; /* Works only for restricted tries (pxlen == l == h) */
struct f_trie_node root; /* Root trie node */
};
struct f_trie_walk_state
{
u8 ipv4;
u8 accept_length; /* Current inter-node prefix position */
u8 start_pos; /* Initial prefix position in stack[0] */
u8 local_pos; /* Current intra-node prefix position */
u8 stack_pos; /* Current node in stack below */
const struct f_trie_node *stack[TRIE_STACK_LENGTH];
};
struct f_tree *f_new_tree(void);
struct f_tree *build_tree(struct f_tree *);
const struct f_tree *find_tree(const struct f_tree *t, const struct f_val *val);
@ -182,12 +125,75 @@ void tree_walk(const struct f_tree *t, void (*hook)(const struct f_tree *, void
struct f_trie *f_new_trie(linpool *lp, uint data_size);
void *trie_add_prefix(struct f_trie *t, const net_addr *n, uint l, uint h);
int trie_match_net(const struct f_trie *t, const net_addr *n);
int trie_match_longest_ip4(const struct f_trie *t, const net_addr_ip4 *net, net_addr_ip4 *dst, ip4_addr *found0);
int trie_match_longest_ip6(const struct f_trie *t, const net_addr_ip6 *net, net_addr_ip6 *dst, ip6_addr *found0);
void trie_walk_init(struct f_trie_walk_state *s, const struct f_trie *t, const net_addr *from);
int trie_walk_next(struct f_trie_walk_state *s, net_addr *net);
int trie_same(const struct f_trie *t1, const struct f_trie *t2);
void trie_format(const struct f_trie *t, buffer *buf);
static inline int
trie_match_next_longest_ip4(net_addr_ip4 *n, ip4_addr *found)
{
while (n->pxlen)
{
n->pxlen--;
ip4_clrbit(&n->prefix, n->pxlen);
if (ip4_getbit(*found, n->pxlen))
return 1;
}
return 0;
}
static inline int
trie_match_next_longest_ip6(net_addr_ip6 *n, ip6_addr *found)
{
while (n->pxlen)
{
n->pxlen--;
ip6_clrbit(&n->prefix, n->pxlen);
if (ip6_getbit(*found, n->pxlen))
return 1;
}
return 0;
}
#define TRIE_WALK_TO_ROOT_IP4(trie, net, dst) ({ \
net_addr_ip4 dst; \
ip4_addr _found; \
for (int _n = trie_match_longest_ip4(trie, net, &dst, &_found); \
_n; \
_n = trie_match_next_longest_ip4(&dst, &_found))
#define TRIE_WALK_TO_ROOT_IP6(trie, net, dst) ({ \
net_addr_ip6 dst; \
ip6_addr _found; \
for (int _n = trie_match_longest_ip6(trie, net, &dst, &_found); \
_n; \
_n = trie_match_next_longest_ip6(&dst, &_found))
#define TRIE_WALK_TO_ROOT_END })
#define TRIE_WALK(trie, net, from) ({ \
net_addr net; \
struct f_trie_walk_state tws_; \
trie_walk_init(&tws_, trie, from); \
while (trie_walk_next(&tws_, &net))
#define TRIE_WALK_END })
#define F_CMP_ERROR 999
const char *f_type_name(enum f_type t);
const char *f_type_name(btype t);
enum btype f_type_element_type(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);
@ -195,6 +201,8 @@ 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);
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)
{ return (v->type == T_IP) && ipa_is_ip4(v->val.ip); }
int val_in_range(const struct f_val *v1, const struct f_val *v2);
@ -220,7 +228,17 @@ undef_value(struct f_val v)
}
extern const struct f_val f_const_empty_path, f_const_empty_clist, f_const_empty_eclist, f_const_empty_lclist;
static inline const 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;
}
}
enum filter_return f_eval(const struct f_line *expr, struct linpool *tmp_pool, struct f_val *pres);
enum filter_return f_eval(const struct f_line *expr, struct f_val *pres);
#endif

View File

@ -94,7 +94,7 @@ FID_DUMP_BODY()m4_dnl
debug("%s" $4 "\n", INDENT, $5);
]])
FID_INTERPRET_EXEC()m4_dnl
const $1 $2 = whati->$2
$1 $2 = whati->$2
FID_INTERPRET_BODY')
# Instruction arguments are needed only until linearization is done.
@ -256,7 +256,7 @@ FID_INTERPRET_BODY()')
m4_define(SYMBOL, `FID_MEMBER(struct symbol *, sym, [[strcmp(f1->sym->name, f2->sym->name) || (f1->sym->class != f2->sym->class)]], "symbol %s", item->sym->name)')
m4_define(RTC, `FID_MEMBER(struct rtable_config *, rtc, [[strcmp(f1->rtc->name, f2->rtc->name)]], "route table %s", item->rtc->name)')
m4_define(STATIC_ATTR, `FID_MEMBER(struct f_static_attr, sa, f1->sa.sa_code != f2->sa.sa_code,,)')
m4_define(DYNAMIC_ATTR, `FID_MEMBER(struct f_dynamic_attr, da, f1->da.ea_code != f2->da.ea_code,,)')
m4_define(DYNAMIC_ATTR, `FID_MEMBER(const struct ea_class *, da, f1->da != f2->da,,)')
m4_define(ACCESS_RTE, `FID_HIC(,[[do { if (!fs->rte) runtime("No route to access"); } while (0)]],NEVER_CONSTANT())')
# 2) Code wrapping
@ -490,7 +490,7 @@ fi_constant(struct f_inst *what, struct f_val val)
}
static int
f_const_promotion(struct f_inst *arg, enum f_type want)
f_const_promotion(struct f_inst *arg, btype want)
{
if (arg->fi_code != FI_CONSTANT)
return 0;
@ -640,7 +640,7 @@ FID_WR_PUT(4)m4_dnl
struct f_inst {
struct f_inst *next; /* Next instruction */
enum f_instruction_code fi_code; /* Instruction code */
enum f_type type; /* Type of returned value, if known */
btype type; /* Type of returned value, if known */
int size; /* How many instructions are underneath */
int lineno; /* Line number */
union {

View File

@ -240,6 +240,16 @@
if (v2.val.i == 0) runtime( "Mother told me not to divide by 0" );
RESULT(T_INT, i, v1.val.i / v2.val.i);
}
INST(FI_BITOR, 2, 1) {
ARG(1,T_INT);
ARG(2,T_INT);
RESULT(T_INT, i, v1.val.i | v2.val.i);
}
INST(FI_BITAND, 2, 1) {
ARG(1,T_INT);
ARG(2,T_INT);
RESULT(T_INT, i, v1.val.i & v2.val.i);
}
INST(FI_AND, 1, 1) {
ARG(1,T_BOOL);
ARG_TYPE_STATIC(2,T_BOOL);
@ -519,78 +529,100 @@
{
STATIC_ATTR;
ACCESS_RTE;
struct rta *rta = fs->rte->attrs;
ACCESS_EATTRS;
switch (sa.sa_code)
{
case SA_FROM: RESULT(sa.f_type, ip, rta->from); break;
case SA_GW: RESULT(sa.f_type, ip, rta->nh.gw); break;
case SA_NET: RESULT(sa.f_type, net, fs->rte->net); break;
case SA_PROTO: RESULT(sa.f_type, s, fs->rte->src->proto->name); break;
case SA_SOURCE: RESULT(sa.f_type, i, rta->source); break;
case SA_SCOPE: RESULT(sa.f_type, i, rta->scope); break;
case SA_DEST: RESULT(sa.f_type, i, rta->dest); break;
case SA_IFNAME: RESULT(sa.f_type, s, rta->nh.iface ? rta->nh.iface->name : ""); break;
case SA_IFINDEX: RESULT(sa.f_type, i, rta->nh.iface ? rta->nh.iface->index : 0); break;
case SA_WEIGHT: RESULT(sa.f_type, i, rta->nh.weight + 1); break;
case SA_PREF: RESULT(sa.f_type, i, rta->pref); break;
case SA_GW_MPLS: RESULT(sa.f_type, i, rta->nh.labels ? rta->nh.label[0] : MPLS_NULL); break;
case SA_NET: RESULT(sa.type, net, fs->rte->net); break;
case SA_PROTO: RESULT(sa.type, s, fs->rte->src->proto->name); break;
default:
bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code);
{
struct eattr *nhea = ea_find(*fs->eattrs, &ea_gen_nexthop);
struct nexthop_adata *nhad = nhea ? (struct nexthop_adata *) nhea->u.ptr : NULL;
struct nexthop *nh = nhad ? &nhad->nh : NULL;
switch (sa.sa_code)
{
case SA_DEST:
RESULT(sa.type, i, nhad ?
(NEXTHOP_IS_REACHABLE(nhad) ? RTD_UNICAST : nhad->dest)
: RTD_NONE);
break;
case SA_GW:
RESULT(sa.type, ip, nh ? nh->gw : IPA_NONE);
break;
case SA_IFNAME:
RESULT(sa.type, s, (nh && nh->iface) ? nh->iface->name : "");
break;
case SA_IFINDEX:
RESULT(sa.type, i, (nh && nh->iface) ? nh->iface->index : 0);
break;
case SA_WEIGHT:
RESULT(sa.type, i, (nh ? nh->weight : 0) + 1);
break;
case SA_GW_MPLS:
RESULT(sa.type, i, (nh && nh->labels) ? nh->label[0] : MPLS_NULL);
break;
default:
bug("Invalid static attribute access (%u/%u)", sa.type, sa.sa_code);
}
}
}
}
}
INST(FI_RTA_SET, 1, 0) {
ACCESS_RTE;
ACCESS_EATTRS;
ARG_ANY(1);
STATIC_ATTR;
ARG_TYPE(1, sa.f_type);
ARG_TYPE(1, sa.type);
f_rta_cow(fs);
{
struct rta *rta = fs->rte->attrs;
union {
struct nexthop_adata nha;
struct {
struct adata ad;
struct nexthop nh;
u32 label;
};
} nha;
nha.ad = (struct adata) {
.length = sizeof (struct nexthop_adata) - sizeof (struct adata),
};
eattr *a = NULL;
switch (sa.sa_code)
{
case SA_FROM:
rta->from = v1.val.ip;
break;
case SA_GW:
{
ip_addr ip = v1.val.ip;
struct iface *ifa = ipa_is_link_local(ip) ? rta->nh.iface : NULL;
neighbor *n = neigh_find(fs->rte->src->proto, ip, ifa, 0);
if (!n || (n->scope == SCOPE_HOST))
runtime( "Invalid gw address" );
rta->dest = RTD_UNICAST;
rta->nh.gw = ip;
rta->nh.iface = n->iface;
rta->nh.next = NULL;
rta->hostentry = NULL;
rta->nh.labels = 0;
}
break;
case SA_SCOPE:
rta->scope = v1.val.i;
break;
case SA_DEST:
{
int i = v1.val.i;
if ((i != RTD_BLACKHOLE) && (i != RTD_UNREACHABLE) && (i != RTD_PROHIBIT))
runtime( "Destination can be changed only to blackhole, unreachable or prohibit" );
rta->dest = i;
rta->nh.gw = IPA_NONE;
rta->nh.iface = NULL;
rta->nh.next = NULL;
rta->hostentry = NULL;
rta->nh.labels = 0;
nha.nha.dest = i;
nha.ad.length = NEXTHOP_DEST_SIZE;
break;
}
case SA_GW:
{
struct eattr *nh_ea = ea_find(*fs->eattrs, &ea_gen_nexthop);
ip_addr ip = v1.val.ip;
struct iface *ifa = (ipa_is_link_local(ip) && nh_ea) ?
((struct nexthop_adata *) nh_ea->u.ptr)->nh.iface : NULL;
neighbor *n = neigh_find(fs->rte->src->proto, ip, ifa, 0);
if (!n || (n->scope == SCOPE_HOST))
runtime( "Invalid gw address" );
nha.nh = (struct nexthop) {
.gw = ip,
.iface = n->iface,
};
}
break;
@ -600,12 +632,9 @@
if (!ifa)
runtime( "Invalid iface name" );
rta->dest = RTD_UNICAST;
rta->nh.gw = IPA_NONE;
rta->nh.iface = ifa;
rta->nh.next = NULL;
rta->hostentry = NULL;
rta->nh.labels = 0;
nha.nh = (struct nexthop) {
.iface = ifa,
};
}
break;
@ -614,13 +643,20 @@
if (v1.val.i >= 0x100000)
runtime( "Invalid MPLS label" );
struct eattr *nh_ea = ea_find(*fs->eattrs, &ea_gen_nexthop);
if (!nh_ea)
runtime( "No nexthop to add a MPLS label to" );
nha.nh = ((struct nexthop_adata *) nh_ea->u.ptr)->nh;
if (v1.val.i != MPLS_NULL)
{
rta->nh.label[0] = v1.val.i;
rta->nh.labels = 1;
nha.nh.label[0] = v1.val.i;
nha.nh.labels = 1;
nha.ad.length = sizeof nha - sizeof (struct adata);
}
else
rta->nh.labels = 0;
nha.nh.labels = 0;
}
break;
@ -629,22 +665,36 @@
int i = v1.val.i;
if (i < 1 || i > 256)
runtime( "Setting weight value out of bounds" );
if (rta->dest != RTD_UNICAST)
struct eattr *nh_ea = ea_find(*fs->eattrs, &ea_gen_nexthop);
if (!nh_ea)
runtime( "No nexthop to set weight on" );
struct nexthop_adata *nhad = (struct nexthop_adata *) nh_ea->u.ptr;
if (!NEXTHOP_IS_REACHABLE(nhad))
runtime( "Setting weight needs regular nexthop " );
struct nexthop_adata *nhax = (struct nexthop_adata *) tmp_copy_adata(&nhad->ad);
/* Set weight on all next hops */
for (struct nexthop *nh = &rta->nh; nh; nh = nh->next)
NEXTHOP_WALK(nh, nhax)
nh->weight = i - 1;
a = ea_set_attr(fs->eattrs,
EA_LITERAL_DIRECT_ADATA(&ea_gen_nexthop, 0, &nhax->ad));
}
break;
case SA_PREF:
rta->pref = v1.val.i;
break;
default:
bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code);
bug("Invalid static attribute access (%u/%u)", sa.type, sa.sa_code);
}
if (!a)
a = ea_set_attr(fs->eattrs,
EA_LITERAL_DIRECT_ADATA(&ea_gen_nexthop, 0, tmp_copy_adata(&nha.ad)));
a->originated = 1;
a->fresh = 1;
}
}
@ -652,74 +702,30 @@
DYNAMIC_ATTR;
ACCESS_RTE;
ACCESS_EATTRS;
RESULT_TYPE(da.f_type);
RESULT_TYPE(da->type);
{
eattr *e = ea_find(*fs->eattrs, da.ea_code);
const struct f_val *empty;
const eattr *e = ea_find(*fs->eattrs, da->id);
if (!e) {
/* A special case: undefined as_path looks like empty as_path */
if (da.type == EAF_TYPE_AS_PATH) {
RESULT_(T_PATH, ad, &null_adata);
break;
if (e)
{
ASSERT_DIE(e->type == da->type);
switch (e->type) {
case T_IP:
RESULT_(T_IP, ip, *((const ip_addr *) e->u.ptr->data));
break;
default:
RESULT_VAL([[(struct f_val) {
.type = e->type,
.val.bval = e->u,
}]]);
}
/* The same special case for int_set */
if (da.type == EAF_TYPE_INT_SET) {
RESULT_(T_CLIST, ad, &null_adata);
break;
}
/* The same special case for ec_set */
if (da.type == EAF_TYPE_EC_SET) {
RESULT_(T_ECLIST, ad, &null_adata);
break;
}
/* The same special case for lc_set */
if (da.type == EAF_TYPE_LC_SET) {
RESULT_(T_LCLIST, ad, &null_adata);
break;
}
/* Undefined value */
RESULT_VOID;
break;
}
switch (e->type & EAF_TYPE_MASK) {
case EAF_TYPE_INT:
RESULT_(da.f_type, i, e->u.data);
break;
case EAF_TYPE_ROUTER_ID:
RESULT_(T_QUAD, i, e->u.data);
break;
case EAF_TYPE_OPAQUE:
RESULT_(T_ENUM_EMPTY, i, 0);
break;
case EAF_TYPE_IP_ADDRESS:
RESULT_(T_IP, ip, *((ip_addr *) e->u.ptr->data));
break;
case EAF_TYPE_AS_PATH:
RESULT_(T_PATH, ad, e->u.ptr);
break;
case EAF_TYPE_BITFIELD:
RESULT_(T_BOOL, i, !!(e->u.data & (1u << da.bit)));
break;
case EAF_TYPE_INT_SET:
RESULT_(T_CLIST, ad, e->u.ptr);
break;
case EAF_TYPE_EC_SET:
RESULT_(T_ECLIST, ad, e->u.ptr);
break;
case EAF_TYPE_LC_SET:
RESULT_(T_LCLIST, ad, e->u.ptr);
break;
case EAF_TYPE_UNDEF:
else if (empty = f_get_empty(da->type))
RESULT_VAL(*empty);
else
RESULT_VOID;
break;
default:
bug("Unknown dynamic attribute type");
}
}
}
@ -728,62 +734,34 @@
ACCESS_EATTRS;
ARG_ANY(1);
DYNAMIC_ATTR;
ARG_TYPE(1, da.f_type);
ARG_TYPE(1, da->type);
{
struct ea_list *l = lp_alloc(fs->pool, sizeof(struct ea_list) + sizeof(eattr));
struct eattr *a;
l->next = NULL;
l->flags = EALF_SORTED;
l->count = 1;
l->attrs[0].id = da.ea_code;
l->attrs[0].flags = 0;
l->attrs[0].type = da.type | EAF_ORIGINATED | EAF_FRESH;
if (da->type >= EAF_TYPE__MAX)
bug("Unsupported attribute type");
switch (da.type) {
case EAF_TYPE_INT:
case EAF_TYPE_ROUTER_ID:
l->attrs[0].u.data = v1.val.i;
break;
f_rta_cow(fs);
case EAF_TYPE_OPAQUE:
switch (da->type) {
case T_OPAQUE:
case T_IFACE:
runtime( "Setting opaque attribute is not allowed" );
break;
case EAF_TYPE_IP_ADDRESS:;
int len = sizeof(ip_addr);
struct adata *ad = lp_alloc(fs->pool, sizeof(struct adata) + len);
ad->length = len;
(* (ip_addr *) ad->data) = v1.val.ip;
l->attrs[0].u.ptr = ad;
break;
case EAF_TYPE_AS_PATH:
case EAF_TYPE_INT_SET:
case EAF_TYPE_EC_SET:
case EAF_TYPE_LC_SET:
l->attrs[0].u.ptr = v1.val.ad;
break;
case EAF_TYPE_BITFIELD:
{
/* First, we have to find the old value */
eattr *e = ea_find(*fs->eattrs, da.ea_code);
u32 data = e ? e->u.data : 0;
if (v1.val.i)
l->attrs[0].u.data = data | (1u << da.bit);
else
l->attrs[0].u.data = data & ~(1u << da.bit);
}
case T_IP:
a = ea_set_attr(fs->eattrs,
EA_LITERAL_STORE_ADATA(da, 0, &v1.val.ip, sizeof(ip_addr)));
break;
default:
bug("Unknown dynamic attribute type");
a = ea_set_attr(fs->eattrs,
EA_LITERAL_GENERIC(da->id, da->type, 0, .u = v1.val.bval));
break;
}
f_rta_cow(fs);
l->next = *fs->eattrs;
*fs->eattrs = l;
a->originated = 1;
a->fresh = 1;
}
}
@ -792,21 +770,8 @@
ACCESS_RTE;
ACCESS_EATTRS;
{
struct ea_list *l = lp_alloc(fs->pool, sizeof(struct ea_list) + sizeof(eattr));
l->next = NULL;
l->flags = EALF_SORTED;
l->count = 1;
l->attrs[0].id = da.ea_code;
l->attrs[0].flags = 0;
l->attrs[0].type = EAF_TYPE_UNDEF | EAF_ORIGINATED | EAF_FRESH;
l->attrs[0].u.data = 0;
f_rta_cow(fs);
l->next = *fs->eattrs;
*fs->eattrs = l;
}
f_rta_cow(fs);
ea_unset_attr(fs->eattrs, 1, da);
}
INST(FI_LENGTH, 1, 1) { /* Get length of */
@ -901,14 +866,31 @@
((net_addr_roa6 *) v1.val.net)->max_pxlen);
}
INST(FI_ROA_ASN, 1, 1) { /* Get ROA ASN */
ARG(1, T_NET);
if (!net_is_roa(v1.val.net))
runtime( "ROA expected" );
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:
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)->asn :
((net_addr_roa6 *) v1.val.net)->asn);
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 ... */
@ -942,6 +924,89 @@
RESULT(T_INT, i, as_path_get_last_nonaggregated(v1.val.ad));
}
INST(FI_PAIR_DATA, 1, 1) { /* Get data part from the standard community */
ARG(1, T_PAIR);
RESULT(T_INT, i, v1.val.i & 0xFFFF);
}
INST(FI_LC_DATA1, 1, 1) { /* Get data1 part from the large community */
ARG(1, T_LC);
RESULT(T_INT, i, v1.val.lc.ldp1);
}
INST(FI_LC_DATA2, 1, 1) { /* Get data2 part from the large community */
ARG(1, T_LC);
RESULT(T_INT, i, v1.val.lc.ldp2);
}
INST(FI_MIN, 1, 1) { /* Get minimum element from set */
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;
case T_ECLIST:
{
u64 val = 0;
ec_set_min(v1.val.ad, &val);
RESULT_(T_EC, ec, val);
}
break;
case T_LCLIST:
{
lcomm val = { 0, 0, 0 };
lc_set_min(v1.val.ad, &val);
RESULT_(T_LC, lc, val);
}
break;
default:
runtime( "Clist or lclist expected" );
}
}
INST(FI_MAX, 1, 1) { /* Get maximum element from set */
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;
case T_ECLIST:
{
u64 val = 0;
ec_set_max(v1.val.ad, &val);
RESULT_(T_EC, ec, val);
}
break;
case T_LCLIST:
{
lcomm val = { 0, 0, 0 };
lc_set_max(v1.val.ad, &val);
RESULT_(T_LC, lc, val);
}
break;
default:
runtime( "Clist or lclist expected" );
}
}
INST(FI_RETURN, 1, 1) {
NEVER_CONSTANT;
/* Acquire the return value */
@ -1208,37 +1273,7 @@
runtime("Can't filter non-[e|l]clist");
}
INST(FI_ROA_CHECK_IMPLICIT, 0, 1) { /* ROA Check */
NEVER_CONSTANT;
RTC(1);
struct rtable *table = rtc->table;
ACCESS_RTE;
ACCESS_EATTRS;
const net_addr *net = fs->rte->net;
/* We ignore temporary attributes, probably not a problem here */
/* 0x02 is a value of BA_AS_PATH, we don't want to include BGP headers */
eattr *e = ea_find(*fs->eattrs, EA_CODE(PROTOCOL_BGP, 0x02));
if (!e || ((e->type & EAF_TYPE_MASK) != EAF_TYPE_AS_PATH))
runtime("Missing AS_PATH attribute");
u32 as = 0;
as_path_get_last(e->u.ptr, &as);
if (!table)
runtime("Missing ROA table");
if (table->addr_type != NET_ROA4 && table->addr_type != NET_ROA6)
runtime("Table type must be either ROA4 or ROA6");
if (table->addr_type != (net->type == NET_IP4 ? NET_ROA4 : NET_ROA6))
RESULT(T_ENUM_ROA, i, ROA_UNKNOWN); /* Prefix and table type mismatch */
else
RESULT(T_ENUM_ROA, i, [[ net_roa_check(table, net, as) ]]);
}
INST(FI_ROA_CHECK_EXPLICIT, 2, 1) { /* ROA Check */
INST(FI_ROA_CHECK, 2, 1) { /* ROA Check */
NEVER_CONSTANT;
ARG(1, T_NET);
ARG(2, T_INT);

View File

@ -87,15 +87,17 @@ void f_add_lines(const struct f_line_item *what, struct filter_iterator *fit);
struct filter *f_new_where(struct f_inst *);
static inline struct f_dynamic_attr f_new_dynamic_attr(u8 type, enum f_type f_type, uint code) /* Type as core knows it, type as filters know it, and code of dynamic attribute */
{ return (struct f_dynamic_attr) { .type = type, .f_type = f_type, .ea_code = code }; } /* f_type currently unused; will be handy for static type checking */
static inline struct f_dynamic_attr f_new_dynamic_attr_bit(u8 bit, enum f_type f_type, uint code) /* Type as core knows it, type as filters know it, and code of dynamic attribute */
{ return (struct f_dynamic_attr) { .type = EAF_TYPE_BITFIELD, .bit = bit, .f_type = f_type, .ea_code = code }; } /* f_type currently unused; will be handy for static type checking */
static inline struct f_static_attr f_new_static_attr(int f_type, int code, int readonly)
{ return (struct f_static_attr) { .f_type = f_type, .sa_code = code, .readonly = readonly }; }
struct f_inst *f_generate_complex(enum f_instruction_code fi_code, struct f_dynamic_attr da, struct f_inst *argument);
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);

View File

@ -2,7 +2,7 @@
* Filters: utility functions
*
* Copyright 1998 Pavel Machek <pavel@ucw.cz>
* 2017 Jan Maria Matejka <mq@ucw.cz>
* 2017 Maria Matejka <mq@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
@ -13,7 +13,7 @@
#include "filter/f-inst.h"
#include "lib/idm.h"
#include "nest/protocol.h"
#include "nest/route.h"
#include "nest/rt.h"
#define P(a,b) ((a<<8) | b)
@ -40,144 +40,3 @@ struct filter *f_new_where(struct f_inst *where)
f->root = f_linearize(cond);
return f;
}
#define CA_KEY(n) n->name, n->fda.type
#define CA_NEXT(n) n->next
#define CA_EQ(na,ta,nb,tb) (!strcmp(na,nb) && (ta == tb))
#define CA_FN(n,t) (mem_hash(n, strlen(n)) ^ (t*0xaae99453U))
#define CA_ORDER 8 /* Fixed */
struct ca_storage {
struct ca_storage *next;
struct f_dynamic_attr fda;
u32 uc;
char name[0];
};
HASH(struct ca_storage) ca_hash;
static struct idm ca_idm;
static struct ca_storage **ca_storage;
static uint ca_storage_max;
static void
ca_free(resource *r)
{
struct custom_attribute *ca = (void *) r;
struct ca_storage *cas = HASH_FIND(ca_hash, CA, ca->name, ca->fda->type);
ASSERT(cas);
ca->name = NULL;
ca->fda = NULL;
if (!--cas->uc) {
uint id = EA_CUSTOM_ID(cas->fda.ea_code);
idm_free(&ca_idm, id);
HASH_REMOVE(ca_hash, CA, cas);
ca_storage[id] = NULL;
mb_free(cas);
}
}
static void
ca_dump(resource *r)
{
struct custom_attribute *ca = (void *) r;
debug("name \"%s\" id 0x%04x ea_type 0x%02x f_type 0x%02x\n",
ca->name, ca->fda->ea_code, ca->fda->type, ca->fda->f_type);
}
static struct resclass ca_class = {
.name = "Custom attribute",
.size = sizeof(struct custom_attribute),
.free = ca_free,
.dump = ca_dump,
.lookup = NULL,
.memsize = NULL,
};
struct custom_attribute *
ca_lookup(pool *p, const char *name, int f_type)
{
int ea_type;
switch (f_type) {
case T_INT:
ea_type = EAF_TYPE_INT;
break;
case T_IP:
ea_type = EAF_TYPE_IP_ADDRESS;
break;
case T_QUAD:
ea_type = EAF_TYPE_ROUTER_ID;
break;
case T_PATH:
ea_type = EAF_TYPE_AS_PATH;
break;
case T_CLIST:
ea_type = EAF_TYPE_INT_SET;
break;
case T_ECLIST:
ea_type = EAF_TYPE_EC_SET;
break;
case T_LCLIST:
ea_type = EAF_TYPE_LC_SET;
break;
default:
cf_error("Custom route attribute of unsupported type");
}
static int inited = 0;
if (!inited) {
idm_init(&ca_idm, &root_pool, 8);
HASH_INIT(ca_hash, &root_pool, CA_ORDER);
ca_storage_max = 256;
ca_storage = mb_allocz(&root_pool, sizeof(struct ca_storage *) * ca_storage_max);
inited++;
}
struct ca_storage *cas = HASH_FIND(ca_hash, CA, name, ea_type);
if (cas) {
cas->uc++;
} else {
uint id = idm_alloc(&ca_idm);
if (id >= EA_CUSTOM_BIT)
cf_error("Too many custom attributes.");
if (id >= ca_storage_max) {
ca_storage_max *= 2;
ca_storage = mb_realloc(ca_storage, sizeof(struct ca_storage *) * ca_storage_max * 2);
}
cas = mb_allocz(&root_pool, sizeof(struct ca_storage) + strlen(name) + 1);
cas->fda = f_new_dynamic_attr(ea_type, f_type, EA_CUSTOM(id));
cas->uc = 1;
strcpy(cas->name, name);
ca_storage[id] = cas;
HASH_INSERT(ca_hash, CA, cas);
}
struct custom_attribute *ca = ralloc(p, &ca_class);
ca->fda = &(cas->fda);
ca->name = cas->name;
return ca;
}
const char *
ea_custom_name(uint ea)
{
uint id = EA_CUSTOM_ID(ea);
if (id >= ca_storage_max)
return NULL;
if (!ca_storage[id])
return NULL;
return ca_storage[id]->name;
}

View File

@ -35,10 +35,10 @@
#include "lib/ip.h"
#include "lib/net.h"
#include "lib/flowspec.h"
#include "nest/route.h"
#include "nest/rt.h"
#include "nest/protocol.h"
#include "nest/iface.h"
#include "nest/attrs.h"
#include "lib/attrs.h"
#include "conf/conf.h"
#include "filter/filter.h"
#include "filter/f-inst.h"
@ -79,9 +79,6 @@ struct filter_state {
/* Cached pointer to ea_list */
struct ea_list **eattrs;
/* Linpool for adata allocation */
struct linpool *pool;
/* Buffer for log output */
struct buffer buf;
@ -117,7 +114,7 @@ f_rta_cow(struct filter_state *fs)
* at the end of f_run()), also the lock of hostentry is inherited (we
* suppose hostentry is not changed by filters).
*/
fs->rte->attrs = rta_do_cow(fs->rte->attrs, fs->pool);
fs->rte->attrs = rta_do_cow(fs->rte->attrs, tmp_linpool);
/* Re-cache the ea_list */
f_cache_eattrs(fs);
@ -185,8 +182,8 @@ interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val)
return F_ERROR; \
} while(0)
#define falloc(size) lp_alloc(fs->pool, size)
#define fpool fs->pool
#define falloc(size) tmp_alloc(size)
#define fpool tmp_linpool
#define ACCESS_EATTRS do { if (!fs->eattrs) f_cache_eattrs(fs); } while (0)
@ -237,7 +234,7 @@ interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val)
* tmp_pool, otherwise the filters may modify it.
*/
enum filter_return
f_run(const struct filter *filter, struct rte *rte, struct linpool *tmp_pool, int flags)
f_run(const struct filter *filter, struct rte *rte, int flags)
{
if (filter == FILTER_ACCEPT)
return F_ACCEPT;
@ -250,7 +247,6 @@ f_run(const struct filter *filter, struct rte *rte, struct linpool *tmp_pool, in
/* Initialize the filter state */
filter_state = (struct filter_state) {
.rte = rte,
.pool = tmp_pool,
.flags = flags,
};
@ -285,11 +281,10 @@ f_run(const struct filter *filter, struct rte *rte, struct linpool *tmp_pool, in
*/
enum filter_return
f_eval_rte(const struct f_line *expr, struct rte *rte, struct linpool *tmp_pool)
f_eval_rte(const struct f_line *expr, struct rte *rte)
{
filter_state = (struct filter_state) {
.rte = rte,
.pool = tmp_pool,
};
f_stack_init(filter_state);
@ -308,11 +303,9 @@ f_eval_rte(const struct f_line *expr, struct rte *rte, struct linpool *tmp_pool)
* @pres: here the output will be stored
*/
enum filter_return
f_eval(const struct f_line *expr, struct linpool *tmp_pool, struct f_val *pres)
f_eval(const struct f_line *expr, struct f_val *pres)
{
filter_state = (struct filter_state) {
.pool = tmp_pool,
};
filter_state = (struct filter_state) {};
f_stack_init(filter_state);
@ -331,9 +324,7 @@ uint
f_eval_int(const struct f_line *expr)
{
/* Called independently in parse-time to eval expressions */
filter_state = (struct filter_state) {
.pool = cfg_mem,
};
filter_state = (struct filter_state) {};
f_stack_init(filter_state);
@ -354,10 +345,10 @@ f_eval_int(const struct f_line *expr)
* f_eval_buf - get a value of a term and print it to the supplied buffer
*/
enum filter_return
f_eval_buf(const struct f_line *expr, struct linpool *tmp_pool, buffer *buf)
f_eval_buf(const struct f_line *expr, buffer *buf)
{
struct f_val val;
enum filter_return fret = f_eval(expr, tmp_pool, &val);
enum filter_return fret = f_eval(expr, &val);
if (fret <= F_RETURN)
val_format(&val, buf);
return fret;
@ -425,6 +416,23 @@ filter_commit(struct config *new, struct config *old)
}
}
void channel_filter_dump(const struct filter *f)
{
if (f == FILTER_ACCEPT)
debug(" ALL");
else if (f == FILTER_REJECT)
debug(" NONE");
else if (f == FILTER_UNDEF)
debug(" UNDEF");
else if (f->sym) {
ASSERT(f->sym->filter == f);
debug(" named filter %s", f->sym->name);
} else {
debug("\n");
f_dump_line(f->root, 2);
}
}
void filters_dump_all(void)
{
struct symbol *sym;
@ -444,19 +452,10 @@ void filters_dump_all(void)
struct channel *c;
WALK_LIST(c, sym->proto->proto->channels) {
debug(" Channel %s (%s) IMPORT", c->name, net_label[c->net_type]);
if (c->in_filter == FILTER_ACCEPT)
debug(" ALL\n");
else if (c->in_filter == FILTER_REJECT)
debug(" NONE\n");
else if (c->in_filter == FILTER_UNDEF)
debug(" UNDEF\n");
else if (c->in_filter->sym) {
ASSERT(c->in_filter->sym->filter == c->in_filter);
debug(" named filter %s\n", c->in_filter->sym->name);
} else {
debug("\n");
f_dump_line(c->in_filter->root, 2);
}
channel_filter_dump(c->in_filter);
debug(" EXPORT", c->name, net_label[c->net_type]);
channel_filter_dump(c->out_filter);
debug("\n");
}
}
}

View File

@ -13,8 +13,8 @@
#include "lib/resource.h"
#include "lib/ip.h"
#include "lib/macro.h"
#include "nest/route.h"
#include "nest/attrs.h"
#include "nest/rt.h"
#include "lib/attrs.h"
/* Possible return values of filter execution */
enum filter_return {
@ -51,10 +51,10 @@ struct filter {
struct rte;
enum filter_return f_run(const struct filter *filter, struct rte *rte, struct linpool *tmp_pool, int flags);
enum filter_return f_eval_rte(const struct f_line *expr, struct rte *rte, struct linpool *tmp_pool);
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_eval_buf(const struct f_line *expr, struct linpool *tmp_pool, buffer *buf);
enum filter_return f_eval_buf(const struct f_line *expr, buffer *buf);
const char *filter_name(const struct filter *filter);
int filter_same(const struct filter *new, const struct filter *old);
@ -70,13 +70,4 @@ void filters_dump_all(void);
#define FF_SILENT 2 /* Silent filter execution */
/* Custom route attributes */
struct custom_attribute {
resource r;
struct f_dynamic_attr *fda;
const char *name;
};
struct custom_attribute *ca_lookup(pool *p, const char *name, int ea_type);
#endif

View File

@ -46,9 +46,7 @@ run_function(const void *arg)
if (t->cmp)
return t->result == f_same(t->fn, t->cmp);
linpool *tmp = lp_new_default(&root_pool);
enum filter_return fret = f_eval(t->fn, tmp, NULL);
rfree(tmp);
enum filter_return fret = f_eval(t->fn, NULL);
return (fret < F_REJECT);
}

View File

@ -9,7 +9,109 @@ router id 62.168.0.1;
/* We have to setup any protocol */
protocol device { }
/* Setting some custom attributes, enough to force BIRD to reallocate the attribute idmap */
attribute int test_ca_int1;
attribute int test_ca_int2;
attribute int test_ca_int3;
attribute int test_ca_int4;
attribute int test_ca_int5;
attribute int test_ca_int6;
attribute int test_ca_int7;
attribute int test_ca_int8;
attribute int test_ca_int9;
attribute int test_ca_int10;
attribute ip test_ca_ip1;
attribute ip test_ca_ip2;
attribute ip test_ca_ip3;
attribute ip test_ca_ip4;
attribute ip test_ca_ip5;
attribute ip test_ca_ip6;
attribute ip test_ca_ip7;
attribute ip test_ca_ip8;
attribute ip test_ca_ip9;
attribute ip test_ca_ip10;
attribute quad test_ca_quad1;
attribute quad test_ca_quad2;
attribute quad test_ca_quad3;
attribute quad test_ca_quad4;
attribute quad test_ca_quad5;
attribute quad test_ca_quad6;
attribute quad test_ca_quad7;
attribute quad test_ca_quad8;
attribute quad test_ca_quad9;
attribute quad test_ca_quad10;
attribute bgppath test_ca_bgppath1;
attribute bgppath test_ca_bgppath2;
attribute bgppath test_ca_bgppath3;
attribute bgppath test_ca_bgppath4;
attribute bgppath test_ca_bgppath5;
attribute bgppath test_ca_bgppath6;
attribute bgppath test_ca_bgppath7;
attribute bgppath test_ca_bgppath8;
attribute bgppath test_ca_bgppath9;
attribute bgppath test_ca_bgppath10;
attribute clist test_ca_clist1;
attribute clist test_ca_clist2;
attribute clist test_ca_clist3;
attribute clist test_ca_clist4;
attribute clist test_ca_clist5;
attribute clist test_ca_clist6;
attribute clist test_ca_clist7;
attribute clist test_ca_clist8;
attribute clist test_ca_clist9;
attribute clist test_ca_clist10;
attribute eclist test_ca_eclist1;
attribute eclist test_ca_eclist2;
attribute eclist test_ca_eclist3;
attribute eclist test_ca_eclist4;
attribute eclist test_ca_eclist5;
attribute eclist test_ca_eclist6;
attribute eclist test_ca_eclist7;
attribute eclist test_ca_eclist8;
attribute eclist test_ca_eclist9;
attribute eclist test_ca_eclist10;
attribute lclist test_ca_lclist1;
attribute lclist test_ca_lclist2;
attribute lclist test_ca_lclist3;
attribute lclist test_ca_lclist4;
attribute lclist test_ca_lclist5;
attribute lclist test_ca_lclist6;
attribute lclist test_ca_lclist7;
attribute lclist test_ca_lclist8;
attribute lclist test_ca_lclist9;
attribute lclist test_ca_lclist10;
attribute lclist test_ca_lclist_max1;
attribute lclist test_ca_lclist_max2;
attribute lclist test_ca_lclist_max3;
attribute lclist test_ca_lclist_max4;
attribute lclist test_ca_lclist_max5;
attribute lclist test_ca_lclist_max6;
attribute lclist test_ca_lclist_max7;
attribute lclist test_ca_lclist_max8;
attribute lclist test_ca_lclist_max9;
attribute lclist test_ca_lclist_max10;
attribute lclist test_ca_lclist_max11;
attribute lclist test_ca_lclist_max12;
attribute lclist test_ca_lclist_max13;
attribute lclist test_ca_lclist_max14;
attribute lclist test_ca_lclist_max15;
attribute lclist test_ca_lclist_max16;
attribute lclist test_ca_lclist_max17;
attribute lclist test_ca_lclist_max18;
attribute lclist test_ca_lclist_max19;
attribute lclist test_ca_lclist_max20;
attribute lclist test_ca_lclist_max21;
/* Uncomment this to get an error */
#attribute int bgp_path;
/*
* Common definitions and functions
@ -111,6 +213,14 @@ int i;
bt_assert(!(i = 4));
bt_assert(1 <= 1);
bt_assert(!(1234 < 1234));
bt_assert(10 - 5 = 5);
bt_assert(4294967295 + 1 = 0);
bt_assert(6*9=54);
bt_assert(984/41 = 24);
bt_assert(123/45 = 2);
bt_assert(0xfee1a | 0xbeef = 0xffeff);
bt_assert(0xfee1a & 0xbeef = 0xae0a);
}
bt_test_suite(t_int, "Testing integers");
@ -335,6 +445,26 @@ ip p;
p = 1234:5678::;
bt_assert(!p.is_v4);
bt_assert(p.mask(24) = 1234:5600::);
p = 1:2:3:4:5:6:7:8;
bt_assert(!p.is_v4);
bt_assert(format(p) = "1:2:3:4:5:6:7:8");
bt_assert(p.mask(64) = 1:2:3:4::);
p = 10:20:30:40:50:60:70:80;
bt_assert(!p.is_v4);
bt_assert(format(p) = "10:20:30:40:50:60:70:80");
bt_assert(p.mask(64) = 10:20:30:40::);
p = 1090:20a0:30b0:40c0:50d0:60e0:70f0:8000;
bt_assert(!p.is_v4);
bt_assert(format(p) = "1090:20a0:30b0:40c0:50d0:60e0:70f0:8000");
bt_assert(p.mask(64) = 1090:20a0:30b0:40c0::);
p = ::fffe:6:c0c:936d:88c7:35d3;
bt_assert(!p.is_v4);
bt_assert(format(p) = "::fffe:6:c0c:936d:88c7:35d3");
bt_assert(p.mask(64) = 0:0:fffe:6::);
}
bt_test_suite(t_ip, "Testing ip address");
@ -378,9 +508,9 @@ bt_test_suite(t_ip_set, "Testing sets of ip address");
function t_enum()
{
bt_assert(format(RTS_STATIC) = "(enum 30)1");
bt_assert(format(NET_IP4) = "(enum 36)1");
bt_assert(format(NET_VPN6) = "(enum 36)4");
bt_assert(format(RTS_STATIC) = "(enum 31)1");
bt_assert(format(NET_IP4) = "(enum 3b)1");
bt_assert(format(NET_VPN6) = "(enum 3b)4");
bt_assert(RTS_STATIC ~ [RTS_STATIC, RTS_DEVICE]);
bt_assert(RTS_BGP !~ [RTS_STATIC, RTS_DEVICE]);
@ -478,6 +608,33 @@ prefix set pxs;
bt_assert(1.2.0.0/16 ~ [ 1.0.0.0/8{ 15 , 17 } ]);
bt_assert([ 10.0.0.0/8{ 15 , 17 } ] != [ 11.0.0.0/8{ 15 , 17 } ]);
/* Formatting of prefix sets, some cases are a bit strange */
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.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.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
bt_assert(format([ 10.10.160.0/19+ ]) = "[10.10.160.0/19{0.0.32.0}, 10.10.160.0/20{0.0.31.255}, 10.10.176.0/20{0.0.31.255}]");
bt_assert(format([ ::/0 ]) = "[::/0]");
bt_assert(format([ 11:22:33:44:55:66:77:88/128 ]) = "[11:22:33:44:55:66:77:88/128{::1}]");
bt_assert(format([ 11:22:33:44::/64 ]) = "[11:22:33:44::/64{0:0:0:1::}]");
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: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: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}]");
}
bt_test_suite(t_prefix_set, "Testing prefix sets");
@ -557,6 +714,12 @@ prefix set pxs;
bt_assert(2000::/29 !~ pxs);
bt_assert(1100::/10 !~ pxs);
bt_assert(2010::/26 !~ pxs);
pxs = [ 52E0::/13{13,128} ];
bt_assert(52E7:BE81:379B:E6FD:541F:B0D0::/93 ~ pxs);
pxs = [ 41D8:8718::/30{0,30}, 413A:99A8:6C00::/38{38,128} ];
bt_assert(4180::/9 ~ pxs);
}
bt_test_suite(t_prefix6_set, "Testing prefix IPv6 sets");
@ -683,6 +846,11 @@ clist l;
clist l2;
clist r;
{
bt_assert((10, 20).asn = 10);
bt_assert((10, 20).data = 20);
bt_assert(p23.asn = 2);
bt_assert(p23.data = 3);
l = - empty -;
bt_assert(l !~ [(*,*)]);
bt_assert((l ~ [(*,*)]) != (l !~ [(*,*)]));
@ -775,6 +943,12 @@ clist r;
r = filter(l, [(3,1), (*,2)]);
bt_assert(r = add(add(-empty-, (3,1)), (3,2)));
bt_assert(format(r) = "(clist (3,1) (3,2))");
# minimim & maximum element
r = add(add(add(add(add(-empty-, (2,1)), (1,3)), (2,2)), (3,1)), (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));
}
bt_test_suite(t_clist, "Testing lists of communities");
@ -880,6 +1054,12 @@ eclist r;
r = filter(el, [(rt, 10, 1), (rt, 10, 25..30), (ro, 10, 40)]);
bt_assert(r = add(add(--empty--, (rt, 10, 1)), (rt, 10, 30)));
bt_assert(format(r) = "(eclist (rt, 10, 1) (rt, 10, 30))");
# minimim & maximum element
r = add(add(add(add(add(--empty--, (rt, 2, 1)), (rt, 1, 3)), (rt, 2, 2)), (rt, 3, 1)), (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));
}
bt_test_suite(t_eclist, "Testing lists of extended communities");
@ -939,6 +1119,10 @@ lclist r;
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);
ll = --- empty ---;
ll = add(ll, (ten, 20, 30));
ll = add(ll, (1000, 2000, 3000));
@ -985,6 +1169,12 @@ lclist r;
r = filter(ll, [(5..15, *, *), (20, 15..25, *)]);
bt_assert(r = add(add(---empty---, (10, 10, 10)), (20, 20, 20)));
bt_assert(format(r) = "(lclist (10, 10, 10) (20, 20, 20))");
# minimim & maximum element
r = add(add(add(add(add(---empty---, (2, 3, 3)), (1, 2, 3)), (2, 3, 1)), (3, 1, 2)), (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));
}
bt_test_suite(t_lclist, "Testing lists of large communities");
@ -1243,6 +1433,7 @@ function __test2()
filter testf
int j;
bool t;
{
print "Heya, filtering route to ", net.ip, " prefixlen ", net.len, " source ", source;
print "This route was from ", from;
@ -1254,6 +1445,56 @@ int j;
rip_metric = 14;
unset(rip_metric);
test_ca_int1 = 42;
test_ca_ip2 = 1.3.5.7;
test_ca_quad3 = 2.4.6.8;
test_ca_bgppath4 = +empty+;
test_ca_clist5 = -empty-;
test_ca_eclist6 = --empty--;
test_ca_lclist7 = ---empty---;
igp_metric = 53;
babel_metric = 64;
t = defined(babel_router_id);
j = babel_seqno;
bgp_origin = ORIGIN_IGP;
bgp_path = +empty+;
bgp_next_hop = 3456:789a:bcde:f012::3456:789a;
bgp_med = 71;
bgp_local_pref = 942;
t = defined(bgp_atomic_aggr);
t = defined(bgp_aggregator);
bgp_community = -empty-;
bgp_originator_id = 9.7.5.3;
bgp_cluster_list = -empty-;
t = defined(bgp_mp_reach_nlri);
t = defined(bgp_mp_unreach_nlri);
bgp_ext_community = --empty--;
bgp_as4_path = +empty+;
t = defined(bgp_as4_aggregator);
t = defined(bgp_aigp);
bgp_large_community = ---empty---;
t = defined(bgp_mpls_label_stack);
ospf_metric1 = 64;
ospf_metric2 = 111;
ospf_tag = 654432;
radv_preference = RA_PREF_LOW;
radv_lifetime = 28;
rip_metric = 2;
rip_tag = 4;
t = defined(rip_from);
krt_source = 17;
krt_metric = 19;
# krt_lock_mtu = false;
# krt_lock_window = true;
# krt_lock_rtt = krt_lock_rttvar && krt_lock_sstresh || krt_lock_cwnd;
accept "ok I take that";
}

View File

@ -38,12 +38,6 @@ protocol static {
print from;
from = 1.2.3.4;
print from;
print scope;
scope = SCOPE_HOST;
print scope;
if !(scope ~ [ SCOPE_HOST, SCOPE_SITE ]) then {
print "Failed in test";
}
preference = 15;
print preference;

View File

@ -19,10 +19,7 @@ static void
start_conf_env(void)
{
bt_bird_init();
pool *p = rp_new(&root_pool, "helper_pool");
linpool *l = lp_new_default(p);
cfg_mem = l;
cfg_mem = tmp_linpool;
}
static struct f_tree *

File diff suppressed because it is too large Load Diff

View File

@ -14,9 +14,12 @@
#include "conf/conf.h"
#define TESTS_NUM 10
#define PREFIXES_NUM 10
#define PREFIXES_NUM 32
#define PREFIX_TESTS_NUM 10000
#define PREFIX_BENCH_NUM 100000000
#define TRIE_BUFFER_SIZE 1024
#define TEST_BUFFER_SIZE (1024*1024)
#define BIG_BUFFER_SIZE 10000
/* Wrapping structure for storing f_prefixes structures in list */
@ -31,146 +34,849 @@ xrandom(u32 max)
return (bt_random() % max);
}
static inline uint
get_exp_random(void)
{
uint r, n = 0;
for (r = bt_random(); r & 1; r = r >> 1)
n++;
return n;
}
static int
is_prefix_included(list *prefixes, struct f_prefix *needle)
compare_prefixes(const void *a, const void *b)
{
return net_compare(&((const struct f_prefix *) a)->net,
&((const struct f_prefix *) b)->net);
}
static inline int
matching_ip4_nets(const net_addr_ip4 *a, const net_addr_ip4 *b)
{
ip4_addr cmask = ip4_mkmask(MIN(a->pxlen, b->pxlen));
return ip4_compare(ip4_and(a->prefix, cmask), ip4_and(b->prefix, cmask)) == 0;
}
static inline int
matching_ip6_nets(const net_addr_ip6 *a, const net_addr_ip6 *b)
{
ip6_addr cmask = ip6_mkmask(MIN(a->pxlen, b->pxlen));
return ip6_compare(ip6_and(a->prefix, cmask), ip6_and(b->prefix, cmask)) == 0;
}
static inline int
matching_nets(const net_addr *a, const net_addr *b)
{
if (a->type != b->type)
return 0;
return (a->type == NET_IP4) ?
matching_ip4_nets((const net_addr_ip4 *) a, (const net_addr_ip4 *) b) :
matching_ip6_nets((const net_addr_ip6 *) a, (const net_addr_ip6 *) b);
}
static int
is_prefix_included(list *prefixes, const net_addr *needle)
{
struct f_prefix_node *n;
WALK_LIST(n, *prefixes)
{
ip6_addr cmask = ip6_mkmask(MIN(n->prefix.net.pxlen, needle->net.pxlen));
ip6_addr ip = net6_prefix(&n->prefix.net);
ip6_addr needle_ip = net6_prefix(&needle->net);
if ((ipa_compare(ipa_and(ip, cmask), ipa_and(needle_ip, cmask)) == 0) &&
(n->prefix.lo <= needle->net.pxlen) && (needle->net.pxlen <= n->prefix.hi))
if (matching_nets(&n->prefix.net, needle) &&
(n->prefix.lo <= needle->pxlen) && (needle->pxlen <= n->prefix.hi))
{
bt_debug("FOUND\t" PRIip6 "/%d %d-%d\n", ARGip6(net6_prefix(&n->prefix.net)), n->prefix.net.pxlen, n->prefix.lo, n->prefix.hi);
char buf[64];
bt_format_net(buf, 64, &n->prefix.net);
bt_debug("FOUND %s %d-%d\n", buf, n->prefix.lo, n->prefix.hi);
return 1; /* OK */
}
}
return 0; /* FAIL */
}
static struct f_prefix
get_random_ip6_prefix(void)
static void
get_random_net(net_addr *net, int v6)
{
struct f_prefix p;
u8 pxlen = xrandom(120)+8;
ip6_addr ip6 = ip6_build(bt_random(),bt_random(),bt_random(),bt_random());
net_addr_ip6 net6 = NET_ADDR_IP6(ip6, pxlen);
p.net = *((net_addr*) &net6);
if (bt_random() % 2)
if (!v6)
{
p.lo = 0;
p.hi = p.net.pxlen;
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
{
p.lo = p.net.pxlen;
p.hi = net_max_prefix_length[p.net.type];
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);
}
return p;
}
static void
generate_random_ipv6_prefixes(list *prefixes)
get_random_prefix(struct f_prefix *px, int v6, int tight)
{
int i;
for (i = 0; i < PREFIXES_NUM; i++)
get_random_net(&px->net, v6);
if (tight)
{
struct f_prefix f = get_random_ip6_prefix();
struct f_prefix_node *px = calloc(1, sizeof(struct f_prefix_node));
px->prefix = f;
bt_debug("ADD\t" PRIip6 "/%d %d-%d\n", ARGip6(net6_prefix(&px->prefix.net)), px->prefix.net.pxlen, px->prefix.lo, px->prefix.hi);
add_tail(prefixes, &px->n);
px->lo = px->hi = px->net.pxlen;
}
else if (bt_random() % 2)
{
px->lo = 0;
px->hi = px->net.pxlen;
}
else
{
px->lo = px->net.pxlen;
px->hi = net_max_prefix_length[px->net.type];
}
}
static void
get_random_ip4_subnet(net_addr_ip4 *net, const net_addr_ip4 *src, int pxlen)
{
*net = NET_ADDR_IP4(ip4_and(src->prefix, ip4_mkmask(pxlen)), pxlen);
if (pxlen > src->pxlen)
{
ip4_addr rnd = ip4_from_u32((u32) bt_random());
ip4_addr mask = ip4_xor(ip4_mkmask(src->pxlen), ip4_mkmask(pxlen));
net->prefix = ip4_or(net->prefix, ip4_and(rnd, mask));
}
}
static void
get_random_ip6_subnet(net_addr_ip6 *net, const net_addr_ip6 *src, int pxlen)
{
*net = NET_ADDR_IP6(ip6_and(src->prefix, ip6_mkmask(pxlen)), pxlen);
if (pxlen > src->pxlen)
{
ip6_addr rnd = ip6_build(bt_random(), bt_random(), bt_random(), bt_random());
ip6_addr mask = ip6_xor(ip6_mkmask(src->pxlen), ip6_mkmask(pxlen));
net->prefix = ip6_or(net->prefix, ip6_and(rnd, mask));
}
}
static void
get_random_subnet(net_addr *net, const net_addr *src, int pxlen)
{
if (src->type == NET_IP4)
get_random_ip4_subnet((net_addr_ip4 *) net, (const net_addr_ip4 *) src, pxlen);
else
get_random_ip6_subnet((net_addr_ip6 *) net, (const net_addr_ip6 *) src, pxlen);
}
static void
get_inner_net(net_addr *net, const struct f_prefix *src)
{
int pxlen, step;
if (bt_random() % 2)
{
step = get_exp_random();
step = MIN(step, src->hi - src->lo);
pxlen = (bt_random() % 2) ? (src->lo + step) : (src->hi - step);
}
else
pxlen = src->lo + bt_random() % (src->hi - src->lo + 1);
get_random_subnet(net, &src->net, pxlen);
}
static void
swap_random_bits_ip4(net_addr_ip4 *net, int num)
{
for (int i = 0; i < num; i++)
{
ip4_addr swap = IP4_NONE;
ip4_setbit(&swap, bt_random() % net->pxlen);
net->prefix = ip4_xor(net->prefix, swap);
}
}
static void
swap_random_bits_ip6(net_addr_ip6 *net, int num)
{
for (int i = 0; i < num; i++)
{
ip6_addr swap = IP6_NONE;
ip6_setbit(&swap, bt_random() % net->pxlen);
net->prefix = ip6_xor(net->prefix, swap);
}
}
static void
swap_random_bits(net_addr *net, int num)
{
if (net->type == NET_IP4)
swap_random_bits_ip4((net_addr_ip4 *) net, num);
else
swap_random_bits_ip6((net_addr_ip6 *) net, num);
}
static void
get_outer_net(net_addr *net, const struct f_prefix *src)
{
int pxlen, step;
int inside = 0;
int max = net_max_prefix_length[src->net.type];
if ((src->lo > 0) && (bt_random() % 3))
{
step = 1 + get_exp_random();
step = MIN(step, src->lo);
pxlen = src->lo - step;
}
else if ((src->hi < max) && (bt_random() % 2))
{
step = 1 + get_exp_random();
step = MIN(step, max - src->hi);
pxlen = src->hi + step;
}
else
{
pxlen = src->lo + bt_random() % (src->hi - src->lo + 1);
inside = 1;
}
get_random_subnet(net, &src->net, pxlen);
/* Perhaps swap some bits in prefix */
if ((net->pxlen > 0) && (inside || (bt_random() % 4)))
swap_random_bits(net, 1 + get_exp_random());
}
static list *
make_random_prefix_list(int num, int v6, int tight)
{
list *prefixes = lp_allocz(tmp_linpool, sizeof(struct f_prefix_node));
init_list(prefixes);
for (int i = 0; i < num; i++)
{
struct f_prefix_node *px = lp_allocz(tmp_linpool, sizeof(struct f_prefix_node));
get_random_prefix(&px->prefix, v6, tight);
add_tail(prefixes, &px->n);
char buf[64];
bt_format_net(buf, 64, &px->prefix.net);
bt_debug("ADD %s{%d,%d}\n", buf, px->prefix.lo, px->prefix.hi);
}
return prefixes;
}
static struct f_trie *
make_trie_from_prefix_list(list *prefixes)
{
struct f_trie *trie = f_new_trie(tmp_linpool, 0);
struct f_prefix_node *n;
WALK_LIST(n, *prefixes)
trie_add_prefix(trie, &n->prefix.net, n->prefix.lo, n->prefix.hi);
return trie;
}
/*
* Read sequence of prefixes from file handle and return prefix list.
* Each prefix is on one line, sequence terminated by empty line or eof.
* Arg @plus means prefix should include all longer ones.
*/
static list *
read_prefix_list(FILE *f, int v6, int plus)
{
ASSERT(!v6);
uint a0, a1, a2, a3, pl;
char s[32];
int n;
list *pxlist = lp_allocz(tmp_linpool, sizeof(struct f_prefix_node));
init_list(pxlist);
errno = 0;
while (fgets(s, 32, f))
{
if (s[0] == '\n')
return pxlist;
n = sscanf(s, "%u.%u.%u.%u/%u", &a0, &a1, &a2, &a3, &pl);
if (n != 5)
bt_abort_msg("Invalid content of trie_data");
struct f_prefix_node *px = lp_allocz(tmp_linpool, sizeof(struct f_prefix_node));
net_fill_ip4(&px->prefix.net, ip4_build(a0, a1, a2, a3), pl);
px->prefix.lo = pl;
px->prefix.hi = plus ? IP4_MAX_PREFIX_LENGTH : pl;
add_tail(pxlist, &px->n);
char buf[64];
bt_format_net(buf, 64, &px->prefix.net);
bt_debug("ADD %s{%d,%d}\n", buf, px->prefix.lo, px->prefix.hi);
}
bt_syscall(errno, "fgets()");
return EMPTY_LIST(*pxlist) ? NULL : pxlist;
}
/*
* Open file, read multiple sequences of prefixes from it. Fill @data with
* prefix lists and @trie with generated tries. Return number of sequences /
* tries. Use separate linpool @lp0 for prefix lists and @lp1 for tries.
* Arg @plus means prefix should include all longer ones.
*/
static int
t_match_net(void)
read_prefix_file(const char *filename, int plus,
list *data[], struct f_trie *trie[])
{
FILE *f = fopen(filename, "r");
bt_syscall(!f, "fopen(%s)", filename);
int n = 0;
list *pxlist;
while (pxlist = read_prefix_list(f, 0, plus))
{
data[n] = pxlist;
trie[n] = make_trie_from_prefix_list(pxlist);
bt_debug("NEXT\n");
n++;
}
fclose(f);
bt_debug("DONE reading %d tries\n", n);
return n;
}
/*
* Select random subset of @dn prefixes from prefix list @src of length @sn,
* and store them to buffer @dst (of size @dn). Prefixes may be chosen multiple
* times. Randomize order of prefixes in @dst buffer.
*/
static void
select_random_prefix_subset(list *src[], net_addr dst[], int sn, int dn)
{
int pn = 0;
if (!dn)
return;
/* Compute total prefix number */
for (int i = 0; i < sn; i++)
pn += list_length(src[i]);
/* Change of selecting a prefix */
int rnd = (pn / dn) + 10;
int n = 0;
/* Iterate indefinitely over src array */
for (int i = 0; 1; i++, i = (i < sn) ? i : 0)
{
struct f_prefix_node *px;
WALK_LIST(px, *src[i])
{
if (xrandom(rnd) != 0)
continue;
net_copy(&dst[n], &px->prefix.net);
n++;
/* We have enough */
if (n == dn)
goto done;
}
}
done:
/* Shuffle networks */
for (int i = 0; i < dn; i++)
{
int j = xrandom(dn);
if (i == j)
continue;
net_addr tmp;
net_copy(&tmp, &dst[i]);
net_copy(&dst[i], &dst[j]);
net_copy(&dst[j], &tmp);
}
}
/* Fill @dst buffer with @dn randomly generated /32 prefixes */
static void
make_random_addresses(net_addr dst[], int dn)
{
for (int i = 0; i < dn; i++)
net_fill_ip4(&dst[i], ip4_from_u32((u32) bt_random()), IP4_MAX_PREFIX_LENGTH);
}
static void
test_match_net(list *prefixes, struct f_trie *trie, const net_addr *net)
{
char buf[64];
bt_format_net(buf, 64, net);
bt_debug("TEST %s\n", buf);
int should_be = is_prefix_included(prefixes, net);
int is_there = trie_match_net(trie, net);
bt_assert_msg(should_be == is_there, "Prefix %s %s match", buf,
(should_be ? "should" : "should not"));
}
static int
t_match_random_net(void)
{
bt_bird_init();
bt_config_parse(BT_CONFIG_SIMPLE);
uint round;
for (round = 0; round < TESTS_NUM; round++)
int v6 = 0;
for (int round = 0; round < TESTS_NUM; round++)
{
list prefixes; /* of structs f_extended_prefix */
init_list(&prefixes);
struct f_trie *trie = f_new_trie(config->mem, 0);
list *prefixes = make_random_prefix_list(PREFIXES_NUM, v6, 0);
struct f_trie *trie = make_trie_from_prefix_list(prefixes);
generate_random_ipv6_prefixes(&prefixes);
struct f_prefix_node *n;
WALK_LIST(n, prefixes)
for (int i = 0; i < PREFIX_TESTS_NUM; i++)
{
trie_add_prefix(trie, &n->prefix.net, n->prefix.lo, n->prefix.hi);
net_addr net;
get_random_net(&net, v6);
test_match_net(prefixes, trie, &net);
}
int i;
for (i = 0; i < PREFIX_TESTS_NUM; i++)
{
struct f_prefix f = get_random_ip6_prefix();
bt_debug("TEST\t" PRIip6 "/%d\n", ARGip6(net6_prefix(&f.net)), f.net.pxlen);
int should_be = is_prefix_included(&prefixes, &f);
int is_there = trie_match_net(trie, &f.net);
bt_assert_msg(should_be == is_there, "Prefix " PRIip6 "/%d %s", ARGip6(net6_prefix(&f.net)), f.net.pxlen, (should_be ? "should be found in trie" : "should not be found in trie"));
}
struct f_prefix_node *nxt;
WALK_LIST_DELSAFE(n, nxt, prefixes)
{
free(n);
}
v6 = !v6;
tmp_flush();
}
bt_bird_cleanup();
return 1;
}
static int
t_match_inner_net(void)
{
bt_bird_init();
bt_config_parse(BT_CONFIG_SIMPLE);
int v6 = 0;
for (int round = 0; round < TESTS_NUM; round++)
{
list *prefixes = make_random_prefix_list(PREFIXES_NUM, v6, 0);
struct f_trie *trie = make_trie_from_prefix_list(prefixes);
struct f_prefix_node *n = HEAD(*prefixes);
for (int i = 0; i < PREFIX_TESTS_NUM; i++)
{
net_addr net;
get_inner_net(&net, &n->prefix);
test_match_net(prefixes, trie, &net);
n = NODE_VALID(NODE_NEXT(n)) ? NODE_NEXT(n) : HEAD(*prefixes);
}
v6 = !v6;
tmp_flush();
}
bt_bird_cleanup();
return 1;
}
static int
t_match_outer_net(void)
{
bt_bird_init();
bt_config_parse(BT_CONFIG_SIMPLE);
int v6 = 0;
for (int round = 0; round < TESTS_NUM; round++)
{
list *prefixes = make_random_prefix_list(PREFIXES_NUM, v6, 0);
struct f_trie *trie = make_trie_from_prefix_list(prefixes);
struct f_prefix_node *n = HEAD(*prefixes);
for (int i = 0; i < PREFIX_TESTS_NUM; i++)
{
net_addr net;
get_outer_net(&net, &n->prefix);
test_match_net(prefixes, trie, &net);
n = NODE_VALID(NODE_NEXT(n)) ? NODE_NEXT(n) : HEAD(*prefixes);
}
v6 = !v6;
tmp_flush();
}
v6 = !v6;
bt_bird_cleanup();
return 1;
}
/*
* Read prefixes from @filename, build set of tries, prepare test data and do
* PREFIX_BENCH_NUM trie lookups. With @plus = 0, use random subset of known
* prefixes as test data, with @plus = 1, use randomly generated /32 prefixes
* as test data.
*/
static int
benchmark_trie_dataset(const char *filename, int plus)
{
int n = 0;
list *data[TRIE_BUFFER_SIZE];
struct f_trie *trie[TRIE_BUFFER_SIZE];
net_addr *nets;
bt_reset_suite_case_timer();
bt_log_suite_case_result(1, "Reading %s", filename, n);
n = read_prefix_file(filename, plus, data, trie);
bt_log_suite_case_result(1, "Read prefix data, %d lists, ", n);
size_t trie_size = rmemsize(tmp_linpool).effective * 1000 / (1024*1024);
bt_log_suite_case_result(1, "Trie size %u.%03u MB",
(uint) (trie_size / 1000), (uint) (trie_size % 1000));
int t = PREFIX_BENCH_NUM / n;
int tb = MIN(t, TEST_BUFFER_SIZE);
nets = tmp_alloc(tb * sizeof(net_addr));
if (!plus)
select_random_prefix_subset(data, nets, n, tb);
else
make_random_addresses(nets, tb);
bt_log_suite_case_result(1, "Make test data, %d (%d) tests", t, tb);
bt_reset_suite_case_timer();
/*
int match = 0;
for (int i = 0; i < t; i++)
for (int j = 0; j < n; j++)
test_match_net(data[j], trie[j], &nets[i]);
*/
int match = 0;
for (int i = 0; i < t; i++)
for (int j = 0; j < n; j++)
if (trie_match_net(trie[j], &nets[i % TEST_BUFFER_SIZE]))
match++;
bt_log_suite_case_result(1, "Matching done, %d / %d matches", match, t * n);
tmp_flush();
return 1;
}
static int UNUSED
t_bench_trie_datasets_subset(void)
{
bt_bird_init();
bt_config_parse(BT_CONFIG_SIMPLE);
/* Specific datasets, not included */
benchmark_trie_dataset("trie-data-bgp-1", 0);
benchmark_trie_dataset("trie-data-bgp-10", 0);
benchmark_trie_dataset("trie-data-bgp-100", 0);
benchmark_trie_dataset("trie-data-bgp-1000", 0);
bt_bird_cleanup();
return 1;
}
static int UNUSED
t_bench_trie_datasets_random(void)
{
bt_bird_init();
bt_config_parse(BT_CONFIG_SIMPLE);
/* Specific datasets, not included */
benchmark_trie_dataset("trie-data-bgp-1", 1);
benchmark_trie_dataset("trie-data-bgp-10", 1);
benchmark_trie_dataset("trie-data-bgp-100", 1);
benchmark_trie_dataset("trie-data-bgp-1000", 1);
bt_bird_cleanup();
return 1;
}
static int
t_trie_same(void)
{
bt_bird_init();
bt_config_parse(BT_CONFIG_SIMPLE);
int round;
for (round = 0; round < TESTS_NUM*4; round++)
int v6 = 0;
for (int round = 0; round < TESTS_NUM*4; round++)
{
struct f_trie * trie1 = f_new_trie(config->mem, 0);
struct f_trie * trie2 = f_new_trie(config->mem, 0);
list prefixes; /* a list of f_extended_prefix structures */
init_list(&prefixes);
int i;
for (i = 0; i < 100; i++)
generate_random_ipv6_prefixes(&prefixes);
list *prefixes = make_random_prefix_list(100 * PREFIXES_NUM, v6, 0);
struct f_trie *trie1 = f_new_trie(tmp_linpool, 0);
struct f_trie *trie2 = f_new_trie(tmp_linpool, 0);
struct f_prefix_node *n;
WALK_LIST(n, prefixes)
{
WALK_LIST(n, *prefixes)
trie_add_prefix(trie1, &n->prefix.net, n->prefix.lo, n->prefix.hi);
}
WALK_LIST_BACKWARDS(n, prefixes)
{
WALK_LIST_BACKWARDS(n, *prefixes)
trie_add_prefix(trie2, &n->prefix.net, n->prefix.lo, n->prefix.hi);
}
bt_assert(trie_same(trie1, trie2));
struct f_prefix_node *nxt;
WALK_LIST_DELSAFE(n, nxt, prefixes)
{
free(n);
}
v6 = !v6;
tmp_flush();
}
bt_bird_cleanup();
return 1;
}
static inline void
log_networks(const net_addr *a, const net_addr *b)
{
if (bt_verbose >= BT_VERBOSE_ABSOLUTELY_ALL)
{
char buf0[64];
char buf1[64];
bt_format_net(buf0, 64, a);
bt_format_net(buf1, 64, b);
bt_debug("Found %s expected %s\n", buf0, buf1);
}
}
static int
t_trie_walk(void)
{
bt_bird_init();
bt_config_parse(BT_CONFIG_SIMPLE);
for (int round = 0; round < TESTS_NUM*8; round++)
{
int level = round / TESTS_NUM;
int v6 = level % 2;
int num = PREFIXES_NUM * (int[]){1, 10, 100, 1000}[level / 2];
int pos = 0, end = 0;
list *prefixes = make_random_prefix_list(num, v6, 1);
struct f_trie *trie = make_trie_from_prefix_list(prefixes);
struct f_prefix *pxset = malloc((num + 1) * sizeof(struct f_prefix));
struct f_prefix_node *n;
WALK_LIST(n, *prefixes)
pxset[pos++] = n->prefix;
memset(&pxset[pos], 0, sizeof (struct f_prefix));
qsort(pxset, num, sizeof(struct f_prefix), compare_prefixes);
/* Full walk */
bt_debug("Full walk (round %d, %d nets)\n", round, num);
pos = 0;
uint pxc = 0;
TRIE_WALK(trie, net, NULL)
{
log_networks(&net, &pxset[pos].net);
bt_assert(net_equal(&net, &pxset[pos].net));
/* Skip possible duplicates */
while (net_equal(&pxset[pos].net, &pxset[pos + 1].net))
pos++;
pos++;
pxc++;
}
TRIE_WALK_END;
bt_assert(pos == num);
bt_assert(pxc == trie->prefix_count);
bt_debug("Full walk done\n");
/* Prepare net for subnet walk - start with random prefix */
pos = bt_random() % num;
end = pos + (int[]){2, 2, 3, 4}[level / 2];
end = MIN(end, num);
struct f_prefix from = pxset[pos];
/* Find a common superprefix to several subsequent prefixes */
for (; pos < end; pos++)
{
if (net_equal(&from.net, &pxset[pos].net))
continue;
int common = !v6 ?
ip4_pxlen(net4_prefix(&from.net), net4_prefix(&pxset[pos].net)) :
ip6_pxlen(net6_prefix(&from.net), net6_prefix(&pxset[pos].net));
from.net.pxlen = MIN(from.net.pxlen, common);
if (!v6)
((net_addr_ip4 *) &from.net)->prefix =
ip4_and(net4_prefix(&from.net), net4_prefix(&pxset[pos].net));
else
((net_addr_ip6 *) &from.net)->prefix =
ip6_and(net6_prefix(&from.net), net6_prefix(&pxset[pos].net));
}
/* Fix irrelevant bits */
if (!v6)
((net_addr_ip4 *) &from.net)->prefix =
ip4_and(net4_prefix(&from.net), ip4_mkmask(net4_pxlen(&from.net)));
else
((net_addr_ip6 *) &from.net)->prefix =
ip6_and(net6_prefix(&from.net), ip6_mkmask(net6_pxlen(&from.net)));
/* Find initial position for final prefix */
for (pos = 0; pos < num; pos++)
if (compare_prefixes(&pxset[pos], &from) >= 0)
break;
int p0 = pos;
char buf0[64];
bt_format_net(buf0, 64, &from.net);
bt_debug("Subnet walk for %s (round %d, %d nets)\n", buf0, round, num);
/* Subnet walk */
TRIE_WALK(trie, net, &from.net)
{
log_networks(&net, &pxset[pos].net);
bt_assert(net_equal(&net, &pxset[pos].net));
bt_assert(net_in_netX(&net, &from.net));
/* Skip possible duplicates */
while (net_equal(&pxset[pos].net, &pxset[pos + 1].net))
pos++;
pos++;
}
TRIE_WALK_END;
bt_assert((pos == num) || !net_in_netX(&pxset[pos].net, &from.net));
bt_debug("Subnet walk done for %s (found %d nets)\n", buf0, pos - p0);
tmp_flush();
}
bt_bird_cleanup();
return 1;
}
static int
find_covering_nets(struct f_prefix *prefixes, int num, const net_addr *net, net_addr *found)
{
struct f_prefix key;
net_addr *n = &key.net;
int found_num = 0;
net_copy(n, net);
while (1)
{
struct f_prefix *px =
bsearch(&key, prefixes, num, sizeof(struct f_prefix), compare_prefixes);
if (px)
{
net_copy(&found[found_num], n);
found_num++;
}
if (n->pxlen == 0)
return found_num;
n->pxlen--;
if (n->type == NET_IP4)
ip4_clrbit(&((net_addr_ip4 *) n)->prefix, n->pxlen);
else
ip6_clrbit(&((net_addr_ip6 *) n)->prefix, n->pxlen);
}
}
static int
t_trie_walk_to_root(void)
{
bt_bird_init();
bt_config_parse(BT_CONFIG_SIMPLE);
for (int round = 0; round < TESTS_NUM * 4; round++)
{
int level = round / TESTS_NUM;
int v6 = level % 2;
int num = PREFIXES_NUM * (int[]){32, 512}[level / 2];
int pos = 0;
int st = 0, sn = 0, sm = 0;
list *prefixes = make_random_prefix_list(num, v6, 1);
struct f_trie *trie = make_trie_from_prefix_list(prefixes);
struct f_prefix *pxset = malloc((num + 1) * sizeof(struct f_prefix));
struct f_prefix_node *pxn;
WALK_LIST(pxn, *prefixes)
pxset[pos++] = pxn->prefix;
memset(&pxset[pos], 0, sizeof (struct f_prefix));
qsort(pxset, num, sizeof(struct f_prefix), compare_prefixes);
int i;
for (i = 0; i < (PREFIX_TESTS_NUM / 10); i++)
{
net_addr from;
get_random_net(&from, v6);
net_addr found[129];
int found_num = find_covering_nets(pxset, num, &from, found);
int n = 0;
if (bt_verbose >= BT_VERBOSE_ABSOLUTELY_ALL)
{
char buf[64];
bt_format_net(buf, 64, &from);
bt_debug("Lookup for %s (expect %d)\n", buf, found_num);
}
/* Walk to root, separate for IPv4 and IPv6 */
if (!v6)
{
TRIE_WALK_TO_ROOT_IP4(trie, (net_addr_ip4 *) &from, net)
{
log_networks((net_addr *) &net, &found[n]);
bt_assert((n < found_num) && net_equal((net_addr *) &net, &found[n]));
n++;
}
TRIE_WALK_TO_ROOT_END;
}
else
{
TRIE_WALK_TO_ROOT_IP6(trie, (net_addr_ip6 *) &from, net)
{
log_networks((net_addr *) &net, &found[n]);
bt_assert((n < found_num) && net_equal((net_addr *) &net, &found[n]));
n++;
}
TRIE_WALK_TO_ROOT_END;
}
bt_assert(n == found_num);
/* Stats */
st += n;
sn += !!n;
sm = MAX(sm, n);
}
bt_debug("Success in %d / %d, sum %d, max %d\n", sn, i, st, sm);
tmp_flush();
}
bt_bird_cleanup();
return 1;
}
@ -179,8 +885,15 @@ main(int argc, char *argv[])
{
bt_init(argc, argv);
bt_test_suite(t_match_net, "Testing random prefix matching");
bt_test_suite(t_match_random_net, "Testing random prefix matching");
bt_test_suite(t_match_inner_net, "Testing random inner prefix matching");
bt_test_suite(t_match_outer_net, "Testing random outer prefix matching");
bt_test_suite(t_trie_same, "A trie filled forward should be same with a trie filled backward.");
bt_test_suite(t_trie_walk, "Testing TRIE_WALK() on random tries");
bt_test_suite(t_trie_walk_to_root, "Testing TRIE_WALK_TO_ROOT() on random tries");
// bt_test_suite(t_bench_trie_datasets_subset, "Benchmark tries from datasets by random subset of nets");
// bt_test_suite(t_bench_trie_datasets_random, "Benchmark tries from datasets by generated addresses");
return bt_exit_value();
}

View File

@ -1,7 +1,7 @@
src := bitmap.c bitops.c blake2s.c blake2b.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c strtoul.c tbf.c timer.c xmalloc.c
src := a-path.c a-set.c bitmap.c bitops.c blake2s.c blake2b.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c strtoul.c tbf.c timer.c xmalloc.c
obj := $(src-o-files)
$(all-daemon)
tests_src := bitmap_test.c heap_test.c buffer_test.c event_test.c flowspec_test.c bitops_test.c patmatch_test.c fletcher16_test.c slist_test.c checksum_test.c lists_test.c mac_test.c ip_test.c hash_test.c printf_test.c
tests_src := a-set_test.c a-path_test.c bitmap_test.c heap_test.c buffer_test.c event_test.c flowspec_test.c bitops_test.c patmatch_test.c fletcher16_test.c slist_test.c checksum_test.c lists_test.c mac_test.c ip_test.c hash_test.c printf_test.c slab_test.c type_test.c
tests_targets := $(tests_targets) $(tests-target-files)
tests_objs := $(tests_objs) $(src-o-files)

View File

@ -8,8 +8,8 @@
*/
#include "nest/bird.h"
#include "nest/route.h"
#include "nest/attrs.h"
#include "nest/rt.h"
#include "lib/attrs.h"
#include "lib/resource.h"
#include "lib/unaligned.h"
#include "lib/string.h"
@ -591,7 +591,7 @@ as_path_match_set(const struct adata *path, const struct f_tree *set)
p += 2;
for (i=0; i<n; i++)
{
struct f_val v = {T_INT, .val.i = get_as(p)};
struct f_val v = { .type = T_INT, .val.i = get_as(p)};
if (find_tree(set, &v))
return 1;
p += BS;
@ -631,7 +631,7 @@ as_path_filter(struct linpool *pool, const struct adata *path, const struct f_tr
if (set)
{
struct f_val v = {T_INT, .val.i = as};
struct f_val v = { .type = T_INT, .val.i = as};
match = !!find_tree(set, &v);
}
else

View File

@ -9,8 +9,8 @@
#include "test/birdtest.h"
#include "test/bt-utils.h"
#include "nest/route.h"
#include "nest/attrs.h"
#include "nest/rt.h"
#include "lib/attrs.h"
#include "lib/resource.h"
#define TESTS_NUM 30
@ -23,8 +23,6 @@
static int
t_as_path_match(void)
{
resource_init();
int round;
for (round = 0; round < TESTS_NUM; round++)
{
@ -32,14 +30,13 @@ t_as_path_match(void)
struct adata *as_path = &empty_as_path;
u32 first_prepended, last_prepended;
first_prepended = last_prepended = 0;
struct linpool *lp = lp_new_default(&root_pool);
struct f_path_mask *mask = alloca(sizeof(struct f_path_mask) + AS_PATH_LENGTH * sizeof(struct f_path_mask_item));
mask->len = AS_PATH_LENGTH;
for (int i = AS_PATH_LENGTH - 1; i >= 0; i--)
{
u32 val = bt_random();
as_path = as_path_prepend(lp, as_path, val);
as_path = as_path_prepend(tmp_linpool, as_path, val);
bt_debug("Prepending ASN: %10u \n", val);
if (i == 0)
@ -61,7 +58,7 @@ t_as_path_match(void)
bt_assert(as_path_get_last(as_path, &asn));
bt_assert_msg(asn == first_prepended, "as_path_get_last() should return the first prepended ASN");
rfree(lp);
tmp_flush();
}
return 1;
@ -70,16 +67,13 @@ t_as_path_match(void)
static int
t_path_format(void)
{
resource_init();
struct adata empty_as_path = {};
struct adata *as_path = &empty_as_path;
struct linpool *lp = lp_new_default(&root_pool);
uint i;
for (i = 4294967285; i <= 4294967294; i++)
{
as_path = as_path_prepend(lp, as_path, i);
as_path = as_path_prepend(tmp_linpool, as_path, i);
bt_debug("Prepending ASN: %10u \n", i);
}
@ -97,7 +91,7 @@ t_path_format(void)
as_path_format(as_path, buf2, SMALL_BUFFER_SIZE);
bt_assert_msg(strcmp(buf2, "4294967294 42...") == 0, "Small Buffer(%zu): '%s'", strlen(buf2), buf2);
rfree(lp);
tmp_flush();
return 1;
}
@ -116,11 +110,8 @@ count_asn_in_array(const u32 *array, u32 asn)
static int
t_path_include(void)
{
resource_init();
struct adata empty_as_path = {};
struct adata *as_path = &empty_as_path;
struct linpool *lp = lp_new_default(&root_pool);
u32 as_nums[AS_PATH_LENGTH] = {};
int i;
@ -128,7 +119,7 @@ t_path_include(void)
{
u32 val = bt_random();
as_nums[i] = val;
as_path = as_path_prepend(lp, as_path, val);
as_path = as_path_prepend(tmp_linpool, as_path, val);
}
for (i = 0; i < AS_PATH_LENGTH; i++)
@ -136,8 +127,8 @@ t_path_include(void)
int counts_of_contains = count_asn_in_array(as_nums, as_nums[i]);
bt_assert_msg(as_path_contains(as_path, as_nums[i], counts_of_contains), "AS Path should contains %d-times number %d", counts_of_contains, as_nums[i]);
bt_assert(as_path_filter(lp, as_path, NULL, as_nums[i], 0) != NULL);
bt_assert(as_path_filter(lp, as_path, NULL, as_nums[i], 1) != NULL);
bt_assert(as_path_filter(tmp_linpool, as_path, NULL, as_nums[i], 0) != NULL);
bt_assert(as_path_filter(tmp_linpool, as_path, NULL, as_nums[i], 1) != NULL);
}
for (i = 0; i < 10000; i++)
@ -152,7 +143,7 @@ t_path_include(void)
bt_assert_msg(result == 0, "As path should not contain the number %u", test_val);
}
rfree(lp);
tmp_flush();
return 1;
}
@ -161,16 +152,13 @@ t_path_include(void)
static int
t_as_path_converting(void)
{
resource_init();
struct adata empty_as_path = {};
struct adata *as_path = &empty_as_path;
struct linpool *lp = lp_new_default(&root_pool);
#define AS_PATH_LENGTH_FOR_CONVERTING_TEST 10
int i;
for (i = 0; i < AS_PATH_LENGTH_FOR_CONVERTING_TEST; i++)
as_path = as_path_prepend(lp, as_path, i);
as_path = as_path_prepend(tmp_linpool, as_path, i);
bt_debug("data length: %u \n", as_path->length);

View File

@ -10,8 +10,8 @@
#include <stdlib.h>
#include "nest/bird.h"
#include "nest/route.h"
#include "nest/attrs.h"
#include "nest/rt.h"
#include "lib/attrs.h"
#include "lib/resource.h"
#include "lib/string.h"
@ -516,6 +516,48 @@ int_set_sort(struct linpool *pool, const struct adata *src)
return dst;
}
int
int_set_min(const struct adata *list, u32 *val)
{
if (!list)
return 0;
u32 *l = (u32 *) list->data;
int len = int_set_get_size(list);
int i;
if (len < 1)
return 0;
*val = *l++;
for (i = 1; i < len; i++, l++)
if (int_set_cmp(val, l) > 0)
*val = *l;
return 1;
}
int
int_set_max(const struct adata *list, u32 *val)
{
if (!list)
return 0;
u32 *l = (u32 *) list->data;
int len = int_set_get_size(list);
int i;
if (len < 1)
return 0;
*val = *l++;
for (i = 1; i < len; i++, l++)
if (int_set_cmp(val, l) < 0)
*val = *l;
return 1;
}
static int
ec_set_cmp(const void *X, const void *Y)
@ -541,6 +583,50 @@ ec_set_sort_x(struct adata *set)
qsort(set->data, set->length / 8, 8, ec_set_cmp);
}
int
ec_set_min(const struct adata *list, u64 *val)
{
if (!list)
return 0;
u32 *l = int_set_get_data(list);
int len = int_set_get_size(list);
int i;
if (len < 1)
return 0;
u32 *res = l; l += 2;
for (i = 2; i < len; i += 2, l += 2)
if (ec_set_cmp(res, l) > 0)
res = l;
*val = ec_generic(res[0], res[1]);
return 1;
}
int
ec_set_max(const struct adata *list, u64 *val)
{
if (!list)
return 0;
u32 *l = int_set_get_data(list);
int len = int_set_get_size(list);
int i;
if (len < 1)
return 0;
u32 *res = l; l += 2;
for (i = 2; i < len; i += 2, l += 2)
if (ec_set_cmp(res, l) < 0)
res = l;
*val = ec_generic(res[0], res[1]);
return 1;
}
static int
lc_set_cmp(const void *X, const void *Y)
@ -563,3 +649,47 @@ lc_set_sort(struct linpool *pool, const struct adata *src)
qsort(dst->data, dst->length / LCOMM_LENGTH, LCOMM_LENGTH, lc_set_cmp);
return dst;
}
int
lc_set_min(const struct adata *list, lcomm *val)
{
if (!list)
return 0;
u32 *l = int_set_get_data(list);
int len = int_set_get_size(list);
int i;
if (len < 1)
return 0;
u32 *res = l; l += 3;
for (i = 3; i < len; i += 3, l += 3)
if (lc_set_cmp(res, l) > 0)
res = l;
*val = (lcomm) { res[0], res[1], res[2] };
return 1;
}
int
lc_set_max(const struct adata *list, lcomm *val)
{
if (!list)
return 0;
u32 *l = int_set_get_data(list);
int len = int_set_get_size(list);
int i;
if (len < 1)
return 0;
u32 *res = l; l += 3;
for (i = 3; i < len; i += 3, l += 3)
if (lc_set_cmp(res, l) < 0)
res = l;
*val = (lcomm) { res[0], res[1], res[2] };
return 1;
}

View File

@ -10,8 +10,8 @@
#include "test/bt-utils.h"
#include "lib/net.h"
#include "nest/route.h"
#include "nest/attrs.h"
#include "nest/rt.h"
#include "lib/attrs.h"
#include "lib/resource.h"
#define SET_SIZE 10
@ -25,8 +25,6 @@ static byte buf[BUFFER_SIZE] = {};
#define SET_SIZE_FOR_FORMAT_OUTPUT 10
struct linpool *lp;
enum set_type
{
SET_TYPE_INT,
@ -38,24 +36,23 @@ generate_set_sequence(enum set_type type, int len)
{
struct adata empty_as_path = {};
set_sequence = set_sequence_same = set_sequence_higher = set_random = &empty_as_path;
lp = lp_new_default(&root_pool);
int i;
for (i = 0; i < len; i++)
{
if (type == SET_TYPE_INT)
{
set_sequence = int_set_add(lp, set_sequence, i);
set_sequence_same = int_set_add(lp, set_sequence_same, i);
set_sequence_higher = int_set_add(lp, set_sequence_higher, i + SET_SIZE);
set_random = int_set_add(lp, set_random, bt_random());
set_sequence = int_set_add(tmp_linpool, set_sequence, i);
set_sequence_same = int_set_add(tmp_linpool, set_sequence_same, i);
set_sequence_higher = int_set_add(tmp_linpool, set_sequence_higher, i + SET_SIZE);
set_random = int_set_add(tmp_linpool, set_random, bt_random());
}
else if (type == SET_TYPE_EC)
{
set_sequence = ec_set_add(lp, set_sequence, i);
set_sequence_same = ec_set_add(lp, set_sequence_same, i);
set_sequence_higher = ec_set_add(lp, set_sequence_higher, i + SET_SIZE);
set_random = ec_set_add(lp, set_random, (bt_random() << 32 | bt_random()));
set_sequence = ec_set_add(tmp_linpool, set_sequence, i);
set_sequence_same = ec_set_add(tmp_linpool, set_sequence_same, i);
set_sequence_higher = ec_set_add(tmp_linpool, set_sequence_higher, i + SET_SIZE);
set_random = ec_set_add(tmp_linpool, set_random, (bt_random() << 32 | bt_random()));
}
else
bt_abort_msg("This should be unreachable");
@ -71,7 +68,6 @@ t_set_int_contains(void)
{
int i;
resource_init();
generate_set_sequence(SET_TYPE_INT, SET_SIZE);
bt_assert(int_set_get_size(set_sequence) == SET_SIZE);
@ -85,33 +81,29 @@ t_set_int_contains(void)
for (i = 0; i < SET_SIZE; i++)
bt_assert_msg(data[i] == i, "(data[i] = %d) == i = %d)", data[i], i);
rfree(lp);
return 1;
}
static int
t_set_int_union(void)
{
resource_init();
generate_set_sequence(SET_TYPE_INT, SET_SIZE);
const struct adata *set_union;
set_union = int_set_union(lp, set_sequence, set_sequence_same);
set_union = int_set_union(tmp_linpool, set_sequence, set_sequence_same);
bt_assert(int_set_get_size(set_union) == SET_SIZE);
bt_assert(int_set_format(set_union, 0, 2, buf, BUFFER_SIZE) == 0);
set_union = int_set_union(lp, set_sequence, set_sequence_higher);
set_union = int_set_union(tmp_linpool, set_sequence, set_sequence_higher);
bt_assert_msg(int_set_get_size(set_union) == SET_SIZE*2, "int_set_get_size(set_union) %d, SET_SIZE*2 %d", int_set_get_size(set_union), SET_SIZE*2);
bt_assert(int_set_format(set_union, 0, 2, buf, BUFFER_SIZE) == 0);
rfree(lp);
return 1;
}
static int
t_set_int_format(void)
{
resource_init();
generate_set_sequence(SET_TYPE_INT, SET_SIZE_FOR_FORMAT_OUTPUT);
bt_assert(int_set_format(set_sequence, 0, 0, buf, BUFFER_SIZE) == 0);
@ -125,21 +117,19 @@ t_set_int_format(void)
bt_assert(int_set_format(set_sequence, 1, 0, buf, BUFFER_SIZE) == 0);
bt_assert(strcmp(buf, "(0,0) (0,1) (0,2) (0,3) (0,4) (0,5) (0,6) (0,7) (0,8) (0,9)") == 0);
rfree(lp);
return 1;
}
static int
t_set_int_delete(void)
{
resource_init();
generate_set_sequence(SET_TYPE_INT, SET_SIZE);
const struct adata *deleting_sequence = set_sequence;
u32 i;
for (i = 0; i < SET_SIZE; i++)
{
deleting_sequence = int_set_del(lp, deleting_sequence, i);
deleting_sequence = int_set_del(tmp_linpool, deleting_sequence, i);
bt_assert_msg(int_set_get_size(deleting_sequence) == (int) (SET_SIZE-1-i),
"int_set_get_size(deleting_sequence) %d == SET_SIZE-1-i %d",
int_set_get_size(deleting_sequence),
@ -160,7 +150,6 @@ t_set_ec_contains(void)
{
u32 i;
resource_init();
generate_set_sequence(SET_TYPE_EC, SET_SIZE);
bt_assert(ec_set_get_size(set_sequence) == SET_SIZE);
@ -174,62 +163,54 @@ t_set_ec_contains(void)
// for (i = 0; i < SET_SIZE; i++)
// bt_assert_msg(data[i] == (SET_SIZE-1-i), "(data[i] = %d) == ((SET_SIZE-1-i) = %d)", data[i], SET_SIZE-1-i);
rfree(lp);
return 1;
}
static int
t_set_ec_union(void)
{
resource_init();
generate_set_sequence(SET_TYPE_EC, SET_SIZE);
const struct adata *set_union;
set_union = ec_set_union(lp, set_sequence, set_sequence_same);
set_union = ec_set_union(tmp_linpool, set_sequence, set_sequence_same);
bt_assert(ec_set_get_size(set_union) == SET_SIZE);
bt_assert(ec_set_format(set_union, 0, buf, BUFFER_SIZE) == 0);
set_union = ec_set_union(lp, set_sequence, set_sequence_higher);
set_union = ec_set_union(tmp_linpool, set_sequence, set_sequence_higher);
bt_assert_msg(ec_set_get_size(set_union) == SET_SIZE*2, "ec_set_get_size(set_union) %d, SET_SIZE*2 %d", ec_set_get_size(set_union), SET_SIZE*2);
bt_assert(ec_set_format(set_union, 0, buf, BUFFER_SIZE) == 0);
rfree(lp);
return 1;
}
static int
t_set_ec_format(void)
{
resource_init();
const struct adata empty_as_path = {};
set_sequence = set_sequence_same = set_sequence_higher = set_random = &empty_as_path;
lp = lp_new_default(&root_pool);
u64 i = 0;
set_sequence = ec_set_add(lp, set_sequence, i);
set_sequence = ec_set_add(tmp_linpool, set_sequence, i);
for (i = 1; i < SET_SIZE_FOR_FORMAT_OUTPUT; i++)
set_sequence = ec_set_add(lp, set_sequence, i + ((i%2) ? ((u64)EC_RO << 48) : ((u64)EC_RT << 48)));
set_sequence = ec_set_add(tmp_linpool, set_sequence, i + ((i%2) ? ((u64)EC_RO << 48) : ((u64)EC_RT << 48)));
bt_assert(ec_set_format(set_sequence, 0, buf, BUFFER_SIZE) == 0);
bt_assert_msg(strcmp(buf, "(unknown 0x0, 0, 0) (ro, 0, 1) (rt, 0, 2) (ro, 0, 3) (rt, 0, 4) (ro, 0, 5) (rt, 0, 6) (ro, 0, 7) (rt, 0, 8) (ro, 0, 9)") == 0,
"ec_set_format() returns '%s'", buf);
rfree(lp);
return 1;
}
static int
t_set_ec_delete(void)
{
resource_init();
generate_set_sequence(SET_TYPE_EC, SET_SIZE);
const struct adata *deleting_sequence = set_sequence;
u32 i;
for (i = 0; i < SET_SIZE; i++)
{
deleting_sequence = ec_set_del(lp, deleting_sequence, i);
deleting_sequence = ec_set_del(tmp_linpool, deleting_sequence, i);
bt_assert_msg(ec_set_get_size(deleting_sequence) == (int) (SET_SIZE-1-i),
"ec_set_get_size(deleting_sequence) %d == SET_SIZE-1-i %d",
ec_set_get_size(deleting_sequence), SET_SIZE-1-i);

View File

@ -11,7 +11,39 @@
#include <stdint.h>
#include "lib/unaligned.h"
#include "nest/route.h"
typedef struct adata {
uint length; /* Length of data */
byte data[0];
} adata;
#define ADATA_SIZE(s) BIRD_CPU_ALIGN(sizeof(struct adata) + s)
extern const adata null_adata; /* adata of length 0 */
static inline struct adata *
lp_alloc_adata(struct linpool *pool, uint len)
{
struct adata *ad = lp_alloc(pool, sizeof(struct adata) + len);
ad->length = len;
return ad;
}
static inline struct adata *
lp_store_adata(struct linpool *pool, const void *buf, uint len)
{
struct adata *ad = lp_alloc_adata(pool, len);
memcpy(ad->data, buf, len);
return ad;
}
#define tmp_alloc_adata(len) lp_alloc_adata(tmp_linpool, len)
#define tmp_store_adata(buf, len) lp_store_adata(tmp_linpool, buf, len)
#define tmp_copy_adata(ad) tmp_store_adata((ad)->data, (ad)->length)
static inline int adata_same(const struct adata *a, const struct adata *b)
{ return (a->length == b->length && !memcmp(a->data, b->data, a->length)); }
/* a-path.c */
@ -218,6 +250,12 @@ struct adata *ec_set_del_nontrans(struct linpool *pool, const struct adata *set)
struct adata *int_set_sort(struct linpool *pool, const struct adata *src);
struct adata *ec_set_sort(struct linpool *pool, const struct adata *src);
struct adata *lc_set_sort(struct linpool *pool, const struct adata *src);
int int_set_min(const struct adata *list, u32 *val);
int ec_set_min(const struct adata *list, u64 *val);
int lc_set_min(const struct adata *list, lcomm *val);
int int_set_max(const struct adata *list, u32 *val);
int ec_set_max(const struct adata *list, u64 *val);
int lc_set_max(const struct adata *list, lcomm *val);
void ec_set_sort_x(struct adata *set); /* Sort in place */

View File

@ -9,16 +9,29 @@
#ifndef _BIRD_BIRDLIB_H_
#define _BIRD_BIRDLIB_H_
#include "sysdep/config.h"
#include "lib/alloca.h"
/* Ugly structure offset handling macros */
struct align_probe { char x; long int y; };
#define OFFSETOF(s, i) ((size_t) &((s *)0)->i)
#define SKIP_BACK(s, i, p) ((s *)((char *)p - OFFSETOF(s, i)))
#define BIRD_ALIGN(s, a) (((s)+a-1)&~(a-1))
#define CPU_STRUCT_ALIGN (sizeof(struct align_probe))
#define CPU_STRUCT_ALIGN (MAX_(_Alignof(void*), _Alignof(u64)))
#define BIRD_CPU_ALIGN(s) BIRD_ALIGN((s), CPU_STRUCT_ALIGN)
/* Structure item alignment macros */
#define PADDING_NAME(id) _padding_##id
#define PADDING_(id, sz) u8 PADDING_NAME(id)[sz]
#if CPU_POINTER_ALIGNMENT == 4
#define PADDING(id, n32, n64) PADDING_(id, n32)
#elif CPU_POINTER_ALIGNMENT == 8
#define PADDING(id, n32, n64) PADDING_(id, n64)
#else
#error "Strange CPU pointer alignment: " CPU_POINTER_ALIGNMENT
#endif
/* Utility macros */
@ -32,6 +45,9 @@ struct align_probe { char x; long int y; };
#define MAX(a,b) MAX_(a,b)
#endif
#define ROUND_DOWN_POW2(a,b) ((a) & ~((b)-1))
#define ROUND_UP_POW2(a,b) (((a)+((b)-1)) & ~((b)-1))
#define U64(c) UINT64_C(c)
#define ABS(a) ((a)>=0 ? (a) : -(a))
#define DELTA(a,b) (((a)>=(b))?(a)-(b):(b)-(a))

View File

@ -24,7 +24,6 @@ t_bmap_set_clear_random(void)
{
struct bmap b;
resource_init();
bmap_init(&b, &root_pool, 1024);
char expected[MAX_NUM] = {};
@ -60,7 +59,6 @@ t_hmap_set_clear_random(void)
{
struct hmap b;
resource_init();
hmap_init(&b, &root_pool, 1024);
char expected[MAX_NUM] = {};
@ -119,7 +117,6 @@ t_hmap_set_clear_fill(void)
{
struct hmap b;
resource_init();
hmap_init(&b, &root_pool, 1024);
char expected[MAX_NUM] = {};

View File

@ -41,7 +41,6 @@ fill_expected_array(void)
static void
init_buffer(void)
{
resource_init();
buffer_pool = &root_pool;
BUFFER_INIT(buf, buffer_pool, MAX_NUM);
}

View File

@ -157,6 +157,7 @@ ev_run_list(event_list *l)
io_log_event(e->hook, e->data);
ev_run(e);
tmp_flush();
}
return !EMPTY_LIST(*l);
@ -184,6 +185,7 @@ ev_run_list_limited(event_list *l, uint limit)
io_log_event(e->hook, e->data);
ev_run(e);
tmp_flush();
limit--;
}

View File

@ -15,7 +15,7 @@
#include "nest/locks.h"
#include "sysdep/unix/unix.h"
#include "nest/iface.h"
#include "nest/route.h"
#include "nest/rt.h"
#define MAX_NUM 4
@ -53,11 +53,10 @@ t_ev_run_list(void)
{
int i;
resource_init();
olock_init();
timer_init();
io_init();
rt_init();
io_init();
if_init();
// roa_init();
config_init();

119
lib/fib.h Normal file
View File

@ -0,0 +1,119 @@
/*
* BIRD Internet Routing Daemon -- Network prefix storage
*
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
* (c) 2022 Maria Matejka <mq@jmq.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef _BIRD_LIB_FIB_H_
#define _BIRD_LIB_FIB_H_
/*
* BIRD FIBs are generic data structure for storing network prefixes.
* Also used for the master routing table. Currently implemented as
* a hash table.
*
* Available operations:
* - insertion of new entry
* - deletion of entry
* - searching for entry by network prefix
* - asynchronous retrieval of fib contents
*/
struct fib;
struct fib_node {
struct fib_node *next; /* Next in hash chain */
struct fib_iterator *readers; /* List of readers of this node */
net_addr addr[0];
};
struct fib_iterator { /* See lib/slists.h for an explanation */
struct fib_iterator *prev, *next; /* Must be synced with struct fib_node! */
byte efef; /* 0xff to distinguish between iterator and node */
byte pad[3];
struct fib_node *node; /* Or NULL if freshly merged */
uint hash;
};
typedef void (*fib_init_fn)(struct fib *, void *);
struct fib {
pool *fib_pool; /* Pool holding all our data */
slab *fib_slab; /* Slab holding all fib nodes */
struct fib_node **hash_table; /* Node hash table */
uint hash_size; /* Number of hash table entries (a power of two) */
uint hash_order; /* Binary logarithm of hash_size */
uint hash_shift; /* 32 - hash_order */
uint addr_type; /* Type of address data stored in fib (NET_*) */
uint node_size; /* FIB node size, 0 for nonuniform */
uint node_offset; /* Offset of fib_node struct inside of user data */
uint entries; /* Number of entries */
uint entries_min, entries_max; /* Entry count limits (else start rehashing) */
fib_init_fn init; /* Constructor */
};
static inline void * fib_node_to_user(struct fib *f, struct fib_node *e)
{ return e ? (void *) ((char *) e - f->node_offset) : NULL; }
static inline struct fib_node * fib_user_to_node(struct fib *f, void *e)
{ return e ? (void *) ((char *) e + f->node_offset) : NULL; }
void fib_init(struct fib *f, pool *p, uint addr_type, uint node_size, uint node_offset, uint hash_order, fib_init_fn init);
void *fib_find(struct fib *, const net_addr *); /* Find or return NULL if doesn't exist */
void *fib_get_chain(struct fib *f, const net_addr *a); /* Find first node in linked list from hash table */
void *fib_get(struct fib *, const net_addr *); /* Find or create new if nonexistent */
void *fib_route(struct fib *, const net_addr *); /* Longest-match routing lookup */
void fib_delete(struct fib *, void *); /* Remove fib entry */
void fib_free(struct fib *); /* Destroy the fib */
void fib_check(struct fib *); /* Consistency check for debugging */
void fit_init(struct fib_iterator *, struct fib *); /* Internal functions, don't call */
struct fib_node *fit_get(struct fib *, struct fib_iterator *);
void fit_put(struct fib_iterator *, struct fib_node *);
void fit_put_next(struct fib *f, struct fib_iterator *i, struct fib_node *n, uint hpos);
void fit_put_end(struct fib_iterator *i);
void fit_copy(struct fib *f, struct fib_iterator *dst, struct fib_iterator *src);
#define FIB_WALK(fib, type, z) do { \
struct fib_node *fn_, **ff_ = (fib)->hash_table; \
uint count_ = (fib)->hash_size; \
type *z; \
while (count_--) \
for (fn_ = *ff_++; z = fib_node_to_user(fib, fn_); fn_=fn_->next)
#define FIB_WALK_END } while (0)
#define FIB_ITERATE_INIT(it, fib) fit_init(it, fib)
#define FIB_ITERATE_START(fib, it, type, z) do { \
struct fib_node *fn_ = fit_get(fib, it); \
uint count_ = (fib)->hash_size; \
uint hpos_ = (it)->hash; \
type *z; \
for(;;) { \
if (!fn_) \
{ \
if (++hpos_ >= count_) \
break; \
fn_ = (fib)->hash_table[hpos_]; \
continue; \
} \
z = fib_node_to_user(fib, fn_);
#define FIB_ITERATE_END fn_ = fn_->next; } } while(0)
#define FIB_ITERATE_PUT(it) fit_put(it, fn_)
#define FIB_ITERATE_PUT_NEXT(it, fib) fit_put_next(fib, it, fn_, hpos_)
#define FIB_ITERATE_PUT_END(it) fit_put_end(it)
#define FIB_ITERATE_UNLINK(it, fib) fit_get(fib, it)
#define FIB_ITERATE_COPY(dst, src, fib) fit_copy(fib, dst, src)
#endif

View File

@ -446,10 +446,7 @@ t_validation6(void)
static int
t_builder4(void)
{
resource_init();
struct flow_builder *fb = flow_builder_init(&root_pool);
linpool *lp = lp_new_default(&root_pool);
/* Expectation */
@ -492,7 +489,7 @@ t_builder4(void)
flow_builder_set_type(fb, FLOW_TYPE_TCP_FLAGS);
flow_builder_add_op_val(fb, 0, 0x55);
net_addr_flow4 *res = flow_builder4_finalize(fb, lp);
net_addr_flow4 *res = flow_builder4_finalize(fb, tmp_linpool);
bt_assert(memcmp(res, expect, expect->length) == 0);
@ -529,8 +526,6 @@ t_builder6(void)
{
net_addr_ip6 ip;
resource_init();
linpool *lp = lp_new_default(&root_pool);
struct flow_builder *fb = flow_builder_init(&root_pool);
fb->ipv6 = 1;
@ -574,7 +569,7 @@ t_builder6(void)
flow_builder_set_type(fb, FLOW_TYPE_LABEL);
flow_builder_add_op_val(fb, 0, 0x55);
net_addr_flow6 *res = flow_builder6_finalize(fb, lp);
net_addr_flow6 *res = flow_builder6_finalize(fb, tmp_linpool);
bt_assert(memcmp(res, expect, expect->length) == 0);
/* Reverse order */
@ -601,7 +596,7 @@ t_builder6(void)
flow_builder_set_type(fb, FLOW_TYPE_DST_PREFIX);
flow_builder6_add_pfx(fb, &ip, 61);
res = flow_builder6_finalize(fb, lp);
res = flow_builder6_finalize(fb, tmp_linpool);
bt_assert(memcmp(res, expect, expect->length) == 0);
return 1;

View File

@ -61,7 +61,6 @@ dump_nodes(void)
static void
init_hash_(uint order)
{
resource_init();
my_pool = rp_new(&root_pool, "Test pool");
HASH_INIT(hash, my_pool, order);

View File

@ -85,25 +85,29 @@ ip4_classify(ip4_addr ad)
u32 a = _I(ad);
u32 b = a >> 24U;
if (b && b <= 0xdf)
if (b < 0xe0)
{
if (b == 0x7f)
if (b == 0x00) /* 0.0.0.0/8 This network */
return IADDR_INVALID;
if (b == 0x7f) /* 127.0.0.0/8 Loopback address */
return IADDR_HOST | SCOPE_HOST;
else if ((b == 0x0a) ||
((a & 0xffff0000) == 0xc0a80000) ||
((a & 0xfff00000) == 0xac100000))
if ((b == 0x0a) || /* 10.0.0.0/8 Private range */
((a & 0xffff0000) == 0xc0a80000) || /* 192.168.0.0/16 Private range */
((a & 0xfff00000) == 0xac100000)) /* 172.16.0.0/12 Private range */
return IADDR_HOST | SCOPE_SITE;
else
return IADDR_HOST | SCOPE_UNIVERSE;
return IADDR_HOST | SCOPE_UNIVERSE;
}
if (b >= 0xe0 && b <= 0xef)
if (b < 0xf0) /* 224.0.0.0/4 Multicast address */
return IADDR_MULTICAST | SCOPE_UNIVERSE;
if (a == 0xffffffff)
if (a == 0xffffffff) /* 255.255.255.255 Broadcast address */
return IADDR_BROADCAST | SCOPE_LINK;
return IADDR_INVALID;
return IADDR_HOST | SCOPE_SITE; /* 240.0.0.0/4 Reserved / private */
}
int

View File

@ -279,11 +279,35 @@ static inline uint ip6_pxlen(ip6_addr a, ip6_addr b)
return 32 * i + 31 - u32_log2(a.addr[i] ^ b.addr[i]);
}
static inline int ip4_prefix_equal(ip4_addr a, ip4_addr b, uint n)
{
return (_I(a) ^ _I(b)) < ((u64) 1 << (32 - n));
}
static inline int ip6_prefix_equal(ip6_addr a, ip6_addr b, uint n)
{
uint n0 = n / 32;
uint n1 = n % 32;
return
((n0 <= 0) || (_I0(a) == _I0(b))) &&
((n0 <= 1) || (_I1(a) == _I1(b))) &&
((n0 <= 2) || (_I2(a) == _I2(b))) &&
((n0 <= 3) || (_I3(a) == _I3(b))) &&
(!n1 || ((a.addr[n0] ^ b.addr[n0]) < (1u << (32 - n1))));
}
static inline u32 ip4_getbit(ip4_addr a, uint pos)
{ return _I(a) & (0x80000000 >> pos); }
{ return (_I(a) >> (31 - pos)) & 1; }
static inline u32 ip4_getbits(ip4_addr a, uint pos, uint n)
{ return (_I(a) >> ((32 - n) - pos)) & ((1u << n) - 1); }
static inline u32 ip6_getbit(ip6_addr a, uint pos)
{ return a.addr[pos / 32] & (0x80000000 >> (pos % 32)); }
{ return (a.addr[pos / 32] >> (31 - (pos % 32))) & 0x1; }
static inline u32 ip6_getbits(ip6_addr a, uint pos, uint n)
{ return (a.addr[pos / 32] >> ((32 - n) - (pos % 32))) & ((1u << n) - 1); }
static inline u32 ip4_setbit(ip4_addr *a, uint pos)
{ return _I(*a) |= (0x80000000 >> pos); }
@ -297,6 +321,13 @@ static inline u32 ip4_clrbit(ip4_addr *a, uint pos)
static inline u32 ip6_clrbit(ip6_addr *a, uint pos)
{ return a->addr[pos / 32] &= ~(0x80000000 >> (pos % 32)); }
static inline ip4_addr ip4_setbits(ip4_addr a, uint pos, uint val)
{ _I(a) |= val << (31 - pos); return a; }
static inline ip6_addr ip6_setbits(ip6_addr a, uint pos, uint val)
{ a.addr[pos / 32] |= val << (31 - pos % 32); return a; }
static inline ip4_addr ip4_opposite_m1(ip4_addr a)
{ return _MI4(_I(a) ^ 1); }
@ -331,11 +362,7 @@ static inline ip6_addr ip6_hton(ip6_addr a)
static inline ip6_addr ip6_ntoh(ip6_addr a)
{ return _MI6(ntohl(_I0(a)), ntohl(_I1(a)), ntohl(_I2(a)), ntohl(_I3(a))); }
#define MPLS_MAX_LABEL_STACK 8
typedef struct mpls_label_stack {
uint len;
u32 stack[MPLS_MAX_LABEL_STACK];
} mpls_label_stack;
#define MPLS_MAX_LABEL_STACK 16
static inline int
mpls_get(const char *buf, int buflen, u32 *stack)

View File

@ -167,6 +167,70 @@ t_ip6_ntop(void)
return bt_assert_batch(test_vectors, test_ipa_ntop, bt_fmt_ipa, bt_fmt_str);
}
static int
t_ip4_prefix_equal(void)
{
bt_assert( ip4_prefix_equal(ip4_from_u32(0x12345678), ip4_from_u32(0x1234ffff), 16));
bt_assert(!ip4_prefix_equal(ip4_from_u32(0x12345678), ip4_from_u32(0x1234ffff), 17));
bt_assert( ip4_prefix_equal(ip4_from_u32(0x12345678), ip4_from_u32(0x12345000), 21));
bt_assert(!ip4_prefix_equal(ip4_from_u32(0x12345678), ip4_from_u32(0x12345000), 22));
bt_assert( ip4_prefix_equal(ip4_from_u32(0x00000000), ip4_from_u32(0xffffffff), 0));
bt_assert( ip4_prefix_equal(ip4_from_u32(0x12345678), ip4_from_u32(0x12345678), 0));
bt_assert( ip4_prefix_equal(ip4_from_u32(0x12345678), ip4_from_u32(0x12345678), 32));
bt_assert(!ip4_prefix_equal(ip4_from_u32(0x12345678), ip4_from_u32(0x12345679), 32));
bt_assert(!ip4_prefix_equal(ip4_from_u32(0x12345678), ip4_from_u32(0x92345678), 32));
return 1;
}
static int
t_ip6_prefix_equal(void)
{
bt_assert( ip6_prefix_equal(ip6_build(0x20010db8, 0x12345678, 0x10101010, 0x20202020),
ip6_build(0x20010db8, 0x1234ffff, 0xfefefefe, 0xdcdcdcdc),
48));
bt_assert(!ip6_prefix_equal(ip6_build(0x20010db8, 0x12345678, 0x10101010, 0x20202020),
ip6_build(0x20010db8, 0x1234ffff, 0xfefefefe, 0xdcdcdcdc),
49));
bt_assert(!ip6_prefix_equal(ip6_build(0x20010db8, 0x12345678, 0x10101010, 0x20202020),
ip6_build(0x20020db8, 0x12345678, 0xfefefefe, 0xdcdcdcdc),
48));
bt_assert( ip6_prefix_equal(ip6_build(0x20010db8, 0x12345678, 0x10101010, 0x20202020),
ip6_build(0x20010db8, 0x12345678, 0xfefefefe, 0xdcdcdcdc),
64));
bt_assert(!ip6_prefix_equal(ip6_build(0x20010db8, 0x12345678, 0x10101010, 0x20202020),
ip6_build(0x20010db8, 0x1234567e, 0xfefefefe, 0xdcdcdcdc),
64));
bt_assert( ip6_prefix_equal(ip6_build(0x20010db8, 0x12345678, 0x10101010, 0x20002020),
ip6_build(0x20010db8, 0x12345678, 0x10101010, 0x20202020),
106));
bt_assert(!ip6_prefix_equal(ip6_build(0x20010db8, 0x12345678, 0x10101010, 0x20002020),
ip6_build(0x20010db8, 0x12345678, 0x10101010, 0x20202020),
107));
bt_assert( ip6_prefix_equal(ip6_build(0xfeef0db8, 0x87654321, 0x10101010, 0x20202020),
ip6_build(0x20010db8, 0x12345678, 0xfefefefe, 0xdcdcdcdc),
0));
bt_assert( ip6_prefix_equal(ip6_build(0x20010db8, 0x12345678, 0x10101010, 0x20202020),
ip6_build(0x20010db8, 0x12345678, 0x10101010, 0x20202020),
128));
bt_assert(!ip6_prefix_equal(ip6_build(0x20010db8, 0x12345678, 0x10101010, 0x20202020),
ip6_build(0x20010db8, 0x12345678, 0x10101010, 0x20202021),
128));
return 1;
}
int
main(int argc, char *argv[])
{
@ -176,6 +240,8 @@ main(int argc, char *argv[])
bt_test_suite(t_ip6_pton, "Converting IPv6 string to ip6_addr struct");
bt_test_suite(t_ip4_ntop, "Converting ip4_addr struct to IPv4 string");
bt_test_suite(t_ip6_ntop, "Converting ip6_addr struct to IPv6 string");
bt_test_suite(t_ip4_prefix_equal, "Testing ip4_prefix_equal()");
bt_test_suite(t_ip6_prefix_equal, "Testing ip6_prefix_equal()");
return bt_exit_value();
}

View File

@ -42,6 +42,7 @@ typedef union list { /* In fact two overlayed nodes */
};
} list;
#define STATIC_LIST_INIT(name) name = { .head = &name.tail_node, .tail = &name.head_node, .null = NULL }
#define NODE (node *)
#define HEAD(list) ((void *)((list).head))

View File

@ -27,26 +27,24 @@
struct lp_chunk {
struct lp_chunk *next;
uint size;
uintptr_t data_align[0];
byte data[0];
};
const int lp_chunk_size = sizeof(struct lp_chunk);
#define LP_DATA_SIZE (page_size - OFFSETOF(struct lp_chunk, data))
struct linpool {
resource r;
byte *ptr, *end;
pool *p;
struct lp_chunk *first, *current; /* Normal (reusable) chunks */
struct lp_chunk *first_large; /* Large chunks */
uint chunk_size, threshold, total:31, use_pages:1, total_large;
uint total, total_large;
};
static void lp_free(resource *);
static void lp_dump(resource *);
static resource *lp_lookup(resource *, unsigned long);
static size_t lp_memsize(resource *r);
static struct resmem lp_memsize(resource *r);
static struct resclass lp_class = {
"LinPool",
@ -60,26 +58,14 @@ static struct resclass lp_class = {
/**
* lp_new - create a new linear memory pool
* @p: pool
* @blk: block size
*
* lp_new() creates a new linear memory pool resource inside the pool @p.
* The linear pool consists of a list of memory chunks of size at least
* @blk.
* The linear pool consists of a list of memory chunks of page size.
*/
linpool
*lp_new(pool *p, uint blk)
*lp_new(pool *p)
{
linpool *m = ralloc(p, &lp_class);
m->p = p;
if (!blk)
{
m->use_pages = 1;
blk = page_size - lp_chunk_size;
}
m->chunk_size = blk;
m->threshold = 3*blk/4;
return m;
return ralloc(p, &lp_class);
}
/**
@ -110,14 +96,13 @@ lp_alloc(linpool *m, uint size)
else
{
struct lp_chunk *c;
if (size >= m->threshold)
if (size > LP_DATA_SIZE)
{
/* Too large => allocate large chunk */
c = xmalloc(sizeof(struct lp_chunk) + size);
m->total_large += size;
c->next = m->first_large;
m->first_large = c;
c->size = size;
}
else
{
@ -129,14 +114,10 @@ lp_alloc(linpool *m, uint size)
else
{
/* Need to allocate a new chunk */
if (m->use_pages)
c = alloc_page(m->p);
else
c = xmalloc(sizeof(struct lp_chunk) + m->chunk_size);
c = alloc_page();
m->total += m->chunk_size;
m->total += LP_DATA_SIZE;
c->next = NULL;
c->size = m->chunk_size;
if (m->current)
m->current->next = c;
@ -145,7 +126,7 @@ lp_alloc(linpool *m, uint size)
}
m->current = c;
m->ptr = c->data + size;
m->end = c->data + m->chunk_size;
m->end = c->data + LP_DATA_SIZE;
}
return c->data;
}
@ -207,7 +188,7 @@ lp_flush(linpool *m)
/* Move ptr to the first chunk and free all large chunks */
m->current = c = m->first;
m->ptr = c ? c->data : NULL;
m->end = c ? c->data + m->chunk_size : NULL;
m->end = c ? c->data + LP_DATA_SIZE : NULL;
while (c = m->first_large)
{
@ -230,6 +211,7 @@ lp_save(linpool *m, lp_state *p)
{
p->current = m->current;
p->large = m->first_large;
p->total_large = m->total_large;
p->ptr = m->ptr;
}
@ -251,12 +233,12 @@ lp_restore(linpool *m, lp_state *p)
/* Move ptr to the saved pos and free all newer large chunks */
m->current = c = p->current;
m->ptr = p->ptr;
m->end = c ? c->data + m->chunk_size : NULL;
m->end = c ? c->data + LP_DATA_SIZE : NULL;
m->total_large = p->total_large;
while ((c = m->first_large) && (c != p->large))
{
m->first_large = c->next;
m->total_large -= c->size;
xfree(c);
}
}
@ -270,10 +252,7 @@ lp_free(resource *r)
for(d=m->first; d; d = c)
{
c = d->next;
if (m->use_pages)
free_page(m->p, d);
else
xfree(d);
free_page(d);
}
for(d=m->first_large; d; d = c)
{
@ -293,30 +272,33 @@ lp_dump(resource *r)
;
for(cntl=0, c=m->first_large; c; c=c->next, cntl++)
;
debug("(chunk=%d threshold=%d count=%d+%d total=%d+%d)\n",
m->chunk_size,
m->threshold,
debug("(count=%d+%d total=%d+%d)\n",
cnt,
cntl,
m->total,
m->total_large);
}
static size_t
static struct resmem
lp_memsize(resource *r)
{
linpool *m = (linpool *) r;
struct lp_chunk *c;
int cnt = 0;
struct resmem sz = {
.overhead = sizeof(struct linpool) + ALLOC_OVERHEAD,
.effective = m->total_large,
};
for(c=m->first; c; c=c->next)
cnt++;
for(c=m->first_large; c; c=c->next)
cnt++;
for (struct lp_chunk *c = m->first_large; c; c = c->next)
sz.overhead += sizeof(struct lp_chunk) + ALLOC_OVERHEAD;
return ALLOC_OVERHEAD + sizeof(struct linpool) +
cnt * (ALLOC_OVERHEAD + sizeof(struct lp_chunk)) +
m->total + m->total_large;
uint regular = 0;
for (struct lp_chunk *c = m->first; c; c = c->next)
regular++;
sz.effective += LP_DATA_SIZE * regular;
sz.overhead += (sizeof(struct lp_chunk) + ALLOC_OVERHEAD) * regular;
return sz;
}
@ -327,10 +309,7 @@ lp_lookup(resource *r, unsigned long a)
struct lp_chunk *c;
for(c=m->first; c; c=c->next)
if ((unsigned long) c->data <= a && (unsigned long) c->data + c->size > a)
return r;
for(c=m->first_large; c; c=c->next)
if ((unsigned long) c->data <= a && (unsigned long) c->data + c->size > a)
if ((unsigned long) c->data <= a && (unsigned long) c->data + LP_DATA_SIZE > a)
return r;
return NULL;
}

View File

@ -38,6 +38,7 @@
#define NB_IP (NB_IP4 | NB_IP6)
#define NB_VPN (NB_VPN4 | NB_VPN6)
#define NB_ROA (NB_ROA4 | NB_ROA6)
#define NB_FLOW (NB_FLOW4 | NB_FLOW6)
#define NB_DEST (NB_IP | NB_IP6_SADR | NB_VPN | NB_MPLS)
#define NB_ANY 0xffffffff

View File

@ -568,3 +568,51 @@ buffer_puts(buffer *buf, const char *str)
buf->pos = (bp < be) ? bp : buf->end;
}
#define POOL_PRINTF_MAXBUF 1024
char *mb_vsprintf(pool *p, const char *fmt, va_list args)
{
char buf[POOL_PRINTF_MAXBUF];
int count = bvsnprintf(buf, POOL_PRINTF_MAXBUF, fmt, args);
if (count < 0)
bug("Attempted to mb_vsprintf() a too long string");
char *out = mb_alloc(p, count + 1);
memcpy(out, buf, count + 1);
return out;
}
char *mb_sprintf(pool *p, const char *fmt, ...)
{
va_list args;
char *out;
va_start(args, fmt);
out = mb_vsprintf(p, fmt, args);
va_end(args);
return out;
}
char *lp_vsprintf(linpool *p, const char *fmt, va_list args)
{
char buf[POOL_PRINTF_MAXBUF];
int count = bvsnprintf(buf, POOL_PRINTF_MAXBUF, fmt, args);
if (count < 0)
bug("Attempted to mb_vsprintf() a too long string");
char *out = lp_alloc(p, count + 1);
memcpy(out, buf, count + 1);
return out;
}
char *lp_sprintf(linpool *p, const char *fmt, ...)
{
va_list args;
char *out;
va_start(args, fmt);
out = lp_vsprintf(p, fmt, args);
va_end(args);
return out;
}

View File

@ -2,6 +2,7 @@
* BIRD Resource Manager
*
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
* (c) 2021 Maria Matejka <mq@jmq.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
@ -31,22 +32,13 @@
struct pool {
resource r;
list inside;
struct pool_pages *pages;
const char *name;
};
struct pool_pages {
uint free;
uint used;
void *ptr[0];
};
#define POOL_PAGES_MAX ((page_size - sizeof(struct pool_pages)) / sizeof (void *))
static void pool_dump(resource *);
static void pool_free(resource *);
static resource *pool_lookup(resource *, unsigned long);
static size_t pool_memsize(resource *P);
static struct resmem pool_memsize(resource *P);
static struct resclass pool_class = {
"Pool",
@ -59,9 +51,6 @@ static struct resclass pool_class = {
pool root_pool;
void *alloc_sys_page(void);
void free_sys_page(void *);
static int indent;
/**
@ -81,6 +70,20 @@ rp_new(pool *p, const char *name)
return z;
}
pool *
rp_newf(pool *p, const char *fmt, ...)
{
pool *z = rp_new(p, NULL);
va_list args;
va_start(args, fmt);
z->name = mb_vsprintf(p, fmt, args);
va_end(args);
return z;
}
static void
pool_free(resource *P)
{
@ -94,14 +97,6 @@ pool_free(resource *P)
xfree(r);
r = rr;
}
if (p->pages)
{
ASSERT_DIE(!p->pages->used);
for (uint i=0; i<p->pages->free; i++)
free_sys_page(p->pages->ptr[i]);
free_sys_page(p->pages);
}
}
static void
@ -117,18 +112,22 @@ pool_dump(resource *P)
indent -= 3;
}
static size_t
static struct resmem
pool_memsize(resource *P)
{
pool *p = (pool *) P;
resource *r;
size_t sum = sizeof(pool) + ALLOC_OVERHEAD;
struct resmem sum = {
.effective = 0,
.overhead = sizeof(pool) + ALLOC_OVERHEAD,
};
WALK_LIST(r, p->inside)
sum += rmemsize(r);
if (p->pages)
sum += page_size * (p->pages->used + p->pages->free + 1);
{
struct resmem add = rmemsize(r);
sum.effective += add.effective;
sum.overhead += add.overhead;
}
return sum;
}
@ -216,14 +215,17 @@ rdump(void *res)
debug("NULL\n");
}
size_t
struct resmem
rmemsize(void *res)
{
resource *r = res;
if (!r)
return 0;
return (struct resmem) {};
if (!r->class->memsize)
return r->class->size + ALLOC_OVERHEAD;
return (struct resmem) {
.effective = r->class->size - sizeof(resource),
.overhead = ALLOC_OVERHEAD + sizeof(resource),
};
return r->class->memsize(r);
}
@ -282,11 +284,33 @@ rlookup(unsigned long a)
void
resource_init(void)
{
resource_sys_init();
root_pool.r.class = &pool_class;
root_pool.name = "Root";
init_list(&root_pool.inside);
tmp_init(&root_pool);
}
_Thread_local struct tmp_resources tmp_res;
void
tmp_init(pool *p)
{
tmp_res.lp = lp_new_default(p);
tmp_res.parent = p;
tmp_res.pool = rp_new(p, "TMP");
}
void
tmp_flush(void)
{
lp_flush(tmp_linpool);
rfree(tmp_res.pool);
tmp_res.pool = rp_new(tmp_res.parent, "TMP");
}
/**
* DOC: Memory blocks
*
@ -328,11 +352,14 @@ mbl_lookup(resource *r, unsigned long a)
return NULL;
}
static size_t
static struct resmem
mbl_memsize(resource *r)
{
struct mblock *m = (struct mblock *) r;
return ALLOC_OVERHEAD + sizeof(struct mblock) + m->size;
return (struct resmem) {
.effective = m->size,
.overhead = ALLOC_OVERHEAD + sizeof(struct mblock),
};
}
static struct resclass mb_class = {
@ -416,21 +443,6 @@ mb_realloc(void *m, unsigned size)
return b->data;
}
/**
* mb_move - move a memory block
* @m: memory block
* @p: target pool
*
* mb_move() moves the given memory block to another pool in the same way
* as rmove() moves a plain resource.
*/
void
mb_move(void *m, pool *p)
{
struct mblock *b = SKIP_BACK(struct mblock, data, m);
rmove(b, p);
}
/**
* mb_free - free a memory block
@ -448,39 +460,6 @@ mb_free(void *m)
rfree(b);
}
void *
alloc_page(pool *p)
{
if (!p->pages)
{
p->pages = alloc_sys_page();
p->pages->free = 0;
p->pages->used = 1;
}
else
p->pages->used++;
if (p->pages->free)
{
void *ptr = p->pages->ptr[--p->pages->free];
bzero(ptr, page_size);
return ptr;
}
else
return alloc_sys_page();
}
void
free_page(pool *p, void *ptr)
{
ASSERT_DIE(p->pages);
p->pages->used--;
if (p->pages->free >= POOL_PAGES_MAX)
return free_sys_page(ptr);
else
p->pages->ptr[p->pages->free++] = ptr;
}
#define STEP_UP(x) ((x) + (x)/2 + 4)

View File

@ -2,6 +2,7 @@
* BIRD Resource Manager
*
* (c) 1998--1999 Martin Mares <mj@ucw.cz>
* (c) 2021 Maria Matejka <mq@jmq.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
@ -11,6 +12,11 @@
#include "lib/lists.h"
struct resmem {
size_t effective; /* Memory actually used for data storage */
size_t overhead; /* Overhead memory imposed by allocator strategies */
};
/* Resource */
typedef struct resource {
@ -26,11 +32,11 @@ struct resclass {
void (*free)(resource *); /* Freeing function */
void (*dump)(resource *); /* Dump to debug output */
resource *(*lookup)(resource *, unsigned long); /* Look up address (only for debugging) */
size_t (*memsize)(resource *); /* Return size of memory used by the resource, may be NULL */
struct resmem (*memsize)(resource *); /* Return size of memory used by the resource, may be NULL */
};
/* Estimate of system allocator overhead per item, for memory consumtion stats */
#define ALLOC_OVERHEAD 8
#define ALLOC_OVERHEAD 16
/* Generic resource manipulation */
@ -38,9 +44,10 @@ typedef struct pool pool;
void resource_init(void);
pool *rp_new(pool *, const char *); /* Create new pool */
pool *rp_newf(pool *, const char *, ...); /* Create a new pool with a formatted string as its name */
void rfree(void *); /* Free single resource */
void rdump(void *); /* Dump to debug output */
size_t rmemsize(void *res); /* Return size of memory used by the resource */
struct resmem rmemsize(void *res); /* Return size of memory used by the resource */
void rlookup(unsigned long); /* Look up address (only for debugging) */
void rmove(void *, pool *); /* Move to a different pool */
@ -53,7 +60,6 @@ extern pool root_pool;
void *mb_alloc(pool *, unsigned size);
void *mb_allocz(pool *, unsigned size);
void *mb_realloc(void *m, unsigned size);
void mb_move(void *, pool *);
void mb_free(void *);
/* Memory pools with linear allocation */
@ -63,9 +69,10 @@ typedef struct linpool linpool;
typedef struct lp_state {
void *current, *large;
byte *ptr;
uint total_large;
} lp_state;
linpool *lp_new(pool *, unsigned blk);
linpool *lp_new(pool *);
void *lp_alloc(linpool *, unsigned size); /* Aligned */
void *lp_allocu(linpool *, unsigned size); /* Unaligned */
void *lp_allocz(linpool *, unsigned size); /* With clear */
@ -73,10 +80,23 @@ void lp_flush(linpool *); /* Free everything, but leave linpool */
void lp_save(linpool *m, lp_state *p); /* Save state */
void lp_restore(linpool *m, lp_state *p); /* Restore state */
extern const int lp_chunk_size;
#define LP_GAS 1024
#define LP_GOOD_SIZE(x) (((x + LP_GAS - 1) & (~(LP_GAS - 1))) - lp_chunk_size)
#define lp_new_default(p) lp_new(p, 0)
struct tmp_resources {
pool *pool, *parent;
linpool *lp;
};
extern _Thread_local struct tmp_resources tmp_res;
#define tmp_linpool tmp_res.lp
#define tmp_alloc(sz) lp_alloc(tmp_linpool, sz)
#define tmp_allocu(sz) lp_allocu(tmp_linpool, sz)
#define tmp_allocz(sz) lp_allocz(tmp_linpool, sz)
void tmp_init(pool *p);
void tmp_flush(void);
#define lp_new_default lp_new
/* Slabs */
@ -85,7 +105,7 @@ typedef struct slab slab;
slab *sl_new(pool *, unsigned size);
void *sl_alloc(slab *);
void *sl_allocz(slab *);
void sl_free(slab *, void *);
void sl_free(void *);
/*
* Low-level memory allocation functions, please don't use
@ -94,12 +114,12 @@ void sl_free(slab *, void *);
void buffer_realloc(void **buf, unsigned *size, unsigned need, unsigned item_size);
extern long page_size;
/* Allocator of whole pages; for use in slabs and other high-level allocators. */
void *alloc_page(pool *);
void free_page(pool *, void *);
#define PAGE_HEAD(x) ((void *) (((intptr_t) (x)) & ~(page_size-1)))
extern long page_size;
void *alloc_page(void);
void free_page(void *);
void resource_sys_init(void);
#ifdef HAVE_LIBDMALLOC
/*

433
lib/route.h Normal file
View File

@ -0,0 +1,433 @@
/*
* BIRD Internet Routing Daemon -- Routing data structures
*
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
* (c) 2022 Maria Matejka <mq@jmq.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef _BIRD_LIB_ROUTE_H_
#define _BIRD_LIB_ROUTE_H_
#include "lib/type.h"
struct network;
struct proto;
struct cli;
typedef struct rte {
struct rta *attrs; /* Attributes of this route */
const net_addr *net; /* Network this RTE belongs to */
struct rte_src *src; /* Route source that created the route */
struct rt_import_hook *sender; /* Import hook used to send the route to the routing table */
btime lastmod; /* Last modified (set by table) */
u32 id; /* Table specific route id */
byte flags; /* Table-specific flags */
byte pflags; /* Protocol-specific flags */
u8 generation; /* If this route import is based on other previously exported route,
this value should be 1 + MAX(generation of the parent routes).
Otherwise the route is independent and this value is zero. */
} rte;
#define REF_FILTERED 2 /* Route is rejected by import filter */
#define REF_STALE 4 /* Route is stale in a refresh cycle */
#define REF_DISCARD 8 /* Route is scheduled for discard */
#define REF_MODIFY 16 /* Route is scheduled for modify */
/* Route is valid for propagation (may depend on other flags in the future), accepts NULL */
static inline int rte_is_valid(rte *r) { return r && !(r->flags & REF_FILTERED); }
/* Route just has REF_FILTERED flag */
static inline int rte_is_filtered(rte *r) { return !!(r->flags & REF_FILTERED); }
struct rte_src {
struct rte_src *next; /* Hash chain */
struct proto *proto; /* Protocol the source is based on */
u32 private_id; /* Private ID, assigned by the protocol */
u32 global_id; /* Globally unique ID of the source */
unsigned uc; /* Use count */
};
struct rte_src *rt_find_source(struct proto *p, u32 id);
struct rte_src *rt_get_source(struct proto *p, u32 id);
static inline void rt_lock_source(struct rte_src *src) { src->uc++; }
static inline void rt_unlock_source(struct rte_src *src) { src->uc--; }
void rt_prune_sources(void);
/*
* Route Attributes
*
* Beware: All standard BGP attributes must be represented here instead
* of making them local to the route. This is needed to ensure proper
* construction of BGP route attribute lists.
*/
/* Nexthop structure */
struct nexthop {
ip_addr gw; /* Next hop */
struct iface *iface; /* Outgoing interface */
byte flags;
byte weight;
byte labels; /* Number of all labels */
u32 label[0];
};
/* For packing one into eattrs */
struct nexthop_adata {
struct adata ad;
/* There is either a set of nexthops or a special destination (RTD_*) */
union {
struct nexthop nh;
uint dest;
};
};
#define NEXTHOP_DEST_SIZE (OFFSETOF(struct nexthop_adata, dest) + sizeof(uint) - OFFSETOF(struct adata, data))
#define NEXTHOP_DEST_LITERAL(x) ((struct nexthop_adata) { \
.ad.length = NEXTHOP_DEST_SIZE, .dest = (x), })
#define RNF_ONLINK 0x1 /* Gateway is onlink regardless of IP ranges */
typedef struct rta {
struct rta *next, **pprev; /* Hash chain */
u32 uc; /* Use count */
u32 hash_key; /* Hash over important fields */
struct ea_list *eattrs; /* Extended Attribute chain */
u16 cached:1; /* Are attributes cached? */
} rta;
#define RTS_STATIC 1 /* Normal static route */
#define RTS_INHERIT 2 /* Route inherited from kernel */
#define RTS_DEVICE 3 /* Device route */
#define RTS_STATIC_DEVICE 4 /* Static device route */
#define RTS_REDIRECT 5 /* Learned via redirect */
#define RTS_RIP 6 /* RIP route */
#define RTS_OSPF 7 /* OSPF route */
#define RTS_OSPF_IA 8 /* OSPF inter-area route */
#define RTS_OSPF_EXT1 9 /* OSPF external route type 1 */
#define RTS_OSPF_EXT2 10 /* OSPF external route type 2 */
#define RTS_BGP 11 /* BGP route */
#define RTS_PIPE 12 /* Inter-table wormhole */
#define RTS_BABEL 13 /* Babel route */
#define RTS_RPKI 14 /* Route Origin Authorization */
#define RTS_PERF 15 /* Perf checker */
#define RTS_MAX 16
#define RTD_NONE 0 /* Undefined next hop */
#define RTD_UNICAST 1 /* A standard next hop */
#define RTD_BLACKHOLE 2 /* Silently drop packets */
#define RTD_UNREACHABLE 3 /* Reject as unreachable */
#define RTD_PROHIBIT 4 /* Administratively prohibited */
#define RTD_MAX 5
extern const char * rta_dest_names[RTD_MAX];
static inline const char *rta_dest_name(uint n)
{ return (n < RTD_MAX) ? rta_dest_names[n] : "???"; }
/*
* Extended Route Attributes
*/
typedef struct eattr {
word id; /* EA_CODE(PROTOCOL_..., protocol-dependent ID) */
byte flags; /* Protocol-dependent flags */
byte type; /* Attribute type */
byte rfu:5;
byte originated:1; /* The attribute has originated locally */
byte fresh:1; /* An uncached attribute (e.g. modified in export filter) */
byte undef:1; /* Explicitly undefined */
PADDING(unused, 3, 3);
union bval u;
} eattr;
#define EA_CODE_MASK 0xffff
#define EA_ALLOW_UNDEF 0x10000 /* ea_find: allow EAF_TYPE_UNDEF */
#define EA_BIT(n) ((n) << 24) /* Used in bitfield accessors */
#define EA_BIT_GET(ea) ((ea) >> 24)
typedef struct ea_list {
struct ea_list *next; /* In case we have an override list */
byte flags; /* Flags: EALF_... */
byte rfu;
word count; /* Number of attributes */
eattr attrs[0]; /* Attribute definitions themselves */
} ea_list;
#define EALF_SORTED 1 /* Attributes are sorted by code */
#define EALF_BISECT 2 /* Use interval bisection for searching */
#define EALF_CACHED 4 /* Attributes belonging to cached rta */
struct ea_class {
#define EA_CLASS_INSIDE \
const char *name; /* Name (both print and filter) */ \
struct symbol *sym; /* Symbol to export to configs */ \
uint id; /* Autoassigned attribute ID */ \
uint uc; /* Reference count */ \
btype type; /* Data type ID */ \
uint readonly:1; /* This attribute can't be changed by filters */ \
uint conf:1; /* Requested by config */ \
void (*format)(const eattr *ea, byte *buf, uint size); \
void (*stored)(const eattr *ea); /* When stored into global hash */ \
void (*freed)(const eattr *ea); /* When released from global hash */ \
EA_CLASS_INSIDE;
};
struct ea_class_ref {
resource r;
struct ea_class *class;
};
void ea_register_init(struct ea_class *);
struct ea_class_ref *ea_register_alloc(pool *, struct ea_class);
#define EA_REGISTER_ALL_HELPER(x) ea_register_init(x);
#define EA_REGISTER_ALL(...) MACRO_FOREACH(EA_REGISTER_ALL_HELPER, __VA_ARGS__)
struct ea_class *ea_class_find_by_id(uint id);
struct ea_class *ea_class_find_by_name(const char *name);
static inline struct ea_class *ea_class_self(struct ea_class *self) { return self; }
#define ea_class_find(_arg) _Generic((_arg), \
uint: ea_class_find_by_id, \
word: ea_class_find_by_id, \
char *: ea_class_find_by_name, \
const char *: ea_class_find_by_name, \
struct ea_class *: ea_class_self)(_arg)
struct ea_walk_state {
ea_list *eattrs; /* Ccurrent ea_list, initially set by caller */
eattr *ea; /* Current eattr, initially NULL */
u32 visited[4]; /* Bitfield, limiting max to 128 */
};
#define ea_find(_l, _arg) _Generic((_arg), uint: ea_find_by_id, struct ea_class *: ea_find_by_class, char *: ea_find_by_name)(_l, _arg)
eattr *ea_find_by_id(ea_list *, unsigned ea);
static inline eattr *ea_find_by_class(ea_list *l, const struct ea_class *def)
{ return ea_find_by_id(l, def->id); }
static inline eattr *ea_find_by_name(ea_list *l, const char *name)
{
const struct ea_class *def = ea_class_find_by_name(name);
return def ? ea_find_by_class(l, def) : NULL;
}
#define ea_get_int(_l, _ident, _def) ({ \
struct ea_class *cls = ea_class_find((_ident)); \
ASSERT_DIE(cls->type & EAF_EMBEDDED); \
const eattr *ea = ea_find((_l), cls->id); \
(ea ? ea->u.data : (_def)); \
})
#define ea_get_ip(_l, _ident, _def) ({ \
struct ea_class *cls = ea_class_find((_ident)); \
ASSERT_DIE(cls->type == T_IP); \
const eattr *ea = ea_find((_l), cls->id); \
(ea ? *((const ip_addr *) ea->u.ptr->data) : (_def)); \
})
eattr *ea_walk(struct ea_walk_state *s, uint id, uint max);
void ea_dump(ea_list *);
int ea_same(ea_list *x, ea_list *y); /* Test whether two ea_lists are identical */
uint ea_hash(ea_list *e); /* Calculate 16-bit hash value */
ea_list *ea_append(ea_list *to, ea_list *what);
void ea_format_bitfield(const struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max);
/* Normalize ea_list; allocates the result from tmp_linpool */
ea_list *ea_normalize(const ea_list *e);
uint ea_list_size(ea_list *);
void ea_list_copy(ea_list *dest, ea_list *src, uint size);
#define EA_LOCAL_LIST(N) struct { ea_list l; eattr a[N]; }
#define EA_LITERAL_EMBEDDED(_class, _flags, _val) ({ \
btype _type = (_class)->type; \
ASSERT_DIE(_type & EAF_EMBEDDED); \
EA_LITERAL_GENERIC((_class)->id, _type, _flags, .u.i = _val); \
})
#define EA_LITERAL_STORE_ADATA(_class, _flags, _buf, _len) ({ \
btype _type = (_class)->type; \
ASSERT_DIE(!(_type & EAF_EMBEDDED)); \
EA_LITERAL_GENERIC((_class)->id, _type, _flags, .u.ad = tmp_store_adata((_buf), (_len))); \
})
#define EA_LITERAL_DIRECT_ADATA(_class, _flags, _adata) ({ \
btype _type = (_class)->type; \
ASSERT_DIE(!(_type & EAF_EMBEDDED)); \
EA_LITERAL_GENERIC((_class)->id, _type, _flags, .u.ad = _adata); \
})
#define EA_LITERAL_GENERIC(_id, _type, _flags, ...) \
((eattr) { .id = _id, .type = _type, .flags = _flags, __VA_ARGS__ })
static inline eattr *
ea_set_attr(ea_list **to, eattr a)
{
EA_LOCAL_LIST(1) *ea = tmp_alloc(sizeof(*ea));
*ea = (typeof(*ea)) {
.l.flags = EALF_SORTED,
.l.count = 1,
.l.next = *to,
.a[0] = a,
};
*to = &ea->l;
return &ea->a[0];
}
static inline void
ea_unset_attr(ea_list **to, _Bool local, const struct ea_class *def)
{
ea_set_attr(to, EA_LITERAL_GENERIC(def->id, 0, 0,
.fresh = local, .originated = local, .undef = 1));
}
static inline void
ea_set_attr_u32(ea_list **to, const struct ea_class *def, uint flags, u64 data)
{ ea_set_attr(to, EA_LITERAL_EMBEDDED(def, flags, data)); }
static inline void
ea_set_attr_data(ea_list **to, const struct ea_class *def, uint flags, const void *data, uint len)
{ ea_set_attr(to, EA_LITERAL_STORE_ADATA(def, flags, data, len)); }
static inline void
ea_copy_attr(ea_list **to, ea_list *from, const struct ea_class *def)
{
eattr *e = ea_find_by_class(from, def);
if (e)
if (e->type & EAF_EMBEDDED)
ea_set_attr_u32(to, def, e->flags, e->u.data);
else
ea_set_attr_data(to, def, e->flags, e->u.ptr->data, e->u.ptr->length);
else
ea_unset_attr(to, 0, def);
}
/*
* Common route attributes
*/
/* Preference: first-order comparison */
extern struct ea_class ea_gen_preference;
static inline u32 rt_get_preference(rte *rt)
{ return ea_get_int(rt->attrs->eattrs, &ea_gen_preference, 0); }
/* IGP metric: second-order comparison */
extern struct ea_class ea_gen_igp_metric;
u32 rt_get_igp_metric(const rte *rt);
#define IGP_METRIC_UNKNOWN 0x80000000 /* Default igp_metric used when no other
protocol-specific metric is availabe */
/* From: Advertising router */
extern struct ea_class ea_gen_from;
/* Source: An old method to devise the route source protocol and kind.
* To be superseded in a near future by something more informative. */
extern struct ea_class ea_gen_source;
static inline u32 rt_get_source_attr(const rte *rt)
{ return ea_get_int(rt->attrs->eattrs, &ea_gen_source, 0); }
/* Flowspec validation result */
enum flowspec_valid {
FLOWSPEC_UNKNOWN = 0,
FLOWSPEC_VALID = 1,
FLOWSPEC_INVALID = 2,
FLOWSPEC__MAX,
};
extern const char * flowspec_valid_names[FLOWSPEC__MAX];
static inline const char *flowspec_valid_name(enum flowspec_valid v)
{ return (v < FLOWSPEC__MAX) ? flowspec_valid_names[v] : "???"; }
extern struct ea_class ea_gen_flowspec_valid;
static inline enum flowspec_valid rt_get_flowspec_valid(rte *rt)
{ return ea_get_int(rt->attrs->eattrs, &ea_gen_flowspec_valid, FLOWSPEC_UNKNOWN); }
/* Next hop: For now, stored as adata */
extern struct ea_class ea_gen_nexthop;
static inline void ea_set_dest(struct ea_list **to, uint flags, uint dest)
{
struct nexthop_adata nhad = NEXTHOP_DEST_LITERAL(dest);
ea_set_attr_data(to, &ea_gen_nexthop, flags, &nhad.ad.data, nhad.ad.length);
}
/* Next hop structures */
#define NEXTHOP_ALIGNMENT (_Alignof(struct nexthop))
#define NEXTHOP_MAX_SIZE (sizeof(struct nexthop) + sizeof(u32)*MPLS_MAX_LABEL_STACK)
#define NEXTHOP_SIZE(_nh) NEXTHOP_SIZE_CNT(((_nh)->labels))
#define NEXTHOP_SIZE_CNT(cnt) BIRD_ALIGN((sizeof(struct nexthop) + sizeof(u32) * (cnt)), NEXTHOP_ALIGNMENT)
#define nexthop_size(nh) NEXTHOP_SIZE((nh))
#define NEXTHOP_NEXT(_nh) ((void *) (_nh) + NEXTHOP_SIZE(_nh))
#define NEXTHOP_END(_nhad) ((_nhad)->ad.data + (_nhad)->ad.length)
#define NEXTHOP_VALID(_nh, _nhad) ((void *) (_nh) < (void *) NEXTHOP_END(_nhad))
#define NEXTHOP_ONE(_nhad) (NEXTHOP_NEXT(&(_nhad)->nh) == NEXTHOP_END(_nhad))
#define NEXTHOP_WALK(_iter, _nhad) for ( \
struct nexthop *_iter = &(_nhad)->nh; \
(void *) _iter < (void *) NEXTHOP_END(_nhad); \
_iter = NEXTHOP_NEXT(_iter))
static inline int nexthop_same(struct nexthop_adata *x, struct nexthop_adata *y)
{ return adata_same(&x->ad, &y->ad); }
struct nexthop_adata *nexthop_merge(struct nexthop_adata *x, struct nexthop_adata *y, int max, linpool *lp);
struct nexthop_adata *nexthop_sort(struct nexthop_adata *x, linpool *lp);
int nexthop_is_sorted(struct nexthop_adata *x);
#define NEXTHOP_IS_REACHABLE(nhad) ((nhad)->ad.length > NEXTHOP_DEST_SIZE)
/* Route has regular, reachable nexthop (i.e. not RTD_UNREACHABLE and like) */
static inline int rte_is_reachable(rte *r)
{
eattr *nhea = ea_find(r->attrs->eattrs, &ea_gen_nexthop);
if (!nhea)
return 0;
struct nexthop_adata *nhad = (void *) nhea->u.ptr;
return NEXTHOP_IS_REACHABLE(nhad);
}
static inline int nhea_dest(eattr *nhea)
{
if (!nhea)
return RTD_NONE;
struct nexthop_adata *nhad = nhea ? (struct nexthop_adata *) nhea->u.ptr : NULL;
if (NEXTHOP_IS_REACHABLE(nhad))
return RTD_UNICAST;
else
return nhad->dest;
}
static inline int rte_dest(const rte *r)
{
return nhea_dest(ea_find(r->attrs->eattrs, &ea_gen_nexthop));
}
void rta_init(void);
#define rta_size(...) (sizeof(rta))
#define RTA_MAX_SIZE (sizeof(rta))
rta *rta_lookup(rta *); /* Get rta equivalent to this one, uc++ */
static inline int rta_is_cached(rta *r) { return r->cached; }
static inline rta *rta_clone(rta *r) { r->uc++; return r; }
void rta__free(rta *r);
static inline void rta_free(rta *r) { if (r && !--r->uc) rta__free(r); }
rta *rta_do_cow(rta *o, linpool *lp);
static inline rta * rta_cow(rta *r, linpool *lp) { return rta_is_cached(r) ? rta_do_cow(r, lp) : r; }
void rta_dump(rta *);
void rta_dump_all(void);
void rta_show(struct cli *, rta *);
#endif

View File

@ -32,6 +32,7 @@
#include "nest/bird.h"
#include "lib/resource.h"
#include "lib/string.h"
#include "lib/tlists.h"
#undef FAKE_SLAB /* Turn on if you want to debug memory allocations */
@ -42,7 +43,7 @@
static void slab_free(resource *r);
static void slab_dump(resource *r);
static resource *slab_lookup(resource *r, unsigned long addr);
static size_t slab_memsize(resource *r);
static struct resmem slab_memsize(resource *r);
#ifdef FAKE_SLAB
@ -98,7 +99,7 @@ sl_allocz(slab *s)
}
void
sl_free(slab *s, void *oo)
sl_free(void *oo)
{
struct sl_obj *o = SKIP_BACK(struct sl_obj, data, oo);
@ -128,7 +129,7 @@ slab_dump(resource *r)
debug("(%d objects per %d bytes)\n", cnt, s->size);
}
static size_t
static struct resmem
slab_memsize(resource *r)
{
slab *s = (slab *) r;
@ -138,7 +139,10 @@ slab_memsize(resource *r)
WALK_LIST(o, s->objs)
cnt++;
return ALLOC_OVERHEAD + sizeof(struct slab) + cnt * (ALLOC_OVERHEAD + s->size);
return (struct resmem) {
.effective = cnt * s->size,
.overhead = ALLOC_OVERHEAD + sizeof(struct slab) + cnt * ALLOC_OVERHEAD,
};
}
@ -150,12 +154,38 @@ slab_memsize(resource *r)
#define MAX_EMPTY_HEADS 1
enum sl_head_state {
slh_empty = 2,
slh_partial = 0,
slh_full = 1,
} PACKED;
struct sl_head {
struct slab *slab;
TLIST_NODE(sl_head, struct sl_head) n;
u16 num_full;
enum sl_head_state state;
u32 used_bits[0];
};
struct sl_alignment { /* Magic structure for testing of alignment */
byte data;
int x[0];
};
#define TLIST_PREFIX sl_head
#define TLIST_TYPE struct sl_head
#define TLIST_ITEM n
#define TLIST_WANT_WALK
#define TLIST_WANT_ADD_HEAD
#include "lib/tlists.h"
struct slab {
resource r;
pool *p;
uint obj_size, head_size, head_bitfield_len;
uint objs_per_slab, num_empty_heads, data_size;
list empty_heads, partial_heads, full_heads;
struct sl_head_list empty_heads, partial_heads, full_heads;
};
static struct resclass sl_class = {
@ -167,18 +197,15 @@ static struct resclass sl_class = {
slab_memsize
};
struct sl_head {
node n;
u32 num_full;
u32 used_bits[0];
};
#define SL_GET_HEAD(x) ((struct sl_head *) (((uintptr_t) (x)) & ~(page_size-1)))
struct sl_alignment { /* Magic structure for testing of alignment */
byte data;
int x[0];
};
#define SL_HEAD_CHANGE_STATE(_s, _h, _from, _to) ({ \
ASSERT_DIE(_h->state == slh_##_from); \
sl_head_rem_node(&_s->_from##_heads, _h); \
sl_head_add_head(&_s->_to##_heads, _h); \
_h->state = slh_##_to; \
})
#define SL_GET_HEAD(x) ((struct sl_head *) PAGE_HEAD(x))
/**
* sl_new - create a new Slab
@ -192,10 +219,9 @@ slab *
sl_new(pool *p, uint size)
{
slab *s = ralloc(p, &sl_class);
s->p = p;
uint align = sizeof(struct sl_alignment);
if (align < sizeof(int))
align = sizeof(int);
if (align < sizeof(void *))
align = sizeof(void *);
s->data_size = size;
size = (size + align - 1) / align * align;
s->obj_size = size;
@ -216,9 +242,6 @@ sl_new(pool *p, uint size)
bug("Slab: object too large");
s->num_empty_heads = 0;
init_list(&s->empty_heads);
init_list(&s->partial_heads);
init_list(&s->full_heads);
return s;
}
@ -235,8 +258,7 @@ sl_alloc(slab *s)
struct sl_head *h;
redo:
h = HEAD(s->partial_heads);
if (!h->n.next)
if (!(h = s->partial_heads.first))
goto no_partial;
okay:
for (uint i=0; i<s->head_bitfield_len; i++)
@ -256,26 +278,27 @@ okay:
return out;
}
rem_node(&h->n);
add_tail(&s->full_heads, &h->n);
SL_HEAD_CHANGE_STATE(s, h, partial, full);
goto redo;
no_partial:
h = HEAD(s->empty_heads);
if (h->n.next)
if (h = s->empty_heads.first)
{
rem_node(&h->n);
add_head(&s->partial_heads, &h->n);
SL_HEAD_CHANGE_STATE(s, h, empty, partial);
s->num_empty_heads--;
goto okay;
}
h = alloc_page(s->p);
h = alloc_page();
ASSERT_DIE(SL_GET_HEAD(h) == h);
#ifdef POISON
memset(h, 0xba, page_size);
#endif
ASSERT_DIE(SL_GET_HEAD(h) == h);
memset(h, 0, s->head_size);
add_head(&s->partial_heads, &h->n);
h->slab = s;
sl_head_add_head(&s->partial_heads, h);
goto okay;
}
@ -304,9 +327,10 @@ sl_allocz(slab *s)
* and returns it back to the Slab @s.
*/
void
sl_free(slab *s, void *oo)
sl_free(void *oo)
{
struct sl_head *h = SL_GET_HEAD(oo);
struct slab *s = h->slab;
#ifdef POISON
memset(oo, 0xdb, s->data_size);
@ -319,24 +343,22 @@ sl_free(slab *s, void *oo)
h->used_bits[pos / 32] &= ~(1 << (pos % 32));
if (h->num_full-- == s->objs_per_slab)
{
rem_node(&h->n);
add_head(&s->partial_heads, &h->n);
}
if ((h->num_full-- == s->objs_per_slab) && (h->state == slh_full))
SL_HEAD_CHANGE_STATE(s, h, full, partial);
else if (!h->num_full)
{
rem_node(&h->n);
sl_head_rem_node(&s->partial_heads, h);
if (s->num_empty_heads >= MAX_EMPTY_HEADS)
{
#ifdef POISON
memset(h, 0xde, page_size);
#endif
free_page(s->p, h);
free_page(h);
}
else
{
add_head(&s->empty_heads, &h->n);
sl_head_add_head(&s->empty_heads, h);
h->state = slh_empty;
s->num_empty_heads++;
}
}
@ -346,14 +368,13 @@ static void
slab_free(resource *r)
{
slab *s = (slab *) r;
struct sl_head *h, *g;
WALK_LIST_DELSAFE(h, g, s->empty_heads)
free_page(s->p, h);
WALK_LIST_DELSAFE(h, g, s->partial_heads)
free_page(s->p, h);
WALK_LIST_DELSAFE(h, g, s->full_heads)
free_page(s->p, h);
WALK_TLIST_DELSAFE(sl_head, h, &s->empty_heads)
free_page(h);
WALK_TLIST_DELSAFE(sl_head, h, &s->partial_heads)
free_page(h);
WALK_TLIST_DELSAFE(sl_head, h, &s->full_heads)
free_page(h);
}
static void
@ -361,45 +382,53 @@ slab_dump(resource *r)
{
slab *s = (slab *) r;
int ec=0, pc=0, fc=0;
struct sl_head *h;
WALK_LIST(h, s->empty_heads)
WALK_TLIST(sl_head, h, &s->empty_heads)
ec++;
WALK_LIST(h, s->partial_heads)
WALK_TLIST(sl_head, h, &s->partial_heads)
pc++;
WALK_LIST(h, s->full_heads)
WALK_TLIST(sl_head, h, &s->full_heads)
fc++;
debug("(%de+%dp+%df blocks per %d objs per %d bytes)\n", ec, pc, fc, s->objs_per_slab, s->obj_size);
}
static size_t
static struct resmem
slab_memsize(resource *r)
{
slab *s = (slab *) r;
size_t heads = 0;
struct sl_head *h;
WALK_LIST(h, s->empty_heads)
heads++;
WALK_LIST(h, s->partial_heads)
heads++;
WALK_LIST(h, s->full_heads)
WALK_TLIST(sl_head, h, &s->full_heads)
heads++;
// return ALLOC_OVERHEAD + sizeof(struct slab) + heads * (ALLOC_OVERHEAD + page_size);
return ALLOC_OVERHEAD + sizeof(struct slab); /* The page sizes are accounted for in the pool */
size_t items = heads * s->objs_per_slab;
WALK_TLIST(sl_head, h, &s->partial_heads)
{
heads++;
items += h->num_full;
}
WALK_TLIST(sl_head, h, &s->empty_heads)
heads++;
size_t eff = items * s->data_size;
return (struct resmem) {
.effective = eff,
.overhead = ALLOC_OVERHEAD + sizeof(struct slab) + heads * page_size - eff,
};
}
static resource *
slab_lookup(resource *r, unsigned long a)
{
slab *s = (slab *) r;
struct sl_head *h;
WALK_LIST(h, s->partial_heads)
WALK_TLIST(sl_head, h, &s->partial_heads)
if ((unsigned long) h < a && (unsigned long) h + page_size < a)
return r;
WALK_LIST(h, s->full_heads)
WALK_TLIST(sl_head, h, &s->full_heads)
if ((unsigned long) h < a && (unsigned long) h + page_size < a)
return r;
return NULL;

171
lib/slab_test.c Normal file
View File

@ -0,0 +1,171 @@
/*
* BIRD Library -- Slab Alloc / Dealloc Tests
*
* (c) 2022 Maria Matejka <mq@jmq.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "test/birdtest.h"
#include "lib/resource.h"
#include "lib/bitops.h"
static const int sizes[] = {
8, 12, 18, 27, 41, 75, 131, 269,
};
#define TEST_SIZE 1024 * 128
#define ITEMS(sz) TEST_SIZE / ( (sz) >> u32_log2((sz))/2 )
struct test_request {
int size;
enum strategy {
TEST_NONE,
TEST_FORWARDS,
TEST_BACKWARDS,
TEST_RANDOM,
TEST_MIXED,
TEST__MAX,
} strategy;
};
const char * const strategy_name[TEST__MAX] = {
[TEST_FORWARDS] = "forwards",
[TEST_BACKWARDS] = "backwards",
[TEST_RANDOM] = "random",
[TEST_MIXED] = "mixed",
};
static inline byte *test_alloc(slab *s, int sz, struct resmem *sliz)
{
byte *out = sl_alloc(s);
for (int p=0; p < sz; p++)
out[p] = p & 0xff;
struct resmem ns = rmemsize((resource *) s);
bt_assert(sliz->effective + sz == ns.effective);
bt_assert((sliz->overhead - sz - ns.overhead) % page_size == 0);
*sliz = ns;
return out;
}
static inline void test_free(slab *s, byte *block, int sz, struct resmem *sliz)
{
for (int p=0; p < sz; p++)
{
bt_assert(block[p] == (p & 0xff));
block[p]++;
}
sl_free(block);
struct resmem ns = rmemsize((resource *) s);
bt_assert(sliz->effective - sz == ns.effective);
bt_assert((sliz->overhead + sz - ns.overhead) % page_size == 0);
*sliz = ns;
}
static inline struct resmem get_memsize(slab *s)
{
struct resmem sz = rmemsize((resource *) s);
bt_assert(sz.effective == 0);
return sz;
}
static int
t_slab(const void *data)
{
const struct test_request *tr = data;
int sz = tr->size;
slab *s = sl_new(&root_pool, sz);
struct resmem sliz = get_memsize(s);
int n = ITEMS(sz);
byte **block = mb_alloc(&root_pool, n * sizeof(*block));
switch (tr->strategy) {
case TEST_FORWARDS:
for (int i = 0; i < n; i++)
block[i] = test_alloc(s, sz, &sliz);
for (int i = 0; i < n; i++)
test_free(s, block[i], sz, &sliz);
break;
case TEST_BACKWARDS:
for (int i = 0; i < n; i++)
block[i] = test_alloc(s, sz, &sliz);
for (int i = n - 1; i >= 0; i--)
test_free(s, block[i], sz, &sliz);
break;
case TEST_RANDOM:
for (int i = 0; i < n; i++)
block[i] = test_alloc(s, sz, &sliz);
for (int i = 0; i < n; i++)
{
int pos = bt_random() % (n - i);
test_free(s, block[pos], sz, &sliz);
if (pos != n - i - 1)
block[pos] = block[n - i - 1];
}
break;
case TEST_MIXED:
{
int cur = 0;
int pending = n;
while (cur + pending > 0) {
int action = bt_random() % (cur + pending);
if (action < cur) {
test_free(s, block[action], sz, &sliz);
if (action != --cur)
block[action] = block[cur];
} else {
block[cur++] = test_alloc(s, sz, &sliz);
pending--;
}
}
break;
}
default: bug("This shouldn't happen");
}
mb_free(block);
return 1;
}
int main(int argc, char *argv[])
{
bt_init(argc, argv);
struct test_request tr;
for (uint i = 0; i < sizeof(sizes) / sizeof(*sizes); i++)
for (uint strategy = TEST_FORWARDS; strategy < TEST__MAX; strategy++)
{
tr = (struct test_request) {
.size = sizes[i],
.strategy = strategy,
};
bt_test_suite_arg(t_slab, &tr, "Slab allocator test, size=%d, strategy=%s",
tr.size, strategy_name[strategy]);
}
return bt_exit_value();
}

View File

@ -123,6 +123,7 @@ extern int sk_priority_control; /* Suggested priority for control traffic, shou
#define SKF_TTL_RX 0x08 /* Report TTL / Hop Limit for RX packets */
#define SKF_BIND 0x10 /* Bind datagram socket to given source address */
#define SKF_HIGH_PORT 0x20 /* Choose port from high range if possible */
#define SKF_FREEBIND 0x40 /* Allow socket to bind to a nonlocal address */
#define SKF_THREAD 0x100 /* Socked used in thread, Do not add to main loop */
#define SKF_TRUNCATED 0x200 /* Received packet was truncated, set by IO layer */

View File

@ -20,6 +20,11 @@ int bvsprintf(char *str, const char *fmt, va_list args);
int bsnprintf(char *str, int size, const char *fmt, ...);
int bvsnprintf(char *str, int size, const char *fmt, va_list args);
char *mb_sprintf(pool *p, const char *fmt, ...);
char *mb_vsprintf(pool *p, const char *fmt, va_list args);
char *lp_sprintf(linpool *p, const char *fmt, ...);
char *lp_vsprintf(linpool *p, const char *fmt, va_list args);
int buffer_vprint(buffer *buf, const char *fmt, va_list args);
int buffer_print(buffer *buf, const char *fmt, ...);
void buffer_puts(buffer *buf, const char *str);

View File

@ -233,6 +233,7 @@ timers_fire(struct timeloop *loop)
io_log_event(t->hook, t->data);
t->hook(t);
tmp_flush();
}
}

172
lib/tlists.h Normal file
View File

@ -0,0 +1,172 @@
/*
* BIRD Library -- Typed Linked Lists
*
* (c) 2022 Maria Matejka <mq@jmq.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*
*
* This implementation of linked lists forces its members to be
* typed. On the other hand, it needs to be implemented as ugly macros to
* keep the needed genericity.
*
* Usage:
* 1. Include this file
* 2. Define the node structure
* 3. For every list type you need to define:
* A. #define TLIST_PREFIX and other macros
* B. Include this file once again
*
* Macros to define:
* TLIST_PREFIX: prefix to prepend to everything generated
* TLIST_TYPE: the actual node type
* TLIST_ITEM: where the tlist structure is
* TLIST_WANT_WALK: if defined, generates a helper functions for list walking macros
* TLIST_WANT_ADD_HEAD: if defined, TLIST_PREFIX_add_head() is generated to
* add an item to the beginning of the list
* TLIST_WANT_ADD_TAIL: if defined, TLIST_PREFIX_add_tail() is generated to
* add an item to the end of the list
*
* TLIST_PREFIX_rem_node() is generated always.
*
* All these macros are #undef-ed by including this file.
*
* Example:
*
* #include "lib/tlists.h"
*
* struct foo {
* ...
* TLIST_NODE(bar, struct foo) baz;
* ...
* };
*
* #define TLIST_PREFIX bar
* #define TLIST_TYPE struct foo
* #define TLIST_ITEM baz
*
* #define TLIST_WANT_WALK
* #define TLIST_WANT_ADD_HEAD
*
* #include "lib/tlists.h"
*
* ...
* (end of example)
*
*/
#ifdef _BIRD_LIB_TLISTS_H_
# ifdef TLIST_PREFIX
/* Check for mandatory arguments */
#ifndef TLIST_TYPE
#error "TLIST_TYPE must be defined"
#endif
#ifndef TLIST_ITEM
#error "TLIST_ITEM must be defined"
#endif
#ifndef TLIST_PREFIX
#error "TLIST_PREFIX must be defined"
#endif
#define TLIST_NAME(x) MACRO_CONCAT_AFTER(TLIST_PREFIX,_##x)
#ifndef TLIST_LIST_STRUCT
#define TLIST_LIST_STRUCT TLIST_NAME(list)
#endif
typedef struct TLIST_LIST_STRUCT {
TLIST_TYPE *first;
TLIST_TYPE *last;
} TLIST_LIST_STRUCT;
#ifdef TLIST_WANT_WALK
static inline struct TLIST_NAME(node) * TLIST_NAME(node_get)(TLIST_TYPE *node)
{ return &(node->TLIST_ITEM); }
#endif
#ifdef TLIST_WANT_ADD_HEAD
static inline void TLIST_NAME(add_head)(TLIST_LIST_STRUCT *list, TLIST_TYPE *node)
{
ASSERT_DIE(!node->TLIST_ITEM.prev && !node->TLIST_ITEM.next);
if (node->TLIST_ITEM.next = list->first)
list->first->TLIST_ITEM.prev = node;
else
list->last = node;
list->first = node;
}
#endif
#ifdef TLIST_WANT_ADD_TAIL
static inline void TLIST_NAME(add_tail)(TLIST_LIST_STRUCT *list, TLIST_TYPE *node)
{
ASSERT_DIE(!node->TLIST_ITEM.prev && !node->TLIST_ITEM.next);
if (node->TLIST_ITEM.prev = list->last)
list->last->TLIST_ITEM.next = node;
else
list->first = node;
list->last = node;
}
#endif
static inline void TLIST_NAME(rem_node)(TLIST_LIST_STRUCT *list, TLIST_TYPE *node)
{
if (node->TLIST_ITEM.prev)
node->TLIST_ITEM.prev->TLIST_ITEM.next = node->TLIST_ITEM.next;
else
{
ASSERT_DIE(list->first == node);
list->first = node->TLIST_ITEM.next;
}
if (node->TLIST_ITEM.next)
node->TLIST_ITEM.next->TLIST_ITEM.prev = node->TLIST_ITEM.prev;
else
{
ASSERT_DIE(list->last == node);
list->last = node->TLIST_ITEM.prev;
}
node->TLIST_ITEM.next = node->TLIST_ITEM.prev = NULL;
}
#undef TLIST_PREFIX
#undef TLIST_NAME
#undef TLIST_LIST_STRUCT
#undef TLIST_TYPE
#undef TLIST_ITEM
#undef TLIST_WANT_ADD_HEAD
#undef TLIST_WANT_ADD_TAIL
# endif
#else
#define _BIRD_LIB_TLISTS_H_
#include "lib/macro.h"
#if defined(TLIST_NAME) || defined(TLIST_PREFIX)
#error "You should first include lib/tlists.h without requesting a TLIST"
#endif
#define TLIST_NODE(_name, _type) struct _name##_node { _type *next; _type *prev; }
#define TLIST_LIST(_name) struct _name##_list
/* Use ->first and ->last to access HEAD and TAIL */
#define THEAD(_name, _list) (_list)->first
#define TTAIL(_name, _list) (_list)->last
/* Walkaround macros: simple and resilient to node removal */
#define WALK_TLIST(_name, _node, _list) \
for (typeof((_list)->first) _node = (_list)->first; \
_node; _node = _name##_node_get((_node))->next)
#define WALK_TLIST_DELSAFE(_name, _node, _list) \
for (typeof((_list)->first) _node = (_list)->first, \
_helper = _node ? _name##_node_get((_list)->first)->next : NULL; \
_node; \
(_node = _helper) ? (_helper = _name##_node_get(_helper)->next) : 0)
/* Empty check */
#define EMPTY_TLIST(_name, _list) (!(_list)->first)
#endif

112
lib/type.h Normal file
View File

@ -0,0 +1,112 @@
/*
* BIRD Internet Routing Daemon -- Internal Data Types
*
* (c) 2022 Maria Matejka <mq@jmq.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef _BIRD_TYPE_H_
#define _BIRD_TYPE_H_
#include "lib/birdlib.h"
#include "lib/attrs.h"
union bval {
#define BVAL_ITEMS \
struct { \
u32 data; /* Integer type inherited from eattrs */ \
PADDING(data, 0, 4); /* Must be padded on 64-bits */ \
}; \
struct { \
u32 i; /* Integer type inherited from filters */ \
PADDING(i, 0, 4); /* Must be padded on 64-bits */ \
}; \
const struct adata *ptr; /* Generic attribute data inherited from eattrs */ \
const struct adata *ad; /* Generic attribute data inherited from filters */ \
BVAL_ITEMS;
};
union bval_long {
union bval bval; /* For direct assignments */
BVAL_ITEMS; /* For item-wise access */
u64 ec;
lcomm lc;
ip_addr ip;
const net_addr *net;
const char *s;
const struct f_tree *t;
const struct f_trie *ti;
const struct f_path_mask *path_mask;
struct f_path_mask_item pmi;
};
/* Internal types */
enum btype {
/* Nothing. Simply nothing. */
T_VOID = 0,
/* 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 */
/* Types shared with eattrs */
T_INT = 0x01, /* 32-bit unsigned integer number */
T_IP = 0x04, /* IP address */
T_QUAD = 0x05, /* Router ID (IPv4 address) */
T_PATH = 0x06, /* BGP AS path (encoding per RFC 1771:4.3) */
T_CLIST = 0x0a, /* Set of u32's (e.g., a community list) */
T_ECLIST = 0x0e, /* Set of pairs of u32's - ext. community list */
T_LCLIST = 0x08, /* Set of triplets of u32's - large community list */
T_ENUM_BGP_ORIGIN = 0x11, /* BGP Origin enum */
T_ENUM_RA_PREFERENCE = 0x13, /* RA Preference enum */
T_ENUM_FLOWSPEC_VALID = 0x15, /* Flowspec validation result */
#define EAF_TYPE__MAX 0x1f
#define EAF_EMBEDDED 0x01 /* Data stored in eattr.u.data (part of type spec) */
/* Otherwise, attribute data is adata */
/* Other user visible types which fit in int */
T_BOOL = 0xa0,
T_PAIR = 0xa4, /* Notice that pair is stored as integer: first << 16 | second */
/* Put enumerational types in 0x20..0x3f range */
T_ENUM_LO = 0x10,
T_ENUM_HI = 0x3f,
T_ENUM_RTS = 0x31,
T_ENUM_SCOPE = 0x33,
T_ENUM_RTD = 0x37,
T_ENUM_ROA = 0x39,
T_ENUM_NETTYPE = 0x3b,
T_ENUM_AF = 0x3d,
/* new enums go here */
#define T_ENUM T_ENUM_LO ... T_ENUM_HI
/* Bigger ones */
T_NET = 0xb0,
T_STRING = 0xb4,
T_PATH_MASK = 0xb8, /* mask for BGP path */
T_EC = 0xbc, /* Extended community value, u64 */
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_SET = 0x80,
T_PREFIX_SET = 0x84,
} PACKED;
typedef enum btype btype;
STATIC_ASSERT(sizeof(btype) == sizeof(byte));
#endif

79
lib/type_test.c Normal file
View File

@ -0,0 +1,79 @@
/*
* BIRD Library -- Data Type Alignment Tests
*
* (c) 2022 Maria Matejka <mq@jmq.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#include "test/birdtest.h"
#include "lib/type.h"
#include "lib/route.h"
#define CHECK_ONE(val) \
for (uint i=0; i<sizeof(val); i++) \
bt_assert(((const u8 *) &val)[i] == (u8) ~0);
#define SET_PADDING(val, name) \
for (uint i=0; i<sizeof(val.PADDING_NAME(name)); i++) \
val.PADDING_NAME(name)[i] = ~0;
static int
t_bval(void)
{
union bval v;
memset(&v, 0, sizeof(v));
v.data = ~0;
SET_PADDING(v, data);
CHECK_ONE(v);
memset(&v, 0, sizeof(v));
v.i = ~0;
SET_PADDING(v, i);
CHECK_ONE(v);
memset(&v, 0, sizeof(v));
v.ptr = (void *) ~0;
CHECK_ONE(v);
memset(&v, 0, sizeof(v));
v.ad = (void *) ~0;
CHECK_ONE(v);
return 1;
}
static int
t_eattr(void)
{
struct eattr e;
memset(&e, 0, sizeof(e));
e.id = ~0;
e.flags = ~0;
e.type = ~0;
e.rfu = ~0;
e.originated = ~0;
e.fresh = ~0;
e.undef = ~0;
memset(&e.u, ~0, sizeof(e.u)); /* Assumes t_bval passed */
SET_PADDING(e, unused);
CHECK_ONE(e);
return 1;
}
int main(int argc, char *argv[])
{
bt_init(argc, argv);
bt_test_suite(t_bval, "Structure alignment test: bval");
bt_test_suite(t_eattr, "Structure alignment test: eattr");
return bt_exit_value();
}

View File

@ -1,6 +1,6 @@
Summary: BIRD Internet Routing Daemon
Name: bird
Version: 2.0.8
Version: 2.0.9
Release: 1
Copyright: GPL
Group: Networking/Daemons

View File

@ -1,9 +1,10 @@
FROM ubuntu:20.10
FROM ubuntu:21.10
ENV DEBIAN_FRONTEND=noninteractive
RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
RUN apt-get -y update
RUN apt-get -y upgrade
RUN apt-get -y install \
RUN apt-get -y --no-install-recommends install \
tzdata \
build-essential \
flex \
bison \

View File

@ -1,8 +1,13 @@
src := a-path.c a-set.c cli.c cmds.c iface.c locks.c neighbor.c password.c proto.c rt-attr.c rt-dev.c rt-fib.c rt-show.c rt-table.c
src := cli.c cmds.c iface.c locks.c neighbor.c password.c proto.c proto-build.c rt-attr.c rt-dev.c rt-fib.c rt-show.c rt-table.c
obj := $(src-o-files)
$(all-daemon)
$(cf-local)
$(call proto-build,dev_build)
tests_src := a-set_test.c a-path_test.c
$(proto-build-c): $(lastword $(MAKEFILE_LIST))
$(E)echo GEN $@
$(Q)echo "#include \"lib/birdlib.h\"\n$(patsubst %,void %(void);\n,$(PROTO_BUILD)) void protos_build_gen(void) { $(patsubst %, %();\n,$(PROTO_BUILD))}" > $@
tests_src :=
tests_targets := $(tests_targets) $(tests-target-files)
tests_objs := $(tests_objs) $(src-o-files)

View File

@ -9,7 +9,6 @@
#ifndef _BIRD_BIRD_H_
#define _BIRD_BIRD_H_
#include "sysdep/config.h"
#include "lib/birdlib.h"
#include "lib/ip.h"
#include "lib/net.h"

View File

@ -8,7 +8,7 @@
#include "nest/bird.h"
#include "nest/protocol.h"
#include "nest/route.h"
#include "nest/rt.h"
#include "nest/cli.h"
#include "conf/conf.h"
#include "nest/cmds.h"
@ -51,47 +51,80 @@ cmd_show_symbols(struct sym_show_data *sd)
cli_msg(1010, "%-8s\t%s", sd->sym->name, cf_symbol_class_name(sd->sym));
else
{
HASH_WALK(config->sym_hash, next, sym)
{
if (!sym->scope->active)
continue;
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;
if (sd->type && (sym->class != sd->type))
continue;
cli_msg(-1010, "%-8s\t%s", sym->name, cf_symbol_class_name(sym));
}
HASH_WALK_END;
cli_msg(-1010, "%-8s\t%s", sym->name, cf_symbol_class_name(sym));
}
HASH_WALK_END;
cli_msg(0, "");
}
}
static void
print_size(char *dsc, size_t val)
#define SIZE_SUFFIX " kMGT"
#define SIZE_FORMAT "% 4u.%1u % 1cB"
#define SIZE_ARGS(a) (a).val, (a).decimal, SIZE_SUFFIX[(a).magnitude]
struct size_args {
u64 val:48;
u64 decimal:8;
u64 magnitude:8;
};
static struct size_args
get_size_args(u64 val)
{
char *px = " kMG";
int i = 0;
while ((val >= 10000) && (i < 3))
#define VALDEC 10 /* One decimal place */
val *= VALDEC;
uint i = 0;
while ((val >= 10000 * VALDEC) && (i < 4))
{
val = (val + 512) / 1024;
i++;
}
cli_msg(-1018, "%-17s %4u %cB", dsc, (unsigned) val, px[i]);
return (struct size_args) {
.val = (val / VALDEC),
.decimal = (val % VALDEC),
.magnitude = i,
};
}
static void
print_size(char *dsc, struct resmem vals)
{
struct size_args effective = get_size_args(vals.effective);
struct size_args overhead = get_size_args(vals.overhead);
cli_msg(-1018, "%-17s " SIZE_FORMAT " " SIZE_FORMAT, dsc, SIZE_ARGS(effective), SIZE_ARGS(overhead));
}
extern pool *rt_table_pool;
extern pool *rta_pool;
extern uint *pages_kept;
void
cmd_show_memory(void)
{
cli_msg(-1018, "BIRD memory usage");
cli_msg(-1018, "%-17s Effective Overhead", "");
print_size("Routing tables:", rmemsize(rt_table_pool));
print_size("Route attributes:", rmemsize(rta_pool));
print_size("Protocols:", rmemsize(proto_pool));
print_size("Total:", rmemsize(&root_pool));
struct resmem total = rmemsize(&root_pool);
#ifdef HAVE_MMAP
print_size("Standby memory:", (struct resmem) { .overhead = page_size * *pages_kept });
total.overhead += page_size * *pages_kept;
#endif
print_size("Total:", total);
cli_msg(0, "");
}
@ -101,7 +134,7 @@ cmd_eval(const struct f_line *expr)
buffer buf;
LOG_BUFFER_INIT(buf);
if (f_eval_buf(expr, this_cli->parser_pool, &buf) > F_RETURN)
if (f_eval_buf(expr, &buf) > F_RETURN)
{
cli_msg(8008, "runtime error");
return;

View File

@ -17,6 +17,7 @@ CF_HDR
CF_DEFINES
static struct rtable_config *this_table;
static struct proto_config *this_proto;
static struct channel_config *this_channel;
static struct iface_patt *this_ipatt;
@ -111,19 +112,20 @@ proto_postconfig(void)
CF_DECLS
CF_KEYWORDS(ROUTER, ID, HOSTNAME, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT)
CF_KEYWORDS(ROUTER, ID, HOSTNAME, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT, PIPE)
CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, VRF, DEFAULT, TABLE, STATES, ROUTES, FILTERS)
CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, SADR, MPLS)
CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED, RPKI)
CF_KEYWORDS(PASSWORD, KEY, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, CHANNELS, INTERFACES)
CF_KEYWORDS(ALGORITHM, KEYED, HMAC, MD5, SHA1, SHA256, SHA384, SHA512, BLAKE2S128, BLAKE2S256, BLAKE2B256, BLAKE2B512)
CF_KEYWORDS(PRIMARY, STATS, COUNT, BY, FOR, COMMANDS, PREEXPORT, NOEXPORT, EXPORTED, GENERATE)
CF_KEYWORDS(BGP, PASSWORDS, DESCRIPTION, SORTED)
CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP)
CF_KEYWORDS(PRIMARY, STATS, COUNT, FOR, IN, COMMANDS, PREEXPORT, NOEXPORT, EXPORTED, GENERATE)
CF_KEYWORDS(BGP, PASSWORDS, DESCRIPTION)
CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, CLASS, DSCP)
CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LONG, ROUTE, PROTOCOL, BASE, LOG, S, MS, US)
CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, AS)
CF_KEYWORDS(MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE)
CF_KEYWORDS(CHECK, LINK)
CF_KEYWORDS(SORTED, TRIE, MIN, MAX, SETTLE, TIME)
/* For r_args_channel */
CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, IPV6_SADR, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC)
@ -131,7 +133,7 @@ CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, IPV6_SADR, VPN4,
CF_ENUM(T_ENUM_RTS, RTS_, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE, BABEL)
CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED)
CF_ENUM(T_ENUM_RTD, RTD_, UNICAST, BLACKHOLE, UNREACHABLE, PROHIBIT)
CF_ENUM(T_ENUM_RTD, RTD_, BLACKHOLE, UNREACHABLE, PROHIBIT)
CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID)
CF_ENUM_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6)
@ -141,7 +143,7 @@ CF_ENUM_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6)
%type <s> optproto
%type <ra> r_args
%type <sd> sym_args
%type <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_mode limit_action net_type table_sorted tos password_algorithm
%type <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_mode limit_action net_type tos password_algorithm
%type <ps> proto_patt proto_patt2
%type <cc> channel_start proto_channel
%type <cl> limit_spec
@ -206,16 +208,37 @@ CF_ENUM(T_ENUM_NETTYPE, NET_, IP4, IP6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, IP
conf: table ;
table_sorted:
{ $$ = 0; }
| SORTED { $$ = 1; }
table: table_start table_sorted table_opt_list ;
table_start: net_type TABLE symbol {
this_table = rt_new_table($3, $1);
}
;
table: net_type TABLE symbol table_sorted {
struct rtable_config *cf;
cf = rt_new_table($3, $1);
cf->sorted = $4;
table_sorted:
/* empty */
| SORTED { this_table->sorted = 1; }
;
table_opt:
SORTED bool { this_table->sorted = $2; }
| TRIE bool {
if (!net_val_match(this_table->addr_type, NB_IP | NB_VPN | NB_ROA | NB_IP6_SADR))
cf_error("Trie option not supported for %s table", net_label[this_table->addr_type]);
this_table->trie_used = $2;
}
| MIN SETTLE TIME expr_us { this_table->min_settle_time = $4; }
| MAX SETTLE TIME expr_us { this_table->max_settle_time = $4; }
;
table_opts:
/* empty */
| table_opts table_opt ';'
;
table_opt_list:
/* empty */
| '{' table_opts '}'
;
@ -348,6 +371,7 @@ debug_default:
DEBUG PROTOCOLS debug_mask { new_config->proto_default_debug = $3; }
| DEBUG CHANNELS debug_mask { new_config->channel_default_debug = $3; }
| DEBUG COMMANDS expr { new_config->cli_debug = $3; }
| DEBUG PIPE bool { new_config->pipe_debug = $3; }
;
/* MRTDUMP PROTOCOLS is in systep/unix/config.Y */
@ -615,20 +639,28 @@ r_args:
$$ = cfg_allocz(sizeof(struct rt_show_data));
init_list(&($$->tables));
$$->filter = FILTER_ACCEPT;
$$->running_on_config = new_config->fallback;
$$->running_on_config = config;
}
| r_args net_any {
$$ = $1;
if ($$->addr) cf_error("Only one prefix expected");
$$->addr = $2;
$$->addr_mode = RSD_ADDR_EQUAL;
}
| r_args FOR r_args_for {
$$ = $1;
if ($$->addr) cf_error("Only one prefix expected");
$$->show_for = 1;
$$->addr = $3;
$$->addr_mode = RSD_ADDR_FOR;
}
| r_args TABLE CF_SYM_KNOWN {
| r_args IN net_any {
$$ = $1;
if ($$->addr) cf_error("Only one prefix expected");
if (!net_type_match($3, NB_IP)) cf_error("Only IP networks accepted for 'in' argument");
$$->addr = $3;
$$->addr_mode = RSD_ADDR_IN;
}
| r_args TABLE symbol_known {
cf_assert_symbol($3, SYM_TABLE);
$$ = $1;
rt_show_add_table($$, $3->table->table);
@ -673,7 +705,7 @@ r_args:
$$ = $1;
$$->filtered = 1;
}
| r_args export_mode CF_SYM_KNOWN {
| r_args export_mode symbol_known {
cf_assert_symbol($3, SYM_PROTO);
struct proto_config *c = (struct proto_config *) $3->proto;
$$ = $1;
@ -690,7 +722,7 @@ r_args:
$$->export_channel = $3;
$$->tables_defined_by = RSD_TDB_INDIRECT;
}
| r_args PROTOCOL CF_SYM_KNOWN {
| r_args PROTOCOL symbol_known {
cf_assert_symbol($3, SYM_PROTO);
struct proto_config *c = (struct proto_config *) $3->proto;
$$ = $1;
@ -819,8 +851,10 @@ CF_CLI(DUMP NEIGHBORS,,, [[Dump neighbor cache]])
{ neigh_dump_all(); cli_msg(0, ""); } ;
CF_CLI(DUMP ATTRIBUTES,,, [[Dump attribute cache]])
{ rta_dump_all(); cli_msg(0, ""); } ;
CF_CLI(DUMP ROUTES,,, [[Dump routing table]])
CF_CLI(DUMP ROUTES,,, [[Dump routes]])
{ rt_dump_all(); cli_msg(0, ""); } ;
CF_CLI(DUMP TABLES,,, [[Dump table connections]])
{ rt_dump_hooks_all(); cli_msg(0, ""); } ;
CF_CLI(DUMP PROTOCOLS,,, [[Dump protocol information]])
{ protos_dump_all(); cli_msg(0, ""); } ;
CF_CLI(DUMP FILTER ALL,,, [[Dump all filters in linearized form]])
@ -890,9 +924,6 @@ proto_patt2:
| TEXT { $$.ptr = $1; $$.patt = 1; }
;
dynamic_attr: IGP_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_GEN_IGP_METRIC); } ;
CF_CODE
CF_END

View File

@ -591,7 +591,7 @@ ifa_update(struct ifa *a)
if (ipa_equal(b->brd, a->brd) &&
ipa_equal(b->opposite, a->opposite) &&
b->scope == a->scope &&
!((b->flags ^ a->flags) & IA_PEER))
!((b->flags ^ a->flags) & (IA_SECONDARY | IA_PEER | IA_HOST)))
{
b->flags |= IA_UPDATED;
return b;

49
nest/limit.h Normal file
View File

@ -0,0 +1,49 @@
/*
* BIRD Internet Routing Daemon -- Limits
*
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
* (c) 2021 Maria Matejka <mq@jmq.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef _BIRD_LIMIT_H_
#define _BIRD_LIMIT_H_
struct limit {
u32 max;
u32 count;
int (*action)(struct limit *, void *data);
};
static inline int limit_do_action(struct limit *l, void *data)
{
return l->action ? l->action(l, data) : 1;
}
static inline int limit_push(struct limit *l, void *data)
{
if ((l->count >= l->max) && limit_do_action(l, data))
return 1;
l->count++;
return 0;
}
static inline void limit_pop(struct limit *l)
{
--l->count;
}
static inline void limit_reset(struct limit *l)
{
l->count = 0;
}
static inline void limit_update(struct limit *l, void *data, u32 max)
{
if (l->count > (l->max = max))
limit_do_action(l, data);
}
#endif

View File

@ -345,7 +345,7 @@ neigh_free(neighbor *n)
{
rem_node(&n->n);
rem_node(&n->if_n);
sl_free(neigh_slab, n);
sl_free(n);
}
/**

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,8 @@
#include "lib/lists.h"
#include "lib/resource.h"
#include "lib/event.h"
#include "nest/route.h"
#include "nest/rt.h"
#include "nest/limit.h"
#include "conf/conf.h"
struct iface;
@ -37,38 +38,20 @@ struct symbol;
* Routing Protocol
*/
enum protocol_class {
PROTOCOL_NONE,
PROTOCOL_BABEL,
PROTOCOL_BFD,
PROTOCOL_BGP,
PROTOCOL_DEVICE,
PROTOCOL_DIRECT,
PROTOCOL_KERNEL,
PROTOCOL_OSPF,
PROTOCOL_MRT,
PROTOCOL_PERF,
PROTOCOL_PIPE,
PROTOCOL_RADV,
PROTOCOL_RIP,
PROTOCOL_RPKI,
PROTOCOL_STATIC,
PROTOCOL__MAX
};
extern struct protocol *class_to_protocol[PROTOCOL__MAX];
struct protocol {
node n;
char *name;
char *template; /* Template for automatic generation of names */
int name_counter; /* Counter for automatic name generation */
enum protocol_class class; /* Machine readable protocol class */
uint preference; /* Default protocol preference */
uint channel_mask; /* Mask of accepted channel types (NB_*) */
uint proto_size; /* Size of protocol data structure */
uint config_size; /* Size of protocol config data structure */
uint eattr_begin; /* First ID of registered eattrs */
uint eattr_end; /* End of eattr id zone */
void (*preconfig)(struct protocol *, struct config *); /* Just before configuring */
void (*postconfig)(struct proto_config *); /* After configuring each instance */
struct proto * (*init)(struct proto_config *); /* Create new instance */
@ -76,16 +59,15 @@ struct protocol {
void (*dump)(struct proto *); /* Debugging dump */
int (*start)(struct proto *); /* Start the instance */
int (*shutdown)(struct proto *); /* Stop the instance */
void (*cleanup)(struct proto *); /* Called after shutdown when protocol became hungry/down */
void (*get_status)(struct proto *, byte *buf); /* Get instance status (for `show protocols' command) */
void (*get_route_info)(struct rte *, byte *buf); /* Get route information (for `show route' command) */
int (*get_attr)(const struct eattr *, byte *buf, int buflen); /* ASCIIfy dynamic attribute (returns GA_*) */
// int (*get_attr)(const struct eattr *, byte *buf, int buflen); /* ASCIIfy dynamic attribute (returns GA_*) */
void (*show_proto_info)(struct proto *); /* Show protocol info (for `show protocols all' command) */
void (*copy_config)(struct proto_config *, struct proto_config *); /* Copy config from given protocol instance */
};
void protos_build(void);
void proto_build(struct protocol *);
void protos_build(void); /* Called from sysdep to initialize protocols */
void proto_build(struct protocol *); /* Called from protocol to register itself */
void protos_preconfig(struct config *);
void protos_commit(struct config *new, struct config *old, int force_restart, int type);
struct proto * proto_spawn(struct proto_config *cf, uint disabled);
@ -132,31 +114,6 @@ struct proto_config {
};
/* Protocol statistics */
struct proto_stats {
/* Import - from protocol to core */
u32 imp_routes; /* Number of routes successfully imported to the (adjacent) routing table */
u32 filt_routes; /* Number of routes rejected in import filter but kept in the routing table */
u32 pref_routes; /* Number of routes selected as best in the (adjacent) routing table */
u32 imp_updates_received; /* Number of route updates received */
u32 imp_updates_invalid; /* Number of route updates rejected as invalid */
u32 imp_updates_filtered; /* Number of route updates rejected by filters */
u32 imp_updates_ignored; /* Number of route updates rejected as already in route table */
u32 imp_updates_accepted; /* Number of route updates accepted and imported */
u32 imp_withdraws_received; /* Number of route withdraws received */
u32 imp_withdraws_invalid; /* Number of route withdraws rejected as invalid */
u32 imp_withdraws_ignored; /* Number of route withdraws rejected as already not in route table */
u32 imp_withdraws_accepted; /* Number of route withdraws accepted and processed */
/* Export - from core to protocol */
u32 exp_routes; /* Number of routes successfully exported to the protocol */
u32 exp_updates_received; /* Number of route updates received */
u32 exp_updates_rejected; /* Number of route updates rejected by protocol */
u32 exp_updates_filtered; /* Number of route updates rejected by filters */
u32 exp_updates_accepted; /* Number of route updates accepted and exported */
u32 exp_withdraws_received; /* Number of route withdraws received */
u32 exp_withdraws_accepted; /* Number of route withdraws accepted and processed */
};
struct proto {
node n; /* Node in global proto_list */
struct protocol *proto; /* Protocol */
@ -235,7 +192,7 @@ struct proto {
struct rte *(*rte_modify)(struct rte *, struct linpool *);
void (*rte_insert)(struct network *, struct rte *);
void (*rte_remove)(struct network *, struct rte *);
u32 (*rte_igp_metric)(struct rte *);
u32 (*rte_igp_metric)(const struct rte *);
/* Hic sunt protocol-specific data */
};
@ -275,7 +232,7 @@ void channel_graceful_restart_unlock(struct channel *c);
#define DEFAULT_GR_WAIT 240
void channel_show_limit(struct channel_limit *l, const char *dsc);
void channel_show_limit(struct limit *l, const char *dsc, int active, int action);
void channel_show_info(struct channel *c);
void channel_cmd_debug(struct channel *c, uint mask);
@ -430,18 +387,29 @@ extern struct proto_config *cf_dev_proto;
#define PLA_RESTART 4 /* Force protocol restart */
#define PLA_DISABLE 5 /* Shutdown and disable protocol */
#define PLS_INITIAL 0 /* Initial limit state after protocol start */
#define PLS_ACTIVE 1 /* Limit was hit */
#define PLS_BLOCKED 2 /* Limit is active and blocking new routes */
struct channel_limit {
u32 limit; /* Maximum number of prefixes */
u8 action; /* Action to take (PLA_*) */
u8 state; /* State of limit (PLS_*) */
};
void channel_notify_limit(struct channel *c, struct channel_limit *l, int dir, u32 rt_count);
struct channel_limit_data {
struct channel *c;
int dir;
};
#define CLP__RX(_c) (&(_c)->rx_limit)
#define CLP__IN(_c) (&(_c)->in_limit)
#define CLP__OUT(_c) (&(_c)->out_limit)
#if 0
#define CHANNEL_LIMIT_LOG(_c, _dir, _op) log(L_TRACE "%s.%s: %s limit %s %u", (_c)->proto->name, (_c)->name, #_dir, _op, (CLP__##_dir(_c))->count)
#else
#define CHANNEL_LIMIT_LOG(_c, _dir, _op)
#endif
#define CHANNEL_LIMIT_PUSH(_c, _dir) ({ CHANNEL_LIMIT_LOG(_c, _dir, "push from"); struct channel_limit_data cld = { .c = (_c), .dir = PLD_##_dir }; limit_push(CLP__##_dir(_c), &cld); })
#define CHANNEL_LIMIT_POP(_c, _dir) ({ limit_pop(CLP__##_dir(_c)); CHANNEL_LIMIT_LOG(_c, _dir, "pop to"); })
/*
* Channels
@ -484,6 +452,7 @@ struct channel_config {
struct proto_config *parent; /* Where channel is defined (proto or template) */
struct rtable_config *table; /* Table we're attached to */
const struct filter *in_filter, *out_filter; /* Attached filters */
struct channel_limit rx_limit; /* Limit for receiving routes from protocol
(relevant when in_keep_filtered is active) */
struct channel_limit in_limit; /* Limit for importing routes from protocol */
@ -500,7 +469,6 @@ struct channel_config {
struct channel {
node n; /* Node in proto->channels */
node table_node; /* Node in table->channels */
const char *name; /* Channel name (may be NULL) */
const struct channel_class *channel;
@ -509,14 +477,39 @@ struct channel {
struct rtable *table;
const struct filter *in_filter; /* Input filter */
const struct filter *out_filter; /* Output filter */
struct bmap export_map; /* Keeps track which routes passed export filter */
struct channel_limit rx_limit; /* Receive limit (for in_keep_filtered) */
struct channel_limit in_limit; /* Input limit */
struct channel_limit out_limit; /* Output limit */
struct bmap export_map; /* Keeps track which routes were really exported */
struct bmap export_reject_map; /* Keeps track which routes were rejected by export filter */
struct limit rx_limit; /* Receive limit (for in_keep_filtered) */
struct limit in_limit; /* Input limit */
struct limit out_limit; /* Output limit */
u8 limit_actions[PLD_MAX]; /* Limit actions enum */
u8 limit_active; /* Flags for active limits */
struct channel_import_stats {
/* Import - from protocol to core */
u32 updates_received; /* Number of route updates received */
u32 updates_invalid; /* Number of route updates rejected as invalid */
u32 updates_filtered; /* Number of route updates rejected by filters */
u32 updates_limited_rx; /* Number of route updates exceeding the rx_limit */
u32 updates_limited_in; /* Number of route updates exceeding the in_limit */
u32 withdraws_received; /* Number of route withdraws received */
u32 withdraws_invalid; /* Number of route withdraws rejected as invalid */
} import_stats;
struct channel_export_stats {
/* Export - from core to protocol */
u32 updates_rejected; /* Number of route updates rejected by protocol */
u32 updates_filtered; /* Number of route updates rejected by filters */
u32 updates_accepted; /* Number of route updates accepted and exported */
u32 updates_limited; /* Number of route updates exceeding the out_limit */
u32 withdraws_accepted; /* Number of route withdraws accepted and processed */
} export_stats;
struct rt_import_request in_req; /* Table import connection */
struct rt_export_request out_req; /* Table export connection */
struct event *feed_event; /* Event responsible for feeding */
struct fib_iterator feed_fit; /* Routing table iterator used during feeding */
struct proto_stats stats; /* Per-channel protocol statistics */
u32 refeed_count; /* Number of routes exported during refeed regardless of out_limit */
u8 net_type; /* Routing table network type (NET_*), 0 for undefined */
@ -529,10 +522,7 @@ struct channel {
u8 stale; /* Used in reconfiguration */
u8 channel_state;
u8 export_state; /* Route export state (ES_*, see below) */
u8 feed_active;
u8 flush_active;
u8 refeeding; /* We are refeeding (valid only if export_state == ES_FEEDING) */
u8 refeeding; /* Refeeding the channel. */
u8 reloadable; /* Hook reload_routes() is allowed on the channel */
u8 gr_lock; /* Graceful restart mechanism should wait for this channel */
u8 gr_wait; /* Route export to channel is postponed until graceful restart */
@ -580,34 +570,34 @@ struct channel {
* restricted by that and is on volition of the protocol. Generally, channels
* are opened in protocols' start() hooks when going to PS_UP.
*
* CS_FLUSHING - The transitional state between initialized channel and closed
* CS_STOP - The transitional state between initialized channel and closed
* channel. The channel is still initialized, but no route exchange is allowed.
* Instead, the associated table is running flush loop to remove routes imported
* through the channel. After that, the channel changes state to CS_DOWN and
* is detached from the table (the table is unlocked and the channel is unlinked
* from it). Unlike other states, the CS_FLUSHING state is not explicitly
* from it). Unlike other states, the CS_STOP state is not explicitly
* entered or left by the protocol. A protocol may request to close a channel
* (by calling channel_close()), which causes the channel to change state to
* CS_FLUSHING and later to CS_DOWN. Also note that channels are closed
* CS_STOP and later to CS_DOWN. Also note that channels are closed
* automatically by the core when the protocol is going down.
*
* CS_PAUSE - Almost the same as CS_STOP, just the table import is kept and
* the table export is stopped before transitioning to CS_START.
*
* Allowed transitions:
*
* CS_DOWN -> CS_START / CS_UP
* CS_START -> CS_UP / CS_FLUSHING
* CS_UP -> CS_START / CS_FLUSHING
* CS_FLUSHING -> CS_DOWN (automatic)
* CS_START -> CS_UP / CS_STOP
* CS_UP -> CS_PAUSE / CS_STOP
* CS_PAUSE -> CS_START (automatic)
* CS_STOP -> CS_DOWN (automatic)
*/
#define CS_DOWN 0
#define CS_START 1
#define CS_UP 2
#define CS_FLUSHING 3
#define ES_DOWN 0
#define ES_FEEDING 1
#define ES_READY 2
#define CS_STOP 3
#define CS_PAUSE 4
struct channel_config *proto_cf_find_channel(struct proto_config *p, uint net_type);
static inline struct channel_config *proto_cf_main_channel(struct proto_config *pc)
@ -625,7 +615,7 @@ void channel_schedule_reload(struct channel *c);
static inline void channel_init(struct channel *c) { channel_set_state(c, CS_START); }
static inline void channel_open(struct channel *c) { channel_set_state(c, CS_UP); }
static inline void channel_close(struct channel *c) { channel_set_state(c, CS_FLUSHING); }
static inline void channel_close(struct channel *c) { channel_set_state(c, CS_STOP); }
void channel_request_feeding(struct channel *c);
void *channel_config_new(const struct channel_class *cc, const char *name, uint net_type, struct proto_config *proto);

View File

@ -1,727 +0,0 @@
/*
* BIRD Internet Routing Daemon -- Routing Table
*
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef _BIRD_ROUTE_H_
#define _BIRD_ROUTE_H_
#include "lib/lists.h"
#include "lib/bitmap.h"
#include "lib/resource.h"
#include "lib/net.h"
struct ea_list;
struct protocol;
struct proto;
struct rte_src;
struct symbol;
struct timer;
struct filter;
struct cli;
/*
* Generic data structure for storing network prefixes. Also used
* for the master routing table. Currently implemented as a hash
* table.
*
* Available operations:
* - insertion of new entry
* - deletion of entry
* - searching for entry by network prefix
* - asynchronous retrieval of fib contents
*/
struct fib_node {
struct fib_node *next; /* Next in hash chain */
struct fib_iterator *readers; /* List of readers of this node */
net_addr addr[0];
};
struct fib_iterator { /* See lib/slists.h for an explanation */
struct fib_iterator *prev, *next; /* Must be synced with struct fib_node! */
byte efef; /* 0xff to distinguish between iterator and node */
byte pad[3];
struct fib_node *node; /* Or NULL if freshly merged */
uint hash;
};
typedef void (*fib_init_fn)(void *);
struct fib {
pool *fib_pool; /* Pool holding all our data */
slab *fib_slab; /* Slab holding all fib nodes */
struct fib_node **hash_table; /* Node hash table */
uint hash_size; /* Number of hash table entries (a power of two) */
uint hash_order; /* Binary logarithm of hash_size */
uint hash_shift; /* 32 - hash_order */
uint addr_type; /* Type of address data stored in fib (NET_*) */
uint node_size; /* FIB node size, 0 for nonuniform */
uint node_offset; /* Offset of fib_node struct inside of user data */
uint entries; /* Number of entries */
uint entries_min, entries_max; /* Entry count limits (else start rehashing) */
fib_init_fn init; /* Constructor */
};
static inline void * fib_node_to_user(struct fib *f, struct fib_node *e)
{ return e ? (void *) ((char *) e - f->node_offset) : NULL; }
static inline struct fib_node * fib_user_to_node(struct fib *f, void *e)
{ return e ? (void *) ((char *) e + f->node_offset) : NULL; }
void fib_init(struct fib *f, pool *p, uint addr_type, uint node_size, uint node_offset, uint hash_order, fib_init_fn init);
void *fib_find(struct fib *, const net_addr *); /* Find or return NULL if doesn't exist */
void *fib_get_chain(struct fib *f, const net_addr *a); /* Find first node in linked list from hash table */
void *fib_get(struct fib *, const net_addr *); /* Find or create new if nonexistent */
void *fib_route(struct fib *, const net_addr *); /* Longest-match routing lookup */
void fib_delete(struct fib *, void *); /* Remove fib entry */
void fib_free(struct fib *); /* Destroy the fib */
void fib_check(struct fib *); /* Consistency check for debugging */
void fit_init(struct fib_iterator *, struct fib *); /* Internal functions, don't call */
struct fib_node *fit_get(struct fib *, struct fib_iterator *);
void fit_put(struct fib_iterator *, struct fib_node *);
void fit_put_next(struct fib *f, struct fib_iterator *i, struct fib_node *n, uint hpos);
void fit_put_end(struct fib_iterator *i);
void fit_copy(struct fib *f, struct fib_iterator *dst, struct fib_iterator *src);
#define FIB_WALK(fib, type, z) do { \
struct fib_node *fn_, **ff_ = (fib)->hash_table; \
uint count_ = (fib)->hash_size; \
type *z; \
while (count_--) \
for (fn_ = *ff_++; z = fib_node_to_user(fib, fn_); fn_=fn_->next)
#define FIB_WALK_END } while (0)
#define FIB_ITERATE_INIT(it, fib) fit_init(it, fib)
#define FIB_ITERATE_START(fib, it, type, z) do { \
struct fib_node *fn_ = fit_get(fib, it); \
uint count_ = (fib)->hash_size; \
uint hpos_ = (it)->hash; \
type *z; \
for(;;) { \
if (!fn_) \
{ \
if (++hpos_ >= count_) \
break; \
fn_ = (fib)->hash_table[hpos_]; \
continue; \
} \
z = fib_node_to_user(fib, fn_);
#define FIB_ITERATE_END fn_ = fn_->next; } } while(0)
#define FIB_ITERATE_PUT(it) fit_put(it, fn_)
#define FIB_ITERATE_PUT_NEXT(it, fib) fit_put_next(fib, it, fn_, hpos_)
#define FIB_ITERATE_PUT_END(it) fit_put_end(it)
#define FIB_ITERATE_UNLINK(it, fib) fit_get(fib, it)
#define FIB_ITERATE_COPY(dst, src, fib) fit_copy(fib, dst, src)
/*
* Master Routing Tables. Generally speaking, each of them contains a FIB
* with each entry pointing to a list of route entries representing routes
* to given network (with the selected one at the head).
*
* Each of the RTE's contains variable data (the preference and protocol-dependent
* metrics) and a pointer to a route attribute block common for many routes).
*
* It's guaranteed that there is at most one RTE for every (prefix,proto) pair.
*/
struct rtable_config {
node n;
char *name;
struct rtable *table;
struct proto_config *krt_attached; /* Kernel syncer attached to this table */
uint addr_type; /* Type of address data stored in table (NET_*) */
int gc_max_ops; /* Maximum number of operations before GC is run */
int gc_min_time; /* Minimum time between two consecutive GC runs */
byte sorted; /* Routes of network are sorted according to rte_better() */
byte internal; /* Internal table of a protocol */
btime min_settle_time; /* Minimum settle time for notifications */
btime max_settle_time; /* Maximum settle time for notifications */
};
typedef struct rtable {
resource r;
node n; /* Node in list of all tables */
pool *rp; /* Resource pool to allocate everything from, including itself */
struct slab *rte_slab; /* Slab to allocate route objects */
struct fib fib;
char *name; /* Name of this table */
list channels; /* List of attached channels (struct channel) */
uint addr_type; /* Type of address data stored in table (NET_*) */
int pipe_busy; /* Pipe loop detection */
int use_count; /* Number of protocols using this table */
u32 rt_count; /* Number of routes in the table */
byte internal; /* Internal table of a protocol */
struct hmap id_map;
struct hostcache *hostcache;
struct rtable_config *config; /* Configuration of this table */
struct config *deleted; /* Table doesn't exist in current configuration,
* delete as soon as use_count becomes 0 and remove
* obstacle from this routing table.
*/
struct event *rt_event; /* Routing table event */
btime last_rt_change; /* Last time when route changed */
btime base_settle_time; /* Start time of rtable settling interval */
btime gc_time; /* Time of last GC */
int gc_counter; /* Number of operations since last GC */
byte prune_state; /* Table prune state, 1 -> scheduled, 2-> running */
byte hcu_scheduled; /* Hostcache update is scheduled */
byte nhu_state; /* Next Hop Update state */
struct fib_iterator prune_fit; /* Rtable prune FIB iterator */
struct fib_iterator nhu_fit; /* Next Hop Update FIB iterator */
list subscribers; /* Subscribers for notifications */
struct timer *settle_timer; /* Settle time for notifications */
} rtable;
struct rt_subscription {
node n;
rtable *tab;
void (*hook)(struct rt_subscription *b);
void *data;
};
#define NHU_CLEAN 0
#define NHU_SCHEDULED 1
#define NHU_RUNNING 2
#define NHU_DIRTY 3
typedef struct network {
struct rte_storage *routes; /* Available routes for this network */
struct fib_node n; /* FIB flags reserved for kernel syncer */
} net;
struct hostcache {
slab *slab; /* Slab holding all hostentries */
struct hostentry **hash_table; /* Hash table for hostentries */
unsigned hash_order, hash_shift;
unsigned hash_max, hash_min;
unsigned hash_items;
linpool *lp; /* Linpool for trie */
struct f_trie *trie; /* Trie of prefixes that might affect hostentries */
list hostentries; /* List of all hostentries */
byte update_hostcache;
};
struct hostentry {
node ln;
ip_addr addr; /* IP address of host, part of key */
ip_addr link; /* (link-local) IP address of host, used as gw
if host is directly attached */
struct rtable *tab; /* Dependent table, part of key */
struct hostentry *next; /* Next in hash chain */
unsigned hash_key; /* Hash key */
unsigned uc; /* Use count */
struct rta *src; /* Source rta entry */
byte dest; /* Chosen route destination type (RTD_...) */
byte nexthop_linkable; /* Nexthop list is completely non-device */
u32 igp_metric; /* Chosen route IGP metric */
};
typedef struct rte {
struct rta *attrs; /* Attributes of this route */
const net_addr *net; /* Network this RTE belongs to */
struct rte_src *src; /* Route source that created the route */
struct channel *sender; /* Channel used to send the route to the routing table */
btime lastmod; /* Last modified (set by table) */
u32 id; /* Table specific route id */
byte flags; /* Table-specific flags */
byte pflags; /* Protocol-specific flags */
} rte;
struct rte_storage {
struct rte_storage *next; /* Next in chain */
struct rte rte; /* Route data */
};
#define RTE_COPY(r, l) ((r) ? (((*(l)) = (r)->rte), (l)) : NULL)
#define RTE_OR_NULL(r) ((r) ? &((r)->rte) : NULL)
#define REF_FILTERED 2 /* Route is rejected by import filter */
#define REF_STALE 4 /* Route is stale in a refresh cycle */
#define REF_DISCARD 8 /* Route is scheduled for discard */
#define REF_MODIFY 16 /* Route is scheduled for modify */
/* Route is valid for propagation (may depend on other flags in the future), accepts NULL */
static inline int rte_is_valid(rte *r) { return r && !(r->flags & REF_FILTERED); }
/* Route just has REF_FILTERED flag */
static inline int rte_is_filtered(rte *r) { return !!(r->flags & REF_FILTERED); }
/* Types of route announcement, also used as flags */
#define RA_UNDEF 0 /* Undefined RA type */
#define RA_OPTIMAL 1 /* Announcement of optimal route change */
#define RA_ACCEPTED 2 /* Announcement of first accepted route */
#define RA_ANY 3 /* Announcement of any route change */
#define RA_MERGED 4 /* Announcement of optimal route merged with next ones */
/* Return value of preexport() callback */
#define RIC_ACCEPT 1 /* Accepted by protocol */
#define RIC_PROCESS 0 /* Process it through import filter */
#define RIC_REJECT -1 /* Rejected by protocol */
#define RIC_DROP -2 /* Silently dropped by protocol */
/**
* rte_update - enter a new update to a routing table
* @c: channel doing the update
* @net: network address
* @rte: a &rte representing the new route
* @src: old route source identifier
*
* This function imports a new route to the appropriate table (via the channel).
* Table keys are @net (obligatory) and @rte->attrs->src.
* Both the @net and @rte pointers can be local.
*
* The route attributes (@rte->attrs) are obligatory. They can be also allocated
* locally. Anyway, if you use an already-cached attribute object, you shall
* call rta_clone() on that object yourself. (This semantics may change in future.)
*
* If the route attributes are local, you may set @rte->attrs->src to NULL, then
* the protocol's default route source will be supplied.
*
* When rte_update() gets a route, it automatically validates it. This includes
* checking for validity of the given network and next hop addresses and also
* checking for host-scope or link-scope routes. Then the import filters are
* processed and if accepted, the route is passed to route table recalculation.
*
* The accepted routes are then inserted into the table, replacing the old route
* for the same @net identified by @src. Then the route is announced
* to all the channels connected to the table using the standard export mechanism.
* Setting @rte to NULL makes this a withdraw, otherwise @rte->src must be the same
* as @src.
*
* All memory used for temporary allocations is taken from a special linpool
* @rte_update_pool and freed when rte_update() finishes.
*/
void rte_update(struct channel *c, const net_addr *net, struct rte *rte, struct rte_src *src);
extern list routing_tables;
struct config;
void rt_init(void);
void rt_preconfig(struct config *);
void rt_commit(struct config *new, struct config *old);
void rt_lock_table(rtable *);
void rt_unlock_table(rtable *);
void rt_subscribe(rtable *tab, struct rt_subscription *s);
void rt_unsubscribe(struct rt_subscription *s);
rtable *rt_setup(pool *, struct rtable_config *);
static inline void rt_shutdown(rtable *r) { rfree(r->rp); }
static inline net *net_find(rtable *tab, const net_addr *addr) { return (net *) fib_find(&tab->fib, addr); }
static inline net *net_find_valid(rtable *tab, const net_addr *addr)
{ net *n = net_find(tab, addr); return (n && n->routes && rte_is_valid(&n->routes->rte)) ? n : NULL; }
static inline net *net_get(rtable *tab, const net_addr *addr) { return (net *) fib_get(&tab->fib, addr); }
void *net_route(rtable *tab, const net_addr *n);
int net_roa_check(rtable *tab, const net_addr *n, u32 asn);
int rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filter);
rte *rt_export_merged(struct channel *c, net *net, linpool *pool, int silent);
void rt_refresh_begin(rtable *t, struct channel *c);
void rt_refresh_end(rtable *t, struct channel *c);
void rt_modify_stale(rtable *t, struct channel *c);
void rt_schedule_prune(rtable *t);
void rte_dump(struct rte_storage *);
void rte_free(struct rte_storage *, rtable *);
struct rte_storage *rte_store(const rte *, net *net, rtable *);
void rt_dump(rtable *);
void rt_dump_all(void);
int rt_feed_channel(struct channel *c);
void rt_feed_channel_abort(struct channel *c);
int rt_reload_channel(struct channel *c);
void rt_reload_channel_abort(struct channel *c);
void rt_prune_sync(rtable *t, int all);
int rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old, struct rte_storage **old_exported, int refeed);
struct rtable_config *rt_new_table(struct symbol *s, uint addr_type);
/* Default limit for ECMP next hops, defined in sysdep code */
extern const int rt_default_ecmp;
struct rt_show_data_rtable {
node n;
rtable *table;
struct channel *export_channel;
};
struct rt_show_data {
net_addr *addr;
list tables;
struct rt_show_data_rtable *tab; /* Iterator over table list */
struct rt_show_data_rtable *last_table; /* Last table in output */
struct fib_iterator fit; /* Iterator over networks in table */
int verbose, tables_defined_by;
const struct filter *filter;
struct proto *show_protocol;
struct proto *export_protocol;
struct channel *export_channel;
struct config *running_on_config;
struct krt_proto *kernel;
int export_mode, primary_only, filtered, stats, show_for;
int table_open; /* Iteration (fit) is open */
int net_counter, rt_counter, show_counter, table_counter;
int net_counter_last, rt_counter_last, show_counter_last;
};
void rt_show(struct rt_show_data *);
struct rt_show_data_rtable * rt_show_add_table(struct rt_show_data *d, rtable *t);
/* Value of table definition mode in struct rt_show_data */
#define RSD_TDB_DEFAULT 0 /* no table specified */
#define RSD_TDB_INDIRECT 0 /* show route ... protocol P ... */
#define RSD_TDB_ALL RSD_TDB_SET /* show route ... table all ... */
#define RSD_TDB_DIRECT RSD_TDB_SET | RSD_TDB_NMN /* show route ... table X table Y ... */
#define RSD_TDB_SET 0x1 /* internal: show empty tables */
#define RSD_TDB_NMN 0x2 /* internal: need matching net */
/* Value of export_mode in struct rt_show_data */
#define RSEM_NONE 0 /* Export mode not used */
#define RSEM_PREEXPORT 1 /* Routes ready for export, before filtering */
#define RSEM_EXPORT 2 /* Routes accepted by export filter */
#define RSEM_NOEXPORT 3 /* Routes rejected by export filter */
#define RSEM_EXPORTED 4 /* Routes marked in export map */
/*
* Route Attributes
*
* Beware: All standard BGP attributes must be represented here instead
* of making them local to the route. This is needed to ensure proper
* construction of BGP route attribute lists.
*/
/* Nexthop structure */
struct nexthop {
ip_addr gw; /* Next hop */
struct iface *iface; /* Outgoing interface */
struct nexthop *next;
byte flags;
byte weight;
byte labels_orig; /* Number of labels before hostentry was applied */
byte labels; /* Number of all labels */
u32 label[0];
};
#define RNF_ONLINK 0x1 /* Gateway is onlink regardless of IP ranges */
struct rte_src {
struct rte_src *next; /* Hash chain */
struct proto *proto; /* Protocol the source is based on */
u32 private_id; /* Private ID, assigned by the protocol */
u32 global_id; /* Globally unique ID of the source */
unsigned uc; /* Use count */
};
typedef struct rta {
struct rta *next, **pprev; /* Hash chain */
u32 uc; /* Use count */
u32 hash_key; /* Hash over important fields */
struct ea_list *eattrs; /* Extended Attribute chain */
struct hostentry *hostentry; /* Hostentry for recursive next-hops */
ip_addr from; /* Advertising router */
u32 igp_metric; /* IGP metric to next hop (for iBGP routes) */
u16 cached:1; /* Are attributes cached? */
u16 source:7; /* Route source (RTS_...) */
u16 scope:4; /* Route scope (SCOPE_... -- see ip.h) */
u16 dest:4; /* Route destination type (RTD_...) */
word pref;
struct nexthop nh; /* Next hop */
} rta;
#define RTS_STATIC 1 /* Normal static route */
#define RTS_INHERIT 2 /* Route inherited from kernel */
#define RTS_DEVICE 3 /* Device route */
#define RTS_STATIC_DEVICE 4 /* Static device route */
#define RTS_REDIRECT 5 /* Learned via redirect */
#define RTS_RIP 6 /* RIP route */
#define RTS_OSPF 7 /* OSPF route */
#define RTS_OSPF_IA 8 /* OSPF inter-area route */
#define RTS_OSPF_EXT1 9 /* OSPF external route type 1 */
#define RTS_OSPF_EXT2 10 /* OSPF external route type 2 */
#define RTS_BGP 11 /* BGP route */
#define RTS_PIPE 12 /* Inter-table wormhole */
#define RTS_BABEL 13 /* Babel route */
#define RTS_RPKI 14 /* Route Origin Authorization */
#define RTS_PERF 15 /* Perf checker */
#define RTS_MAX 16
#define RTD_NONE 0 /* Undefined next hop */
#define RTD_UNICAST 1 /* Next hop is neighbor router */
#define RTD_BLACKHOLE 2 /* Silently drop packets */
#define RTD_UNREACHABLE 3 /* Reject as unreachable */
#define RTD_PROHIBIT 4 /* Administratively prohibited */
#define RTD_MAX 5
#define IGP_METRIC_UNKNOWN 0x80000000 /* Default igp_metric used when no other
protocol-specific metric is availabe */
extern const char * rta_dest_names[RTD_MAX];
static inline const char *rta_dest_name(uint n)
{ return (n < RTD_MAX) ? rta_dest_names[n] : "???"; }
/* Route has regular, reachable nexthop (i.e. not RTD_UNREACHABLE and like) */
static inline int rte_is_reachable(rte *r)
{ return r->attrs->dest == RTD_UNICAST; }
/*
* Extended Route Attributes
*/
typedef struct eattr {
word id; /* EA_CODE(PROTOCOL_..., protocol-dependent ID) */
byte flags; /* Protocol-dependent flags */
byte type; /* Attribute type and several flags (EAF_...) */
union {
uintptr_t data;
const struct adata *ptr; /* Attribute data elsewhere */
} u;
} eattr;
#define EA_CODE(proto,id) (((proto) << 8) | (id))
#define EA_ID(ea) ((ea) & 0xff)
#define EA_PROTO(ea) ((ea) >> 8)
#define EA_CUSTOM(id) ((id) | EA_CUSTOM_BIT)
#define EA_IS_CUSTOM(ea) ((ea) & EA_CUSTOM_BIT)
#define EA_CUSTOM_ID(ea) ((ea) & ~EA_CUSTOM_BIT)
const char *ea_custom_name(uint ea);
#define EA_GEN_IGP_METRIC EA_CODE(PROTOCOL_NONE, 0)
#define EA_CODE_MASK 0xffff
#define EA_CUSTOM_BIT 0x8000
#define EA_ALLOW_UNDEF 0x10000 /* ea_find: allow EAF_TYPE_UNDEF */
#define EA_BIT(n) ((n) << 24) /* Used in bitfield accessors */
#define EA_BIT_GET(ea) ((ea) >> 24)
#define EAF_TYPE_MASK 0x1f /* Mask with this to get type */
#define EAF_TYPE_INT 0x01 /* 32-bit unsigned integer number */
#define EAF_TYPE_OPAQUE 0x02 /* Opaque byte string (not filterable) */
#define EAF_TYPE_IP_ADDRESS 0x04 /* IP address */
#define EAF_TYPE_ROUTER_ID 0x05 /* Router ID (IPv4 address) */
#define EAF_TYPE_AS_PATH 0x06 /* BGP AS path (encoding per RFC 1771:4.3) */
#define EAF_TYPE_BITFIELD 0x09 /* 32-bit embedded bitfield */
#define EAF_TYPE_INT_SET 0x0a /* Set of u32's (e.g., a community list) */
#define EAF_TYPE_PTR 0x0d /* Pointer to an object */
#define EAF_TYPE_EC_SET 0x0e /* Set of pairs of u32's - ext. community list */
#define EAF_TYPE_LC_SET 0x12 /* Set of triplets of u32's - large community list */
#define EAF_TYPE_UNDEF 0x1f /* `force undefined' entry */
#define EAF_EMBEDDED 0x01 /* Data stored in eattr.u.data (part of type spec) */
#define EAF_VAR_LENGTH 0x02 /* Attribute length is variable (part of type spec) */
#define EAF_ORIGINATED 0x20 /* The attribute has originated locally */
#define EAF_FRESH 0x40 /* An uncached attribute (e.g. modified in export filter) */
typedef struct adata {
uint length; /* Length of data */
byte data[0];
} adata;
extern const adata null_adata; /* adata of length 0 */
static inline struct adata *
lp_alloc_adata(struct linpool *pool, uint len)
{
struct adata *ad = lp_alloc(pool, sizeof(struct adata) + len);
ad->length = len;
return ad;
}
static inline int adata_same(const struct adata *a, const struct adata *b)
{ return (a->length == b->length && !memcmp(a->data, b->data, a->length)); }
typedef struct ea_list {
struct ea_list *next; /* In case we have an override list */
byte flags; /* Flags: EALF_... */
byte rfu;
word count; /* Number of attributes */
eattr attrs[0]; /* Attribute definitions themselves */
} ea_list;
#define EALF_SORTED 1 /* Attributes are sorted by code */
#define EALF_BISECT 2 /* Use interval bisection for searching */
#define EALF_CACHED 4 /* Attributes belonging to cached rta */
struct rte_src *rt_find_source(struct proto *p, u32 id);
struct rte_src *rt_get_source(struct proto *p, u32 id);
static inline void rt_lock_source(struct rte_src *src) { src->uc++; }
static inline void rt_unlock_source(struct rte_src *src) { src->uc--; }
void rt_prune_sources(void);
struct ea_walk_state {
ea_list *eattrs; /* Ccurrent ea_list, initially set by caller */
eattr *ea; /* Current eattr, initially NULL */
u32 visited[4]; /* Bitfield, limiting max to 128 */
};
eattr *ea_find(ea_list *, unsigned ea);
eattr *ea_walk(struct ea_walk_state *s, uint id, uint max);
uintptr_t ea_get_int(ea_list *, unsigned ea, uintptr_t def);
void ea_dump(ea_list *);
void ea_sort(ea_list *); /* Sort entries in all sub-lists */
unsigned ea_scan(ea_list *); /* How many bytes do we need for merged ea_list */
void ea_merge(ea_list *from, ea_list *to); /* Merge sub-lists to allocated buffer */
int ea_same(ea_list *x, ea_list *y); /* Test whether two ea_lists are identical */
uint ea_hash(ea_list *e); /* Calculate 16-bit hash value */
ea_list *ea_append(ea_list *to, ea_list *what);
void ea_format_bitfield(const struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max);
#define ea_normalize(ea) do { \
if (ea->next) { \
ea_list *t = alloca(ea_scan(ea)); \
ea_merge(ea, t); \
ea = t; \
} \
ea_sort(ea); \
if (ea->count == 0) \
ea = NULL; \
} while(0) \
static inline eattr *
ea_set_attr(ea_list **to, struct linpool *pool, uint id, uint flags, uint type, uintptr_t val)
{
ea_list *a = lp_alloc(pool, sizeof(ea_list) + sizeof(eattr));
eattr *e = &a->attrs[0];
a->flags = EALF_SORTED;
a->count = 1;
a->next = *to;
*to = a;
e->id = id;
e->type = type;
e->flags = flags;
if (type & EAF_EMBEDDED)
e->u.data = (u32) val;
else
e->u.ptr = (struct adata *) val;
return e;
}
static inline void
ea_set_attr_u32(ea_list **to, struct linpool *pool, uint id, uint flags, uint type, u32 val)
{ ea_set_attr(to, pool, id, flags, type, (uintptr_t) val); }
static inline void
ea_set_attr_ptr(ea_list **to, struct linpool *pool, uint id, uint flags, uint type, struct adata *val)
{ ea_set_attr(to, pool, id, flags, type, (uintptr_t) val); }
static inline void
ea_set_attr_data(ea_list **to, struct linpool *pool, uint id, uint flags, uint type, void *data, uint len)
{
struct adata *a = lp_alloc_adata(pool, len);
memcpy(a->data, data, len);
ea_set_attr(to, pool, id, flags, type, (uintptr_t) a);
}
#define NEXTHOP_MAX_SIZE (sizeof(struct nexthop) + sizeof(u32)*MPLS_MAX_LABEL_STACK)
static inline size_t nexthop_size(const struct nexthop *nh)
{ return sizeof(struct nexthop) + sizeof(u32)*nh->labels; }
int nexthop__same(struct nexthop *x, struct nexthop *y); /* Compare multipath nexthops */
static inline int nexthop_same(struct nexthop *x, struct nexthop *y)
{ return (x == y) || nexthop__same(x, y); }
struct nexthop *nexthop_merge(struct nexthop *x, struct nexthop *y, int rx, int ry, int max, linpool *lp);
struct nexthop *nexthop_sort(struct nexthop *x);
static inline void nexthop_link(struct rta *a, struct nexthop *from)
{ memcpy(&a->nh, from, nexthop_size(from)); }
void nexthop_insert(struct nexthop **n, struct nexthop *y);
int nexthop_is_sorted(struct nexthop *x);
void rta_init(void);
static inline size_t rta_size(const rta *a) { return sizeof(rta) + sizeof(u32)*a->nh.labels; }
#define RTA_MAX_SIZE (sizeof(rta) + sizeof(u32)*MPLS_MAX_LABEL_STACK)
rta *rta_lookup(rta *); /* Get rta equivalent to this one, uc++ */
static inline int rta_is_cached(rta *r) { return r->cached; }
static inline rta *rta_clone(rta *r) { r->uc++; return r; }
void rta__free(rta *r);
static inline void rta_free(rta *r) { if (r && !--r->uc) rta__free(r); }
rta *rta_do_cow(rta *o, linpool *lp);
static inline rta * rta_cow(rta *r, linpool *lp) { return rta_is_cached(r) ? rta_do_cow(r, lp) : r; }
void rta_dump(rta *);
void rta_dump_all(void);
void rta_show(struct cli *, rta *);
u32 rt_get_igp_metric(rte *);
struct hostentry * rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep);
void rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls);
static inline void
rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr gw, ip_addr ll, mpls_label_stack *mls)
{
rta_apply_hostentry(a, rt_get_hostentry(tab, gw, ll, dep), mls);
}
/*
* rta_set_recursive_next_hop() acquires hostentry from hostcache and fills
* rta->hostentry field. New hostentry has zero use count. Cached rta locks its
* hostentry (increases its use count), uncached rta does not lock it. Hostentry
* with zero use count is removed asynchronously during host cache update,
* therefore it is safe to hold such hostentry temorarily. Hostentry holds a
* lock for a 'source' rta, mainly to share multipath nexthops.
*
* There is no need to hold a lock for hostentry->dep table, because that table
* contains routes responsible for that hostentry, and therefore is non-empty if
* given hostentry has non-zero use count. If the hostentry has zero use count,
* the entry is removed before dep is referenced.
*
* The protocol responsible for routes with recursive next hops should hold a
* lock for a 'source' table governing that routes (argument tab to
* rta_set_recursive_next_hop()), because its routes reference hostentries
* (through rta) related to the governing table. When all such routes are
* removed, rtas are immediately removed achieving zero uc. Then the 'source'
* table lock could be immediately released, although hostentries may still
* exist - they will be freed together with the 'source' table.
*/
static inline void rt_lock_hostentry(struct hostentry *he) { if (he) he->uc++; }
static inline void rt_unlock_hostentry(struct hostentry *he) { if (he) he->uc--; }
/*
* Default protocol preferences
*/
#define DEF_PREF_DIRECT 240 /* Directly connected */
#define DEF_PREF_STATIC 200 /* Static route */
#define DEF_PREF_OSPF 150 /* OSPF intra-area, inter-area and type 1 external routes */
#define DEF_PREF_BABEL 130 /* Babel */
#define DEF_PREF_RIP 120 /* RIP */
#define DEF_PREF_BGP 100 /* BGP */
#define DEF_PREF_RPKI 100 /* RPKI */
#define DEF_PREF_INHERITED 10 /* Routes inherited from other routing daemons */
/*
* Route Origin Authorization
*/
#define ROA_UNKNOWN 0
#define ROA_VALID 1
#define ROA_INVALID 2
#endif

File diff suppressed because it is too large Load Diff

View File

@ -18,7 +18,7 @@
#include "nest/bird.h"
#include "nest/iface.h"
#include "nest/protocol.h"
#include "nest/route.h"
#include "nest/rt.h"
#include "nest/rt-dev.h"
#include "conf/conf.h"
#include "lib/resource.h"
@ -79,14 +79,16 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
/* Use iface ID as local source ID */
struct rte_src *src = rt_get_source(P, ad->iface->index);
rta a0 = {
.pref = c->preference,
.source = RTS_DEVICE,
.scope = SCOPE_UNIVERSE,
.dest = RTD_UNICAST,
.nh.iface = ad->iface,
rta a0 = {};
struct nexthop_adata nhad = {
.nh = { .iface = ad->iface, },
.ad = { .length = (void *) NEXTHOP_NEXT(&nhad.nh) - (void *) nhad.ad.data, },
};
ea_set_attr_u32(&a0.eattrs, &ea_gen_preference, 0, c->preference);
ea_set_attr_u32(&a0.eattrs, &ea_gen_source, 0, RTS_DEVICE);
ea_set_attr_data(&a0.eattrs, &ea_gen_nexthop, 0, nhad.ad.data, nhad.ad.length);
rte e0 = {
.attrs = rta_lookup(&a0),
.src = src,
@ -184,7 +186,6 @@ dev_copy_config(struct proto_config *dest, struct proto_config *src)
struct protocol proto_device = {
.name = "Direct",
.template = "direct%d",
.class = PROTOCOL_DIRECT,
.preference = DEF_PREF_DIRECT,
.channel_mask = NB_IP | NB_IP6_SADR,
.proto_size = sizeof(struct rt_dev_proto),
@ -194,3 +195,9 @@ struct protocol proto_device = {
.reconfigure = dev_reconfigure,
.copy_config = dev_copy_config
};
void
dev_build(void)
{
proto_build(&proto_device);
}

View File

@ -55,7 +55,7 @@
#undef LOCAL_DEBUG
#include "nest/bird.h"
#include "nest/route.h"
#include "nest/rt.h"
#include "lib/string.h"
/*
@ -331,7 +331,7 @@ fib_get(struct fib *f, const net_addr *a)
memset(b, 0, f->node_offset);
if (f->init)
f->init(b);
f->init(f, b);
if (f->entries++ > f->entries_max)
fib_rehash(f, HASH_HI_STEP);
@ -475,7 +475,7 @@ fib_delete(struct fib *f, void *E)
}
if (f->fib_slab)
sl_free(f->fib_slab, E);
sl_free(E);
else
mb_free(E);

View File

@ -10,11 +10,12 @@
#undef LOCAL_DEBUG
#include "nest/bird.h"
#include "nest/route.h"
#include "nest/rt.h"
#include "nest/protocol.h"
#include "nest/cli.h"
#include "nest/iface.h"
#include "filter/filter.h"
#include "filter/data.h"
#include "sysdep/unix/krt.h"
static void
@ -44,32 +45,38 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary
rta *a = e->attrs;
int sync_error = d->kernel ? krt_get_sync_error(d->kernel, e) : 0;
void (*get_route_info)(struct rte *, byte *buf);
struct nexthop *nh;
eattr *nhea = net_type_match(e->net, NB_DEST) ?
ea_find(a->eattrs, &ea_gen_nexthop) : NULL;
struct nexthop_adata *nhad = nhea ? (struct nexthop_adata *) nhea->u.ptr : NULL;
int dest = nhad ? (NEXTHOP_IS_REACHABLE(nhad) ? RTD_UNICAST : nhad->dest) : RTD_NONE;
int flowspec_valid = net_is_flow(e->net) ? rt_get_flowspec_valid(e) : FLOWSPEC_UNKNOWN;
tm_format_time(tm, &config->tf_route, e->lastmod);
if (ipa_nonzero(a->from) && !ipa_equal(a->from, a->nh.gw))
bsprintf(from, " from %I", a->from);
ip_addr a_from = ea_get_ip(a->eattrs, &ea_gen_from, IPA_NONE);
if (ipa_nonzero(a_from) && (!nhad || !ipa_equal(a_from, nhad->nh.gw)))
bsprintf(from, " from %I", a_from);
else
from[0] = 0;
/* Need to normalize the extended attributes */
if (d->verbose && !rta_is_cached(a) && a->eattrs)
ea_normalize(a->eattrs);
a->eattrs = ea_normalize(a->eattrs);
get_route_info = e->src->proto->proto->get_route_info;
if (get_route_info)
get_route_info(e, info);
else
bsprintf(info, " (%d)", a->pref);
bsprintf(info, " (%d)", rt_get_preference(e));
if (d->last_table != d->tab)
rt_show_table(c, d);
cli_printf(c, -1007, "%-20s %s [%s %s%s]%s%s", ia, rta_dest_name(a->dest),
e->src->proto->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info);
cli_printf(c, -1007, "%-20s %s [%s %s%s]%s%s", ia,
net_is_flow(e->net) ? flowspec_valid_name(flowspec_valid) : rta_dest_name(dest),
e->src->proto->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info);
if (a->dest == RTD_UNICAST)
for (nh = &(a->nh); nh; nh = nh->next)
if (dest == RTD_UNICAST)
NEXTHOP_WALK(nh, nhad)
{
char mpls[MPLS_MAX_LABEL_STACK*12 + 5], *lsp = mpls;
char *onlink = (nh->flags & RNF_ONLINK) ? " onlink" : "";
@ -83,7 +90,7 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary
}
*lsp = '\0';
if (a->nh.next)
if (!NEXTHOP_ONE(nhad))
bsprintf(weight, " weight %d", nh->weight + 1);
if (ipa_nonzero(nh->gw))
@ -98,6 +105,29 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary
rta_show(c, a);
}
static uint
rte_feed_count(net *n)
{
uint count = 0;
for (struct rte_storage *e = n->routes; e; e = e->next)
if (rte_is_valid(RTE_OR_NULL(e)))
count++;
return count;
}
static void
rte_feed_obtain(net *n, rte **feed, uint count)
{
uint i = 0;
for (struct rte_storage *e = n->routes; e; e = e->next)
if (rte_is_valid(RTE_OR_NULL(e)))
{
ASSERT_DIE(i < count);
feed[i++] = &e->rte;
}
ASSERT_DIE(i == count);
}
static void
rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
{
@ -109,10 +139,9 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
ASSUME(!d->export_mode || ec);
int first = 1;
int first_show = 1;
int pass = 0;
bsnprintf(ia, sizeof(ia), "%N", n->n.addr);
for (struct rte_storage *er = n->routes; er; er = er->next)
{
if (rte_is_filtered(&er->rte) != d->filtered)
@ -128,7 +157,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
struct rte e = er->rte;
/* Export channel is down, do not try to export routes to it */
if (ec && (ec->export_state == ES_DOWN))
if (ec && !ec->out_req.hook)
goto skip;
if (d->export_mode == RSEM_EXPORTED)
@ -143,7 +172,14 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
{
/* Special case for merged export */
pass = 1;
rte *em = rt_export_merged(ec, n, c->show_pool, 1);
uint count = rte_feed_count(n);
if (!count)
goto skip;
rte **feed = alloca(count * sizeof(rte *));
rte_feed_obtain(n, feed, count);
rte *em = rt_export_merged(ec, feed, count, c->show_pool, 1);
if (em)
e = *em;
else
@ -168,7 +204,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
* command may change the export filter and do not update routes.
*/
int do_export = (ic > 0) ||
(f_run(ec->out_filter, &e, c->show_pool, FF_SILENT) <= F_ACCEPT);
(f_run(ec->out_filter, &e, FF_SILENT) <= F_ACCEPT);
if (do_export != (d->export_mode == RSEM_EXPORT))
goto skip;
@ -181,14 +217,21 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
if (d->show_protocol && (d->show_protocol != e.src->proto))
goto skip;
if (f_run(d->filter, &e, c->show_pool, 0) > F_ACCEPT)
if (f_run(d->filter, &e, 0) > F_ACCEPT)
goto skip;
if (d->stats < 2)
{
if (first_show)
net_format(n->n.addr, ia, sizeof(ia));
else
ia[0] = 0;
rt_show_rte(c, ia, &e, d, (n->routes == er));
first_show = 0;
}
d->show_counter++;
ia[0] = 0;
skip:
lp_flush(c->show_pool);
@ -205,9 +248,12 @@ rt_show_cleanup(struct cli *c)
struct rt_show_data_rtable *tab;
/* Unlink the iterator */
if (d->table_open)
if (d->table_open && !d->trie_walk)
fit_get(&d->tab->table->fib, &d->fit);
if (d->walk_lock)
rt_unlock_trie(d->tab->table, d->walk_lock);
/* Unlock referenced tables */
WALK_LIST(tab, d->tables)
rt_unlock_table(tab->table);
@ -217,12 +263,13 @@ static void
rt_show_cont(struct cli *c)
{
struct rt_show_data *d = c->rover;
struct rtable *tab = d->tab->table;
#ifdef DEBUGGING
unsigned max = 4;
#else
unsigned max = 64;
#endif
struct fib *fib = &d->tab->table->fib;
struct fib *fib = &tab->fib;
struct fib_iterator *it = &d->fit;
if (d->running_on_config && (d->running_on_config != config))
@ -233,7 +280,22 @@ rt_show_cont(struct cli *c)
if (!d->table_open)
{
FIB_ITERATE_INIT(&d->fit, &d->tab->table->fib);
/* We use either trie-based walk or fib-based walk */
d->trie_walk = tab->trie &&
(d->addr_mode == RSD_ADDR_IN) &&
net_val_match(tab->addr_type, NB_IP);
if (d->trie_walk && !d->walk_state)
d->walk_state = lp_allocz(c->parser_pool, sizeof (struct f_trie_walk_state));
if (d->trie_walk)
{
d->walk_lock = rt_lock_trie(tab);
trie_walk_init(d->walk_state, tab->trie, d->addr);
}
else
FIB_ITERATE_INIT(&d->fit, &tab->fib);
d->table_open = 1;
d->table_counter++;
d->kernel = rt_show_get_kernel(d);
@ -246,16 +308,44 @@ rt_show_cont(struct cli *c)
rt_show_table(c, d);
}
FIB_ITERATE_START(fib, it, net, n)
if (d->trie_walk)
{
if (!max--)
/* Trie-based walk */
net_addr addr;
while (trie_walk_next(d->walk_state, &addr))
{
FIB_ITERATE_PUT(it);
return;
net *n = net_find(tab, &addr);
if (!n)
continue;
rt_show_net(c, n, d);
if (!--max)
return;
}
rt_show_net(c, n, d);
rt_unlock_trie(tab, d->walk_lock);
d->walk_lock = NULL;
}
else
{
/* fib-based walk */
FIB_ITERATE_START(fib, it, net, n)
{
if ((d->addr_mode == RSD_ADDR_IN) && (!net_in_netX(n->n.addr, d->addr)))
goto next;
if (!max--)
{
FIB_ITERATE_PUT(it);
return;
}
rt_show_net(c, n, d);
next:;
}
FIB_ITERATE_END;
}
FIB_ITERATE_END;
if (d->stats)
{
@ -264,7 +354,7 @@ rt_show_cont(struct cli *c)
cli_printf(c, -1007, "%d of %d routes for %d networks in table %s",
d->show_counter - d->show_counter_last, d->rt_counter - d->rt_counter_last,
d->net_counter - d->net_counter_last, d->tab->table->name);
d->net_counter - d->net_counter_last, tab->name);
}
d->kernel = NULL;
@ -315,7 +405,7 @@ rt_show_get_default_tables(struct rt_show_data *d)
{
WALK_LIST(c, d->export_protocol->channels)
{
if (c->export_state == ES_DOWN)
if (!c->out_req.hook)
continue;
tab = rt_show_add_table(d, c->table);
@ -395,7 +485,7 @@ rt_show(struct rt_show_data *d)
rt_show_prepare_tables(d);
if (!d->addr)
if (!d->addr || (d->addr_mode == RSD_ADDR_IN))
{
WALK_LIST(tab, d->tables)
rt_lock_table(tab->table);
@ -413,7 +503,7 @@ rt_show(struct rt_show_data *d)
d->tab = tab;
d->kernel = rt_show_get_kernel(d);
if (d->show_for)
if (d->addr_mode == RSD_ADDR_FOR)
n = net_route(tab->table, d->addr);
else
n = net_find(tab->table, d->addr);

File diff suppressed because it is too large Load Diff

486
nest/rt.h Normal file
View File

@ -0,0 +1,486 @@
/*
* BIRD Internet Routing Daemon -- Routing Table
*
* (c) 1998--2000 Martin Mares <mj@ucw.cz>
* (c) 2019--2021 Maria Matejka <mq@jmq.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
#ifndef _BIRD_NEST_RT_H_
#define _BIRD_NEST_RT_H_
#include "lib/lists.h"
#include "lib/bitmap.h"
#include "lib/resource.h"
#include "lib/net.h"
#include "lib/type.h"
#include "lib/fib.h"
#include "lib/route.h"
struct ea_list;
struct protocol;
struct proto;
struct channel;
struct rte_src;
struct symbol;
struct timer;
struct filter;
struct f_trie;
struct f_trie_walk_state;
struct cli;
/*
* Master Routing Tables. Generally speaking, each of them contains a FIB
* with each entry pointing to a list of route entries representing routes
* to given network (with the selected one at the head).
*
* Each of the RTE's contains variable data (the preference and protocol-dependent
* metrics) and a pointer to a route attribute block common for many routes).
*
* It's guaranteed that there is at most one RTE for every (prefix,proto) pair.
*/
struct rtable_config {
node n;
char *name;
struct rtable *table;
struct proto_config *krt_attached; /* Kernel syncer attached to this table */
uint addr_type; /* Type of address data stored in table (NET_*) */
int gc_max_ops; /* Maximum number of operations before GC is run */
int gc_min_time; /* Minimum time between two consecutive GC runs */
byte sorted; /* Routes of network are sorted according to rte_better() */
byte internal; /* Internal table of a protocol */
byte trie_used; /* Rtable has attached trie */
btime min_settle_time; /* Minimum settle time for notifications */
btime max_settle_time; /* Maximum settle time for notifications */
};
typedef struct rtable {
resource r;
node n; /* Node in list of all tables */
pool *rp; /* Resource pool to allocate everything from, including itself */
struct slab *rte_slab; /* Slab to allocate route objects */
struct fib fib;
struct f_trie *trie; /* Trie of prefixes defined in fib */
char *name; /* Name of this table */
uint addr_type; /* Type of address data stored in table (NET_*) */
int use_count; /* Number of protocols using this table */
u32 rt_count; /* Number of routes in the table */
list imports; /* Registered route importers */
list exports; /* Registered route exporters */
struct hmap id_map;
struct hostcache *hostcache;
struct rtable_config *config; /* Configuration of this table */
struct config *deleted; /* Table doesn't exist in current configuration,
* delete as soon as use_count becomes 0 and remove
* obstacle from this routing table.
*/
struct event *rt_event; /* Routing table event */
btime last_rt_change; /* Last time when route changed */
btime base_settle_time; /* Start time of rtable settling interval */
btime gc_time; /* Time of last GC */
int gc_counter; /* Number of operations since last GC */
byte prune_state; /* Table prune state, 1 -> scheduled, 2-> running */
byte prune_trie; /* Prune prefix trie during next table prune */
byte hcu_scheduled; /* Hostcache update is scheduled */
byte nhu_state; /* Next Hop Update state */
byte internal; /* This table is internal for some other object */
struct fib_iterator prune_fit; /* Rtable prune FIB iterator */
struct fib_iterator nhu_fit; /* Next Hop Update FIB iterator */
struct f_trie *trie_new; /* New prefix trie defined during pruning */
struct f_trie *trie_old; /* Old prefix trie waiting to be freed */
u32 trie_lock_count; /* Prefix trie locked by walks */
u32 trie_old_lock_count; /* Old prefix trie locked by walks */
struct tbf rl_pipe; /* Rate limiting token buffer for pipe collisions */
list subscribers; /* Subscribers for notifications */
struct timer *settle_timer; /* Settle time for notifications */
list flowspec_links; /* List of flowspec links, src for NET_IPx and dst for NET_FLOWx */
struct f_trie *flowspec_trie; /* Trie for evaluation of flowspec notifications */
} rtable;
struct rt_subscription {
node n;
rtable *tab;
void (*hook)(struct rt_subscription *b);
void *data;
};
struct rt_flowspec_link {
node n;
rtable *src;
rtable *dst;
u32 uc;
};
#define NHU_CLEAN 0
#define NHU_SCHEDULED 1
#define NHU_RUNNING 2
#define NHU_DIRTY 3
typedef struct network {
struct rte_storage *routes; /* Available routes for this network */
struct fib_node n; /* FIB flags reserved for kernel syncer */
} net;
struct hostcache {
slab *slab; /* Slab holding all hostentries */
struct hostentry **hash_table; /* Hash table for hostentries */
unsigned hash_order, hash_shift;
unsigned hash_max, hash_min;
unsigned hash_items;
linpool *lp; /* Linpool for trie */
struct f_trie *trie; /* Trie of prefixes that might affect hostentries */
list hostentries; /* List of all hostentries */
byte update_hostcache;
};
struct hostentry {
node ln;
ip_addr addr; /* IP address of host, part of key */
ip_addr link; /* (link-local) IP address of host, used as gw
if host is directly attached */
struct rtable *tab; /* Dependent table, part of key */
struct hostentry *next; /* Next in hash chain */
unsigned hash_key; /* Hash key */
unsigned uc; /* Use count */
struct rta *src; /* Source rta entry */
byte nexthop_linkable; /* Nexthop list is completely non-device */
u32 igp_metric; /* Chosen route IGP metric */
};
struct rte_storage {
struct rte_storage *next; /* Next in chain */
struct rte rte; /* Route data */
};
#define RTE_COPY(r, l) ((r) ? (((*(l)) = (r)->rte), (l)) : NULL)
#define RTE_OR_NULL(r) ((r) ? &((r)->rte) : NULL)
/* Table-channel connections */
struct rt_import_request {
struct rt_import_hook *hook; /* The table part of importer */
char *name;
u8 trace_routes;
void (*dump_req)(struct rt_import_request *req);
void (*log_state_change)(struct rt_import_request *req, u8 state);
/* Preimport is called when the @new route is just-to-be inserted, replacing @old.
* Return a route (may be different or modified in-place) to continue or NULL to withdraw. */
struct rte *(*preimport)(struct rt_import_request *req, struct rte *new, struct rte *old);
struct rte *(*rte_modify)(struct rte *, struct linpool *);
};
struct rt_import_hook {
node n;
rtable *table; /* The connected table */
struct rt_import_request *req; /* The requestor */
struct rt_import_stats {
/* Import - from protocol to core */
u32 pref; /* Number of routes selected as best in the (adjacent) routing table */
u32 updates_ignored; /* Number of route updates rejected as already in route table */
u32 updates_accepted; /* Number of route updates accepted and imported */
u32 withdraws_ignored; /* Number of route withdraws rejected as already not in route table */
u32 withdraws_accepted; /* Number of route withdraws accepted and processed */
} stats;
btime last_state_change; /* Time of last state transition */
u8 import_state; /* IS_* */
void (*stopped)(struct rt_import_request *); /* Stored callback when import is stopped */
};
struct rt_pending_export {
struct rte_storage *new, *new_best, *old, *old_best;
};
struct rt_export_request {
struct rt_export_hook *hook; /* Table part of the export */
char *name;
u8 trace_routes;
/* There are two methods of export. You can either request feeding every single change
* or feeding the whole route feed. In case of regular export, &export_one is preferred.
* Anyway, when feeding, &export_bulk is preferred, falling back to &export_one.
* Thus, for RA_OPTIMAL, &export_one is only set,
* for RA_MERGED and RA_ACCEPTED, &export_bulk is only set
* and for RA_ANY, both are set to accomodate for feeding all routes but receiving single changes
*/
void (*export_one)(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe);
void (*export_bulk)(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *rpe, rte **feed, uint count);
void (*dump_req)(struct rt_export_request *req);
void (*log_state_change)(struct rt_export_request *req, u8);
};
struct rt_export_hook {
node n;
rtable *table; /* The connected table */
pool *pool;
linpool *lp;
struct rt_export_request *req; /* The requestor */
struct rt_export_stats {
/* Export - from core to protocol */
u32 updates_received; /* Number of route updates received */
u32 withdraws_received; /* Number of route withdraws received */
} stats;
struct fib_iterator feed_fit; /* Routing table iterator used during feeding */
btime last_state_change; /* Time of last state transition */
u8 refeed_pending; /* Refeeding and another refeed is scheduled */
u8 export_state; /* Route export state (TES_*, see below) */
struct event *event; /* Event running all the export operations */
void (*stopped)(struct rt_export_request *); /* Stored callback when export is stopped */
};
#define TIS_DOWN 0
#define TIS_UP 1
#define TIS_STOP 2
#define TIS_FLUSHING 3
#define TIS_WAITING 4
#define TIS_CLEARED 5
#define TIS_MAX 6
#define TES_DOWN 0
#define TES_HUNGRY 1
#define TES_FEEDING 2
#define TES_READY 3
#define TES_STOP 4
#define TES_MAX 5
void rt_request_import(rtable *tab, struct rt_import_request *req);
void rt_request_export(rtable *tab, struct rt_export_request *req);
void rt_stop_import(struct rt_import_request *, void (*stopped)(struct rt_import_request *));
void rt_stop_export(struct rt_export_request *, void (*stopped)(struct rt_export_request *));
const char *rt_import_state_name(u8 state);
const char *rt_export_state_name(u8 state);
static inline u8 rt_import_get_state(struct rt_import_hook *ih) { return ih ? ih->import_state : TIS_DOWN; }
static inline u8 rt_export_get_state(struct rt_export_hook *eh) { return eh ? eh->export_state : TES_DOWN; }
void rte_import(struct rt_import_request *req, const net_addr *net, rte *new, struct rte_src *src);
/* Types of route announcement, also used as flags */
#define RA_UNDEF 0 /* Undefined RA type */
#define RA_OPTIMAL 1 /* Announcement of optimal route change */
#define RA_ACCEPTED 2 /* Announcement of first accepted route */
#define RA_ANY 3 /* Announcement of any route change */
#define RA_MERGED 4 /* Announcement of optimal route merged with next ones */
/* Return value of preexport() callback */
#define RIC_ACCEPT 1 /* Accepted by protocol */
#define RIC_PROCESS 0 /* Process it through import filter */
#define RIC_REJECT -1 /* Rejected by protocol */
#define RIC_DROP -2 /* Silently dropped by protocol */
#define rte_update channel_rte_import
/**
* rte_update - enter a new update to a routing table
* @c: channel doing the update
* @net: network address
* @rte: a &rte representing the new route
* @src: old route source identifier
*
* This function imports a new route to the appropriate table (via the channel).
* Table keys are @net (obligatory) and @rte->attrs->src.
* Both the @net and @rte pointers can be local.
*
* The route attributes (@rte->attrs) are obligatory. They can be also allocated
* locally. Anyway, if you use an already-cached attribute object, you shall
* call rta_clone() on that object yourself. (This semantics may change in future.)
*
* If the route attributes are local, you may set @rte->attrs->src to NULL, then
* the protocol's default route source will be supplied.
*
* When rte_update() gets a route, it automatically validates it. This includes
* checking for validity of the given network and next hop addresses and also
* checking for host-scope or link-scope routes. Then the import filters are
* processed and if accepted, the route is passed to route table recalculation.
*
* The accepted routes are then inserted into the table, replacing the old route
* for the same @net identified by @src. Then the route is announced
* to all the channels connected to the table using the standard export mechanism.
* Setting @rte to NULL makes this a withdraw, otherwise @rte->src must be the same
* as @src.
*
* All memory used for temporary allocations is taken from a special linpool
* @rte_update_pool and freed when rte_update() finishes.
*/
void rte_update(struct channel *c, const net_addr *net, struct rte *rte, struct rte_src *src);
extern list routing_tables;
struct config;
void rt_init(void);
void rt_preconfig(struct config *);
void rt_commit(struct config *new, struct config *old);
void rt_lock_table(rtable *);
void rt_unlock_table(rtable *);
struct f_trie * rt_lock_trie(rtable *tab);
void rt_unlock_trie(rtable *tab, struct f_trie *trie);
void rt_subscribe(rtable *tab, struct rt_subscription *s);
void rt_unsubscribe(struct rt_subscription *s);
void rt_flowspec_link(rtable *src, rtable *dst);
void rt_flowspec_unlink(rtable *src, rtable *dst);
rtable *rt_setup(pool *, struct rtable_config *);
static inline void rt_shutdown(rtable *r) { rfree(r->rp); }
static inline net *net_find(rtable *tab, const net_addr *addr) { return (net *) fib_find(&tab->fib, addr); }
static inline net *net_find_valid(rtable *tab, const net_addr *addr)
{ net *n = net_find(tab, addr); return (n && n->routes && rte_is_valid(&n->routes->rte)) ? n : NULL; }
static inline net *net_get(rtable *tab, const net_addr *addr) { return (net *) fib_get(&tab->fib, addr); }
net *net_get(rtable *tab, const net_addr *addr);
net *net_route(rtable *tab, const net_addr *n);
int rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filter);
rte *rt_export_merged(struct channel *c, rte ** feed, uint count, linpool *pool, int silent);
void rt_refresh_begin(rtable *t, struct rt_import_request *);
void rt_refresh_end(rtable *t, struct rt_import_request *);
void rt_modify_stale(rtable *t, struct rt_import_request *);
void rt_schedule_prune(rtable *t);
void rte_dump(struct rte_storage *);
void rte_free(struct rte_storage *);
struct rte_storage *rte_store(const rte *, net *net, rtable *);
void rt_dump(rtable *);
void rt_dump_all(void);
void rt_dump_hooks(rtable *);
void rt_dump_hooks_all(void);
int rt_reload_channel(struct channel *c);
void rt_reload_channel_abort(struct channel *c);
void rt_refeed_channel(struct channel *c);
void rt_prune_sync(rtable *t, int all);
int rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *src);
int rte_update_out(struct channel *c, const net_addr *n, rte *new, const rte *old, struct rte_storage **old_exported);
struct rtable_config *rt_new_table(struct symbol *s, uint addr_type);
static inline int rt_is_ip(rtable *tab)
{ return (tab->addr_type == NET_IP4) || (tab->addr_type == NET_IP6); }
static inline int rt_is_vpn(rtable *tab)
{ return (tab->addr_type == NET_VPN4) || (tab->addr_type == NET_VPN6); }
static inline int rt_is_roa(rtable *tab)
{ return (tab->addr_type == NET_ROA4) || (tab->addr_type == NET_ROA6); }
static inline int rt_is_flow(rtable *tab)
{ return (tab->addr_type == NET_FLOW4) || (tab->addr_type == NET_FLOW6); }
/* Default limit for ECMP next hops, defined in sysdep code */
extern const int rt_default_ecmp;
struct rt_show_data_rtable {
node n;
rtable *table;
struct channel *export_channel;
};
struct rt_show_data {
net_addr *addr;
list tables;
struct rt_show_data_rtable *tab; /* Iterator over table list */
struct rt_show_data_rtable *last_table; /* Last table in output */
struct fib_iterator fit; /* Iterator over networks in table */
struct f_trie_walk_state *walk_state; /* Iterator over networks in trie */
struct f_trie *walk_lock; /* Locked trie for walking */
int verbose, tables_defined_by;
const struct filter *filter;
struct proto *show_protocol;
struct proto *export_protocol;
struct channel *export_channel;
struct config *running_on_config;
struct krt_proto *kernel;
struct rt_export_hook *kernel_export_hook;
int export_mode, addr_mode, primary_only, filtered, stats;
int table_open; /* Iteration (fit) is open */
int trie_walk; /* Current table is iterated using trie */
int net_counter, rt_counter, show_counter, table_counter;
int net_counter_last, rt_counter_last, show_counter_last;
};
void rt_show(struct rt_show_data *);
struct rt_show_data_rtable * rt_show_add_table(struct rt_show_data *d, rtable *t);
/* Value of table definition mode in struct rt_show_data */
#define RSD_TDB_DEFAULT 0 /* no table specified */
#define RSD_TDB_INDIRECT 0 /* show route ... protocol P ... */
#define RSD_TDB_ALL RSD_TDB_SET /* show route ... table all ... */
#define RSD_TDB_DIRECT RSD_TDB_SET | RSD_TDB_NMN /* show route ... table X table Y ... */
#define RSD_TDB_SET 0x1 /* internal: show empty tables */
#define RSD_TDB_NMN 0x2 /* internal: need matching net */
/* Value of addr_mode */
#define RSD_ADDR_EQUAL 1 /* Exact query - show route <addr> */
#define RSD_ADDR_FOR 2 /* Longest prefix match - show route for <addr> */
#define RSD_ADDR_IN 3 /* Interval query - show route in <addr> */
/* Value of export_mode in struct rt_show_data */
#define RSEM_NONE 0 /* Export mode not used */
#define RSEM_PREEXPORT 1 /* Routes ready for export, before filtering */
#define RSEM_EXPORT 2 /* Routes accepted by export filter */
#define RSEM_NOEXPORT 3 /* Routes rejected by export filter */
#define RSEM_EXPORTED 4 /* Routes marked in export map */
/* Host entry: Resolve hook for recursive nexthops */
extern struct ea_class ea_gen_hostentry;
struct hostentry_adata {
adata ad;
struct hostentry *he;
u32 labels[0];
};
void
ea_set_hostentry(ea_list **to, struct rtable *dep, struct rtable *tab, ip_addr gw, ip_addr ll, u32 lnum, u32 labels[lnum]);
/*
struct hostentry * rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep);
void rta_apply_hostentry(rta *a, struct hostentry *he, u32 lnum, u32 labels[lnum]);
static inline void
rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr gw, ip_addr ll, u32 lnum, u32 labels[lnum])
{
rta_apply_hostentry(a, rt_get_hostentry(tab, gw, ll, dep), lnum, labels);
}
*/
/*
* Default protocol preferences
*/
#define DEF_PREF_DIRECT 240 /* Directly connected */
#define DEF_PREF_STATIC 200 /* Static route */
#define DEF_PREF_OSPF 150 /* OSPF intra-area, inter-area and type 1 external routes */
#define DEF_PREF_BABEL 130 /* Babel */
#define DEF_PREF_RIP 120 /* RIP */
#define DEF_PREF_BGP 100 /* BGP */
#define DEF_PREF_RPKI 100 /* RPKI */
#define DEF_PREF_INHERITED 10 /* Routes inherited from other routing daemons */
#define DEF_PREF_UNKNOWN 0 /* Routes with no preference set */
/*
* Route Origin Authorization
*/
#define ROA_UNKNOWN 0
#define ROA_VALID 1
#define ROA_INVALID 2
int net_roa_check(rtable *tab, const net_addr *n, u32 asn);
#endif

View File

@ -2,5 +2,6 @@ src := babel.c packets.c
obj := $(src-o-files)
$(all-daemon)
$(cf-local)
$(call proto-build,babel_build)
tests_objs := $(tests_objs) $(src-o-files)

View File

@ -37,6 +37,7 @@
#include <stdlib.h>
#include "babel.h"
#include "lib/macro.h"
#define LOG_PKT_AUTH(msg, args...) \
log_rl(&p->log_pkt_tbf, L_AUTH "%s: " msg, p->p.name, args)
@ -58,12 +59,14 @@ static void babel_update_cost(struct babel_neighbor *n);
static inline void babel_kick_timer(struct babel_proto *p);
static inline void babel_iface_kick_timer(struct babel_iface *ifa);
static struct ea_class ea_babel_metric, ea_babel_router_id, ea_babel_seqno;
/*
* Functions to maintain data structures
*/
static void
babel_init_entry(void *E)
babel_init_entry(struct fib *f UNUSED, void *E)
{
struct babel_entry *e = E;
@ -119,7 +122,7 @@ babel_get_source(struct babel_proto *p, struct babel_entry *e, u64 router_id)
}
static void
babel_expire_sources(struct babel_proto *p, struct babel_entry *e)
babel_expire_sources(struct babel_proto *p UNUSED, struct babel_entry *e)
{
struct babel_source *n, *nx;
btime now_ = current_time();
@ -129,7 +132,7 @@ babel_expire_sources(struct babel_proto *p, struct babel_entry *e)
if (n->expires && n->expires <= now_)
{
rem_node(NODE n);
sl_free(p->source_slab, n);
sl_free(n);
}
}
}
@ -174,7 +177,7 @@ babel_retract_route(struct babel_proto *p, struct babel_route *r)
}
static void
babel_flush_route(struct babel_proto *p, struct babel_route *r)
babel_flush_route(struct babel_proto *p UNUSED, struct babel_route *r)
{
DBG("Babel: Flush route %N router_id %lR neigh %I\n",
r->e->n.addr, r->router_id, r->neigh->addr);
@ -185,7 +188,7 @@ babel_flush_route(struct babel_proto *p, struct babel_route *r)
if (r->e->selected == r)
r->e->selected = NULL;
sl_free(p->route_slab, r);
sl_free(r);
}
static void
@ -336,13 +339,13 @@ found:
}
static void
babel_remove_seqno_request(struct babel_proto *p, struct babel_seqno_request *sr)
babel_remove_seqno_request(struct babel_proto *p UNUSED, struct babel_seqno_request *sr)
{
if (sr->nbr)
rem_node(&sr->nbr_node);
rem_node(NODE sr);
sl_free(p->seqno_slab, sr);
sl_free(sr);
}
static int
@ -640,37 +643,14 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
if (r)
{
rta a0 = {
.source = RTS_BABEL,
.scope = SCOPE_UNIVERSE,
.dest = RTD_UNICAST,
.pref = c->preference,
.from = r->neigh->addr,
.nh.gw = r->next_hop,
.nh.iface = r->neigh->ifa->iface,
.eattrs = alloca(sizeof(ea_list) + 3*sizeof(eattr)),
};
*a0.eattrs = (ea_list) { .count = 3 };
a0.eattrs->attrs[0] = (eattr) {
.id = EA_BABEL_METRIC,
.type = EAF_TYPE_INT,
.u.data = r->metric,
};
struct adata *ad = alloca(sizeof(struct adata) + sizeof(u64));
ad->length = sizeof(u64);
memcpy(ad->data, &(r->router_id), sizeof(u64));
a0.eattrs->attrs[1] = (eattr) {
.id = EA_BABEL_ROUTER_ID,
.type = EAF_TYPE_OPAQUE,
.u.ptr = ad,
};
a0.eattrs->attrs[2] = (eattr) {
.id = EA_BABEL_SEQNO,
.type = EAF_TYPE_INT,
.u.data = r->seqno,
struct nexthop_adata nhad = {
.nh = {
.gw = r->next_hop,
.iface = r->neigh->ifa->iface,
},
.ad = {
.length = sizeof nhad - sizeof nhad.ad,
},
};
/*
@ -679,7 +659,25 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
* have routing work.
*/
if (!neigh_find(&p->p, r->next_hop, r->neigh->ifa->iface, 0))
a0.nh.flags = RNF_ONLINK;
nhad.nh.flags = RNF_ONLINK;
struct {
ea_list l;
eattr a[7];
} eattrs = {
.l.count = ARRAY_SIZE(eattrs.a),
.a = {
EA_LITERAL_EMBEDDED(&ea_gen_preference, 0, c->preference),
EA_LITERAL_STORE_ADATA(&ea_gen_from, 0, &r->neigh->addr, sizeof(r->neigh->addr)),
EA_LITERAL_EMBEDDED(&ea_gen_source, 0, RTS_BABEL),
EA_LITERAL_STORE_ADATA(&ea_gen_nexthop, 0, nhad.ad.data, nhad.ad.length),
EA_LITERAL_EMBEDDED(&ea_babel_metric, 0, r->metric),
EA_LITERAL_STORE_ADATA(&ea_babel_router_id, 0, &r->router_id, sizeof(r->router_id)),
EA_LITERAL_EMBEDDED(&ea_babel_seqno, 0, r->seqno),
}
};
rta a0 = { .eattrs = &eattrs.l, };
rte e0 = {
.attrs = &a0,
@ -692,12 +690,11 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
else if (e->valid && (e->router_id != p->router_id))
{
/* Unreachable */
rta a0 = {
.source = RTS_BABEL,
.scope = SCOPE_UNIVERSE,
.dest = RTD_UNREACHABLE,
.pref = 1,
};
rta a0 = {};
ea_set_attr_u32(&a0.eattrs, &ea_gen_preference, 0, 1);
ea_set_attr_u32(&a0.eattrs, &ea_gen_source, 0, RTS_BABEL);
ea_set_dest(&a0.eattrs, 0, RTD_UNREACHABLE);
rte e0 = {
.attrs = &a0,
@ -862,14 +859,14 @@ babel_send_ihus(struct babel_iface *ifa)
}
static void
babel_send_hello(struct babel_iface *ifa)
babel_send_hello(struct babel_iface *ifa, uint interval)
{
struct babel_proto *p = ifa->proto;
union babel_msg msg = {};
msg.type = BABEL_TLV_HELLO;
msg.hello.seqno = ifa->hello_seqno++;
msg.hello.interval = ifa->cf->hello_interval;
msg.hello.interval = interval ?: ifa->cf->hello_interval;
TRACE(D_PACKETS, "Sending hello on %s with seqno %d interval %t",
ifa->ifname, msg.hello.seqno, (btime) msg.hello.interval);
@ -1577,7 +1574,7 @@ babel_iface_timer(timer *t)
if (now_ >= ifa->next_hello)
{
babel_send_hello(ifa);
babel_send_hello(ifa, 0);
ifa->next_hello += hello_period * (1 + (now_ - ifa->next_hello) / hello_period);
}
@ -1624,7 +1621,7 @@ babel_iface_start(struct babel_iface *ifa)
tm_start(ifa->timer, 100 MS);
ifa->up = 1;
babel_send_hello(ifa);
babel_send_hello(ifa, 0);
babel_send_wildcard_retraction(ifa);
babel_send_wildcard_request(ifa);
babel_send_update(ifa, 0); /* Full update */
@ -1919,7 +1916,7 @@ babel_reconfigure_ifaces(struct babel_proto *p, struct babel_config *cf)
struct babel_iface *ifa = babel_find_iface(p, iface);
struct babel_iface_config *ic = (void *) iface_patt_find(&cf->iface_list, iface, NULL);
if (ic && iface_is_valid(p, iface))
if (ic && !iface_is_valid(p, iface))
ic = NULL;
if (ifa && ic)
@ -2031,39 +2028,42 @@ static void
babel_get_route_info(rte *rte, byte *buf)
{
u64 rid = 0;
eattr *e = ea_find(rte->attrs->eattrs, EA_BABEL_ROUTER_ID);
eattr *e = ea_find(rte->attrs->eattrs, &ea_babel_router_id);
if (e)
memcpy(&rid, e->u.ptr->data, sizeof(u64));
buf += bsprintf(buf, " (%d/%d) [%lR]", rte->attrs->pref,
ea_get_int(rte->attrs->eattrs, EA_BABEL_METRIC, BABEL_INFINITY), rid);
buf += bsprintf(buf, " (%d/%d) [%lR]",
rt_get_preference(rte),
ea_get_int(rte->attrs->eattrs, &ea_babel_metric, BABEL_INFINITY), rid);
}
static int
babel_get_attr(const eattr *a, byte *buf, int buflen UNUSED)
static void
babel_router_id_format(const eattr *a, byte *buf, uint len)
{
switch (a->id)
{
case EA_BABEL_SEQNO:
return GA_FULL;
case EA_BABEL_METRIC:
bsprintf(buf, "metric: %d", a->u.data);
return GA_FULL;
case EA_BABEL_ROUTER_ID:
{
u64 rid = 0;
memcpy(&rid, a->u.ptr->data, sizeof(u64));
bsprintf(buf, "router_id: %lR", rid);
return GA_FULL;
}
default:
return GA_UNKNOWN;
}
u64 rid = 0;
memcpy(&rid, a->u.ptr->data, sizeof(u64));
bsnprintf(buf, len, "%lR", rid);
}
static struct ea_class ea_babel_metric = {
.name = "babel_metric",
.type = T_INT,
};
static struct ea_class ea_babel_router_id = {
.name = "babel_router_id",
.type = T_OPAQUE,
.readonly = 1,
.format = babel_router_id_format,
};
static struct ea_class ea_babel_seqno = {
.name = "babel_seqno",
.type = T_INT,
.readonly = 1,
};
void
babel_show_interfaces(struct proto *P, const char *iff)
{
@ -2262,9 +2262,13 @@ babel_kick_timer(struct babel_proto *p)
static int
babel_preexport(struct channel *c, struct rte *new)
{
struct rta *a = new->attrs;
if (new->src->proto != c->proto)
return 0;
/* Reject our own unreachable routes */
if ((a->dest == RTD_UNREACHABLE) && (new->src->proto == c->proto))
eattr *ea = ea_find(new->attrs->eattrs, &ea_gen_nexthop);
struct nexthop_adata *nhad = (void *) ea->u.ptr;
if (!NEXTHOP_IS_REACHABLE(nhad))
return -1;
return 0;
@ -2285,13 +2289,13 @@ babel_rt_notify(struct proto *P, struct channel *c UNUSED, const net_addr *net,
{
/* Update */
uint rt_seqno;
uint rt_metric = ea_get_int(new->attrs->eattrs, EA_BABEL_METRIC, 0);
uint rt_metric = ea_get_int(new->attrs->eattrs, &ea_babel_metric, 0);
u64 rt_router_id = 0;
if (new->src->proto == P)
{
rt_seqno = ea_find(new->attrs->eattrs, EA_BABEL_SEQNO)->u.data;
eattr *e = ea_find(new->attrs->eattrs, EA_BABEL_ROUTER_ID);
rt_seqno = ea_get_int(new->attrs->eattrs, &ea_babel_seqno, 0);
eattr *e = ea_find(new->attrs->eattrs, &ea_babel_router_id);
if (e)
memcpy(&rt_router_id, e->u.ptr->data, sizeof(u64));
}
@ -2342,16 +2346,16 @@ babel_rt_notify(struct proto *P, struct channel *c UNUSED, const net_addr *net,
static int
babel_rte_better(struct rte *new, struct rte *old)
{
uint new_metric = ea_find(new->attrs->eattrs, EA_BABEL_SEQNO)->u.data;
uint old_metric = ea_find(old->attrs->eattrs, EA_BABEL_SEQNO)->u.data;
uint new_metric = ea_get_int(new->attrs->eattrs, &ea_babel_metric, BABEL_INFINITY);
uint old_metric = ea_get_int(old->attrs->eattrs, &ea_babel_metric, BABEL_INFINITY);
return new_metric < old_metric;
}
static u32
babel_rte_igp_metric(struct rte *rt)
babel_rte_igp_metric(const rte *rt)
{
return ea_get_int(rt->attrs->eattrs, EA_BABEL_METRIC, BABEL_INFINITY);
return ea_get_int(rt->attrs->eattrs, &ea_babel_metric, BABEL_INFINITY);
}
@ -2435,6 +2439,11 @@ babel_iface_shutdown(struct babel_iface *ifa)
{
if (ifa->sk)
{
/*
* Retract all our routes and lower the hello interval so peers' neighbour
* state expires quickly
*/
babel_send_hello(ifa, BABEL_MIN_INTERVAL);
babel_send_wildcard_retraction(ifa);
babel_send_queue(ifa);
}
@ -2479,11 +2488,9 @@ babel_reconfigure(struct proto *P, struct proto_config *CF)
return 1;
}
struct protocol proto_babel = {
.name = "Babel",
.template = "babel%d",
.class = PROTOCOL_BABEL,
.preference = DEF_PREF_BABEL,
.channel_mask = NB_IP | NB_IP6_SADR,
.proto_size = sizeof(struct babel_proto),
@ -2495,5 +2502,16 @@ struct protocol proto_babel = {
.shutdown = babel_shutdown,
.reconfigure = babel_reconfigure,
.get_route_info = babel_get_route_info,
.get_attr = babel_get_attr
};
void
babel_build(void)
{
proto_build(&proto_babel);
EA_REGISTER_ALL(
&ea_babel_metric,
&ea_babel_router_id,
&ea_babel_seqno
);
}

View File

@ -16,7 +16,7 @@
#include "nest/bird.h"
#include "nest/cli.h"
#include "nest/iface.h"
#include "nest/route.h"
#include "nest/rt.h"
#include "nest/protocol.h"
#include "nest/locks.h"
#include "nest/password.h"
@ -26,10 +26,6 @@
#include "lib/string.h"
#include "lib/timer.h"
#define EA_BABEL_METRIC EA_CODE(PROTOCOL_BABEL, 0)
#define EA_BABEL_ROUTER_ID EA_CODE(PROTOCOL_BABEL, 1)
#define EA_BABEL_SEQNO EA_CODE(PROTOCOL_BABEL, 2)
#define BABEL_MAGIC 42
#define BABEL_VERSION 2
#define BABEL_PORT 6696

View File

@ -24,7 +24,7 @@ CF_DECLS
CF_KEYWORDS(BABEL, INTERFACE, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT,
TYPE, WIRED, WIRELESS, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK, LINK,
NEXT, HOP, IPV4, IPV6, BABEL_METRIC, SHOW, INTERFACES, NEIGHBORS,
NEXT, HOP, IPV4, IPV6, SHOW, INTERFACES, NEIGHBORS,
ENTRIES, RANDOMIZE, ROUTER, ID, AUTHENTICATION, NONE, MAC, PERMISSIVE)
CF_GRAMMAR
@ -163,8 +163,6 @@ babel_iface_opt_list:
babel_iface:
babel_iface_start iface_patt_list_nopx babel_iface_opt_list babel_iface_finish;
dynamic_attr: BABEL_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_BABEL_METRIC); } ;
CF_CLI_HELP(SHOW BABEL, ..., [[Show information about Babel protocol]]);
CF_CLI(SHOW BABEL INTERFACES, optproto opttext, [<name>] [\"<interface>\"], [[Show information about Babel interfaces]])

View File

@ -1318,7 +1318,6 @@ babel_send_to(struct babel_iface *ifa, ip_addr dest)
static uint
babel_write_queue(struct babel_iface *ifa, list *queue)
{
struct babel_proto *p = ifa->proto;
struct babel_write_state state = { .next_hop_ip6 = ifa->addr };
if (EMPTY_LIST(*queue))
@ -1346,7 +1345,7 @@ babel_write_queue(struct babel_iface *ifa, list *queue)
pos += len;
rem_node(NODE msg);
sl_free(p->msg_slab, msg);
sl_free(msg);
}
pos += babel_auth_add_tlvs(ifa, (struct babel_tlv *) pos, end - pos);
@ -1507,13 +1506,13 @@ babel_process_packet(struct babel_iface *ifa,
else if (res == PARSE_IGNORE)
{
DBG("Babel: Ignoring TLV of type %d\n", tlv->type);
sl_free(p->msg_slab, msg);
sl_free(msg);
}
else /* PARSE_ERROR */
{
LOG_PKT("Bad TLV from %I via %s type %d pos %d - parse error",
saddr, ifa->iface->name, tlv->type, (int) ((byte *)tlv - (byte *)pkt));
sl_free(p->msg_slab, msg);
sl_free(msg);
break;
}
}
@ -1525,7 +1524,7 @@ babel_process_packet(struct babel_iface *ifa,
if (tlv_data[msg->msg.type].handle_tlv)
tlv_data[msg->msg.type].handle_tlv(&msg->msg, ifa);
rem_node(NODE msg);
sl_free(p->msg_slab, msg);
sl_free(msg);
}
}
@ -2011,7 +2010,7 @@ babel_auth_sign(struct babel_iface *ifa, ip_addr dest)
}
DBG("Added MAC signatures (%d bytes) on ifa %s for dest %I\n",
tot_len, ifa->ifname, dest);
pos - (pkt + len), ifa->ifname, dest);
return pos - (pkt + len);
}

View File

@ -2,5 +2,6 @@ src := bfd.c io.c packets.c
obj := $(src-o-files)
$(all-daemon)
$(cf-local)
$(call proto-build,bfd_build)
tests_objs := $(tests_objs) $(src-o-files)
tests_objs := $(tests_objs) $(src-o-files)

View File

@ -113,8 +113,8 @@
#define HASH_IP_EQ(a1,n1,a2,n2) ipa_equal(a1, a2) && n1 == n2
#define HASH_IP_FN(a,n) ipa_hash(a) ^ u32_hash(n)
static list bfd_proto_list;
static list bfd_wait_list;
static list STATIC_LIST_INIT(bfd_proto_list);
static list STATIC_LIST_INIT(bfd_wait_list);
const char *bfd_state_names[] = { "AdminDown", "Down", "Init", "Up" };
@ -508,7 +508,7 @@ bfd_remove_session(struct bfd_proto *p, struct bfd_session *s)
HASH_REMOVE(p->session_hash_id, HASH_ID, s);
HASH_REMOVE(p->session_hash_ip, HASH_IP, s);
sl_free(p->session_slab, s);
sl_free(s);
TRACE(D_EVENTS, "Session to %I removed", ip);
@ -582,6 +582,9 @@ bfd_get_iface(struct bfd_proto *p, ip_addr local, struct iface *iface)
ifa->sk = bfd_open_tx_sk(p, local, iface);
ifa->uc = 1;
if (cf->strict_bind)
ifa->rx = bfd_open_rx_sk_bound(p, local, iface);
add_tail(&p->iface_list, &ifa->n);
return ifa;
@ -599,6 +602,12 @@ bfd_free_iface(struct bfd_iface *ifa)
rfree(ifa->sk);
}
if (ifa->rx)
{
sk_stop(ifa->rx);
rfree(ifa->rx);
}
rem_node(&ifa->n);
mb_free(ifa);
}
@ -998,13 +1007,6 @@ bfd_notify_init(struct bfd_proto *p)
* BFD protocol glue
*/
void
bfd_init_all(void)
{
init_list(&bfd_proto_list);
init_list(&bfd_wait_list);
}
static struct proto *
bfd_init(struct proto_config *c)
{
@ -1038,17 +1040,20 @@ bfd_start(struct proto *P)
birdloop_enter(p->loop);
if (cf->accept_ipv4 && cf->accept_direct)
p->rx4_1 = bfd_open_rx_sk(p, 0, SK_IPV4);
if (!cf->strict_bind)
{
if (cf->accept_ipv4 && cf->accept_direct)
p->rx4_1 = bfd_open_rx_sk(p, 0, SK_IPV4);
if (cf->accept_ipv4 && cf->accept_multihop)
p->rx4_m = bfd_open_rx_sk(p, 1, SK_IPV4);
if (cf->accept_ipv4 && cf->accept_multihop)
p->rx4_m = bfd_open_rx_sk(p, 1, SK_IPV4);
if (cf->accept_ipv6 && cf->accept_direct)
p->rx6_1 = bfd_open_rx_sk(p, 0, SK_IPV6);
if (cf->accept_ipv6 && cf->accept_direct)
p->rx6_1 = bfd_open_rx_sk(p, 0, SK_IPV6);
if (cf->accept_ipv6 && cf->accept_multihop)
p->rx6_m = bfd_open_rx_sk(p, 1, SK_IPV6);
if (cf->accept_ipv6 && cf->accept_multihop)
p->rx6_m = bfd_open_rx_sk(p, 1, SK_IPV6);
}
birdloop_leave(p->loop);
@ -1102,7 +1107,8 @@ bfd_reconfigure(struct proto *P, struct proto_config *c)
if ((new->accept_ipv4 != old->accept_ipv4) ||
(new->accept_ipv6 != old->accept_ipv6) ||
(new->accept_direct != old->accept_direct) ||
(new->accept_multihop != old->accept_multihop))
(new->accept_multihop != old->accept_multihop) ||
(new->strict_bind != old->strict_bind))
return 0;
birdloop_mask_wakeups(p->loop);
@ -1177,7 +1183,6 @@ bfd_show_sessions(struct proto *P)
struct protocol proto_bfd = {
.name = "BFD",
.template = "bfd%d",
.class = PROTOCOL_BFD,
.proto_size = sizeof(struct bfd_proto),
.config_size = sizeof(struct bfd_config),
.init = bfd_init,
@ -1186,3 +1191,9 @@ struct protocol proto_bfd = {
.reconfigure = bfd_reconfigure,
.copy_config = bfd_copy_config,
};
void
bfd_build(void)
{
proto_build(&proto_bfd);
}

View File

@ -13,7 +13,7 @@
#include "nest/cli.h"
#include "nest/iface.h"
#include "nest/protocol.h"
#include "nest/route.h"
#include "nest/rt.h"
#include "nest/password.h"
#include "conf/conf.h"
#include "lib/hash.h"
@ -47,6 +47,7 @@ struct bfd_config
u8 accept_ipv6;
u8 accept_direct;
u8 accept_multihop;
u8 strict_bind;
};
struct bfd_iface_config
@ -116,6 +117,7 @@ struct bfd_iface
struct bfd_proto *bfd;
sock *sk;
sock *rx;
u32 uc;
u8 changed;
};
@ -221,6 +223,7 @@ void bfd_show_sessions(struct proto *P);
/* packets.c */
void bfd_send_ctl(struct bfd_proto *p, struct bfd_session *s, int final);
sock * bfd_open_rx_sk(struct bfd_proto *p, int multihop, int inet_version);
sock * bfd_open_rx_sk_bound(struct bfd_proto *p, ip_addr local, struct iface *ifa);
sock * bfd_open_tx_sk(struct bfd_proto *p, ip_addr local, struct iface *ifa);

View File

@ -23,7 +23,8 @@ CF_DECLS
CF_KEYWORDS(BFD, MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE,
INTERFACE, MULTIHOP, NEIGHBOR, DEV, LOCAL, AUTHENTICATION,
NONE, SIMPLE, METICULOUS, KEYED, MD5, SHA1, IPV4, IPV6, DIRECT)
NONE, SIMPLE, METICULOUS, KEYED, MD5, SHA1, IPV4, IPV6, DIRECT,
STRICT, BIND)
%type <iface> bfd_neigh_iface
%type <a> bfd_neigh_local
@ -48,6 +49,7 @@ bfd_proto_item:
| INTERFACE bfd_iface
| MULTIHOP bfd_multihop
| NEIGHBOR bfd_neighbor
| STRICT BIND bool { BFD_CFG->strict_bind = $3; }
;
bfd_proto_opts:

View File

@ -482,6 +482,8 @@ birdloop_main(void *arg)
birdloop_set_current(loop);
tmp_init(loop->pool);
pthread_mutex_lock(&loop->mutex);
while (1)
{

View File

@ -366,7 +366,9 @@ bfd_rx_hook(sock *sk, uint len)
if (ps > BFD_STATE_DOWN)
DROP("invalid init state", ps);
uint ifindex = (sk->sport == BFD_CONTROL_PORT) ? sk->lifindex : 0;
uint ifindex = (sk->sport == BFD_CONTROL_PORT) ?
(sk->iface ? sk->iface->index : sk->lifindex) :
0;
s = bfd_find_session_by_addr(p, sk->faddr, ifindex);
/* FIXME: better session matching and message */
@ -438,6 +440,38 @@ bfd_open_rx_sk(struct bfd_proto *p, int multihop, int af)
return NULL;
}
sock *
bfd_open_rx_sk_bound(struct bfd_proto *p, ip_addr local, struct iface *ifa)
{
sock *sk = sk_new(p->tpool);
sk->type = SK_UDP;
sk->saddr = local;
sk->sport = ifa ? BFD_CONTROL_PORT : BFD_MULTI_CTL_PORT;
sk->iface = ifa;
sk->vrf = p->p.vrf;
sk->data = p;
sk->rbsize = BFD_MAX_LEN;
sk->rx_hook = bfd_rx_hook;
sk->err_hook = bfd_err_hook;
/* TODO: configurable ToS and priority */
sk->tos = IP_PREC_INTERNET_CONTROL;
sk->priority = sk_priority_control;
sk->flags = SKF_THREAD | SKF_BIND | (ifa ? SKF_TTL_RX : 0);
if (sk_open(sk) < 0)
goto err;
sk_start(sk);
return sk;
err:
sk_log_error(sk, p->p.name);
rfree(sk);
return NULL;
}
sock *
bfd_open_tx_sk(struct bfd_proto *p, ip_addr local, struct iface *ifa)
{

View File

@ -2,5 +2,6 @@ src := attrs.c bgp.c packets.c
obj := $(src-o-files)
$(all-daemon)
$(cf-local)
$(call proto-build,bgp_build)
tests_objs := $(tests_objs) $(src-o-files)
tests_objs := $(tests_objs) $(src-o-files)

File diff suppressed because it is too large Load Diff

View File

@ -101,6 +101,7 @@
* RFC 8203 - BGP Administrative Shutdown Communication
* RFC 8212 - Default EBGP Route Propagation Behavior without Policies
* RFC 8654 - Extended Message Support for BGP
* RFC 9117 - Revised Validation Procedure for BGP Flow Specifications
* draft-ietf-idr-ext-opt-param-07
* draft-uttaro-idr-bgp-persistence-04
* draft-walton-bgp-hostname-capability-02
@ -113,7 +114,7 @@
#include "nest/bird.h"
#include "nest/iface.h"
#include "nest/protocol.h"
#include "nest/route.h"
#include "nest/rt.h"
#include "nest/cli.h"
#include "nest/locks.h"
#include "conf/conf.h"
@ -125,9 +126,7 @@
#include "bgp.h"
struct linpool *bgp_linpool; /* Global temporary pool */
struct linpool *bgp_linpool2; /* Global temporary pool for bgp_rt_notify() */
static list bgp_sockets; /* Global list of listening sockets */
static list STATIC_LIST_INIT(bgp_sockets); /* Global list of listening sockets */
static void bgp_connect(struct bgp_proto *p);
@ -157,16 +156,17 @@ bgp_open(struct bgp_proto *p)
ip_addr addr = p->cf->strict_bind ? p->cf->local_ip :
(p->ipv4 ? IPA_NONE4 : IPA_NONE6);
uint port = p->cf->local_port;
/* FIXME: Add some global init? */
if (!bgp_linpool)
init_list(&bgp_sockets);
uint flags = p->cf->free_bind ? SKF_FREEBIND : 0;
uint flag_mask = SKF_FREEBIND;
/* We assume that cf->iface is defined iff cf->local_ip is link-local */
WALK_LIST(bs, bgp_sockets)
if (ipa_equal(bs->sk->saddr, addr) && (bs->sk->sport == port) &&
(bs->sk->iface == ifa) && (bs->sk->vrf == p->p.vrf))
if (ipa_equal(bs->sk->saddr, addr) &&
(bs->sk->sport == port) &&
(bs->sk->iface == ifa) &&
(bs->sk->vrf == p->p.vrf) &&
((bs->sk->flags & flag_mask) == flags))
{
bs->uc++;
p->sock = bs;
@ -180,7 +180,7 @@ bgp_open(struct bgp_proto *p)
sk->sport = port;
sk->iface = ifa;
sk->vrf = p->p.vrf;
sk->flags = 0;
sk->flags = flags;
sk->tos = IP_PREC_INTERNET_CONTROL;
sk->rbsize = BGP_RX_BUFFER_SIZE;
sk->tbsize = BGP_TX_BUFFER_SIZE;
@ -198,12 +198,6 @@ bgp_open(struct bgp_proto *p)
add_tail(&bgp_sockets, &bs->n);
if (!bgp_linpool)
{
bgp_linpool = lp_new_default(proto_pool);
bgp_linpool2 = lp_new_default(proto_pool);
}
return 0;
err:
@ -232,15 +226,6 @@ bgp_close(struct bgp_proto *p)
rfree(bs->sk);
rem_node(&bs->n);
mb_free(bs);
if (!EMPTY_LIST(bgp_sockets))
return;
rfree(bgp_linpool);
bgp_linpool = NULL;
rfree(bgp_linpool2);
bgp_linpool2 = NULL;
}
static inline int
@ -775,25 +760,25 @@ bgp_handle_graceful_restart(struct bgp_proto *p)
{
case BGP_GRS_NONE:
c->gr_active = BGP_GRS_ACTIVE;
rt_refresh_begin(c->c.table, &c->c);
rt_refresh_begin(c->c.table, &c->c.in_req);
break;
case BGP_GRS_ACTIVE:
rt_refresh_end(c->c.table, &c->c);
rt_refresh_begin(c->c.table, &c->c);
rt_refresh_end(c->c.table, &c->c.in_req);
rt_refresh_begin(c->c.table, &c->c.in_req);
break;
case BGP_GRS_LLGR:
rt_refresh_begin(c->c.table, &c->c);
rt_modify_stale(c->c.table, &c->c);
rt_refresh_begin(c->c.table, &c->c.in_req);
rt_modify_stale(c->c.table, &c->c.in_req);
break;
}
}
else
{
/* Just flush the routes */
rt_refresh_begin(c->c.table, &c->c);
rt_refresh_end(c->c.table, &c->c);
rt_refresh_begin(c->c.table, &c->c.in_req);
rt_refresh_end(c->c.table, &c->c.in_req);
}
/* Reset bucket and prefix tables */
@ -834,7 +819,7 @@ bgp_graceful_restart_done(struct bgp_channel *c)
BGP_TRACE(D_EVENTS, "Neighbor graceful restart done");
tm_stop(c->stale_timer);
rt_refresh_end(c->c.table, &c->c);
rt_refresh_end(c->c.table, &c->c.in_req);
}
/**
@ -876,7 +861,7 @@ bgp_graceful_restart_timeout(timer *t)
/* Channel is in GR, and supports LLGR -> start LLGR */
c->gr_active = BGP_GRS_LLGR;
tm_start(c->stale_timer, c->stale_time S);
rt_modify_stale(c->c.table, &c->c);
rt_modify_stale(c->c.table, &c->c.in_req);
}
}
else
@ -914,10 +899,10 @@ bgp_refresh_begin(struct bgp_channel *c)
{ log(L_WARN "%s: BEGIN-OF-RR received before END-OF-RIB, ignoring", p->p.name); return; }
c->load_state = BFS_REFRESHING;
rt_refresh_begin(c->c.table, &c->c);
rt_refresh_begin(c->c.table, &c->c.in_req);
if (c->c.in_table)
rt_refresh_begin(c->c.in_table, &c->c);
rt_refresh_begin(c->c.in_table, &c->c.in_req);
}
/**
@ -938,7 +923,7 @@ bgp_refresh_end(struct bgp_channel *c)
{ log(L_WARN "%s: END-OF-RR received without prior BEGIN-OF-RR, ignoring", p->p.name); return; }
c->load_state = BFS_NONE;
rt_refresh_end(c->c.table, &c->c);
rt_refresh_end(c->c.table, &c->c.in_req);
if (c->c.in_table)
rt_prune_sync(c->c.in_table, 0);
@ -1736,6 +1721,9 @@ bgp_channel_init(struct channel *C, struct channel_config *CF)
if (cf->igp_table_ip6)
c->igp_table_ip6 = cf->igp_table_ip6->table;
if (cf->base_table)
c->base_table = cf->base_table->table;
}
static int
@ -1751,6 +1739,12 @@ bgp_channel_start(struct channel *C)
if (c->igp_table_ip6)
rt_lock_table(c->igp_table_ip6);
if (c->base_table)
{
rt_lock_table(c->base_table);
rt_flowspec_link(c->base_table, c->c.table);
}
c->pool = p->p.pool; // XXXX
bgp_init_bucket_table(c);
bgp_init_prefix_table(c);
@ -1835,6 +1829,12 @@ bgp_channel_cleanup(struct channel *C)
if (c->igp_table_ip6)
rt_unlock_table(c->igp_table_ip6);
if (c->base_table)
{
rt_flowspec_unlink(c->base_table, c->c.table);
rt_unlock_table(c->base_table);
}
c->index = 0;
/* Cleanup rest of bgp_channel starting at pool field */
@ -1882,6 +1882,25 @@ bgp_default_igp_table(struct bgp_config *cf, struct bgp_channel_config *cc, u32
cf_error("Undefined IGP table");
}
static struct rtable_config *
bgp_default_base_table(struct bgp_config *cf, struct bgp_channel_config *cc)
{
/* Expected table type */
u32 type = (cc->afi == BGP_AF_FLOW4) ? NET_IP4 : NET_IP6;
/* First, try appropriate IP channel */
u32 afi2 = BGP_AF(BGP_AFI(cc->afi), BGP_SAFI_UNICAST);
struct bgp_channel_config *cc2 = bgp_find_channel_config(cf, afi2);
if (cc2 && (cc2->c.table->addr_type == type))
return cc2->c.table;
/* Last, try default table of given type */
struct rtable_config *tab = cf->c.global->def_tables[type];
if (tab)
return tab;
cf_error("Undefined base table");
}
void
bgp_postconfig(struct proto_config *CF)
@ -2026,6 +2045,14 @@ bgp_postconfig(struct proto_config *CF)
cf_error("Mismatched IGP table type");
}
/* Default value of base table */
if ((BGP_SAFI(cc->afi) == BGP_SAFI_FLOW) && cc->validate && !cc->base_table)
cc->base_table = bgp_default_base_table(cf, cc);
if (cc->base_table && !cc->base_table->trie_used)
cf_error("Flowspec validation requires base table (%s) with trie",
cc->base_table->name);
if (cf->multihop && (cc->gw_mode == GW_DIRECT))
cf_error("Multihop BGP cannot use direct gateway mode");
@ -2094,7 +2121,7 @@ bgp_reconfigure(struct proto *P, struct proto_config *CF)
return same;
}
#define IGP_TABLE(cf, sym) ((cf)->igp_table_##sym ? (cf)->igp_table_##sym ->table : NULL )
#define TABLE(cf, NAME) ((cf)->NAME ? (cf)->NAME->table : NULL )
static int
bgp_channel_reconfigure(struct channel *C, struct channel_config *CC, int *import_changed, int *export_changed)
@ -2105,6 +2132,7 @@ bgp_channel_reconfigure(struct channel *C, struct channel_config *CC, int *impor
struct bgp_channel_config *old = c->cf;
if ((new->secondary != old->secondary) ||
(new->validate != old->validate) ||
(new->gr_able != old->gr_able) ||
(new->llgr_able != old->llgr_able) ||
(new->llgr_time != old->llgr_time) ||
@ -2112,8 +2140,9 @@ bgp_channel_reconfigure(struct channel *C, struct channel_config *CC, int *impor
(new->add_path != old->add_path) ||
(new->import_table != old->import_table) ||
(new->export_table != old->export_table) ||
(IGP_TABLE(new, ip4) != IGP_TABLE(old, ip4)) ||
(IGP_TABLE(new, ip6) != IGP_TABLE(old, ip6)))
(TABLE(new, igp_table_ip4) != TABLE(old, igp_table_ip4)) ||
(TABLE(new, igp_table_ip6) != TABLE(old, igp_table_ip6)) ||
(TABLE(new, base_table) != TABLE(old, base_table)))
return 0;
if (new->mandatory && !old->mandatory && (C->channel_state != CS_UP))
@ -2438,6 +2467,9 @@ bgp_show_proto_info(struct proto *P)
else
cli_msg(-1006, " Neighbor address: %I%J", p->remote_ip, p->cf->iface);
if ((p->conn == &p->outgoing_conn) && (p->cf->remote_port != BGP_PORT))
cli_msg(-1006, " Neighbor port: %u", p->cf->remote_port);
cli_msg(-1006, " Neighbor AS: %u", p->remote_as);
cli_msg(-1006, " Local AS: %u", p->cf->local_as);
@ -2527,6 +2559,9 @@ bgp_show_proto_info(struct proto *P)
if (c->igp_table_ip6)
cli_msg(-1006, " IGP IPv6 table: %s", c->igp_table_ip6->name);
if (c->base_table)
cli_msg(-1006, " Base table: %s", c->base_table->name);
}
}
}
@ -2544,7 +2579,6 @@ struct channel_class channel_bgp = {
struct protocol proto_bgp = {
.name = "BGP",
.template = "bgp%d",
.class = PROTOCOL_BGP,
.preference = DEF_PREF_BGP,
.channel_mask = NB_IP | NB_VPN | NB_FLOW,
.proto_size = sizeof(struct bgp_proto),
@ -2556,7 +2590,12 @@ struct protocol proto_bgp = {
.reconfigure = bgp_reconfigure,
.copy_config = bgp_copy_config,
.get_status = bgp_get_status,
.get_attr = bgp_get_attr,
.get_route_info = bgp_get_route_info,
.show_proto_info = bgp_show_proto_info
};
void bgp_build(void)
{
proto_build(&proto_bgp);
bgp_register_attrs();
}

View File

@ -14,13 +14,12 @@
#include <stdint.h>
#include <setjmp.h>
#include "nest/bird.h"
#include "nest/route.h"
#include "nest/rt.h"
#include "nest/bfd.h"
//#include "lib/lists.h"
#include "lib/hash.h"
#include "lib/socket.h"
struct linpool;
struct eattr;
@ -86,6 +85,7 @@ struct bgp_config {
int peer_type; /* Internal or external BGP (BGP_PT_*, optional) */
int multihop; /* Number of hops if multihop */
int strict_bind; /* Bind listening socket to local address */
int free_bind; /* Bind listening socket with SKF_FREEBIND */
int ttl_security; /* Enable TTL security [RFC 5082] */
int compare_path_lengths; /* Use path lengths when selecting best route */
int med_metric; /* Compare MULTI_EXIT_DISC even between routes from differen ASes */
@ -146,6 +146,7 @@ struct bgp_channel_config {
u8 mandatory; /* Channel is mandatory in capability negotiation */
u8 gw_mode; /* How we compute route gateway from next_hop attr, see GW_* */
u8 secondary; /* Accept also non-best routes (i.e. RA_ACCEPTED) */
u8 validate; /* Validate Flowspec per RFC 8955 (6) */
u8 gr_able; /* Allow full graceful restart for the channel */
u8 llgr_able; /* Allow full long-lived GR for the channel */
uint llgr_time; /* Long-lived graceful restart stale time */
@ -159,6 +160,7 @@ struct bgp_channel_config {
struct rtable_config *igp_table_ip4; /* Table for recursive IPv4 next hop lookups */
struct rtable_config *igp_table_ip6; /* Table for recursive IPv6 next hop lookups */
struct rtable_config *base_table; /* Base table for Flowspec validation */
};
#define BGP_PT_INTERNAL 1
@ -344,6 +346,7 @@ struct bgp_channel {
rtable *igp_table_ip4; /* Table for recursive IPv4 next hop lookups */
rtable *igp_table_ip6; /* Table for recursive IPv6 next hop lookups */
rtable *base_table; /* Base table for Flowspec validation */
/* Rest are zeroed when down */
pool *pool;
@ -400,7 +403,7 @@ struct bgp_export_state {
int mpls;
u32 attrs_seen[1];
uint err_withdraw;
uint err_reject;
uint local_next_hop;
};
@ -426,6 +429,7 @@ struct bgp_parse_state {
int as4_session;
int add_path;
int mpls;
int reach_nlri_step;
u32 attrs_seen[256/32];
@ -452,7 +456,6 @@ struct bgp_parse_state {
uint err_subcode;
jmp_buf err_jmpbuf;
struct hostentry *hostentry;
adata *mpls_labels;
/* Cached state for bgp_rte_update() */
@ -493,9 +496,6 @@ bgp_parse_error(struct bgp_parse_state *s, uint subcode)
longjmp(s->err_jmpbuf, 1);
}
extern struct linpool *bgp_linpool;
extern struct linpool *bgp_linpool2;
void bgp_start_timer(timer *t, uint value);
void bgp_check_config(struct bgp_config *c);
@ -519,7 +519,9 @@ struct rte_source *bgp_get_source(struct bgp_proto *p, u32 path_id);
static inline int
rta_resolvable(rta *a)
{
return a->dest == RTD_UNICAST;
eattr *nhea = ea_find(a->eattrs, &ea_gen_nexthop);
struct nexthop_adata *nhad = (void *) nhea->u.ptr;
return NEXTHOP_IS_REACHABLE(nhad) || (nhad->dest != RTD_UNREACHABLE);
}
@ -537,34 +539,13 @@ rta_resolvable(rta *a)
/* attrs.c */
static inline eattr *
bgp_find_attr(ea_list *attrs, uint code)
{
return ea_find(attrs, EA_CODE(PROTOCOL_BGP, code));
}
eattr *
bgp_set_attr(ea_list **attrs, struct linpool *pool, uint code, uint flags, uintptr_t val);
bgp_find_attr(ea_list *attrs, uint code);
static inline void
bgp_set_attr_u32(ea_list **to, struct linpool *pool, uint code, uint flags, u32 val)
{ bgp_set_attr(to, pool, code, flags, (uintptr_t) val); }
static inline void
bgp_set_attr_ptr(ea_list **to, struct linpool *pool, uint code, uint flags, const struct adata *val)
{ bgp_set_attr(to, pool, code, flags, (uintptr_t) val); }
static inline void
bgp_set_attr_data(ea_list **to, struct linpool *pool, uint code, uint flags, void *data, uint len)
{
struct adata *a = lp_alloc_adata(pool, len);
bmemcpy(a->data, data, len);
bgp_set_attr(to, pool, code, flags, (uintptr_t) a);
}
static inline void
bgp_unset_attr(ea_list **to, struct linpool *pool, uint code)
{ eattr *e = bgp_set_attr(to, pool, code, 0, 0); e->type = EAF_TYPE_UNDEF; }
void bgp_set_attr_u32(ea_list **to, uint code, uint flags, u32 val);
void bgp_set_attr_ptr(ea_list **to, uint code, uint flags, const struct adata *ad);
void bgp_set_attr_data(ea_list **to, uint code, uint flags, void *data, uint len);
void bgp_unset_attr(ea_list **to, uint code);
int bgp_encode_mp_reach_mrt(struct bgp_write_state *s, eattr *a, byte *buf, uint size);
@ -586,26 +567,27 @@ int bgp_rte_better(struct rte *, struct rte *);
int bgp_rte_mergable(rte *pri, rte *sec);
int bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best);
struct rte *bgp_rte_modify_stale(struct rte *r, struct linpool *pool);
u32 bgp_rte_igp_metric(struct rte *);
u32 bgp_rte_igp_metric(const rte *);
void bgp_rt_notify(struct proto *P, struct channel *C, const net_addr *n, rte *new, const rte *old);
int bgp_preexport(struct channel *, struct rte *);
int bgp_get_attr(const struct eattr *e, byte *buf, int buflen);
void bgp_get_route_info(struct rte *, byte *);
int bgp_total_aigp_metric_(rta *a, u64 *metric, const struct adata **ad);
int bgp_total_aigp_metric_(const rte *e, u64 *metric, const struct adata **ad);
#define BGP_AIGP_METRIC 1
#define BGP_AIGP_MAX U64(0xffffffffffffffff)
static inline u64
bgp_total_aigp_metric(rta *a)
bgp_total_aigp_metric(const rte *e)
{
u64 metric = BGP_AIGP_MAX;
const struct adata *ad;
bgp_total_aigp_metric_(a, &metric, &ad);
bgp_total_aigp_metric_(e, &metric, &ad);
return metric;
}
void bgp_register_attrs(void);
/* packets.c */
@ -642,26 +624,31 @@ void bgp_update_next_hop(struct bgp_export_state *s, eattr *a, ea_list **to);
#define BAF_DECODE_FLAGS 0x0100 /* Private flag - attribute flags are handled by the decode hook */
#define BA_ORIGIN 0x01 /* RFC 4271 */ /* WM */
#define BA_AS_PATH 0x02 /* WM */
#define BA_NEXT_HOP 0x03 /* WM */
#define BA_MULTI_EXIT_DISC 0x04 /* ON */
#define BA_LOCAL_PREF 0x05 /* WD */
#define BA_ATOMIC_AGGR 0x06 /* WD */
#define BA_AGGREGATOR 0x07 /* OT */
#define BA_COMMUNITY 0x08 /* RFC 1997 */ /* OT */
#define BA_ORIGINATOR_ID 0x09 /* RFC 4456 */ /* ON */
#define BA_CLUSTER_LIST 0x0a /* RFC 4456 */ /* ON */
#define BA_MP_REACH_NLRI 0x0e /* RFC 4760 */
#define BA_MP_UNREACH_NLRI 0x0f /* RFC 4760 */
#define BA_EXT_COMMUNITY 0x10 /* RFC 4360 */
#define BA_AS4_PATH 0x11 /* RFC 6793 */
#define BA_AS4_AGGREGATOR 0x12 /* RFC 6793 */
#define BA_AIGP 0x1a /* RFC 7311 */
#define BA_LARGE_COMMUNITY 0x20 /* RFC 8092 */
enum bgp_attr_id {
BA_ORIGIN = 0x01, /* RFC 4271 */ /* WM */
BA_AS_PATH = 0x02, /* WM */
BA_NEXT_HOP = 0x03, /* WM */
BA_MULTI_EXIT_DISC = 0x04, /* ON */
BA_LOCAL_PREF = 0x05, /* WD */
BA_ATOMIC_AGGR = 0x06, /* WD */
BA_AGGREGATOR = 0x07, /* OT */
BA_COMMUNITY = 0x08, /* RFC 1997 */ /* OT */
BA_ORIGINATOR_ID = 0x09, /* RFC 4456 */ /* ON */
BA_CLUSTER_LIST = 0x0a, /* RFC 4456 */ /* ON */
BA_MP_REACH_NLRI = 0x0e, /* RFC 4760 */
BA_MP_UNREACH_NLRI = 0x0f, /* RFC 4760 */
BA_EXT_COMMUNITY = 0x10, /* RFC 4360 */
BA_AS4_PATH = 0x11, /* RFC 6793 */
BA_AS4_AGGREGATOR = 0x12, /* RFC 6793 */
BA_AIGP = 0x1a, /* RFC 7311 */
BA_LARGE_COMMUNITY = 0x20, /* RFC 8092 */
/* Bird's private internal BGP attributes */
#define BA_MPLS_LABEL_STACK 0xfe /* MPLS label stack transfer attribute */
BA_MPLS_LABEL_STACK = 0x100, /* MPLS label stack transfer attribute */
/* Maximum */
BGP_ATTR_MAX,
};
/* BGP connection states */

View File

@ -19,19 +19,18 @@ CF_DECLS
CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
MULTIHOP, STARTUP, VIA, NEXT, HOP, SELF, DEFAULT, PATH, METRIC, ERROR,
START, DELAY, FORGET, WAIT, ENABLE, DISABLE, AFTER, BGP_PATH,
BGP_LOCAL_PREF, BGP_MED, BGP_ORIGIN, BGP_NEXT_HOP, BGP_ATOMIC_AGGR,
BGP_AGGREGATOR, BGP_COMMUNITY, BGP_EXT_COMMUNITY, BGP_LARGE_COMMUNITY,
START, DELAY, FORGET, WAIT, ENABLE, DISABLE, AFTER,
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, BGP_ORIGINATOR_ID,
BGP_CLUSTER_LIST, IGP, TABLE, GATEWAY, DIRECT, RECURSIVE, MED, TTL,
DROP, IGNORE, 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,
STRICT, BIND, CONFEDERATION, MEMBER, MULTICAST, FLOW4, FLOW6, LONG,
LIVED, STALE, IMPORT, IBGP, EBGP, MANDATORY, INTERNAL, EXTERNAL, SETS,
DYNAMIC, RANGE, NAME, DIGITS, BGP_AIGP, AIGP, ORIGINATE, COST, ENFORCE,
FIRST)
DYNAMIC, RANGE, NAME, DIGITS, AIGP, ORIGINATE, COST, ENFORCE,
FIRST, FREE, VALIDATE, BASE)
%type <i> bgp_nh
%type <i32> bgp_afi
@ -44,6 +43,8 @@ CF_KEYWORDS(CEASE, PREFIX, LIMIT, HIT, ADMINISTRATIVE, SHUTDOWN, RESET, PEER,
CF_GRAMMAR
toksym: BGP_MED | BGP_LOCAL_PREF | SOURCE ;
proto: bgp_proto '}' ;
bgp_proto_start: proto_start BGP {
@ -155,6 +156,7 @@ bgp_proto:
}
| bgp_proto DYNAMIC NAME DIGITS expr ';' { BGP_CFG->dynamic_name_digits = $5; if ($5>10) cf_error("Dynamic name digits must be at most 10"); }
| bgp_proto STRICT BIND bool ';' { BGP_CFG->strict_bind = $4; }
| bgp_proto FREE BIND bool ';' { BGP_CFG->free_bind = $4; }
| bgp_proto PATH METRIC bool ';' { BGP_CFG->compare_path_lengths = $4; }
| bgp_proto MED METRIC bool ';' { BGP_CFG->med_metric = $4; }
| bgp_proto IGP METRIC bool ';' { BGP_CFG->igp_metric = $4; }
@ -255,6 +257,11 @@ bgp_channel_item:
| GATEWAY DIRECT { BGP_CC->gw_mode = GW_DIRECT; }
| GATEWAY RECURSIVE { BGP_CC->gw_mode = GW_RECURSIVE; }
| SECONDARY bool { BGP_CC->secondary = $2; }
| VALIDATE bool {
BGP_CC->validate = $2;
if (BGP_SAFI(BGP_CC->afi) != BGP_SAFI_FLOW)
cf_error("Validate option limited to flowspec channels");
}
| GRACEFUL RESTART bool { BGP_CC->gr_able = $3; }
| LONG LIVED GRACEFUL RESTART bool { BGP_CC->llgr_able = $5; }
| LONG LIVED STALE TIME expr { BGP_CC->llgr_time = $5; }
@ -278,6 +285,16 @@ bgp_channel_item:
else
cf_error("Mismatched IGP table type");
}
| BASE TABLE rtable {
if (BGP_SAFI(BGP_CC->afi) != BGP_SAFI_FLOW)
cf_error("Base table option limited to flowspec channels");
if (((BGP_CC->afi == BGP_AF_FLOW4) && ($3->addr_type == NET_IP4)) ||
((BGP_CC->afi == BGP_AF_FLOW6) && ($3->addr_type == NET_IP6)))
BGP_CC->base_table = $3;
else
cf_error("Mismatched base table type");
}
;
bgp_channel_opts:
@ -300,36 +317,6 @@ bgp_channel_end:
bgp_proto_channel: bgp_channel_start bgp_channel_opt_list bgp_channel_end;
dynamic_attr: BGP_ORIGIN
{ $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_ENUM_BGP_ORIGIN, EA_CODE(PROTOCOL_BGP, BA_ORIGIN)); } ;
dynamic_attr: BGP_PATH
{ $$ = f_new_dynamic_attr(EAF_TYPE_AS_PATH, T_PATH, EA_CODE(PROTOCOL_BGP, BA_AS_PATH)); } ;
dynamic_attr: BGP_NEXT_HOP
{ $$ = f_new_dynamic_attr(EAF_TYPE_IP_ADDRESS, T_IP, EA_CODE(PROTOCOL_BGP, BA_NEXT_HOP)); } ;
dynamic_attr: BGP_MED
{ $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_MULTI_EXIT_DISC)); } ;
dynamic_attr: BGP_LOCAL_PREF
{ $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_LOCAL_PREF)); } ;
dynamic_attr: BGP_ATOMIC_AGGR
{ $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_ATOMIC_AGGR)); } ;
dynamic_attr: BGP_AGGREGATOR
{ $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_AGGREGATOR)); } ;
dynamic_attr: BGP_COMMUNITY
{ $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY)); } ;
dynamic_attr: BGP_ORIGINATOR_ID
{ $$ = f_new_dynamic_attr(EAF_TYPE_ROUTER_ID, T_QUAD, EA_CODE(PROTOCOL_BGP, BA_ORIGINATOR_ID)); } ;
dynamic_attr: BGP_CLUSTER_LIST
{ $$ = f_new_dynamic_attr(EAF_TYPE_INT_SET, T_CLIST, EA_CODE(PROTOCOL_BGP, BA_CLUSTER_LIST)); } ;
dynamic_attr: BGP_EXT_COMMUNITY
{ $$ = f_new_dynamic_attr(EAF_TYPE_EC_SET, T_ECLIST, EA_CODE(PROTOCOL_BGP, BA_EXT_COMMUNITY)); } ;
dynamic_attr: BGP_AIGP
{ $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_AIGP)); } ;
dynamic_attr: BGP_LARGE_COMMUNITY
{ $$ = f_new_dynamic_attr(EAF_TYPE_LC_SET, T_LCLIST, EA_CODE(PROTOCOL_BGP, BA_LARGE_COMMUNITY)); } ;
CF_ENUM(T_ENUM_BGP_ORIGIN, ORIGIN_, IGP, EGP, INCOMPLETE)
CF_CODE

View File

@ -15,8 +15,8 @@
#include "nest/bird.h"
#include "nest/iface.h"
#include "nest/protocol.h"
#include "nest/route.h"
#include "nest/attrs.h"
#include "nest/rt.h"
#include "lib/attrs.h"
#include "proto/mrt/mrt.h"
#include "conf/conf.h"
#include "lib/unaligned.h"
@ -932,11 +932,15 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
#define WITHDRAW(msg, args...) \
({ REPORT(msg, ## args); s->err_withdraw = 1; return; })
#define REJECT(msg, args...) \
({ log(L_ERR "%s: " msg, s->proto->p.name, ## args); s->err_reject = 1; return; })
#define BAD_AFI "Unexpected AF <%u/%u> in UPDATE"
#define BAD_NEXT_HOP "Invalid NEXT_HOP attribute"
#define NO_NEXT_HOP "Missing NEXT_HOP attribute"
#define NO_LABEL_STACK "Missing MPLS stack"
#define MISMATCHED_AF " - mismatched address family (%I for %s)"
static void
bgp_apply_next_hop(struct bgp_parse_state *s, rta *a, ip_addr gw, ip_addr ll)
@ -949,67 +953,86 @@ bgp_apply_next_hop(struct bgp_parse_state *s, rta *a, ip_addr gw, ip_addr ll)
neighbor *nbr = NULL;
/* GW_DIRECT -> single_hop -> p->neigh != NULL */
if (ipa_nonzero(gw))
if (ipa_nonzero2(gw))
nbr = neigh_find(&p->p, gw, NULL, 0);
else if (ipa_nonzero(ll))
nbr = neigh_find(&p->p, ll, p->neigh->iface, 0);
else
WITHDRAW(BAD_NEXT_HOP " - zero address");
if (!nbr || (nbr->scope == SCOPE_HOST))
WITHDRAW(BAD_NEXT_HOP);
if (!nbr)
WITHDRAW(BAD_NEXT_HOP " - address %I not directly reachable", ipa_nonzero(gw) ? gw : ll);
a->dest = RTD_UNICAST;
a->nh.gw = nbr->addr;
a->nh.iface = nbr->iface;
a->igp_metric = c->cf->cost;
if (nbr->scope == SCOPE_HOST)
WITHDRAW(BAD_NEXT_HOP " - address %I is local", nbr->addr);
ea_set_attr_u32(&a->eattrs, &ea_gen_igp_metric, 0, c->cf->cost);
struct nexthop_adata nhad = {
.nh = {
.gw = nbr->addr,
.iface = nbr->iface,
},
.ad = {
.length = sizeof nhad - sizeof nhad.ad,
},
};
ea_set_attr_data(&a->eattrs, &ea_gen_nexthop, 0, nhad.ad.data, nhad.ad.length);
}
else /* GW_RECURSIVE */
{
if (ipa_zero(gw))
WITHDRAW(BAD_NEXT_HOP);
if (ipa_zero2(gw))
WITHDRAW(BAD_NEXT_HOP " - zero address");
rtable *tab = ipa_is_ip4(gw) ? c->igp_table_ip4 : c->igp_table_ip6;
s->hostentry = rt_get_hostentry(tab, gw, ll, c->c.table);
if (!s->mpls)
rta_apply_hostentry(a, s->hostentry, NULL);
/* With MPLS, hostentry is applied later in bgp_apply_mpls_labels() */
if (s->mpls)
{
u32 labels[BGP_MPLS_MAX];
ea_set_hostentry(&a->eattrs, c->c.table, tab, gw, ll, BGP_MPLS_MAX, labels);
}
else
ea_set_hostentry(&a->eattrs, c->c.table, tab, gw, ll, 0, NULL);
}
}
static void
bgp_apply_mpls_labels(struct bgp_parse_state *s, rta *a, u32 *labels, uint lnum)
bgp_apply_mpls_labels(struct bgp_parse_state *s, rta *a, u32 lnum, u32 labels[lnum])
{
if (lnum > MPLS_MAX_LABEL_STACK)
{
REPORT("Too many MPLS labels ($u)", lnum);
a->dest = RTD_UNREACHABLE;
a->hostentry = NULL;
a->nh = (struct nexthop) { };
ea_set_dest(&a->eattrs, 0, RTD_UNREACHABLE);
return;
}
/* Handle implicit NULL as empty MPLS stack */
if ((lnum == 1) && (labels[0] == BGP_MPLS_NULL))
lnum = 0;
lnum = s->mpls_labels->length = 0;
if (s->channel->cf->gw_mode == GW_DIRECT)
{
a->nh.labels = lnum;
memcpy(a->nh.label, labels, 4*lnum);
eattr *e = ea_find(a->eattrs, &ea_gen_nexthop);
struct {
struct nexthop_adata nhad;
u32 labels[MPLS_MAX_LABEL_STACK];
} nh;
memcpy(&nh.nhad, e->u.ptr, sizeof(struct adata) + e->u.ptr->length);
nh.nhad.nh.labels = lnum;
memcpy(nh.labels, labels, lnum * sizeof(u32));
nh.nhad.ad.length = sizeof nh.nhad + lnum * sizeof(u32);
}
else /* GW_RECURSIVE */
{
mpls_label_stack ms;
ms.len = lnum;
memcpy(ms.stack, labels, 4*lnum);
rta_apply_hostentry(a, s->hostentry, &ms);
eattr *e = ea_find(a->eattrs, &ea_gen_hostentry);
ASSERT_DIE(e);
struct hostentry_adata *head = (void *) e->u.ptr;
memcpy(&head->labels, labels, lnum * sizeof(u32));
head->ad.length = (void *)(&head->labels[lnum]) - (void *) head->ad.data;
}
}
static int
bgp_match_src(struct bgp_export_state *s, int mode)
{
@ -1039,7 +1062,7 @@ bgp_use_next_hop(struct bgp_export_state *s, eattr *a)
return 1;
/* Keep it when explicitly set in export filter */
if (a->type & EAF_FRESH)
if (a->fresh)
return 1;
/* Check for non-matching AF */
@ -1056,7 +1079,7 @@ bgp_use_next_hop(struct bgp_export_state *s, eattr *a)
return p->neigh && (p->neigh->iface == ifa);
}
static inline int
static inline struct nexthop *
bgp_use_gateway(struct bgp_export_state *s)
{
struct bgp_proto *p = s->proto;
@ -1065,22 +1088,32 @@ bgp_use_gateway(struct bgp_export_state *s)
/* Handle next hop self option - also applies to gateway */
if (c->cf->next_hop_self && bgp_match_src(s, c->cf->next_hop_self))
return 0;
return NULL;
eattr *nhea = ea_find(ra->eattrs, &ea_gen_nexthop);
if (!nhea)
return NULL;
/* We need one valid global gateway */
if ((ra->dest != RTD_UNICAST) || ra->nh.next || ipa_zero(ra->nh.gw) || ipa_is_link_local(ra->nh.gw))
return 0;
struct nexthop_adata *nhad = (struct nexthop_adata *) nhea->u.ptr;
if (!NEXTHOP_IS_REACHABLE(nhad) ||
!NEXTHOP_ONE(nhad) || ipa_zero(nhad->nh.gw) ||
ipa_is_link_local(nhad->nh.gw))
return NULL;
/* Check for non-matching AF */
if ((ipa_is_ip4(ra->nh.gw) != bgp_channel_is_ipv4(c)) && !c->ext_next_hop)
return 0;
if ((ipa_is_ip4(nhad->nh.gw) != bgp_channel_is_ipv4(c)) && !c->ext_next_hop)
return NULL;
/* Use it when exported to internal peers */
if (p->is_interior)
return 1;
return &nhad->nh;
/* Use it when forwarded to single-hop BGP peer on on the same iface */
return p->neigh && (p->neigh->iface == ra->nh.iface);
if (p->neigh && (p->neigh->iface == nhad->nh.iface))
return &nhad->nh;
return NULL;
}
static void
@ -1088,31 +1121,31 @@ bgp_update_next_hop_ip(struct bgp_export_state *s, eattr *a, ea_list **to)
{
if (!a || !bgp_use_next_hop(s, a))
{
if (bgp_use_gateway(s))
struct nexthop *nhloc;
if (nhloc = bgp_use_gateway(s))
{
rta *ra = s->route->attrs;
ip_addr nh[1] = { ra->nh.gw };
bgp_set_attr_data(to, s->pool, BA_NEXT_HOP, 0, nh, 16);
ip_addr nh[1] = { nhloc->gw };
bgp_set_attr_data(to, BA_NEXT_HOP, 0, nh, 16);
if (s->mpls)
{
u32 implicit_null = BGP_MPLS_NULL;
u32 *labels = ra->nh.labels ? ra->nh.label : &implicit_null;
uint lnum = ra->nh.labels ? ra->nh.labels : 1;
bgp_set_attr_data(to, s->pool, BA_MPLS_LABEL_STACK, 0, labels, lnum * 4);
u32 *labels = nhloc->labels ? nhloc->label : &implicit_null;
uint lnum = nhloc->labels ? nhloc->labels : 1;
bgp_set_attr_data(to, BA_MPLS_LABEL_STACK, 0, labels, lnum * 4);
}
}
else
{
ip_addr nh[2] = { s->channel->next_hop_addr, s->channel->link_addr };
bgp_set_attr_data(to, s->pool, BA_NEXT_HOP, 0, nh, ipa_nonzero(nh[1]) ? 32 : 16);
bgp_set_attr_data(to, BA_NEXT_HOP, 0, nh, ipa_nonzero(nh[1]) ? 32 : 16);
s->local_next_hop = 1;
/* TODO: Use local MPLS assigned label */
if (s->mpls)
{
u32 implicit_null = BGP_MPLS_NULL;
bgp_set_attr_data(to, s->pool, BA_MPLS_LABEL_STACK, 0, &implicit_null, 4);
bgp_set_attr_data(to, BA_MPLS_LABEL_STACK, 0, &implicit_null, 4);
}
}
}
@ -1120,28 +1153,28 @@ bgp_update_next_hop_ip(struct bgp_export_state *s, eattr *a, ea_list **to)
/* Check if next hop is valid */
a = bgp_find_attr(*to, BA_NEXT_HOP);
if (!a)
WITHDRAW(NO_NEXT_HOP);
REJECT(NO_NEXT_HOP);
ip_addr *nh = (void *) a->u.ptr->data;
ip_addr peer = s->proto->remote_ip;
uint len = a->u.ptr->length;
/* Forbid zero next hop */
if (ipa_zero(nh[0]) && ((len != 32) || ipa_zero(nh[1])))
WITHDRAW(BAD_NEXT_HOP);
if (ipa_zero2(nh[0]) && ((len != 32) || ipa_zero(nh[1])))
REJECT(BAD_NEXT_HOP " - zero address");
/* Forbid next hop equal to neighbor IP */
if (ipa_equal(peer, nh[0]) || ((len == 32) && ipa_equal(peer, nh[1])))
WITHDRAW(BAD_NEXT_HOP);
REJECT(BAD_NEXT_HOP " - neighbor address %I", peer);
/* Forbid next hop with non-matching AF */
if ((ipa_is_ip4(nh[0]) != bgp_channel_is_ipv4(s->channel)) &&
!s->channel->ext_next_hop)
WITHDRAW(BAD_NEXT_HOP);
REJECT(BAD_NEXT_HOP MISMATCHED_AF, nh[0], s->channel->desc->name);
/* Just check if MPLS stack */
if (s->mpls && !bgp_find_attr(*to, BA_MPLS_LABEL_STACK))
WITHDRAW(NO_LABEL_STACK);
REJECT(NO_LABEL_STACK);
}
static uint
@ -1212,11 +1245,11 @@ bgp_decode_next_hop_ip(struct bgp_parse_state *s, byte *data, uint len, rta *a)
ad->length = 16;
if ((bgp_channel_is_ipv4(c) != ipa_is_ip4(nh[0])) && !c->ext_next_hop)
WITHDRAW(BAD_NEXT_HOP);
WITHDRAW(BAD_NEXT_HOP MISMATCHED_AF, nh[0], c->desc->name);
// XXXX validate next hop
bgp_set_attr_ptr(&(a->eattrs), s->pool, BA_NEXT_HOP, 0, ad);
bgp_set_attr_ptr(&(a->eattrs), BA_NEXT_HOP, 0, ad);
bgp_apply_next_hop(s, a, nh[0], nh[1]);
}
@ -1293,11 +1326,11 @@ bgp_decode_next_hop_vpn(struct bgp_parse_state *s, byte *data, uint len, rta *a)
bgp_parse_error(s, 9);
if ((bgp_channel_is_ipv4(c) != ipa_is_ip4(nh[0])) && !c->ext_next_hop)
WITHDRAW(BAD_NEXT_HOP);
WITHDRAW(BAD_NEXT_HOP MISMATCHED_AF, nh[0], c->desc->name);
// XXXX validate next hop
bgp_set_attr_ptr(&(a->eattrs), s->pool, BA_NEXT_HOP, 0, ad);
bgp_set_attr_ptr(&(a->eattrs), BA_NEXT_HOP, 0, ad);
bgp_apply_next_hop(s, a, nh[0], nh[1]);
}
@ -1322,11 +1355,11 @@ bgp_decode_next_hop_none(struct bgp_parse_state *s UNUSED, byte *data UNUSED, ui
}
static void
bgp_update_next_hop_none(struct bgp_export_state *s, eattr *a, ea_list **to)
bgp_update_next_hop_none(struct bgp_export_state *s UNUSED, eattr *a, ea_list **to)
{
/* NEXT_HOP shall not pass */
if (a)
bgp_unset_attr(to, s->pool, BA_NEXT_HOP);
bgp_unset_attr(to, BA_NEXT_HOP);
}
@ -1335,7 +1368,7 @@ bgp_update_next_hop_none(struct bgp_export_state *s, eattr *a, ea_list **to)
*/
static void
bgp_rte_update(struct bgp_parse_state *s, net_addr *n, u32 path_id, rta *a0)
bgp_rte_update(struct bgp_parse_state *s, const net_addr *n, u32 path_id, rta *a0)
{
if (path_id != s->last_id)
{
@ -1348,6 +1381,10 @@ bgp_rte_update(struct bgp_parse_state *s, net_addr *n, u32 path_id, rta *a0)
if (!a0)
{
/* Route update was changed to withdraw */
if (s->err_withdraw && s->reach_nlri_step)
REPORT("Invalid route %N withdrawn", n);
/* Route withdraw */
rte_update(&s->channel->c, n, NULL, s->last_src);
return;
@ -1392,7 +1429,8 @@ bgp_encode_mpls_labels(struct bgp_write_state *s UNUSED, const adata *mpls, byte
static void
bgp_decode_mpls_labels(struct bgp_parse_state *s, byte **pos, uint *len, uint *pxlen, rta *a)
{
u32 labels[BGP_MPLS_MAX], label;
u32 labels[BGP_MPLS_MAX];
u32 label;
uint lnum = 0;
do {
@ -1406,7 +1444,7 @@ bgp_decode_mpls_labels(struct bgp_parse_state *s, byte **pos, uint *len, uint *p
/* RFC 8277 2.4 - withdraw does not have variable-size MPLS stack but
fixed-size 24-bit Compatibility field, which MUST be ignored */
if (!a && !s->err_withdraw)
if (!s->reach_nlri_step)
return;
}
while (!(label & BGP_MPLS_BOS));
@ -1414,19 +1452,8 @@ bgp_decode_mpls_labels(struct bgp_parse_state *s, byte **pos, uint *len, uint *p
if (!a)
return;
/* Attach MPLS attribute unless we already have one */
if (!s->mpls_labels)
{
s->mpls_labels = lp_alloc_adata(s->pool, 4*BGP_MPLS_MAX);
bgp_set_attr_ptr(&(a->eattrs), s->pool, BA_MPLS_LABEL_STACK, 0, s->mpls_labels);
}
/* Overwrite data in the attribute */
s->mpls_labels->length = 4*lnum;
memcpy(s->mpls_labels->data, labels, 4*lnum);
/* Update next hop entry in rta */
bgp_apply_mpls_labels(s, a, labels, lnum);
bgp_apply_mpls_labels(s, a, lnum, labels);
/* Attributes were changed, invalidate cached entry */
rta_free(s->cached_rta);
@ -2289,11 +2316,14 @@ bgp_create_update(struct bgp_channel *c, byte *buf)
again: ;
struct lp_state tmpp;
lp_save(tmp_linpool, &tmpp);
/* Initialize write state */
struct bgp_write_state s = {
.proto = p,
.channel = c,
.pool = bgp_linpool,
.pool = tmp_linpool,
.mp_reach = (c->afi != BGP_AF_IPV4) || c->ext_next_hop,
.as4_session = p->as4_session,
.add_path = c->add_path_tx,
@ -2319,6 +2349,7 @@ again: ;
if (EMPTY_LIST(buck->prefixes))
{
bgp_free_bucket(c, buck);
lp_restore(tmp_linpool, &tmpp);
goto again;
}
@ -2332,7 +2363,10 @@ again: ;
bgp_defer_bucket(c, buck);
if (!res)
{
lp_restore(tmp_linpool, &tmpp);
goto again;
}
goto done;
}
@ -2343,7 +2377,7 @@ again: ;
done:
BGP_TRACE_RL(&rl_snd_update, D_PACKETS, "Sending UPDATE");
p->stats.tx_updates++;
lp_flush(s.pool);
lp_restore(tmp_linpool, &tmpp);
return res;
}
@ -2433,11 +2467,11 @@ bgp_decode_nlri(struct bgp_parse_state *s, u32 afi, byte *nlri, uint len, ea_lis
{
a = allocz(RTA_MAX_SIZE);
a->source = RTS_BGP;
a->scope = SCOPE_UNIVERSE;
a->from = s->proto->remote_ip;
a->eattrs = ea;
a->pref = c->c.preference;
ea_set_attr_data(&a->eattrs, &ea_gen_from, 0, &s->proto->remote_ip, sizeof(ip_addr));
ea_set_attr_u32(&a->eattrs, &ea_gen_preference, 0, c->c.preference);
ea_set_attr_u32(&a->eattrs, &ea_gen_source, 0, RTS_BGP);
c->desc->decode_next_hop(s, nh, nh_len, a);
bgp_finish_attrs(s, a);
@ -2472,10 +2506,13 @@ bgp_rx_update(struct bgp_conn *conn, byte *pkt, uint len)
bgp_start_timer(conn->hold_timer, conn->hold_time);
struct lp_state tmpp;
lp_save(tmp_linpool, &tmpp);
/* Initialize parse state */
struct bgp_parse_state s = {
.proto = p,
.pool = bgp_linpool,
.pool = tmp_linpool,
.as4_session = p->as4_session,
};
@ -2541,6 +2578,8 @@ bgp_rx_update(struct bgp_conn *conn, byte *pkt, uint len)
if (s.mp_unreach_len)
bgp_decode_nlri(&s, s.mp_unreach_af, s.mp_unreach_nlri, s.mp_unreach_len, NULL, NULL, 0);
s.reach_nlri_step = 1;
if (s.ip_reach_len)
bgp_decode_nlri(&s, BGP_AF_IPV4, s.ip_reach_nlri, s.ip_reach_len,
ea, s.ip_next_hop_data, s.ip_next_hop_len);
@ -2551,7 +2590,7 @@ bgp_rx_update(struct bgp_conn *conn, byte *pkt, uint len)
done:
rta_free(s.cached_rta);
lp_flush(s.pool);
lp_restore(tmp_linpool, &tmpp);
return;
}
@ -2695,7 +2734,7 @@ bgp_rx_route_refresh(struct bgp_conn *conn, byte *pkt, uint len)
{
case BGP_RR_REQUEST:
BGP_TRACE(D_PACKETS, "Got ROUTE-REFRESH");
channel_request_feeding(&c->c);
rt_refeed_channel(&c->c);
break;
case BGP_RR_BEGIN:

View File

@ -2,5 +2,6 @@ src := mrt.c
obj := $(src-o-files)
$(all-daemon)
$(cf-local)
$(call proto-build,mrt_build)
tests_objs := $(tests_objs) $(src-o-files)
tests_objs := $(tests_objs) $(src-o-files)

View File

@ -113,13 +113,13 @@ mrt_buffer_flush(buffer *b)
}
#define MRT_DEFINE_TYPE(S, T) \
static inline void mrt_put_##S##_(buffer *b, T x) \
UNUSED static inline void mrt_put_##S##_(buffer *b, T x) \
{ \
put_##S(b->pos, x); \
b->pos += sizeof(T); \
} \
\
static inline void mrt_put_##S(buffer *b, T x) \
UNUSED static inline void mrt_put_##S(buffer *b, T x) \
{ \
mrt_buffer_need(b, sizeof(T)); \
put_##S(b->pos, x); \
@ -431,7 +431,7 @@ mrt_rib_table_entry_bgp_attrs(struct mrt_table_dump_state *s, rte *r)
/* Attribute list must be normalized for bgp_encode_attrs() */
if (!rta_is_cached(r->attrs))
ea_normalize(eattrs);
eattrs = ea_normalize(eattrs);
mrt_buffer_need(b, MRT_ATTR_BUFFER_SIZE);
byte *pos = b->pos;
@ -525,7 +525,7 @@ mrt_rib_table_dump(struct mrt_table_dump_state *s, net *n, int add_path)
}
rte e = rt->rte;
if (f_run(s->filter, &e, s->linpool, 0) <= F_ACCEPT)
if (f_run(s->filter, &e, 0) <= F_ACCEPT)
mrt_rib_table_entry(s, &e);
lp_flush(s->linpool);
@ -557,8 +557,8 @@ mrt_table_dump_init(pool *pp)
struct mrt_table_dump_state *s = mb_allocz(pool, sizeof(struct mrt_table_dump_state));
s->pool = pool;
s->linpool = lp_new(pool, 4080);
s->peer_lp = lp_new(pool, 4080);
s->linpool = lp_new(pool);
s->peer_lp = lp_new(pool);
mrt_buffer_init(&s->buf, pool, 2 * MRT_ATTR_BUFFER_SIZE);
/* We lock the current config as we may reference it indirectly by filter */
@ -904,7 +904,6 @@ mrt_copy_config(struct proto_config *dest UNUSED, struct proto_config *src UNUSE
struct protocol proto_mrt = {
.name = "MRT",
.template = "mrt%d",
.class = PROTOCOL_MRT,
.proto_size = sizeof(struct mrt_proto),
.config_size = sizeof(struct mrt_config),
.init = mrt_init,
@ -913,3 +912,9 @@ struct protocol proto_mrt = {
.reconfigure = mrt_reconfigure,
.copy_config = mrt_copy_config,
};
void
mrt_build(void)
{
proto_build(&proto_mrt);
}

Some files were not shown because too many files have changed in this diff Show More