Compare commits
147 commits
babel-rtt-
...
master
Author | SHA1 | Date | |
---|---|---|---|
6269f404fc | |||
|
d9763bd7a0 | ||
|
94ebf8e00e | ||
|
071354da5b | ||
|
8657e7e703 | ||
|
1e47b9f203 | ||
|
34ebc4e1ba | ||
|
937ebf2536 | ||
|
4c19a8a984 | ||
|
1124f39f73 | ||
|
e48f898fda | ||
|
a80cd47074 | ||
|
3859e4efc1 | ||
|
e80156d936 | ||
|
a50d2fa65f | ||
|
ff38ee5986 | ||
|
543c8ba097 | ||
|
140c534fb8 | ||
|
bbac9ca958 | ||
|
371eb49043 | ||
|
84545a26cc | ||
|
57308fb277 | ||
|
9d03c3f56c | ||
|
37b6444137 | ||
|
5aebce5e0c | ||
|
e471f9e0fb | ||
|
3242529750 | ||
|
8f79e6b93e | ||
|
8478de8817 | ||
|
92a8565547 | ||
|
da0b589e7b | ||
|
54430df953 | ||
|
605ff0a0eb | ||
|
0f2be469f8 | ||
|
c73343de67 | ||
|
71b3456eed | ||
|
d2c1036a42 | ||
|
dc28c6ed1c | ||
|
16ac6c3c74 | ||
|
bc4ad83dac | ||
|
73abd91ac6 | ||
|
082905a833 | ||
|
ddb1bdf281 | ||
|
722daa9500 | ||
|
2e484f8d29 | ||
|
534d0a4b44 | ||
|
432dfe3b9b | ||
|
4d48ede51d | ||
|
971721c9b5 | ||
|
c73b5d2d3d | ||
|
b5c8fce284 | ||
|
2e5bfeb73a | ||
|
d429bc5c84 | ||
|
7e9cede1fd | ||
|
cb339a3067 | ||
|
1ac8e11bba | ||
|
a2527ee53d | ||
|
f31f4e6eef | ||
|
1e6acf34bb | ||
|
946cedfcfe | ||
|
26bc4f9904 | ||
|
fb1d8f6513 | ||
|
b2d6d2948a | ||
|
8f3c6151b4 | ||
|
9b302c133f | ||
|
cde8094c1f | ||
|
93d6096c87 | ||
|
4c0c507b1f | ||
|
beb5f78ada | ||
|
b867c798c3 | ||
|
141fb51f1a | ||
|
ad686c55c3 | ||
|
f39e9aa203 | ||
|
a8a3d95be5 | ||
|
652be92a21 | ||
|
f196b12c62 | ||
|
097f157182 | ||
|
9e60b500c7 | ||
|
a9c19b923c | ||
|
7bb06b34a1 | ||
|
9a9439d5e1 | ||
|
ba2a076001 | ||
|
5299fb9db0 | ||
|
98fd158e28 | ||
|
d39ef961d1 | ||
|
7e86ff2076 | ||
|
dabd7bccb3 | ||
|
4a23ede2b0 | ||
|
0f68515263 | ||
|
63cf5d5d8c | ||
|
af8568a870 | ||
|
170b20701c | ||
|
ebd807c0b8 | ||
|
3a6eda995e | ||
|
c53f547a0b | ||
|
3c42f7af6a | ||
|
4e60b3ee72 | ||
|
9b6db9f9b8 | ||
|
19e727a248 | ||
|
8a4bc4fdbf | ||
|
24773af9e0 | ||
|
83d9920f90 | ||
|
ff47cd80dd | ||
|
9e60a1fbc3 | ||
|
b90c9b164f | ||
|
eeec9ddbf2 | ||
|
0c59f7ff01 | ||
|
c20506dc07 | ||
|
1c7df2c240 | ||
|
1740ff57e8 | ||
|
c78247f9b9 | ||
|
06ece3265e | ||
|
d814a8cb93 | ||
|
48bf1322aa | ||
|
2e8b8bfcc4 | ||
|
d071aca7aa | ||
|
60880b539b | ||
|
0b295d695a | ||
|
89ff49f8f0 | ||
|
e42eedb912 | ||
|
5cff1d5f02 | ||
|
d5a32563df | ||
|
541881bedf | ||
|
3660f19dd5 | ||
|
eb937358c0 | ||
|
cee0cd148c | ||
|
ddd89ba12d | ||
|
c507fb41bb | ||
|
8216ec3027 | ||
|
5f0cb61d82 | ||
|
8ac20511e1 | ||
|
a0e4c66404 | ||
|
6e13df70fd | ||
|
d471d5fc7c | ||
|
a54f75f454 | ||
|
6cd3771378 | ||
|
3a31c3aad6 | ||
|
d322ee3d54 | ||
|
e5a8eec6d7 | ||
|
bea582cbb5 | ||
|
7f0e598208 | ||
|
2c13759136 | ||
|
ceef6de459 | ||
|
923a6644b2 | ||
|
227e2d5541 | ||
|
eb20251655 | ||
|
47d92d8f9d |
110 changed files with 3339 additions and 2025 deletions
285
.gitlab-ci.yml
285
.gitlab-ci.yml
|
@ -5,9 +5,10 @@ variables:
|
|||
DOCKER_CMD: docker --config="$HOME/.docker/$CI_JOB_ID/"
|
||||
IMG_BASE: registry.nic.cz/labs/bird
|
||||
TOOLS_DIR: /var/lib/gitlab-runner/bird-tools
|
||||
STAYRTR_BINARY: /var/lib/gitlab-runner/stayrtr-0.1-108-g8d18a41-linux-x86_64
|
||||
|
||||
stages:
|
||||
- image
|
||||
# - image
|
||||
- build
|
||||
- pkg
|
||||
- test
|
||||
|
@ -32,185 +33,12 @@ stages:
|
|||
# That's Docker in Docker
|
||||
- dind
|
||||
|
||||
docker_debian-8-amd64:
|
||||
variables:
|
||||
IMG_NAME: "debian-8-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-8-i386:
|
||||
variables:
|
||||
IMG_NAME: "debian-8-i386"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-9-amd64:
|
||||
variables:
|
||||
IMG_NAME: "debian-9-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-9-i386:
|
||||
variables:
|
||||
IMG_NAME: "debian-9-i386"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-10-amd64:
|
||||
variables:
|
||||
IMG_NAME: "debian-10-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-10-i386:
|
||||
variables:
|
||||
IMG_NAME: "debian-10-i386"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-11-amd64:
|
||||
variables:
|
||||
IMG_NAME: "debian-11-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
# GPG error
|
||||
#docker_debian-11-i386:
|
||||
# Docker build example
|
||||
#docker_debian-11-amd64:
|
||||
# variables:
|
||||
# IMG_NAME: "debian-11-i386"
|
||||
# IMG_NAME: "debian-11-amd64"
|
||||
# <<: *docker_build
|
||||
|
||||
docker_debian-testing-amd64:
|
||||
variables:
|
||||
IMG_NAME: "debian-testing-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
# GPG error
|
||||
#docker_debian-testing-i386:
|
||||
# variables:
|
||||
# IMG_NAME: "debian-testing-i386"
|
||||
# <<: *docker_build
|
||||
|
||||
docker_fedora-25-amd64:
|
||||
variables:
|
||||
IMG_NAME: "fedora-25-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_fedora-26-amd64:
|
||||
variables:
|
||||
IMG_NAME: "fedora-26-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_fedora-27-amd64:
|
||||
variables:
|
||||
IMG_NAME: "fedora-27-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_fedora-28-amd64:
|
||||
variables:
|
||||
IMG_NAME: "fedora-28-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_fedora-29-amd64:
|
||||
variables:
|
||||
IMG_NAME: "fedora-29-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_fedora-30-amd64:
|
||||
variables:
|
||||
IMG_NAME: "fedora-30-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_fedora-31-amd64:
|
||||
variables:
|
||||
IMG_NAME: "fedora-31-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_fedora-32-amd64:
|
||||
variables:
|
||||
IMG_NAME: "fedora-32-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_fedora-33-amd64:
|
||||
variables:
|
||||
IMG_NAME: "fedora-33-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_fedora-34-amd64:
|
||||
variables:
|
||||
IMG_NAME: "fedora-34-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_centos-7-amd64:
|
||||
variables:
|
||||
IMG_NAME: "centos-7-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_centos-8-amd64:
|
||||
variables:
|
||||
IMG_NAME: "centos-8-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_ubuntu-14_04-amd64:
|
||||
variables:
|
||||
IMG_NAME: "ubuntu-14.04-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_ubuntu-16_04-amd64:
|
||||
variables:
|
||||
IMG_NAME: "ubuntu-16.04-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_ubuntu-18_04-amd64:
|
||||
variables:
|
||||
IMG_NAME: "ubuntu-18.04-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_ubuntu-20_04-amd64:
|
||||
variables:
|
||||
IMG_NAME: "ubuntu-20.04-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_ubuntu-21_10-amd64:
|
||||
variables:
|
||||
IMG_NAME: "ubuntu-21.10-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
# GPG error
|
||||
#docker_ubuntu-21_04-amd64:
|
||||
# variables:
|
||||
# IMG_NAME: "ubuntu-21.04-amd64"
|
||||
# <<: *docker_build
|
||||
|
||||
docker_opensuse-15.0-amd64:
|
||||
variables:
|
||||
IMG_NAME: "opensuse-15.0-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_opensuse-15.1-amd64:
|
||||
variables:
|
||||
IMG_NAME: "opensuse-15.1-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_opensuse-15.2-amd64:
|
||||
variables:
|
||||
IMG_NAME: "opensuse-15.2-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_opensuse-15.3-amd64:
|
||||
variables:
|
||||
IMG_NAME: "opensuse-15.3-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
# TODO We want to copy these BSDs to our own virtual machines, to make sure
|
||||
# someone doesn't update them by accident.
|
||||
.freebsd-11-i386: &freebsd-11-i386_env
|
||||
tags:
|
||||
- freebsd
|
||||
- i386
|
||||
#only:
|
||||
#- master
|
||||
#- triggers
|
||||
#- tags
|
||||
|
||||
.freebsd-11-amd64: &freebsd-11-amd64_env
|
||||
tags:
|
||||
- freebsd
|
||||
- amd64
|
||||
|
||||
|
||||
.build: &build-base
|
||||
stage: build
|
||||
|
@ -360,17 +188,17 @@ build-opensuse-15.3-amd64:
|
|||
<<: *build-linux
|
||||
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
|
||||
|
@ -416,13 +244,11 @@ build-freebsd-11-i386:
|
|||
# needs: [build-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.nic.cz/labs/bird:debian-9-amd64
|
||||
|
||||
# Dpkg error: PATH is not set
|
||||
pkg-debian-9-i386:
|
||||
<<: *pkg-deb
|
||||
needs: [build-debian-9-i386]
|
||||
|
@ -531,6 +357,7 @@ build-birdlab:
|
|||
- sudo git clean -fx
|
||||
- git pull --ff-only
|
||||
- mv $DIR/bird $DIR/birdc netlab/common
|
||||
- ln -s $STAYRTR_BINARY netlab/common/stayrtr
|
||||
|
||||
.test: &test-base
|
||||
stage: test
|
||||
|
@ -541,7 +368,7 @@ build-birdlab:
|
|||
script:
|
||||
- cd $TOOLS_DIR/netlab
|
||||
- sudo ./stop
|
||||
- sudo ./runtest -m check $TEST_NAME
|
||||
- sudo ./runtest -s v2 -m check $TEST_NAME
|
||||
|
||||
test-ospf-base:
|
||||
<<: *test-base
|
||||
|
@ -613,6 +440,16 @@ test-bgp-merged:
|
|||
variables:
|
||||
TEST_NAME: cf-bgp-merged
|
||||
|
||||
test-bgp-flowspec:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-bgp-flowspec
|
||||
|
||||
test-bgp-rs-multitab:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-bgp-rs-multitab
|
||||
|
||||
test-ebgp-loop:
|
||||
<<: *test-base
|
||||
variables:
|
||||
|
@ -623,12 +460,32 @@ test-ebgp-star:
|
|||
variables:
|
||||
TEST_NAME: cf-ebgp-star
|
||||
|
||||
test-ebgp-role:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-ebgp-role
|
||||
|
||||
test-ebgp-graceful:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-ebgp-graceful
|
||||
|
||||
test-ebgp-import-limit:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-ebgp-import-limit
|
||||
|
||||
test-ibgp-loop:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-ibgp-loop
|
||||
|
||||
test-ibgp-star:
|
||||
test-ibgp-loop-big:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-ibgp-loop-big
|
||||
|
||||
test-ibgp-flat:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-ibgp-flat
|
||||
|
@ -647,3 +504,49 @@ test-rip-base:
|
|||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-rip-base
|
||||
|
||||
test-kernel-learn:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-kernel-learn
|
||||
|
||||
|
||||
.build-birdlab-base: &build-birdlab-base
|
||||
stage: build
|
||||
script:
|
||||
- autoreconf
|
||||
- ./configure
|
||||
- gmake
|
||||
- gmake check
|
||||
|
||||
build-birdlab-debian-11:
|
||||
<<: *build-birdlab-base
|
||||
tags:
|
||||
- birdlab-debian-11
|
||||
- amd64
|
||||
|
||||
build-birdlab-centos-08:
|
||||
<<: *build-birdlab-base
|
||||
tags:
|
||||
- birdlab-centos-08
|
||||
- amd64
|
||||
|
||||
build-birdlab-fedora-37:
|
||||
<<: *build-birdlab-base
|
||||
tags:
|
||||
- birdlab-fedora-37
|
||||
- amd64
|
||||
|
||||
build-birdlab-freebsd-13:
|
||||
<<: *build-birdlab-base
|
||||
tags:
|
||||
- birdlab-freebsd-13
|
||||
- amd64
|
||||
|
||||
build-birdlab-openbsd-71:
|
||||
<<: *build-birdlab-base
|
||||
variables:
|
||||
AUTOCONF_VERSION: "2.71"
|
||||
tags:
|
||||
- birdlab-openbsd-71
|
||||
- amd64
|
||||
|
|
|
@ -26,6 +26,7 @@ INSTALL_DATA=@INSTALL_DATA@
|
|||
client=$(addprefix $(exedir)/,@CLIENT@)
|
||||
daemon=$(exedir)/bird
|
||||
protocols=@protocols@
|
||||
PROTO_BUILD := $(protocols) dev kif krt
|
||||
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
|
|
45
NEWS
45
NEWS
|
@ -1,3 +1,48 @@
|
|||
Version 2.0.11 (2022-11-12)
|
||||
o BGP roles (RFC 9234)
|
||||
o BGP: Keepalive time scaling
|
||||
o BGP: New 'min hold time' and 'min keepalive time' options
|
||||
o BGP: New 'next hop prefer global' option
|
||||
o Filter: For loops and direct recursion
|
||||
o Filter: Mixed declarations of local variables
|
||||
o Filter: Improved static type checks
|
||||
o Filter: Literal [] for empty set
|
||||
o Linux: Netlink KRT improvements
|
||||
o BSD: Experimental support for Netlink API
|
||||
o Memory management improvements
|
||||
o Many bugfixes
|
||||
|
||||
Notes:
|
||||
|
||||
In contrast to prior versions, configured keepalive time in BGP now scales
|
||||
with negotiated hold time to maintain proportion between the keepalive time
|
||||
and the hold time.
|
||||
|
||||
The Linux KRT was updated to use the recent API for IPv6 ECMP routes instead
|
||||
of the legacy one. Consequently, the Linux versions older than 4.11 are no
|
||||
longer supported, at least for IPv6 ECMP routes. Also, routing table scanning
|
||||
now runs separately for each table to avoid congestion.
|
||||
|
||||
There is a minor change in recursive next hop processing. Previously,
|
||||
recursive next hop must be resolved through a non-recursive route, now it must
|
||||
be resolved through a prefix where both the best route and all routes with the
|
||||
same preference (as the best route) are non-recursive. The old behavior might
|
||||
lead in some corner cases to an infinite loop of recursive next hop resolution
|
||||
due to a priority inversion.
|
||||
|
||||
There is a minor change in the 'configure undo' command, it is no longer
|
||||
available after failed reconfiguration, as the old configuration is already
|
||||
released.
|
||||
|
||||
|
||||
Version 2.0.10 (2022-06-16)
|
||||
o BGP performance improvements
|
||||
o BFD: New 'strict bind' option
|
||||
o RPKI: VRF support
|
||||
o Allow use of 240.0.0.0/4 as a private range
|
||||
o BIRD client uses exit status to report errors
|
||||
o Important bugfixes
|
||||
|
||||
Version 2.0.9 (2022-02-09)
|
||||
o BGP: Flowspec validation procedure
|
||||
o Babel: MAC authentication support
|
||||
|
|
|
@ -153,7 +153,7 @@ submit_init_command(char *cmd_raw)
|
|||
if (!cmd)
|
||||
{
|
||||
cleanup();
|
||||
exit(0);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
submit_server_command(cmd);
|
||||
|
|
|
@ -574,6 +574,8 @@ check_eof(void)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline void cf_swap_soft_scope(void);
|
||||
|
||||
static struct symbol *
|
||||
cf_new_symbol(const byte *c)
|
||||
{
|
||||
|
@ -583,6 +585,8 @@ cf_new_symbol(const byte *c)
|
|||
if (l > SYM_MAX_LEN)
|
||||
cf_error("Symbol too long");
|
||||
|
||||
cf_swap_soft_scope();
|
||||
|
||||
s = cfg_allocz(sizeof(struct symbol) + l + 1);
|
||||
*s = (struct symbol) { .scope = conf_this_scope, .class = SYM_VOID, };
|
||||
strcpy(s->name, c);
|
||||
|
@ -656,8 +660,8 @@ cf_localize_symbol(struct symbol *sym)
|
|||
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");
|
||||
if (cf_symbol_is_local(sym))
|
||||
cf_error("Symbol '%s' already defined", sym->name);
|
||||
|
||||
/* Not allocated here yet, doing it now. */
|
||||
return cf_new_symbol(sym->name);
|
||||
|
@ -713,7 +717,7 @@ cf_lex_symbol(const char *data)
|
|||
static void
|
||||
cf_lex_init_kh(void)
|
||||
{
|
||||
HASH_INIT(kw_hash, &root_pool, KW_ORDER);
|
||||
HASH_INIT(kw_hash, config_pool, KW_ORDER);
|
||||
|
||||
struct keyword *k;
|
||||
for (k=keyword_list; k->name; k++)
|
||||
|
@ -787,12 +791,60 @@ cf_push_scope(struct symbol *sym)
|
|||
void
|
||||
cf_pop_scope(void)
|
||||
{
|
||||
ASSERT(!conf_this_scope->soft_scopes);
|
||||
|
||||
conf_this_scope->active = 0;
|
||||
conf_this_scope = conf_this_scope->next;
|
||||
|
||||
ASSERT(conf_this_scope);
|
||||
}
|
||||
|
||||
/**
|
||||
* cf_push_soft_scope - enter new soft scope
|
||||
*
|
||||
* If we want to enter a new anonymous scope that most likely will not contain
|
||||
* any symbols, we can use cf_push_soft_scope() insteas of cf_push_scope().
|
||||
* Such scope will be converted to a regular scope on first use.
|
||||
*/
|
||||
void
|
||||
cf_push_soft_scope(void)
|
||||
{
|
||||
if (conf_this_scope->soft_scopes < 0xfe)
|
||||
conf_this_scope->soft_scopes++;
|
||||
else
|
||||
cf_push_block_scope();
|
||||
}
|
||||
|
||||
/**
|
||||
* cf_pop_soft_scope - leave a soft scope
|
||||
*
|
||||
* Leave a soft scope entered by cf_push_soft_scope().
|
||||
*/
|
||||
void
|
||||
cf_pop_soft_scope(void)
|
||||
{
|
||||
if (conf_this_scope->soft_scopes)
|
||||
conf_this_scope->soft_scopes--;
|
||||
else
|
||||
cf_pop_block_scope();
|
||||
}
|
||||
|
||||
/**
|
||||
* cf_swap_soft_scope - convert soft scope to regular scope
|
||||
*
|
||||
* Soft scopes cannot hold symbols, so they must be converted to regular scopes
|
||||
* on first use. It is done automatically by cf_new_symbol().
|
||||
*/
|
||||
static inline void
|
||||
cf_swap_soft_scope(void)
|
||||
{
|
||||
if (conf_this_scope->soft_scopes)
|
||||
{
|
||||
conf_this_scope->soft_scopes--;
|
||||
cf_push_block_scope();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* cf_symbol_class_name - get name of a symbol class
|
||||
* @sym: symbol
|
||||
|
|
27
conf/conf.c
27
conf/conf.c
|
@ -61,6 +61,7 @@
|
|||
static jmp_buf conf_jmpbuf;
|
||||
|
||||
struct config *config, *new_config;
|
||||
pool *config_pool;
|
||||
|
||||
static struct config *old_config; /* Old configuration */
|
||||
static struct config *future_config; /* New config held here if recon requested during recon */
|
||||
|
@ -89,7 +90,7 @@ int undo_available; /* Undo was not requested from last reconfiguration */
|
|||
struct config *
|
||||
config_alloc(const char *name)
|
||||
{
|
||||
pool *p = rp_new(&root_pool, "Config");
|
||||
pool *p = rp_new(config_pool, "Config");
|
||||
linpool *l = lp_new_default(p);
|
||||
struct config *c = lp_allocz(l, sizeof(struct config));
|
||||
|
||||
|
@ -140,6 +141,7 @@ config_parse(struct config *c)
|
|||
protos_preconfig(c);
|
||||
rt_preconfig(c);
|
||||
cf_parse();
|
||||
rt_postconfig(c);
|
||||
|
||||
if (EMPTY_LIST(c->protos))
|
||||
cf_error("No protocol is specified in the config file");
|
||||
|
@ -199,6 +201,23 @@ config_free(struct config *c)
|
|||
rfree(c->pool);
|
||||
}
|
||||
|
||||
/**
|
||||
* config_free_old - free stored old configuration
|
||||
*
|
||||
* This function frees the old configuration (%old_config) that is saved for the
|
||||
* purpose of undo. It is useful before parsing a new config when reconfig is
|
||||
* requested, to avoid keeping three (perhaps memory-heavy) configs together.
|
||||
*/
|
||||
void
|
||||
config_free_old(void)
|
||||
{
|
||||
tm_stop(config_timer);
|
||||
undo_available = 0;
|
||||
|
||||
config_free(old_config);
|
||||
old_config = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
config_add_obstacle(struct config *c)
|
||||
{
|
||||
|
@ -490,10 +509,12 @@ config_timeout(timer *t UNUSED)
|
|||
void
|
||||
config_init(void)
|
||||
{
|
||||
config_event = ev_new(&root_pool);
|
||||
config_pool = rp_new(&root_pool, "Configurations");
|
||||
|
||||
config_event = ev_new(config_pool);
|
||||
config_event->hook = config_done;
|
||||
|
||||
config_timer = tm_new(&root_pool);
|
||||
config_timer = tm_new(config_pool);
|
||||
config_timer->hook = config_timeout;
|
||||
}
|
||||
|
||||
|
|
19
conf/conf.h
19
conf/conf.h
|
@ -70,6 +70,7 @@ struct config *config_alloc(const char *name);
|
|||
int config_parse(struct config *);
|
||||
int cli_parse(struct config *);
|
||||
void config_free(struct config *);
|
||||
void config_free_old(void);
|
||||
int config_commit(struct config *, int type, uint timeout);
|
||||
int config_confirm(void);
|
||||
int config_undo(void);
|
||||
|
@ -96,7 +97,7 @@ void order_shutdown(int gr);
|
|||
|
||||
|
||||
/* Pools */
|
||||
|
||||
extern pool *config_pool;
|
||||
extern linpool *cfg_mem;
|
||||
|
||||
#define cfg_alloc(size) lp_alloc(cfg_mem, size)
|
||||
|
@ -133,7 +134,9 @@ struct sym_scope {
|
|||
struct sym_scope *next; /* Next on scope stack */
|
||||
struct symbol *name; /* Name of this scope */
|
||||
uint slots; /* Variable slots */
|
||||
int active; /* Currently entered */
|
||||
byte active; /* Currently entered */
|
||||
byte block; /* No independent stack frame */
|
||||
byte soft_scopes; /* Number of soft scopes above */
|
||||
};
|
||||
|
||||
struct bytestring {
|
||||
|
@ -190,6 +193,9 @@ struct symbol *cf_get_symbol(const byte *c);
|
|||
struct symbol *cf_default_name(char *template, int *counter);
|
||||
struct symbol *cf_localize_symbol(struct symbol *sym);
|
||||
|
||||
static inline int cf_symbol_is_local(struct symbol *sym)
|
||||
{ return (sym->scope == conf_this_scope) && !conf_this_scope->soft_scopes; }
|
||||
|
||||
/**
|
||||
* cf_define_symbol - define meaning of a symbol
|
||||
* @sym: symbol to be defined
|
||||
|
@ -213,6 +219,15 @@ struct symbol *cf_localize_symbol(struct symbol *sym);
|
|||
|
||||
void cf_push_scope(struct symbol *);
|
||||
void cf_pop_scope(void);
|
||||
void cf_push_soft_scope(void);
|
||||
void cf_pop_soft_scope(void);
|
||||
|
||||
static inline void cf_push_block_scope(void)
|
||||
{ cf_push_scope(NULL); conf_this_scope->block = 1; }
|
||||
|
||||
static inline void cf_pop_block_scope(void)
|
||||
{ ASSERT(conf_this_scope->block); cf_pop_scope(); }
|
||||
|
||||
char *cf_symbol_class_name(struct symbol *sym);
|
||||
|
||||
/* Parser */
|
||||
|
|
|
@ -75,6 +75,7 @@ CF_DECLS
|
|||
struct f_static_attr fsa;
|
||||
struct f_lval flv;
|
||||
struct f_line *fl;
|
||||
struct f_arg *fa;
|
||||
const struct filter *f;
|
||||
struct f_tree *e;
|
||||
struct f_trie *trie;
|
||||
|
@ -110,7 +111,7 @@ CF_DECLS
|
|||
%type <i> expr bool pxlen4
|
||||
%type <time> expr_us time
|
||||
%type <a> ipa
|
||||
%type <net> net_ip4_ net_ip6_ net_ip6 net_ip_ net_ip net_or_ipa
|
||||
%type <net> net_ip4_ 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
|
||||
|
||||
|
@ -152,14 +153,14 @@ 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");
|
||||
if (f_eval(f_linearize($4, 1), cfg_mem, val) > F_RETURN) cf_error("Runtime error");
|
||||
cf_define_symbol($2, SYM_CONSTANT | val->type, val, val);
|
||||
}
|
||||
;
|
||||
|
||||
expr:
|
||||
NUM
|
||||
| '(' term ')' { $$ = f_eval_int(f_linearize($2)); }
|
||||
| '(' term ')' { $$ = f_eval_int(f_linearize($2, 1)); }
|
||||
| CF_SYM_KNOWN {
|
||||
if ($1->class != (SYM_CONSTANT | T_INT)) cf_error("Number constant expected");
|
||||
$$ = SYM_VAL($1).i; }
|
||||
|
@ -303,6 +304,15 @@ net_:
|
|||
|
||||
/* Networks - regular */
|
||||
|
||||
net_ip4:
|
||||
net_ip4_
|
||||
| CF_SYM_KNOWN {
|
||||
if (($1->class != (SYM_CONSTANT | T_NET)) || (SYM_VAL($1).net->type != NET_IP4))
|
||||
cf_error("IPv4 network constant expected");
|
||||
$$ = * SYM_VAL($1).net;
|
||||
}
|
||||
;
|
||||
|
||||
net_ip6:
|
||||
net_ip6_
|
||||
| CF_SYM_KNOWN {
|
||||
|
|
|
@ -142,7 +142,7 @@ flow_frag_opts:
|
|||
;
|
||||
|
||||
flow4_item:
|
||||
flow_srcdst net_ip {
|
||||
flow_srcdst net_ip4 {
|
||||
flow_builder_set_type(this_flow, $1);
|
||||
flow_builder4_add_pfx(this_flow, (net_addr_ip4 *) &($2));
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ flow6_opts:
|
|||
flow_builder_init:
|
||||
{
|
||||
if (this_flow == NULL)
|
||||
this_flow = flow_builder_init(&root_pool);
|
||||
this_flow = flow_builder_init(config_pool); /* FIXME: This should be allocated from tmp in future */
|
||||
else
|
||||
flow_builder_clear(this_flow);
|
||||
};
|
||||
|
|
|
@ -238,6 +238,8 @@ else
|
|||
;;
|
||||
openbsd*)
|
||||
sysdesc=bsd
|
||||
CPPFLAGS="$CPPFLAGS -I/usr/local/include"
|
||||
LDFLAGS="$LDFLAGS -L/usr/local/lib"
|
||||
;;
|
||||
dragonfly*)
|
||||
sysdesc=bsd
|
||||
|
|
158
doc/bird.sgml
158
doc/bird.sgml
|
@ -684,6 +684,21 @@ to set options.
|
|||
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.
|
||||
|
||||
<tag><label id="rtable-gc-threshold">gc threshold <m/number/</tag>
|
||||
Specify a minimum amount of removed networks that triggers a garbage
|
||||
collection (GC) cycle. Default: 1000.
|
||||
|
||||
<tag><label id="rtable-gc-period">gc period <m/time/</tag>
|
||||
Specify a period of time between consecutive GC cycles. When there is a
|
||||
significant amount of route withdraws, GC cycles are executed repeatedly
|
||||
with given period time (with some random factor). When there is just
|
||||
small amount of changes, GC cycles are not executed. In extensive route
|
||||
server setups, running GC on hundreds of full BGP routing tables can
|
||||
take significant amount of time, therefore they should use higher GC
|
||||
periods. Default: adaptive, based on number of routing tables in the
|
||||
configuration. From 10 s (with <= 25 routing tables) up to 600 s (with
|
||||
>= 1500 routing tables).
|
||||
</descrip>
|
||||
|
||||
|
||||
|
@ -1098,7 +1113,7 @@ This argument can be omitted if there exists only a single instance.
|
|||
Show the list of symbols defined in the configuration (names of
|
||||
protocols, routing tables etc.).
|
||||
|
||||
<tag><label id="cli-show-route">show route [[for] <m/prefix/|<m/IP/] [table (<m/t/ | all)] [filter <m/f/|where <m/c/] [(export|preexport|noexport) <m/p/] [protocol <m/p/] [(stats|count)] [<m/options/]</tag>
|
||||
<tag><label id="cli-show-route">show route [[for] <m/prefix/|<m/IP/] [table (<m/t/ | all)] [(import|export) table <m/p/.<m/c/] [filter <m/f/|where <m/c/] [(export|preexport|noexport) <m/p/] [protocol <m/p/] [(stats|count)] [<m/options/]</tag>
|
||||
Show contents of specified routing tables, that is routes, their metrics
|
||||
and (in case the <cf/all/ switch is given) all their attributes.
|
||||
|
||||
|
@ -1118,6 +1133,11 @@ This argument can be omitted if there exists only a single instance.
|
|||
Last, the set of default tables is used: <cf/master4/, <cf/master6/ and
|
||||
each first table of any other network type.
|
||||
|
||||
<p>There are internal tables when <cf/(import|export) table/ options
|
||||
are used for some channels. They can be selected explicitly with
|
||||
<cf/(import|export) table/ switch, specifying protocol <m/p/ and
|
||||
channel name <m/c/.
|
||||
|
||||
<p>You can also ask for printing only routes processed and accepted by
|
||||
a given filter (<cf>filter <m/name/</cf> or <cf>filter { <m/filter/ }
|
||||
</cf> or matching a given condition (<cf>where <m/condition/</cf>).
|
||||
|
@ -1153,6 +1173,11 @@ This argument can be omitted if there exists only a single instance.
|
|||
restarted otherwise. Changes in filters usually lead to restart of
|
||||
affected protocols.
|
||||
|
||||
The previous configuration is saved and the user can switch back to it
|
||||
with <ref id="cli-configure-undo" name="configure undo"> command. The
|
||||
old saved configuration is released (even if the reconfiguration attempt
|
||||
fails due to e.g. a syntax error).
|
||||
|
||||
If <cf/soft/ option is used, changes in filters does not cause BIRD to
|
||||
restart affected protocols, therefore already accepted routes (according
|
||||
to old filters) would be still propagated, but new routes would be
|
||||
|
@ -1245,8 +1270,8 @@ this:
|
|||
|
||||
<code>
|
||||
filter not_too_far
|
||||
int var;
|
||||
{
|
||||
int var;
|
||||
if defined( rip_metric ) then
|
||||
var = rip_metric;
|
||||
else {
|
||||
|
@ -1275,9 +1300,9 @@ local variables. Recursion is not allowed. Function definitions look like this:
|
|||
|
||||
<code>
|
||||
function name ()
|
||||
int local_variable;
|
||||
{
|
||||
local_variable = 5;
|
||||
int local_variable;
|
||||
int another_variable = 5;
|
||||
}
|
||||
|
||||
function with_parameters (int parameter)
|
||||
|
@ -1286,16 +1311,19 @@ function with_parameters (int parameter)
|
|||
}
|
||||
</code>
|
||||
|
||||
<p>Unlike in C, variables are declared after the <cf/function/ line, but before
|
||||
the first <cf/{/. You can't declare variables in nested blocks. Functions are
|
||||
called like in C: <cf>name(); with_parameters(5);</cf>. Function may return
|
||||
values using the <cf>return <m/[expr]/</cf> command. Returning a value exits
|
||||
from current function (this is similar to C).
|
||||
<p>Like in C programming language, variables are declared inside function body,
|
||||
either at the beginning, or mixed with other statements. Declarations may
|
||||
contain initialization. You can also declare variables in nested blocks, such
|
||||
variables have scope restricted to such block. There is a deprecated syntax to
|
||||
declare variables after the <cf/function/ line, but before the first <cf/{/.
|
||||
Functions are called like in C: <cf>name(); with_parameters(5);</cf>. Function
|
||||
may return values using the <cf>return <m/[expr]/</cf> command. Returning a
|
||||
value exits from current function (this is similar to C).
|
||||
|
||||
<p>Filters are defined in a way similar to functions except they can't have
|
||||
<p>Filters are defined in a way similar to functions except they cannot have
|
||||
explicit parameters. They get a route table entry as an implicit parameter, it
|
||||
is also passed automatically to any functions called. The filter must terminate
|
||||
with either <cf/accept/ or <cf/reject/ statement. If there's a runtime error in
|
||||
with either <cf/accept/ or <cf/reject/ statement. If there is a runtime error in
|
||||
filter, the route is rejected.
|
||||
|
||||
<p>A nice trick to debug filters is to use <cf>show route filter <m/name/</cf>
|
||||
|
@ -1665,7 +1693,8 @@ prefix and an ASN as arguments.
|
|||
<sect>Control structures
|
||||
<label id="control-structures">
|
||||
|
||||
<p>Filters support two control structures: conditions and case switches.
|
||||
<p>Filters support several control structures: conditions, for loops and case
|
||||
switches.
|
||||
|
||||
<p>Syntax of a condition is: <cf>if <M>boolean expression</M> then <m/commandT/;
|
||||
else <m/commandF/;</cf> and you can use <cf>{ <m/command1/; <m/command2/;
|
||||
|
@ -1673,6 +1702,14 @@ else <m/commandF/;</cf> and you can use <cf>{ <m/command1/; <m/command2/;
|
|||
omitted. If the <cf><m>boolean expression</m></cf> is true, <m/commandT/ is
|
||||
executed, otherwise <m/commandF/ is executed.
|
||||
|
||||
<p>For loops allow to iterate over elements in compound data like BGP paths or
|
||||
community lists. The syntax is: <cf>for [ <m/type/ ] <m/variable/ in <m/expr/
|
||||
do <m/command/;</cf> and you can also use compound command like in conditions.
|
||||
The expression is evaluated to a compound data, then for each element from such
|
||||
data the command is executed with the item assigned to the variable. A variable
|
||||
may be an existing one (when just name is used) or a locally defined (when type
|
||||
and name is used). In both cases, it must have the same type as elements.
|
||||
|
||||
<p>The <cf>case</cf> is similar to case from Pascal. Syntax is <cf>case
|
||||
<m/expr/ { else: | <m/num_or_prefix [ .. num_or_prefix]/: <m/statement/ ; [
|
||||
... ] }</cf>. The expression after <cf>case</cf> can be of any type which can be
|
||||
|
@ -1685,16 +1722,21 @@ neither of the <cf/:/ clauses, the statements after <cf/else:/ are executed.
|
|||
<p>Here is example that uses <cf/if/ and <cf/case/ structures:
|
||||
|
||||
<code>
|
||||
if 1234 = i then printn "."; else {
|
||||
print "not 1234";
|
||||
print "You need {} around multiple commands";
|
||||
}
|
||||
|
||||
for int asn in bgp_path do {
|
||||
printn "ASN: ", asn;
|
||||
if asn < 65536 then print " (2B)"; else print " (4B)";
|
||||
}
|
||||
|
||||
case arg1 {
|
||||
2: print "two"; print "I can do more commands without {}";
|
||||
3 .. 5: print "three to five";
|
||||
else: print "something else";
|
||||
}
|
||||
|
||||
if 1234 = i then printn "."; else {
|
||||
print "not 1234";
|
||||
print "You need {} around multiple commands";
|
||||
}
|
||||
</code>
|
||||
|
||||
|
||||
|
@ -1747,7 +1789,7 @@ Common route attributes are:
|
|||
|
||||
<tag><label id="rta-source"><m/enum/ source</tag>
|
||||
what protocol has told me about this route. Possible values:
|
||||
<cf/RTS_DUMMY/, <cf/RTS_STATIC/, <cf/RTS_INHERIT/, <cf/RTS_DEVICE/,
|
||||
<cf/RTS_STATIC/, <cf/RTS_INHERIT/, <cf/RTS_DEVICE/,
|
||||
<cf/RTS_RIP/, <cf/RTS_OSPF/, <cf/RTS_OSPF_IA/, <cf/RTS_OSPF_EXT1/,
|
||||
<cf/RTS_OSPF_EXT2/, <cf/RTS_BGP/, <cf/RTS_PIPE/, <cf/RTS_BABEL/.
|
||||
|
||||
|
@ -1883,7 +1925,7 @@ protocol babel [<name>] {
|
|||
rtt cost <number>;
|
||||
rtt min <time>;
|
||||
rtt max <time>;
|
||||
rtt decay <number>;
|
||||
rtt winlen <number>;
|
||||
send timestamps <switch>;
|
||||
authentication none|mac [permissive];
|
||||
password "<text>";
|
||||
|
@ -2022,10 +2064,10 @@ protocol babel [<name>] {
|
|||
The maximum RTT above which the full RTT cost will start be applied.
|
||||
Default: 120 ms
|
||||
|
||||
<tag><label id="babel-rtt-decay">rtt decay <m/number/</tag>
|
||||
The decay factor used for the exponentional moving average of the RTT
|
||||
samples from each neighbour, in units of 1/256. Higher values discards old
|
||||
RTT samples faster. Must be between 1 and 256. Default: 42
|
||||
<tag><label id="babel-rtt-winlen">rtt winlen <m/number/</tag>
|
||||
The window length user to calculate median of the RTT
|
||||
samples from each neighbour, Lower values discards old
|
||||
RTT samples faster. Must be between 1 and 65535. Default: 100
|
||||
|
||||
<tag><label id="babel-send-timestamps">send timestamps <m/switch/</tag>
|
||||
Whether to send the timestamps used for RTT calculation on this interface.
|
||||
|
@ -2394,6 +2436,7 @@ avoid routing loops.
|
|||
<item> <rfc id="8203"> - BGP Administrative Shutdown Communication
|
||||
<item> <rfc id="8212"> - Default EBGP Route Propagation Behavior without Policies
|
||||
<item> <rfc id="9117"> - Revised Validation Procedure for BGP Flow Specifications
|
||||
<item> <rfc id="9234"> - Route Leak Prevention and Detection Using Roles
|
||||
</itemize>
|
||||
|
||||
<sect1>Route selection rules
|
||||
|
@ -2756,9 +2799,16 @@ using the following configuration parameters:
|
|||
|
||||
<tag><label id="bgp-hold-time">hold time <m/number/</tag>
|
||||
Time in seconds to wait for a Keepalive message from the other side
|
||||
before considering the connection stale. Default: depends on agreement
|
||||
with the neighboring router, we prefer 240 seconds if the other side is
|
||||
willing to accept it.
|
||||
before considering the connection stale. The effective value is
|
||||
negotiated during session establishment and it is a minimum of this
|
||||
configured value and the value proposed by the peer. The zero value has
|
||||
a special meaning, signifying that no keepalives are used. Default: 240
|
||||
seconds.
|
||||
|
||||
<tag><label id="bgp-min-hold-time">min hold time <m/number/</tag>
|
||||
Minimum value of the hold time that is accepted during session negotiation.
|
||||
If the peer proposes a lower value, the session is rejected with error.
|
||||
Default: none.
|
||||
|
||||
<tag><label id="bgp-startup-hold-time">startup hold time <m/number/</tag>
|
||||
Value of the hold timer used before the routers have a chance to exchange
|
||||
|
@ -2766,8 +2816,15 @@ using the following configuration parameters:
|
|||
|
||||
<tag><label id="bgp-keepalive-time">keepalive time <m/number/</tag>
|
||||
Delay in seconds between sending of two consecutive Keepalive messages.
|
||||
The effective value depends on the negotiated hold time, as it is scaled
|
||||
to maintain proportion between the keepalive time and the hold time.
|
||||
Default: One third of the hold time.
|
||||
|
||||
<tag><label id="bgp-min-keepalive-time">min keepalive time <m/number/</tag>
|
||||
Minimum value of the keepalive time that is accepted during session
|
||||
negotiation. If the proposed hold time would lead to a lower value of
|
||||
the keepalive time, the session is rejected with error. Default: none.
|
||||
|
||||
<tag><label id="bgp-connect-delay-time">connect delay time <m/number/</tag>
|
||||
Delay in seconds between protocol startup and the first attempt to
|
||||
connect. Default: 5 seconds.
|
||||
|
@ -2834,6 +2891,29 @@ using the following configuration parameters:
|
|||
protocol itself (for example, if a route is received through eBGP and
|
||||
therefore does not have such attribute). Default: 100 (0 in pre-1.2.0
|
||||
versions of BIRD).
|
||||
|
||||
<tag><label id="bgp-local-role">local role <m/role-name/</tag>
|
||||
BGP roles are a mechanism for route leak prevention and automatic route
|
||||
filtering based on common BGP topology relationships. They are defined
|
||||
in <rfc id="9234">. Instead of manually configuring filters and
|
||||
communities, automatic filtering is done with the help of the OTC
|
||||
attribute - a flag for routes that should be sent only to customers.
|
||||
The same attribute is also used to automatically detect and filter route
|
||||
leaks created by third parties.
|
||||
|
||||
This option is valid for EBGP sessions, but it is not recommended to be
|
||||
used within AS confederations (which would require manual filtering of
|
||||
<cf/bgp_otc/ attribute on confederation boundaries).
|
||||
|
||||
Possible <cf><m/role-name/</cf> values are: <cf/provider/,
|
||||
<cf/rs_server/, <cf/rs_client/, <cf/customer/ and <cf/peer/.
|
||||
Default: No local role assigned.
|
||||
|
||||
<tag><label id="bgp-require-roles">require roles <m/switch/</tag>
|
||||
If this option is set, the BGP roles must be defined on both sides,
|
||||
otherwise the session will not be established. This behavior is defined
|
||||
in <rfc id="9234"> as "strict mode" and is used to enforce corresponding
|
||||
configuration at your conterpart side. Default: disabled.
|
||||
</descrip>
|
||||
|
||||
<sect1>Channel configuration
|
||||
|
@ -2904,6 +2984,20 @@ be used in explicit configuration.
|
|||
BGP session (if acceptable), or the preferred address of an associated
|
||||
interface.
|
||||
|
||||
<tag><label id="bgp-next-hop-prefer">next hop prefer global</tag>
|
||||
Prefer global IPv6 address to link-local IPv6 address for immediate next
|
||||
hops of received routes. For IPv6 routes, the Next Hop attribute may
|
||||
contain both a global IP address and a link-local IP address. For IBGP
|
||||
sessions, the global IP address is resolved (<ref id="bgp-gateway"
|
||||
name="gateway recursive">) through an IGP routing table
|
||||
(<ref id="bgp-igp-table" name="igp table">) to get an immediate next
|
||||
hop. If the resulting IGP route is a direct route (i.e., the next hop is
|
||||
a direct neighbor), then the link-local IP address from the Next Hop
|
||||
attribute is used as the immediate next hop. This option change it to
|
||||
use the global IP address instead. Note that even with this option
|
||||
enabled a route may end with a link-local immediate next hop when the
|
||||
IGP route has one. Default: disabled.
|
||||
|
||||
<tag><label id="bgp-gateway">gateway direct|recursive</tag>
|
||||
For received routes, their <cf/gw/ (immediate next hop) attribute is
|
||||
computed from received <cf/bgp_next_hop/ attribute. This option
|
||||
|
@ -2937,6 +3031,13 @@ be used in explicit configuration.
|
|||
be examined later by <cf/show route/, and can be used to reconfigure
|
||||
import filters without full route refresh. Default: off.
|
||||
|
||||
Note that currently the import table breaks routes with recursive
|
||||
nexthops (e.g. ones from IBGP, see <ref id="bgp-gateway" name="gateway
|
||||
recursive">), they are not properly updated after next hop change. For
|
||||
the same reason, it also breaks re-evaluation of flowspec routes with
|
||||
<ref id="bgp-validate" name="flowspec validation"> option enabled on
|
||||
flowspec channels.
|
||||
|
||||
<tag><label id="bgp-export-table">export table <m/switch/</tag>
|
||||
A BGP export table contains all routes sent to given BGP neighbor, after
|
||||
application of export filters. It is also called <em/Adj-RIB-Out/ in BGP
|
||||
|
@ -3141,6 +3242,11 @@ some of them (marked with `<tt/O/') are optional.
|
|||
This attribute contains accumulated IGP metric, which is a total
|
||||
distance to the destination through multiple autonomous systems.
|
||||
Currently, the attribute is not accessible from filters.
|
||||
|
||||
<tag><label id="bgp-otc">int bgp_otc [O]</tag>
|
||||
This attribute is defined in <rfc id="9234">. OTC is a flag that marks
|
||||
routes that should be sent only to customers. If <ref id="bgp-role"
|
||||
name="local Role"> is configured it set automatically.
|
||||
</descrip>
|
||||
|
||||
<sect1>Example
|
||||
|
|
187
filter/config.Y
187
filter/config.Y
|
@ -22,6 +22,36 @@ static inline u32 pair_b(u32 p) { return p & 0xFFFF; }
|
|||
#define f_generate_complex(fi_code, da, arg) \
|
||||
f_new_inst(FI_EA_SET, f_new_inst(fi_code, f_new_inst(FI_EA_GET, da), arg), da)
|
||||
|
||||
static int
|
||||
f_new_var(struct sym_scope *s)
|
||||
{
|
||||
/*
|
||||
* - A variable is an offset on vstack from vbase.
|
||||
* - Vbase is set on filter start / function call.
|
||||
* - Scopes contain (non-frame) block scopes inside filter/function scope
|
||||
* - Each scope knows number of vars in that scope
|
||||
* - Offset is therefore a sum of 'slots' up to filter/function scope
|
||||
* - New variables are added on top of vstk, so intermediate values cannot
|
||||
* be there during FI_VAR_INIT. I.e. no 'var' inside 'term'.
|
||||
* - Also, each f_line must always have its scope, otherwise a variable may
|
||||
* be defined but not initialized if relevant f_line is not executed.
|
||||
*/
|
||||
|
||||
int offset = s->slots++;
|
||||
|
||||
while (s->block)
|
||||
{
|
||||
s = s->next;
|
||||
ASSERT(s);
|
||||
offset += s->slots;
|
||||
}
|
||||
|
||||
if (offset >= 0xff)
|
||||
cf_error("Too many variables, at most 255 allowed");
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets and their items are during parsing handled as lists, linked
|
||||
* through left ptr. The first item in a list also contains a pointer
|
||||
|
@ -248,10 +278,6 @@ assert_assign(struct f_lval *lval, struct f_inst *expr, const char *start, const
|
|||
setter = f_new_inst(FI_VAR_SET, expr, lval->sym);
|
||||
getter = f_new_inst(FI_VAR_GET, lval->sym);
|
||||
break;
|
||||
case F_LVAL_PREFERENCE:
|
||||
setter = f_new_inst(FI_PREF_SET, expr);
|
||||
getter = f_new_inst(FI_PREF_GET);
|
||||
break;
|
||||
case F_LVAL_SA:
|
||||
setter = f_new_inst(FI_RTA_SET, expr, lval->sa);
|
||||
getter = f_new_inst(FI_RTA_GET, lval->sa);
|
||||
|
@ -277,6 +303,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
|||
INT, BOOL, IP, TYPE, PREFIX, RD, PAIR, QUAD, EC, LC,
|
||||
SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
|
||||
IF, THEN, ELSE, CASE,
|
||||
FOR, IN, DO,
|
||||
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
|
||||
FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS,
|
||||
PREFERENCE,
|
||||
|
@ -296,13 +323,14 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
|||
%nonassoc ELSE
|
||||
|
||||
%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 <x> term cmd cmd_var cmds cmds_scoped constant constructor print_list var var_init var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail
|
||||
%type <fda> dynamic_attr
|
||||
%type <fsa> static_attr
|
||||
%type <f> filter where_filter
|
||||
%type <fl> filter_body function_body
|
||||
%type <flv> lvalue
|
||||
%type <i> type function_args function_vars
|
||||
%type <i> type function_vars
|
||||
%type <fa> function_argsn function_args
|
||||
%type <ecs> ec_kind
|
||||
%type <fret> break_command
|
||||
%type <i32> cnum
|
||||
|
@ -311,6 +339,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
|||
%type <v> set_atom switch_atom fipa
|
||||
%type <px> fprefix
|
||||
%type <t> get_cf_position
|
||||
%type <s> for_var
|
||||
|
||||
CF_GRAMMAR
|
||||
|
||||
|
@ -328,7 +357,7 @@ filter_def:
|
|||
|
||||
conf: filter_eval ;
|
||||
filter_eval:
|
||||
EVAL term { f_eval_int(f_linearize($2)); }
|
||||
EVAL term { f_eval_int(f_linearize($2, 1)); }
|
||||
;
|
||||
|
||||
conf: custom_attr ;
|
||||
|
@ -403,25 +432,28 @@ type:
|
|||
;
|
||||
|
||||
function_argsn:
|
||||
/* EMPTY */
|
||||
/* EMPTY */ { $$ = NULL; }
|
||||
| function_argsn type symbol ';' {
|
||||
if ($3->scope->slots >= 0xfe) cf_error("Too many declarations, at most 255 allowed");
|
||||
cf_define_symbol($3, SYM_VARIABLE | $2, offset, $3->scope->slots++);
|
||||
$$ = cfg_alloc(sizeof(struct f_arg));
|
||||
$$->arg = cf_define_symbol($3, SYM_VARIABLE | $2, offset, sym_->scope->slots++);
|
||||
$$->next = $1;
|
||||
}
|
||||
;
|
||||
|
||||
function_args:
|
||||
'(' ')' { $$ = 0; }
|
||||
'(' ')' { $$ = NULL; }
|
||||
| '(' function_argsn type symbol ')' {
|
||||
cf_define_symbol($4, SYM_VARIABLE | $3, offset, $4->scope->slots++);
|
||||
$$ = $4->scope->slots;
|
||||
$$ = cfg_alloc(sizeof(struct f_arg));
|
||||
$$->arg = cf_define_symbol($4, SYM_VARIABLE | $3, offset, sym_->scope->slots++);
|
||||
$$->next = $2;
|
||||
}
|
||||
;
|
||||
|
||||
function_vars:
|
||||
/* EMPTY */ { $$ = 0; }
|
||||
| function_vars type symbol ';' {
|
||||
cf_define_symbol($3, SYM_VARIABLE | $2, offset, $3->scope->slots++);
|
||||
cf_define_symbol($3, SYM_VARIABLE | $2, offset, f_new_var(sym_->scope));
|
||||
$$ = $1 + 1;
|
||||
}
|
||||
;
|
||||
|
@ -433,10 +465,12 @@ filter:
|
|||
cf_assert_symbol($1, SYM_FILTER);
|
||||
$$ = $1->filter;
|
||||
}
|
||||
| filter_body {
|
||||
| { cf_push_scope(NULL); } filter_body {
|
||||
struct filter *f = cfg_alloc(sizeof(struct filter));
|
||||
*f = (struct filter) { .root = $1 };
|
||||
*f = (struct filter) { .root = $2 };
|
||||
$$ = f;
|
||||
|
||||
cf_pop_scope();
|
||||
}
|
||||
;
|
||||
|
||||
|
@ -449,20 +483,35 @@ where_filter:
|
|||
|
||||
function_body:
|
||||
function_vars '{' cmds '}' {
|
||||
$$ = f_linearize($3);
|
||||
$$ = f_linearize($3, 0);
|
||||
$$->vars = $1;
|
||||
}
|
||||
;
|
||||
|
||||
conf: function_def ;
|
||||
function_def:
|
||||
FUNCTION symbol { DBG( "Beginning of function %s\n", $2->name );
|
||||
FUNCTION symbol {
|
||||
DBG( "Beginning of function %s\n", $2->name );
|
||||
$2 = cf_define_symbol($2, SYM_FUNCTION, function, NULL);
|
||||
cf_push_scope($2);
|
||||
} function_args function_body {
|
||||
DBG("Definition of function %s with %u args and %u local vars.\n", $2->name, $4, $5->vars);
|
||||
$5->args = $4;
|
||||
$2->function = $5;
|
||||
} function_args {
|
||||
/* Make dummy f_line for storing function prototype */
|
||||
struct f_line *dummy = cfg_allocz(sizeof(struct f_line));
|
||||
$2->function = dummy;
|
||||
|
||||
/* Revert the args */
|
||||
while ($4) {
|
||||
struct f_arg *tmp = $4;
|
||||
$4 = $4->next;
|
||||
|
||||
tmp->next = dummy->arg_list;
|
||||
dummy->arg_list = tmp;
|
||||
dummy->args++;
|
||||
}
|
||||
} function_body {
|
||||
$6->args = $2->function->args;
|
||||
$6->arg_list = $2->function->arg_list;
|
||||
$2->function = $6;
|
||||
cf_pop_scope();
|
||||
}
|
||||
;
|
||||
|
@ -473,7 +522,11 @@ cmds: /* EMPTY */ { $$ = NULL; }
|
|||
| cmds_int { $$ = $1.begin; }
|
||||
;
|
||||
|
||||
cmd_prep: cmd {
|
||||
cmds_scoped: { cf_push_soft_scope(); } cmds { cf_pop_soft_scope(); $$ = $2; } ;
|
||||
|
||||
cmd_var: var | cmd ;
|
||||
|
||||
cmd_prep: cmd_var {
|
||||
$$.begin = $$.end = $1;
|
||||
if ($1)
|
||||
while ($$.end->next)
|
||||
|
@ -495,15 +548,6 @@ cmds_int: cmd_prep
|
|||
}
|
||||
;
|
||||
|
||||
block:
|
||||
cmd {
|
||||
$$=$1;
|
||||
}
|
||||
| '{' cmds '}' {
|
||||
$$=$2;
|
||||
}
|
||||
;
|
||||
|
||||
/*
|
||||
* Complex types, their bison value is struct f_val
|
||||
*/
|
||||
|
@ -527,7 +571,7 @@ 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, 1), cfg_mem, &($$)) > F_RETURN) cf_error("Runtime error");
|
||||
if (!f_valid_set_type($$.type)) cf_error("Set-incompatible type");
|
||||
}
|
||||
| CF_SYM_KNOWN {
|
||||
|
@ -539,13 +583,13 @@ set_atom:
|
|||
|
||||
switch_atom:
|
||||
NUM { $$.type = T_INT; $$.val.i = $1; }
|
||||
| '(' term ')' { $$.type = T_INT; $$.val.i = f_eval_int(f_linearize($2)); }
|
||||
| '(' term ')' { $$.type = T_INT; $$.val.i = f_eval_int(f_linearize($2, 1)); }
|
||||
| fipa { $$ = $1; }
|
||||
| ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
|
||||
;
|
||||
|
||||
cnum:
|
||||
term { $$ = f_eval_int(f_linearize($1)); }
|
||||
term { $$ = f_eval_int(f_linearize($1, 1)); }
|
||||
|
||||
pair_item:
|
||||
'(' cnum ',' cnum ')' { $$ = f_new_pair_item($2, $2, $4, $4); }
|
||||
|
@ -629,19 +673,19 @@ fprefix_set:
|
|||
;
|
||||
|
||||
switch_body: /* EMPTY */ { $$ = NULL; }
|
||||
| switch_body switch_items ':' cmds {
|
||||
| switch_body switch_items ':' cmds_scoped {
|
||||
/* Fill data fields */
|
||||
struct f_tree *t;
|
||||
struct f_line *line = f_linearize($4);
|
||||
struct f_line *line = f_linearize($4, 0);
|
||||
for (t = $2; t; t = t->left)
|
||||
t->data = line;
|
||||
$$ = f_merge_items($1, $2);
|
||||
}
|
||||
| switch_body ELSECOL cmds {
|
||||
| switch_body ELSECOL cmds_scoped {
|
||||
struct f_tree *t = f_new_tree();
|
||||
t->from.type = t->to.type = T_VOID;
|
||||
t->right = t;
|
||||
t->data = f_linearize($3);
|
||||
t->data = f_linearize($3, 0);
|
||||
$$ = f_merge_items($1, t);
|
||||
}
|
||||
;
|
||||
|
@ -658,6 +702,7 @@ bgp_path:
|
|||
bgp_path_tail:
|
||||
NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .asn = $1, .kind = PM_ASN, }, }); $$->next = $2; }
|
||||
| NUM DDOT NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .from = $1, .to = $3, .kind = PM_ASN_RANGE }, }); $$->next = $4; }
|
||||
| '[' ']' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .set = NULL, .kind = PM_ASN_SET }, }); $$->next = $3; }
|
||||
| '[' set_items ']' bgp_path_tail {
|
||||
if ($2->from.type != T_INT) cf_error("Only integer sets allowed in path mask");
|
||||
$$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .set = build_tree($2), .kind = PM_ASN_SET }, }); $$->next = $4;
|
||||
|
@ -677,6 +722,7 @@ constant:
|
|||
| fipa { $$ = f_new_inst(FI_CONSTANT, $1); }
|
||||
| VPN_RD { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_RD, .val.ec = $1, }); }
|
||||
| net_ { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_NET, .val.net = $1, }); }
|
||||
| '[' ']' { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_SET, .val.t = NULL, }); }
|
||||
| '[' set_items ']' {
|
||||
DBG( "We've got a set here..." );
|
||||
$$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_SET, .val.t = build_tree($2), });
|
||||
|
@ -700,27 +746,22 @@ var_list: /* EMPTY */ { $$ = NULL; }
|
|||
| var_list ',' term { $$ = $3; $$->next = $1; }
|
||||
|
||||
function_call:
|
||||
CF_SYM_KNOWN '(' var_list ')' {
|
||||
CF_SYM_KNOWN '(' var_list ')'
|
||||
{
|
||||
if ($1->class != SYM_FUNCTION)
|
||||
cf_error("You can't call something which is not a function. Really.");
|
||||
|
||||
struct f_inst *fc = f_new_inst(FI_CALL, $1);
|
||||
uint args = 0;
|
||||
/* Revert the var_list */
|
||||
struct f_inst *args = NULL;
|
||||
while ($3) {
|
||||
args++;
|
||||
struct f_inst *tmp = $3->next;
|
||||
$3->next = fc;
|
||||
struct f_inst *tmp = $3;
|
||||
$3 = $3->next;
|
||||
|
||||
fc = $3;
|
||||
$3 = tmp;
|
||||
tmp->next = args;
|
||||
args = tmp;
|
||||
}
|
||||
|
||||
if (args != $1->function->args)
|
||||
cf_error("Function call '%s' got %u arguments, need %u arguments.",
|
||||
$1->name, args, $1->function->args);
|
||||
|
||||
$$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_VOID });
|
||||
$$->next = fc;
|
||||
$$ = f_new_inst(FI_CALL, args, $1);
|
||||
}
|
||||
;
|
||||
|
||||
|
@ -753,6 +794,7 @@ static_attr:
|
|||
| 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); }
|
||||
;
|
||||
|
||||
|
@ -779,8 +821,6 @@ term:
|
|||
| constant { $$ = $1; }
|
||||
| constructor { $$ = $1; }
|
||||
|
||||
| PREFERENCE { $$ = f_new_inst(FI_PREF_GET); }
|
||||
|
||||
| static_attr { $$ = f_new_inst(FI_RTA_GET, $1); }
|
||||
|
||||
| dynamic_attr { $$ = f_new_inst(FI_EA_GET, $1); }
|
||||
|
@ -847,13 +887,44 @@ print_list: /* EMPTY */ { $$ = NULL; }
|
|||
}
|
||||
;
|
||||
|
||||
var_init:
|
||||
/* empty */ { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { }); }
|
||||
| '=' term { $$ = $2; }
|
||||
;
|
||||
|
||||
var:
|
||||
type symbol var_init ';' {
|
||||
struct symbol *sym = cf_define_symbol($2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope));
|
||||
$$ = f_new_inst(FI_VAR_INIT, $3, sym);
|
||||
}
|
||||
|
||||
for_var:
|
||||
type symbol { $$ = cf_define_symbol($2, SYM_VARIABLE | $1, offset, f_new_var(sym_->scope)); }
|
||||
| CF_SYM_KNOWN { $$ = $1; cf_assert_symbol($1, SYM_VARIABLE); }
|
||||
;
|
||||
|
||||
cmd:
|
||||
IF term THEN block {
|
||||
'{' cmds_scoped '}' {
|
||||
$$ = $2;
|
||||
}
|
||||
| IF term THEN cmd {
|
||||
$$ = f_new_inst(FI_CONDITION, $2, $4, NULL);
|
||||
}
|
||||
| IF term THEN block ELSE block {
|
||||
| IF term THEN cmd ELSE cmd {
|
||||
$$ = f_new_inst(FI_CONDITION, $2, $4, $6);
|
||||
}
|
||||
| FOR {
|
||||
/* Reserve space for walk data on stack */
|
||||
cf_push_block_scope();
|
||||
conf_this_scope->slots += 2;
|
||||
} for_var IN
|
||||
/* Parse term in the parent scope */
|
||||
{ conf_this_scope->active = 0; } term { conf_this_scope->active = 1; }
|
||||
DO cmd {
|
||||
cf_pop_block_scope();
|
||||
$$ = f_new_inst(FI_FOR_INIT, $6, $3);
|
||||
$$->next = f_new_inst(FI_FOR_NEXT, $3, $9);
|
||||
}
|
||||
| CF_SYM_KNOWN '=' term ';' {
|
||||
switch ($1->class) {
|
||||
case SYM_VARIABLE_RANGE:
|
||||
|
@ -878,9 +949,6 @@ cmd:
|
|||
cf_error( "This static attribute is read-only.");
|
||||
$$ = f_new_inst(FI_RTA_SET, $3, $1);
|
||||
}
|
||||
| PREFERENCE '=' term ';' {
|
||||
$$ = f_new_inst(FI_PREF_SET, $3);
|
||||
}
|
||||
| UNSET '(' dynamic_attr ')' ';' {
|
||||
$$ = f_new_inst(FI_EA_UNSET, $3);
|
||||
}
|
||||
|
@ -923,7 +991,6 @@ get_cf_position:
|
|||
|
||||
lvalue:
|
||||
CF_SYM_KNOWN { cf_assert_symbol($1, SYM_VARIABLE); $$ = (struct f_lval) { .type = F_LVAL_VARIABLE, .sym = $1 }; }
|
||||
| PREFERENCE { $$ = (struct f_lval) { .type = F_LVAL_PREFERENCE }; }
|
||||
| static_attr { $$ = (struct f_lval) { .type = F_LVAL_SA, .sa = $1 }; }
|
||||
| dynamic_attr { $$ = (struct f_lval) { .type = F_LVAL_EA, .da = $1 }; };
|
||||
|
||||
|
|
|
@ -72,6 +72,7 @@ enum f_type
|
|||
f_type_element_type(enum f_type t)
|
||||
{
|
||||
switch(t) {
|
||||
case T_PATH: return T_INT;
|
||||
case T_CLIST: return T_PAIR;
|
||||
case T_ECLIST: return T_EC;
|
||||
case T_LCLIST: return T_LC;
|
||||
|
@ -79,6 +80,8 @@ f_type_element_type(enum f_type t)
|
|||
};
|
||||
}
|
||||
|
||||
const struct f_trie f_const_empty_trie = { .ipv4 = -1, };
|
||||
|
||||
const struct f_val f_const_empty_path = {
|
||||
.type = T_PATH,
|
||||
.val.ad = &null_adata,
|
||||
|
@ -91,6 +94,9 @@ const struct f_val f_const_empty_path = {
|
|||
}, f_const_empty_lclist = {
|
||||
.type = T_LCLIST,
|
||||
.val.ad = &null_adata,
|
||||
}, f_const_empty_prefix_set = {
|
||||
.type = T_PREFIX_SET,
|
||||
.val.ti = &f_const_empty_trie,
|
||||
};
|
||||
|
||||
static struct adata *
|
||||
|
@ -187,7 +193,7 @@ val_compare(const struct f_val *v1, const struct f_val *v2)
|
|||
if (val_is_ip4(v1) && (v2->type == T_QUAD))
|
||||
return uint_cmp(ipa_to_u32(v1->val.ip), v2->val.i);
|
||||
|
||||
debug( "Types do not match in val_compare\n" );
|
||||
DBG( "Types do not match in val_compare\n" );
|
||||
return F_CMP_ERROR;
|
||||
}
|
||||
|
||||
|
@ -301,6 +307,12 @@ val_same(const struct f_val *v1, const struct f_val *v2)
|
|||
int
|
||||
clist_set_type(const struct f_tree *set, struct f_val *v)
|
||||
{
|
||||
if (!set)
|
||||
{
|
||||
v->type = T_VOID;
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (set->from.type)
|
||||
{
|
||||
case T_PAIR:
|
||||
|
@ -324,7 +336,7 @@ clist_set_type(const struct f_tree *set, struct f_val *v)
|
|||
}
|
||||
}
|
||||
|
||||
static int
|
||||
int
|
||||
clist_match_set(const struct adata *clist, const struct f_tree *set)
|
||||
{
|
||||
if (!clist)
|
||||
|
@ -345,7 +357,7 @@ clist_match_set(const struct adata *clist, const struct f_tree *set)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
int
|
||||
eclist_match_set(const struct adata *list, const struct f_tree *set)
|
||||
{
|
||||
if (!list)
|
||||
|
@ -369,7 +381,7 @@ eclist_match_set(const struct adata *list, const struct f_tree *set)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
int
|
||||
lclist_match_set(const struct adata *list, const struct f_tree *set)
|
||||
{
|
||||
if (!list)
|
||||
|
@ -537,6 +549,9 @@ val_in_range(const struct f_val *v1, const struct f_val *v2)
|
|||
if (v2->type != T_SET)
|
||||
return F_CMP_ERROR;
|
||||
|
||||
if (!v2->val.t)
|
||||
return 0;
|
||||
|
||||
/* With integrated Quad<->IP implicit conversion */
|
||||
if ((v1->type == v2->val.t->from.type) ||
|
||||
((v1->type == T_QUAD) && val_is_ip4(&(v2->val.t->from)) && val_is_ip4(&(v2->val.t->to))))
|
||||
|
|
|
@ -100,6 +100,7 @@ enum f_sa_code {
|
|||
SA_IFNAME,
|
||||
SA_IFINDEX,
|
||||
SA_WEIGHT,
|
||||
SA_PREF,
|
||||
SA_GW_MPLS,
|
||||
} PACKED;
|
||||
|
||||
|
@ -191,6 +192,7 @@ 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);
|
||||
int same_tree(const struct f_tree *t0, const struct f_tree *t2);
|
||||
int tree_node_count(const struct f_tree *t);
|
||||
void tree_format(const struct f_tree *t, buffer *buf);
|
||||
void tree_walk(const struct f_tree *t, void (*hook)(const struct f_tree *, void *), void *data);
|
||||
|
||||
|
@ -279,9 +281,15 @@ int val_in_range(const struct f_val *v1, const struct f_val *v2);
|
|||
|
||||
int clist_set_type(const struct f_tree *set, struct f_val *v);
|
||||
static inline int eclist_set_type(const struct f_tree *set)
|
||||
{ return set->from.type == T_EC; }
|
||||
{ return !set || set->from.type == T_EC; }
|
||||
static inline int lclist_set_type(const struct f_tree *set)
|
||||
{ return set->from.type == T_LC; }
|
||||
{ return !set || set->from.type == T_LC; }
|
||||
static inline int path_set_type(const struct f_tree *set)
|
||||
{ return !set || set->from.type == T_INT; }
|
||||
|
||||
int clist_match_set(const struct adata *clist, const struct f_tree *set);
|
||||
int eclist_match_set(const struct adata *list, const struct f_tree *set);
|
||||
int lclist_match_set(const struct adata *list, const struct f_tree *set);
|
||||
|
||||
const struct adata *clist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos);
|
||||
const struct adata *eclist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos);
|
||||
|
@ -297,7 +305,7 @@ undef_value(struct f_val v)
|
|||
(v.val.ad == &null_adata);
|
||||
}
|
||||
|
||||
extern const struct f_val f_const_empty_path, f_const_empty_clist, f_const_empty_eclist, f_const_empty_lclist;
|
||||
extern const struct f_val f_const_empty_path, f_const_empty_clist, f_const_empty_eclist, f_const_empty_lclist, f_const_empty_prefix_set;
|
||||
|
||||
enum filter_return f_eval(const struct f_line *expr, struct linpool *tmp_pool, struct f_val *pres);
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ m4_divert(-1)m4_dnl
|
|||
#
|
||||
# 101 content of per-inst struct
|
||||
# 102 constructor arguments
|
||||
# 110 constructor attributes
|
||||
# 103 constructor body
|
||||
# 104 dump line item content
|
||||
# (there may be nothing in dump-line content and
|
||||
|
@ -45,6 +46,7 @@ m4_divert(-1)m4_dnl
|
|||
# Here are macros to allow you to _divert to the right directions.
|
||||
m4_define(FID_STRUCT_IN, `m4_divert(101)')
|
||||
m4_define(FID_NEW_ARGS, `m4_divert(102)')
|
||||
m4_define(FID_NEW_ATTRIBUTES, `m4_divert(110)')
|
||||
m4_define(FID_NEW_BODY, `m4_divert(103)')
|
||||
m4_define(FID_DUMP_BODY, `m4_divert(104)m4_define([[FID_DUMP_BODY_EXISTS]])')
|
||||
m4_define(FID_LINEARIZE_BODY, `m4_divert(105)')
|
||||
|
@ -106,15 +108,18 @@ FID_STRUCT_IN()m4_dnl
|
|||
struct f_inst * f$1;
|
||||
FID_NEW_ARGS()m4_dnl
|
||||
, struct f_inst * f$1
|
||||
FID_NEW_ATTRIBUTES()m4_dnl
|
||||
NONNULL(m4_eval($1+1))
|
||||
FID_NEW_BODY()m4_dnl
|
||||
whati->f$1 = f$1;
|
||||
for (const struct f_inst *child = f$1; child; child = child->next) {
|
||||
what->size += child->size;
|
||||
const struct f_inst *child$1 = f$1;
|
||||
do {
|
||||
what->size += child$1->size;
|
||||
FID_IFCONST([[
|
||||
if (child->fi_code != FI_CONSTANT)
|
||||
if (child$1->fi_code != FI_CONSTANT)
|
||||
constargs = 0;
|
||||
]])
|
||||
}
|
||||
} while (child$1 = child$1->next);
|
||||
FID_LINEARIZE_BODY
|
||||
pos = linearize(dest, whati->f$1, pos);
|
||||
FID_INTERPRET_BODY()')
|
||||
|
@ -186,6 +191,12 @@ if (f$1->type && f$2->type && (f$1->type != f$2->type) &&
|
|||
cf_error("Arguments $1 and $2 of %s must be of the same type", f_instruction_name(what->fi_code));
|
||||
FID_INTERPRET_BODY()')
|
||||
|
||||
m4_define(ARG_PREFER_SAME_TYPE, `
|
||||
FID_NEW_BODY()m4_dnl
|
||||
if (f$1->type && f$2->type && (f$1->type != f$2->type))
|
||||
(void) (f_const_promotion(f$2, f$1->type) || f_const_promotion(f$1, f$2->type));
|
||||
FID_INTERPRET_BODY()')
|
||||
|
||||
# Executing another filter line. This replaces the recursion
|
||||
# that was needed in the former implementation.
|
||||
m4_define(LINEX, `FID_INTERPRET_EXEC()LINEX_($1)FID_INTERPRET_NEW()return $1 FID_INTERPRET_BODY()')
|
||||
|
@ -210,7 +221,7 @@ whati->f$1 = f$1;
|
|||
FID_DUMP_BODY()m4_dnl
|
||||
f_dump_line(item->fl$1, indent + 1);
|
||||
FID_LINEARIZE_BODY()m4_dnl
|
||||
item->fl$1 = f_linearize(whati->f$1);
|
||||
item->fl$1 = f_linearize(whati->f$1, $2);
|
||||
FID_SAME_BODY()m4_dnl
|
||||
if (!f_same(f1->fl$1, f2->fl$1)) return 0;
|
||||
FID_ITERATE_BODY()m4_dnl
|
||||
|
@ -238,9 +249,13 @@ m4_define(ERROR,
|
|||
# This macro specifies result type and makes there are no conflicting definitions
|
||||
m4_define(RESULT_TYPE,
|
||||
`m4_ifdef([[INST_RESULT_TYPE]],
|
||||
[[m4_ifelse(INST_RESULT_TYPE,$1,,[[ERROR([[Multiple type definitons]])]])]],
|
||||
[[m4_ifelse(INST_RESULT_TYPE,$1,,[[ERROR([[Multiple type definitions in]] INST_NAME)]])]],
|
||||
[[m4_define(INST_RESULT_TYPE,$1) RESULT_TYPE_($1)]])')
|
||||
|
||||
m4_define(RESULT_TYPE_CHECK,
|
||||
`m4_ifelse(INST_OUTVAL,0,,
|
||||
[[m4_ifdef([[INST_RESULT_TYPE]],,[[ERROR([[Missing type definition in]] INST_NAME)]])]])')
|
||||
|
||||
m4_define(RESULT_TYPE_, `
|
||||
FID_NEW_BODY()m4_dnl
|
||||
what->type = $1;
|
||||
|
@ -294,6 +309,7 @@ m4_define(FID_ITERATE, `FID_ZONE(10, Iteration)')
|
|||
|
||||
# This macro does all the code wrapping. See inline comments.
|
||||
m4_define(INST_FLUSH, `m4_ifdef([[INST_NAME]], [[
|
||||
RESULT_TYPE_CHECK()m4_dnl Check for defined RESULT_TYPE()
|
||||
FID_ENUM()m4_dnl Contents of enum fi_code { ... }
|
||||
INST_NAME(),
|
||||
FID_ENUM_STR()m4_dnl Contents of const char * indexed by enum fi_code
|
||||
|
@ -309,7 +325,9 @@ m4_undivert(107)m4_dnl
|
|||
FID_NEW()m4_dnl Constructor and interpreter code together
|
||||
FID_HIC(
|
||||
[[m4_dnl Public declaration of constructor in H file
|
||||
struct f_inst *f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code
|
||||
struct f_inst *
|
||||
m4_undivert(110)m4_dnl
|
||||
f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code
|
||||
m4_undivert(102)m4_dnl
|
||||
);]],
|
||||
[[m4_dnl The one case in The Big Switch inside interpreter
|
||||
|
@ -321,7 +339,9 @@ m4_undivert(102)m4_dnl
|
|||
break;
|
||||
]],
|
||||
[[m4_dnl Constructor itself
|
||||
struct f_inst *f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code
|
||||
struct f_inst *
|
||||
m4_undivert(110)m4_dnl
|
||||
f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code
|
||||
m4_undivert(102)m4_dnl
|
||||
)
|
||||
{
|
||||
|
@ -365,6 +385,7 @@ case INST_NAME(): {
|
|||
#undef whati
|
||||
#undef item
|
||||
dest->items[pos].fi_code = what->fi_code;
|
||||
dest->items[pos].flags = what->flags;
|
||||
dest->items[pos].lineno = what->lineno;
|
||||
break;
|
||||
}
|
||||
|
@ -392,6 +413,7 @@ m4_define(INST, `m4_dnl This macro is called on beginning of each instruction
|
|||
INST_FLUSH()m4_dnl First, old data is flushed
|
||||
m4_define([[INST_NAME]], [[$1]])m4_dnl Then we store instruction name,
|
||||
m4_define([[INST_INVAL]], [[$2]])m4_dnl instruction input value count,
|
||||
m4_define([[INST_OUTVAL]], [[$3]])m4_dnl instruction output value count,
|
||||
m4_undefine([[INST_NEVER_CONSTANT]])m4_dnl reset NEVER_CONSTANT trigger,
|
||||
m4_undefine([[INST_RESULT_TYPE]])m4_dnl and reset RESULT_TYPE value.
|
||||
FID_INTERPRET_BODY()m4_dnl By default, every code is interpreter code.
|
||||
|
@ -495,6 +517,11 @@ f_const_promotion(struct f_inst *arg, enum f_type want)
|
|||
return 1;
|
||||
}
|
||||
|
||||
else if ((c->type == T_SET) && (!c->val.t) && (want == T_PREFIX_SET)) {
|
||||
*c = f_const_empty_prefix_set;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -550,7 +577,7 @@ FID_WR_PUT(8)
|
|||
}
|
||||
|
||||
struct f_line *
|
||||
f_linearize_concat(const struct f_inst * const inst[], uint count)
|
||||
f_linearize_concat(const struct f_inst * const inst[], uint count, uint results)
|
||||
{
|
||||
uint len = 0;
|
||||
for (uint i=0; i<count; i++)
|
||||
|
@ -562,6 +589,8 @@ f_linearize_concat(const struct f_inst * const inst[], uint count)
|
|||
for (uint i=0; i<count; i++)
|
||||
out->len = linearize(out, inst[i], out->len);
|
||||
|
||||
out->results = results;
|
||||
|
||||
#ifdef LOCAL_DEBUG
|
||||
f_dump_line(out, 0);
|
||||
#endif
|
||||
|
@ -630,6 +659,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_instruction_flags flags; /* Flags, instruction-specific */
|
||||
enum f_type type; /* Type of returned value, if known */
|
||||
int size; /* How many instructions are underneath */
|
||||
int lineno; /* Line number */
|
||||
|
|
287
filter/f-inst.c
287
filter/f-inst.c
|
@ -62,8 +62,9 @@
|
|||
* m4_dnl INST(FI_NOP, in, out) { enum value, input args, output args
|
||||
* m4_dnl ARG(num, type); argument, its id (in data fields) and type accessible by v1, v2, v3
|
||||
* m4_dnl ARG_ANY(num); argument with no type check accessible by v1, v2, v3
|
||||
* m4_dnl ARG_TYPE(num, type); just declare the type of argument
|
||||
* m4_dnl VARARG; variable-length argument list; accessible by vv(i) and whati->varcount
|
||||
* m4_dnl LINE(num, unused); this argument has to be converted to its own f_line
|
||||
* m4_dnl LINE(num, out); this argument has to be converted to its own f_line
|
||||
* m4_dnl SYMBOL; symbol handed from config
|
||||
* m4_dnl STATIC_ATTR; static attribute definition
|
||||
* m4_dnl DYNAMIC_ATTR; dynamic attribute definition
|
||||
|
@ -80,10 +81,17 @@
|
|||
* m4_dnl )
|
||||
*
|
||||
* m4_dnl RESULT(type, union-field, value); putting this on value stack
|
||||
* m4_dnl RESULT_(type, union-field, value); like RESULT(), but do not declare the type
|
||||
* m4_dnl RESULT_VAL(value-struct); pass the struct f_val directly
|
||||
* m4_dnl RESULT_TYPE(type); just declare the type of result value
|
||||
* m4_dnl RESULT_VOID; return undef
|
||||
* m4_dnl }
|
||||
*
|
||||
* Note that runtime arguments m4_dnl (ARG*, VARARG) must be defined before
|
||||
* parse-time arguments m4_dnl (LINE, SYMBOL, ...). During linearization,
|
||||
* first ones move position in f_line by linearizing arguments first, while
|
||||
* second ones store data to the current position.
|
||||
*
|
||||
* Also note that the { ... } blocks are not respected by M4 at all.
|
||||
* If you get weird unmatched-brace-pair errors, check what it generated and why.
|
||||
* What is really considered as one instruction is not the { ... } block
|
||||
|
@ -91,6 +99,24 @@
|
|||
*
|
||||
* Other code is just copied into the interpreter part.
|
||||
*
|
||||
* The filter language uses a simple type system, where values have types
|
||||
* (constants T_*) and also terms (instructions) are statically typed. Our
|
||||
* static typing is partial (some terms do not declare types of arguments
|
||||
* or results), therefore it can detect most but not all type errors and
|
||||
* therefore we still have runtime type checks.
|
||||
*
|
||||
* m4_dnl Types of arguments are declared by macros ARG() and ARG_TYPE(),
|
||||
* m4_dnl types of results are declared by RESULT() and RESULT_TYPE().
|
||||
* m4_dnl Macros ARG_ANY(), RESULT_() and RESULT_VAL() do not declare types
|
||||
* m4_dnl themselves, but can be combined with ARG_TYPE() / RESULT_TYPE().
|
||||
*
|
||||
* m4_dnl Note that types should be declared only once. If there are
|
||||
* m4_dnl multiple RESULT() macros in an instruction definition, they must
|
||||
* m4_dnl use the exact same expression for type, or they should be replaced
|
||||
* m4_dnl by multiple RESULT_() macros and a common RESULT_TYPE() macro.
|
||||
* m4_dnl See e.g. FI_EA_GET or FI_MIN instructions.
|
||||
*
|
||||
*
|
||||
* If you are satisfied with this, you don't need to read the following
|
||||
* detailed description of what is really done with the instruction definitions.
|
||||
*
|
||||
|
@ -216,6 +242,37 @@
|
|||
*
|
||||
* m4_dnl If you are stymied, see FI_CALL or FI_CONSTANT or just search for
|
||||
* m4_dnl the mentioned macros in this file to see what is happening there in wild.
|
||||
*
|
||||
*
|
||||
* A note about soundness of the type system:
|
||||
*
|
||||
* A type system is sound when types of expressions are consistent with
|
||||
* types of values resulting from evaluation of such expressions. Untyped
|
||||
* expressions are ok, but badly typed expressions are not sound. So is
|
||||
* the type system of BIRD filtering code sound? There are some points:
|
||||
*
|
||||
* All cases of (one) m4_dnl RESULT() macro are obviously ok, as the macro
|
||||
* both declares a type and returns a value. One have to check instructions
|
||||
* that use m4_dnl RESULT_TYPE() macro. There are two issues:
|
||||
*
|
||||
* FI_AND, FI_OR - second argument is statically checked to be T_BOOL and
|
||||
* passed as result without dynamic typecheck, declared to be T_BOOL. If
|
||||
* an untyped non-bool expression is used as a second argument, then
|
||||
* the mismatched type is returned.
|
||||
*
|
||||
* FI_VAR_GET - soundness depends on consistency of declared symbol types
|
||||
* and stored values. This is maintained when values are stored by
|
||||
* FI_VAR_SET, but when they are stored by FI_CALL, only static checking is
|
||||
* used, so when an untyped expression returning mismatched value is used
|
||||
* as a function argument, then inconsistent value is stored and subsequent
|
||||
* FI_VAR_GET would be unsound.
|
||||
*
|
||||
* Both of these issues are inconsequential, as mismatched values from
|
||||
* unsound expressions will be caught by dynamic typechecks like mismatched
|
||||
* values from untyped expressions.
|
||||
*
|
||||
* Also note that FI_CALL is the only expression without properly declared
|
||||
* result type.
|
||||
*/
|
||||
|
||||
/* Binary operators */
|
||||
|
@ -246,7 +303,7 @@
|
|||
RESULT_TYPE(T_BOOL);
|
||||
|
||||
if (v1.val.i)
|
||||
LINE(2,0);
|
||||
LINE(2,1);
|
||||
else
|
||||
RESULT_VAL(v1);
|
||||
}
|
||||
|
@ -256,7 +313,7 @@
|
|||
RESULT_TYPE(T_BOOL);
|
||||
|
||||
if (!v1.val.i)
|
||||
LINE(2,0);
|
||||
LINE(2,1);
|
||||
else
|
||||
RESULT_VAL(v1);
|
||||
}
|
||||
|
@ -349,7 +406,7 @@
|
|||
break;
|
||||
|
||||
case T_SET:
|
||||
if (vv(i).val.t->from.type != T_INT)
|
||||
if (!path_set_type(vv(i).val.t))
|
||||
runtime("Only integer sets allowed in path mask");
|
||||
|
||||
pm->item[i] = (struct f_path_mask_item) {
|
||||
|
@ -371,12 +428,14 @@
|
|||
INST(FI_NEQ, 2, 1) {
|
||||
ARG_ANY(1);
|
||||
ARG_ANY(2);
|
||||
ARG_PREFER_SAME_TYPE(1, 2);
|
||||
RESULT(T_BOOL, i, !val_same(&v1, &v2));
|
||||
}
|
||||
|
||||
INST(FI_EQ, 2, 1) {
|
||||
ARG_ANY(1);
|
||||
ARG_ANY(2);
|
||||
ARG_PREFER_SAME_TYPE(1, 2);
|
||||
RESULT(T_BOOL, i, val_same(&v1, &v2));
|
||||
}
|
||||
|
||||
|
@ -447,6 +506,18 @@
|
|||
RESULT(T_BOOL, i, ipa_is_ip4(v1.val.ip));
|
||||
}
|
||||
|
||||
INST(FI_VAR_INIT, 1, 0) {
|
||||
NEVER_CONSTANT;
|
||||
ARG_ANY(1);
|
||||
SYMBOL;
|
||||
ARG_TYPE(1, sym->class & 0xff);
|
||||
|
||||
/* New variable is always the last on stack */
|
||||
uint pos = curline.vbase + sym->offset;
|
||||
fstk->vstk[pos] = v1;
|
||||
fstk->vcnt = pos + 1;
|
||||
}
|
||||
|
||||
/* Set to indirect value prepared in v1 */
|
||||
INST(FI_VAR_SET, 1, 0) {
|
||||
NEVER_CONSTANT;
|
||||
|
@ -477,12 +548,100 @@
|
|||
RESULT_VAL(val);
|
||||
}
|
||||
|
||||
INST(FI_FOR_INIT, 1, 0) {
|
||||
NEVER_CONSTANT;
|
||||
ARG_ANY(1);
|
||||
SYMBOL;
|
||||
|
||||
FID_NEW_BODY()
|
||||
ASSERT((sym->class & ~0xff) == SYM_VARIABLE);
|
||||
|
||||
/* Static type check */
|
||||
if (f1->type)
|
||||
{
|
||||
enum f_type t_var = (sym->class & 0xff);
|
||||
enum f_type t_arg = f_type_element_type(f1->type);
|
||||
if (!t_arg)
|
||||
cf_error("Value of expression in FOR must be iterable, got %s",
|
||||
f_type_name(f1->type));
|
||||
if (t_var != t_arg)
|
||||
cf_error("Loop variable '%s' in FOR must be %s, is %s",
|
||||
sym->name, f_type_name(t_arg), f_type_name(t_var));
|
||||
}
|
||||
|
||||
FID_INTERPRET_BODY()
|
||||
|
||||
/* Dynamic type check */
|
||||
if ((sym->class & 0xff) != f_type_element_type(v1.type))
|
||||
runtime("Mismatched argument and variable type");
|
||||
|
||||
/* Setup the index */
|
||||
v2 = (struct f_val) { .type = T_INT, .val.i = 0 };
|
||||
|
||||
/* Keep v1 and v2 on the stack */
|
||||
fstk->vcnt += 2;
|
||||
}
|
||||
|
||||
INST(FI_FOR_NEXT, 2, 0) {
|
||||
NEVER_CONSTANT;
|
||||
SYMBOL;
|
||||
|
||||
/* Type checks are done in FI_FOR_INIT */
|
||||
|
||||
/* Loop variable */
|
||||
struct f_val *var = &fstk->vstk[curline.vbase + sym->offset];
|
||||
int step = 0;
|
||||
|
||||
switch(v1.type)
|
||||
{
|
||||
case T_PATH:
|
||||
var->type = T_INT;
|
||||
step = as_path_walk(v1.val.ad, &v2.val.i, &var->val.i);
|
||||
break;
|
||||
|
||||
case T_CLIST:
|
||||
var->type = T_PAIR;
|
||||
step = int_set_walk(v1.val.ad, &v2.val.i, &var->val.i);
|
||||
break;
|
||||
|
||||
case T_ECLIST:
|
||||
var->type = T_EC;
|
||||
step = ec_set_walk(v1.val.ad, &v2.val.i, &var->val.ec);
|
||||
break;
|
||||
|
||||
case T_LCLIST:
|
||||
var->type = T_LC;
|
||||
step = lc_set_walk(v1.val.ad, &v2.val.i, &var->val.lc);
|
||||
break;
|
||||
|
||||
default:
|
||||
runtime( "Clist or lclist expected" );
|
||||
}
|
||||
|
||||
if (step)
|
||||
{
|
||||
/* Keep v1 and v2 on the stack */
|
||||
fstk->vcnt += 2;
|
||||
|
||||
/* Repeat this instruction */
|
||||
curline.pos--;
|
||||
|
||||
/* Execute the loop body */
|
||||
LINE(1, 0);
|
||||
|
||||
/* Space for loop variable, may be unused */
|
||||
fstk->vcnt += 1;
|
||||
}
|
||||
else
|
||||
var->type = T_VOID;
|
||||
}
|
||||
|
||||
INST(FI_CONDITION, 1, 0) {
|
||||
ARG(1, T_BOOL);
|
||||
if (v1.val.i)
|
||||
LINE(2,0);
|
||||
else
|
||||
LINE(3,1);
|
||||
LINE(3,0);
|
||||
}
|
||||
|
||||
INST(FI_PRINT, 0, 0) {
|
||||
|
@ -526,13 +685,14 @@
|
|||
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->n.addr); break;
|
||||
case SA_PROTO: RESULT(sa.f_type, s, rta->src->proto->name); 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;
|
||||
|
||||
default:
|
||||
|
@ -561,7 +721,7 @@
|
|||
{
|
||||
ip_addr ip = v1.val.ip;
|
||||
struct iface *ifa = ipa_is_link_local(ip) ? rta->nh.iface : NULL;
|
||||
neighbor *n = neigh_find(rta->src->proto, ip, ifa, 0);
|
||||
neighbor *n = neigh_find((*fs->rte)->src->proto, ip, ifa, 0);
|
||||
if (!n || (n->scope == SCOPE_HOST))
|
||||
runtime( "Invalid gw address" );
|
||||
|
||||
|
@ -637,6 +797,10 @@
|
|||
}
|
||||
break;
|
||||
|
||||
case SA_PREF:
|
||||
rta->pref = v1.val.i;
|
||||
break;
|
||||
|
||||
default:
|
||||
bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code);
|
||||
}
|
||||
|
@ -709,9 +873,6 @@
|
|||
case EAF_TYPE_LC_SET:
|
||||
RESULT_(T_LCLIST, ad, e->u.ptr);
|
||||
break;
|
||||
case EAF_TYPE_UNDEF:
|
||||
RESULT_VOID;
|
||||
break;
|
||||
default:
|
||||
bug("Unknown dynamic attribute type");
|
||||
}
|
||||
|
@ -732,7 +893,10 @@
|
|||
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;
|
||||
l->attrs[0].type = da.type;
|
||||
l->attrs[0].originated = 1;
|
||||
l->attrs[0].fresh = 1;
|
||||
l->attrs[0].undef = 0;
|
||||
|
||||
switch (da.type) {
|
||||
case EAF_TYPE_INT:
|
||||
|
@ -787,35 +951,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;
|
||||
}
|
||||
}
|
||||
|
||||
INST(FI_PREF_GET, 0, 1) {
|
||||
ACCESS_RTE;
|
||||
RESULT(T_INT, i, (*fs->rte)->pref);
|
||||
}
|
||||
|
||||
INST(FI_PREF_SET, 1, 0) {
|
||||
ACCESS_RTE;
|
||||
ARG(1,T_INT);
|
||||
if (v1.val.i > 0xFFFF)
|
||||
runtime( "Setting preference value out of bounds" );
|
||||
f_rte_cow(fs);
|
||||
(*fs->rte)->pref = v1.val.i;
|
||||
ea_unset_attr(fs->eattrs, fs->pool, 1, da.ea_code);
|
||||
}
|
||||
|
||||
INST(FI_LENGTH, 1, 1) { /* Get length of */
|
||||
|
@ -983,7 +1120,7 @@
|
|||
RESULT(T_INT, i, v1.val.lc.ldp2);
|
||||
}
|
||||
|
||||
INST(FI_MIN, 1, 1) { /* Get minimum element from set */
|
||||
INST(FI_MIN, 1, 1) { /* Get minimum element from list */
|
||||
ARG_ANY(1);
|
||||
RESULT_TYPE(f_type_element_type(v1.type));
|
||||
switch(v1.type)
|
||||
|
@ -1017,7 +1154,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
INST(FI_MAX, 1, 1) { /* Get maximum element from set */
|
||||
INST(FI_MAX, 1, 1) { /* Get maximum element from list */
|
||||
ARG_ANY(1);
|
||||
RESULT_TYPE(f_type_element_type(v1.type));
|
||||
switch(v1.type)
|
||||
|
@ -1051,7 +1188,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
INST(FI_RETURN, 1, 1) {
|
||||
INST(FI_RETURN, 1, 0) {
|
||||
NEVER_CONSTANT;
|
||||
/* Acquire the return value */
|
||||
ARG_ANY(1);
|
||||
|
@ -1079,28 +1216,59 @@
|
|||
|
||||
INST(FI_CALL, 0, 1) {
|
||||
NEVER_CONSTANT;
|
||||
VARARG;
|
||||
SYMBOL;
|
||||
|
||||
/* Fake result type declaration */
|
||||
RESULT_TYPE(T_VOID);
|
||||
|
||||
FID_NEW_BODY()
|
||||
ASSERT(sym->class == SYM_FUNCTION);
|
||||
|
||||
if (whati->varcount != sym->function->args)
|
||||
cf_error("Function '%s' expects %u arguments, got %u arguments",
|
||||
sym->name, sym->function->args, whati->varcount);
|
||||
|
||||
/* Typecheck individual arguments */
|
||||
struct f_inst *a = fvar;
|
||||
struct f_arg *b = sym->function->arg_list;
|
||||
for (uint i = 1; a && b; a = a->next, b = b->next, i++)
|
||||
{
|
||||
enum f_type b_type = b->arg->class & 0xff;
|
||||
|
||||
if (a->type && (a->type != b_type) && !f_const_promotion(a, b_type))
|
||||
cf_error("Argument %u of '%s' must be %s, got %s",
|
||||
i, sym->name, f_type_name(b_type), f_type_name(a->type));
|
||||
}
|
||||
ASSERT(!a && !b);
|
||||
|
||||
/* Add implicit void slot for the return value */
|
||||
struct f_inst *tmp = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_VOID });
|
||||
tmp->next = whati->fvar;
|
||||
whati->fvar = tmp;
|
||||
what->size += tmp->size;
|
||||
|
||||
/* Mark recursive calls, they have dummy f_line */
|
||||
if (!sym->function->len)
|
||||
what->flags |= FIF_RECURSIVE;
|
||||
|
||||
FID_SAME_BODY()
|
||||
if (!(f1->sym->flags & SYM_FLAG_SAME))
|
||||
if (!(f1->sym->flags & SYM_FLAG_SAME) && !(f1_->flags & FIF_RECURSIVE))
|
||||
return 0;
|
||||
|
||||
FID_ITERATE_BODY()
|
||||
if (!(what->flags & FIF_RECURSIVE))
|
||||
BUFFER_PUSH(fit->lines) = whati->sym->function;
|
||||
|
||||
FID_INTERPRET_BODY()
|
||||
|
||||
/* Push the body on stack */
|
||||
LINEX(sym->function);
|
||||
curline.vbase = curline.ventry;
|
||||
curline.emask |= FE_RETURN;
|
||||
|
||||
/* Before this instruction was called, there was the T_VOID
|
||||
* automatic return value pushed on value stack and also
|
||||
* sym->function->args function arguments. Setting the
|
||||
* vbase to point to first argument. */
|
||||
ASSERT(curline.ventry >= sym->function->args);
|
||||
curline.ventry -= sym->function->args;
|
||||
curline.vbase = curline.ventry;
|
||||
/* Arguments on stack */
|
||||
fstk->vcnt += sym->function->args;
|
||||
|
||||
/* Storage for local variables */
|
||||
memset(&(fstk->vstk[fstk->vcnt]), 0, sizeof(struct f_val) * sym->function->vars);
|
||||
|
@ -1213,17 +1381,10 @@
|
|||
|
||||
if (v1.type == T_PATH)
|
||||
{
|
||||
const struct f_tree *set = NULL;
|
||||
u32 key = 0;
|
||||
|
||||
if (v2.type == T_INT)
|
||||
key = v2.val.i;
|
||||
else if ((v2.type == T_SET) && (v2.val.t->from.type == T_INT))
|
||||
set = v2.val.t;
|
||||
if ((v2.type == T_SET) && path_set_type(v2.val.t) || (v2.type == T_INT))
|
||||
RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 0) ]]);
|
||||
else
|
||||
runtime("Can't delete non-integer (set)");
|
||||
|
||||
RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, set, key, 0) ]]);
|
||||
}
|
||||
|
||||
else if (v1.type == T_CLIST)
|
||||
|
@ -1275,10 +1436,8 @@
|
|||
|
||||
if (v1.type == T_PATH)
|
||||
{
|
||||
u32 key = 0;
|
||||
|
||||
if ((v2.type == T_SET) && (v2.val.t->from.type == T_INT))
|
||||
RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, v2.val.t, key, 1) ]]);
|
||||
if ((v2.type == T_SET) && path_set_type(v2.val.t))
|
||||
RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 1) ]]);
|
||||
else
|
||||
runtime("Can't filter integer");
|
||||
}
|
||||
|
@ -1368,7 +1527,7 @@
|
|||
|
||||
}
|
||||
|
||||
INST(FI_FORMAT, 1, 0) { /* Format */
|
||||
INST(FI_FORMAT, 1, 1) { /* Format */
|
||||
ARG_ANY(1);
|
||||
RESULT(T_STRING, s, val_format_str(fpool, &v1));
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
/* Flags for instructions */
|
||||
enum f_instruction_flags {
|
||||
FIF_PRINTED = 1, /* FI_PRINT_AND_DIE: message put in buffer */
|
||||
FIF_RECURSIVE = 1, /* FI_CALL: function is directly recursive */
|
||||
} PACKED;
|
||||
|
||||
/* Include generated filter instruction declarations */
|
||||
|
@ -35,19 +35,26 @@ const char *f_instruction_name_(enum f_instruction_code fi);
|
|||
static inline const char *f_instruction_name(enum f_instruction_code fi)
|
||||
{ return f_instruction_name_(fi) + 3; }
|
||||
|
||||
struct f_arg {
|
||||
struct symbol *arg;
|
||||
struct f_arg *next;
|
||||
};
|
||||
|
||||
/* Filter structures for execution */
|
||||
/* Line of instructions to be unconditionally executed one after another */
|
||||
struct f_line {
|
||||
uint len; /* Line length */
|
||||
u8 args; /* Function: Args required */
|
||||
u8 vars;
|
||||
u8 results; /* Results left on stack: cmd -> 0, term -> 1 */
|
||||
struct f_arg *arg_list;
|
||||
struct f_line_item items[0]; /* The items themselves */
|
||||
};
|
||||
|
||||
/* Convert the f_inst infix tree to the f_line structures */
|
||||
struct f_line *f_linearize_concat(const struct f_inst * const inst[], uint count);
|
||||
static inline struct f_line *f_linearize(const struct f_inst *root)
|
||||
{ return f_linearize_concat(&root, 1); }
|
||||
struct f_line *f_linearize_concat(const struct f_inst * const inst[], uint count, uint results);
|
||||
static inline struct f_line *f_linearize(const struct f_inst *root, uint results)
|
||||
{ return f_linearize_concat(&root, 1, results); }
|
||||
|
||||
void f_dump_line(const struct f_line *, uint indent);
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ struct filter *f_new_where(struct f_inst *where)
|
|||
f_new_inst(FI_DIE, F_REJECT));
|
||||
|
||||
struct filter *f = cfg_allocz(sizeof(struct filter));
|
||||
f->root = f_linearize(cond);
|
||||
f->root = f_linearize(cond, 0);
|
||||
return f;
|
||||
}
|
||||
|
||||
|
@ -128,11 +128,11 @@ ca_lookup(pool *p, const char *name, int f_type)
|
|||
|
||||
static int inited = 0;
|
||||
if (!inited) {
|
||||
idm_init(&ca_idm, &root_pool, 8);
|
||||
HASH_INIT(ca_hash, &root_pool, CA_ORDER);
|
||||
idm_init(&ca_idm, config_pool, 8);
|
||||
HASH_INIT(ca_hash, config_pool, CA_ORDER);
|
||||
|
||||
ca_storage_max = 256;
|
||||
ca_storage = mb_allocz(&root_pool, sizeof(struct ca_storage *) * ca_storage_max);
|
||||
ca_storage = mb_allocz(config_pool, sizeof(struct ca_storage *) * ca_storage_max);
|
||||
|
||||
inited++;
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ ca_lookup(pool *p, const char *name, int f_type)
|
|||
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 = mb_allocz(config_pool, sizeof(struct ca_storage) + strlen(name) + 1);
|
||||
cas->fda = f_new_dynamic_attr(ea_type, f_type, EA_CUSTOM(id));
|
||||
cas->uc = 1;
|
||||
|
||||
|
|
|
@ -215,8 +215,7 @@ interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val)
|
|||
}
|
||||
|
||||
/* End of current line. Drop local variables before exiting. */
|
||||
fstk->vcnt -= curline.line->vars;
|
||||
fstk->vcnt -= curline.line->args;
|
||||
fstk->vcnt = curline.ventry + curline.line->results;
|
||||
fstk->ecnt--;
|
||||
}
|
||||
|
||||
|
|
|
@ -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, tmp_linpool, NULL);
|
||||
|
||||
return (fret < F_REJECT);
|
||||
}
|
||||
|
|
211
filter/test.conf
211
filter/test.conf
|
@ -44,9 +44,8 @@ bt_test_same(onef, twof, 0);
|
|||
*/
|
||||
|
||||
function t_bool()
|
||||
bool b;
|
||||
{
|
||||
b = true;
|
||||
bool b = true;
|
||||
bt_assert(b);
|
||||
bt_assert(!!b);
|
||||
|
||||
|
@ -82,12 +81,11 @@ define xyzzy = (120+10);
|
|||
define '1a-a1' = (xyzzy-100);
|
||||
|
||||
function t_int()
|
||||
int i;
|
||||
{
|
||||
bt_assert(xyzzy = 130);
|
||||
bt_assert('1a-a1' = 30);
|
||||
|
||||
i = four;
|
||||
int i = four;
|
||||
i = 12*100 + 60/2 + i;
|
||||
i = (i + 0);
|
||||
bt_assert(i = 1234);
|
||||
|
@ -128,14 +126,19 @@ define is2 = [(17+2), 17, 15, 11, 8, 5, 3, 2];
|
|||
define is3 = [5, 17, 2, 11, 8, 15, 3, 19];
|
||||
|
||||
function t_int_set()
|
||||
int set is;
|
||||
{
|
||||
int set is = [];
|
||||
bt_assert(is = []);
|
||||
bt_assert(0 !~ is);
|
||||
|
||||
bt_assert(1 ~ [1,2,3]);
|
||||
bt_assert(5 ~ [1..20]);
|
||||
bt_assert(2 ~ [ 1, 2, 3 ]);
|
||||
bt_assert(5 ~ [ 4 .. 7 ]);
|
||||
bt_assert(1 !~ [ 2, 3, 4 ]);
|
||||
bt_assert(999 !~ [ 666, 333 ]);
|
||||
bt_assert(1 !~ []);
|
||||
bt_assert(1 !~ is);
|
||||
|
||||
is = [ 2, 3, 4, 7..11 ];
|
||||
bt_assert(10 ~ is);
|
||||
|
@ -170,6 +173,7 @@ int set is;
|
|||
bt_assert([1,4..10,20] = [1,4..10,20]);
|
||||
|
||||
bt_assert(format([ 1, 2, 1, 1, 1, 3, 4, 1, 1, 1, 5 ]) = "[1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 5]");
|
||||
bt_assert(format([]) = "[]");
|
||||
}
|
||||
|
||||
bt_test_suite(t_int_set, "Testing sets of integers");
|
||||
|
@ -183,9 +187,8 @@ bt_test_suite(t_int_set, "Testing sets of integers");
|
|||
*/
|
||||
|
||||
function t_string()
|
||||
string st;
|
||||
{
|
||||
st = "Hello";
|
||||
string st = "Hello";
|
||||
bt_assert(format(st) = "Hello");
|
||||
bt_assert(st ~ "Hell*");
|
||||
bt_assert(st ~ "?ello");
|
||||
|
@ -210,9 +213,8 @@ function 'mkpair-a'(int a)
|
|||
}
|
||||
|
||||
function t_pair()
|
||||
pair pp;
|
||||
{
|
||||
pp = (1, 2);
|
||||
pair pp = (1, 2);
|
||||
bt_assert(format(pp) = "(1,2)");
|
||||
bt_assert((1,2) = pp);
|
||||
bt_assert((1,1+1) = pp);
|
||||
|
@ -233,10 +235,11 @@ bt_test_suite(t_pair, "Testing pairs");
|
|||
*/
|
||||
|
||||
function t_pair_set()
|
||||
pair pp;
|
||||
pair set ps;
|
||||
{
|
||||
pp = (1, 2);
|
||||
pair pp = (1, 2);
|
||||
pair set ps = [];
|
||||
bt_assert(pp !~ ps);
|
||||
|
||||
ps = [(1,(one+one)), (3,4)..(4,8), (5,*), (6,3..6)];
|
||||
bt_assert(format(ps) = "[(1,2), (3,4)..(4,8), (5,0)..(5,65535), (6,3)..(6,6)]");
|
||||
bt_assert(pp ~ ps);
|
||||
|
@ -253,6 +256,7 @@ pair set ps;
|
|||
bt_assert((6,6+one) !~ ps);
|
||||
bt_assert(((one+6),2) !~ ps);
|
||||
bt_assert((1,1) !~ ps);
|
||||
bt_assert(pp !~ []);
|
||||
|
||||
ps = [(20..150, 200..300), (50100..50200, 1000..50000), (*, 5+5)];
|
||||
bt_assert((100,200) ~ ps);
|
||||
|
@ -304,6 +308,7 @@ quad qq;
|
|||
qq = 1.2.3.4;
|
||||
bt_assert(qq ~ [1.2.3.4, 5.6.7.8]);
|
||||
bt_assert(qq !~ [1.2.1.1, 1.2.3.5]);
|
||||
bt_assert(qq !~ []);
|
||||
}
|
||||
|
||||
bt_test_suite(t_quad_set, "Testing sets of quads");
|
||||
|
@ -384,6 +389,7 @@ ip set ips;
|
|||
|
||||
bt_assert(1.2.3.4 !~ [ 1.2.3.3, 1.2.3.5 ]);
|
||||
bt_assert(1.2.3.4 ~ [ 1.2.3.3..1.2.3.5 ]);
|
||||
bt_assert(1.2.3.4 !~ []);
|
||||
}
|
||||
|
||||
bt_test_suite(t_ip_set, "Testing sets of ip address");
|
||||
|
@ -398,7 +404,6 @@ bt_test_suite(t_ip_set, "Testing sets of ip address");
|
|||
|
||||
function t_enum()
|
||||
{
|
||||
bt_assert(format(RTS_DUMMY) = "(enum 30)0");
|
||||
bt_assert(format(RTS_STATIC) = "(enum 30)1");
|
||||
bt_assert(format(NET_IP4) = "(enum 36)1");
|
||||
bt_assert(format(NET_VPN6) = "(enum 36)4");
|
||||
|
@ -473,13 +478,34 @@ function test_pxset(prefix set pxs)
|
|||
bt_assert(1.0.0.0/8 ~ [ 1.0.0.0/8+ ]);
|
||||
bt_assert(1.0.0.0/9 !~ [ 1.0.0.0/8- ]);
|
||||
bt_assert(1.2.0.0/17 !~ [ 1.0.0.0/8{ 15 , 16 } ]);
|
||||
bt_assert(net10 !~ []);
|
||||
|
||||
bt_assert([ 10.0.0.0/8{ 15 , 17 } ] = [ 10.0.0.0/8{ 15 , 17 } ]);
|
||||
}
|
||||
|
||||
function test_empty_pxset(prefix set pxs)
|
||||
int set s0;
|
||||
prefix set s1;
|
||||
{
|
||||
s0 = [];
|
||||
s1 = [];
|
||||
bt_assert(pxs != s0);
|
||||
bt_assert(pxs = s1);
|
||||
bt_assert(pxs = []);
|
||||
}
|
||||
|
||||
function t_prefix_set()
|
||||
prefix set pxs;
|
||||
{
|
||||
pxs = [];
|
||||
bt_assert(format(pxs) = "[]");
|
||||
bt_assert(pxs = []);
|
||||
bt_assert(1.2.0.0/16 !~ []);
|
||||
bt_assert(1.2.0.0/16 !~ pxs);
|
||||
|
||||
test_empty_pxset([]);
|
||||
test_empty_pxset(pxs);
|
||||
|
||||
pxs = [ 1.2.0.0/16, 1.4.0.0/16+, 44.66.88.64/30{24,28}, 12.34.56.0/24{8,16} ];
|
||||
bt_assert(format(pxs) = "[1.2.0.0/16{0.1.0.0}, 1.4.0.0/16{0.1.255.255}, 12.34.0.0/16{1.255.0.0}, 44.66.88.64/28{0.0.1.240}]");
|
||||
|
||||
|
@ -564,6 +590,12 @@ bt_test_suite(t_prefix6, "Testing prefix IPv6");
|
|||
function t_prefix6_set()
|
||||
prefix set pxs;
|
||||
{
|
||||
pxs = [];
|
||||
bt_assert(format(pxs) = "[]");
|
||||
bt_assert(pxs = []);
|
||||
bt_assert(12::34/128 !~ []);
|
||||
bt_assert(12::34/128 !~ pxs);
|
||||
|
||||
bt_assert(1180::/16 ~ [ 1100::/8{15, 17} ]);
|
||||
bt_assert(12::34 = 12::34);
|
||||
bt_assert(12::34 ~ [ 12::33..12::35 ]);
|
||||
|
@ -681,6 +713,7 @@ int set set12;
|
|||
bt_assert(3 ~ p2);
|
||||
bt_assert(p2 ~ [2, 10..20]);
|
||||
bt_assert(p2 ~ [4, 10..20]);
|
||||
bt_assert(p2 !~ []);
|
||||
|
||||
p2 = prepend(p2, 5);
|
||||
bt_assert(p2 !~ pm1);
|
||||
|
@ -691,6 +724,8 @@ int set set12;
|
|||
bt_assert(p2 ~ [= 5 [2, 4, 6] 3 [1..2] 1 =]);
|
||||
bt_assert(p2 ~ [= 5 set35 3 set12 set12 =]);
|
||||
bt_assert(p2 ~ mkpath(5, 4));
|
||||
bt_assert(p2 ~ [= * [3] * =]);
|
||||
bt_assert(p2 !~ [= * [] * =]);
|
||||
|
||||
bt_assert(p2.len = 5);
|
||||
bt_assert(p2.first = 5);
|
||||
|
@ -699,6 +734,10 @@ int set set12;
|
|||
bt_assert(p2.len = 5);
|
||||
bt_assert(delete(p2, 3) = prepend(prepend(prepend(prepend(+empty+, 1), 2), 4), 5));
|
||||
bt_assert(filter(p2, [1..3]) = prepend(prepend(prepend(+empty+, 1), 2), 3));
|
||||
bt_assert(delete(p2, []) = p2);
|
||||
bt_assert(filter(p2, []) = +empty+);
|
||||
bt_assert(delete(prepend(prepend(+empty+, 0), 1), []) = prepend(prepend(+empty+, 0), 1));
|
||||
bt_assert(filter(prepend(prepend(+empty+, 0), 1), []) = +empty+);
|
||||
|
||||
p2 = prepend( + empty +, 5 );
|
||||
p2 = prepend( p2, 4 );
|
||||
|
@ -718,6 +757,15 @@ int set set12;
|
|||
bt_assert(delete(p2, [4..5]) = prepend(prepend(prepend(prepend(+empty+, 3), 3), 2), 1));
|
||||
|
||||
bt_assert(format([= 1 2+ 3 =]) = "[= 1 2 + 3 =]");
|
||||
|
||||
# iteration over path
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
for int i in p2 do {
|
||||
x = x + i;
|
||||
y = y + x;
|
||||
}
|
||||
bt_assert(x = 18 && y = 50);
|
||||
}
|
||||
|
||||
bt_test_suite(t_path, "Testing paths");
|
||||
|
@ -759,6 +807,7 @@ clist r;
|
|||
bt_assert(l ~ [(2,2..3)]);
|
||||
bt_assert(l ~ [(1,1..2)]);
|
||||
bt_assert(l ~ [(1,1)..(1,2)]);
|
||||
bt_assert(l !~ []);
|
||||
|
||||
l = add(l, (2,5));
|
||||
l = add(l, (5,one));
|
||||
|
@ -796,6 +845,9 @@ clist r;
|
|||
bt_assert(l !~ [(*,(one+6))]);
|
||||
bt_assert(l !~ [(*, (one+one+one))]);
|
||||
|
||||
bt_assert(delete(l, []) = l);
|
||||
bt_assert(filter(l, []) = -empty-);
|
||||
|
||||
l = delete(l, [(*,(one+onef(3)))]);
|
||||
l = delete(l, [(*,(4+one))]);
|
||||
bt_assert(l = add(-empty-, (3,1)));
|
||||
|
@ -840,6 +892,12 @@ clist r;
|
|||
bt_assert(format(r) = "(clist (2,1) (1,3) (2,2) (3,1) (2,3))");
|
||||
bt_assert(r.min = (1,3));
|
||||
bt_assert(r.max = (3,1));
|
||||
|
||||
# iteration over clist
|
||||
int x = 0;
|
||||
for pair c in r do
|
||||
x = x + c.asn * c.asn * c.data;
|
||||
bt_assert(x = 36);
|
||||
}
|
||||
|
||||
bt_test_suite(t_clist, "Testing lists of communities");
|
||||
|
@ -913,11 +971,15 @@ eclist r;
|
|||
bt_assert((ro, 10.20.30.40, 100) !~ el);
|
||||
bt_assert(el !~ [(rt, 10, 35..40)]);
|
||||
bt_assert(el !~ [(ro, 10, *)]);
|
||||
bt_assert(el !~ []);
|
||||
|
||||
el = add(el, (rt, 10, 40));
|
||||
el2 = filter(el, [(rt, 10, 20..40)] );
|
||||
el2 = add(el2, (rt, 10, 50));
|
||||
|
||||
bt_assert(delete(el, []) = el);
|
||||
bt_assert(filter(el, []) = --empty--);
|
||||
|
||||
# eclist A (1,30,40)
|
||||
bt_assert(el = add(add(add(--empty--, (rt, 10, 1)), (rt, 10, 30)), (rt, 10, 40)));
|
||||
bt_assert(format(el) = "(eclist (rt, 10, 1) (rt, 10, 30) (rt, 10, 40))");
|
||||
|
@ -951,6 +1013,13 @@ eclist r;
|
|||
bt_assert(format(r) = "(eclist (rt, 2, 1) (rt, 1, 3) (rt, 2, 2) (rt, 3, 1) (rt, 2, 3))");
|
||||
bt_assert(r.min = (rt, 1, 3));
|
||||
bt_assert(r.max = (rt, 3, 1));
|
||||
|
||||
# iteration over eclist
|
||||
int x = 0;
|
||||
for ec c in r do
|
||||
if c > (rt, 2, 0) && c < (rt, 3, 0) then
|
||||
x = x + 1;
|
||||
bt_assert(x = 3);
|
||||
}
|
||||
|
||||
bt_test_suite(t_eclist, "Testing lists of extended communities");
|
||||
|
@ -1035,6 +1104,9 @@ lclist r;
|
|||
ll2 = add(ll2, (30, 30, 30));
|
||||
ll2 = add(ll2, (40, 40, 40));
|
||||
|
||||
bt_assert(delete(ll, []) = ll);
|
||||
bt_assert(filter(ll, []) = ---empty---);
|
||||
|
||||
# lclist A (10, 20, 30)
|
||||
bt_assert(format(ll) = "(lclist (10, 10, 10) (20, 20, 20) (30, 30, 30))");
|
||||
|
||||
|
@ -1066,6 +1138,19 @@ lclist r;
|
|||
bt_assert(format(r) = "(lclist (2, 3, 3) (1, 2, 3) (2, 3, 1) (3, 1, 2) (2, 1, 3))");
|
||||
bt_assert(r.min = (1, 2, 3));
|
||||
bt_assert(r.max = (3, 1, 2));
|
||||
|
||||
# iteration over lclist
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
lc mx = (0, 0, 0);
|
||||
for lc c in r do {
|
||||
int asn2 = c.asn * c.asn;
|
||||
x = x + asn2 * c.data1;
|
||||
y = y + asn2 * c.data2;
|
||||
if c > mx then mx = c;
|
||||
}
|
||||
bt_assert(x = 39 && y = 49);
|
||||
bt_assert(mx = r.max);
|
||||
}
|
||||
|
||||
bt_test_suite(t_lclist, "Testing lists of large communities");
|
||||
|
@ -1094,6 +1179,7 @@ lc set lls;
|
|||
bt_assert(ll !~ [(5,10,15), (10,21,30)]);
|
||||
bt_assert(ll !~ [(10,21..25,*)]);
|
||||
bt_assert(ll !~ [(11, *, *)]);
|
||||
bt_assert(ll !~ []);
|
||||
|
||||
lls = [(10, 10, 10), (20, 20, 15..25), (30, 30, *), (40, 35..45, *), (50, *, *), (55..65, *, *)];
|
||||
bt_assert(format(lls) = "[(10, 10, 10), (20, 20, 15)..(20, 20, 25), (30, 30, 0)..(30, 30, 4294967295), (40, 35, 0)..(40, 45, 4294967295), (50, 0, 0)..(50, 4294967295, 4294967295), (55, 0, 0)..(65, 4294967295, 4294967295)]");
|
||||
|
@ -1150,6 +1236,10 @@ bt_test_suite(t_rd, "Testing route distinguishers");
|
|||
function t_rd_set()
|
||||
rd set rds;
|
||||
{
|
||||
rds = [];
|
||||
bt_assert(rds = []);
|
||||
bt_assert(10:20 !~ rds);
|
||||
|
||||
rds = [10:20, 100000:100..100000:200];
|
||||
bt_assert(format(rds) = "[10:20, 100000:100..100000:200]");
|
||||
|
||||
|
@ -1160,6 +1250,7 @@ rd set rds;
|
|||
bt_assert(100000:128 ~ rds);
|
||||
bt_assert(100000:200 ~ rds);
|
||||
bt_assert(100010:150 !~ rds);
|
||||
bt_assert(100010:150 !~ []);
|
||||
}
|
||||
|
||||
bt_test_suite(t_rd_set, "Testing sets of route distinguishers");
|
||||
|
@ -1226,7 +1317,85 @@ function fifteen()
|
|||
return 15;
|
||||
}
|
||||
|
||||
function local_vars(int j)
|
||||
{
|
||||
int k = 10;
|
||||
bt_assert(j = 5 && k = 10);
|
||||
{
|
||||
int j = 15;
|
||||
k = 20;
|
||||
bt_assert(j = 15 && k = 20);
|
||||
}
|
||||
bt_assert(j = 5 && k = 20);
|
||||
|
||||
if j < 10 then
|
||||
{
|
||||
int j = 25;
|
||||
string k = "hello";
|
||||
bt_assert(j = 25 && k = "hello");
|
||||
}
|
||||
bt_assert(j = 5 && k = 20);
|
||||
|
||||
int m = 100;
|
||||
{
|
||||
j = 35;
|
||||
int k = 40;
|
||||
bt_assert(j = 35 && k = 40 && m = 100);
|
||||
}
|
||||
bt_assert(j = 35 && k = 20 && m = 100);
|
||||
}
|
||||
|
||||
function factorial(int x)
|
||||
{
|
||||
if x = 0 then return 0;
|
||||
if x = 1 then return 1;
|
||||
else return x * factorial(x - 1);
|
||||
}
|
||||
|
||||
function fibonacci(int x)
|
||||
{
|
||||
if x = 0 then return 0;
|
||||
if x = 1 then return 1;
|
||||
else return fibonacci(x - 1) + fibonacci(x - 2);
|
||||
}
|
||||
|
||||
function hanoi_init(int a; int b)
|
||||
{
|
||||
if b = 0
|
||||
then return +empty+;
|
||||
else return prepend(hanoi_init(a + 1, b - 1), a);
|
||||
}
|
||||
|
||||
function hanoi_solve(int n; bgppath h_src; bgppath h_dst; bgppath h_aux; bool x; bool y)
|
||||
{
|
||||
# x -> return src or dst
|
||||
# y -> print state
|
||||
|
||||
if n = 0 then { if x then return h_src; else return h_dst; }
|
||||
|
||||
bgppath tmp1 = hanoi_solve(n - 1, h_src, h_aux, h_dst, true, y);
|
||||
bgppath tmp2 = hanoi_solve(n - 1, h_src, h_aux, h_dst, false, false);
|
||||
h_src = tmp1;
|
||||
h_aux = tmp2;
|
||||
|
||||
int v = h_src.first;
|
||||
# bt_assert(h_dst = +empty+ || v < h_dst.first);
|
||||
h_src = delete(h_src, v);
|
||||
h_dst = prepend(h_dst, v);
|
||||
|
||||
if y then
|
||||
print "move: ", v, " src: ", h_src, " dst:", h_dst, " aux:", h_aux;
|
||||
|
||||
tmp1 = hanoi_solve(n - 1, h_aux, h_dst, h_src, true, y);
|
||||
tmp2 = hanoi_solve(n - 1, h_aux, h_dst, h_src, false, false);
|
||||
h_aux = tmp1;
|
||||
h_dst = tmp2;
|
||||
|
||||
if x then return h_src; else return h_dst;
|
||||
}
|
||||
|
||||
function t_call_function()
|
||||
bgppath h_src;
|
||||
{
|
||||
bt_assert(fifteen() = 15);
|
||||
|
||||
|
@ -1238,6 +1407,17 @@ function t_call_function()
|
|||
bt_assert(callme(4, 4) = 16);
|
||||
bt_assert(callme(7, 2) = 14);
|
||||
bt_assert(callmeagain(1, 2, 3) = 6);
|
||||
local_vars(5);
|
||||
|
||||
bt_assert(factorial(5) = 120);
|
||||
bt_assert(factorial(10) = 3628800);
|
||||
|
||||
bt_assert(fibonacci(10) = 55);
|
||||
bt_assert(fibonacci(20) = 6765);
|
||||
|
||||
h_src = hanoi_init(1, 6);
|
||||
bt_assert(format(h_src) = "(path 1 2 3 4 5 6)");
|
||||
bt_assert(hanoi_solve(6, h_src, +empty+, +empty+, false, false) = h_src);
|
||||
}
|
||||
|
||||
bt_test_suite(t_call_function, "Testing calling functions");
|
||||
|
@ -1434,13 +1614,16 @@ filter vpn_filter
|
|||
bt_assert(net.type != NET_IP6);
|
||||
bt_assert(net.rd = 0:1:2);
|
||||
|
||||
bool b = false;
|
||||
case (net.type) {
|
||||
NET_IP4: print "IPV4";
|
||||
NET_IP6: print "IPV6";
|
||||
else: b = true;
|
||||
}
|
||||
bt_assert(b);
|
||||
|
||||
bt_check_assign(from, 10.20.30.40);
|
||||
bt_check_assign(gw, 55.55.55.44);
|
||||
# bt_check_assign(gw, 55.55.55.44);
|
||||
|
||||
bgp_community.add((3,5));
|
||||
bgp_ext_community.add((ro, 135, 999));
|
||||
|
|
|
@ -134,6 +134,14 @@ same_tree(const struct f_tree *t1, const struct f_tree *t2)
|
|||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
tree_node_count(const struct f_tree *t)
|
||||
{
|
||||
if (t == NULL)
|
||||
return 0;
|
||||
|
||||
return 1 + tree_node_count(t->left) + tree_node_count(t->right);
|
||||
}
|
||||
|
||||
static void
|
||||
tree_node_format(const struct f_tree *t, buffer *buf)
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -249,14 +249,14 @@ get_outer_net(net_addr *net, const struct f_prefix *src)
|
|||
}
|
||||
|
||||
static list *
|
||||
make_random_prefix_list(linpool *lp, int num, int v6, int tight)
|
||||
make_random_prefix_list(int num, int v6, int tight)
|
||||
{
|
||||
list *prefixes = lp_allocz(lp, sizeof(struct f_prefix_node));
|
||||
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(lp, sizeof(struct f_prefix_node));
|
||||
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);
|
||||
|
||||
|
@ -269,9 +269,9 @@ make_random_prefix_list(linpool *lp, int num, int v6, int tight)
|
|||
}
|
||||
|
||||
static struct f_trie *
|
||||
make_trie_from_prefix_list(linpool *lp, list *prefixes)
|
||||
make_trie_from_prefix_list(list *prefixes)
|
||||
{
|
||||
struct f_trie *trie = f_new_trie(lp, 0);
|
||||
struct f_trie *trie = f_new_trie(tmp_linpool, 0);
|
||||
|
||||
struct f_prefix_node *n;
|
||||
WALK_LIST(n, *prefixes)
|
||||
|
@ -286,7 +286,7 @@ make_trie_from_prefix_list(linpool *lp, list *prefixes)
|
|||
* Arg @plus means prefix should include all longer ones.
|
||||
*/
|
||||
static list *
|
||||
read_prefix_list(linpool *lp, FILE *f, int v6, int plus)
|
||||
read_prefix_list(FILE *f, int v6, int plus)
|
||||
{
|
||||
ASSERT(!v6);
|
||||
|
||||
|
@ -294,7 +294,7 @@ read_prefix_list(linpool *lp, FILE *f, int v6, int plus)
|
|||
char s[32];
|
||||
int n;
|
||||
|
||||
list *pxlist = lp_allocz(lp, sizeof(struct f_prefix_node));
|
||||
list *pxlist = lp_allocz(tmp_linpool, sizeof(struct f_prefix_node));
|
||||
init_list(pxlist);
|
||||
|
||||
errno = 0;
|
||||
|
@ -308,7 +308,7 @@ read_prefix_list(linpool *lp, FILE *f, int v6, int plus)
|
|||
if (n != 5)
|
||||
bt_abort_msg("Invalid content of trie_data");
|
||||
|
||||
struct f_prefix_node *px = lp_allocz(lp, sizeof(struct f_prefix_node));
|
||||
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;
|
||||
|
@ -331,7 +331,6 @@ read_prefix_list(linpool *lp, FILE *f, int v6, int plus)
|
|||
*/
|
||||
static int
|
||||
read_prefix_file(const char *filename, int plus,
|
||||
linpool *lp0, linpool *lp1,
|
||||
list *data[], struct f_trie *trie[])
|
||||
{
|
||||
FILE *f = fopen(filename, "r");
|
||||
|
@ -339,10 +338,10 @@ read_prefix_file(const char *filename, int plus,
|
|||
|
||||
int n = 0;
|
||||
list *pxlist;
|
||||
while (pxlist = read_prefix_list(lp0, f, 0, plus))
|
||||
while (pxlist = read_prefix_list(f, 0, plus))
|
||||
{
|
||||
data[n] = pxlist;
|
||||
trie[n] = make_trie_from_prefix_list(lp1, pxlist);
|
||||
trie[n] = make_trie_from_prefix_list(pxlist);
|
||||
bt_debug("NEXT\n");
|
||||
n++;
|
||||
}
|
||||
|
@ -437,11 +436,10 @@ t_match_random_net(void)
|
|||
bt_config_parse(BT_CONFIG_SIMPLE);
|
||||
|
||||
int v6 = 0;
|
||||
linpool *lp = lp_new_default(&root_pool);
|
||||
for (int round = 0; round < TESTS_NUM; round++)
|
||||
{
|
||||
list *prefixes = make_random_prefix_list(lp, PREFIXES_NUM, v6, 0);
|
||||
struct f_trie *trie = make_trie_from_prefix_list(lp, prefixes);
|
||||
list *prefixes = make_random_prefix_list(PREFIXES_NUM, v6, 0);
|
||||
struct f_trie *trie = make_trie_from_prefix_list(prefixes);
|
||||
|
||||
for (int i = 0; i < PREFIX_TESTS_NUM; i++)
|
||||
{
|
||||
|
@ -451,7 +449,7 @@ t_match_random_net(void)
|
|||
}
|
||||
|
||||
v6 = !v6;
|
||||
lp_flush(lp);
|
||||
tmp_flush();
|
||||
}
|
||||
|
||||
bt_bird_cleanup();
|
||||
|
@ -465,11 +463,10 @@ t_match_inner_net(void)
|
|||
bt_config_parse(BT_CONFIG_SIMPLE);
|
||||
|
||||
int v6 = 0;
|
||||
linpool *lp = lp_new_default(&root_pool);
|
||||
for (int round = 0; round < TESTS_NUM; round++)
|
||||
{
|
||||
list *prefixes = make_random_prefix_list(lp, PREFIXES_NUM, v6, 0);
|
||||
struct f_trie *trie = make_trie_from_prefix_list(lp, prefixes);
|
||||
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++)
|
||||
|
@ -482,7 +479,7 @@ t_match_inner_net(void)
|
|||
}
|
||||
|
||||
v6 = !v6;
|
||||
lp_flush(lp);
|
||||
tmp_flush();
|
||||
}
|
||||
|
||||
bt_bird_cleanup();
|
||||
|
@ -496,11 +493,10 @@ t_match_outer_net(void)
|
|||
bt_config_parse(BT_CONFIG_SIMPLE);
|
||||
|
||||
int v6 = 0;
|
||||
linpool *lp = lp_new_default(&root_pool);
|
||||
for (int round = 0; round < TESTS_NUM; round++)
|
||||
{
|
||||
list *prefixes = make_random_prefix_list(lp, PREFIXES_NUM, v6, 0);
|
||||
struct f_trie *trie = make_trie_from_prefix_list(lp, prefixes);
|
||||
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++)
|
||||
|
@ -513,7 +509,7 @@ t_match_outer_net(void)
|
|||
}
|
||||
|
||||
v6 = !v6;
|
||||
lp_flush(lp);
|
||||
tmp_flush();
|
||||
}
|
||||
|
||||
v6 = !v6;
|
||||
|
@ -531,24 +527,22 @@ static int
|
|||
benchmark_trie_dataset(const char *filename, int plus)
|
||||
{
|
||||
int n = 0;
|
||||
linpool *lp0 = lp_new_default(&root_pool);
|
||||
linpool *lp1 = lp_new_default(&root_pool);
|
||||
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, lp0, lp1, data, trie);
|
||||
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(lp1).effective * 1000 / (1024*1024);
|
||||
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 = lp_alloc(lp0, tb * sizeof(net_addr));
|
||||
nets = tmp_alloc(tb * sizeof(net_addr));
|
||||
|
||||
if (!plus)
|
||||
select_random_prefix_subset(data, nets, n, tb);
|
||||
|
@ -573,9 +567,7 @@ benchmark_trie_dataset(const char *filename, int plus)
|
|||
|
||||
bt_log_suite_case_result(1, "Matching done, %d / %d matches", match, t * n);
|
||||
|
||||
rfree(lp0);
|
||||
rfree(lp1);
|
||||
|
||||
tmp_flush();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -621,12 +613,11 @@ t_trie_same(void)
|
|||
bt_config_parse(BT_CONFIG_SIMPLE);
|
||||
|
||||
int v6 = 0;
|
||||
linpool *lp = lp_new_default(&root_pool);
|
||||
for (int round = 0; round < TESTS_NUM*4; round++)
|
||||
{
|
||||
list *prefixes = make_random_prefix_list(lp, 100 * PREFIXES_NUM, v6, 0);
|
||||
struct f_trie *trie1 = f_new_trie(lp, 0);
|
||||
struct f_trie *trie2 = f_new_trie(lp, 0);
|
||||
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)
|
||||
|
@ -638,7 +629,7 @@ t_trie_same(void)
|
|||
bt_assert(trie_same(trie1, trie2));
|
||||
|
||||
v6 = !v6;
|
||||
lp_flush(lp);
|
||||
tmp_flush();
|
||||
}
|
||||
|
||||
bt_bird_cleanup();
|
||||
|
@ -664,15 +655,14 @@ t_trie_walk(void)
|
|||
bt_bird_init();
|
||||
bt_config_parse(BT_CONFIG_SIMPLE);
|
||||
|
||||
linpool *lp = lp_new_default(&root_pool);
|
||||
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(lp, num, v6, 1);
|
||||
struct f_trie *trie = make_trie_from_prefix_list(lp, prefixes);
|
||||
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;
|
||||
|
@ -770,7 +760,7 @@ t_trie_walk(void)
|
|||
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);
|
||||
|
||||
lp_flush(lp);
|
||||
tmp_flush();
|
||||
}
|
||||
|
||||
bt_bird_cleanup();
|
||||
|
@ -815,7 +805,6 @@ t_trie_walk_to_root(void)
|
|||
bt_bird_init();
|
||||
bt_config_parse(BT_CONFIG_SIMPLE);
|
||||
|
||||
linpool *lp = lp_new_default(&root_pool);
|
||||
for (int round = 0; round < TESTS_NUM * 4; round++)
|
||||
{
|
||||
int level = round / TESTS_NUM;
|
||||
|
@ -824,8 +813,8 @@ t_trie_walk_to_root(void)
|
|||
int pos = 0;
|
||||
int st = 0, sn = 0, sm = 0;
|
||||
|
||||
list *prefixes = make_random_prefix_list(lp, num, v6, 1);
|
||||
struct f_trie *trie = make_trie_from_prefix_list(lp, prefixes);
|
||||
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;
|
||||
|
@ -884,7 +873,7 @@ t_trie_walk_to_root(void)
|
|||
|
||||
bt_debug("Success in %d / %d, sum %d, max %d\n", sn, i, st, sm);
|
||||
|
||||
lp_flush(lp);
|
||||
tmp_flush();
|
||||
}
|
||||
|
||||
bt_bird_cleanup();
|
||||
|
|
|
@ -2,6 +2,6 @@ src := bitmap.c bitops.c blake2s.c blake2b.c checksum.c event.c flowspec.c idm.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 := 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
|
||||
tests_targets := $(tests_targets) $(tests-target-files)
|
||||
tests_objs := $(tests_objs) $(src-o-files)
|
||||
|
|
|
@ -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] = {};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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--;
|
||||
}
|
||||
|
||||
|
|
|
@ -53,7 +53,6 @@ t_ev_run_list(void)
|
|||
{
|
||||
int i;
|
||||
|
||||
resource_init();
|
||||
olock_init();
|
||||
timer_init();
|
||||
io_init();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -215,6 +215,12 @@ mem_hash_mix(u64 *h, const void *p, uint s)
|
|||
*h = *h * multiplier + pp[i];
|
||||
}
|
||||
|
||||
static inline void
|
||||
mem_hash_mix_num(u64 *h, u64 val)
|
||||
{
|
||||
mem_hash_mix(h, &val, sizeof(val));
|
||||
}
|
||||
|
||||
static inline uint
|
||||
mem_hash_value(u64 *h)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -27,21 +27,22 @@
|
|||
|
||||
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;
|
||||
struct lp_chunk *first, *current; /* Normal (reusable) chunks */
|
||||
struct lp_chunk *first_large; /* Large chunks */
|
||||
uint chunk_size, threshold, total, total_large;
|
||||
uint total, total_large;
|
||||
};
|
||||
|
||||
_Thread_local linpool *tmp_linpool;
|
||||
|
||||
static void lp_free(resource *);
|
||||
static void lp_dump(resource *);
|
||||
static resource *lp_lookup(resource *, unsigned long);
|
||||
|
@ -59,19 +60,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->chunk_size = blk;
|
||||
m->threshold = 3*blk/4;
|
||||
return m;
|
||||
return ralloc(p, &lp_class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -102,14 +98,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
|
||||
{
|
||||
|
@ -121,10 +116,10 @@ lp_alloc(linpool *m, uint size)
|
|||
else
|
||||
{
|
||||
/* Need to allocate a new chunk */
|
||||
c = xmalloc(sizeof(struct lp_chunk) + m->chunk_size);
|
||||
m->total += m->chunk_size;
|
||||
c = alloc_page();
|
||||
|
||||
m->total += LP_DATA_SIZE;
|
||||
c->next = NULL;
|
||||
c->size = m->chunk_size;
|
||||
|
||||
if (m->current)
|
||||
m->current->next = c;
|
||||
|
@ -133,7 +128,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;
|
||||
}
|
||||
|
@ -195,7 +190,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)
|
||||
{
|
||||
|
@ -218,6 +213,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;
|
||||
}
|
||||
|
||||
|
@ -239,12 +235,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);
|
||||
}
|
||||
}
|
||||
|
@ -258,7 +254,7 @@ lp_free(resource *r)
|
|||
for(d=m->first; d; d = c)
|
||||
{
|
||||
c = d->next;
|
||||
xfree(d);
|
||||
free_page(d);
|
||||
}
|
||||
for(d=m->first_large; d; d = c)
|
||||
{
|
||||
|
@ -278,9 +274,7 @@ 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,
|
||||
|
@ -291,19 +285,22 @@ static struct resmem
|
|||
lp_memsize(resource *r)
|
||||
{
|
||||
linpool *m = (linpool *) r;
|
||||
struct lp_chunk *c;
|
||||
int cnt = 0;
|
||||
|
||||
for(c=m->first; c; c=c->next)
|
||||
cnt++;
|
||||
for(c=m->first_large; c; c=c->next)
|
||||
cnt++;
|
||||
|
||||
return (struct resmem) {
|
||||
.effective = m->total + m->total_large,
|
||||
.overhead = ALLOC_OVERHEAD + sizeof(struct linpool) +
|
||||
cnt * (ALLOC_OVERHEAD + sizeof(struct lp_chunk)),
|
||||
struct resmem sz = {
|
||||
.overhead = sizeof(struct linpool) + ALLOC_OVERHEAD,
|
||||
.effective = m->total_large,
|
||||
};
|
||||
|
||||
for (struct lp_chunk *c = m->first_large; c; c = c->next)
|
||||
sz.overhead += sizeof(struct lp_chunk) + ALLOC_OVERHEAD;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -314,10 +311,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;
|
||||
}
|
||||
|
|
48
lib/printf.c
48
lib/printf.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -70,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)
|
||||
{
|
||||
|
@ -270,9 +284,12 @@ 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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -407,21 +424,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
|
||||
|
|
|
@ -44,6 +44,7 @@ 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 */
|
||||
struct resmem rmemsize(void *res); /* Return size of memory used by the resource */
|
||||
|
@ -59,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 */
|
||||
|
@ -69,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 */
|
||||
|
@ -79,10 +80,16 @@ 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, LP_GOOD_SIZE(LP_GAS*4))
|
||||
extern _Thread_local linpool *tmp_linpool; /* Temporary linpool autoflushed regularily */
|
||||
|
||||
#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)
|
||||
|
||||
#define tmp_init(p) tmp_linpool = lp_new_default(p)
|
||||
#define tmp_flush() lp_flush(tmp_linpool)
|
||||
|
||||
#define lp_new_default lp_new
|
||||
|
||||
/* Slabs */
|
||||
|
||||
|
@ -91,7 +98,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
|
||||
|
@ -101,10 +108,11 @@ void sl_free(slab *, void *);
|
|||
void buffer_realloc(void **buf, unsigned *size, unsigned need, unsigned item_size);
|
||||
|
||||
/* Allocator of whole pages; for use in slabs and other high-level allocators. */
|
||||
u64 get_page_size(void);
|
||||
extern long page_size;
|
||||
void *alloc_page(void);
|
||||
void free_page(void *);
|
||||
extern uint pages_kept;
|
||||
|
||||
void resource_sys_init(void);
|
||||
|
||||
#ifdef HAVE_LIBDMALLOC
|
||||
/*
|
||||
|
|
128
lib/slab.c
128
lib/slab.c
|
@ -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 */
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -153,11 +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;
|
||||
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 = {
|
||||
|
@ -169,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 *) (((uintptr_t) (x)) & ~(get_page_size()-1)))
|
||||
|
||||
/**
|
||||
* sl_new - create a new Slab
|
||||
|
@ -202,7 +227,6 @@ sl_new(pool *p, uint size)
|
|||
s->obj_size = size;
|
||||
|
||||
s->head_size = sizeof(struct sl_head);
|
||||
u64 page_size = get_page_size();
|
||||
|
||||
do {
|
||||
s->objs_per_slab = (page_size - s->head_size) / size;
|
||||
|
@ -218,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;
|
||||
}
|
||||
|
||||
|
@ -237,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++)
|
||||
|
@ -258,23 +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();
|
||||
ASSERT_DIE(SL_GET_HEAD(h) == h);
|
||||
|
||||
#ifdef POISON
|
||||
memset(h, 0xba, page_size);
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -303,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);
|
||||
|
@ -318,19 +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(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++;
|
||||
}
|
||||
}
|
||||
|
@ -340,13 +368,12 @@ static void
|
|||
slab_free(resource *r)
|
||||
{
|
||||
slab *s = (slab *) r;
|
||||
struct sl_head *h, *g;
|
||||
|
||||
WALK_LIST_DELSAFE(h, g, s->empty_heads)
|
||||
WALK_TLIST_DELSAFE(sl_head, h, &s->empty_heads)
|
||||
free_page(h);
|
||||
WALK_LIST_DELSAFE(h, g, s->partial_heads)
|
||||
WALK_TLIST_DELSAFE(sl_head, h, &s->partial_heads)
|
||||
free_page(h);
|
||||
WALK_LIST_DELSAFE(h, g, s->full_heads)
|
||||
WALK_TLIST_DELSAFE(sl_head, h, &s->full_heads)
|
||||
free_page(h);
|
||||
}
|
||||
|
||||
|
@ -355,13 +382,12 @@ 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);
|
||||
}
|
||||
|
@ -371,27 +397,26 @@ slab_memsize(resource *r)
|
|||
{
|
||||
slab *s = (slab *) r;
|
||||
size_t heads = 0;
|
||||
struct sl_head *h;
|
||||
|
||||
WALK_LIST(h, s->full_heads)
|
||||
WALK_TLIST(sl_head, h, &s->full_heads)
|
||||
heads++;
|
||||
|
||||
size_t items = heads * s->objs_per_slab;
|
||||
|
||||
WALK_LIST(h, s->partial_heads)
|
||||
WALK_TLIST(sl_head, h, &s->partial_heads)
|
||||
{
|
||||
heads++;
|
||||
items += h->num_full;
|
||||
}
|
||||
|
||||
WALK_LIST(h, s->empty_heads)
|
||||
WALK_TLIST(sl_head, h, &s->empty_heads)
|
||||
heads++;
|
||||
|
||||
size_t eff = items * s->obj_size;
|
||||
size_t eff = items * s->data_size;
|
||||
|
||||
return (struct resmem) {
|
||||
.effective = eff,
|
||||
.overhead = ALLOC_OVERHEAD + sizeof(struct slab) + heads * get_page_size() - eff,
|
||||
.overhead = ALLOC_OVERHEAD + sizeof(struct slab) + heads * page_size - eff,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -399,13 +424,12 @@ static resource *
|
|||
slab_lookup(resource *r, unsigned long a)
|
||||
{
|
||||
slab *s = (slab *) r;
|
||||
struct sl_head *h;
|
||||
|
||||
WALK_LIST(h, s->partial_heads)
|
||||
if ((unsigned long) h < a && (unsigned long) h + get_page_size() < a)
|
||||
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)
|
||||
if ((unsigned long) h < a && (unsigned long) h + get_page_size() < a)
|
||||
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
171
lib/slab_test.c
Normal 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();
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -246,6 +246,7 @@ timers_fire(struct timeloop *loop)
|
|||
io_log_event(t->hook, t->data);
|
||||
|
||||
t->hook(t);
|
||||
tmp_flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
172
lib/tlists.h
Normal file
172
lib/tlists.h
Normal 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
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
Summary: BIRD Internet Routing Daemon
|
||||
Name: bird
|
||||
Version: 2.0.9
|
||||
Version: 2.0.11
|
||||
Release: 1
|
||||
Copyright: GPL
|
||||
Group: Networking/Daemons
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
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 := a-path.c a-set.c 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)
|
||||
|
||||
$(objdir)/nest/proto-build.c: $(lastword $(MAKEFILE_LIST))
|
||||
$(E)echo GEN $@
|
||||
$(Q)echo "$(patsubst %,void %_build(void); ,$(PROTO_BUILD)) void protos_build_gen(void) { $(patsubst %, %_build(); ,$(PROTO_BUILD))}" > $@
|
||||
|
||||
tests_src := a-set_test.c a-path_test.c
|
||||
tests_targets := $(tests_targets) $(tests-target-files)
|
||||
tests_objs := $(tests_objs) $(src-o-files)
|
||||
|
|
|
@ -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;
|
||||
|
@ -602,8 +602,10 @@ as_path_match_set(const struct adata *path, const struct f_tree *set)
|
|||
}
|
||||
|
||||
const struct adata *
|
||||
as_path_filter(struct linpool *pool, const struct adata *path, const struct f_tree *set, u32 key, int pos)
|
||||
as_path_filter(struct linpool *pool, const struct adata *path, const struct f_val *set, int pos)
|
||||
{
|
||||
ASSERT((set->type == T_SET) || (set->type == T_INT));
|
||||
|
||||
if (!path)
|
||||
return NULL;
|
||||
|
||||
|
@ -629,13 +631,13 @@ as_path_filter(struct linpool *pool, const struct adata *path, const struct f_tr
|
|||
u32 as = get_as(p);
|
||||
int match;
|
||||
|
||||
if (set)
|
||||
if (set->type == T_SET)
|
||||
{
|
||||
struct f_val v = {T_INT, .val.i = as};
|
||||
match = !!find_tree(set, &v);
|
||||
struct f_val v = { .type = T_INT, .val.i = as};
|
||||
match = !!find_tree(set->val.t, &v);
|
||||
}
|
||||
else
|
||||
match = (as == key);
|
||||
else /* T_INT */
|
||||
match = (as == set->val.i);
|
||||
|
||||
if (match == pos)
|
||||
{
|
||||
|
@ -667,6 +669,35 @@ as_path_filter(struct linpool *pool, const struct adata *path, const struct f_tr
|
|||
return res;
|
||||
}
|
||||
|
||||
int
|
||||
as_path_walk(const struct adata *path, uint *pos, uint *val)
|
||||
{
|
||||
if (!path)
|
||||
return 0;
|
||||
|
||||
const u8 *p = path->data;
|
||||
const u8 *q = p + path->length;
|
||||
uint n, x = *pos;
|
||||
|
||||
while (p < q)
|
||||
{
|
||||
n = p[1];
|
||||
p += 2;
|
||||
|
||||
if (x < n)
|
||||
{
|
||||
*val = get_as(p + x * BS);
|
||||
*pos += 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
p += n * BS;
|
||||
x -= n;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct pm_pos
|
||||
{
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "nest/route.h"
|
||||
#include "nest/attrs.h"
|
||||
#include "lib/resource.h"
|
||||
#include "filter/data.h"
|
||||
|
||||
#define TESTS_NUM 30
|
||||
#define AS_PATH_LENGTH 1000
|
||||
|
@ -23,8 +24,6 @@
|
|||
static int
|
||||
t_as_path_match(void)
|
||||
{
|
||||
resource_init();
|
||||
|
||||
int round;
|
||||
for (round = 0; round < TESTS_NUM; round++)
|
||||
{
|
||||
|
@ -32,14 +31,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 +59,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 +68,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 +92,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 +111,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 +120,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 +128,9 @@ 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);
|
||||
struct f_val v = { .type = T_INT, .val.i = as_nums[i] };
|
||||
bt_assert(as_path_filter(tmp_linpool, as_path, &v, 0) != NULL);
|
||||
bt_assert(as_path_filter(tmp_linpool, as_path, &v, 1) != NULL);
|
||||
}
|
||||
|
||||
for (i = 0; i < 10000; i++)
|
||||
|
@ -152,7 +145,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 +154,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);
|
||||
|
||||
|
|
48
nest/a-set.c
48
nest/a-set.c
|
@ -693,3 +693,51 @@ lc_set_max(const struct adata *list, lcomm *val)
|
|||
*val = (lcomm) { res[0], res[1], res[2] };
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
int_set_walk(const struct adata *list, uint *pos, uint *val)
|
||||
{
|
||||
if (!list)
|
||||
return 0;
|
||||
|
||||
if (*pos >= (uint) int_set_get_size(list))
|
||||
return 0;
|
||||
|
||||
u32 *res = int_set_get_data(list) + *pos;
|
||||
*val = *res;
|
||||
*pos += 1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
ec_set_walk(const struct adata *list, uint *pos, u64 *val)
|
||||
{
|
||||
if (!list)
|
||||
return 0;
|
||||
|
||||
if (*pos >= (uint) int_set_get_size(list))
|
||||
return 0;
|
||||
|
||||
u32 *res = int_set_get_data(list) + *pos;
|
||||
*val = ec_generic(res[0], res[1]);
|
||||
*pos += 2;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
lc_set_walk(const struct adata *list, uint *pos, lcomm *val)
|
||||
{
|
||||
if (!list)
|
||||
return 0;
|
||||
|
||||
if (*pos >= (uint) int_set_get_size(list))
|
||||
return 0;
|
||||
|
||||
u32 *res = int_set_get_data(list) + *pos;
|
||||
*val = (lcomm) { res[0], res[1], res[2] };
|
||||
*pos += 3;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
16
nest/attrs.h
16
nest/attrs.h
|
@ -28,6 +28,7 @@
|
|||
* to 16bit slot (like in 16bit AS_PATH). See RFC 4893 for details
|
||||
*/
|
||||
|
||||
struct f_val;
|
||||
struct f_tree;
|
||||
|
||||
int as_path_valid(byte *data, uint len, int bs, int sets, int confed, char *err, uint elen);
|
||||
|
@ -49,7 +50,8 @@ int as_path_get_last(const struct adata *path, u32 *last_as);
|
|||
u32 as_path_get_last_nonaggregated(const struct adata *path);
|
||||
int as_path_contains(const struct adata *path, u32 as, int min);
|
||||
int as_path_match_set(const struct adata *path, const struct f_tree *set);
|
||||
const struct adata *as_path_filter(struct linpool *pool, const struct adata *path, const struct f_tree *set, u32 key, int pos);
|
||||
const struct adata *as_path_filter(struct linpool *pool, const struct adata *path, const struct f_val *set, int pos);
|
||||
int as_path_walk(const struct adata *path, uint *pos, uint *val);
|
||||
|
||||
static inline struct adata *as_path_prepend(struct linpool *pool, const struct adata *path, u32 as)
|
||||
{ return as_path_prepend2(pool, path, AS_PATH_SEQUENCE, as); }
|
||||
|
@ -136,6 +138,11 @@ static inline const char *ec_subtype_str(const enum ec_subtype ecs) {
|
|||
}
|
||||
}
|
||||
|
||||
/* Check for EC_RT subtype within different types (0-2) */
|
||||
static inline int ec_type_is_rt(uint type)
|
||||
{ return (type == EC_RT) || (type == (0x0100 | EC_RT)) || (type == (0x0200 | EC_RT)); }
|
||||
|
||||
|
||||
/* Transitive bit (for first u32 half of EC) */
|
||||
#define EC_TBIT 0x40000000
|
||||
|
||||
|
@ -155,9 +162,13 @@ static inline u32 *int_set_get_data(const struct adata *list)
|
|||
|
||||
static inline u32 ec_hi(u64 ec) { return ec >> 32; }
|
||||
static inline u32 ec_lo(u64 ec) { return ec; }
|
||||
|
||||
static inline u64 ec_get(const u32 *l, int i)
|
||||
{ return (((u64) l[i]) << 32) | l[i+1]; }
|
||||
|
||||
static inline void ec_put(u32 *l, int i, u64 val)
|
||||
{ l[i] = ec_hi(val); l[i+1] = ec_lo(val); }
|
||||
|
||||
/* RFC 4360 3.1. Two-Octet AS Specific Extended Community */
|
||||
static inline u64 ec_as2(enum ec_subtype kind, u64 key, u64 val)
|
||||
{ return (((u64) kind | 0x0000) << 48) | (key << 32) | val; }
|
||||
|
@ -224,6 +235,9 @@ 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);
|
||||
int int_set_walk(const struct adata *list, uint *pos, u32 *val);
|
||||
int ec_set_walk(const struct adata *list, uint *pos, u64 *val);
|
||||
int lc_set_walk(const struct adata *list, uint *pos, lcomm *val);
|
||||
|
||||
void ec_set_sort_x(struct adata *set); /* Sort in place */
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ typedef struct cli {
|
|||
node n; /* Node in list of all log hooks */
|
||||
pool *pool;
|
||||
void *priv; /* Private to sysdep layer */
|
||||
byte *rx_buf, *rx_pos, *rx_aux; /* sysdep */
|
||||
byte *rx_buf, *rx_pos; /* sysdep */
|
||||
struct cli_out *tx_buf, *tx_pos, *tx_write;
|
||||
event *event;
|
||||
void (*cont)(struct cli *c);
|
||||
|
|
|
@ -108,6 +108,7 @@ print_size(char *dsc, struct resmem vals)
|
|||
|
||||
extern pool *rt_table_pool;
|
||||
extern pool *rta_pool;
|
||||
extern uint *pages_kept;
|
||||
|
||||
void
|
||||
cmd_show_memory(void)
|
||||
|
@ -117,10 +118,11 @@ cmd_show_memory(void)
|
|||
print_size("Routing tables:", rmemsize(rt_table_pool));
|
||||
print_size("Route attributes:", rmemsize(rta_pool));
|
||||
print_size("Protocols:", rmemsize(proto_pool));
|
||||
print_size("Current config:", rmemsize(config_pool));
|
||||
struct resmem total = rmemsize(&root_pool);
|
||||
#ifdef HAVE_MMAP
|
||||
print_size("Standby memory:", (struct resmem) { .overhead = get_page_size() * pages_kept });
|
||||
total.overhead += get_page_size() * pages_kept;
|
||||
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, "");
|
||||
|
|
|
@ -125,12 +125,12 @@ CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LONG, ROUTE, PROTOCOL, BASE, LOG, S, MS, US)
|
|||
CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
|
||||
CF_KEYWORDS(MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE)
|
||||
CF_KEYWORDS(CHECK, LINK)
|
||||
CF_KEYWORDS(SORTED, TRIE, MIN, MAX, SETTLE, TIME)
|
||||
CF_KEYWORDS(SORTED, TRIE, MIN, MAX, SETTLE, TIME, GC, THRESHOLD, PERIOD)
|
||||
|
||||
/* 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)
|
||||
|
||||
CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT,
|
||||
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)
|
||||
|
@ -165,7 +165,7 @@ rtrid:
|
|||
|
||||
idval:
|
||||
NUM { $$ = $1; }
|
||||
| '(' term ')' { $$ = f_eval_int(f_linearize($2)); }
|
||||
| '(' term ')' { $$ = f_eval_int(f_linearize($2, 1)); }
|
||||
| IP4 { $$ = ip4_to_u32($1); }
|
||||
| CF_SYM_KNOWN {
|
||||
if ($1->class == (SYM_CONSTANT | T_INT) || $1->class == (SYM_CONSTANT | T_QUAD))
|
||||
|
@ -229,6 +229,8 @@ table_opt:
|
|||
}
|
||||
| MIN SETTLE TIME expr_us { this_table->min_settle_time = $4; }
|
||||
| MAX SETTLE TIME expr_us { this_table->max_settle_time = $4; }
|
||||
| GC THRESHOLD expr { this_table->gc_threshold = $3; }
|
||||
| GC PERIOD expr_us { this_table->gc_period = (uint) $3; if ($3 > 3600 S_) cf_error("GC period must be at most 3600 s"); }
|
||||
;
|
||||
|
||||
table_opts:
|
||||
|
@ -630,7 +632,7 @@ CF_CLI(SHOW INTERFACES SUMMARY,,, [[Show summary of network interfaces]])
|
|||
{ if_show_summary(); } ;
|
||||
|
||||
CF_CLI_HELP(SHOW ROUTE, ..., [[Show routing table]])
|
||||
CF_CLI(SHOW ROUTE, r_args, [[[<prefix>|for <prefix>|for <ip>] [table <t>] [filter <f>|where <cond>] [all] [primary] [filtered] [(export|preexport|noexport) <p>] [protocol <p>] [stats|count]]], [[Show routing table]])
|
||||
CF_CLI(SHOW ROUTE, r_args, [[[<prefix>|for <prefix>|for <ip>] [table <t>] [(import|export) table <p>.<c>] [filter <f>|where <cond>] [all] [primary] [filtered] [(export|preexport|noexport) <p>] [protocol <p>] [stats|count]]], [[Show routing table]])
|
||||
{ rt_show($3); } ;
|
||||
|
||||
r_args:
|
||||
|
@ -858,7 +860,7 @@ CF_CLI(DUMP FILTER ALL,,, [[Dump all filters in linearized form]])
|
|||
{ filters_dump_all(); cli_msg(0, ""); } ;
|
||||
|
||||
CF_CLI(EVAL, term, <expr>, [[Evaluate an expression]])
|
||||
{ cmd_eval(f_linearize($2)); } ;
|
||||
{ cmd_eval(f_linearize($2, 1)); } ;
|
||||
|
||||
CF_CLI_HELP(ECHO, ..., [[Control echoing of log messages]])
|
||||
CF_CLI(ECHO, echo_mask echo_size, (all | off | { debug|trace|info|remote|warning|error|auth [, ...] }) [<buffer-size>], [[Control echoing of log messages]]) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -75,16 +75,6 @@ int reconfigure(struct proto *p, struct proto_config *c)
|
|||
void dump(struct proto *p)
|
||||
{ DUMMY; }
|
||||
|
||||
/**
|
||||
* dump_attrs - dump protocol-dependent attributes
|
||||
* @e: a route entry
|
||||
*
|
||||
* This hook dumps all attributes in the &rte which belong to this
|
||||
* protocol to the debug output.
|
||||
*/
|
||||
void dump_attrs(rte *e)
|
||||
{ DUMMY; }
|
||||
|
||||
/**
|
||||
* start - request instance startup
|
||||
* @p: protocol instance
|
||||
|
@ -227,36 +217,6 @@ void rt_notify(struct proto *p, net *net, rte *new, rte *old, ea_list *attrs)
|
|||
void neigh_notify(neighbor *neigh)
|
||||
{ DUMMY; }
|
||||
|
||||
/**
|
||||
* make_tmp_attrs - convert embedded attributes to temporary ones
|
||||
* @e: route entry
|
||||
* @pool: linear pool to allocate attribute memory in
|
||||
*
|
||||
* This hook is called by the routing table functions if they need
|
||||
* to convert the protocol attributes embedded directly in the &rte
|
||||
* to temporary extended attributes in order to distribute them
|
||||
* to other protocols or to filters. make_tmp_attrs() creates
|
||||
* an &ea_list in the linear pool @pool, fills it with values of the
|
||||
* temporary attributes and returns a pointer to it.
|
||||
*/
|
||||
ea_list *make_tmp_attrs(rte *e, struct linpool *pool)
|
||||
{ DUMMY; }
|
||||
|
||||
/**
|
||||
* store_tmp_attrs - convert temporary attributes to embedded ones
|
||||
* @e: route entry
|
||||
* @attrs: temporary attributes to be converted
|
||||
*
|
||||
* This hook is an exact opposite of make_tmp_attrs() -- it takes
|
||||
* a list of extended attributes and converts them to attributes
|
||||
* embedded in the &rte corresponding to this protocol.
|
||||
*
|
||||
* You must be prepared for any of the attributes being missing
|
||||
* from the list and use default values instead.
|
||||
*/
|
||||
void store_tmp_attrs(rte *e, ea_list *attrs)
|
||||
{ DUMMY; }
|
||||
|
||||
/**
|
||||
* preexport - pre-filtering decisions before route export
|
||||
* @p: protocol instance the route is going to be exported to
|
||||
|
|
54
nest/proto.c
54
nest/proto.c
|
@ -23,9 +23,9 @@
|
|||
#include "filter/f-inst.h"
|
||||
|
||||
pool *proto_pool;
|
||||
list proto_list;
|
||||
list STATIC_LIST_INIT(proto_list);
|
||||
|
||||
static list protocol_list;
|
||||
static list STATIC_LIST_INIT(protocol_list);
|
||||
struct protocol *class_to_protocol[PROTOCOL__MAX];
|
||||
|
||||
#define CD(c, msg, args...) ({ if (c->debug & D_STATES) log(L_TRACE "%s.%s: " msg, c->proto->name, c->name ?: "?", ## args); })
|
||||
|
@ -804,6 +804,7 @@ channel_config_get(const struct channel_class *cc, const char *name, uint net_ty
|
|||
cf_error("Multiple %s channels", name);
|
||||
|
||||
cf->parent = proto;
|
||||
cf->copy = 1;
|
||||
return cf;
|
||||
}
|
||||
|
||||
|
@ -829,6 +830,9 @@ static int reconfigure_type; /* Hack to propagate type info to channel_reconfig
|
|||
int
|
||||
channel_reconfigure(struct channel *c, struct channel_config *cf)
|
||||
{
|
||||
/* Touched by reconfiguration */
|
||||
c->stale = 0;
|
||||
|
||||
/* FIXME: better handle these changes, also handle in_keep_filtered */
|
||||
if ((c->table != cf->table->table) || (cf->ra_mode && (c->ra_mode != cf->ra_mode)))
|
||||
return 0;
|
||||
|
@ -1651,6 +1655,8 @@ proto_build(struct protocol *p)
|
|||
/* FIXME: convert this call to some protocol hook */
|
||||
extern void bfd_init_all(void);
|
||||
|
||||
void protos_build_gen(void);
|
||||
|
||||
/**
|
||||
* protos_build - build a protocol list
|
||||
*
|
||||
|
@ -1663,44 +1669,7 @@ extern void bfd_init_all(void);
|
|||
void
|
||||
protos_build(void)
|
||||
{
|
||||
init_list(&proto_list);
|
||||
init_list(&protocol_list);
|
||||
|
||||
proto_build(&proto_device);
|
||||
#ifdef CONFIG_RADV
|
||||
proto_build(&proto_radv);
|
||||
#endif
|
||||
#ifdef CONFIG_RIP
|
||||
proto_build(&proto_rip);
|
||||
#endif
|
||||
#ifdef CONFIG_STATIC
|
||||
proto_build(&proto_static);
|
||||
#endif
|
||||
#ifdef CONFIG_MRT
|
||||
proto_build(&proto_mrt);
|
||||
#endif
|
||||
#ifdef CONFIG_OSPF
|
||||
proto_build(&proto_ospf);
|
||||
#endif
|
||||
#ifdef CONFIG_PIPE
|
||||
proto_build(&proto_pipe);
|
||||
#endif
|
||||
#ifdef CONFIG_BGP
|
||||
proto_build(&proto_bgp);
|
||||
#endif
|
||||
#ifdef CONFIG_BFD
|
||||
proto_build(&proto_bfd);
|
||||
bfd_init_all();
|
||||
#endif
|
||||
#ifdef CONFIG_BABEL
|
||||
proto_build(&proto_babel);
|
||||
#endif
|
||||
#ifdef CONFIG_RPKI
|
||||
proto_build(&proto_rpki);
|
||||
#endif
|
||||
#ifdef CONFIG_PERF
|
||||
proto_build(&proto_perf);
|
||||
#endif
|
||||
protos_build_gen();
|
||||
|
||||
proto_pool = rp_new(&root_pool, "Protocols");
|
||||
proto_shutdown_timer = tm_new(proto_pool);
|
||||
|
@ -2243,9 +2212,14 @@ proto_apply_cmd_symbol(const struct symbol *s, void (* cmd)(struct proto *, uint
|
|||
return;
|
||||
}
|
||||
|
||||
if (s->proto->proto)
|
||||
{
|
||||
cmd(s->proto->proto, arg, 0);
|
||||
cli_msg(0, "");
|
||||
}
|
||||
else
|
||||
cli_msg(9002, "%s does not exist", s->name);
|
||||
}
|
||||
|
||||
static void
|
||||
proto_apply_cmd_patt(const char *patt, void (* cmd)(struct proto *, uintptr_t, int), uintptr_t arg)
|
||||
|
|
|
@ -74,7 +74,6 @@ struct protocol {
|
|||
struct proto * (*init)(struct proto_config *); /* Create new instance */
|
||||
int (*reconfigure)(struct proto *, struct proto_config *); /* Try to reconfigure instance, returns success */
|
||||
void (*dump)(struct proto *); /* Debugging dump */
|
||||
void (*dump_attrs)(struct rte *); /* Dump protocol-dependent attributes */
|
||||
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 */
|
||||
|
@ -85,8 +84,8 @@ struct protocol {
|
|||
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);
|
||||
|
@ -198,12 +197,11 @@ struct proto {
|
|||
* ifa_notify Notify protocol about interface address changes.
|
||||
* rt_notify Notify protocol about routing table updates.
|
||||
* neigh_notify Notify protocol about neighbor cache events.
|
||||
* make_tmp_attrs Add attributes to rta from from private attrs stored in rte. The route and rta MUST NOT be cached.
|
||||
* store_tmp_attrs Store private attrs back to rte and undef added attributes. The route and rta MUST NOT be cached.
|
||||
* preexport Called as the first step of the route exporting process.
|
||||
* It can construct a new rte, add private attributes and
|
||||
* decide whether the route shall be exported: 1=yes, -1=no,
|
||||
* 0=process it through the export filter set by the user.
|
||||
* It can decide whether the route shall be exported:
|
||||
* -1 = reject,
|
||||
* 0 = continue to export filter
|
||||
* 1 = accept immediately
|
||||
* reload_routes Request channel to reload all its routes to the core
|
||||
* (using rte_update()). Returns: 0=reload cannot be done,
|
||||
* 1= reload is scheduled and will happen (asynchronously).
|
||||
|
@ -215,9 +213,7 @@ struct proto {
|
|||
void (*ifa_notify)(struct proto *, unsigned flags, struct ifa *a);
|
||||
void (*rt_notify)(struct proto *, struct channel *, struct network *net, struct rte *new, struct rte *old);
|
||||
void (*neigh_notify)(struct neighbor *neigh);
|
||||
void (*make_tmp_attrs)(struct rte *rt, struct linpool *pool);
|
||||
void (*store_tmp_attrs)(struct rte *rt, struct linpool *pool);
|
||||
int (*preexport)(struct proto *, struct rte **rt, struct linpool *pool);
|
||||
int (*preexport)(struct channel *, struct rte *rt);
|
||||
void (*reload_routes)(struct channel *);
|
||||
void (*feed_begin)(struct channel *, int initial);
|
||||
void (*feed_end)(struct channel *);
|
||||
|
@ -235,11 +231,11 @@ struct proto {
|
|||
|
||||
int (*rte_recalculate)(struct rtable *, struct network *, struct rte *, struct rte *, struct rte *);
|
||||
int (*rte_better)(struct rte *, struct rte *);
|
||||
int (*rte_same)(struct rte *, struct rte *);
|
||||
int (*rte_mergable)(struct rte *, struct rte *);
|
||||
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 *);
|
||||
|
||||
/* Hic sunt protocol-specific data */
|
||||
};
|
||||
|
@ -469,7 +465,6 @@ struct channel_class {
|
|||
|
||||
|
||||
void (*dump)(struct proto *); /* Debugging dump */
|
||||
void (*dump_attrs)(struct rte *); /* Dump protocol-dependent attributes */
|
||||
|
||||
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) */
|
||||
|
@ -498,6 +493,7 @@ struct channel_config {
|
|||
u8 ra_mode; /* Mode of received route advertisements (RA_*) */
|
||||
u16 preference; /* Default route preference */
|
||||
u32 debug; /* Debugging flags (D_*) */
|
||||
u8 copy; /* Value from channel_config_get() is new (0) or from template (1) */
|
||||
u8 merge_limit; /* Maximal number of nexthops for RA_MERGED */
|
||||
u8 in_keep_filtered; /* Routes rejected in import filter are kept */
|
||||
u8 rpki_reload; /* RPKI changes trigger channel reload */
|
||||
|
|
139
nest/route.h
139
nest/route.h
|
@ -148,8 +148,8 @@ struct rtable_config {
|
|||
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 */
|
||||
uint gc_threshold; /* Maximum number of operations before GC is run */
|
||||
uint gc_period; /* Approximate 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 */
|
||||
|
@ -180,10 +180,11 @@ typedef struct rtable {
|
|||
* obstacle from this routing table.
|
||||
*/
|
||||
struct event *rt_event; /* Routing table event */
|
||||
struct timer *prune_timer; /* Timer for periodic pruning / GC */
|
||||
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 */
|
||||
uint 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 */
|
||||
|
@ -255,50 +256,13 @@ struct hostentry {
|
|||
typedef struct rte {
|
||||
struct rte *next;
|
||||
net *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 */
|
||||
struct rta *attrs; /* Attributes of this route */
|
||||
u32 id; /* Table specific route id */
|
||||
byte flags; /* Flags (REF_...) */
|
||||
byte pflags; /* Protocol-specific flags */
|
||||
word pref; /* Route preference */
|
||||
btime lastmod; /* Last modified */
|
||||
union { /* Protocol-dependent data (metrics etc.) */
|
||||
#ifdef CONFIG_RIP
|
||||
struct {
|
||||
struct iface *from; /* Incoming iface */
|
||||
u8 metric; /* RIP metric */
|
||||
u16 tag; /* External route tag */
|
||||
} rip;
|
||||
#endif
|
||||
#ifdef CONFIG_OSPF
|
||||
struct {
|
||||
u32 metric1, metric2; /* OSPF Type 1 and Type 2 metrics */
|
||||
u32 tag; /* External route tag */
|
||||
u32 router_id; /* Router that originated this route */
|
||||
} ospf;
|
||||
#endif
|
||||
#ifdef CONFIG_BGP
|
||||
struct {
|
||||
u8 suppressed; /* Used for deterministic MED comparison */
|
||||
s8 stale; /* Route is LLGR_STALE, -1 if unknown */
|
||||
struct rtable *base_table; /* Base table for Flowspec validation */
|
||||
} bgp;
|
||||
#endif
|
||||
#ifdef CONFIG_BABEL
|
||||
struct {
|
||||
u16 seqno; /* Babel seqno */
|
||||
u16 metric; /* Babel metric */
|
||||
u64 router_id; /* Babel router id */
|
||||
} babel;
|
||||
#endif
|
||||
struct { /* Routes generated by krt sync (both temporary and inherited ones) */
|
||||
s8 src; /* Alleged route source (see krt.h) */
|
||||
u8 proto; /* Kernel source protocol ID */
|
||||
u8 seen; /* Seen during last scan */
|
||||
u8 best; /* Best route in network, propagated to core */
|
||||
u32 metric; /* Kernel metric */
|
||||
} krt;
|
||||
} u;
|
||||
} rte;
|
||||
|
||||
#define REF_COW 1 /* Copy this rte on write */
|
||||
|
@ -332,6 +296,7 @@ struct config;
|
|||
|
||||
void rt_init(void);
|
||||
void rt_preconfig(struct config *);
|
||||
void rt_postconfig(struct config *);
|
||||
void rt_commit(struct config *new, struct config *old);
|
||||
void rt_lock_table(rtable *);
|
||||
void rt_unlock_table(rtable *);
|
||||
|
@ -352,10 +317,10 @@ net *net_get(rtable *tab, const net_addr *addr);
|
|||
net *net_route(rtable *tab, const net_addr *n);
|
||||
int net_roa_check(rtable *tab, const net_addr *n, u32 asn);
|
||||
rte *rte_find(net *net, struct rte_src *src);
|
||||
rte *rte_get_temp(struct rta *);
|
||||
rte *rte_get_temp(struct rta *, struct rte_src *src);
|
||||
void rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src);
|
||||
/* rte_update() moved to protocol.h to avoid dependency conflicts */
|
||||
int rt_examine(rtable *t, net_addr *a, struct proto *p, const struct filter *filter);
|
||||
int rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filter);
|
||||
rte *rt_export_merged(struct channel *c, net *net, rte **rt_free, linpool *pool, int silent);
|
||||
void rt_refresh_begin(rtable *t, struct channel *c);
|
||||
void rt_refresh_end(rtable *t, struct channel *c);
|
||||
|
@ -366,10 +331,6 @@ void rte_free(rte *);
|
|||
rte *rte_do_cow(rte *);
|
||||
static inline rte * rte_cow(rte *r) { return (r->flags & REF_COW) ? rte_do_cow(r) : r; }
|
||||
rte *rte_cow_rta(rte *r, linpool *lp);
|
||||
void rte_init_tmp_attrs(struct rte *r, linpool *lp, uint max);
|
||||
void rte_make_tmp_attr(struct rte *r, uint id, uint type, uintptr_t val);
|
||||
void rte_make_tmp_attrs(struct rte **r, struct linpool *pool, struct rta **old_attrs);
|
||||
uintptr_t rte_store_tmp_attr(struct rte *r, uint id);
|
||||
void rt_dump(rtable *);
|
||||
void rt_dump_all(void);
|
||||
int rt_feed_channel(struct channel *c);
|
||||
|
@ -487,18 +448,17 @@ typedef struct rta {
|
|||
u32 uc; /* Use count */
|
||||
u32 hash_key; /* Hash over important fields */
|
||||
struct ea_list *eattrs; /* Extended Attribute chain */
|
||||
struct rte_src *src; /* Route source that created the route */
|
||||
struct hostentry *hostentry; /* Hostentry for recursive next-hops */
|
||||
ip_addr from; /* Advertising router */
|
||||
u32 igp_metric; /* IGP metric to next hop (for iBGP routes) */
|
||||
u8 source; /* Route source (RTS_...) */
|
||||
u8 scope; /* Route scope (SCOPE_... -- see ip.h) */
|
||||
u8 dest; /* Route destination type (RTD_...) */
|
||||
u8 aflags;
|
||||
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_DUMMY 0 /* Dummy route to be removed soon */
|
||||
#define RTS_STATIC 1 /* Normal static route */
|
||||
#define RTS_INHERIT 2 /* Route inherited from kernel */
|
||||
#define RTS_DEVICE 3 /* Device route */
|
||||
|
@ -516,11 +476,6 @@ typedef struct rta {
|
|||
#define RTS_PERF 15 /* Perf checker */
|
||||
#define RTS_MAX 16
|
||||
|
||||
#define RTC_UNICAST 0
|
||||
#define RTC_BROADCAST 1
|
||||
#define RTC_MULTICAST 2
|
||||
#define RTC_ANYCAST 3 /* IPv6 Anycast */
|
||||
|
||||
#define RTD_NONE 0 /* Undefined next hop */
|
||||
#define RTD_UNICAST 1 /* Next hop is neighbor router */
|
||||
#define RTD_BLACKHOLE 2 /* Silently drop packets */
|
||||
|
@ -528,8 +483,6 @@ typedef struct rta {
|
|||
#define RTD_PROHIBIT 4 /* Administratively prohibited */
|
||||
#define RTD_MAX 5
|
||||
|
||||
#define RTAF_CACHED 1 /* This is a cached rta */
|
||||
|
||||
#define IGP_METRIC_UNKNOWN 0x80000000 /* Default igp_metric used when no other
|
||||
protocol-specific metric is availabe */
|
||||
|
||||
|
@ -551,9 +504,12 @@ static inline int rte_is_reachable(rte *r)
|
|||
typedef struct eattr {
|
||||
word id; /* EA_CODE(PROTOCOL_..., protocol-dependent ID) */
|
||||
byte flags; /* Protocol-dependent flags */
|
||||
byte type; /* Attribute type and several flags (EAF_...) */
|
||||
byte type:5; /* Attribute type */
|
||||
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 */
|
||||
union {
|
||||
u32 data;
|
||||
uintptr_t data;
|
||||
const struct adata *ptr; /* Attribute data elsewhere */
|
||||
} u;
|
||||
} eattr;
|
||||
|
@ -562,7 +518,6 @@ typedef struct eattr {
|
|||
#define EA_CODE(proto,id) (((proto) << 8) | (id))
|
||||
#define EA_ID(ea) ((ea) & 0xff)
|
||||
#define EA_PROTO(ea) ((ea) >> 8)
|
||||
#define EA_ID_FLAG(ea) (1 << EA_ID(ea))
|
||||
#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)
|
||||
|
@ -587,11 +542,9 @@ const char *ea_custom_name(uint ea);
|
|||
#define EAF_TYPE_INT_SET 0x0a /* Set of u32's (e.g., a community list) */
|
||||
#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_TYPE_IFACE 0x16 /* Interface pointer stored in adata */
|
||||
#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 */
|
||||
|
@ -623,7 +576,6 @@ typedef struct 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 */
|
||||
#define EALF_TEMP 8 /* Temporary ea_list added by make_tmp_attrs hooks */
|
||||
|
||||
struct rte_src *rt_find_source(struct proto *p, u32 id);
|
||||
struct rte_src *rt_get_source(struct proto *p, u32 id);
|
||||
|
@ -639,7 +591,7 @@ struct ea_walk_state {
|
|||
|
||||
eattr *ea_find(ea_list *, unsigned ea);
|
||||
eattr *ea_walk(struct ea_walk_state *s, uint id, uint max);
|
||||
int ea_get_int(ea_list *, unsigned ea, int def);
|
||||
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 */
|
||||
|
@ -660,27 +612,50 @@ void ea_format_bitfield(const struct eattr *a, byte *buf, int bufsize, const cha
|
|||
ea = NULL; \
|
||||
} while(0) \
|
||||
|
||||
struct ea_one_attr_list {
|
||||
ea_list l;
|
||||
eattr a;
|
||||
};
|
||||
|
||||
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];
|
||||
struct ea_one_attr_list *ea = lp_alloc(pool, sizeof(*ea));
|
||||
*ea = (struct ea_one_attr_list) {
|
||||
.l.flags = EALF_SORTED,
|
||||
.l.count = 1,
|
||||
.l.next = *to,
|
||||
|
||||
a->flags = EALF_SORTED;
|
||||
a->count = 1;
|
||||
a->next = *to;
|
||||
*to = a;
|
||||
|
||||
e->id = id;
|
||||
e->type = type;
|
||||
e->flags = flags;
|
||||
.a.id = id,
|
||||
.a.type = type,
|
||||
.a.flags = flags,
|
||||
};
|
||||
|
||||
if (type & EAF_EMBEDDED)
|
||||
e->u.data = (u32) val;
|
||||
ea->a.u.data = val;
|
||||
else
|
||||
e->u.ptr = (struct adata *) val;
|
||||
ea->a.u.ptr = (struct adata *) val;
|
||||
|
||||
return e;
|
||||
*to = &ea->l;
|
||||
|
||||
return &ea->a;
|
||||
}
|
||||
|
||||
static inline void
|
||||
ea_unset_attr(ea_list **to, struct linpool *pool, _Bool local, uint code)
|
||||
{
|
||||
struct ea_one_attr_list *ea = lp_alloc(pool, sizeof(*ea));
|
||||
*ea = (struct ea_one_attr_list) {
|
||||
.l.flags = EALF_SORTED,
|
||||
.l.count = 1,
|
||||
.l.next = *to,
|
||||
.a.id = code,
|
||||
.a.fresh = local,
|
||||
.a.originated = local,
|
||||
.a.undef = 1,
|
||||
};
|
||||
|
||||
*to = &ea->l;
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
@ -718,7 +693,7 @@ 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->aflags & RTAF_CACHED; }
|
||||
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); }
|
||||
|
|
|
@ -61,7 +61,6 @@
|
|||
const adata null_adata; /* adata of length 0 */
|
||||
|
||||
const char * const rta_src_names[RTS_MAX] = {
|
||||
[RTS_DUMMY] = "",
|
||||
[RTS_STATIC] = "static",
|
||||
[RTS_INHERIT] = "inherit",
|
||||
[RTS_DEVICE] = "device",
|
||||
|
@ -155,7 +154,7 @@ rt_prune_sources(void)
|
|||
{
|
||||
HASH_DO_REMOVE(src_hash, RSH, sp);
|
||||
idm_free(&src_ids, src->global_id);
|
||||
sl_free(rte_src_slab, src);
|
||||
sl_free(src);
|
||||
}
|
||||
}
|
||||
HASH_WALK_FILTER_END;
|
||||
|
@ -392,7 +391,7 @@ nexthop_free(struct nexthop *o)
|
|||
while (o)
|
||||
{
|
||||
n = o->next;
|
||||
sl_free(nexthop_slab(o), o);
|
||||
sl_free(o);
|
||||
o = n;
|
||||
}
|
||||
}
|
||||
|
@ -449,8 +448,7 @@ ea_find(ea_list *e, unsigned id)
|
|||
{
|
||||
eattr *a = ea__find(e, id & EA_CODE_MASK);
|
||||
|
||||
if (a && (a->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF &&
|
||||
!(id & EA_ALLOW_UNDEF))
|
||||
if (a && a->undef && !(id & EA_ALLOW_UNDEF))
|
||||
return NULL;
|
||||
return a;
|
||||
}
|
||||
|
@ -517,7 +515,7 @@ ea_walk(struct ea_walk_state *s, uint id, uint max)
|
|||
|
||||
BIT32_SET(s->visited, n);
|
||||
|
||||
if ((a->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF)
|
||||
if (a->undef)
|
||||
continue;
|
||||
|
||||
s->eattrs = e;
|
||||
|
@ -541,8 +539,8 @@ ea_walk(struct ea_walk_state *s, uint id, uint max)
|
|||
* by calling ea_find() to find the attribute, extracting its value or returning
|
||||
* a provided default if no such attribute is present.
|
||||
*/
|
||||
int
|
||||
ea_get_int(ea_list *e, unsigned id, int def)
|
||||
uintptr_t
|
||||
ea_get_int(ea_list *e, unsigned id, uintptr_t def)
|
||||
{
|
||||
eattr *a = ea_find(e, id);
|
||||
if (!a)
|
||||
|
@ -617,14 +615,17 @@ ea_do_prune(ea_list *e)
|
|||
|
||||
/* Now s0 is the most recent version, s[-1] the oldest one */
|
||||
/* Drop undefs */
|
||||
if ((s0->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF)
|
||||
if (s0->undef)
|
||||
continue;
|
||||
|
||||
/* Copy the newest version to destination */
|
||||
*d = *s0;
|
||||
|
||||
/* Preserve info whether it originated locally */
|
||||
d->type = (d->type & ~(EAF_ORIGINATED|EAF_FRESH)) | (s[-1].type & EAF_ORIGINATED);
|
||||
d->originated = s[-1].originated;
|
||||
|
||||
/* Not fresh any more, we prefer surstroemming */
|
||||
d->fresh = 0;
|
||||
|
||||
/* Next destination */
|
||||
d++;
|
||||
|
@ -738,6 +739,9 @@ ea_same(ea_list *x, ea_list *y)
|
|||
if (a->id != b->id ||
|
||||
a->flags != b->flags ||
|
||||
a->type != b->type ||
|
||||
a->originated != b->originated ||
|
||||
a->fresh != b->fresh ||
|
||||
a->undef != b->undef ||
|
||||
((a->type & EAF_EMBEDDED) ? a->u.data != b->u.data : !adata_same(a->u.ptr, b->u.ptr)))
|
||||
return 0;
|
||||
}
|
||||
|
@ -940,6 +944,10 @@ ea_show(struct cli *c, const eattr *e)
|
|||
{
|
||||
*pos++ = ':';
|
||||
*pos++ = ' ';
|
||||
|
||||
if (e->undef)
|
||||
bsprintf(pos, "undefined");
|
||||
else
|
||||
switch (e->type & EAF_TYPE_MASK)
|
||||
{
|
||||
case EAF_TYPE_INT:
|
||||
|
@ -969,7 +977,6 @@ ea_show(struct cli *c, const eattr *e)
|
|||
case EAF_TYPE_LC_SET:
|
||||
ea_show_lc_set(c, ad, pos, buf, end);
|
||||
return;
|
||||
case EAF_TYPE_UNDEF:
|
||||
default:
|
||||
bsprintf(pos, "<type %02x>", e->type);
|
||||
}
|
||||
|
@ -1005,7 +1012,7 @@ ea_dump(ea_list *e)
|
|||
eattr *a = &e->attrs[i];
|
||||
debug(" %02x:%02x.%02x", EA_PROTO(a->id), EA_ID(a->id), a->flags);
|
||||
debug("=%c", "?iO?I?P???S?????" [a->type & EAF_TYPE_MASK]);
|
||||
if (a->type & EAF_ORIGINATED)
|
||||
if (a->originated)
|
||||
debug("o");
|
||||
if (a->type & EAF_EMBEDDED)
|
||||
debug(":%08x", a->u.data);
|
||||
|
@ -1104,13 +1111,14 @@ rta_hash(rta *a)
|
|||
u64 h;
|
||||
mem_hash_init(&h);
|
||||
#define MIX(f) mem_hash_mix(&h, &(a->f), sizeof(a->f));
|
||||
MIX(src);
|
||||
#define BMIX(f) mem_hash_mix_num(&h, a->f);
|
||||
MIX(hostentry);
|
||||
MIX(from);
|
||||
MIX(igp_metric);
|
||||
MIX(source);
|
||||
MIX(scope);
|
||||
MIX(dest);
|
||||
BMIX(source);
|
||||
BMIX(scope);
|
||||
BMIX(dest);
|
||||
MIX(pref);
|
||||
#undef MIX
|
||||
|
||||
return mem_hash_value(&h) ^ nexthop_hash(&(a->nh)) ^ ea_hash(a->eattrs);
|
||||
|
@ -1119,8 +1127,7 @@ rta_hash(rta *a)
|
|||
static inline int
|
||||
rta_same(rta *x, rta *y)
|
||||
{
|
||||
return (x->src == y->src &&
|
||||
x->source == y->source &&
|
||||
return (x->source == y->source &&
|
||||
x->scope == y->scope &&
|
||||
x->dest == y->dest &&
|
||||
x->igp_metric == y->igp_metric &&
|
||||
|
@ -1198,7 +1205,7 @@ rta_lookup(rta *o)
|
|||
rta *r;
|
||||
uint h;
|
||||
|
||||
ASSERT(!(o->aflags & RTAF_CACHED));
|
||||
ASSERT(!o->cached);
|
||||
if (o->eattrs)
|
||||
ea_normalize(o->eattrs);
|
||||
|
||||
|
@ -1209,8 +1216,7 @@ rta_lookup(rta *o)
|
|||
|
||||
r = rta_copy(o);
|
||||
r->hash_key = h;
|
||||
r->aflags = RTAF_CACHED;
|
||||
rt_lock_source(r->src);
|
||||
r->cached = 1;
|
||||
rt_lock_hostentry(r->hostentry);
|
||||
rta_insert(r);
|
||||
|
||||
|
@ -1223,18 +1229,17 @@ rta_lookup(rta *o)
|
|||
void
|
||||
rta__free(rta *a)
|
||||
{
|
||||
ASSERT(rta_cache_count && (a->aflags & RTAF_CACHED));
|
||||
ASSERT(rta_cache_count && a->cached);
|
||||
rta_cache_count--;
|
||||
*a->pprev = a->next;
|
||||
if (a->next)
|
||||
a->next->pprev = a->pprev;
|
||||
rt_unlock_hostentry(a->hostentry);
|
||||
rt_unlock_source(a->src);
|
||||
if (a->nh.next)
|
||||
nexthop_free(a->nh.next);
|
||||
ea_free(a->eattrs);
|
||||
a->aflags = 0; /* Poison the entry */
|
||||
sl_free(rta_slab(a), a);
|
||||
a->cached = 0;
|
||||
sl_free(a);
|
||||
}
|
||||
|
||||
rta *
|
||||
|
@ -1248,7 +1253,7 @@ rta_do_cow(rta *o, linpool *lp)
|
|||
memcpy(*nhn, nho, nexthop_size(nho));
|
||||
nhn = &((*nhn)->next);
|
||||
}
|
||||
r->aflags = 0;
|
||||
r->cached = 0;
|
||||
r->uc = 0;
|
||||
return r;
|
||||
}
|
||||
|
@ -1262,16 +1267,16 @@ rta_do_cow(rta *o, linpool *lp)
|
|||
void
|
||||
rta_dump(rta *a)
|
||||
{
|
||||
static char *rts[] = { "RTS_DUMMY", "RTS_STATIC", "RTS_INHERIT", "RTS_DEVICE",
|
||||
static char *rts[] = { "", "RTS_STATIC", "RTS_INHERIT", "RTS_DEVICE",
|
||||
"RTS_STAT_DEV", "RTS_REDIR", "RTS_RIP",
|
||||
"RTS_OSPF", "RTS_OSPF_IA", "RTS_OSPF_EXT1",
|
||||
"RTS_OSPF_EXT2", "RTS_BGP", "RTS_PIPE", "RTS_BABEL" };
|
||||
static char *rtd[] = { "", " DEV", " HOLE", " UNREACH", " PROHIBIT" };
|
||||
|
||||
debug("p=%s uc=%d %s %s%s h=%04x",
|
||||
a->src->proto->name, a->uc, rts[a->source], ip_scope_text(a->scope),
|
||||
debug("pref=%d uc=%d %s %s%s h=%04x",
|
||||
a->pref, a->uc, rts[a->source], ip_scope_text(a->scope),
|
||||
rtd[a->dest], a->hash_key);
|
||||
if (!(a->aflags & RTAF_CACHED))
|
||||
if (!a->cached)
|
||||
debug(" !CACHED");
|
||||
debug(" <-%I", a->from);
|
||||
if (a->dest == RTD_UNICAST)
|
||||
|
|
|
@ -83,7 +83,7 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
|
|||
struct rte_src *src = rt_get_source(P, ad->iface->index);
|
||||
|
||||
rta a0 = {
|
||||
.src = src,
|
||||
.pref = c->preference,
|
||||
.source = RTS_DEVICE,
|
||||
.scope = SCOPE_UNIVERSE,
|
||||
.dest = RTD_UNICAST,
|
||||
|
@ -91,7 +91,7 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
|
|||
};
|
||||
|
||||
a = rta_lookup(&a0);
|
||||
e = rte_get_temp(a);
|
||||
e = rte_get_temp(a, src);
|
||||
e->pflags = 0;
|
||||
rte_update2(c, net, e, src);
|
||||
}
|
||||
|
@ -195,3 +195,9 @@ struct protocol proto_device = {
|
|||
.reconfigure = dev_reconfigure,
|
||||
.copy_config = dev_copy_config
|
||||
};
|
||||
|
||||
void
|
||||
dev_build(void)
|
||||
{
|
||||
proto_build(&proto_device);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -57,17 +57,17 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary
|
|||
if (d->verbose && !rta_is_cached(a) && a->eattrs)
|
||||
ea_normalize(a->eattrs);
|
||||
|
||||
get_route_info = a->src->proto->proto->get_route_info;
|
||||
get_route_info = e->src->proto->proto->get_route_info;
|
||||
if (get_route_info)
|
||||
get_route_info(e, info);
|
||||
else
|
||||
bsprintf(info, " (%d)", e->pref);
|
||||
bsprintf(info, " (%d)", a->pref);
|
||||
|
||||
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),
|
||||
a->src->proto->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info);
|
||||
e->src->proto->name, tm, from, primary ? (sync_error ? " !" : " *") : "", info);
|
||||
|
||||
if (a->dest == RTD_UNICAST)
|
||||
for (nh = &(a->nh); nh; nh = nh->next)
|
||||
|
@ -127,7 +127,6 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
|
|||
continue;
|
||||
|
||||
ee = e;
|
||||
rte_make_tmp_attrs(&e, c->show_pool, NULL);
|
||||
|
||||
/* Export channel is down, do not try to export routes to it */
|
||||
if (ec && (ec->export_state == ES_DOWN))
|
||||
|
@ -154,7 +153,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
|
|||
else if (d->export_mode)
|
||||
{
|
||||
struct proto *ep = ec->proto;
|
||||
int ic = ep->preexport ? ep->preexport(ep, &e, c->show_pool) : 0;
|
||||
int ic = ep->preexport ? ep->preexport(ec, e) : 0;
|
||||
|
||||
if (ec->ra_mode == RA_OPTIMAL || ec->ra_mode == RA_MERGED)
|
||||
pass = 1;
|
||||
|
@ -180,7 +179,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
|
|||
}
|
||||
}
|
||||
|
||||
if (d->show_protocol && (d->show_protocol != e->attrs->src->proto))
|
||||
if (d->show_protocol && (d->show_protocol != e->src->proto))
|
||||
goto skip;
|
||||
|
||||
if (f_run(d->filter, &e, c->show_pool, 0) > F_ACCEPT)
|
||||
|
@ -393,7 +392,7 @@ rt_show_get_default_tables(struct rt_show_data *d)
|
|||
}
|
||||
|
||||
for (int i=1; i<NET_MAX; i++)
|
||||
if (config->def_tables[i])
|
||||
if (config->def_tables[i] && config->def_tables[i]->table)
|
||||
rt_show_add_table(d, config->def_tables[i]->table);
|
||||
}
|
||||
|
||||
|
|
428
nest/rt-table.c
428
nest/rt-table.c
|
@ -124,6 +124,7 @@ static void rt_next_hop_update(rtable *tab);
|
|||
static inline void rt_prune_table(rtable *tab);
|
||||
static inline void rt_schedule_notify(rtable *tab);
|
||||
static void rt_flowspec_notify(rtable *tab, net *net);
|
||||
static void rt_kick_prune_timer(rtable *tab);
|
||||
|
||||
|
||||
static void
|
||||
|
@ -548,7 +549,7 @@ rte_find(net *net, struct rte_src *src)
|
|||
{
|
||||
rte *e = net->routes;
|
||||
|
||||
while (e && e->attrs->src != src)
|
||||
while (e && e->src != src)
|
||||
e = e->next;
|
||||
return e;
|
||||
}
|
||||
|
@ -563,14 +564,14 @@ rte_find(net *net, struct rte_src *src)
|
|||
* the protocol.
|
||||
*/
|
||||
rte *
|
||||
rte_get_temp(rta *a)
|
||||
rte_get_temp(rta *a, struct rte_src *src)
|
||||
{
|
||||
rte *e = sl_alloc(rte_slab);
|
||||
|
||||
e->attrs = a;
|
||||
e->id = 0;
|
||||
e->flags = 0;
|
||||
e->pref = 0;
|
||||
rt_lock_source(e->src = src);
|
||||
return e;
|
||||
}
|
||||
|
||||
|
@ -580,6 +581,8 @@ rte_do_cow(rte *r)
|
|||
rte *e = sl_alloc(rte_slab);
|
||||
|
||||
memcpy(e, r, sizeof(rte));
|
||||
|
||||
rt_lock_source(e->src);
|
||||
e->attrs = rta_clone(r->attrs);
|
||||
e->flags = 0;
|
||||
return e;
|
||||
|
@ -617,176 +620,6 @@ rte_cow_rta(rte *r, linpool *lp)
|
|||
return r;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* rte_init_tmp_attrs - initialize temporary ea_list for route
|
||||
* @r: route entry to be modified
|
||||
* @lp: linpool from which to allocate attributes
|
||||
* @max: maximum number of added temporary attribus
|
||||
*
|
||||
* This function is supposed to be called from make_tmp_attrs() and
|
||||
* store_tmp_attrs() hooks before rte_make_tmp_attr() / rte_store_tmp_attr()
|
||||
* functions. It allocates &ea_list with length for @max items for temporary
|
||||
* attributes and puts it on top of eattrs stack.
|
||||
*/
|
||||
void
|
||||
rte_init_tmp_attrs(rte *r, linpool *lp, uint max)
|
||||
{
|
||||
struct ea_list *e = lp_alloc(lp, sizeof(struct ea_list) + max * sizeof(eattr));
|
||||
|
||||
e->next = r->attrs->eattrs;
|
||||
e->flags = EALF_SORTED | EALF_TEMP;
|
||||
e->count = 0;
|
||||
|
||||
r->attrs->eattrs = e;
|
||||
}
|
||||
|
||||
/**
|
||||
* rte_make_tmp_attr - make temporary eattr from private route fields
|
||||
* @r: route entry to be modified
|
||||
* @id: attribute ID
|
||||
* @type: attribute type
|
||||
* @val: attribute value (u32 or adata ptr)
|
||||
*
|
||||
* This function is supposed to be called from make_tmp_attrs() hook for
|
||||
* each temporary attribute, after temporary &ea_list was initialized by
|
||||
* rte_init_tmp_attrs(). It checks whether temporary attribute is supposed to
|
||||
* be defined (based on route pflags) and if so then it fills &eattr field in
|
||||
* preallocated temporary &ea_list on top of route @r eattrs stack.
|
||||
*
|
||||
* Note that it may require free &eattr in temporary &ea_list, so it must not be
|
||||
* called more times than @max argument of rte_init_tmp_attrs().
|
||||
*/
|
||||
void
|
||||
rte_make_tmp_attr(rte *r, uint id, uint type, uintptr_t val)
|
||||
{
|
||||
if (r->pflags & EA_ID_FLAG(id))
|
||||
{
|
||||
ea_list *e = r->attrs->eattrs;
|
||||
eattr *a = &e->attrs[e->count++];
|
||||
a->id = id;
|
||||
a->type = type;
|
||||
a->flags = 0;
|
||||
|
||||
if (type & EAF_EMBEDDED)
|
||||
a->u.data = (u32) val;
|
||||
else
|
||||
a->u.ptr = (struct adata *) val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* rte_store_tmp_attr - store temporary eattr to private route fields
|
||||
* @r: route entry to be modified
|
||||
* @id: attribute ID
|
||||
*
|
||||
* This function is supposed to be called from store_tmp_attrs() hook for
|
||||
* each temporary attribute, after temporary &ea_list was initialized by
|
||||
* rte_init_tmp_attrs(). It checks whether temporary attribute is defined in
|
||||
* route @r eattrs stack, updates route pflags accordingly, undefines it by
|
||||
* filling &eattr field in preallocated temporary &ea_list on top of the eattrs
|
||||
* stack, and returns the value. Caller is supposed to store it in the
|
||||
* appropriate private field.
|
||||
*
|
||||
* Note that it may require free &eattr in temporary &ea_list, so it must not be
|
||||
* called more times than @max argument of rte_init_tmp_attrs()
|
||||
*/
|
||||
uintptr_t
|
||||
rte_store_tmp_attr(rte *r, uint id)
|
||||
{
|
||||
ea_list *e = r->attrs->eattrs;
|
||||
eattr *a = ea_find(e->next, id);
|
||||
|
||||
if (a)
|
||||
{
|
||||
e->attrs[e->count++] = (struct eattr) { .id = id, .type = EAF_TYPE_UNDEF };
|
||||
r->pflags |= EA_ID_FLAG(id);
|
||||
return (a->type & EAF_EMBEDDED) ? a->u.data : (uintptr_t) a->u.ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
r->pflags &= ~EA_ID_FLAG(id);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* rte_make_tmp_attrs - prepare route by adding all relevant temporary route attributes
|
||||
* @r: route entry to be modified (may be replaced if COW)
|
||||
* @lp: linpool from which to allocate attributes
|
||||
* @old_attrs: temporary ref to old &rta (may be NULL)
|
||||
*
|
||||
* This function expands privately stored protocol-dependent route attributes
|
||||
* to a uniform &eattr / &ea_list representation. It is essentially a wrapper
|
||||
* around protocol make_tmp_attrs() hook, which does some additional work like
|
||||
* ensuring that route @r is writable.
|
||||
*
|
||||
* The route @r may be read-only (with %REF_COW flag), in that case rw copy is
|
||||
* obtained by rte_cow() and @r is replaced. If @rte is originally rw, it may be
|
||||
* directly modified (and it is never copied).
|
||||
*
|
||||
* If the @old_attrs ptr is supplied, the function obtains another reference of
|
||||
* old cached &rta, that is necessary in some cases (see rte_cow_rta() for
|
||||
* details). It is freed by rte_store_tmp_attrs(), or manually by rta_free().
|
||||
*
|
||||
* Generally, if caller ensures that @r is read-only (e.g. in route export) then
|
||||
* it may ignore @old_attrs (and set it to NULL), but must handle replacement of
|
||||
* @r. If caller ensures that @r is writable (e.g. in route import) then it may
|
||||
* ignore replacement of @r, but it must handle @old_attrs.
|
||||
*/
|
||||
void
|
||||
rte_make_tmp_attrs(rte **r, linpool *lp, rta **old_attrs)
|
||||
{
|
||||
void (*make_tmp_attrs)(rte *r, linpool *lp);
|
||||
make_tmp_attrs = (*r)->attrs->src->proto->make_tmp_attrs;
|
||||
|
||||
if (!make_tmp_attrs)
|
||||
return;
|
||||
|
||||
/* We may need to keep ref to old attributes, will be freed in rte_store_tmp_attrs() */
|
||||
if (old_attrs)
|
||||
*old_attrs = rta_is_cached((*r)->attrs) ? rta_clone((*r)->attrs) : NULL;
|
||||
|
||||
*r = rte_cow_rta(*r, lp);
|
||||
make_tmp_attrs(*r, lp);
|
||||
}
|
||||
|
||||
/**
|
||||
* rte_store_tmp_attrs - store temporary route attributes back to private route fields
|
||||
* @r: route entry to be modified
|
||||
* @lp: linpool from which to allocate attributes
|
||||
* @old_attrs: temporary ref to old &rta
|
||||
*
|
||||
* This function stores temporary route attributes that were expanded by
|
||||
* rte_make_tmp_attrs() back to private route fields and also undefines them.
|
||||
* It is essentially a wrapper around protocol store_tmp_attrs() hook, which
|
||||
* does some additional work like shortcut if there is no change and cleanup
|
||||
* of @old_attrs reference obtained by rte_make_tmp_attrs().
|
||||
*/
|
||||
static void
|
||||
rte_store_tmp_attrs(rte *r, linpool *lp, rta *old_attrs)
|
||||
{
|
||||
void (*store_tmp_attrs)(rte *rt, linpool *lp);
|
||||
store_tmp_attrs = r->attrs->src->proto->store_tmp_attrs;
|
||||
|
||||
if (!store_tmp_attrs)
|
||||
return;
|
||||
|
||||
ASSERT(!rta_is_cached(r->attrs));
|
||||
|
||||
/* If there is no new ea_list, we just skip the temporary ea_list */
|
||||
ea_list *ea = r->attrs->eattrs;
|
||||
if (ea && (ea->flags & EALF_TEMP))
|
||||
r->attrs->eattrs = ea->next;
|
||||
else
|
||||
store_tmp_attrs(r, lp);
|
||||
|
||||
/* Free ref we got in rte_make_tmp_attrs(), have to do rta_lookup() first */
|
||||
r->attrs = rta_lookup(r->attrs);
|
||||
rta_free(old_attrs);
|
||||
}
|
||||
|
||||
|
||||
static int /* Actually better or at least as good as */
|
||||
rte_better(rte *new, rte *old)
|
||||
{
|
||||
|
@ -797,20 +630,20 @@ rte_better(rte *new, rte *old)
|
|||
if (!rte_is_valid(new))
|
||||
return 0;
|
||||
|
||||
if (new->pref > old->pref)
|
||||
if (new->attrs->pref > old->attrs->pref)
|
||||
return 1;
|
||||
if (new->pref < old->pref)
|
||||
if (new->attrs->pref < old->attrs->pref)
|
||||
return 0;
|
||||
if (new->attrs->src->proto->proto != old->attrs->src->proto->proto)
|
||||
if (new->src->proto->proto != old->src->proto->proto)
|
||||
{
|
||||
/*
|
||||
* If the user has configured protocol preferences, so that two different protocols
|
||||
* have the same preference, try to break the tie by comparing addresses. Not too
|
||||
* useful, but keeps the ordering of routes unambiguous.
|
||||
*/
|
||||
return new->attrs->src->proto->proto > old->attrs->src->proto->proto;
|
||||
return new->src->proto->proto > old->src->proto->proto;
|
||||
}
|
||||
if (better = new->attrs->src->proto->rte_better)
|
||||
if (better = new->src->proto->rte_better)
|
||||
return better(new, old);
|
||||
return 0;
|
||||
}
|
||||
|
@ -823,13 +656,13 @@ rte_mergable(rte *pri, rte *sec)
|
|||
if (!rte_is_valid(pri) || !rte_is_valid(sec))
|
||||
return 0;
|
||||
|
||||
if (pri->pref != sec->pref)
|
||||
if (pri->attrs->pref != sec->attrs->pref)
|
||||
return 0;
|
||||
|
||||
if (pri->attrs->src->proto->proto != sec->attrs->src->proto->proto)
|
||||
if (pri->src->proto->proto != sec->src->proto->proto)
|
||||
return 0;
|
||||
|
||||
if (mergable = pri->attrs->src->proto->rte_mergable)
|
||||
if (mergable = pri->src->proto->rte_mergable)
|
||||
return mergable(pri, sec);
|
||||
|
||||
return 0;
|
||||
|
@ -838,8 +671,8 @@ rte_mergable(rte *pri, rte *sec)
|
|||
static void
|
||||
rte_trace(struct channel *c, rte *e, int dir, char *msg)
|
||||
{
|
||||
log(L_TRACE "%s.%s %c %s %N %s",
|
||||
c->proto->name, c->name ?: "?", dir, msg, e->net->n.addr,
|
||||
log(L_TRACE "%s.%s %c %s %N %uL %uG %s",
|
||||
c->proto->name, c->name ?: "?", dir, msg, e->net->n.addr, e->src->private_id, e->src->global_id,
|
||||
rta_dest_name(e->attrs->dest));
|
||||
}
|
||||
|
||||
|
@ -869,7 +702,7 @@ export_filter_(struct channel *c, rte *rt0, rte **rt_free, linpool *pool, int si
|
|||
rt = rt0;
|
||||
*rt_free = NULL;
|
||||
|
||||
v = p->preexport ? p->preexport(p, &rt, pool) : 0;
|
||||
v = p->preexport ? p->preexport(c, rt) : 0;
|
||||
if (v < 0)
|
||||
{
|
||||
if (silent)
|
||||
|
@ -887,8 +720,6 @@ export_filter_(struct channel *c, rte *rt0, rte **rt_free, linpool *pool, int si
|
|||
goto accept;
|
||||
}
|
||||
|
||||
rte_make_tmp_attrs(&rt, pool, NULL);
|
||||
|
||||
v = filter && ((filter == FILTER_REJECT) ||
|
||||
(f_run(filter, &rt, pool,
|
||||
(silent ? FF_SILENT : 0)) > F_ACCEPT));
|
||||
|
@ -902,12 +733,6 @@ export_filter_(struct channel *c, rte *rt0, rte **rt_free, linpool *pool, int si
|
|||
goto reject;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PIPE
|
||||
/* Pipes need rte with stored tmpattrs, remaining protocols need expanded tmpattrs */
|
||||
if (p->proto == &proto_pipe)
|
||||
rte_store_tmp_attrs(rt, pool, NULL);
|
||||
#endif
|
||||
|
||||
accept:
|
||||
if (rt != rt0)
|
||||
*rt_free = rt;
|
||||
|
@ -1287,6 +1112,19 @@ rte_announce(rtable *tab, uint type, net *net, rte *new, rte *old,
|
|||
break;
|
||||
|
||||
case RA_ACCEPTED:
|
||||
/*
|
||||
* The (new != old) condition is problematic here, as it would break
|
||||
* the second usage pattern (announcement after bulk change, used in
|
||||
* rt_next_hop_update_net(), which sends both new and old as NULL).
|
||||
*
|
||||
* But recursive next hops do not work with sorted tables anyways,
|
||||
* such configuration is forbidden in BGP and not supported in
|
||||
* rt_notify_accepted().
|
||||
*
|
||||
* The condition is needed to eliminate spurious announcements where
|
||||
* both old and new routes are not valid (so they are NULL).
|
||||
*/
|
||||
if (new != old)
|
||||
rt_notify_accepted(c, net, new, old, 0);
|
||||
break;
|
||||
|
||||
|
@ -1350,16 +1188,18 @@ rte_validate(rte *e)
|
|||
void
|
||||
rte_free(rte *e)
|
||||
{
|
||||
rt_unlock_source(e->src);
|
||||
if (rta_is_cached(e->attrs))
|
||||
rta_free(e->attrs);
|
||||
sl_free(rte_slab, e);
|
||||
sl_free(e);
|
||||
}
|
||||
|
||||
static inline void
|
||||
rte_free_quick(rte *e)
|
||||
{
|
||||
rt_unlock_source(e->src);
|
||||
rta_free(e->attrs);
|
||||
sl_free(rte_slab, e);
|
||||
sl_free(e);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -1369,8 +1209,7 @@ rte_same(rte *x, rte *y)
|
|||
return
|
||||
x->attrs == y->attrs &&
|
||||
x->pflags == y->pflags &&
|
||||
x->pref == y->pref &&
|
||||
(!x->attrs->src->proto->rte_same || x->attrs->src->proto->rte_same(x, y)) &&
|
||||
x->src == y->src &&
|
||||
rte_is_filtered(x) == rte_is_filtered(y);
|
||||
}
|
||||
|
||||
|
@ -1391,7 +1230,7 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
|
|||
k = &net->routes; /* Find and remove original route from the same protocol */
|
||||
while (old = *k)
|
||||
{
|
||||
if (old->attrs->src == src)
|
||||
if (old->src == src)
|
||||
{
|
||||
/* If there is the same route in the routing table but from
|
||||
* a different sender, then there are two paths from the
|
||||
|
@ -1641,9 +1480,8 @@ rte_recalculate(struct channel *c, net *net, rte *new, struct rte_src *src)
|
|||
rte_announce(table, RA_UNDEF, net, new, old, net->routes, old_best);
|
||||
|
||||
if (!net->routes &&
|
||||
(table->gc_counter++ >= table->config->gc_max_ops) &&
|
||||
(table->gc_time + table->config->gc_min_time <= current_time()))
|
||||
rt_schedule_prune(table);
|
||||
(table->gc_counter++ >= table->config->gc_threshold))
|
||||
rt_kick_prune_timer(table);
|
||||
|
||||
if (old_ok && p->rte_remove)
|
||||
p->rte_remove(net, old);
|
||||
|
@ -1674,26 +1512,6 @@ rte_update_unlock(void)
|
|||
lp_flush(rte_update_pool);
|
||||
}
|
||||
|
||||
static inline void
|
||||
rte_hide_dummy_routes(net *net, rte **dummy)
|
||||
{
|
||||
if (net->routes && net->routes->attrs->source == RTS_DUMMY)
|
||||
{
|
||||
*dummy = net->routes;
|
||||
net->routes = (*dummy)->next;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
rte_unhide_dummy_routes(net *net, rte **dummy)
|
||||
{
|
||||
if (*dummy)
|
||||
{
|
||||
(*dummy)->next = net->routes;
|
||||
net->routes = *dummy;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* rte_update - enter a new update to a routing table
|
||||
* @table: table to be updated
|
||||
|
@ -1742,7 +1560,6 @@ rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
|
|||
// struct proto *p = c->proto;
|
||||
struct proto_stats *stats = &c->stats;
|
||||
const struct filter *filter = c->in_filter;
|
||||
rte *dummy = NULL;
|
||||
net *nn;
|
||||
|
||||
ASSERT(c->channel_state == CS_UP);
|
||||
|
@ -1758,9 +1575,6 @@ rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
|
|||
new->net = nn;
|
||||
new->sender = c;
|
||||
|
||||
if (!new->pref)
|
||||
new->pref = c->preference;
|
||||
|
||||
stats->imp_updates_received++;
|
||||
if (!rte_validate(new))
|
||||
{
|
||||
|
@ -1782,9 +1596,6 @@ rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
|
|||
}
|
||||
else if (filter)
|
||||
{
|
||||
rta *old_attrs = NULL;
|
||||
rte_make_tmp_attrs(&new, rte_update_pool, &old_attrs);
|
||||
|
||||
int fr = f_run(filter, &new, rte_update_pool, 0);
|
||||
if (fr > F_ACCEPT)
|
||||
{
|
||||
|
@ -1792,15 +1603,10 @@ rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
|
|||
rte_trace_in(D_FILTERS, c, new, "filtered out");
|
||||
|
||||
if (! c->in_keep_filtered)
|
||||
{
|
||||
rta_free(old_attrs);
|
||||
goto drop;
|
||||
}
|
||||
|
||||
new->flags |= REF_FILTERED;
|
||||
}
|
||||
|
||||
rte_store_tmp_attrs(new, rte_update_pool, old_attrs);
|
||||
}
|
||||
if (!rta_is_cached(new->attrs)) /* Need to copy attributes */
|
||||
new->attrs = rta_lookup(new->attrs);
|
||||
|
@ -1824,9 +1630,7 @@ rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
|
|||
|
||||
recalc:
|
||||
/* And recalculate the best route */
|
||||
rte_hide_dummy_routes(nn, &dummy);
|
||||
rte_recalculate(c, nn, new, src);
|
||||
rte_unhide_dummy_routes(nn, &dummy);
|
||||
|
||||
rte_update_unlock();
|
||||
return;
|
||||
|
@ -1855,7 +1659,7 @@ static inline void
|
|||
rte_discard(rte *old) /* Non-filtered route deletion, used during garbage collection */
|
||||
{
|
||||
rte_update_lock();
|
||||
rte_recalculate(old->sender, old->net, NULL, old->attrs->src);
|
||||
rte_recalculate(old->sender, old->net, NULL, old->src);
|
||||
rte_update_unlock();
|
||||
}
|
||||
|
||||
|
@ -1875,7 +1679,7 @@ rte_modify(rte *old)
|
|||
new->flags = (old->flags & ~REF_MODIFY) | REF_COW;
|
||||
}
|
||||
|
||||
rte_recalculate(old->sender, old->net, new, old->attrs->src);
|
||||
rte_recalculate(old->sender, old->net, new, old->src);
|
||||
}
|
||||
|
||||
rte_update_unlock();
|
||||
|
@ -1883,8 +1687,9 @@ rte_modify(rte *old)
|
|||
|
||||
/* Check rtable for best route to given net whether it would be exported do p */
|
||||
int
|
||||
rt_examine(rtable *t, net_addr *a, struct proto *p, const struct filter *filter)
|
||||
rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filter)
|
||||
{
|
||||
struct proto *p = c->proto;
|
||||
net *n = net_find(t, a);
|
||||
rte *rt = n ? n->routes : NULL;
|
||||
|
||||
|
@ -1894,12 +1699,9 @@ rt_examine(rtable *t, net_addr *a, struct proto *p, const struct filter *filter)
|
|||
rte_update_lock();
|
||||
|
||||
/* Rest is stripped down export_filter() */
|
||||
int v = p->preexport ? p->preexport(p, &rt, rte_update_pool) : 0;
|
||||
int v = p->preexport ? p->preexport(c, rt) : 0;
|
||||
if (v == RIC_PROCESS)
|
||||
{
|
||||
rte_make_tmp_attrs(&rt, rte_update_pool, NULL);
|
||||
v = (f_run(filter, &rt, rte_update_pool, FF_SILENT) <= F_ACCEPT);
|
||||
}
|
||||
|
||||
/* Discard temporary rte */
|
||||
if (rt != n->routes)
|
||||
|
@ -1999,10 +1801,8 @@ rte_dump(rte *e)
|
|||
{
|
||||
net *n = e->net;
|
||||
debug("%-1N ", n->n.addr);
|
||||
debug("PF=%02x pref=%d ", e->pflags, e->pref);
|
||||
debug("PF=%02x ", e->pflags);
|
||||
rta_dump(e->attrs);
|
||||
if (e->attrs->src->proto->proto->dump_attrs)
|
||||
e->attrs->src->proto->proto->dump_attrs(e);
|
||||
debug("\n");
|
||||
}
|
||||
|
||||
|
@ -2098,6 +1898,29 @@ rt_event(void *ptr)
|
|||
}
|
||||
|
||||
|
||||
static void
|
||||
rt_prune_timer(timer *t)
|
||||
{
|
||||
rtable *tab = t->data;
|
||||
|
||||
if (tab->gc_counter >= tab->config->gc_threshold)
|
||||
rt_schedule_prune(tab);
|
||||
}
|
||||
|
||||
static void
|
||||
rt_kick_prune_timer(rtable *tab)
|
||||
{
|
||||
/* Return if prune is already scheduled */
|
||||
if (tm_active(tab->prune_timer) || (tab->prune_state & 1))
|
||||
return;
|
||||
|
||||
/* Randomize GC period to +/- 50% */
|
||||
btime gc_period = tab->config->gc_period;
|
||||
gc_period = (gc_period / 2) + (random_u32() % (uint) gc_period);
|
||||
tm_start(tab->prune_timer, gc_period);
|
||||
}
|
||||
|
||||
|
||||
static inline btime
|
||||
rt_settled_time(rtable *tab)
|
||||
{
|
||||
|
@ -2299,12 +2122,7 @@ static struct resclass rt_class = {
|
|||
rtable *
|
||||
rt_setup(pool *pp, struct rtable_config *cf)
|
||||
{
|
||||
int ns = strlen("Routing table ") + strlen(cf->name) + 1;
|
||||
void *nb = mb_alloc(pp, ns);
|
||||
ASSERT_DIE(ns - 1 == bsnprintf(nb, ns, "Routing table %s", cf->name));
|
||||
|
||||
pool *p = rp_new(pp, nb);
|
||||
mb_move(nb, p);
|
||||
pool *p = rp_newf(pp, "Routing table %s", cf->name);
|
||||
|
||||
rtable *t = ralloc(p, &rt_class);
|
||||
t->rp = p;
|
||||
|
@ -2333,6 +2151,7 @@ rt_setup(pool *pp, struct rtable_config *cf)
|
|||
hmap_set(&t->id_map, 0);
|
||||
|
||||
t->rt_event = ev_new_init(p, rt_event, t);
|
||||
t->prune_timer = tm_new_init(p, rt_prune_timer, t, 0, 0);
|
||||
t->last_rt_change = t->gc_time = current_time();
|
||||
|
||||
if (rt_is_flow(t))
|
||||
|
@ -2380,7 +2199,7 @@ static void
|
|||
rt_prune_table(rtable *tab)
|
||||
{
|
||||
struct fib_iterator *fit = &tab->prune_fit;
|
||||
int limit = 512;
|
||||
int limit = 2000;
|
||||
|
||||
struct channel *c;
|
||||
node *n, *x;
|
||||
|
@ -2403,6 +2222,9 @@ rt_prune_table(rtable *tab)
|
|||
FIB_ITERATE_INIT(fit, &tab->fib);
|
||||
tab->prune_state = 2;
|
||||
|
||||
tab->gc_counter = 0;
|
||||
tab->gc_time = current_time();
|
||||
|
||||
if (tab->prune_trie)
|
||||
{
|
||||
/* Init prefix trie pruning */
|
||||
|
@ -2462,9 +2284,6 @@ again:
|
|||
fib_check(&tab->fib);
|
||||
#endif
|
||||
|
||||
tab->gc_counter = 0;
|
||||
tab->gc_time = current_time();
|
||||
|
||||
/* state change 2->0, 3->1 */
|
||||
tab->prune_state &= 1;
|
||||
|
||||
|
@ -2591,6 +2410,20 @@ rt_preconfig(struct config *c)
|
|||
rt_new_table(cf_get_symbol("master6"), NET_IP6);
|
||||
}
|
||||
|
||||
void
|
||||
rt_postconfig(struct config *c)
|
||||
{
|
||||
uint num_tables = list_length(&c->tables);
|
||||
btime def_gc_period = 400 MS * num_tables;
|
||||
def_gc_period = MAX(def_gc_period, 10 S);
|
||||
def_gc_period = MIN(def_gc_period, 600 S);
|
||||
|
||||
struct rtable_config *rc;
|
||||
WALK_LIST(rc, c->tables)
|
||||
if (rc->gc_period == (uint) -1)
|
||||
rc->gc_period = (uint) def_gc_period;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Some functions for handing internal next hop updates
|
||||
|
@ -2716,11 +2549,12 @@ rt_next_hop_update_rte(rtable *tab UNUSED, rte *old)
|
|||
memcpy(mls.stack, &a->nh.label[a->nh.labels - mls.len], mls.len * sizeof(u32));
|
||||
|
||||
rta_apply_hostentry(a, old->attrs->hostentry, &mls);
|
||||
a->aflags = 0;
|
||||
a->cached = 0;
|
||||
|
||||
rte *e = sl_alloc(rte_slab);
|
||||
memcpy(e, old, sizeof(rte));
|
||||
e->attrs = rta_lookup(a);
|
||||
rt_lock_source(e->src);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
@ -2844,12 +2678,16 @@ static rte *
|
|||
rt_flowspec_update_rte(rtable *tab, rte *r)
|
||||
{
|
||||
#ifdef CONFIG_BGP
|
||||
if ((r->attrs->source != RTS_BGP) || !r->u.bgp.base_table)
|
||||
if ((r->attrs->source != RTS_BGP) || (r->sender->proto != r->src->proto))
|
||||
return NULL;
|
||||
|
||||
struct bgp_channel *bc = (struct bgp_channel *) r->sender;
|
||||
if (!bc->base_table)
|
||||
return NULL;
|
||||
|
||||
const net_addr *n = r->net->n.addr;
|
||||
struct bgp_proto *p = (void *) r->attrs->src->proto;
|
||||
int valid = rt_flowspec_check(r->u.bgp.base_table, tab, n, r->attrs, p->is_interior);
|
||||
struct bgp_proto *p = (void *) r->src->proto;
|
||||
int valid = rt_flowspec_check(bc->base_table, tab, n, r->attrs, p->is_interior);
|
||||
int dest = valid ? RTD_NONE : RTD_UNREACHABLE;
|
||||
|
||||
if (dest == r->attrs->dest)
|
||||
|
@ -2858,11 +2696,12 @@ rt_flowspec_update_rte(rtable *tab, rte *r)
|
|||
rta *a = alloca(RTA_MAX_SIZE);
|
||||
memcpy(a, r->attrs, rta_size(r->attrs));
|
||||
a->dest = dest;
|
||||
a->aflags = 0;
|
||||
a->cached = 0;
|
||||
|
||||
rte *new = sl_alloc(rte_slab);
|
||||
memcpy(new, r, sizeof(rte));
|
||||
new->attrs = rta_lookup(a);
|
||||
rt_lock_source(new->src);
|
||||
|
||||
return new;
|
||||
#else
|
||||
|
@ -2898,8 +2737,8 @@ rt_next_hop_update_net(rtable *tab, net *n)
|
|||
|
||||
/* Call a pre-comparison hook */
|
||||
/* Not really an efficient way to compute this */
|
||||
if (e->attrs->src->proto->rte_recalculate)
|
||||
e->attrs->src->proto->rte_recalculate(tab, n, new, e, NULL);
|
||||
if (e->src->proto->rte_recalculate)
|
||||
e->src->proto->rte_recalculate(tab, n, new, e, NULL);
|
||||
|
||||
if (e != old_best)
|
||||
rte_free_quick(e);
|
||||
|
@ -2999,8 +2838,8 @@ rt_new_table(struct symbol *s, uint addr_type)
|
|||
cf_define_symbol(s, SYM_TABLE, table, c);
|
||||
c->name = s->name;
|
||||
c->addr_type = addr_type;
|
||||
c->gc_max_ops = 1000;
|
||||
c->gc_min_time = 5;
|
||||
c->gc_threshold = 1000;
|
||||
c->gc_period = (uint) -1; /* set in rt_postconfig() */
|
||||
c->min_settle_time = 1 S;
|
||||
c->max_settle_time = 20 S;
|
||||
|
||||
|
@ -3232,9 +3071,6 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr
|
|||
{
|
||||
net = net_get(tab, n);
|
||||
|
||||
if (!new->pref)
|
||||
new->pref = c->preference;
|
||||
|
||||
if (!rta_is_cached(new->attrs))
|
||||
new->attrs = rta_lookup(new->attrs);
|
||||
}
|
||||
|
@ -3248,7 +3084,7 @@ rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src *sr
|
|||
|
||||
/* Find the old rte */
|
||||
for (pos = &net->routes; old = *pos; pos = &old->next)
|
||||
if (old->attrs->src == src)
|
||||
if (old->src == src)
|
||||
{
|
||||
if (new && rte_same(old, new))
|
||||
{
|
||||
|
@ -3353,7 +3189,7 @@ rt_reload_channel(struct channel *c)
|
|||
return 0;
|
||||
}
|
||||
|
||||
rte_update2(c, e->net->n.addr, rte_do_cow(e), e->attrs->src);
|
||||
rte_update2(c, e->net->n.addr, rte_do_cow(e), e->src);
|
||||
}
|
||||
|
||||
c->reload_next_rte = NULL;
|
||||
|
@ -3436,9 +3272,7 @@ rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, int re
|
|||
if (new)
|
||||
{
|
||||
net = net_get(tab, n);
|
||||
src = new->attrs->src;
|
||||
|
||||
rte_store_tmp_attrs(new, rte_update_pool, NULL);
|
||||
src = new->src;
|
||||
|
||||
if (!rta_is_cached(new->attrs))
|
||||
new->attrs = rta_lookup(new->attrs);
|
||||
|
@ -3446,7 +3280,7 @@ rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, int re
|
|||
else
|
||||
{
|
||||
net = net_find(tab, n);
|
||||
src = old0->attrs->src;
|
||||
src = old0->src;
|
||||
|
||||
if (!net)
|
||||
goto drop_withdraw;
|
||||
|
@ -3454,7 +3288,7 @@ rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, int re
|
|||
|
||||
/* Find the old rte */
|
||||
for (pos = &net->routes; old = *pos; pos = &old->next)
|
||||
if ((c->ra_mode != RA_ANY) || (old->attrs->src == src))
|
||||
if ((c->ra_mode != RA_ANY) || (old->src == src))
|
||||
{
|
||||
if (new && rte_same(old, new))
|
||||
{
|
||||
|
@ -3603,7 +3437,7 @@ hc_delete_hostentry(struct hostcache *hc, pool *p, struct hostentry *he)
|
|||
|
||||
rem_node(&he->ln);
|
||||
hc_remove(hc, he);
|
||||
sl_free(hc->slab, he);
|
||||
sl_free(he);
|
||||
|
||||
hc->hash_items--;
|
||||
if (hc->hash_items < hc->hash_min)
|
||||
|
@ -3620,7 +3454,7 @@ rt_init_hostcache(rtable *tab)
|
|||
hc_alloc_table(hc, tab->rp, HC_DEF_ORDER);
|
||||
hc->slab = sl_new(tab->rp, sizeof(struct hostentry));
|
||||
|
||||
hc->lp = lp_new(tab->rp, LP_GOOD_SIZE(1024));
|
||||
hc->lp = lp_new(tab->rp);
|
||||
hc->trie = f_new_trie(hc->lp, 0);
|
||||
|
||||
tab->hostcache = hc;
|
||||
|
@ -3679,36 +3513,12 @@ rt_get_igp_metric(rte *rt)
|
|||
if (ea)
|
||||
return ea->u.data;
|
||||
|
||||
rta *a = rt->attrs;
|
||||
|
||||
#ifdef CONFIG_OSPF
|
||||
if ((a->source == RTS_OSPF) ||
|
||||
(a->source == RTS_OSPF_IA) ||
|
||||
(a->source == RTS_OSPF_EXT1))
|
||||
return rt->u.ospf.metric1;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RIP
|
||||
if (a->source == RTS_RIP)
|
||||
return rt->u.rip.metric;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BGP
|
||||
if (a->source == RTS_BGP)
|
||||
{
|
||||
u64 metric = bgp_total_aigp_metric(rt);
|
||||
return (u32) MIN(metric, (u64) IGP_METRIC_UNKNOWN);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BABEL
|
||||
if (a->source == RTS_BABEL)
|
||||
return rt->u.babel.metric;
|
||||
#endif
|
||||
|
||||
if (a->source == RTS_DEVICE)
|
||||
if (rt->attrs->source == RTS_DEVICE)
|
||||
return 0;
|
||||
|
||||
if (rt->src->proto->rte_igp_metric)
|
||||
return rt->src->proto->rte_igp_metric(rt);
|
||||
|
||||
return IGP_METRIC_UNKNOWN;
|
||||
}
|
||||
|
||||
|
@ -3732,9 +3542,10 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
|
|||
{
|
||||
rte *e = n->routes;
|
||||
rta *a = e->attrs;
|
||||
pxlen = n->n.addr->pxlen;
|
||||
word pref = a->pref;
|
||||
|
||||
if (a->hostentry)
|
||||
for (rte *ee = n->routes; ee; ee = ee->next)
|
||||
if ((ee->attrs->pref >= pref) && ee->attrs->hostentry)
|
||||
{
|
||||
/* Recursive route should not depend on another recursive route */
|
||||
log(L_WARN "Next hop address %I resolvable through recursive route for %N",
|
||||
|
@ -3742,6 +3553,8 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
|
|||
goto done;
|
||||
}
|
||||
|
||||
pxlen = n->n.addr->pxlen;
|
||||
|
||||
if (a->dest == RTD_UNICAST)
|
||||
{
|
||||
for (struct nexthop *nh = &(a->nh); nh; nh = nh->next)
|
||||
|
@ -3803,6 +3616,7 @@ rt_update_hostcache(rtable *tab)
|
|||
struct hostentry *
|
||||
rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep)
|
||||
{
|
||||
ip_addr link = ipa_zero(ll) ? a : ll;
|
||||
struct hostentry *he;
|
||||
|
||||
if (!tab->hostcache)
|
||||
|
@ -3811,10 +3625,10 @@ rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep)
|
|||
u32 k = hc_hash(a, dep);
|
||||
struct hostcache *hc = tab->hostcache;
|
||||
for (he = hc->hash_table[k >> hc->hash_shift]; he != NULL; he = he->next)
|
||||
if (ipa_equal(he->addr, a) && (he->tab == dep))
|
||||
if (ipa_equal(he->addr, a) && ipa_equal(he->link, link) && (he->tab == dep))
|
||||
return he;
|
||||
|
||||
he = hc_new_hostentry(hc, tab->rp, a, ipa_zero(ll) ? a : ll, dep, k);
|
||||
he = hc_new_hostentry(hc, tab->rp, a, link, dep, k);
|
||||
rt_update_hostentry(tab, he);
|
||||
return he;
|
||||
}
|
||||
|
|
|
@ -119,7 +119,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 +129,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -273,7 +273,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);
|
||||
|
@ -284,7 +284,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
|
||||
|
@ -411,6 +411,7 @@ babel_add_seqno_request(struct babel_proto *p, struct babel_entry *e,
|
|||
|
||||
/* Found older */
|
||||
rem_node(NODE sr);
|
||||
|
||||
if (sr->nbr)
|
||||
rem_node(&sr->nbr_node);
|
||||
|
||||
|
@ -436,13 +437,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
|
||||
|
@ -535,6 +536,10 @@ babel_get_neighbor(struct babel_iface *ifa, ip_addr addr)
|
|||
init_list(&nbr->requests);
|
||||
add_tail(&ifa->neigh_list, NODE nbr);
|
||||
|
||||
nbr->srtt_pool = mb_allocz(ifa->pool, ifa->cf->rtt_win_len*sizeof(btime));
|
||||
nbr->srtt_pool_sorted = mb_allocz(ifa->pool, ifa->cf->rtt_win_len*sizeof(btime));
|
||||
nbr->srtt_poll_idx = nbr->srtt_pool_len = 0;
|
||||
|
||||
return nbr;
|
||||
}
|
||||
|
||||
|
@ -555,13 +560,12 @@ babel_flush_neighbor(struct babel_proto *p, struct babel_neighbor *nbr)
|
|||
|
||||
struct babel_seqno_request *sr;
|
||||
WALK_LIST_FIRST2(sr, nbr_node, nbr->requests)
|
||||
{
|
||||
sr->nbr = NULL;
|
||||
rem_node(&sr->nbr_node);
|
||||
}
|
||||
babel_remove_seqno_request(p, sr);
|
||||
|
||||
nbr->ifa = NULL;
|
||||
rem_node(NODE nbr);
|
||||
mb_free(nbr->srtt_pool);
|
||||
mb_free(nbr->srtt_pool_sorted);
|
||||
mb_free(nbr);
|
||||
}
|
||||
|
||||
|
@ -764,13 +768,36 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
|
|||
if (r)
|
||||
{
|
||||
rta a0 = {
|
||||
.src = p->p.main_source,
|
||||
.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,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -782,11 +809,7 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
|
|||
a0.nh.flags = RNF_ONLINK;
|
||||
|
||||
rta *a = rta_lookup(&a0);
|
||||
rte *rte = rte_get_temp(a);
|
||||
rte->u.babel.seqno = r->seqno;
|
||||
rte->u.babel.metric = r->metric;
|
||||
rte->u.babel.router_id = r->router_id;
|
||||
rte->pflags = EA_ID_FLAG(EA_BABEL_METRIC) | EA_ID_FLAG(EA_BABEL_ROUTER_ID);
|
||||
rte *rte = rte_get_temp(a, p->p.main_source);
|
||||
|
||||
e->unreachable = 0;
|
||||
rte_update2(c, e->n.addr, rte, p->p.main_source);
|
||||
|
@ -795,17 +818,15 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
|
|||
{
|
||||
/* Unreachable */
|
||||
rta a0 = {
|
||||
.src = p->p.main_source,
|
||||
.source = RTS_BABEL,
|
||||
.scope = SCOPE_UNIVERSE,
|
||||
.dest = RTD_UNREACHABLE,
|
||||
.pref = 1,
|
||||
};
|
||||
|
||||
rta *a = rta_lookup(&a0);
|
||||
rte *rte = rte_get_temp(a);
|
||||
memset(&rte->u.babel, 0, sizeof(rte->u.babel));
|
||||
rte *rte = rte_get_temp(a, p->p.main_source);
|
||||
rte->pflags = 0;
|
||||
rte->pref = 1;
|
||||
|
||||
e->unreachable = 1;
|
||||
rte_update2(c, e->n.addr, rte, p->p.main_source);
|
||||
|
@ -1306,6 +1327,21 @@ babel_handle_hello(union babel_msg *m, struct babel_iface *ifa)
|
|||
}
|
||||
}
|
||||
|
||||
static int
|
||||
babel_btime_cmp(const void *b1, const void *b2)
|
||||
{
|
||||
btime r;
|
||||
switch (r = *(btime*)b1 - *(btime*)b2)
|
||||
{
|
||||
case 0:
|
||||
return 0;
|
||||
break;
|
||||
default:
|
||||
return r > (btime)0 ? 1 : -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
babel_handle_ihu(union babel_msg *m, struct babel_iface *ifa)
|
||||
{
|
||||
|
@ -1343,12 +1379,24 @@ babel_handle_ihu(union babel_msg *m, struct babel_iface *ifa)
|
|||
|
||||
if (n->srtt)
|
||||
{
|
||||
uint decay = n->ifa->cf->rtt_decay;
|
||||
|
||||
n->srtt = (decay * rtt_sample + (256 - decay) * n->srtt) / 256;
|
||||
}
|
||||
u16 pool_idx = n->srtt_poll_idx % n->ifa->cf->rtt_win_len;
|
||||
n->srtt_poll_idx = pool_idx + 1;
|
||||
*(n->srtt_pool + pool_idx) = rtt_sample;
|
||||
if (n->srtt_pool_len < n->ifa->cf->rtt_win_len)
|
||||
n->srtt_pool_len++;
|
||||
memcpy(n->srtt_pool_sorted, n->srtt_pool, (size_t)n->srtt_pool_len*sizeof(btime));
|
||||
// use qsort for now for testing
|
||||
qsort(n->srtt_pool_sorted, (size_t)n->srtt_pool_len, sizeof(btime), babel_btime_cmp);
|
||||
int mid = n->srtt_pool_len / (u16)2;
|
||||
if (!n->srtt_pool_len || n->srtt_pool_len % (u16)2)
|
||||
n->srtt = *(n->srtt_pool_sorted + mid);
|
||||
else
|
||||
n->srtt = rtt_sample;
|
||||
n->srtt = (*(n->srtt_pool_sorted + mid - (u16)1) + *(n->srtt_pool_sorted + mid)) / (u16)2;
|
||||
}
|
||||
else {
|
||||
*(n->srtt_pool) = n->srtt = rtt_sample;
|
||||
n->srtt_poll_idx = n->srtt_pool_len = 1;
|
||||
}
|
||||
|
||||
TRACE(D_EVENTS, "RTT sample for neighbour %I on %s: %u us (srtt %u.%03u ms)",
|
||||
n->addr, ifa->ifname, rtt_sample, n->srtt/1000, n->srtt%1000);
|
||||
|
@ -2193,7 +2241,13 @@ babel_dump(struct proto *P)
|
|||
static void
|
||||
babel_get_route_info(rte *rte, byte *buf)
|
||||
{
|
||||
buf += bsprintf(buf, " (%d/%d) [%lR]", rte->pref, rte->u.babel.metric, rte->u.babel.router_id);
|
||||
u64 rid = 0;
|
||||
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);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -2201,6 +2255,9 @@ babel_get_attr(const eattr *a, byte *buf, int buflen UNUSED)
|
|||
{
|
||||
switch (a->id)
|
||||
{
|
||||
case EA_BABEL_SEQNO:
|
||||
return GA_FULL;
|
||||
|
||||
case EA_BABEL_METRIC:
|
||||
bsprintf(buf, "metric: %d", a->u.data);
|
||||
return GA_FULL;
|
||||
|
@ -2415,38 +2472,16 @@ babel_kick_timer(struct babel_proto *p)
|
|||
|
||||
|
||||
static int
|
||||
babel_preexport(struct proto *P, struct rte **new, struct linpool *pool UNUSED)
|
||||
babel_preexport(struct channel *C, struct rte *new)
|
||||
{
|
||||
struct rta *a = (*new)->attrs;
|
||||
|
||||
struct rta *a = new->attrs;
|
||||
/* Reject our own unreachable routes */
|
||||
if ((a->dest == RTD_UNREACHABLE) && (a->src->proto == P))
|
||||
if ((a->dest == RTD_UNREACHABLE) && (new->src->proto == C->proto))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
babel_make_tmp_attrs(struct rte *rt, struct linpool *pool)
|
||||
{
|
||||
struct adata *id = lp_alloc_adata(pool, sizeof(u64));
|
||||
memcpy(id->data, &rt->u.babel.router_id, sizeof(u64));
|
||||
|
||||
rte_init_tmp_attrs(rt, pool, 2);
|
||||
rte_make_tmp_attr(rt, EA_BABEL_METRIC, EAF_TYPE_INT, rt->u.babel.metric);
|
||||
rte_make_tmp_attr(rt, EA_BABEL_ROUTER_ID, EAF_TYPE_OPAQUE, (uintptr_t) id);
|
||||
}
|
||||
|
||||
static void
|
||||
babel_store_tmp_attrs(struct rte *rt, struct linpool *pool)
|
||||
{
|
||||
rte_init_tmp_attrs(rt, pool, 2);
|
||||
rt->u.babel.metric = rte_store_tmp_attr(rt, EA_BABEL_METRIC);
|
||||
|
||||
/* EA_BABEL_ROUTER_ID is read-only, we do not really save the value */
|
||||
rte_store_tmp_attr(rt, EA_BABEL_ROUTER_ID);
|
||||
}
|
||||
|
||||
/*
|
||||
* babel_rt_notify - core tells us about new route (possibly our own),
|
||||
* so store it into our data structures.
|
||||
|
@ -2461,10 +2496,22 @@ babel_rt_notify(struct proto *P, struct channel *c UNUSED, struct network *net,
|
|||
if (new)
|
||||
{
|
||||
/* Update */
|
||||
uint internal = (new->attrs->src->proto == P);
|
||||
uint rt_seqno = internal ? new->u.babel.seqno : p->update_seqno;
|
||||
uint rt_seqno;
|
||||
uint rt_metric = ea_get_int(new->attrs->eattrs, EA_BABEL_METRIC, 0);
|
||||
u64 rt_router_id = internal ? new->u.babel.router_id : p->router_id;
|
||||
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);
|
||||
if (e)
|
||||
memcpy(&rt_router_id, e->u.ptr->data, sizeof(u64));
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_seqno = p->update_seqno;
|
||||
rt_router_id = p->router_id;
|
||||
}
|
||||
|
||||
if (rt_metric > BABEL_INFINITY)
|
||||
{
|
||||
|
@ -2507,15 +2554,16 @@ babel_rt_notify(struct proto *P, struct channel *c UNUSED, struct network *net,
|
|||
static int
|
||||
babel_rte_better(struct rte *new, struct rte *old)
|
||||
{
|
||||
return new->u.babel.metric < old->u.babel.metric;
|
||||
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 int
|
||||
babel_rte_same(struct rte *new, struct rte *old)
|
||||
static u32
|
||||
babel_rte_igp_metric(struct rte *rt)
|
||||
{
|
||||
return ((new->u.babel.seqno == old->u.babel.seqno) &&
|
||||
(new->u.babel.metric == old->u.babel.metric) &&
|
||||
(new->u.babel.router_id == old->u.babel.router_id));
|
||||
return ea_get_int(rt->attrs->eattrs, EA_BABEL_METRIC, BABEL_INFINITY);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2549,10 +2597,8 @@ babel_init(struct proto_config *CF)
|
|||
P->if_notify = babel_if_notify;
|
||||
P->rt_notify = babel_rt_notify;
|
||||
P->preexport = babel_preexport;
|
||||
P->make_tmp_attrs = babel_make_tmp_attrs;
|
||||
P->store_tmp_attrs = babel_store_tmp_attrs;
|
||||
P->rte_better = babel_rte_better;
|
||||
P->rte_same = babel_rte_same;
|
||||
P->rte_igp_metric = babel_rte_igp_metric;
|
||||
|
||||
return P;
|
||||
}
|
||||
|
@ -2668,3 +2714,9 @@ struct protocol proto_babel = {
|
|||
.get_route_info = babel_get_route_info,
|
||||
.get_attr = babel_get_attr
|
||||
};
|
||||
|
||||
void
|
||||
babel_build(void)
|
||||
{
|
||||
proto_build(&proto_babel);
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
#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
|
||||
|
@ -58,7 +59,7 @@
|
|||
#define BABEL_RTT_MAX_VALUE (600 S_)
|
||||
#define BABEL_RTT_MIN (10 MS_)
|
||||
#define BABEL_RTT_MAX (120 MS_)
|
||||
#define BABEL_RTT_DECAY 42
|
||||
#define BABEL_RTT_WINLEN 100
|
||||
|
||||
/*
|
||||
* Constants for calculating metric smoothing. Chosen so that:
|
||||
|
@ -163,7 +164,7 @@ struct babel_iface_config {
|
|||
btime rtt_min; /* rtt above which to start penalising metric */
|
||||
btime rtt_max; /* max rtt metric penalty applied above this */
|
||||
u16 rtt_cost; /* metric penalty to apply at rtt_max */
|
||||
u16 rtt_decay; /* decay of neighbour RTT (units of 1/256) */
|
||||
u16 rtt_win_len; /* smoothing windows length */
|
||||
u8 rtt_send; /* whether to send timestamps on this interface */
|
||||
|
||||
u16 rx_buffer; /* RX buffer size, 0 for MTU */
|
||||
|
@ -256,6 +257,10 @@ struct babel_neighbor {
|
|||
u32 last_tstamp;
|
||||
btime last_tstamp_rcvd;
|
||||
btime srtt;
|
||||
btime *srtt_pool; // an array of btime
|
||||
btime *srtt_pool_sorted;
|
||||
u16 srtt_pool_len; // initial length
|
||||
u16 srtt_poll_idx; // head
|
||||
|
||||
u32 auth_pc;
|
||||
u8 auth_passed;
|
||||
|
|
|
@ -26,7 +26,7 @@ CF_KEYWORDS(BABEL, INTERFACE, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT,
|
|||
TYPE, WIRED, WIRELESS, TUNNEL, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK,
|
||||
LINK, NEXT, HOP, IPV4, IPV6, BABEL_METRIC, SHOW, INTERFACES, NEIGHBORS,
|
||||
ENTRIES, RANDOMIZE, ROUTER, ID, AUTHENTICATION, NONE, MAC, PERMISSIVE,
|
||||
RTT, MIN, MAX, DECAY, SEND, TIMESTAMPS)
|
||||
RTT, MIN, MAX, DECAY, WINLEN, SEND, TIMESTAMPS)
|
||||
|
||||
CF_GRAMMAR
|
||||
|
||||
|
@ -77,7 +77,7 @@ babel_iface_start:
|
|||
BABEL_IFACE->tx_priority = sk_priority_control;
|
||||
BABEL_IFACE->rtt_min = BABEL_RTT_MIN;
|
||||
BABEL_IFACE->rtt_max = BABEL_RTT_MAX;
|
||||
BABEL_IFACE->rtt_decay = BABEL_RTT_DECAY;
|
||||
BABEL_IFACE->rtt_win_len = BABEL_RTT_WINLEN;
|
||||
BABEL_IFACE->rtt_send = 1;
|
||||
BABEL_IFACE->check_link = 1;
|
||||
};
|
||||
|
@ -171,7 +171,7 @@ babel_iface_item:
|
|||
| RTT MIN expr_us { BABEL_IFACE->rtt_min = $3; }
|
||||
| RTT MAX expr_us { BABEL_IFACE->rtt_max = $3; }
|
||||
| RTT COST expr { BABEL_IFACE->rtt_cost = $3; if ($3 >= BABEL_INFINITY) cf_error("RTT cost must be < 65535"); }
|
||||
| RTT DECAY expr { BABEL_IFACE->rtt_decay = $3; if (($3 < 1) || ($3 > 256)) cf_error("RTT decay must be between 1-256"); }
|
||||
| RTT WINLEN expr { BABEL_IFACE->rtt_win_len = $3; if (($3 < 1) || ($3 > 65535)) cf_error("RTT winlen must be between 1-65535"); }
|
||||
| SEND TIMESTAMPS bool { BABEL_IFACE->rtt_send = $3; }
|
||||
| password_list
|
||||
;
|
||||
|
|
|
@ -1429,7 +1429,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))
|
||||
|
@ -1457,7 +1456,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);
|
||||
|
@ -1625,13 +1624,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;
|
||||
}
|
||||
}
|
||||
|
@ -1643,7 +1642,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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
@ -1007,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)
|
||||
{
|
||||
|
@ -1199,3 +1192,9 @@ struct protocol proto_bfd = {
|
|||
.reconfigure = bfd_reconfigure,
|
||||
.copy_config = bfd_copy_config,
|
||||
};
|
||||
|
||||
void
|
||||
bfd_build(void)
|
||||
{
|
||||
proto_build(&proto_bfd);
|
||||
}
|
||||
|
|
|
@ -482,6 +482,8 @@ birdloop_main(void *arg)
|
|||
|
||||
birdloop_set_current(loop);
|
||||
|
||||
tmp_init(loop->pool);
|
||||
|
||||
pthread_mutex_lock(&loop->mutex);
|
||||
while (1)
|
||||
{
|
||||
|
|
|
@ -106,7 +106,7 @@ bgp_set_attr(ea_list **attrs, struct linpool *pool, uint code, uint flags, uintp
|
|||
({ REPORT(msg, ## args); s->err_withdraw = 1; return; })
|
||||
|
||||
#define UNSET(a) \
|
||||
({ a->type = EAF_TYPE_UNDEF; return; })
|
||||
({ a->undef = 1; return; })
|
||||
|
||||
#define REJECT(msg, args...) \
|
||||
({ log(L_ERR "%s: " msg, s->proto->p.name, ## args); s->err_reject = 1; return; })
|
||||
|
@ -374,6 +374,13 @@ bgp_init_aigp_metric(rte *e, u64 *metric, const struct adata **ad)
|
|||
return *metric < IGP_METRIC_UNKNOWN;
|
||||
}
|
||||
|
||||
u32
|
||||
bgp_rte_igp_metric(struct rte *rt)
|
||||
{
|
||||
u64 metric = bgp_total_aigp_metric(rt);
|
||||
return (u32) MIN(metric, (u64) IGP_METRIC_UNKNOWN);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Attribute hooks
|
||||
|
@ -896,6 +903,18 @@ bgp_decode_large_community(struct bgp_parse_state *s, uint code UNUSED, uint fla
|
|||
bgp_set_attr_ptr(to, s->pool, BA_LARGE_COMMUNITY, flags, ad);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
bgp_decode_otc(struct bgp_parse_state *s, uint code UNUSED, uint flags, byte *data UNUSED, uint len, ea_list **to)
|
||||
{
|
||||
if (len != 4)
|
||||
WITHDRAW(BAD_LENGTH, "OTC", len);
|
||||
|
||||
u32 val = get_u32(data);
|
||||
bgp_set_attr_u32(to, s->pool, BA_ONLY_TO_CUSTOMER, flags, val);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
bgp_export_mpls_label_stack(struct bgp_export_state *s, eattr *a)
|
||||
{
|
||||
|
@ -1109,6 +1128,13 @@ static const struct bgp_attr_desc bgp_attr_table[] = {
|
|||
.encode = bgp_encode_u32s,
|
||||
.decode = bgp_decode_large_community,
|
||||
},
|
||||
[BA_ONLY_TO_CUSTOMER] = {
|
||||
.name = "otc",
|
||||
.type = EAF_TYPE_INT,
|
||||
.flags = BAF_OPTIONAL | BAF_TRANSITIVE,
|
||||
.encode = bgp_encode_u32,
|
||||
.decode = bgp_decode_otc,
|
||||
},
|
||||
[BA_MPLS_LABEL_STACK] = {
|
||||
.name = "mpls_label_stack",
|
||||
.type = EAF_TYPE_INT_SET,
|
||||
|
@ -1146,7 +1172,7 @@ bgp_export_attr(struct bgp_export_state *s, eattr *a, ea_list *to)
|
|||
a->flags = (a->flags & BAF_PARTIAL) | desc->flags;
|
||||
|
||||
/* Set partial bit if new opt-trans attribute is attached to non-local route */
|
||||
if ((s->src != NULL) && (a->type & EAF_ORIGINATED) &&
|
||||
if ((s->src != NULL) && (a->originated) &&
|
||||
(a->flags & BAF_OPTIONAL) && (a->flags & BAF_TRANSITIVE))
|
||||
a->flags |= BAF_PARTIAL;
|
||||
|
||||
|
@ -1154,7 +1180,7 @@ bgp_export_attr(struct bgp_export_state *s, eattr *a, ea_list *to)
|
|||
CALL(desc->export, s, a);
|
||||
|
||||
/* Attribute might become undefined in hook */
|
||||
if ((a->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF)
|
||||
if (a->undef)
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
@ -1445,6 +1471,29 @@ bgp_finish_attrs(struct bgp_parse_state *s, rta *a)
|
|||
REPORT("Discarding AIGP attribute received on non-AIGP session");
|
||||
bgp_unset_attr(&a->eattrs, s->pool, BA_AIGP);
|
||||
}
|
||||
|
||||
/* Handle OTC ingress procedure, RFC 9234 */
|
||||
if (bgp_channel_is_role_applicable(s->channel))
|
||||
{
|
||||
struct bgp_proto *p = s->proto;
|
||||
eattr *e = bgp_find_attr(a->eattrs, BA_ONLY_TO_CUSTOMER);
|
||||
|
||||
/* Reject routes from downstream if they are leaked */
|
||||
if (e && (p->cf->local_role == BGP_ROLE_PROVIDER ||
|
||||
p->cf->local_role == BGP_ROLE_RS_SERVER))
|
||||
WITHDRAW("Route leak detected - OTC attribute from downstream");
|
||||
|
||||
/* Reject routes from peers if they are leaked */
|
||||
if (e && (p->cf->local_role == BGP_ROLE_PEER) && (e->u.data != p->cf->remote_as))
|
||||
WITHDRAW("Route leak detected - OTC attribute with mismatched ASN (%u)",
|
||||
(uint) e->u.data);
|
||||
|
||||
/* Mark routes from upstream if it did not happened before */
|
||||
if (!e && (p->cf->local_role == BGP_ROLE_CUSTOMER ||
|
||||
p->cf->local_role == BGP_ROLE_PEER ||
|
||||
p->cf->local_role == BGP_ROLE_RS_CLIENT))
|
||||
bgp_set_attr_u32(&a->eattrs, s->pool, BA_ONLY_TO_CUSTOMER, 0, p->cf->remote_as);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1458,7 +1507,7 @@ bgp_finish_attrs(struct bgp_parse_state *s, rta *a)
|
|||
#define RBH_FN(a,h) h
|
||||
|
||||
#define RBH_REHASH bgp_rbh_rehash
|
||||
#define RBH_PARAMS /8, *2, 2, 2, 8, 20
|
||||
#define RBH_PARAMS /8, *2, 2, 2, 12, 20
|
||||
|
||||
|
||||
HASH_DEFINE_REHASH_FN(RBH, struct bgp_bucket)
|
||||
|
@ -1600,7 +1649,7 @@ bgp_withdraw_bucket(struct bgp_channel *c, struct bgp_bucket *b)
|
|||
#define PXH_FN(n,i,h) h
|
||||
|
||||
#define PXH_REHASH bgp_pxh_rehash
|
||||
#define PXH_PARAMS /8, *2, 2, 2, 8, 24
|
||||
#define PXH_PARAMS /8, *2, 2, 2, 12, 24
|
||||
|
||||
|
||||
HASH_DEFINE_REHASH_FN(PXH, struct bgp_prefix)
|
||||
|
@ -1626,7 +1675,8 @@ bgp_free_prefix_table(struct bgp_channel *c)
|
|||
static struct bgp_prefix *
|
||||
bgp_get_prefix(struct bgp_channel *c, net_addr *net, u32 path_id)
|
||||
{
|
||||
u32 hash = net_hash(net) ^ u32_hash(path_id);
|
||||
/* We must use a different hash function than the rtable */
|
||||
u32 hash = u32_hash(net_hash(net) ^ u32_hash(path_id));
|
||||
struct bgp_prefix *px = HASH_FIND(c->prefix_hash, PXH, net, path_id, hash);
|
||||
|
||||
if (px)
|
||||
|
@ -1657,7 +1707,7 @@ bgp_free_prefix(struct bgp_channel *c, struct bgp_prefix *px)
|
|||
HASH_REMOVE2(c->prefix_hash, PXH, c->pool, px);
|
||||
|
||||
if (c->prefix_slab)
|
||||
sl_free(c->prefix_slab, px);
|
||||
sl_free(px);
|
||||
else
|
||||
mb_free(px);
|
||||
}
|
||||
|
@ -1668,12 +1718,16 @@ bgp_free_prefix(struct bgp_channel *c, struct bgp_prefix *px)
|
|||
*/
|
||||
|
||||
int
|
||||
bgp_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
|
||||
bgp_preexport(struct channel *C, rte *e)
|
||||
{
|
||||
rte *e = *new;
|
||||
struct proto *SRC = e->attrs->src->proto;
|
||||
struct bgp_proto *p = (struct bgp_proto *) P;
|
||||
struct proto *SRC = e->src->proto;
|
||||
struct bgp_proto *p = (struct bgp_proto *) C->proto;
|
||||
struct bgp_proto *src = (SRC->proto == &proto_bgp) ? (struct bgp_proto *) SRC : NULL;
|
||||
struct bgp_channel *c = (struct bgp_channel *) C;
|
||||
|
||||
/* Ignore non-BGP channels */
|
||||
if (C->channel != &channel_bgp)
|
||||
return -1;
|
||||
|
||||
/* Reject our routes */
|
||||
if (src == p)
|
||||
|
@ -1701,11 +1755,11 @@ bgp_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
|
|||
}
|
||||
|
||||
/* Handle well-known communities, RFC 1997 */
|
||||
struct eattr *c;
|
||||
struct eattr *a;
|
||||
if (p->cf->interpret_communities &&
|
||||
(c = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY))))
|
||||
(a = bgp_find_attr(e->attrs->eattrs, BA_COMMUNITY)))
|
||||
{
|
||||
const struct adata *d = c->u.ptr;
|
||||
const struct adata *d = a->u.ptr;
|
||||
|
||||
/* Do not export anywhere */
|
||||
if (int_set_contains(d, BGP_COMM_NO_ADVERTISE))
|
||||
|
@ -1724,13 +1778,23 @@ bgp_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* Do not export routes marked with OTC to upstream, RFC 9234 */
|
||||
if (bgp_channel_is_role_applicable(c))
|
||||
{
|
||||
a = bgp_find_attr(e->attrs->eattrs, BA_ONLY_TO_CUSTOMER);
|
||||
if (a && (p->cf->local_role==BGP_ROLE_CUSTOMER ||
|
||||
p->cf->local_role==BGP_ROLE_PEER ||
|
||||
p->cf->local_role==BGP_ROLE_RS_CLIENT))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ea_list *
|
||||
bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *attrs0, struct linpool *pool)
|
||||
{
|
||||
struct proto *SRC = e->attrs->src->proto;
|
||||
struct proto *SRC = e->src->proto;
|
||||
struct bgp_proto *src = (SRC->proto == &proto_bgp) ? (void *) SRC : NULL;
|
||||
struct bgp_export_state s = { .proto = p, .channel = c, .pool = pool, .src = src, .route = e, .mpls = c->desc->mpls };
|
||||
ea_list *attrs = attrs0;
|
||||
|
@ -1770,7 +1834,7 @@ bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *at
|
|||
|
||||
/* MULTI_EXIT_DESC attribute - accept only if set in export filter */
|
||||
a = bgp_find_attr(attrs0, BA_MULTI_EXIT_DISC);
|
||||
if (a && !(a->type & EAF_FRESH))
|
||||
if (a && !(a->fresh))
|
||||
bgp_unset_attr(&attrs, pool, BA_MULTI_EXIT_DISC);
|
||||
}
|
||||
|
||||
|
@ -1833,6 +1897,16 @@ bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *at
|
|||
}
|
||||
}
|
||||
|
||||
/* Mark routes for downstream with OTC, RFC 9234 */
|
||||
if (bgp_channel_is_role_applicable(c))
|
||||
{
|
||||
a = bgp_find_attr(attrs, BA_ONLY_TO_CUSTOMER);
|
||||
if (!a && (p->cf->local_role == BGP_ROLE_PROVIDER ||
|
||||
p->cf->local_role == BGP_ROLE_PEER ||
|
||||
p->cf->local_role == BGP_ROLE_RS_SERVER))
|
||||
bgp_set_attr_u32(&attrs, pool, BA_ONLY_TO_CUSTOMER, 0, p->public_as);
|
||||
}
|
||||
|
||||
/*
|
||||
* Presence of mandatory attributes ORIGIN and AS_PATH is ensured by above
|
||||
* conditions. Presence and validity of quasi-mandatory NEXT_HOP attribute
|
||||
|
@ -1852,9 +1926,13 @@ bgp_rt_notify(struct proto *P, struct channel *C, net *n, rte *new, rte *old)
|
|||
struct bgp_prefix *px;
|
||||
u32 path;
|
||||
|
||||
/* Ignore non-BGP channels */
|
||||
if (C->channel != &channel_bgp)
|
||||
return;
|
||||
|
||||
if (new)
|
||||
{
|
||||
struct ea_list *attrs = bgp_update_attrs(p, c, new, new->attrs->eattrs, bgp_linpool2);
|
||||
struct ea_list *attrs = bgp_update_attrs(p, c, new, new->attrs->eattrs, tmp_linpool);
|
||||
|
||||
/* Error during attribute processing */
|
||||
if (!attrs)
|
||||
|
@ -1862,14 +1940,12 @@ bgp_rt_notify(struct proto *P, struct channel *C, net *n, rte *new, rte *old)
|
|||
|
||||
/* If attributes are invalid, we fail back to withdraw */
|
||||
buck = attrs ? bgp_get_bucket(c, attrs) : bgp_get_withdraw_bucket(c);
|
||||
path = new->attrs->src->global_id;
|
||||
|
||||
lp_flush(bgp_linpool2);
|
||||
path = new->src->global_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
buck = bgp_get_withdraw_bucket(c);
|
||||
path = old->attrs->src->global_id;
|
||||
path = old->src->global_id;
|
||||
}
|
||||
|
||||
px = bgp_get_prefix(c, n->n.addr, c->add_path_tx ? path : 0);
|
||||
|
@ -1889,34 +1965,44 @@ bgp_get_neighbor(rte *r)
|
|||
return as;
|
||||
|
||||
/* If AS_PATH is not defined, we treat rte as locally originated */
|
||||
struct bgp_proto *p = (void *) r->attrs->src->proto;
|
||||
struct bgp_proto *p = (void *) r->src->proto;
|
||||
return p->cf->confederation ?: p->local_as;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rte_stale(rte *r)
|
||||
{
|
||||
if (r->u.bgp.stale < 0)
|
||||
{
|
||||
if (r->pflags & BGP_REF_STALE)
|
||||
return 1;
|
||||
|
||||
if (r->pflags & BGP_REF_NOT_STALE)
|
||||
return 0;
|
||||
|
||||
/* If staleness is unknown, compute and cache it */
|
||||
eattr *a = ea_find(r->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY));
|
||||
r->u.bgp.stale = a && int_set_contains(a->u.ptr, BGP_COMM_LLGR_STALE);
|
||||
if (a && int_set_contains(a->u.ptr, BGP_COMM_LLGR_STALE))
|
||||
{
|
||||
r->pflags |= BGP_REF_STALE;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
r->pflags |= BGP_REF_NOT_STALE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return r->u.bgp.stale;
|
||||
}
|
||||
|
||||
int
|
||||
bgp_rte_better(rte *new, rte *old)
|
||||
{
|
||||
struct bgp_proto *new_bgp = (struct bgp_proto *) new->attrs->src->proto;
|
||||
struct bgp_proto *old_bgp = (struct bgp_proto *) old->attrs->src->proto;
|
||||
struct bgp_proto *new_bgp = (struct bgp_proto *) new->src->proto;
|
||||
struct bgp_proto *old_bgp = (struct bgp_proto *) old->src->proto;
|
||||
eattr *x, *y;
|
||||
u32 n, o;
|
||||
|
||||
/* Skip suppressed routes (see bgp_rte_recalculate()) */
|
||||
n = new->u.bgp.suppressed;
|
||||
o = old->u.bgp.suppressed;
|
||||
n = new->pflags & BGP_REF_SUPPRESSED;
|
||||
o = old->pflags & BGP_REF_SUPPRESSED;
|
||||
if (n > o)
|
||||
return 0;
|
||||
if (n < o)
|
||||
|
@ -2054,13 +2140,13 @@ bgp_rte_better(rte *new, rte *old)
|
|||
int
|
||||
bgp_rte_mergable(rte *pri, rte *sec)
|
||||
{
|
||||
struct bgp_proto *pri_bgp = (struct bgp_proto *) pri->attrs->src->proto;
|
||||
struct bgp_proto *sec_bgp = (struct bgp_proto *) sec->attrs->src->proto;
|
||||
struct bgp_proto *pri_bgp = (struct bgp_proto *) pri->src->proto;
|
||||
struct bgp_proto *sec_bgp = (struct bgp_proto *) sec->src->proto;
|
||||
eattr *x, *y;
|
||||
u32 p, s;
|
||||
|
||||
/* Skip suppressed routes (see bgp_rte_recalculate()) */
|
||||
if (pri->u.bgp.suppressed != sec->u.bgp.suppressed)
|
||||
if ((pri->pflags ^ sec->pflags) & BGP_REF_SUPPRESSED)
|
||||
return 0;
|
||||
|
||||
/* RFC 4271 9.1.2.1. Route resolvability test */
|
||||
|
@ -2133,13 +2219,13 @@ bgp_rte_mergable(rte *pri, rte *sec)
|
|||
static inline int
|
||||
same_group(rte *r, u32 lpref, u32 lasn)
|
||||
{
|
||||
return (r->pref == lpref) && (bgp_get_neighbor(r) == lasn);
|
||||
return (r->attrs->pref == lpref) && (bgp_get_neighbor(r) == lasn);
|
||||
}
|
||||
|
||||
static inline int
|
||||
use_deterministic_med(rte *r)
|
||||
{
|
||||
struct proto *P = r->attrs->src->proto;
|
||||
struct proto *P = r->src->proto;
|
||||
return (P->proto == &proto_bgp) && ((struct bgp_proto *) P)->cf->deterministic_med;
|
||||
}
|
||||
|
||||
|
@ -2148,9 +2234,9 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
|
|||
{
|
||||
rte *r, *s;
|
||||
rte *key = new ? new : old;
|
||||
u32 lpref = key->pref;
|
||||
u32 lpref = key->attrs->pref;
|
||||
u32 lasn = bgp_get_neighbor(key);
|
||||
int old_suppressed = old ? old->u.bgp.suppressed : 0;
|
||||
int old_suppressed = old ? !!(old->pflags & BGP_REF_SUPPRESSED) : 0;
|
||||
|
||||
/*
|
||||
* Proper RFC 4271 path selection is a bit complicated, it cannot be
|
||||
|
@ -2202,11 +2288,11 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
|
|||
*/
|
||||
|
||||
if (new)
|
||||
new->u.bgp.suppressed = 1;
|
||||
new->pflags |= BGP_REF_SUPPRESSED;
|
||||
|
||||
if (old)
|
||||
{
|
||||
old->u.bgp.suppressed = 1;
|
||||
old->pflags |= BGP_REF_SUPPRESSED;
|
||||
|
||||
/* The fast case - replace not best with worse (or remove not best) */
|
||||
if (old_suppressed && !(new && bgp_rte_better(new, old)))
|
||||
|
@ -2218,7 +2304,7 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
|
|||
for (s=net->routes; rte_is_valid(s); s=s->next)
|
||||
if (use_deterministic_med(s) && same_group(s, lpref, lasn))
|
||||
{
|
||||
s->u.bgp.suppressed = 1;
|
||||
s->pflags |= BGP_REF_SUPPRESSED;
|
||||
if (!r || bgp_rte_better(s, r))
|
||||
r = s;
|
||||
}
|
||||
|
@ -2229,16 +2315,16 @@ bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best)
|
|||
|
||||
/* Found if new is mergable with best-in-group */
|
||||
if (new && (new != r) && bgp_rte_mergable(r, new))
|
||||
new->u.bgp.suppressed = 0;
|
||||
new->pflags &= ~BGP_REF_SUPPRESSED;
|
||||
|
||||
/* Found all existing routes mergable with best-in-group */
|
||||
for (s=net->routes; rte_is_valid(s); s=s->next)
|
||||
if (use_deterministic_med(s) && same_group(s, lpref, lasn))
|
||||
if ((s != r) && bgp_rte_mergable(r, s))
|
||||
s->u.bgp.suppressed = 0;
|
||||
s->pflags &= ~BGP_REF_SUPPRESSED;
|
||||
|
||||
/* Found best-in-group */
|
||||
r->u.bgp.suppressed = 0;
|
||||
r->pflags &= ~BGP_REF_SUPPRESSED;
|
||||
|
||||
/*
|
||||
* There are generally two reasons why we have to force
|
||||
|
@ -2286,7 +2372,7 @@ bgp_rte_modify_stale(struct rte *r, struct linpool *pool)
|
|||
r = rte_cow_rta(r, pool);
|
||||
bgp_set_attr_ptr(&(r->attrs->eattrs), pool, BA_COMMUNITY, flags,
|
||||
int_set_add(pool, ad, BGP_COMM_LLGR_STALE));
|
||||
r->u.bgp.stale = 1;
|
||||
r->pflags |= BGP_REF_STALE;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
@ -2371,9 +2457,9 @@ bgp_get_route_info(rte *e, byte *buf)
|
|||
eattr *o = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_ORIGIN));
|
||||
u32 origas;
|
||||
|
||||
buf += bsprintf(buf, " (%d", e->pref);
|
||||
buf += bsprintf(buf, " (%d", e->attrs->pref);
|
||||
|
||||
if (e->u.bgp.suppressed)
|
||||
if (e->pflags & BGP_REF_SUPPRESSED)
|
||||
buf += bsprintf(buf, "-");
|
||||
|
||||
if (rte_stale(e))
|
||||
|
|
113
proto/bgp/bgp.c
113
proto/bgp/bgp.c
|
@ -102,6 +102,7 @@
|
|||
* 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
|
||||
* RFC 9234 - Route Leak Prevention and Detection Using Roles
|
||||
* draft-ietf-idr-ext-opt-param-07
|
||||
* draft-uttaro-idr-bgp-persistence-04
|
||||
* draft-walton-bgp-hostname-capability-02
|
||||
|
@ -126,9 +127,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);
|
||||
|
@ -161,10 +160,6 @@ bgp_open(struct bgp_proto *p)
|
|||
uint flags = p->cf->free_bind ? SKF_FREEBIND : 0;
|
||||
uint flag_mask = SKF_FREEBIND;
|
||||
|
||||
/* FIXME: Add some global init? */
|
||||
if (!bgp_linpool)
|
||||
init_list(&bgp_sockets);
|
||||
|
||||
/* We assume that cf->iface is defined iff cf->local_ip is link-local */
|
||||
|
||||
WALK_LIST(bs, bgp_sockets)
|
||||
|
@ -204,12 +199,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:
|
||||
|
@ -238,15 +227,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
|
||||
|
@ -280,7 +260,7 @@ static inline struct bgp_channel *
|
|||
bgp_find_channel(struct bgp_proto *p, u32 afi)
|
||||
{
|
||||
struct bgp_channel *c;
|
||||
WALK_LIST(c, p->p.channels)
|
||||
BGP_WALK_CHANNELS(p, c)
|
||||
if (c->afi == afi)
|
||||
return c;
|
||||
|
||||
|
@ -606,7 +586,7 @@ bgp_conn_enter_established_state(struct bgp_conn *conn)
|
|||
/* Summary state of ADD_PATH RX for active channels */
|
||||
uint summary_add_path_rx = 0;
|
||||
|
||||
WALK_LIST(c, p->p.channels)
|
||||
BGP_WALK_CHANNELS(p, c)
|
||||
{
|
||||
const struct bgp_af_caps *loc = bgp_find_af_caps(local, c->afi);
|
||||
const struct bgp_af_caps *rem = bgp_find_af_caps(peer, c->afi);
|
||||
|
@ -688,7 +668,7 @@ bgp_conn_enter_established_state(struct bgp_conn *conn)
|
|||
p->channel_count = num;
|
||||
p->summary_add_path_rx = summary_add_path_rx;
|
||||
|
||||
WALK_LIST(c, p->p.channels)
|
||||
BGP_WALK_CHANNELS(p, c)
|
||||
{
|
||||
if (c->c.disabled)
|
||||
continue;
|
||||
|
@ -767,7 +747,7 @@ bgp_handle_graceful_restart(struct bgp_proto *p)
|
|||
p->gr_active_num = 0;
|
||||
|
||||
struct bgp_channel *c;
|
||||
WALK_LIST(c, p->p.channels)
|
||||
BGP_WALK_CHANNELS(p, c)
|
||||
{
|
||||
/* FIXME: perhaps check for channel state instead of disabled flag? */
|
||||
if (c->c.disabled)
|
||||
|
@ -862,7 +842,7 @@ bgp_graceful_restart_timeout(timer *t)
|
|||
if (p->llgr_ready)
|
||||
{
|
||||
struct bgp_channel *c;
|
||||
WALK_LIST(c, p->p.channels)
|
||||
BGP_WALK_CHANNELS(p, c)
|
||||
{
|
||||
/* Channel is not in GR and is already flushed */
|
||||
if (!c->gr_active)
|
||||
|
@ -1414,6 +1394,10 @@ bgp_reload_routes(struct channel *C)
|
|||
struct bgp_proto *p = (void *) C->proto;
|
||||
struct bgp_channel *c = (void *) C;
|
||||
|
||||
/* Ignore non-BGP channels */
|
||||
if (C->channel != &channel_bgp)
|
||||
return;
|
||||
|
||||
ASSERT(p->conn && (p->route_refresh || c->c.in_table));
|
||||
|
||||
if (c->c.in_table)
|
||||
|
@ -1428,6 +1412,10 @@ bgp_feed_begin(struct channel *C, int initial)
|
|||
struct bgp_proto *p = (void *) C->proto;
|
||||
struct bgp_channel *c = (void *) C;
|
||||
|
||||
/* Ignore non-BGP channels */
|
||||
if (C->channel != &channel_bgp)
|
||||
return;
|
||||
|
||||
/* This should not happen */
|
||||
if (!p->conn)
|
||||
return;
|
||||
|
@ -1453,6 +1441,10 @@ bgp_feed_end(struct channel *C)
|
|||
struct bgp_proto *p = (void *) C->proto;
|
||||
struct bgp_channel *c = (void *) C;
|
||||
|
||||
/* Ignore non-BGP channels */
|
||||
if (C->channel != &channel_bgp)
|
||||
return;
|
||||
|
||||
/* This should not happen */
|
||||
if (!p->conn)
|
||||
return;
|
||||
|
@ -1567,7 +1559,7 @@ bgp_start(struct proto *P)
|
|||
if (p->p.gr_recovery && p->cf->gr_mode)
|
||||
{
|
||||
struct bgp_channel *c;
|
||||
WALK_LIST(c, p->p.channels)
|
||||
BGP_WALK_CHANNELS(p, c)
|
||||
channel_graceful_restart_lock(&c->c);
|
||||
}
|
||||
|
||||
|
@ -1700,6 +1692,7 @@ bgp_init(struct proto_config *CF)
|
|||
P->rte_mergable = bgp_rte_mergable;
|
||||
P->rte_recalculate = cf->deterministic_med ? bgp_rte_recalculate : NULL;
|
||||
P->rte_modify = bgp_rte_modify_stale;
|
||||
P->rte_igp_metric = bgp_rte_igp_metric;
|
||||
|
||||
p->cf = cf;
|
||||
p->is_internal = (cf->local_as == cf->remote_as);
|
||||
|
@ -1720,7 +1713,7 @@ bgp_init(struct proto_config *CF)
|
|||
|
||||
/* Add all channels */
|
||||
struct bgp_channel_config *cc;
|
||||
WALK_LIST(cc, CF->channels)
|
||||
BGP_CF_WALK_CHANNELS(cf, cc)
|
||||
proto_add_channel(P, &cc->c);
|
||||
|
||||
return P;
|
||||
|
@ -1866,7 +1859,7 @@ bgp_find_channel_config(struct bgp_config *cf, u32 afi)
|
|||
{
|
||||
struct bgp_channel_config *cc;
|
||||
|
||||
WALK_LIST(cc, cf->c.channels)
|
||||
BGP_CF_WALK_CHANNELS(cf, cc)
|
||||
if (cc->afi == afi)
|
||||
return cc;
|
||||
|
||||
|
@ -1983,6 +1976,15 @@ bgp_postconfig(struct proto_config *CF)
|
|||
if (internal && cf->rs_client)
|
||||
cf_error("Only external neighbor can be RS client");
|
||||
|
||||
if (internal && (cf->local_role != BGP_ROLE_UNDEFINED))
|
||||
cf_error("Local role cannot be set on IBGP sessions");
|
||||
|
||||
if (interior && (cf->local_role != BGP_ROLE_UNDEFINED))
|
||||
log(L_WARN "BGP roles are not recommended to be used within AS confederations");
|
||||
|
||||
if (cf->require_roles && (cf->local_role == BGP_ROLE_UNDEFINED))
|
||||
cf_error("Local role must be set if roles are required");
|
||||
|
||||
if (!cf->confederation && cf->confederation_member)
|
||||
cf_error("Confederation ID must be set for member sessions");
|
||||
|
||||
|
@ -2005,9 +2007,24 @@ bgp_postconfig(struct proto_config *CF)
|
|||
if (internal && cf->enforce_first_as)
|
||||
cf_error("Enforce first AS check is requires EBGP sessions");
|
||||
|
||||
if (cf->keepalive_time > cf->hold_time)
|
||||
cf_error("Keepalive time must be at most hold time");
|
||||
|
||||
if (cf->keepalive_time > (cf->hold_time / 2))
|
||||
log(L_WARN "Keepalive time should be at most 1/2 of hold time");
|
||||
|
||||
if (cf->min_hold_time > cf->hold_time)
|
||||
cf_error("Min hold time (%u) exceeds hold time (%u)",
|
||||
cf->min_hold_time, cf->hold_time);
|
||||
|
||||
uint keepalive_time = cf->keepalive_time ?: cf->hold_time / 3;
|
||||
if (cf->min_keepalive_time > keepalive_time)
|
||||
cf_error("Min keepalive time (%u) exceeds keepalive time (%u)",
|
||||
cf->min_keepalive_time, keepalive_time);
|
||||
|
||||
|
||||
struct bgp_channel_config *cc;
|
||||
WALK_LIST(cc, CF->channels)
|
||||
BGP_CF_WALK_CHANNELS(cf, cc)
|
||||
{
|
||||
/* Handle undefined import filter */
|
||||
if (cc->c.in_filter == FILTER_UNDEF)
|
||||
|
@ -2035,6 +2052,10 @@ bgp_postconfig(struct proto_config *CF)
|
|||
if (!cc->gw_mode)
|
||||
cc->gw_mode = cf->multihop ? GW_RECURSIVE : GW_DIRECT;
|
||||
|
||||
/* Different default for next_hop_prefer */
|
||||
if (!cc->next_hop_prefer)
|
||||
cc->next_hop_prefer = (cc->gw_mode == GW_DIRECT) ? NHP_GLOBAL : NHP_LOCAL;
|
||||
|
||||
/* Defaults based on proto config */
|
||||
if (cc->gr_able == 0xff)
|
||||
cc->gr_able = (cf->gr_mode == BGP_GR_ABLE);
|
||||
|
@ -2114,20 +2135,16 @@ bgp_reconfigure(struct proto *P, struct proto_config *CF)
|
|||
WALK_LIST(C, p->p.channels)
|
||||
C->stale = 1;
|
||||
|
||||
WALK_LIST(cc, new->c.channels)
|
||||
BGP_CF_WALK_CHANNELS(new, cc)
|
||||
{
|
||||
C = (struct channel *) bgp_find_channel(p, cc->afi);
|
||||
same = proto_configure_channel(P, &C, &cc->c) && same;
|
||||
|
||||
if (C)
|
||||
C->stale = 0;
|
||||
}
|
||||
|
||||
WALK_LIST_DELSAFE(C, C2, p->p.channels)
|
||||
if (C->stale)
|
||||
same = proto_configure_channel(P, &C, NULL) && same;
|
||||
|
||||
|
||||
if (same && (p->start_state > BSS_PREPARE))
|
||||
bgp_update_bfd(p, new->bfd);
|
||||
|
||||
|
@ -2169,6 +2186,7 @@ bgp_channel_reconfigure(struct channel *C, struct channel_config *CC, int *impor
|
|||
return 0;
|
||||
|
||||
if ((new->gw_mode != old->gw_mode) ||
|
||||
(new->next_hop_prefer != old->next_hop_prefer) ||
|
||||
(new->aigp != old->aigp) ||
|
||||
(new->cost != old->cost))
|
||||
{
|
||||
|
@ -2345,6 +2363,15 @@ bgp_show_afis(int code, char *s, u32 *afis, uint count)
|
|||
cli_msg(code, b.start);
|
||||
}
|
||||
|
||||
static const char *
|
||||
bgp_format_role_name(u8 role)
|
||||
{
|
||||
static const char *bgp_role_names[] = { "provider", "rs_server", "rs_client", "customer", "peer" };
|
||||
if (role == BGP_ROLE_UNDEFINED) return "undefined";
|
||||
if (role < ARRAY_SIZE(bgp_role_names)) return bgp_role_names[role];
|
||||
return "?";
|
||||
}
|
||||
|
||||
static void
|
||||
bgp_show_capabilities(struct bgp_proto *p UNUSED, struct bgp_caps *caps)
|
||||
{
|
||||
|
@ -2473,6 +2500,9 @@ bgp_show_capabilities(struct bgp_proto *p UNUSED, struct bgp_caps *caps)
|
|||
|
||||
if (caps->hostname)
|
||||
cli_msg(-1006, " Hostname: %s", caps->hostname);
|
||||
|
||||
if (caps->role != BGP_ROLE_UNDEFINED)
|
||||
cli_msg(-1006, " Role: %s", bgp_format_role_name(caps->role));
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -2487,6 +2517,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);
|
||||
|
||||
|
@ -2557,6 +2590,9 @@ bgp_show_proto_info(struct proto *P)
|
|||
{
|
||||
channel_show_info(&c->c);
|
||||
|
||||
if (c->c.channel != &channel_bgp)
|
||||
continue;
|
||||
|
||||
if (p->gr_active_num)
|
||||
cli_msg(-1006, " Neighbor GR: %s", bgp_gr_states[c->gr_active]);
|
||||
|
||||
|
@ -2612,3 +2648,8 @@ struct protocol proto_bgp = {
|
|||
.get_route_info = bgp_get_route_info,
|
||||
.show_proto_info = bgp_show_proto_info
|
||||
};
|
||||
|
||||
void bgp_build(void)
|
||||
{
|
||||
proto_build(&proto_bgp);
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
#include "lib/hash.h"
|
||||
#include "lib/socket.h"
|
||||
|
||||
struct linpool;
|
||||
struct eattr;
|
||||
|
||||
|
||||
|
@ -114,13 +113,18 @@ struct bgp_config {
|
|||
int gr_mode; /* Graceful restart mode (BGP_GR_*) */
|
||||
int llgr_mode; /* Long-lived graceful restart mode (BGP_LLGR_*) */
|
||||
int setkey; /* Set MD5 password to system SA/SP database */
|
||||
u8 local_role; /* Set peering role with neighbor [RFC 9234] */
|
||||
int require_roles; /* Require configured roles on both sides */
|
||||
/* Times below are in seconds */
|
||||
unsigned gr_time; /* Graceful restart timeout */
|
||||
unsigned llgr_time; /* Long-lived graceful restart stale time */
|
||||
unsigned connect_delay_time; /* Minimum delay between connect attempts */
|
||||
unsigned connect_retry_time; /* Timeout for connect attempts */
|
||||
unsigned hold_time, initial_hold_time;
|
||||
unsigned hold_time;
|
||||
unsigned min_hold_time; /* Minimum accepted hold time */
|
||||
unsigned initial_hold_time;
|
||||
unsigned keepalive_time;
|
||||
unsigned min_keepalive_time; /* Minimum accepted keepalive time */
|
||||
unsigned error_amnesia_time; /* Errors are forgotten after */
|
||||
unsigned error_delay_time_min; /* Time to wait after an error is detected */
|
||||
unsigned error_delay_time_max;
|
||||
|
@ -144,6 +148,7 @@ struct bgp_channel_config {
|
|||
ip_addr next_hop_addr; /* Local address for NEXT_HOP attribute */
|
||||
u8 next_hop_self; /* Always set next hop to local IP address (NH_*) */
|
||||
u8 next_hop_keep; /* Do not modify next hop attribute (NH_*) */
|
||||
u8 next_hop_prefer; /* Prefer global or link-local next hop (NHP_*) */
|
||||
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) */
|
||||
|
@ -167,6 +172,13 @@ struct bgp_channel_config {
|
|||
#define BGP_PT_INTERNAL 1
|
||||
#define BGP_PT_EXTERNAL 2
|
||||
|
||||
#define BGP_ROLE_UNDEFINED 255
|
||||
#define BGP_ROLE_PROVIDER 0
|
||||
#define BGP_ROLE_RS_SERVER 1
|
||||
#define BGP_ROLE_RS_CLIENT 2
|
||||
#define BGP_ROLE_CUSTOMER 3
|
||||
#define BGP_ROLE_PEER 4
|
||||
|
||||
#define NH_NO 0
|
||||
#define NH_ALL 1
|
||||
#define NH_IBGP 2
|
||||
|
@ -179,6 +191,9 @@ struct bgp_channel_config {
|
|||
#define GW_DIRECT 1
|
||||
#define GW_RECURSIVE 2
|
||||
|
||||
#define NHP_GLOBAL 1
|
||||
#define NHP_LOCAL 2
|
||||
|
||||
#define BGP_ADD_PATH_RX 1
|
||||
#define BGP_ADD_PATH_TX 2
|
||||
#define BGP_ADD_PATH_FULL 3
|
||||
|
@ -203,6 +218,10 @@ struct bgp_channel_config {
|
|||
|
||||
#define BGP_BFD_GRACEFUL 2 /* BFD down triggers graceful restart */
|
||||
|
||||
/* rte->pflags */
|
||||
#define BGP_REF_SUPPRESSED 0x1 /* Used for deterministic MED comparison */
|
||||
#define BGP_REF_STALE 0x2 /* Route is LLGR_STATE */
|
||||
#define BGP_REF_NOT_STALE 0x4 /* Route is NOT LLGR_STATE */
|
||||
|
||||
struct bgp_af_caps {
|
||||
u32 afi;
|
||||
|
@ -223,6 +242,7 @@ struct bgp_caps {
|
|||
u8 ext_messages; /* Extended message length, RFC draft */
|
||||
u8 route_refresh; /* Route refresh capability, RFC 2918 */
|
||||
u8 enhanced_refresh; /* Enhanced route refresh, RFC 7313 */
|
||||
u8 role; /* BGP role capability, RFC 9234 */
|
||||
|
||||
u8 gr_aware; /* Graceful restart capability, RFC 4724 */
|
||||
u8 gr_flags; /* Graceful restart flags */
|
||||
|
@ -454,7 +474,6 @@ struct bgp_parse_state {
|
|||
jmp_buf err_jmpbuf;
|
||||
|
||||
struct hostentry *hostentry;
|
||||
struct rtable *base_table;
|
||||
adata *mpls_labels;
|
||||
|
||||
/* Cached state for bgp_rte_update() */
|
||||
|
@ -473,6 +492,9 @@ struct bgp_parse_state {
|
|||
#define BGP_RX_BUFFER_EXT_SIZE 65535
|
||||
#define BGP_TX_BUFFER_EXT_SIZE 65535
|
||||
|
||||
#define BGP_CF_WALK_CHANNELS(P,C) WALK_LIST(C, P->c.channels) if (C->c.channel == &channel_bgp)
|
||||
#define BGP_WALK_CHANNELS(P,C) WALK_LIST(C, P->p.channels) if (C->c.channel == &channel_bgp)
|
||||
|
||||
static inline int bgp_channel_is_ipv4(struct bgp_channel *c)
|
||||
{ return BGP_AFI(c->afi) == BGP_AFI_IPV4; }
|
||||
|
||||
|
@ -485,6 +507,12 @@ static inline int bgp_cc_is_ipv4(struct bgp_channel_config *c)
|
|||
static inline int bgp_cc_is_ipv6(struct bgp_channel_config *c)
|
||||
{ return BGP_AFI(c->afi) == BGP_AFI_IPV6; }
|
||||
|
||||
static inline int bgp_channel_is_role_applicable(struct bgp_channel *c)
|
||||
{ return (c->afi == BGP_AF_IPV4 || c->afi == BGP_AF_IPV6); }
|
||||
|
||||
static inline int bgp_cc_is_role_applicable(struct bgp_channel_config *c)
|
||||
{ return (c->afi == BGP_AF_IPV4 || c->afi == BGP_AF_IPV6); }
|
||||
|
||||
static inline uint bgp_max_packet_length(struct bgp_conn *conn)
|
||||
{ return conn->ext_messages ? BGP_MAX_EXT_MSG_LENGTH : BGP_MAX_MESSAGE_LENGTH; }
|
||||
|
||||
|
@ -495,9 +523,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);
|
||||
|
@ -564,9 +589,7 @@ bgp_set_attr_data(ea_list **to, struct linpool *pool, uint code, uint flags, voi
|
|||
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; }
|
||||
#define bgp_unset_attr(to, pool, code) ea_unset_attr(to, pool, 0, code)
|
||||
|
||||
int bgp_encode_mp_reach_mrt(struct bgp_write_state *s, eattr *a, byte *buf, uint size);
|
||||
|
||||
|
@ -588,8 +611,9 @@ 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 *);
|
||||
void bgp_rt_notify(struct proto *P, struct channel *C, net *n, rte *new, rte *old);
|
||||
int bgp_preexport(struct proto *, struct rte **, struct linpool *);
|
||||
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 *buf);
|
||||
int bgp_total_aigp_metric_(rte *e, u64 *metric, const struct adata **ad);
|
||||
|
@ -660,6 +684,7 @@ void bgp_update_next_hop(struct bgp_export_state *s, eattr *a, ea_list **to);
|
|||
#define BA_AS4_AGGREGATOR 0x12 /* RFC 6793 */
|
||||
#define BA_AIGP 0x1a /* RFC 7311 */
|
||||
#define BA_LARGE_COMMUNITY 0x20 /* RFC 8092 */
|
||||
#define BA_ONLY_TO_CUSTOMER 0x23 /* RFC 9234 */
|
||||
|
||||
/* Bird's private internal BGP attributes */
|
||||
#define BA_MPLS_LABEL_STACK 0xfe /* MPLS label stack transfer attribute */
|
||||
|
|
|
@ -31,7 +31,8 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
|
|||
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, FREE, VALIDATE, BASE)
|
||||
FIRST, FREE, VALIDATE, BASE, ROLE, ROLES, PEER, PROVIDER, CUSTOMER,
|
||||
RS_SERVER, RS_CLIENT, REQUIRE, BGP_OTC, PREFER, GLOBAL)
|
||||
|
||||
%type <i> bgp_nh
|
||||
%type <i32> bgp_afi
|
||||
|
@ -40,7 +41,7 @@ CF_KEYWORDS(CEASE, PREFIX, LIMIT, HIT, ADMINISTRATIVE, SHUTDOWN, RESET, PEER,
|
|||
CONFIGURATION, CHANGE, DECONFIGURED, CONNECTION, REJECTED, COLLISION,
|
||||
OUT, OF, RESOURCES)
|
||||
|
||||
%type<i> bgp_cease_mask bgp_cease_list bgp_cease_flag
|
||||
%type<i> bgp_cease_mask bgp_cease_list bgp_cease_flag bgp_role_name
|
||||
|
||||
CF_GRAMMAR
|
||||
|
||||
|
@ -72,6 +73,7 @@ bgp_proto_start: proto_start BGP {
|
|||
BGP_CFG->llgr_mode = -1;
|
||||
BGP_CFG->llgr_time = 3600;
|
||||
BGP_CFG->setkey = 1;
|
||||
BGP_CFG->local_role = BGP_ROLE_UNDEFINED;
|
||||
BGP_CFG->dynamic_name = "dynbgp";
|
||||
BGP_CFG->check_link = -1;
|
||||
}
|
||||
|
@ -114,6 +116,14 @@ bgp_cease_flag:
|
|||
| OUT OF RESOURCES { $$ = 1 << 8; }
|
||||
;
|
||||
|
||||
bgp_role_name:
|
||||
PEER { $$ = BGP_ROLE_PEER; }
|
||||
| PROVIDER { $$ = BGP_ROLE_PROVIDER; }
|
||||
| CUSTOMER { $$ = BGP_ROLE_CUSTOMER; }
|
||||
| RS_SERVER { $$ = BGP_ROLE_RS_SERVER; }
|
||||
| RS_CLIENT { $$ = BGP_ROLE_RS_CLIENT; }
|
||||
;
|
||||
|
||||
bgp_proto:
|
||||
bgp_proto_start proto_name '{'
|
||||
| bgp_proto proto_item ';'
|
||||
|
@ -143,7 +153,8 @@ bgp_proto:
|
|||
| bgp_proto RS CLIENT bool ';' { BGP_CFG->rs_client = $4; }
|
||||
| bgp_proto CONFEDERATION expr ';' { BGP_CFG->confederation = $3; }
|
||||
| bgp_proto CONFEDERATION MEMBER bool ';' { BGP_CFG->confederation_member = $4; }
|
||||
| bgp_proto HOLD TIME expr ';' { BGP_CFG->hold_time = $4; }
|
||||
| bgp_proto HOLD TIME expr ';' { BGP_CFG->hold_time = $4; if (($4 && $4<3) || ($4>65535)) cf_error("Hold time must be in range 3-65535 or zero"); }
|
||||
| bgp_proto MIN HOLD TIME expr ';' { BGP_CFG->min_hold_time = $5; }
|
||||
| bgp_proto STARTUP HOLD TIME expr ';' { BGP_CFG->initial_hold_time = $5; }
|
||||
| bgp_proto DIRECT ';' { BGP_CFG->multihop = 0; }
|
||||
| bgp_proto MULTIHOP ';' { BGP_CFG->multihop = 64; }
|
||||
|
@ -167,7 +178,8 @@ bgp_proto:
|
|||
| bgp_proto START DELAY TIME expr ';' { BGP_CFG->connect_delay_time = $5; log(L_WARN "%s: Start delay time option is deprecated, use connect delay time", this_proto->name); }
|
||||
| bgp_proto CONNECT DELAY TIME expr ';' { BGP_CFG->connect_delay_time = $5; }
|
||||
| bgp_proto CONNECT RETRY TIME expr ';' { BGP_CFG->connect_retry_time = $5; }
|
||||
| bgp_proto KEEPALIVE TIME expr ';' { BGP_CFG->keepalive_time = $4; }
|
||||
| bgp_proto KEEPALIVE TIME expr ';' { BGP_CFG->keepalive_time = $4; if (($4<1) || ($4>65535)) cf_error("Keepalive time must be in range 1-65535"); }
|
||||
| bgp_proto MIN KEEPALIVE TIME expr ';' { BGP_CFG->min_keepalive_time = $5; }
|
||||
| bgp_proto ERROR FORGET TIME expr ';' { BGP_CFG->error_amnesia_time = $5; }
|
||||
| bgp_proto ERROR WAIT TIME expr ',' expr ';' { BGP_CFG->error_delay_time_min = $5; BGP_CFG->error_delay_time_max = $7; }
|
||||
| bgp_proto DISABLE AFTER ERROR bool ';' { BGP_CFG->disable_after_error = $5; }
|
||||
|
@ -197,6 +209,8 @@ bgp_proto:
|
|||
| bgp_proto BFD GRACEFUL ';' { init_bfd_opts(&BGP_CFG->bfd); BGP_CFG->bfd->mode = BGP_BFD_GRACEFUL; }
|
||||
| bgp_proto BFD { open_bfd_opts(&BGP_CFG->bfd); } bfd_opts { close_bfd_opts(); } ';'
|
||||
| bgp_proto ENFORCE FIRST AS bool ';' { BGP_CFG->enforce_first_as = $5; }
|
||||
| bgp_proto LOCAL ROLE bgp_role_name ';' { BGP_CFG->local_role = $4; }
|
||||
| bgp_proto REQUIRE ROLES bool ';' { BGP_CFG->require_roles = $4; }
|
||||
;
|
||||
|
||||
bgp_afi:
|
||||
|
@ -251,6 +265,7 @@ bgp_channel_item:
|
|||
| NEXT HOP ADDRESS ipa { BGP_CC->next_hop_addr = $4; }
|
||||
| NEXT HOP SELF bgp_nh { BGP_CC->next_hop_self = $4; }
|
||||
| NEXT HOP KEEP bgp_nh { BGP_CC->next_hop_keep = $4; }
|
||||
| NEXT HOP PREFER GLOBAL { BGP_CC->next_hop_prefer = NHP_GLOBAL; }
|
||||
| MANDATORY bool { BGP_CC->mandatory = $2; }
|
||||
| MISSING LLADDR bgp_lladdr { log(L_WARN "%s.%s: Missing lladdr option is deprecated and ignored, remove it", this_proto->name, this_channel->name); }
|
||||
| GATEWAY DIRECT { BGP_CC->gw_mode = GW_DIRECT; }
|
||||
|
@ -343,6 +358,8 @@ 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)); } ;
|
||||
dynamic_attr: BGP_OTC
|
||||
{ $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_CODE(PROTOCOL_BGP, BA_ONLY_TO_CUSTOMER)); } ;
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -238,6 +238,7 @@ bgp_prepare_capabilities(struct bgp_conn *conn)
|
|||
caps->ext_messages = p->cf->enable_extended_messages;
|
||||
caps->route_refresh = p->cf->enable_refresh;
|
||||
caps->enhanced_refresh = p->cf->enable_refresh;
|
||||
caps->role = p->cf->local_role;
|
||||
|
||||
if (caps->as4_support)
|
||||
caps->as4_number = p->public_as;
|
||||
|
@ -261,7 +262,7 @@ bgp_prepare_capabilities(struct bgp_conn *conn)
|
|||
}
|
||||
|
||||
/* Allocate and fill per-AF fields */
|
||||
WALK_LIST(c, p->p.channels)
|
||||
BGP_WALK_CHANNELS(p, c)
|
||||
{
|
||||
ac = &caps->af_data[caps->af_count++];
|
||||
ac->afi = c->afi;
|
||||
|
@ -350,6 +351,13 @@ bgp_write_capabilities(struct bgp_conn *conn, byte *buf)
|
|||
*buf++ = 0; /* Capability data length */
|
||||
}
|
||||
|
||||
if (caps->role != BGP_ROLE_UNDEFINED)
|
||||
{
|
||||
*buf++ = 9; /* Capability 9: Announce chosen BGP role */
|
||||
*buf++ = 1; /* Capability data length */
|
||||
*buf++ = caps->role;
|
||||
}
|
||||
|
||||
if (caps->gr_aware)
|
||||
{
|
||||
*buf++ = 64; /* Capability 64: Support for graceful restart */
|
||||
|
@ -449,11 +457,15 @@ bgp_read_capabilities(struct bgp_conn *conn, byte *pos, int len)
|
|||
struct bgp_proto *p = conn->bgp;
|
||||
struct bgp_caps *caps;
|
||||
struct bgp_af_caps *ac;
|
||||
uint err_subcode = 0;
|
||||
int i, cl;
|
||||
u32 af;
|
||||
|
||||
if (!conn->remote_caps)
|
||||
{
|
||||
caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps) + sizeof(struct bgp_af_caps));
|
||||
caps->role = BGP_ROLE_UNDEFINED;
|
||||
}
|
||||
else
|
||||
{
|
||||
caps = conn->remote_caps;
|
||||
|
@ -513,6 +525,21 @@ bgp_read_capabilities(struct bgp_conn *conn, byte *pos, int len)
|
|||
caps->ext_messages = 1;
|
||||
break;
|
||||
|
||||
case 9: /* BGP role capability, RFC 9234 */
|
||||
if (cl != 1)
|
||||
goto err;
|
||||
|
||||
/* Reserved value */
|
||||
if (pos[2] == BGP_ROLE_UNDEFINED)
|
||||
{ err_subcode = 11; goto err; }
|
||||
|
||||
/* Multiple inconsistent values */
|
||||
if ((caps->role != BGP_ROLE_UNDEFINED) && (caps->role != pos[2]))
|
||||
{ err_subcode = 11; goto err; }
|
||||
|
||||
caps->role = pos[2];
|
||||
break;
|
||||
|
||||
case 64: /* Graceful restart capability, RFC 4724 */
|
||||
if (cl % 4 != 2)
|
||||
goto err;
|
||||
|
@ -638,7 +665,7 @@ bgp_read_capabilities(struct bgp_conn *conn, byte *pos, int len)
|
|||
|
||||
err:
|
||||
mb_free(caps);
|
||||
bgp_error(conn, 2, 0, NULL, 0);
|
||||
bgp_error(conn, 2, err_subcode, NULL, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -654,7 +681,7 @@ bgp_check_capabilities(struct bgp_conn *conn)
|
|||
/* This is partially overlapping with bgp_conn_enter_established_state(),
|
||||
but we need to run this just after we receive OPEN message */
|
||||
|
||||
WALK_LIST(c, p->p.channels)
|
||||
BGP_WALK_CHANNELS(p, c)
|
||||
{
|
||||
const struct bgp_af_caps *loc = bgp_find_af_caps(local, c->afi);
|
||||
const struct bgp_af_caps *rem = bgp_find_af_caps(remote, c->afi);
|
||||
|
@ -820,9 +847,25 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
|
|||
if (bgp_read_options(conn, pkt+29, pkt[28], len-29) < 0)
|
||||
return;
|
||||
|
||||
/* RFC 4271 4.2 - hold time must be either 0 or at least 3 */
|
||||
if (hold > 0 && hold < 3)
|
||||
{ bgp_error(conn, 2, 6, pkt+22, 2); return; }
|
||||
|
||||
/* Compute effective hold and keepalive times */
|
||||
uint hold_time = MIN(hold, p->cf->hold_time);
|
||||
uint keepalive_time = p->cf->keepalive_time ?
|
||||
(p->cf->keepalive_time * hold_time / p->cf->hold_time) :
|
||||
hold_time / 3;
|
||||
|
||||
/* Keepalive time might be rounded down to zero */
|
||||
if (hold_time && !keepalive_time)
|
||||
keepalive_time = 1;
|
||||
|
||||
/* Check effective values against configured minimums */
|
||||
if ((hold_time < p->cf->min_hold_time) ||
|
||||
(keepalive_time < p->cf->min_keepalive_time))
|
||||
{ bgp_error(conn, 2, 6, pkt+22, 2); return; }
|
||||
|
||||
/* RFC 6286 2.2 - router ID is nonzero and AS-wide unique */
|
||||
if (!id || (p->is_internal && id == p->local_id))
|
||||
{ bgp_error(conn, 2, 3, pkt+24, -4); return; }
|
||||
|
@ -854,6 +897,22 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
|
|||
conn->received_as = asn;
|
||||
}
|
||||
|
||||
/* RFC 9234 4.2 - check role agreement */
|
||||
u8 local_role = p->cf->local_role;
|
||||
u8 neigh_role = caps->role;
|
||||
|
||||
if ((local_role != BGP_ROLE_UNDEFINED) &&
|
||||
(neigh_role != BGP_ROLE_UNDEFINED) &&
|
||||
!((local_role == BGP_ROLE_PEER && neigh_role == BGP_ROLE_PEER) ||
|
||||
(local_role == BGP_ROLE_CUSTOMER && neigh_role == BGP_ROLE_PROVIDER) ||
|
||||
(local_role == BGP_ROLE_PROVIDER && neigh_role == BGP_ROLE_CUSTOMER) ||
|
||||
(local_role == BGP_ROLE_RS_CLIENT && neigh_role == BGP_ROLE_RS_SERVER) ||
|
||||
(local_role == BGP_ROLE_RS_SERVER && neigh_role == BGP_ROLE_RS_CLIENT)))
|
||||
{ bgp_error(conn, 2, 11, NULL, 0); return; }
|
||||
|
||||
if ((p->cf->require_roles) && (neigh_role == BGP_ROLE_UNDEFINED))
|
||||
{ bgp_error(conn, 2, 11, NULL, 0); return; }
|
||||
|
||||
/* Check the other connection */
|
||||
other = (conn == &p->outgoing_conn) ? &p->incoming_conn : &p->outgoing_conn;
|
||||
switch (other->state)
|
||||
|
@ -904,8 +963,8 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
|
|||
}
|
||||
|
||||
/* Update our local variables */
|
||||
conn->hold_time = MIN(hold, p->cf->hold_time);
|
||||
conn->keepalive_time = p->cf->keepalive_time ? : conn->hold_time / 3;
|
||||
conn->hold_time = hold_time;
|
||||
conn->keepalive_time = keepalive_time;
|
||||
conn->as4_session = conn->local_caps->as4_support && caps->as4_support;
|
||||
conn->ext_messages = conn->local_caps->ext_messages && caps->ext_messages;
|
||||
p->remote_id = id;
|
||||
|
@ -977,7 +1036,8 @@ bgp_apply_next_hop(struct bgp_parse_state *s, rta *a, ip_addr gw, ip_addr ll)
|
|||
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);
|
||||
ip_addr lla = (c->cf->next_hop_prefer == NHP_LOCAL) ? ll : IPA_NONE;
|
||||
s->hostentry = rt_get_hostentry(tab, gw, lla, c->c.table);
|
||||
|
||||
if (!s->mpls)
|
||||
rta_apply_hostentry(a, s->hostentry, NULL);
|
||||
|
@ -1025,9 +1085,6 @@ bgp_apply_flow_validation(struct bgp_parse_state *s, const net_addr *n, rta *a)
|
|||
int valid = rt_flowspec_check(c->base_table, c->c.table, n, a, s->proto->is_interior);
|
||||
a->dest = valid ? RTD_NONE : RTD_UNREACHABLE;
|
||||
|
||||
/* Set rte.bgp.base_table later from this state variable */
|
||||
s->base_table = c->base_table;
|
||||
|
||||
/* Invalidate cached rta if dest changes */
|
||||
if (s->cached_rta && (s->cached_rta->dest != a->dest))
|
||||
{
|
||||
|
@ -1065,13 +1122,17 @@ 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 */
|
||||
if ((ipa_is_ip4(*nh) != bgp_channel_is_ipv4(c)) && !c->ext_next_hop)
|
||||
return 0;
|
||||
|
||||
/* Do not pass NEXT_HOP between different VRFs */
|
||||
if (p->p.vrf_set && s->src && s->src->p.vrf_set && (p->p.vrf != s->src->p.vrf))
|
||||
return 0;
|
||||
|
||||
/* Keep it when exported to internal peers */
|
||||
if (p->is_interior && ipa_nonzero(*nh))
|
||||
return 1;
|
||||
|
@ -1101,6 +1162,10 @@ bgp_use_gateway(struct bgp_export_state *s)
|
|||
if ((ipa_is_ip4(ra->nh.gw) != bgp_channel_is_ipv4(c)) && !c->ext_next_hop)
|
||||
return 0;
|
||||
|
||||
/* Do not use gateway from different VRF */
|
||||
if (p->p.vrf_set && ra->nh.iface && (p->p.vrf != ra->nh.iface->master))
|
||||
return 0;
|
||||
|
||||
/* Use it when exported to internal peers */
|
||||
if (p->is_interior)
|
||||
return 1;
|
||||
|
@ -1127,6 +1192,8 @@ bgp_update_next_hop_ip(struct bgp_export_state *s, eattr *a, ea_list **to)
|
|||
uint lnum = ra->nh.labels ? ra->nh.labels : 1;
|
||||
bgp_set_attr_data(to, s->pool, BA_MPLS_LABEL_STACK, 0, labels, lnum * 4);
|
||||
}
|
||||
else
|
||||
bgp_unset_attr(to, s->pool, BA_MPLS_LABEL_STACK);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1140,6 +1207,8 @@ bgp_update_next_hop_ip(struct bgp_export_state *s, eattr *a, ea_list **to)
|
|||
u32 implicit_null = BGP_MPLS_NULL;
|
||||
bgp_set_attr_data(to, s->pool, BA_MPLS_LABEL_STACK, 0, &implicit_null, 4);
|
||||
}
|
||||
else
|
||||
bgp_unset_attr(to, s->pool, BA_MPLS_LABEL_STACK);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1386,8 +1455,6 @@ bgp_rte_update(struct bgp_parse_state *s, const net_addr *n, u32 path_id, rta *a
|
|||
/* Prepare cached route attributes */
|
||||
if (s->cached_rta == NULL)
|
||||
{
|
||||
a0->src = s->last_src;
|
||||
|
||||
/* Workaround for rta_lookup() breaking eattrs */
|
||||
ea_list *ea = a0->eattrs;
|
||||
s->cached_rta = rta_lookup(a0);
|
||||
|
@ -1395,12 +1462,9 @@ bgp_rte_update(struct bgp_parse_state *s, const net_addr *n, u32 path_id, rta *a
|
|||
}
|
||||
|
||||
rta *a = rta_clone(s->cached_rta);
|
||||
rte *e = rte_get_temp(a);
|
||||
rte *e = rte_get_temp(a, s->last_src);
|
||||
|
||||
e->pflags = 0;
|
||||
e->u.bgp.suppressed = 0;
|
||||
e->u.bgp.stale = -1;
|
||||
e->u.bgp.base_table = s->base_table;
|
||||
rte_update3(&s->channel->c, n, e, s->last_src);
|
||||
}
|
||||
|
||||
|
@ -2331,11 +2395,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,
|
||||
|
@ -2361,6 +2428,7 @@ again: ;
|
|||
if (EMPTY_LIST(buck->prefixes))
|
||||
{
|
||||
bgp_free_bucket(c, buck);
|
||||
lp_restore(tmp_linpool, &tmpp);
|
||||
goto again;
|
||||
}
|
||||
|
||||
|
@ -2374,7 +2442,10 @@ again: ;
|
|||
bgp_defer_bucket(c, buck);
|
||||
|
||||
if (!res)
|
||||
{
|
||||
lp_restore(tmp_linpool, &tmpp);
|
||||
goto again;
|
||||
}
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
@ -2385,7 +2456,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;
|
||||
}
|
||||
|
@ -2464,8 +2535,6 @@ bgp_decode_nlri(struct bgp_parse_state *s, u32 afi, byte *nlri, uint len, ea_lis
|
|||
s->last_id = 0;
|
||||
s->last_src = s->proto->p.main_source;
|
||||
|
||||
s->base_table = NULL;
|
||||
|
||||
/*
|
||||
* IPv4 BGP and MP-BGP may be used together in one update, therefore we do not
|
||||
* add BA_NEXT_HOP in bgp_decode_attrs(), but we add it here independently for
|
||||
|
@ -2481,6 +2550,7 @@ bgp_decode_nlri(struct bgp_parse_state *s, u32 afi, byte *nlri, uint len, ea_lis
|
|||
a->scope = SCOPE_UNIVERSE;
|
||||
a->from = s->proto->remote_ip;
|
||||
a->eattrs = ea;
|
||||
a->pref = c->c.preference;
|
||||
|
||||
c->desc->decode_next_hop(s, nh, nh_len, a);
|
||||
bgp_finish_attrs(s, a);
|
||||
|
@ -2515,10 +2585,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,
|
||||
};
|
||||
|
||||
|
@ -2596,7 +2669,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;
|
||||
}
|
||||
|
||||
|
@ -2984,6 +3057,7 @@ static struct {
|
|||
{ 2, 6, "Unacceptable hold time" },
|
||||
{ 2, 7, "Required capability missing" }, /* [RFC5492] */
|
||||
{ 2, 8, "No supported AFI/SAFI" }, /* This error msg is nonstandard */
|
||||
{ 2,11, "Role mismatch" }, /* From Open Policy, RFC 9234 */
|
||||
{ 3, 0, "Invalid UPDATE message" },
|
||||
{ 3, 1, "Malformed attribute list" },
|
||||
{ 3, 2, "Unrecognized well-known attribute" },
|
||||
|
@ -3078,8 +3152,8 @@ bgp_log_error(struct bgp_proto *p, u8 class, char *msg, uint code, uint subcode,
|
|||
|
||||
if (len)
|
||||
{
|
||||
/* Bad peer AS - we would like to print the AS */
|
||||
if ((code == 2) && (subcode == 2) && ((len == 2) || (len == 4)))
|
||||
/* Bad peer AS / unacceptable hold time - print the value as decimal number */
|
||||
if ((code == 2) && ((subcode == 2) || (subcode == 6)) && ((len == 2) || (len == 4)))
|
||||
{
|
||||
t += bsprintf(t, ": %u", (len == 2) ? get_u16(data) : get_u32(data));
|
||||
goto done;
|
||||
|
|
|
@ -472,9 +472,9 @@ mrt_rib_table_entry(struct mrt_table_dump_state *s, rte *r)
|
|||
|
||||
#ifdef CONFIG_BGP
|
||||
/* Find peer index */
|
||||
if (r->attrs->src->proto->proto == &proto_bgp)
|
||||
if (r->src->proto->proto == &proto_bgp)
|
||||
{
|
||||
struct bgp_proto *p = (void *) r->attrs->src->proto;
|
||||
struct bgp_proto *p = (void *) r->src->proto;
|
||||
struct mrt_peer_entry *n =
|
||||
HASH_FIND(s->peer_hash, PEER, p->remote_id, p->remote_as, p->remote_ip);
|
||||
|
||||
|
@ -488,7 +488,7 @@ mrt_rib_table_entry(struct mrt_table_dump_state *s, rte *r)
|
|||
|
||||
/* Path Identifier */
|
||||
if (s->add_path)
|
||||
mrt_put_u32(b, r->attrs->src->private_id);
|
||||
mrt_put_u32(b, r->src->private_id);
|
||||
|
||||
/* Route Attributes */
|
||||
mrt_put_u16(b, 0);
|
||||
|
@ -519,14 +519,12 @@ mrt_rib_table_dump(struct mrt_table_dump_state *s, net *n, int add_path)
|
|||
continue;
|
||||
|
||||
/* Skip routes that should be reported in the other phase */
|
||||
if (!s->always_add_path && (!rt->attrs->src->private_id != !s->add_path))
|
||||
if (!s->always_add_path && (!rt->src->private_id != !s->add_path))
|
||||
{
|
||||
s->want_add_path = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
rte_make_tmp_attrs(&rt, s->linpool, NULL);
|
||||
|
||||
if (f_run(s->filter, &rt, s->linpool, 0) <= F_ACCEPT)
|
||||
mrt_rib_table_entry(s, rt);
|
||||
|
||||
|
@ -562,8 +560,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 */
|
||||
|
@ -918,3 +916,9 @@ struct protocol proto_mrt = {
|
|||
.reconfigure = mrt_reconfigure,
|
||||
.copy_config = mrt_copy_config,
|
||||
};
|
||||
|
||||
void
|
||||
mrt_build(void)
|
||||
{
|
||||
proto_build(&proto_mrt);
|
||||
}
|
||||
|
|
|
@ -522,7 +522,10 @@ static inline void
|
|||
add_nbma_node(struct ospf_iface *ifa, struct nbma_node *src, int found)
|
||||
{
|
||||
struct nbma_node *n = mb_alloc(ifa->pool, sizeof(struct nbma_node));
|
||||
|
||||
n->n = (node) {};
|
||||
add_tail(&ifa->nbma_list, NODE n);
|
||||
|
||||
n->ip = src->ip;
|
||||
n->eligible = src->eligible;
|
||||
n->found = found;
|
||||
|
|
|
@ -107,12 +107,10 @@
|
|||
#include <stdlib.h>
|
||||
#include "ospf.h"
|
||||
|
||||
static int ospf_preexport(struct proto *P, rte **new, struct linpool *pool);
|
||||
static void ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool);
|
||||
static void ospf_store_tmp_attrs(struct rte *rt, struct linpool *pool);
|
||||
static int ospf_preexport(struct channel *P, rte *new);
|
||||
static void ospf_reload_routes(struct channel *C);
|
||||
static int ospf_rte_better(struct rte *new, struct rte *old);
|
||||
static int ospf_rte_same(struct rte *new, struct rte *old);
|
||||
static u32 ospf_rte_igp_metric(struct rte *rt);
|
||||
static void ospf_disp(timer *timer);
|
||||
|
||||
|
||||
|
@ -301,7 +299,7 @@ ospf_start(struct proto *P)
|
|||
p->lsab_size = 256;
|
||||
p->lsab_used = 0;
|
||||
p->lsab = mb_alloc(P->pool, p->lsab_size);
|
||||
p->nhpool = lp_new(P->pool, 12*sizeof(struct nexthop));
|
||||
p->nhpool = lp_new(P->pool);
|
||||
init_list(&(p->iface_list));
|
||||
init_list(&(p->area_list));
|
||||
fib_init(&p->rtf, P->pool, ospf_get_af(p), sizeof(ort), OFFSETOF(ort, fn), 0, NULL);
|
||||
|
@ -378,10 +376,8 @@ ospf_init(struct proto_config *CF)
|
|||
P->reload_routes = ospf_reload_routes;
|
||||
P->feed_begin = ospf_feed_begin;
|
||||
P->feed_end = ospf_feed_end;
|
||||
P->make_tmp_attrs = ospf_make_tmp_attrs;
|
||||
P->store_tmp_attrs = ospf_store_tmp_attrs;
|
||||
P->rte_better = ospf_rte_better;
|
||||
P->rte_same = ospf_rte_same;
|
||||
P->rte_igp_metric = ospf_rte_igp_metric;
|
||||
|
||||
return P;
|
||||
}
|
||||
|
@ -390,7 +386,9 @@ ospf_init(struct proto_config *CF)
|
|||
static int
|
||||
ospf_rte_better(struct rte *new, struct rte *old)
|
||||
{
|
||||
if (new->u.ospf.metric1 == LSINFINITY)
|
||||
u32 new_metric1 = ea_get_int(new->attrs->eattrs, EA_OSPF_METRIC1, LSINFINITY);
|
||||
|
||||
if (new_metric1 == LSINFINITY)
|
||||
return 0;
|
||||
|
||||
if(new->attrs->source < old->attrs->source) return 1;
|
||||
|
@ -398,27 +396,27 @@ ospf_rte_better(struct rte *new, struct rte *old)
|
|||
|
||||
if(new->attrs->source == RTS_OSPF_EXT2)
|
||||
{
|
||||
if(new->u.ospf.metric2 < old->u.ospf.metric2) return 1;
|
||||
if(new->u.ospf.metric2 > old->u.ospf.metric2) return 0;
|
||||
u32 old_metric2 = ea_get_int(old->attrs->eattrs, EA_OSPF_METRIC2, LSINFINITY);
|
||||
u32 new_metric2 = ea_get_int(new->attrs->eattrs, EA_OSPF_METRIC2, LSINFINITY);
|
||||
if(new_metric2 < old_metric2) return 1;
|
||||
if(new_metric2 > old_metric2) return 0;
|
||||
}
|
||||
|
||||
if (new->u.ospf.metric1 < old->u.ospf.metric1)
|
||||
u32 old_metric1 = ea_get_int(old->attrs->eattrs, EA_OSPF_METRIC1, LSINFINITY);
|
||||
if (new_metric1 < old_metric1)
|
||||
return 1;
|
||||
|
||||
return 0; /* Old is shorter or same */
|
||||
}
|
||||
|
||||
static int
|
||||
ospf_rte_same(struct rte *new, struct rte *old)
|
||||
static u32
|
||||
ospf_rte_igp_metric(struct rte *rt)
|
||||
{
|
||||
/* new->attrs == old->attrs always */
|
||||
return
|
||||
new->u.ospf.metric1 == old->u.ospf.metric1 &&
|
||||
new->u.ospf.metric2 == old->u.ospf.metric2 &&
|
||||
new->u.ospf.tag == old->u.ospf.tag &&
|
||||
new->u.ospf.router_id == old->u.ospf.router_id;
|
||||
}
|
||||
if (rt->attrs->source == RTS_OSPF_EXT2)
|
||||
return IGP_METRIC_UNKNOWN;
|
||||
|
||||
return ea_get_int(rt->attrs->eattrs, EA_OSPF_METRIC1, LSINFINITY);
|
||||
}
|
||||
|
||||
void
|
||||
ospf_schedule_rtcalc(struct ospf_proto *p)
|
||||
|
@ -484,14 +482,13 @@ ospf_disp(timer * timer)
|
|||
* import to the filters.
|
||||
*/
|
||||
static int
|
||||
ospf_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
|
||||
ospf_preexport(struct channel *C, rte *e)
|
||||
{
|
||||
struct ospf_proto *p = (struct ospf_proto *) P;
|
||||
struct ospf_proto *p = (struct ospf_proto *) C->proto;
|
||||
struct ospf_area *oa = ospf_main_area(p);
|
||||
rte *e = *new;
|
||||
|
||||
/* Reject our own routes */
|
||||
if (e->attrs->src->proto == P)
|
||||
if (e->src->proto == &p->p)
|
||||
return -1;
|
||||
|
||||
/* Do not export routes to stub areas */
|
||||
|
@ -501,26 +498,6 @@ ospf_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool)
|
||||
{
|
||||
rte_init_tmp_attrs(rt, pool, 4);
|
||||
rte_make_tmp_attr(rt, EA_OSPF_METRIC1, EAF_TYPE_INT, rt->u.ospf.metric1);
|
||||
rte_make_tmp_attr(rt, EA_OSPF_METRIC2, EAF_TYPE_INT, rt->u.ospf.metric2);
|
||||
rte_make_tmp_attr(rt, EA_OSPF_TAG, EAF_TYPE_INT, rt->u.ospf.tag);
|
||||
rte_make_tmp_attr(rt, EA_OSPF_ROUTER_ID, EAF_TYPE_ROUTER_ID, rt->u.ospf.router_id);
|
||||
}
|
||||
|
||||
static void
|
||||
ospf_store_tmp_attrs(struct rte *rt, struct linpool *pool)
|
||||
{
|
||||
rte_init_tmp_attrs(rt, pool, 4);
|
||||
rt->u.ospf.metric1 = rte_store_tmp_attr(rt, EA_OSPF_METRIC1);
|
||||
rt->u.ospf.metric2 = rte_store_tmp_attr(rt, EA_OSPF_METRIC2);
|
||||
rt->u.ospf.tag = rte_store_tmp_attr(rt, EA_OSPF_TAG);
|
||||
rt->u.ospf.router_id = rte_store_tmp_attr(rt, EA_OSPF_ROUTER_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* ospf_shutdown - Finish of OSPF instance
|
||||
* @P: OSPF protocol instance
|
||||
|
@ -558,6 +535,9 @@ ospf_shutdown(struct proto *P)
|
|||
}
|
||||
FIB_WALK_END;
|
||||
|
||||
if (tm_active(p->disp_timer))
|
||||
tm_stop(p->disp_timer);
|
||||
|
||||
return PS_DOWN;
|
||||
}
|
||||
|
||||
|
@ -607,16 +587,20 @@ ospf_get_route_info(rte * rte, byte * buf)
|
|||
}
|
||||
|
||||
buf += bsprintf(buf, " %s", type);
|
||||
buf += bsprintf(buf, " (%d/%d", rte->pref, rte->u.ospf.metric1);
|
||||
buf += bsprintf(buf, " (%d/%d", rte->attrs->pref, ea_get_int(rte->attrs->eattrs, EA_OSPF_METRIC1, LSINFINITY));
|
||||
if (rte->attrs->source == RTS_OSPF_EXT2)
|
||||
buf += bsprintf(buf, "/%d", rte->u.ospf.metric2);
|
||||
buf += bsprintf(buf, "/%d", ea_get_int(rte->attrs->eattrs, EA_OSPF_METRIC2, LSINFINITY));
|
||||
buf += bsprintf(buf, ")");
|
||||
if ((rte->attrs->source == RTS_OSPF_EXT1 || rte->attrs->source == RTS_OSPF_EXT2) && rte->u.ospf.tag)
|
||||
if (rte->attrs->source == RTS_OSPF_EXT1 || rte->attrs->source == RTS_OSPF_EXT2)
|
||||
{
|
||||
buf += bsprintf(buf, " [%x]", rte->u.ospf.tag);
|
||||
eattr *ea = ea_find(rte->attrs->eattrs, EA_OSPF_TAG);
|
||||
if (ea && (ea->u.data > 0))
|
||||
buf += bsprintf(buf, " [%x]", ea->u.data);
|
||||
}
|
||||
if (rte->u.ospf.router_id)
|
||||
buf += bsprintf(buf, " [%R]", rte->u.ospf.router_id);
|
||||
|
||||
eattr *ea = ea_find(rte->attrs->eattrs, EA_OSPF_ROUTER_ID);
|
||||
if (ea)
|
||||
buf += bsprintf(buf, " [%R]", ea->u.data);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -1550,3 +1534,9 @@ struct protocol proto_ospf = {
|
|||
.get_attr = ospf_get_attr,
|
||||
.get_route_info = ospf_get_route_info
|
||||
};
|
||||
|
||||
void
|
||||
ospf_build(void)
|
||||
{
|
||||
proto_build(&proto_ospf);
|
||||
}
|
||||
|
|
|
@ -144,7 +144,7 @@ orta_compare(const struct ospf_proto *p, const orta *new, const orta *old)
|
|||
{
|
||||
int r;
|
||||
|
||||
if (old->type == RTS_DUMMY)
|
||||
if (!old->type)
|
||||
return 1;
|
||||
|
||||
/* Prefer intra-area to inter-area to externals */
|
||||
|
@ -195,7 +195,7 @@ orta_compare_asbr(const struct ospf_proto *p, const orta *new, const orta *old)
|
|||
{
|
||||
int r;
|
||||
|
||||
if (old->type == RTS_DUMMY)
|
||||
if (!old->type)
|
||||
return 1;
|
||||
|
||||
if (!p->rfc1583)
|
||||
|
@ -225,7 +225,7 @@ orta_compare_ext(const struct ospf_proto *p, const orta *new, const orta *old)
|
|||
{
|
||||
int r;
|
||||
|
||||
if (old->type == RTS_DUMMY)
|
||||
if (!old->type)
|
||||
return 1;
|
||||
|
||||
/* 16.4 (6a) - prefer routes with lower type */
|
||||
|
@ -2053,32 +2053,54 @@ again1:
|
|||
if (nf->n.type) /* Add the route */
|
||||
{
|
||||
rta a0 = {
|
||||
.src = p->p.main_source,
|
||||
.source = nf->n.type,
|
||||
.scope = SCOPE_UNIVERSE,
|
||||
.dest = RTD_UNICAST,
|
||||
.nh = *(nf->n.nhs),
|
||||
.pref = p->p.main_channel->preference,
|
||||
};
|
||||
|
||||
if (reload || ort_changed(nf, &a0))
|
||||
{
|
||||
a0.eattrs = alloca(sizeof(ea_list) + 4 * sizeof(eattr));
|
||||
memset(a0.eattrs, 0, sizeof(ea_list));
|
||||
|
||||
nf->old_metric1 = nf->n.metric1;
|
||||
nf->old_metric2 = nf->n.metric2;
|
||||
nf->old_tag = nf->n.tag;
|
||||
nf->old_rid = nf->n.rid;
|
||||
|
||||
a0.eattrs->attrs[a0.eattrs->count++] = (eattr) {
|
||||
.id = EA_OSPF_METRIC1,
|
||||
.type = EAF_TYPE_INT,
|
||||
.u.data = nf->n.metric1,
|
||||
};
|
||||
|
||||
if (nf->n.type == RTS_OSPF_EXT2)
|
||||
a0.eattrs->attrs[a0.eattrs->count++] = (eattr) {
|
||||
.id = EA_OSPF_METRIC2,
|
||||
.type = EAF_TYPE_INT,
|
||||
.u.data = nf->n.metric2,
|
||||
};
|
||||
|
||||
if ((nf->n.type == RTS_OSPF_EXT1) || (nf->n.type == RTS_OSPF_EXT2))
|
||||
a0.eattrs->attrs[a0.eattrs->count++] = (eattr) {
|
||||
.id = EA_OSPF_TAG,
|
||||
.type = EAF_TYPE_INT,
|
||||
.u.data = nf->n.tag,
|
||||
};
|
||||
|
||||
a0.eattrs->attrs[a0.eattrs->count++] = (eattr) {
|
||||
.id = EA_OSPF_ROUTER_ID,
|
||||
.type = EAF_TYPE_ROUTER_ID,
|
||||
.u.data = nf->n.rid,
|
||||
};
|
||||
|
||||
rta *a = rta_lookup(&a0);
|
||||
rte *e = rte_get_temp(a);
|
||||
rte *e = rte_get_temp(a, p->p.main_source);
|
||||
|
||||
rta_free(nf->old_rta);
|
||||
nf->old_rta = rta_clone(a);
|
||||
e->u.ospf.metric1 = nf->old_metric1 = nf->n.metric1;
|
||||
e->u.ospf.metric2 = nf->old_metric2 = nf->n.metric2;
|
||||
e->u.ospf.tag = nf->old_tag = nf->n.tag;
|
||||
e->u.ospf.router_id = nf->old_rid = nf->n.rid;
|
||||
e->pflags = EA_ID_FLAG(EA_OSPF_METRIC1) | EA_ID_FLAG(EA_OSPF_ROUTER_ID);
|
||||
|
||||
if (nf->n.type == RTS_OSPF_EXT2)
|
||||
e->pflags |= EA_ID_FLAG(EA_OSPF_METRIC2);
|
||||
|
||||
/* Perhaps onfly if tag is non-zero? */
|
||||
if ((nf->n.type == RTS_OSPF_EXT1) || (nf->n.type == RTS_OSPF_EXT2))
|
||||
e->pflags |= EA_ID_FLAG(EA_OSPF_TAG);
|
||||
|
||||
DBG("Mod rte type %d - %N via %I on iface %s, met %d\n",
|
||||
a0.source, nf->fn.addr, a0.gw, a0.iface ? a0.iface->name : "(none)", nf->n.metric1);
|
||||
|
|
|
@ -2135,7 +2135,7 @@ ospf_hash_delete(struct top_graph *f, struct top_hash_entry *e)
|
|||
if (*ee == e)
|
||||
{
|
||||
*ee = e->next;
|
||||
sl_free(f->hash_slab, e);
|
||||
sl_free(e);
|
||||
if (f->hash_entries-- < f->hash_entries_min)
|
||||
ospf_top_rehash(f, -HASH_LO_STEP);
|
||||
return;
|
||||
|
|
|
@ -143,10 +143,10 @@ perf_loop(void *data)
|
|||
|
||||
if (!p->attrs_per_rte || !(i % p->attrs_per_rte)) {
|
||||
struct rta a0 = {
|
||||
.src = p->p.main_source,
|
||||
.source = RTS_PERF,
|
||||
.scope = SCOPE_UNIVERSE,
|
||||
.dest = RTD_UNICAST,
|
||||
.pref = p->p.main_channel->preference,
|
||||
.nh.iface = p->ifa->iface,
|
||||
.nh.gw = gw,
|
||||
.nh.weight = 1,
|
||||
|
@ -161,7 +161,7 @@ perf_loop(void *data)
|
|||
clock_gettime(CLOCK_MONOTONIC, &ts_generated);
|
||||
|
||||
for (uint i=0; i<N; i++) {
|
||||
rte *e = rte_get_temp(p->data[i].a);
|
||||
rte *e = rte_get_temp(p->data[i].a, p->p.main_source);
|
||||
e->pflags = 0;
|
||||
|
||||
rte_update(P, &(p->data[i].net), e);
|
||||
|
@ -315,3 +315,9 @@ struct protocol proto_perf = {
|
|||
.reconfigure = perf_reconfigure,
|
||||
.copy_config = perf_copy_config,
|
||||
};
|
||||
|
||||
void
|
||||
perf_build(void)
|
||||
{
|
||||
proto_build(&proto_perf);
|
||||
}
|
||||
|
|
|
@ -43,6 +43,10 @@
|
|||
|
||||
#include "pipe.h"
|
||||
|
||||
#ifdef CONFIG_BGP
|
||||
#include "proto/bgp/bgp.h"
|
||||
#endif
|
||||
|
||||
static void
|
||||
pipe_rt_notify(struct proto *P, struct channel *src_ch, net *n, rte *new, rte *old)
|
||||
{
|
||||
|
@ -65,34 +69,26 @@ pipe_rt_notify(struct proto *P, struct channel *src_ch, net *n, rte *new, rte *o
|
|||
|
||||
if (new)
|
||||
{
|
||||
src = new->src;
|
||||
|
||||
a = alloca(rta_size(new->attrs));
|
||||
memcpy(a, new->attrs, rta_size(new->attrs));
|
||||
|
||||
a->aflags = 0;
|
||||
a->cached = 0;
|
||||
a->hostentry = NULL;
|
||||
e = rte_get_temp(a);
|
||||
e->pflags = 0;
|
||||
|
||||
/* Copy protocol specific embedded attributes. */
|
||||
memcpy(&(e->u), &(new->u), sizeof(e->u));
|
||||
e->pref = new->pref;
|
||||
e = rte_get_temp(a, src);
|
||||
e->pflags = new->pflags;
|
||||
|
||||
#ifdef CONFIG_BGP
|
||||
/* Hack to cleanup cached value */
|
||||
if (e->attrs->src->proto->proto == &proto_bgp)
|
||||
{
|
||||
e->u.bgp.stale = -1;
|
||||
e->u.bgp.base_table = NULL;
|
||||
}
|
||||
if (e->src->proto->proto == &proto_bgp)
|
||||
e->pflags &= ~(BGP_REF_STALE | BGP_REF_NOT_STALE);
|
||||
#endif
|
||||
|
||||
src = a->src;
|
||||
}
|
||||
else
|
||||
{
|
||||
e = NULL;
|
||||
src = old->attrs->src;
|
||||
src = old->src;
|
||||
}
|
||||
|
||||
src_ch->table->pipe_busy = 1;
|
||||
|
@ -101,11 +97,11 @@ pipe_rt_notify(struct proto *P, struct channel *src_ch, net *n, rte *new, rte *o
|
|||
}
|
||||
|
||||
static int
|
||||
pipe_preexport(struct proto *P, rte **ee, struct linpool *p UNUSED)
|
||||
pipe_preexport(struct channel *C, rte *e)
|
||||
{
|
||||
struct proto *pp = (*ee)->sender->proto;
|
||||
struct proto *pp = e->sender->proto;
|
||||
|
||||
if (pp == P)
|
||||
if (pp == C->proto)
|
||||
return -1; /* Avoid local loops automatically */
|
||||
|
||||
return 0;
|
||||
|
@ -307,3 +303,9 @@ struct protocol proto_pipe = {
|
|||
.get_status = pipe_get_status,
|
||||
.show_proto_info = pipe_show_proto_info
|
||||
};
|
||||
|
||||
void
|
||||
pipe_build(void)
|
||||
{
|
||||
proto_build(&proto_pipe);
|
||||
}
|
||||
|
|
|
@ -391,12 +391,12 @@ radv_net_match_trigger(struct radv_config *cf, net *n)
|
|||
}
|
||||
|
||||
int
|
||||
radv_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
|
||||
radv_preexport(struct channel *C, rte *new)
|
||||
{
|
||||
// struct radv_proto *p = (struct radv_proto *) P;
|
||||
struct radv_config *cf = (struct radv_config *) (P->cf);
|
||||
struct radv_config *cf = (struct radv_config *) (C->proto->cf);
|
||||
|
||||
if (radv_net_match_trigger(cf, (*new)->net))
|
||||
if (radv_net_match_trigger(cf, new->net))
|
||||
return RIC_PROCESS;
|
||||
|
||||
if (cf->propagate_routes)
|
||||
|
@ -555,7 +555,7 @@ radv_check_active(struct radv_proto *p)
|
|||
return 1;
|
||||
|
||||
struct channel *c = p->p.main_channel;
|
||||
return rt_examine(c->table, &cf->trigger, &p->p, c->out_filter);
|
||||
return rt_examine(c->table, &cf->trigger, c, c->out_filter);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -771,3 +771,9 @@ struct protocol proto_radv = {
|
|||
.get_status = radv_get_status,
|
||||
.get_attr = radv_get_attr
|
||||
};
|
||||
|
||||
void
|
||||
radv_build(void)
|
||||
{
|
||||
proto_build(&proto_radv);
|
||||
}
|
||||
|
|
102
proto/rip/rip.c
102
proto/rip/rip.c
|
@ -108,14 +108,14 @@ rip_add_rte(struct rip_proto *p, struct rip_rte **rp, struct rip_rte *src)
|
|||
}
|
||||
|
||||
static inline void
|
||||
rip_remove_rte(struct rip_proto *p, struct rip_rte **rp)
|
||||
rip_remove_rte(struct rip_proto *p UNUSED, struct rip_rte **rp)
|
||||
{
|
||||
struct rip_rte *rt = *rp;
|
||||
|
||||
rip_unlock_neighbor(rt->from);
|
||||
|
||||
*rp = rt->next;
|
||||
sl_free(p->rte_slab, rt);
|
||||
sl_free(rt);
|
||||
}
|
||||
|
||||
static inline int rip_same_rte(struct rip_rte *a, struct rip_rte *b)
|
||||
|
@ -124,6 +124,11 @@ static inline int rip_same_rte(struct rip_rte *a, struct rip_rte *b)
|
|||
static inline int rip_valid_rte(struct rip_rte *rt)
|
||||
{ return rt->from->ifa != NULL; }
|
||||
|
||||
struct rip_iface_adata {
|
||||
struct adata ad;
|
||||
struct iface *iface;
|
||||
};
|
||||
|
||||
/**
|
||||
* rip_announce_rte - announce route from RIP routing table to the core
|
||||
* @p: RIP instance
|
||||
|
@ -145,7 +150,7 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
|
|||
{
|
||||
/* Update */
|
||||
rta a0 = {
|
||||
.src = p->p.main_source,
|
||||
.pref = p->p.main_channel->preference,
|
||||
.source = RTS_RIP,
|
||||
.scope = SCOPE_UNIVERSE,
|
||||
.dest = RTD_UNICAST,
|
||||
|
@ -188,13 +193,39 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
|
|||
a0.nh.iface = rt->from->ifa->iface;
|
||||
}
|
||||
|
||||
rta *a = rta_lookup(&a0);
|
||||
rte *e = rte_get_temp(a);
|
||||
struct {
|
||||
ea_list l;
|
||||
eattr e[3];
|
||||
struct rip_iface_adata riad;
|
||||
} ea_block = {
|
||||
.l = { .count = 3, },
|
||||
.e = {
|
||||
{
|
||||
.id = EA_RIP_METRIC,
|
||||
.type = EAF_TYPE_INT,
|
||||
.u.data = rt_metric,
|
||||
},
|
||||
{
|
||||
.id = EA_RIP_TAG,
|
||||
.type = EAF_TYPE_INT,
|
||||
.u.data = rt_tag,
|
||||
},
|
||||
{
|
||||
.id = EA_RIP_FROM,
|
||||
.type = EAF_TYPE_IFACE,
|
||||
.u.ptr = &ea_block.riad.ad,
|
||||
}
|
||||
},
|
||||
.riad = {
|
||||
.ad = { .length = sizeof(struct rip_iface_adata) - sizeof(struct adata) },
|
||||
.iface = a0.nh.iface,
|
||||
},
|
||||
};
|
||||
|
||||
e->u.rip.from = a0.nh.iface;
|
||||
e->u.rip.metric = rt_metric;
|
||||
e->u.rip.tag = rt_tag;
|
||||
e->pflags = EA_ID_FLAG(EA_RIP_METRIC) | EA_ID_FLAG(EA_RIP_TAG);
|
||||
a0.eattrs = &ea_block.l;
|
||||
|
||||
rta *a = rta_lookup(&a0);
|
||||
rte *e = rte_get_temp(a, p->p.main_source);
|
||||
|
||||
rte_update(&p->p, en->n.addr, e);
|
||||
}
|
||||
|
@ -307,8 +338,10 @@ rip_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *net, s
|
|||
if (new)
|
||||
{
|
||||
/* Update */
|
||||
u32 rt_metric = ea_get_int(new->attrs->eattrs, EA_RIP_METRIC, 1);
|
||||
u32 rt_tag = ea_get_int(new->attrs->eattrs, EA_RIP_TAG, 0);
|
||||
u32 rt_metric = ea_get_int(new->attrs->eattrs, EA_RIP_METRIC, 1);
|
||||
const eattr *rie = ea_find(new->attrs->eattrs, EA_RIP_FROM);
|
||||
struct iface *rt_from = rie ? ((struct rip_iface_adata *) rie->u.ptr)->iface : NULL;
|
||||
|
||||
if (rt_metric > p->infinity)
|
||||
{
|
||||
|
@ -339,7 +372,7 @@ rip_rt_notify(struct proto *P, struct channel *ch UNUSED, struct network *net, s
|
|||
en->valid = RIP_ENTRY_VALID;
|
||||
en->metric = rt_metric;
|
||||
en->tag = rt_tag;
|
||||
en->from = (new->attrs->src->proto == P) ? new->u.rip.from : NULL;
|
||||
en->from = (new->src->proto == P) ? rt_from : NULL;
|
||||
en->iface = new->attrs->nh.iface;
|
||||
en->next_hop = new->attrs->nh.gw;
|
||||
}
|
||||
|
@ -1068,37 +1101,18 @@ rip_reload_routes(struct channel *C)
|
|||
rip_kick_timer(p);
|
||||
}
|
||||
|
||||
static void
|
||||
rip_make_tmp_attrs(struct rte *rt, struct linpool *pool)
|
||||
static u32
|
||||
rip_rte_igp_metric(struct rte *rt)
|
||||
{
|
||||
rte_init_tmp_attrs(rt, pool, 2);
|
||||
rte_make_tmp_attr(rt, EA_RIP_METRIC, EAF_TYPE_INT, rt->u.rip.metric);
|
||||
rte_make_tmp_attr(rt, EA_RIP_TAG, EAF_TYPE_INT, rt->u.rip.tag);
|
||||
}
|
||||
|
||||
static void
|
||||
rip_store_tmp_attrs(struct rte *rt, struct linpool *pool)
|
||||
{
|
||||
rte_init_tmp_attrs(rt, pool, 2);
|
||||
rt->u.rip.metric = rte_store_tmp_attr(rt, EA_RIP_METRIC);
|
||||
rt->u.rip.tag = rte_store_tmp_attr(rt, EA_RIP_TAG);
|
||||
return ea_get_int(rt->attrs->eattrs, EA_RIP_METRIC, IGP_METRIC_UNKNOWN);
|
||||
}
|
||||
|
||||
static int
|
||||
rip_rte_better(struct rte *new, struct rte *old)
|
||||
{
|
||||
return new->u.rip.metric < old->u.rip.metric;
|
||||
return rip_rte_igp_metric(new) < rip_rte_igp_metric(old);
|
||||
}
|
||||
|
||||
static int
|
||||
rip_rte_same(struct rte *new, struct rte *old)
|
||||
{
|
||||
return ((new->u.rip.metric == old->u.rip.metric) &&
|
||||
(new->u.rip.tag == old->u.rip.tag) &&
|
||||
(new->u.rip.from == old->u.rip.from));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
rip_postconfig(struct proto_config *CF)
|
||||
{
|
||||
|
@ -1120,10 +1134,8 @@ rip_init(struct proto_config *CF)
|
|||
P->rt_notify = rip_rt_notify;
|
||||
P->neigh_notify = rip_neigh_notify;
|
||||
P->reload_routes = rip_reload_routes;
|
||||
P->make_tmp_attrs = rip_make_tmp_attrs;
|
||||
P->store_tmp_attrs = rip_store_tmp_attrs;
|
||||
P->rte_better = rip_rte_better;
|
||||
P->rte_same = rip_rte_same;
|
||||
P->rte_igp_metric = rip_rte_igp_metric;
|
||||
|
||||
return P;
|
||||
}
|
||||
|
@ -1198,10 +1210,14 @@ rip_reconfigure(struct proto *P, struct proto_config *CF)
|
|||
static void
|
||||
rip_get_route_info(rte *rte, byte *buf)
|
||||
{
|
||||
buf += bsprintf(buf, " (%d/%d)", rte->pref, rte->u.rip.metric);
|
||||
struct rip_proto *p = (struct rip_proto *) rte->src->proto;
|
||||
u32 rt_metric = ea_get_int(rte->attrs->eattrs, EA_RIP_METRIC, p->infinity);
|
||||
u32 rt_tag = ea_get_int(rte->attrs->eattrs, EA_RIP_TAG, 0);
|
||||
|
||||
if (rte->u.rip.tag)
|
||||
bsprintf(buf, " [%04x]", rte->u.rip.tag);
|
||||
buf += bsprintf(buf, " (%d/%d)", rte->attrs->pref, rt_metric);
|
||||
|
||||
if (rt_tag)
|
||||
bsprintf(buf, " [%04x]", rt_tag);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -1338,3 +1354,9 @@ struct protocol proto_rip = {
|
|||
.get_route_info = rip_get_route_info,
|
||||
.get_attr = rip_get_attr
|
||||
};
|
||||
|
||||
void
|
||||
rip_build(void)
|
||||
{
|
||||
proto_build(&proto_rip);
|
||||
}
|
||||
|
|
|
@ -197,6 +197,7 @@ struct rip_rte
|
|||
|
||||
#define EA_RIP_METRIC EA_CODE(PROTOCOL_RIP, 0)
|
||||
#define EA_RIP_TAG EA_CODE(PROTOCOL_RIP, 1)
|
||||
#define EA_RIP_FROM EA_CODE(PROTOCOL_RIP, 2)
|
||||
|
||||
static inline int rip_is_v2(struct rip_proto *p)
|
||||
{ return p->rip2; }
|
||||
|
|
|
@ -121,18 +121,18 @@ rpki_table_add_roa(struct rpki_cache *cache, struct channel *channel, const net_
|
|||
struct rpki_proto *p = cache->p;
|
||||
|
||||
rta a0 = {
|
||||
.src = p->p.main_source,
|
||||
.pref = channel->preference,
|
||||
.source = RTS_RPKI,
|
||||
.scope = SCOPE_UNIVERSE,
|
||||
.dest = RTD_NONE,
|
||||
};
|
||||
|
||||
rta *a = rta_lookup(&a0);
|
||||
rte *e = rte_get_temp(a);
|
||||
rte *e = rte_get_temp(a, p->p.main_source);
|
||||
|
||||
e->pflags = 0;
|
||||
|
||||
rte_update2(channel, &pfxr->n, e, a0.src);
|
||||
rte_update2(channel, &pfxr->n, e, e->src);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -278,12 +278,13 @@ rpki_cache_change_state(struct rpki_cache *cache, const enum rpki_cache_state ne
|
|||
|
||||
case RPKI_CS_NO_INCR_UPDATE_AVAIL:
|
||||
/* Server was unable to answer the last Serial Query and sent Cache Reset. */
|
||||
rpki_cache_change_state(cache, RPKI_CS_RESET);
|
||||
break;
|
||||
|
||||
case RPKI_CS_ERROR_NO_DATA_AVAIL:
|
||||
/* No validation records are available on the cache server. */
|
||||
|
||||
if (old_state == RPKI_CS_ESTABLISHED)
|
||||
rpki_cache_change_state(cache, RPKI_CS_RESET);
|
||||
else
|
||||
rpki_schedule_next_retry(cache);
|
||||
break;
|
||||
|
||||
case RPKI_CS_ERROR_FATAL:
|
||||
|
@ -453,6 +454,11 @@ rpki_retry_hook(timer *tm)
|
|||
}
|
||||
break;
|
||||
|
||||
case RPKI_CS_NO_INCR_UPDATE_AVAIL:
|
||||
case RPKI_CS_ERROR_NO_DATA_AVAIL:
|
||||
rpki_cache_change_state(cache, RPKI_CS_RESET);
|
||||
break;
|
||||
|
||||
default:
|
||||
rpki_cache_change_state(cache, RPKI_CS_CONNECTING);
|
||||
break;
|
||||
|
@ -828,16 +834,27 @@ rpki_show_proto_info(struct proto *P)
|
|||
if (cache)
|
||||
{
|
||||
const char *transport_name = "---";
|
||||
uint default_port = 0;
|
||||
|
||||
switch (cf->tr_config.type)
|
||||
{
|
||||
#if HAVE_LIBSSH
|
||||
case RPKI_TR_SSH: transport_name = "SSHv2"; break;
|
||||
case RPKI_TR_SSH:
|
||||
transport_name = "SSHv2";
|
||||
default_port = RPKI_SSH_PORT;
|
||||
break;
|
||||
#endif
|
||||
case RPKI_TR_TCP: transport_name = "Unprotected over TCP"; break;
|
||||
case RPKI_TR_TCP:
|
||||
transport_name = "Unprotected over TCP";
|
||||
default_port = RPKI_TCP_PORT;
|
||||
break;
|
||||
};
|
||||
|
||||
cli_msg(-1006, " Cache server: %s", cf->hostname);
|
||||
|
||||
if (cf->port != default_port)
|
||||
cli_msg(-1006, " Cache port: %u", cf->port);
|
||||
|
||||
cli_msg(-1006, " Status: %s", rpki_cache_state_to_str(cache->state));
|
||||
cli_msg(-1006, " Transport: %s", transport_name);
|
||||
cli_msg(-1006, " Protocol version: %u", cache->version);
|
||||
|
@ -949,3 +966,9 @@ struct protocol proto_rpki = {
|
|||
.reconfigure = rpki_reconfigure,
|
||||
.get_status = rpki_get_status,
|
||||
};
|
||||
|
||||
void
|
||||
rpki_build(void)
|
||||
{
|
||||
proto_build(&proto_rpki);
|
||||
}
|
||||
|
|
|
@ -85,6 +85,7 @@ rpki_tr_open(struct rpki_tr_sock *tr)
|
|||
sk->rbsize = RPKI_RX_BUFFER_SIZE;
|
||||
sk->tbsize = RPKI_TX_BUFFER_SIZE;
|
||||
sk->tos = IP_PREC_INTERNET_CONTROL;
|
||||
sk->vrf = cache->p->p.vrf;
|
||||
|
||||
if (ipa_zero(sk->daddr) && sk->host)
|
||||
{
|
||||
|
|
|
@ -40,7 +40,7 @@ static_route_finish(void)
|
|||
if (net_type_match(this_srt->net, NB_DEST) == !this_srt->dest)
|
||||
cf_error("Unexpected or missing nexthop/type");
|
||||
|
||||
this_srt->cmds = f_linearize(this_srt_cmds);
|
||||
this_srt->cmds = f_linearize(this_srt_cmds, 0);
|
||||
}
|
||||
|
||||
CF_DECLS
|
||||
|
|
|
@ -56,10 +56,11 @@ static void
|
|||
static_announce_rte(struct static_proto *p, struct static_route *r)
|
||||
{
|
||||
rta *a = allocz(RTA_MAX_SIZE);
|
||||
a->src = static_get_source(p, r->index);
|
||||
struct rte_src *src = static_get_source(p, r->index);
|
||||
a->source = RTS_STATIC;
|
||||
a->scope = SCOPE_UNIVERSE;
|
||||
a->dest = r->dest;
|
||||
a->pref = p->p.main_channel->preference;
|
||||
|
||||
if (r->dest == RTD_UNICAST)
|
||||
{
|
||||
|
@ -102,7 +103,7 @@ static_announce_rte(struct static_proto *p, struct static_route *r)
|
|||
return;
|
||||
|
||||
/* We skip rta_lookup() here */
|
||||
rte *e = rte_get_temp(a);
|
||||
rte *e = rte_get_temp(a, src);
|
||||
e->pflags = 0;
|
||||
|
||||
if (r->cmds)
|
||||
|
@ -119,7 +120,7 @@ static_announce_rte(struct static_proto *p, struct static_route *r)
|
|||
e->net = NULL;
|
||||
}
|
||||
|
||||
rte_update2(p->p.main_channel, r->net, e, a->src);
|
||||
rte_update2(p->p.main_channel, r->net, e, src);
|
||||
r->state = SRS_CLEAN;
|
||||
|
||||
if (r->cmds)
|
||||
|
@ -131,7 +132,7 @@ withdraw:
|
|||
if (r->state == SRS_DOWN)
|
||||
return;
|
||||
|
||||
rte_update2(p->p.main_channel, r->net, NULL, a->src);
|
||||
rte_update2(p->p.main_channel, r->net, NULL, src);
|
||||
r->state = SRS_DOWN;
|
||||
}
|
||||
|
||||
|
@ -485,7 +486,7 @@ static_start(struct proto *P)
|
|||
struct static_route *r;
|
||||
|
||||
if (!static_lp)
|
||||
static_lp = lp_new(&root_pool, LP_GOOD_SIZE(1024));
|
||||
static_lp = lp_new(&root_pool);
|
||||
|
||||
if (p->igp_table_ip4)
|
||||
rt_lock_table(p->igp_table_ip4);
|
||||
|
@ -721,9 +722,9 @@ static_get_route_info(rte *rte, byte *buf)
|
|||
{
|
||||
eattr *a = ea_find(rte->attrs->eattrs, EA_GEN_IGP_METRIC);
|
||||
if (a)
|
||||
buf += bsprintf(buf, " (%d/%u)", rte->pref, a->u.data);
|
||||
buf += bsprintf(buf, " (%d/%u)", rte->attrs->pref, a->u.data);
|
||||
else
|
||||
buf += bsprintf(buf, " (%d)", rte->pref);
|
||||
buf += bsprintf(buf, " (%d)", rte->attrs->pref);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -792,3 +793,9 @@ struct protocol proto_static = {
|
|||
.copy_config = static_copy_config,
|
||||
.get_route_info = static_get_route_info,
|
||||
};
|
||||
|
||||
void
|
||||
static_build(void)
|
||||
{
|
||||
proto_build(&proto_static);
|
||||
}
|
||||
|
|
|
@ -190,6 +190,10 @@ static inline void
|
|||
sockaddr_fill_dl(struct sockaddr_dl *sa, struct iface *ifa)
|
||||
{
|
||||
uint len = OFFSETOF(struct sockaddr_dl, sdl_data);
|
||||
|
||||
/* Workaround for FreeBSD 13.0 */
|
||||
len = MAX(len, sizeof(struct sockaddr));
|
||||
|
||||
memset(sa, 0, len);
|
||||
sa->sdl_len = len;
|
||||
sa->sdl_family = AF_LINK;
|
||||
|
@ -347,7 +351,7 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
|
|||
}
|
||||
|
||||
void
|
||||
krt_replace_rte(struct krt_proto *p, net *n, rte *new, rte *old)
|
||||
krt_replace_rte(struct krt_proto *p, net *n UNUSED, rte *new, rte *old)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
|
@ -519,7 +523,6 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
|
|||
net = net_get(p->p.main_channel->table, &ndst);
|
||||
|
||||
rta a = {
|
||||
.src = p->p.main_source,
|
||||
.source = RTS_INHERIT,
|
||||
.scope = SCOPE_UNIVERSE,
|
||||
};
|
||||
|
@ -580,18 +583,23 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
|
|||
}
|
||||
|
||||
done:
|
||||
e = rte_get_temp(&a);
|
||||
e = rte_get_temp(&a, p->p.main_source);
|
||||
e->net = net;
|
||||
e->u.krt.src = src;
|
||||
e->u.krt.proto = src2;
|
||||
e->u.krt.seen = 0;
|
||||
e->u.krt.best = 0;
|
||||
e->u.krt.metric = 0;
|
||||
|
||||
ea_list *ea = alloca(sizeof(ea_list) + 1 * sizeof(eattr));
|
||||
*ea = (ea_list) { .count = 1, .next = e->attrs->eattrs };
|
||||
e->attrs->eattrs = ea;
|
||||
|
||||
ea->attrs[0] = (eattr) {
|
||||
.id = EA_KRT_SOURCE,
|
||||
.type = EAF_TYPE_INT,
|
||||
.u.data = src2,
|
||||
};
|
||||
|
||||
if (scan)
|
||||
krt_got_route(p, e);
|
||||
krt_got_route(p, e, src);
|
||||
else
|
||||
krt_got_route_async(p, e, new);
|
||||
krt_got_route_async(p, e, new, src);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1198,7 +1206,7 @@ kif_update_sysdep_addr(struct iface *i)
|
|||
return 0;
|
||||
|
||||
ip4_addr old = i->sysdep;
|
||||
i->sysdep = ipa_to_ip4(ipa_from_sa4(&ifr.ifr_addr));
|
||||
i->sysdep = ipa_to_ip4(ipa_from_sa4((sockaddr *) &ifr.ifr_addr));
|
||||
|
||||
return !ip4_equal(i->sysdep, old);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ Available configuration variables:
|
|||
CONFIG_AUTO_ROUTES Device routes are added automagically by the kernel
|
||||
CONFIG_SELF_CONSCIOUS We're able to recognize whether route was installed by us
|
||||
CONFIG_MULTIPLE_TABLES The kernel supports multiple routing tables
|
||||
CONFIG_ALL_TABLES_AT_ONCE Kernel scanner wants to process all tables at once
|
||||
CONFIG_SINGLE_ROUTE There is only one route per network
|
||||
|
||||
CONFIG_MC_PROPER_SRC Multicast packets have source address according to socket saddr field
|
||||
|
|
34
sysdep/cf/bsd-netlink.h
Normal file
34
sysdep/cf/bsd-netlink.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Configuration for FreeBSD based systems with netlink support
|
||||
*
|
||||
* (c) 2022 Alexander Chernikov <melifaro@FreeBSD.org>
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#define CONFIG_AUTO_ROUTES
|
||||
#define CONFIG_SELF_CONSCIOUS
|
||||
#define CONFIG_MULTIPLE_TABLES
|
||||
#define CONFIG_SINGLE_ROUTE
|
||||
|
||||
#define CONFIG_SKIP_MC_BIND
|
||||
#define CONFIG_NO_IFACE_BIND
|
||||
#define CONFIG_USE_HDRINCL
|
||||
|
||||
#define CONFIG_INCLUDE_SYSIO_H "sysdep/bsd/sysio.h"
|
||||
#define CONFIG_INCLUDE_KRTSYS_H "sysdep/linux/krt-sys.h"
|
||||
|
||||
#define CONFIG_FREEBSD_NETLINK
|
||||
|
||||
#ifndef AF_MPLS
|
||||
#define AF_MPLS 39
|
||||
#endif
|
||||
|
||||
#ifndef SO_RCVBUFFORCE
|
||||
#define SO_RCVBUFFORCE SO_RCVBUF
|
||||
#endif
|
||||
|
||||
/*
|
||||
Link: sysdep/unix
|
||||
Link: sysdep/bsd-netlink
|
||||
*/
|
|
@ -9,7 +9,6 @@
|
|||
#define CONFIG_AUTO_ROUTES
|
||||
#define CONFIG_SELF_CONSCIOUS
|
||||
#define CONFIG_MULTIPLE_TABLES
|
||||
#define CONFIG_ALL_TABLES_AT_ONCE
|
||||
#define CONFIG_IP6_SADR_KERNEL
|
||||
|
||||
#define CONFIG_MC_PROPER_SRC
|
||||
|
@ -18,9 +17,12 @@
|
|||
#define CONFIG_INCLUDE_SYSIO_H "sysdep/linux/sysio.h"
|
||||
#define CONFIG_INCLUDE_KRTSYS_H "sysdep/linux/krt-sys.h"
|
||||
|
||||
#define CONFIG_LINUX_NETLINK
|
||||
|
||||
#define CONFIG_RESTRICTED_PRIVILEGES
|
||||
#define CONFIG_INCLUDE_SYSPRIV_H "sysdep/linux/syspriv.h"
|
||||
|
||||
#define CONFIG_MADV_DONTNEED_TO_FREE
|
||||
|
||||
#ifndef AF_MPLS
|
||||
#define AF_MPLS 28
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#ifdef GIT_LABEL
|
||||
#define BIRD_VERSION XSTR1(GIT_LABEL)
|
||||
#else
|
||||
#define BIRD_VERSION "2.0.9"
|
||||
#define BIRD_VERSION "2.0.11"
|
||||
#endif
|
||||
|
||||
/* Include parameters determined by configure script */
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#include <alloca.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
@ -28,10 +27,16 @@
|
|||
#include "lib/hash.h"
|
||||
#include "conf/conf.h"
|
||||
|
||||
#ifdef CONFIG_LINUX_NETLINK
|
||||
#include <asm/types.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#endif
|
||||
#ifdef CONFIG_FREEBSD_NETLINK
|
||||
#include <netlink/netlink.h>
|
||||
#include <netlink/netlink_route.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MPLS_KERNEL
|
||||
#include <linux/lwtunnel.h>
|
||||
|
@ -74,51 +79,16 @@
|
|||
#endif
|
||||
|
||||
#define krt_ipv4(p) ((p)->af == AF_INET)
|
||||
#define krt_ecmp6(p) ((p)->af == AF_INET6)
|
||||
|
||||
const int rt_default_ecmp = 16;
|
||||
|
||||
/*
|
||||
* Structure nl_parse_state keeps state of received route processing. Ideally,
|
||||
* we could just independently parse received Netlink messages and immediately
|
||||
* propagate received routes to the rest of BIRD, but older Linux kernel (before
|
||||
* version 4.11) represents and announces IPv6 ECMP routes not as one route with
|
||||
* multiple next hops (like RTA_MULTIPATH in IPv4 ECMP), but as a sequence of
|
||||
* routes with the same prefix. More recent kernels work as with IPv4.
|
||||
*
|
||||
* Therefore, BIRD keeps currently processed route in nl_parse_state structure
|
||||
* and postpones its propagation until we expect it to be final; i.e., when
|
||||
* non-matching route is received or when the scan ends. When another matching
|
||||
* route is received, it is merged with the already processed route to form an
|
||||
* ECMP route. Note that merging is done only for IPv6 (merge == 1), but the
|
||||
* postponing is done in both cases (for simplicity). All IPv4 routes or IPv6
|
||||
* routes with RTA_MULTIPATH set are just considered non-matching.
|
||||
*
|
||||
* This is ignored for asynchronous notifications (every notification is handled
|
||||
* as a separate route). It is not an issue for our routes, as we ignore such
|
||||
* notifications anyways. But importing alien IPv6 ECMP routes does not work
|
||||
* properly with older kernels.
|
||||
*
|
||||
* Whatever the kernel version is, IPv6 ECMP routes are sent as multiple routes
|
||||
* for the same prefix.
|
||||
*/
|
||||
|
||||
struct nl_parse_state
|
||||
{
|
||||
struct krt_proto *proto;
|
||||
struct linpool *pool;
|
||||
int scan;
|
||||
int merge;
|
||||
|
||||
net *net;
|
||||
rta *attrs;
|
||||
struct krt_proto *proto;
|
||||
s8 new;
|
||||
s8 krt_src;
|
||||
u8 krt_type;
|
||||
u8 krt_proto;
|
||||
u32 krt_metric;
|
||||
|
||||
u32 rta_flow; /* Used during parsing */
|
||||
u32 rta_flow;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -161,16 +131,13 @@ nl_open_sock(struct nl_sock *nl)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
static int
|
||||
nl_set_strict_dump(struct nl_sock *nl UNUSED, int strict UNUSED)
|
||||
{
|
||||
/*
|
||||
* Strict checking is not necessary, it improves behavior on newer kernels.
|
||||
* If it is not available (missing SOL_NETLINK compile-time, or ENOPROTOOPT
|
||||
* run-time), we can just ignore it.
|
||||
*/
|
||||
#ifdef SOL_NETLINK
|
||||
setsockopt(nl->fd, SOL_NETLINK, NETLINK_GET_STRICT_CHK, &strict, sizeof(strict));
|
||||
return setsockopt(nl->fd, SOL_NETLINK, NETLINK_GET_STRICT_CHK, &strict, sizeof(strict));
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -198,10 +165,17 @@ nl_cfg_rx_buffer_size(struct config *cfg)
|
|||
static void
|
||||
nl_open(void)
|
||||
{
|
||||
if ((nl_scan.fd >= 0) && (nl_req.fd >= 0))
|
||||
return;
|
||||
|
||||
nl_open_sock(&nl_scan);
|
||||
nl_open_sock(&nl_req);
|
||||
|
||||
nl_set_strict_dump(&nl_scan, 1);
|
||||
if (nl_set_strict_dump(&nl_scan, 1) < 0)
|
||||
{
|
||||
log(L_WARN "KRT: Netlink strict checking failed, will scan all tables at once");
|
||||
krt_use_shared_scan();
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -256,11 +230,13 @@ nl_request_dump_addr(int af)
|
|||
}
|
||||
|
||||
static void
|
||||
nl_request_dump_route(int af)
|
||||
nl_request_dump_route(int af, int table_id)
|
||||
{
|
||||
struct {
|
||||
struct nlmsghdr nh;
|
||||
struct rtmsg rtm;
|
||||
struct rtattr rta;
|
||||
u32 table_id;
|
||||
} req = {
|
||||
.nh.nlmsg_type = RTM_GETROUTE,
|
||||
.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)),
|
||||
|
@ -269,7 +245,17 @@ nl_request_dump_route(int af)
|
|||
.rtm.rtm_family = af,
|
||||
};
|
||||
|
||||
send(nl_scan.fd, &req, sizeof(req), 0);
|
||||
if (table_id < 256)
|
||||
req.rtm.rtm_table = table_id;
|
||||
else
|
||||
{
|
||||
req.rta.rta_type = RTA_TABLE;
|
||||
req.rta.rta_len = RTA_LENGTH(4);
|
||||
req.table_id = table_id;
|
||||
req.nh.nlmsg_len = NLMSG_ALIGN(req.nh.nlmsg_len) + req.rta.rta_len;
|
||||
}
|
||||
|
||||
send(nl_scan.fd, &req, req.nh.nlmsg_len, 0);
|
||||
nl_scan.last_hdr = NULL;
|
||||
}
|
||||
|
||||
|
@ -831,11 +817,11 @@ nl_parse_multipath(struct nl_parse_state *s, struct krt_proto *p, const net_addr
|
|||
rv->gw = rta_get_via(a[RTA_VIA]);
|
||||
#endif
|
||||
|
||||
if (ipa_nonzero(rv->gw))
|
||||
{
|
||||
if (nh->rtnh_flags & RTNH_F_ONLINK)
|
||||
rv->flags |= RNF_ONLINK;
|
||||
|
||||
if (ipa_nonzero(rv->gw))
|
||||
{
|
||||
neighbor *nbr;
|
||||
nbr = neigh_find(&p->p, rv->gw, rv->iface,
|
||||
(rv->flags & RNF_ONLINK) ? NEF_ONLINK : 0);
|
||||
|
@ -1325,7 +1311,7 @@ nh_bufsize(struct nexthop *nh)
|
|||
}
|
||||
|
||||
static int
|
||||
nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh)
|
||||
nl_send_route(struct krt_proto *p, rte *e, int op)
|
||||
{
|
||||
eattr *ea;
|
||||
net *net = e->net;
|
||||
|
@ -1397,8 +1383,6 @@ nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh)
|
|||
|
||||
if (p->af == AF_MPLS)
|
||||
priority = 0;
|
||||
else if (a->source == RTS_DUMMY)
|
||||
priority = e->u.krt.metric;
|
||||
else if (KRT_CF->sys.metric)
|
||||
priority = KRT_CF->sys.metric;
|
||||
else if ((op != NL_OP_DELETE) && (ea = ea_find(eattrs, EA_KRT_METRIC)))
|
||||
|
@ -1409,15 +1393,17 @@ nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh)
|
|||
|
||||
/* For route delete, we do not specify remaining route attributes */
|
||||
if (op == NL_OP_DELETE)
|
||||
goto dest;
|
||||
goto done;
|
||||
|
||||
/* Default scope is LINK for device routes, UNIVERSE otherwise */
|
||||
if (p->af == AF_MPLS)
|
||||
r->r.rtm_scope = RT_SCOPE_UNIVERSE;
|
||||
else if (ea = ea_find(eattrs, EA_KRT_SCOPE))
|
||||
r->r.rtm_scope = ea->u.data;
|
||||
else if (a->dest == RTD_UNICAST && ipa_zero(a->nh.gw))
|
||||
r->r.rtm_scope = RT_SCOPE_LINK;
|
||||
else
|
||||
r->r.rtm_scope = (dest == RTD_UNICAST && ipa_zero(nh->gw)) ? RT_SCOPE_LINK : RT_SCOPE_UNIVERSE;
|
||||
r->r.rtm_scope = RT_SCOPE_UNIVERSE;
|
||||
|
||||
if (ea = ea_find(eattrs, EA_KRT_PREFSRC))
|
||||
nl_add_attr_ipa(&r->h, rsize, RTA_PREFSRC, *(ip_addr *)ea->u.ptr->data);
|
||||
|
@ -1440,13 +1426,12 @@ nl_send_route(struct krt_proto *p, rte *e, int op, int dest, struct nexthop *nh)
|
|||
if (metrics[0])
|
||||
nl_add_metrics(&r->h, rsize, metrics, KRT_METRICS_MAX);
|
||||
|
||||
|
||||
dest:
|
||||
switch (dest)
|
||||
switch (a->dest)
|
||||
{
|
||||
case RTD_UNICAST:
|
||||
r->r.rtm_type = RTN_UNICAST;
|
||||
if (nh->next && !krt_ecmp6(p))
|
||||
struct nexthop *nh = &(a->nh);
|
||||
if (nh->next)
|
||||
nl_add_multipath(&r->h, rsize, nh, p->af, eattrs);
|
||||
else
|
||||
{
|
||||
|
@ -1472,82 +1457,53 @@ dest:
|
|||
bug("krt_capable inconsistent with nl_send_route");
|
||||
}
|
||||
|
||||
done:
|
||||
/* Ignore missing for DELETE */
|
||||
return nl_exchange(&r->h, (op == NL_OP_DELETE));
|
||||
}
|
||||
|
||||
static inline int
|
||||
nl_add_rte(struct krt_proto *p, rte *e)
|
||||
nl_allow_replace(struct krt_proto *p, rte *new)
|
||||
{
|
||||
rta *a = e->attrs;
|
||||
int err = 0;
|
||||
/*
|
||||
* We use NL_OP_REPLACE for IPv4, it has an issue with not checking for
|
||||
* matching rtm_protocol, but that is OK when dedicated priority is used.
|
||||
*
|
||||
* For IPv6, the NL_OP_REPLACE is still broken even in Linux 4.19 LTS
|
||||
* (although it seems to be fixed in Linux 5.10 LTS) for sequence:
|
||||
*
|
||||
* ip route add 2001:db8::/32 via fe80::1 dev eth0
|
||||
* ip route replace 2001:db8::/32 dev eth0
|
||||
*
|
||||
* (it ends with two routes instead of replacing the first by the second one)
|
||||
*
|
||||
* Replacing with direct and special type (e.g. unreachable) routes does not
|
||||
* work, but replacing with regular routes work reliably
|
||||
*/
|
||||
|
||||
if (krt_ecmp6(p) && a->nh.next)
|
||||
{
|
||||
struct nexthop *nh = &(a->nh);
|
||||
if (krt_ipv4(p))
|
||||
return 1;
|
||||
|
||||
err = nl_send_route(p, e, NL_OP_ADD, RTD_UNICAST, nh);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
for (nh = nh->next; nh; nh = nh->next)
|
||||
err += nl_send_route(p, e, NL_OP_APPEND, RTD_UNICAST, nh);
|
||||
|
||||
return err;
|
||||
rta *a = new->attrs;
|
||||
return (a->dest == RTD_UNICAST) && ipa_nonzero(a->nh.gw);
|
||||
}
|
||||
|
||||
return nl_send_route(p, e, NL_OP_ADD, a->dest, &(a->nh));
|
||||
}
|
||||
|
||||
static inline int
|
||||
nl_delete_rte(struct krt_proto *p, rte *e)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
/* For IPv6, we just repeatedly request DELETE until we get error */
|
||||
do
|
||||
err = nl_send_route(p, e, NL_OP_DELETE, RTD_NONE, NULL);
|
||||
while (krt_ecmp6(p) && !err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline int
|
||||
nl_replace_rte(struct krt_proto *p, rte *e)
|
||||
{
|
||||
rta *a = e->attrs;
|
||||
return nl_send_route(p, e, NL_OP_REPLACE, a->dest, &(a->nh));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
krt_replace_rte(struct krt_proto *p, net *n UNUSED, rte *new, rte *old)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
/*
|
||||
* We use NL_OP_REPLACE for IPv4, it has an issue with not checking for
|
||||
* matching rtm_protocol, but that is OK when dedicated priority is used.
|
||||
*
|
||||
* We do not use NL_OP_REPLACE for IPv6, as it has broken semantics for ECMP
|
||||
* and with some kernel versions ECMP replace crashes kernel. Would need more
|
||||
* testing and checks for kernel versions.
|
||||
*
|
||||
* For IPv6, we use NL_OP_DELETE and then NL_OP_ADD. We also do not trust the
|
||||
* old route value, so we do not try to optimize IPv6 ECMP reconfigurations.
|
||||
*/
|
||||
|
||||
if (krt_ipv4(p) && old && new)
|
||||
if (old && new && nl_allow_replace(p, new))
|
||||
{
|
||||
err = nl_replace_rte(p, new);
|
||||
err = nl_send_route(p, new, NL_OP_REPLACE);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (old)
|
||||
nl_delete_rte(p, old);
|
||||
nl_send_route(p, old, NL_OP_DELETE);
|
||||
|
||||
if (new)
|
||||
err = nl_add_rte(p, new);
|
||||
err = nl_send_route(p, new, NL_OP_ADD);
|
||||
}
|
||||
|
||||
if (new)
|
||||
|
@ -1559,61 +1515,6 @@ krt_replace_rte(struct krt_proto *p, net *n UNUSED, rte *new, rte *old)
|
|||
}
|
||||
}
|
||||
|
||||
static int
|
||||
nl_mergable_route(struct nl_parse_state *s, net *net, struct krt_proto *p, uint priority, uint krt_type, uint rtm_family)
|
||||
{
|
||||
/* Route merging is used for IPv6 scans */
|
||||
if (!s->scan || (rtm_family != AF_INET6))
|
||||
return 0;
|
||||
|
||||
/* Saved and new route must have same network, proto/table, and priority */
|
||||
if ((s->net != net) || (s->proto != p) || (s->krt_metric != priority))
|
||||
return 0;
|
||||
|
||||
/* Both must be regular unicast routes */
|
||||
if ((s->krt_type != RTN_UNICAST) || (krt_type != RTN_UNICAST))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
nl_announce_route(struct nl_parse_state *s)
|
||||
{
|
||||
rte *e = rte_get_temp(s->attrs);
|
||||
e->net = s->net;
|
||||
e->u.krt.src = s->krt_src;
|
||||
e->u.krt.proto = s->krt_proto;
|
||||
e->u.krt.seen = 0;
|
||||
e->u.krt.best = 0;
|
||||
e->u.krt.metric = s->krt_metric;
|
||||
|
||||
if (s->scan)
|
||||
krt_got_route(s->proto, e);
|
||||
else
|
||||
krt_got_route_async(s->proto, e, s->new);
|
||||
|
||||
s->net = NULL;
|
||||
s->attrs = NULL;
|
||||
s->proto = NULL;
|
||||
lp_flush(s->pool);
|
||||
}
|
||||
|
||||
static inline void
|
||||
nl_parse_begin(struct nl_parse_state *s, int scan)
|
||||
{
|
||||
memset(s, 0, sizeof (struct nl_parse_state));
|
||||
s->pool = nl_linpool;
|
||||
s->scan = scan;
|
||||
}
|
||||
|
||||
static inline void
|
||||
nl_parse_end(struct nl_parse_state *s)
|
||||
{
|
||||
if (s->net)
|
||||
nl_announce_route(s);
|
||||
}
|
||||
|
||||
|
||||
#define SKIP0(ARG, ...) do { DBG("KRT: Ignoring route - " ARG, ##__VA_ARGS__); return; } while(0)
|
||||
#define SKIP(ARG, ...) do { DBG("KRT: Ignoring route %N - " ARG, &dst, ##__VA_ARGS__); return; } while(0)
|
||||
|
@ -1751,14 +1652,29 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
|
|||
|
||||
net *net = net_get(p->p.main_channel->table, n);
|
||||
|
||||
if (s->net && !nl_mergable_route(s, net, p, priority, i->rtm_type, i->rtm_family))
|
||||
nl_announce_route(s);
|
||||
|
||||
rta *ra = lp_allocz(s->pool, RTA_MAX_SIZE);
|
||||
ra->src = p->p.main_source;
|
||||
ra->source = RTS_INHERIT;
|
||||
ra->scope = SCOPE_UNIVERSE;
|
||||
|
||||
{
|
||||
ea_list *ea = lp_alloc(s->pool, sizeof(ea_list) + 2 * sizeof(eattr));
|
||||
*ea = (ea_list) { .flags = EALF_SORTED, .count = 2 };
|
||||
ea->next = ra->eattrs;
|
||||
ra->eattrs = ea;
|
||||
|
||||
ea->attrs[0] = (eattr) {
|
||||
.id = EA_KRT_SOURCE,
|
||||
.type = EAF_TYPE_INT,
|
||||
.u.data = i->rtm_protocol
|
||||
};
|
||||
|
||||
ea->attrs[1] = (eattr) {
|
||||
.id = EA_KRT_METRIC,
|
||||
.type = EAF_TYPE_INT,
|
||||
.u.data = priority,
|
||||
};
|
||||
}
|
||||
|
||||
if (a[RTA_FLOW])
|
||||
s->rta_flow = rta_get_u32(a[RTA_FLOW]);
|
||||
else
|
||||
|
@ -1797,6 +1713,9 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
|
|||
ra->nh.gw = rta_get_via(a[RTA_VIA]);
|
||||
#endif
|
||||
|
||||
if (i->rtm_flags & RTNH_F_ONLINK)
|
||||
ra->nh.flags |= RNF_ONLINK;
|
||||
|
||||
if (ipa_nonzero(ra->nh.gw))
|
||||
{
|
||||
/* Silently skip strange 6to4 routes */
|
||||
|
@ -1804,9 +1723,6 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
|
|||
if ((i->rtm_family == AF_INET6) && ipa_in_netX(ra->nh.gw, (net_addr *) &sit))
|
||||
return;
|
||||
|
||||
if (i->rtm_flags & RTNH_F_ONLINK)
|
||||
ra->nh.flags |= RNF_ONLINK;
|
||||
|
||||
neighbor *nbr;
|
||||
nbr = neigh_find(&p->p, ra->nh.gw, ra->nh.iface,
|
||||
(ra->nh.flags & RNF_ONLINK) ? NEF_ONLINK : 0);
|
||||
|
@ -1864,30 +1780,33 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
|
|||
ra->eattrs = ea;
|
||||
ea->flags = EALF_SORTED;
|
||||
ea->count = 1;
|
||||
ea->attrs[0].id = EA_KRT_SCOPE;
|
||||
ea->attrs[0].flags = 0;
|
||||
ea->attrs[0].type = EAF_TYPE_INT;
|
||||
ea->attrs[0].u.data = i->rtm_scope;
|
||||
ea->attrs[0] = (eattr) {
|
||||
.id = EA_KRT_SCOPE,
|
||||
.flags = 0,
|
||||
.type = EAF_TYPE_INT,
|
||||
.u.data = i->rtm_scope,
|
||||
};
|
||||
}
|
||||
|
||||
if (a[RTA_PREFSRC])
|
||||
{
|
||||
ip_addr ps = rta_get_ipa(a[RTA_PREFSRC]);
|
||||
|
||||
struct adata *ad = lp_alloc(s->pool, sizeof(struct adata) + sizeof(ps));
|
||||
ad->length = sizeof(ps);
|
||||
memcpy(ad->data, &ps, sizeof(ps));
|
||||
|
||||
ea_list *ea = lp_alloc(s->pool, sizeof(ea_list) + sizeof(eattr));
|
||||
ea->next = ra->eattrs;
|
||||
ra->eattrs = ea;
|
||||
ea->flags = EALF_SORTED;
|
||||
ea->count = 1;
|
||||
ea->attrs[0].id = EA_KRT_PREFSRC;
|
||||
ea->attrs[0].flags = 0;
|
||||
ea->attrs[0].type = EAF_TYPE_IP_ADDRESS;
|
||||
|
||||
struct adata *ad = lp_alloc(s->pool, sizeof(struct adata) + sizeof(ps));
|
||||
ad->length = sizeof(ps);
|
||||
memcpy(ad->data, &ps, sizeof(ps));
|
||||
|
||||
ea->attrs[0].u.ptr = ad;
|
||||
ea->attrs[0] = (eattr) {
|
||||
.id = EA_KRT_PREFSRC,
|
||||
.flags = 0,
|
||||
.type = EAF_TYPE_IP_ADDRESS,
|
||||
.u.ptr = ad,
|
||||
};
|
||||
}
|
||||
|
||||
/* Can be set per-route or per-nexthop */
|
||||
|
@ -1898,10 +1817,12 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
|
|||
ra->eattrs = ea;
|
||||
ea->flags = EALF_SORTED;
|
||||
ea->count = 1;
|
||||
ea->attrs[0].id = EA_KRT_REALM;
|
||||
ea->attrs[0].flags = 0;
|
||||
ea->attrs[0].type = EAF_TYPE_INT;
|
||||
ea->attrs[0].u.data = s->rta_flow;
|
||||
ea->attrs[0] = (eattr) {
|
||||
.id = EA_KRT_REALM,
|
||||
.flags = 0,
|
||||
.type = EAF_TYPE_INT,
|
||||
.u.data = s->rta_flow,
|
||||
};
|
||||
}
|
||||
|
||||
if (a[RTA_METRICS])
|
||||
|
@ -1918,13 +1839,12 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
|
|||
|
||||
for (t = 1; t < KRT_METRICS_MAX; t++)
|
||||
if (metrics[0] & (1 << t))
|
||||
{
|
||||
ea->attrs[n].id = EA_CODE(PROTOCOL_KERNEL, KRT_METRICS_OFFSET + t);
|
||||
ea->attrs[n].flags = 0;
|
||||
ea->attrs[n].type = EAF_TYPE_INT; /* FIXME: Some are EAF_TYPE_BITFIELD */
|
||||
ea->attrs[n].u.data = metrics[t];
|
||||
n++;
|
||||
}
|
||||
ea->attrs[n++] = (eattr) {
|
||||
.id = EA_CODE(PROTOCOL_KERNEL, KRT_METRICS_OFFSET + t),
|
||||
.flags = 0,
|
||||
.type = EAF_TYPE_INT, /* FIXME: Some are EAF_TYPE_BITFIELD */
|
||||
.u.data = metrics[t],
|
||||
};
|
||||
|
||||
if (n > 0)
|
||||
{
|
||||
|
@ -1935,60 +1855,40 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Ideally, now we would send the received route to the rest of kernel code.
|
||||
* But IPv6 ECMP routes before 4.11 are sent as a sequence of routes, so we
|
||||
* postpone it and merge next hops until the end of the sequence. Note that
|
||||
* when doing merging of next hops, we expect the new route to be unipath.
|
||||
* Otherwise, we ignore additional next hops in nexthop_insert().
|
||||
*/
|
||||
rte *e = rte_get_temp(ra, p->p.main_source);
|
||||
e->net = net;
|
||||
|
||||
if (!s->net)
|
||||
{
|
||||
/* Store the new route */
|
||||
s->net = net;
|
||||
s->attrs = ra;
|
||||
s->proto = p;
|
||||
s->new = new;
|
||||
s->krt_src = krt_src;
|
||||
s->krt_type = i->rtm_type;
|
||||
s->krt_proto = i->rtm_protocol;
|
||||
s->krt_metric = priority;
|
||||
}
|
||||
if (s->scan)
|
||||
krt_got_route(p, e, krt_src);
|
||||
else
|
||||
{
|
||||
/* Merge next hops with the stored route */
|
||||
rta *oa = s->attrs;
|
||||
krt_got_route_async(p, e, new, krt_src);
|
||||
|
||||
struct nexthop *nhs = &oa->nh;
|
||||
nexthop_insert(&nhs, &ra->nh);
|
||||
|
||||
/* Perhaps new nexthop is inserted at the first position */
|
||||
if (nhs == &ra->nh)
|
||||
{
|
||||
/* Swap rtas */
|
||||
s->attrs = ra;
|
||||
|
||||
/* Keep old eattrs */
|
||||
ra->eattrs = oa->eattrs;
|
||||
}
|
||||
}
|
||||
lp_flush(s->pool);
|
||||
}
|
||||
|
||||
void
|
||||
krt_do_scan(struct krt_proto *p UNUSED) /* CONFIG_ALL_TABLES_AT_ONCE => p is NULL */
|
||||
krt_do_scan(struct krt_proto *p)
|
||||
{
|
||||
struct nlmsghdr *h;
|
||||
struct nl_parse_state s;
|
||||
struct nl_parse_state s = {
|
||||
.proto = p,
|
||||
.pool = nl_linpool,
|
||||
.scan = 1,
|
||||
};
|
||||
|
||||
nl_parse_begin(&s, 1);
|
||||
nl_request_dump_route(AF_UNSPEC);
|
||||
/* Table-specific scan or shared scan */
|
||||
if (p)
|
||||
nl_request_dump_route(p->af, krt_table_id(p));
|
||||
else
|
||||
nl_request_dump_route(AF_UNSPEC, 0);
|
||||
|
||||
struct nlmsghdr *h;
|
||||
while (h = nl_get_scan())
|
||||
{
|
||||
if (h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE)
|
||||
nl_parse_route(&s, h);
|
||||
else
|
||||
log(L_DEBUG "nl_scan_fire: Unknown packet received (type=%d)", h->nlmsg_type);
|
||||
nl_parse_end(&s);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2003,16 +1903,18 @@ static struct config *nl_last_config; /* For tracking changes to nl_async_bufsiz
|
|||
static void
|
||||
nl_async_msg(struct nlmsghdr *h)
|
||||
{
|
||||
struct nl_parse_state s;
|
||||
struct nl_parse_state s = {
|
||||
.proto = NULL,
|
||||
.pool = nl_linpool,
|
||||
.scan = 0,
|
||||
};
|
||||
|
||||
switch (h->nlmsg_type)
|
||||
{
|
||||
case RTM_NEWROUTE:
|
||||
case RTM_DELROUTE:
|
||||
DBG("KRT: Received async route notification (%d)\n", h->nlmsg_type);
|
||||
nl_parse_begin(&s, 0);
|
||||
nl_parse_route(&s, h);
|
||||
nl_parse_end(&s);
|
||||
break;
|
||||
case RTM_NEWLINK:
|
||||
case RTM_DELLINK:
|
||||
|
|
|
@ -19,111 +19,229 @@
|
|||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MMAP
|
||||
#define KEEP_PAGES 512
|
||||
long page_size = 0;
|
||||
|
||||
#ifdef HAVE_MMAP
|
||||
#define KEEP_PAGES_MAIN_MAX 256
|
||||
#define KEEP_PAGES_MAIN_MIN 8
|
||||
#define CLEANUP_PAGES_BULK 256
|
||||
|
||||
STATIC_ASSERT(KEEP_PAGES_MAIN_MIN * 4 < KEEP_PAGES_MAIN_MAX);
|
||||
|
||||
static u64 page_size = 0;
|
||||
static _Bool use_fake = 0;
|
||||
|
||||
uint pages_kept = 0;
|
||||
static list pages_list;
|
||||
|
||||
static void cleanup_pages(void *data);
|
||||
static event page_cleanup_event = { .hook = cleanup_pages };
|
||||
|
||||
#if DEBUGGING
|
||||
struct free_page {
|
||||
node unused[42];
|
||||
node n;
|
||||
};
|
||||
#else
|
||||
static const u64 page_size = 4096; /* Fake page size */
|
||||
struct free_page {
|
||||
node n;
|
||||
};
|
||||
#endif
|
||||
|
||||
u64 get_page_size(void)
|
||||
{
|
||||
if (page_size)
|
||||
return page_size;
|
||||
#define EP_POS_MAX ((page_size - OFFSETOF(struct empty_pages, pages)) / sizeof (void *))
|
||||
|
||||
#ifdef HAVE_MMAP
|
||||
if (page_size = sysconf(_SC_PAGESIZE))
|
||||
struct empty_pages {
|
||||
node n;
|
||||
uint pos;
|
||||
void *pages[0];
|
||||
};
|
||||
|
||||
struct free_pages {
|
||||
list pages; /* List of (struct free_page) keeping free pages without releasing them (hot) */
|
||||
list empty; /* List of (struct empty_pages) keeping invalidated pages mapped for us (cold) */
|
||||
u16 min, max; /* Minimal and maximal number of free pages kept */
|
||||
uint cnt; /* Number of free pages in list */
|
||||
event cleanup;
|
||||
};
|
||||
|
||||
static void global_free_pages_cleanup_event(void *);
|
||||
|
||||
static struct free_pages global_free_pages = {
|
||||
.min = KEEP_PAGES_MAIN_MIN,
|
||||
.max = KEEP_PAGES_MAIN_MAX,
|
||||
.cleanup = { .hook = global_free_pages_cleanup_event },
|
||||
};
|
||||
|
||||
uint *pages_kept = &global_free_pages.cnt;
|
||||
|
||||
static void *
|
||||
alloc_sys_page(void)
|
||||
{
|
||||
if ((u64_popcount(page_size) > 1) || (page_size > 16384))
|
||||
{
|
||||
/* Too big or strange page, use the aligned allocator instead */
|
||||
page_size = 4096;
|
||||
use_fake = 1;
|
||||
}
|
||||
return page_size;
|
||||
void *ptr = mmap(NULL, page_size, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
|
||||
if (ptr == MAP_FAILED)
|
||||
bug("mmap(%lu) failed: %m", page_size);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
bug("Page size must be non-zero");
|
||||
extern int shutting_down; /* Shutdown requested. */
|
||||
|
||||
#else // ! HAVE_MMAP
|
||||
#define use_fake 1
|
||||
#endif
|
||||
}
|
||||
|
||||
void *
|
||||
alloc_page(void)
|
||||
{
|
||||
#ifdef HAVE_MMAP
|
||||
if (pages_kept)
|
||||
{
|
||||
node *page = TAIL(pages_list);
|
||||
rem_node(page);
|
||||
pages_kept--;
|
||||
memset(page, 0, get_page_size());
|
||||
return page;
|
||||
}
|
||||
|
||||
if (!use_fake)
|
||||
{
|
||||
void *ret = mmap(NULL, get_page_size(), PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
if (ret == MAP_FAILED)
|
||||
bug("mmap(%lu) failed: %m", (long unsigned int) page_size);
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
/* If the system page allocator is goofy, we use posix_memalign to get aligned blocks of memory. */
|
||||
if (use_fake)
|
||||
{
|
||||
void *ptr = NULL;
|
||||
int err = posix_memalign(&ptr, page_size, page_size);
|
||||
|
||||
if (err || !ptr)
|
||||
bug("posix_memalign(%lu) failed", (long unsigned int) page_size);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
#ifdef HAVE_MMAP
|
||||
struct free_pages *fps = &global_free_pages;
|
||||
|
||||
/* If there is any free page kept hot, we use it. */
|
||||
if (fps->cnt)
|
||||
{
|
||||
struct free_page *fp = SKIP_BACK(struct free_page, n, HEAD(fps->pages));
|
||||
rem_node(&fp->n);
|
||||
|
||||
/* If the hot-free-page cache is getting short, request the cleanup routine to replenish the cache */
|
||||
if ((--fps->cnt < fps->min) && !shutting_down)
|
||||
ev_schedule(&fps->cleanup);
|
||||
|
||||
return fp;
|
||||
}
|
||||
|
||||
/* If there is any free page kept cold, we use that. */
|
||||
if (!EMPTY_LIST(fps->empty))
|
||||
{
|
||||
struct empty_pages *ep = HEAD(fps->empty);
|
||||
|
||||
/* Either the keeper page contains at least one cold page pointer, return that */
|
||||
if (ep->pos)
|
||||
return ep->pages[--ep->pos];
|
||||
|
||||
/* Or the keeper page has no more cold page pointer, return the keeper page */
|
||||
rem_node(&ep->n);
|
||||
return ep;
|
||||
}
|
||||
|
||||
/* And in the worst case, allocate a new page by mmap() */
|
||||
return alloc_sys_page();
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
free_page(void *ptr)
|
||||
{
|
||||
#ifdef HAVE_MMAP
|
||||
if (!use_fake)
|
||||
/* If the system page allocator is goofy, we just free the block and care no more. */
|
||||
if (use_fake)
|
||||
{
|
||||
if (!pages_kept)
|
||||
init_list(&pages_list);
|
||||
|
||||
memset(ptr, 0, sizeof(node));
|
||||
add_tail(&pages_list, ptr);
|
||||
|
||||
if (++pages_kept > KEEP_PAGES)
|
||||
ev_schedule(&page_cleanup_event);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
free(ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef HAVE_MMAP
|
||||
struct free_pages *fps = &global_free_pages;
|
||||
struct free_page *fp = ptr;
|
||||
|
||||
/* Otherwise, we add the free page to the hot-free-page list */
|
||||
fp->n = (node) {};
|
||||
add_tail(&fps->pages, &fp->n);
|
||||
|
||||
/* And if there are too many hot free pages, we ask for page cleanup */
|
||||
if ((++fps->cnt > fps->max) && !shutting_down)
|
||||
ev_schedule(&fps->cleanup);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_MMAP
|
||||
static void
|
||||
cleanup_pages(void *data UNUSED)
|
||||
global_free_pages_cleanup_event(void *data UNUSED)
|
||||
{
|
||||
for (uint seen = 0; (pages_kept > KEEP_PAGES) && (seen < KEEP_PAGES); seen++)
|
||||
/* Cleanup on shutdown is ignored. All pages may be kept hot, OS will take care. */
|
||||
if (shutting_down)
|
||||
return;
|
||||
|
||||
struct free_pages *fps = &global_free_pages;
|
||||
|
||||
/* Cleanup may get called when hot free page cache is short of pages. Replenishing. */
|
||||
while (fps->cnt / 2 < fps->min)
|
||||
{
|
||||
void *ptr = HEAD(pages_list);
|
||||
rem_node(ptr);
|
||||
if (munmap(ptr, get_page_size()) == 0)
|
||||
pages_kept--;
|
||||
else if (errno == ENOMEM)
|
||||
add_tail(&pages_list, ptr);
|
||||
else
|
||||
bug("munmap(%p) failed: %m", ptr);
|
||||
struct free_page *fp = alloc_sys_page();
|
||||
fp->n = (node) {};
|
||||
add_tail(&fps->pages, &fp->n);
|
||||
fps->cnt++;
|
||||
}
|
||||
|
||||
if (pages_kept > KEEP_PAGES)
|
||||
ev_schedule(&page_cleanup_event);
|
||||
/* Or the hot free page cache is too big. Moving some pages to the cold free page cache. */
|
||||
for (int limit = CLEANUP_PAGES_BULK; limit && (fps->cnt > fps->max / 2); fps->cnt--, limit--)
|
||||
{
|
||||
struct free_page *fp = SKIP_BACK(struct free_page, n, TAIL(fps->pages));
|
||||
rem_node(&fp->n);
|
||||
|
||||
/* Empty pages are stored as pointers. To store them, we need a pointer block. */
|
||||
struct empty_pages *ep;
|
||||
if (EMPTY_LIST(fps->empty) || ((ep = HEAD(fps->empty))->pos == EP_POS_MAX))
|
||||
{
|
||||
/* There is either no pointer block or the last block is full. We use this block as a pointer block. */
|
||||
ep = (struct empty_pages *) fp;
|
||||
*ep = (struct empty_pages) {};
|
||||
add_head(&fps->empty, &ep->n);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* We store this block as a pointer into the first free place
|
||||
* and tell the OS that the underlying memory is trash. */
|
||||
ep->pages[ep->pos++] = fp;
|
||||
if (madvise(fp, page_size,
|
||||
#ifdef CONFIG_MADV_DONTNEED_TO_FREE
|
||||
MADV_DONTNEED
|
||||
#else
|
||||
MADV_FREE
|
||||
#endif
|
||||
) < 0)
|
||||
bug("madvise(%p) failed: %m", fp);
|
||||
}
|
||||
}
|
||||
|
||||
/* If the hot free page cleanup hit the limit, re-schedule this routine
|
||||
* to allow for other routines to run. */
|
||||
if (fps->cnt > fps->max)
|
||||
ev_schedule(&fps->cleanup);
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
resource_sys_init(void)
|
||||
{
|
||||
#ifdef HAVE_MMAP
|
||||
ASSERT_DIE(global_free_pages.cnt == 0);
|
||||
|
||||
/* Check what page size the system supports */
|
||||
if (!(page_size = sysconf(_SC_PAGESIZE)))
|
||||
die("System page size must be non-zero");
|
||||
|
||||
if ((u64_popcount(page_size) == 1) && (page_size >= (1 << 10)) && (page_size <= (1 << 18)))
|
||||
{
|
||||
/* We assume that page size has only one bit and is between 1K and 256K (incl.).
|
||||
* Otherwise, the assumptions in lib/slab.c (sl_head's num_full range) aren't met. */
|
||||
|
||||
struct free_pages *fps = &global_free_pages;
|
||||
|
||||
init_list(&fps->pages);
|
||||
init_list(&fps->empty);
|
||||
global_free_pages_cleanup_event(NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Too big or strange page, use the aligned allocator instead */
|
||||
log(L_WARN "Got strange memory page size (%ld), using the aligned allocator instead", (s64) page_size);
|
||||
use_fake = 1;
|
||||
#endif
|
||||
|
||||
page_size = 4096;
|
||||
}
|
||||
|
|
|
@ -1854,8 +1854,8 @@ sk_read_ssh(sock *s)
|
|||
|
||||
/* sk_read() and sk_write() are called from BFD's event loop */
|
||||
|
||||
int
|
||||
sk_read(sock *s, int revents)
|
||||
static inline int
|
||||
sk_read_noflush(sock *s, int revents)
|
||||
{
|
||||
switch (s->type)
|
||||
{
|
||||
|
@ -1918,7 +1918,15 @@ sk_read(sock *s, int revents)
|
|||
}
|
||||
|
||||
int
|
||||
sk_write(sock *s)
|
||||
sk_read(sock *s, int revents)
|
||||
{
|
||||
int e = sk_read_noflush(s, revents);
|
||||
tmp_flush();
|
||||
return e;
|
||||
}
|
||||
|
||||
static inline int
|
||||
sk_write_noflush(sock *s)
|
||||
{
|
||||
switch (s->type)
|
||||
{
|
||||
|
@ -1966,6 +1974,14 @@ sk_write(sock *s)
|
|||
}
|
||||
}
|
||||
|
||||
int
|
||||
sk_write(sock *s)
|
||||
{
|
||||
int e = sk_write_noflush(s);
|
||||
tmp_flush();
|
||||
return e;
|
||||
}
|
||||
|
||||
int sk_is_ipv4(sock *s)
|
||||
{ return s->af == AF_INET; }
|
||||
|
||||
|
@ -1984,6 +2000,7 @@ sk_err(sock *s, int revents)
|
|||
}
|
||||
|
||||
s->err_hook(s, se);
|
||||
tmp_flush();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -2046,8 +2063,8 @@ io_update_time(void)
|
|||
event_open->duration = last_time - event_open->timestamp;
|
||||
|
||||
if (event_open->duration > config->latency_limit)
|
||||
log(L_WARN "Event 0x%p 0x%p took %d ms",
|
||||
event_open->hook, event_open->data, (int) (event_open->duration TO_MS));
|
||||
log(L_WARN "Event 0x%p 0x%p took %u.%03u ms",
|
||||
event_open->hook, event_open->data, (uint) (event_open->duration TO_MS), (uint) (event_open->duration % 1000));
|
||||
|
||||
event_open = NULL;
|
||||
}
|
||||
|
@ -2151,8 +2168,8 @@ watchdog_stop(void)
|
|||
|
||||
btime duration = last_time - loop_time;
|
||||
if (duration > config->watchdog_warning)
|
||||
log(L_WARN "I/O loop cycle took %d ms for %d events",
|
||||
(int) (duration TO_MS), event_log_num);
|
||||
log(L_WARN "I/O loop cycle took %u.%03u ms for %d events",
|
||||
(uint) (duration TO_MS), (uint) (duration % 1000), event_log_num);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -243,6 +243,13 @@ struct protocol proto_unix_iface = {
|
|||
.copy_config = kif_copy_config
|
||||
};
|
||||
|
||||
void
|
||||
kif_build(void)
|
||||
{
|
||||
proto_build(&proto_unix_iface);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Tracing of routes
|
||||
*/
|
||||
|
@ -277,22 +284,23 @@ static struct tbf rl_alien = TBF_DEFAULT_LOG_LIMITS;
|
|||
* the same key.
|
||||
*/
|
||||
|
||||
static inline u32
|
||||
krt_metric(rte *a)
|
||||
{
|
||||
eattr *ea = ea_find(a->attrs->eattrs, EA_KRT_METRIC);
|
||||
return ea ? ea->u.data : 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
krt_same_key(rte *a, rte *b)
|
||||
{
|
||||
return a->u.krt.metric == b->u.krt.metric;
|
||||
return (krt_metric(a) == krt_metric(b));
|
||||
}
|
||||
|
||||
static inline int
|
||||
krt_uptodate(rte *a, rte *b)
|
||||
{
|
||||
if (a->attrs != b->attrs)
|
||||
return 0;
|
||||
|
||||
if (a->u.krt.proto != b->u.krt.proto)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
return (a->attrs == b->attrs);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -300,9 +308,7 @@ krt_learn_announce_update(struct krt_proto *p, rte *e)
|
|||
{
|
||||
net *n = e->net;
|
||||
rta *aa = rta_clone(e->attrs);
|
||||
rte *ee = rte_get_temp(aa);
|
||||
ee->pflags = EA_ID_FLAG(EA_KRT_SOURCE) | EA_ID_FLAG(EA_KRT_METRIC);
|
||||
ee->u.krt = e->u.krt;
|
||||
rte *ee = rte_get_temp(aa, p->p.main_source);
|
||||
rte_update(&p->p, n->n.addr, ee);
|
||||
}
|
||||
|
||||
|
@ -312,6 +318,15 @@ krt_learn_announce_delete(struct krt_proto *p, net *n)
|
|||
rte_update(&p->p, n->n.addr, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
krt_learn_alien_attr(struct channel *c, rte *e)
|
||||
{
|
||||
ASSERT(!e->attrs->cached);
|
||||
e->attrs->pref = c->preference;
|
||||
|
||||
e->attrs = rta_lookup(e->attrs);
|
||||
}
|
||||
|
||||
/* Called when alien route is discovered during scan */
|
||||
static void
|
||||
krt_learn_scan(struct krt_proto *p, rte *e)
|
||||
|
@ -320,7 +335,7 @@ krt_learn_scan(struct krt_proto *p, rte *e)
|
|||
net *n = net_get(p->krt_table, n0->n.addr);
|
||||
rte *m, **mm;
|
||||
|
||||
e->attrs = rta_lookup(e->attrs);
|
||||
krt_learn_alien_attr(p->p.main_channel, e);
|
||||
|
||||
for(mm=&n->routes; m = *mm; mm=&m->next)
|
||||
if (krt_same_key(m, e))
|
||||
|
@ -331,7 +346,7 @@ krt_learn_scan(struct krt_proto *p, rte *e)
|
|||
{
|
||||
krt_trace_in_rl(&rl_alien, p, e, "[alien] seen");
|
||||
rte_free(e);
|
||||
m->u.krt.seen = 1;
|
||||
m->pflags |= KRT_REF_SEEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -347,7 +362,7 @@ krt_learn_scan(struct krt_proto *p, rte *e)
|
|||
{
|
||||
e->next = n->routes;
|
||||
n->routes = e;
|
||||
e->u.krt.seen = 1;
|
||||
e->pflags |= KRT_REF_SEEN;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -377,24 +392,23 @@ again:
|
|||
ee = &n->routes;
|
||||
while (e = *ee)
|
||||
{
|
||||
if (e->u.krt.best)
|
||||
if (e->pflags & KRT_REF_BEST)
|
||||
old_best = e;
|
||||
|
||||
if (!e->u.krt.seen)
|
||||
if (!(e->pflags & KRT_REF_SEEN))
|
||||
{
|
||||
*ee = e->next;
|
||||
rte_free(e);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!best || best->u.krt.metric > e->u.krt.metric)
|
||||
if (!best || krt_metric(best) > krt_metric(e))
|
||||
{
|
||||
best = e;
|
||||
pbest = ee;
|
||||
}
|
||||
|
||||
e->u.krt.seen = 0;
|
||||
e->u.krt.best = 0;
|
||||
e->pflags &= ~(KRT_REF_SEEN | KRT_REF_BEST);
|
||||
ee = &e->next;
|
||||
}
|
||||
if (!n->routes)
|
||||
|
@ -408,18 +422,18 @@ again:
|
|||
goto again;
|
||||
}
|
||||
|
||||
best->u.krt.best = 1;
|
||||
best->pflags |= KRT_REF_BEST;
|
||||
*pbest = best->next;
|
||||
best->next = n->routes;
|
||||
n->routes = best;
|
||||
|
||||
if ((best != old_best) || p->reload)
|
||||
{
|
||||
DBG("%I/%d: announcing (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric);
|
||||
DBG("%I/%d: announcing (metric=%d)\n", n->n.prefix, n->n.pxlen, krt_metric(best));
|
||||
krt_learn_announce_update(p, best);
|
||||
}
|
||||
else
|
||||
DBG("%I/%d: uptodate (metric=%d)\n", n->n.prefix, n->n.pxlen, best->u.krt.metric);
|
||||
DBG("%I/%d: uptodate (metric=%d)\n", n->n.prefix, n->n.pxlen, krt_metric(best));
|
||||
}
|
||||
FIB_ITERATE_END;
|
||||
|
||||
|
@ -433,7 +447,7 @@ krt_learn_async(struct krt_proto *p, rte *e, int new)
|
|||
net *n = net_get(p->krt_table, n0->n.addr);
|
||||
rte *g, **gg, *best, **bestp, *old_best;
|
||||
|
||||
e->attrs = rta_lookup(e->attrs);
|
||||
krt_learn_alien_attr(p->p.main_channel, e);
|
||||
|
||||
old_best = n->routes;
|
||||
for(gg=&n->routes; g = *gg; gg = &g->next)
|
||||
|
@ -476,18 +490,18 @@ krt_learn_async(struct krt_proto *p, rte *e, int new)
|
|||
bestp = &n->routes;
|
||||
for(gg=&n->routes; g=*gg; gg=&g->next)
|
||||
{
|
||||
if (best->u.krt.metric > g->u.krt.metric)
|
||||
if (krt_metric(best) > krt_metric(g))
|
||||
{
|
||||
best = g;
|
||||
bestp = gg;
|
||||
}
|
||||
|
||||
g->u.krt.best = 0;
|
||||
g->pflags &= ~KRT_REF_BEST;
|
||||
}
|
||||
|
||||
if (best)
|
||||
{
|
||||
best->u.krt.best = 1;
|
||||
best->pflags |= KRT_REF_BEST;
|
||||
*bestp = best->next;
|
||||
best->next = n->routes;
|
||||
n->routes = best;
|
||||
|
@ -528,12 +542,6 @@ krt_dump(struct proto *P)
|
|||
rt_dump(p->krt_table);
|
||||
}
|
||||
|
||||
static void
|
||||
krt_dump_attrs(rte *e)
|
||||
{
|
||||
debug(" [m=%d,p=%d]", e->u.krt.metric, e->u.krt.proto);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -582,8 +590,6 @@ krt_export_net(struct krt_proto *p, net *net, rte **rt_free)
|
|||
if (filter == FILTER_REJECT)
|
||||
return NULL;
|
||||
|
||||
rte_make_tmp_attrs(&rt, krt_filter_lp, NULL);
|
||||
|
||||
/* We could run krt_preexport() here, but it is already handled by krt_is_installed() */
|
||||
|
||||
if (filter == FILTER_ACCEPT)
|
||||
|
@ -624,13 +630,14 @@ krt_same_dest(rte *k, rte *e)
|
|||
*/
|
||||
|
||||
void
|
||||
krt_got_route(struct krt_proto *p, rte *e)
|
||||
krt_got_route(struct krt_proto *p, rte *e, s8 src)
|
||||
{
|
||||
rte *new = NULL, *rt_free = NULL;
|
||||
net *n = e->net;
|
||||
e->pflags = 0;
|
||||
|
||||
#ifdef KRT_ALLOW_LEARN
|
||||
switch (e->u.krt.src)
|
||||
switch (src)
|
||||
{
|
||||
case KRT_SRC_KERNEL:
|
||||
goto ignore;
|
||||
|
@ -752,11 +759,12 @@ krt_prune(struct krt_proto *p)
|
|||
}
|
||||
|
||||
void
|
||||
krt_got_route_async(struct krt_proto *p, rte *e, int new)
|
||||
krt_got_route_async(struct krt_proto *p, rte *e, int new, s8 src)
|
||||
{
|
||||
net *net = e->net;
|
||||
e->pflags = 0;
|
||||
|
||||
switch (e->u.krt.src)
|
||||
switch (src)
|
||||
{
|
||||
case KRT_SRC_BIRD:
|
||||
/* Should be filtered by the back end */
|
||||
|
@ -783,18 +791,17 @@ krt_got_route_async(struct krt_proto *p, rte *e, int new)
|
|||
rte_free(e);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Periodic scanning
|
||||
*/
|
||||
|
||||
|
||||
#ifdef CONFIG_ALL_TABLES_AT_ONCE
|
||||
|
||||
static timer *krt_scan_timer;
|
||||
static int krt_scan_count;
|
||||
static timer *krt_scan_all_timer;
|
||||
static int krt_scan_all_count;
|
||||
static _Bool krt_scan_all_tables;
|
||||
|
||||
static void
|
||||
krt_scan(timer *t UNUSED)
|
||||
krt_scan_all(timer *t UNUSED)
|
||||
{
|
||||
struct krt_proto *p;
|
||||
node *n;
|
||||
|
@ -815,35 +822,42 @@ krt_scan(timer *t UNUSED)
|
|||
}
|
||||
|
||||
static void
|
||||
krt_scan_timer_start(struct krt_proto *p)
|
||||
krt_scan_all_timer_start(struct krt_proto *p)
|
||||
{
|
||||
if (!krt_scan_count)
|
||||
krt_scan_timer = tm_new_init(krt_pool, krt_scan, NULL, KRT_CF->scan_time, 0);
|
||||
if (!krt_scan_all_count)
|
||||
krt_scan_all_timer = tm_new_init(krt_pool, krt_scan_all, NULL, KRT_CF->scan_time, 0);
|
||||
|
||||
krt_scan_count++;
|
||||
krt_scan_all_count++;
|
||||
|
||||
tm_start(krt_scan_timer, 1 S);
|
||||
tm_start(krt_scan_all_timer, 1 S);
|
||||
}
|
||||
|
||||
static void
|
||||
krt_scan_timer_stop(struct krt_proto *p UNUSED)
|
||||
krt_scan_all_timer_stop(void)
|
||||
{
|
||||
krt_scan_count--;
|
||||
ASSERT(krt_scan_all_count > 0);
|
||||
|
||||
if (!krt_scan_count)
|
||||
krt_scan_all_count--;
|
||||
|
||||
if (!krt_scan_all_count)
|
||||
{
|
||||
rfree(krt_scan_timer);
|
||||
krt_scan_timer = NULL;
|
||||
rfree(krt_scan_all_timer);
|
||||
krt_scan_all_timer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
krt_scan_timer_kick(struct krt_proto *p UNUSED)
|
||||
krt_scan_all_timer_kick(void)
|
||||
{
|
||||
tm_start(krt_scan_timer, 0);
|
||||
tm_start(krt_scan_all_timer, 0);
|
||||
}
|
||||
|
||||
void
|
||||
krt_use_shared_scan(void)
|
||||
{
|
||||
krt_scan_all_tables = 1;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void
|
||||
krt_scan(timer *t)
|
||||
|
@ -860,55 +874,44 @@ krt_scan(timer *t)
|
|||
|
||||
static void
|
||||
krt_scan_timer_start(struct krt_proto *p)
|
||||
{
|
||||
if (krt_scan_all_tables)
|
||||
krt_scan_all_timer_start(p);
|
||||
else
|
||||
{
|
||||
p->scan_timer = tm_new_init(p->p.pool, krt_scan, p, KRT_CF->scan_time, 0);
|
||||
tm_start(p->scan_timer, 1 S);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
krt_scan_timer_stop(struct krt_proto *p)
|
||||
{
|
||||
if (krt_scan_all_tables)
|
||||
krt_scan_all_timer_stop();
|
||||
else
|
||||
tm_stop(p->scan_timer);
|
||||
}
|
||||
|
||||
static void
|
||||
krt_scan_timer_kick(struct krt_proto *p)
|
||||
{
|
||||
if (krt_scan_all_tables)
|
||||
krt_scan_all_timer_kick();
|
||||
else
|
||||
tm_start(p->scan_timer, 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Updates
|
||||
*/
|
||||
|
||||
static void
|
||||
krt_make_tmp_attrs(struct rte *rt, struct linpool *pool)
|
||||
{
|
||||
rte_init_tmp_attrs(rt, pool, 2);
|
||||
rte_make_tmp_attr(rt, EA_KRT_SOURCE, EAF_TYPE_INT, rt->u.krt.proto);
|
||||
rte_make_tmp_attr(rt, EA_KRT_METRIC, EAF_TYPE_INT, rt->u.krt.metric);
|
||||
}
|
||||
|
||||
static void
|
||||
krt_store_tmp_attrs(struct rte *rt, struct linpool *pool)
|
||||
{
|
||||
rte_init_tmp_attrs(rt, pool, 2);
|
||||
rt->u.krt.proto = rte_store_tmp_attr(rt, EA_KRT_SOURCE);
|
||||
rt->u.krt.metric = rte_store_tmp_attr(rt, EA_KRT_METRIC);
|
||||
}
|
||||
|
||||
static int
|
||||
krt_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
|
||||
krt_preexport(struct channel *C, rte *e)
|
||||
{
|
||||
// struct krt_proto *p = (struct krt_proto *) P;
|
||||
rte *e = *new;
|
||||
|
||||
if (e->attrs->src->proto == P)
|
||||
if (e->src->proto == C->proto)
|
||||
return -1;
|
||||
|
||||
if (!krt_capable(e))
|
||||
|
@ -934,7 +937,7 @@ krt_rt_notify(struct proto *P, struct channel *ch UNUSED, net *net,
|
|||
* kernel, which would remove the new imported route instead.
|
||||
*/
|
||||
rte *best = net->routes;
|
||||
if (!new && best && (best->attrs->src->proto == P))
|
||||
if (!new && best && (best->src->proto == P))
|
||||
return;
|
||||
#endif
|
||||
|
||||
|
@ -983,14 +986,6 @@ krt_feed_end(struct channel *C)
|
|||
}
|
||||
|
||||
|
||||
static int
|
||||
krt_rte_same(rte *a, rte *b)
|
||||
{
|
||||
/* src is always KRT_SRC_ALIEN and type is irrelevant */
|
||||
return (a->u.krt.proto == b->u.krt.proto) && (a->u.krt.metric == b->u.krt.metric);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Protocol glue
|
||||
*/
|
||||
|
@ -1016,11 +1011,6 @@ krt_postconfig(struct proto_config *CF)
|
|||
if (! proto_cf_main_channel(CF))
|
||||
cf_error("Channel not specified");
|
||||
|
||||
#ifdef CONFIG_ALL_TABLES_AT_ONCE
|
||||
if (krt_cf->scan_time != cf->scan_time)
|
||||
cf_error("All kernel syncers must use the same table scan interval");
|
||||
#endif
|
||||
|
||||
struct channel_config *cc = proto_cf_main_channel(CF);
|
||||
struct rtable_config *tab = cc->table;
|
||||
if (tab->krt_attached)
|
||||
|
@ -1049,9 +1039,6 @@ krt_init(struct proto_config *CF)
|
|||
p->p.if_notify = krt_if_notify;
|
||||
p->p.reload_routes = krt_reload_routes;
|
||||
p->p.feed_end = krt_feed_end;
|
||||
p->p.make_tmp_attrs = krt_make_tmp_attrs;
|
||||
p->p.store_tmp_attrs = krt_store_tmp_attrs;
|
||||
p->p.rte_same = krt_rte_same;
|
||||
|
||||
krt_sys_init(p);
|
||||
return &p->p;
|
||||
|
@ -1209,6 +1196,11 @@ struct protocol proto_unix_kernel = {
|
|||
.get_attr = krt_get_attr,
|
||||
#ifdef KRT_ALLOW_LEARN
|
||||
.dump = krt_dump,
|
||||
.dump_attrs = krt_dump_attrs,
|
||||
#endif
|
||||
};
|
||||
|
||||
void
|
||||
krt_build(void)
|
||||
{
|
||||
proto_build(&proto_unix_kernel);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,9 @@ struct kif_proto;
|
|||
#define EA_KRT_SOURCE EA_CODE(PROTOCOL_KERNEL, 0)
|
||||
#define EA_KRT_METRIC EA_CODE(PROTOCOL_KERNEL, 1)
|
||||
|
||||
#define KRT_REF_SEEN 0x1 /* Seen in table */
|
||||
#define KRT_REF_BEST 0x2 /* Best in table */
|
||||
|
||||
/* Whenever we recognize our own routes, we allow learing of foreign routes */
|
||||
|
||||
#ifdef CONFIG_SELF_CONSCIOUS
|
||||
|
@ -52,10 +55,7 @@ struct krt_proto {
|
|||
struct rtable *krt_table; /* Internal table of inherited routes */
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_ALL_TABLES_AT_ONCE
|
||||
timer *scan_timer;
|
||||
#endif
|
||||
|
||||
struct bmap sync_map; /* Keeps track which exported routes were successfully written to kernel */
|
||||
struct bmap seen_map; /* Routes seen during last periodic scan */
|
||||
node krt_node; /* Node in krt_proto_list */
|
||||
|
@ -76,8 +76,9 @@ extern pool *krt_pool;
|
|||
|
||||
struct proto_config * kif_init_config(int class);
|
||||
void kif_request_scan(void);
|
||||
void krt_got_route(struct krt_proto *p, struct rte *e);
|
||||
void krt_got_route_async(struct krt_proto *p, struct rte *e, int new);
|
||||
void krt_use_shared_scan(void);
|
||||
void krt_got_route(struct krt_proto *p, struct rte *e, s8 src);
|
||||
void krt_got_route_async(struct krt_proto *p, struct rte *e, int new, s8 src);
|
||||
|
||||
static inline int
|
||||
krt_get_sync_error(struct krt_proto *p, struct rte *e)
|
||||
|
|
|
@ -309,22 +309,15 @@ die(const char *msg, ...)
|
|||
void
|
||||
debug(const char *msg, ...)
|
||||
{
|
||||
#define MAX_DEBUG_BUFSIZE 65536
|
||||
#define MAX_DEBUG_BUFSIZE 16384
|
||||
va_list args;
|
||||
static uint bufsize = 4096;
|
||||
static char *buf = NULL;
|
||||
|
||||
if (!buf)
|
||||
buf = mb_alloc(&root_pool, bufsize);
|
||||
char buf[MAX_DEBUG_BUFSIZE];
|
||||
|
||||
va_start(args, msg);
|
||||
if (dbgf)
|
||||
{
|
||||
while (bvsnprintf(buf, bufsize, msg, args) < 0)
|
||||
if (bufsize >= MAX_DEBUG_BUFSIZE)
|
||||
if (bvsnprintf(buf, MAX_DEBUG_BUFSIZE, msg, args) < 0)
|
||||
bug("Extremely long debug output, split it.");
|
||||
else
|
||||
buf = mb_realloc(buf, (bufsize *= 2));
|
||||
|
||||
fputs(buf, dbgf);
|
||||
}
|
||||
|
|
|
@ -116,7 +116,7 @@ add_num_const(char *name, int val, const char *file, const uint line)
|
|||
struct f_val *v = cfg_alloc(sizeof(struct f_val));
|
||||
*v = (struct f_val) { .type = T_INT, .val.i = val };
|
||||
struct symbol *sym = cf_get_symbol(name);
|
||||
if (sym->class && (sym->scope == conf_this_scope))
|
||||
if (sym->class && cf_symbol_is_local(sym))
|
||||
cf_error("Error reading value for %s from %s:%d: already defined", name, file, line);
|
||||
|
||||
cf_define_symbol(sym, SYM_CONSTANT | T_INT, val, v);
|
||||
|
@ -242,6 +242,8 @@ async_config(void)
|
|||
{
|
||||
struct config *conf;
|
||||
|
||||
config_free_old();
|
||||
|
||||
log(L_INFO "Reconfiguration requested by SIGHUP");
|
||||
if (!unix_read_config(&conf, config_name))
|
||||
{
|
||||
|
@ -280,6 +282,9 @@ cmd_read_config(const char *name)
|
|||
void
|
||||
cmd_check_config(const char *name)
|
||||
{
|
||||
if (cli_access_restricted())
|
||||
return;
|
||||
|
||||
struct config *conf = cmd_read_config(name);
|
||||
if (!conf)
|
||||
return;
|
||||
|
@ -324,6 +329,8 @@ cmd_reconfig(const char *name, int type, uint timeout)
|
|||
if (cli_access_restricted())
|
||||
return;
|
||||
|
||||
config_free_old();
|
||||
|
||||
struct config *conf = cmd_read_config(name);
|
||||
if (!conf)
|
||||
return;
|
||||
|
@ -434,7 +441,7 @@ int
|
|||
cli_get_command(cli *c)
|
||||
{
|
||||
sock *s = c->priv;
|
||||
byte *t = c->rx_aux ? : s->rbuf;
|
||||
byte *t = s->rbuf;
|
||||
byte *tend = s->rpos;
|
||||
byte *d = c->rx_pos;
|
||||
byte *dend = c->rx_buf + CLI_RX_BUF_SIZE - 2;
|
||||
|
@ -445,16 +452,22 @@ cli_get_command(cli *c)
|
|||
t++;
|
||||
else if (*t == '\n')
|
||||
{
|
||||
t++;
|
||||
c->rx_pos = c->rx_buf;
|
||||
c->rx_aux = t;
|
||||
*d = 0;
|
||||
t++;
|
||||
|
||||
/* Move remaining data and reset pointers */
|
||||
uint rest = (t < tend) ? (tend - t) : 0;
|
||||
memmove(s->rbuf, t, rest);
|
||||
s->rpos = s->rbuf + rest;
|
||||
c->rx_pos = c->rx_buf;
|
||||
|
||||
return (d < dend) ? 1 : -1;
|
||||
}
|
||||
else if (d < dend)
|
||||
*d++ = *t++;
|
||||
}
|
||||
c->rx_aux = s->rpos = s->rbuf;
|
||||
|
||||
s->rpos = s->rbuf;
|
||||
c->rx_pos = d;
|
||||
return 0;
|
||||
}
|
||||
|
@ -479,6 +492,14 @@ cli_err(sock *s, int err)
|
|||
cli_free(s->data);
|
||||
}
|
||||
|
||||
static void
|
||||
cli_connect_err(sock *s UNUSED, int err)
|
||||
{
|
||||
ASSERT_DIE(err);
|
||||
if (config->cli_debug)
|
||||
log(L_INFO "Failed to accept CLI connection: %s", strerror(err));
|
||||
}
|
||||
|
||||
static int
|
||||
cli_connect(sock *s, uint size UNUSED)
|
||||
{
|
||||
|
@ -493,7 +514,6 @@ cli_connect(sock *s, uint size UNUSED)
|
|||
s->pool = c->pool; /* We need to have all the socket buffers allocated in the cli pool */
|
||||
s->fast_rx = 1;
|
||||
c->rx_pos = c->rx_buf;
|
||||
c->rx_aux = NULL;
|
||||
rmove(s, c->pool);
|
||||
return 1;
|
||||
}
|
||||
|
@ -507,6 +527,7 @@ cli_init_unix(uid_t use_uid, gid_t use_gid)
|
|||
s = cli_sk = sk_new(cli_pool);
|
||||
s->type = SK_UNIX_PASSIVE;
|
||||
s->rx_hook = cli_connect;
|
||||
s->err_hook = cli_connect_err;
|
||||
s->rbsize = 1024;
|
||||
s->fast_rx = 1;
|
||||
|
||||
|
@ -897,8 +918,6 @@ main(int argc, char **argv)
|
|||
open_pid_file();
|
||||
|
||||
protos_build();
|
||||
proto_build(&proto_unix_kernel);
|
||||
proto_build(&proto_unix_iface);
|
||||
|
||||
struct config *conf = read_config();
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "test/birdtest.h"
|
||||
#include "lib/string.h"
|
||||
#include "lib/event.h"
|
||||
|
||||
#ifdef HAVE_EXECINFO_H
|
||||
#include <execinfo.h>
|
||||
|
@ -119,6 +120,9 @@ bt_init(int argc, char *argv[])
|
|||
clock_gettime(CLOCK_MONOTONIC, &bt_begin);
|
||||
bt_suite_case_begin = bt_suite_begin = bt_begin;
|
||||
|
||||
resource_init();
|
||||
ev_init_list(&global_event_list);
|
||||
|
||||
return;
|
||||
|
||||
usage:
|
||||
|
@ -172,6 +176,8 @@ int bt_run_test_fn(int (*fn)(const void *), const void *fn_arg, int timeout)
|
|||
if (!bt_suite_result)
|
||||
result = 0;
|
||||
|
||||
tmp_flush();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue