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 |
111 changed files with 3791 additions and 2045 deletions
285
.gitlab-ci.yml
285
.gitlab-ci.yml
|
@ -5,9 +5,10 @@ variables:
|
||||||
DOCKER_CMD: docker --config="$HOME/.docker/$CI_JOB_ID/"
|
DOCKER_CMD: docker --config="$HOME/.docker/$CI_JOB_ID/"
|
||||||
IMG_BASE: registry.nic.cz/labs/bird
|
IMG_BASE: registry.nic.cz/labs/bird
|
||||||
TOOLS_DIR: /var/lib/gitlab-runner/bird-tools
|
TOOLS_DIR: /var/lib/gitlab-runner/bird-tools
|
||||||
|
STAYRTR_BINARY: /var/lib/gitlab-runner/stayrtr-0.1-108-g8d18a41-linux-x86_64
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- image
|
# - image
|
||||||
- build
|
- build
|
||||||
- pkg
|
- pkg
|
||||||
- test
|
- test
|
||||||
|
@ -32,185 +33,12 @@ stages:
|
||||||
# That's Docker in Docker
|
# That's Docker in Docker
|
||||||
- dind
|
- dind
|
||||||
|
|
||||||
docker_debian-8-amd64:
|
# Docker build example
|
||||||
variables:
|
#docker_debian-11-amd64:
|
||||||
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:
|
|
||||||
# variables:
|
# variables:
|
||||||
# IMG_NAME: "debian-11-i386"
|
# IMG_NAME: "debian-11-amd64"
|
||||||
# <<: *docker_build
|
# <<: *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
|
.build: &build-base
|
||||||
stage: build
|
stage: build
|
||||||
|
@ -360,17 +188,17 @@ build-opensuse-15.3-amd64:
|
||||||
<<: *build-linux
|
<<: *build-linux
|
||||||
image: registry.nic.cz/labs/bird:opensuse-15.3-amd64
|
image: registry.nic.cz/labs/bird:opensuse-15.3-amd64
|
||||||
|
|
||||||
build-freebsd-11-amd64:
|
#build-freebsd-11-amd64:
|
||||||
<<: *build-base
|
# <<: *build-base
|
||||||
tags:
|
# tags:
|
||||||
- freebsd
|
# - freebsd
|
||||||
- amd64
|
# - amd64
|
||||||
|
|
||||||
build-freebsd-11-i386:
|
#build-freebsd-11-i386:
|
||||||
<<: *build-base
|
# <<: *build-base
|
||||||
tags:
|
# tags:
|
||||||
- freebsd
|
# - freebsd
|
||||||
- i386
|
# - i386
|
||||||
|
|
||||||
|
|
||||||
.pkg-deb: &pkg-deb
|
.pkg-deb: &pkg-deb
|
||||||
|
@ -416,13 +244,11 @@ build-freebsd-11-i386:
|
||||||
# needs: [build-debian-8-i386]
|
# needs: [build-debian-8-i386]
|
||||||
# image: registry.nic.cz/labs/bird:debian-8-i386
|
# image: registry.nic.cz/labs/bird:debian-8-i386
|
||||||
|
|
||||||
# Dpkg error: PATH is not set
|
|
||||||
pkg-debian-9-amd64:
|
pkg-debian-9-amd64:
|
||||||
<<: *pkg-deb
|
<<: *pkg-deb
|
||||||
needs: [build-debian-9-amd64]
|
needs: [build-debian-9-amd64]
|
||||||
image: registry.nic.cz/labs/bird:debian-9-amd64
|
image: registry.nic.cz/labs/bird:debian-9-amd64
|
||||||
|
|
||||||
# Dpkg error: PATH is not set
|
|
||||||
pkg-debian-9-i386:
|
pkg-debian-9-i386:
|
||||||
<<: *pkg-deb
|
<<: *pkg-deb
|
||||||
needs: [build-debian-9-i386]
|
needs: [build-debian-9-i386]
|
||||||
|
@ -531,6 +357,7 @@ build-birdlab:
|
||||||
- sudo git clean -fx
|
- sudo git clean -fx
|
||||||
- git pull --ff-only
|
- git pull --ff-only
|
||||||
- mv $DIR/bird $DIR/birdc netlab/common
|
- mv $DIR/bird $DIR/birdc netlab/common
|
||||||
|
- ln -s $STAYRTR_BINARY netlab/common/stayrtr
|
||||||
|
|
||||||
.test: &test-base
|
.test: &test-base
|
||||||
stage: test
|
stage: test
|
||||||
|
@ -541,7 +368,7 @@ build-birdlab:
|
||||||
script:
|
script:
|
||||||
- cd $TOOLS_DIR/netlab
|
- cd $TOOLS_DIR/netlab
|
||||||
- sudo ./stop
|
- sudo ./stop
|
||||||
- sudo ./runtest -m check $TEST_NAME
|
- sudo ./runtest -s v2 -m check $TEST_NAME
|
||||||
|
|
||||||
test-ospf-base:
|
test-ospf-base:
|
||||||
<<: *test-base
|
<<: *test-base
|
||||||
|
@ -613,6 +440,16 @@ test-bgp-merged:
|
||||||
variables:
|
variables:
|
||||||
TEST_NAME: cf-bgp-merged
|
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-ebgp-loop:
|
||||||
<<: *test-base
|
<<: *test-base
|
||||||
variables:
|
variables:
|
||||||
|
@ -623,12 +460,32 @@ test-ebgp-star:
|
||||||
variables:
|
variables:
|
||||||
TEST_NAME: cf-ebgp-star
|
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-ibgp-loop:
|
||||||
<<: *test-base
|
<<: *test-base
|
||||||
variables:
|
variables:
|
||||||
TEST_NAME: cf-ibgp-loop
|
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
|
<<: *test-base
|
||||||
variables:
|
variables:
|
||||||
TEST_NAME: cf-ibgp-flat
|
TEST_NAME: cf-ibgp-flat
|
||||||
|
@ -647,3 +504,49 @@ test-rip-base:
|
||||||
<<: *test-base
|
<<: *test-base
|
||||||
variables:
|
variables:
|
||||||
TEST_NAME: cf-rip-base
|
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@)
|
client=$(addprefix $(exedir)/,@CLIENT@)
|
||||||
daemon=$(exedir)/bird
|
daemon=$(exedir)/bird
|
||||||
protocols=@protocols@
|
protocols=@protocols@
|
||||||
|
PROTO_BUILD := $(protocols) dev kif krt
|
||||||
|
|
||||||
prefix=@prefix@
|
prefix=@prefix@
|
||||||
exec_prefix=@exec_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)
|
Version 2.0.9 (2022-02-09)
|
||||||
o BGP: Flowspec validation procedure
|
o BGP: Flowspec validation procedure
|
||||||
o Babel: MAC authentication support
|
o Babel: MAC authentication support
|
||||||
|
|
|
@ -153,7 +153,7 @@ submit_init_command(char *cmd_raw)
|
||||||
if (!cmd)
|
if (!cmd)
|
||||||
{
|
{
|
||||||
cleanup();
|
cleanup();
|
||||||
exit(0);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
submit_server_command(cmd);
|
submit_server_command(cmd);
|
||||||
|
|
|
@ -574,6 +574,8 @@ check_eof(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void cf_swap_soft_scope(void);
|
||||||
|
|
||||||
static struct symbol *
|
static struct symbol *
|
||||||
cf_new_symbol(const byte *c)
|
cf_new_symbol(const byte *c)
|
||||||
{
|
{
|
||||||
|
@ -583,6 +585,8 @@ cf_new_symbol(const byte *c)
|
||||||
if (l > SYM_MAX_LEN)
|
if (l > SYM_MAX_LEN)
|
||||||
cf_error("Symbol too long");
|
cf_error("Symbol too long");
|
||||||
|
|
||||||
|
cf_swap_soft_scope();
|
||||||
|
|
||||||
s = cfg_allocz(sizeof(struct symbol) + l + 1);
|
s = cfg_allocz(sizeof(struct symbol) + l + 1);
|
||||||
*s = (struct symbol) { .scope = conf_this_scope, .class = SYM_VOID, };
|
*s = (struct symbol) { .scope = conf_this_scope, .class = SYM_VOID, };
|
||||||
strcpy(s->name, c);
|
strcpy(s->name, c);
|
||||||
|
@ -654,10 +658,10 @@ cf_localize_symbol(struct symbol *sym)
|
||||||
/* If the symbol type is void, it has been recently allocated just in this scope. */
|
/* If the symbol type is void, it has been recently allocated just in this scope. */
|
||||||
if (!sym->class)
|
if (!sym->class)
|
||||||
return sym;
|
return sym;
|
||||||
|
|
||||||
/* If the scope is the current, it is already defined in this scope. */
|
/* If the scope is the current, it is already defined in this scope. */
|
||||||
if (sym->scope == conf_this_scope)
|
if (cf_symbol_is_local(sym))
|
||||||
cf_error("Symbol already defined");
|
cf_error("Symbol '%s' already defined", sym->name);
|
||||||
|
|
||||||
/* Not allocated here yet, doing it now. */
|
/* Not allocated here yet, doing it now. */
|
||||||
return cf_new_symbol(sym->name);
|
return cf_new_symbol(sym->name);
|
||||||
|
@ -713,7 +717,7 @@ cf_lex_symbol(const char *data)
|
||||||
static void
|
static void
|
||||||
cf_lex_init_kh(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;
|
struct keyword *k;
|
||||||
for (k=keyword_list; k->name; k++)
|
for (k=keyword_list; k->name; k++)
|
||||||
|
@ -787,12 +791,60 @@ cf_push_scope(struct symbol *sym)
|
||||||
void
|
void
|
||||||
cf_pop_scope(void)
|
cf_pop_scope(void)
|
||||||
{
|
{
|
||||||
|
ASSERT(!conf_this_scope->soft_scopes);
|
||||||
|
|
||||||
conf_this_scope->active = 0;
|
conf_this_scope->active = 0;
|
||||||
conf_this_scope = conf_this_scope->next;
|
conf_this_scope = conf_this_scope->next;
|
||||||
|
|
||||||
ASSERT(conf_this_scope);
|
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
|
* cf_symbol_class_name - get name of a symbol class
|
||||||
* @sym: symbol
|
* @sym: symbol
|
||||||
|
|
27
conf/conf.c
27
conf/conf.c
|
@ -61,6 +61,7 @@
|
||||||
static jmp_buf conf_jmpbuf;
|
static jmp_buf conf_jmpbuf;
|
||||||
|
|
||||||
struct config *config, *new_config;
|
struct config *config, *new_config;
|
||||||
|
pool *config_pool;
|
||||||
|
|
||||||
static struct config *old_config; /* Old configuration */
|
static struct config *old_config; /* Old configuration */
|
||||||
static struct config *future_config; /* New config held here if recon requested during recon */
|
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 *
|
struct config *
|
||||||
config_alloc(const char *name)
|
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);
|
linpool *l = lp_new_default(p);
|
||||||
struct config *c = lp_allocz(l, sizeof(struct config));
|
struct config *c = lp_allocz(l, sizeof(struct config));
|
||||||
|
|
||||||
|
@ -140,6 +141,7 @@ config_parse(struct config *c)
|
||||||
protos_preconfig(c);
|
protos_preconfig(c);
|
||||||
rt_preconfig(c);
|
rt_preconfig(c);
|
||||||
cf_parse();
|
cf_parse();
|
||||||
|
rt_postconfig(c);
|
||||||
|
|
||||||
if (EMPTY_LIST(c->protos))
|
if (EMPTY_LIST(c->protos))
|
||||||
cf_error("No protocol is specified in the config file");
|
cf_error("No protocol is specified in the config file");
|
||||||
|
@ -199,6 +201,23 @@ config_free(struct config *c)
|
||||||
rfree(c->pool);
|
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
|
void
|
||||||
config_add_obstacle(struct config *c)
|
config_add_obstacle(struct config *c)
|
||||||
{
|
{
|
||||||
|
@ -490,10 +509,12 @@ config_timeout(timer *t UNUSED)
|
||||||
void
|
void
|
||||||
config_init(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_event->hook = config_done;
|
||||||
|
|
||||||
config_timer = tm_new(&root_pool);
|
config_timer = tm_new(config_pool);
|
||||||
config_timer->hook = config_timeout;
|
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 config_parse(struct config *);
|
||||||
int cli_parse(struct config *);
|
int cli_parse(struct config *);
|
||||||
void config_free(struct config *);
|
void config_free(struct config *);
|
||||||
|
void config_free_old(void);
|
||||||
int config_commit(struct config *, int type, uint timeout);
|
int config_commit(struct config *, int type, uint timeout);
|
||||||
int config_confirm(void);
|
int config_confirm(void);
|
||||||
int config_undo(void);
|
int config_undo(void);
|
||||||
|
@ -96,7 +97,7 @@ void order_shutdown(int gr);
|
||||||
|
|
||||||
|
|
||||||
/* Pools */
|
/* Pools */
|
||||||
|
extern pool *config_pool;
|
||||||
extern linpool *cfg_mem;
|
extern linpool *cfg_mem;
|
||||||
|
|
||||||
#define cfg_alloc(size) lp_alloc(cfg_mem, size)
|
#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 sym_scope *next; /* Next on scope stack */
|
||||||
struct symbol *name; /* Name of this scope */
|
struct symbol *name; /* Name of this scope */
|
||||||
uint slots; /* Variable slots */
|
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 {
|
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_default_name(char *template, int *counter);
|
||||||
struct symbol *cf_localize_symbol(struct symbol *sym);
|
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
|
* cf_define_symbol - define meaning of a symbol
|
||||||
* @sym: symbol to be defined
|
* @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_push_scope(struct symbol *);
|
||||||
void cf_pop_scope(void);
|
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);
|
char *cf_symbol_class_name(struct symbol *sym);
|
||||||
|
|
||||||
/* Parser */
|
/* Parser */
|
||||||
|
|
|
@ -75,6 +75,7 @@ CF_DECLS
|
||||||
struct f_static_attr fsa;
|
struct f_static_attr fsa;
|
||||||
struct f_lval flv;
|
struct f_lval flv;
|
||||||
struct f_line *fl;
|
struct f_line *fl;
|
||||||
|
struct f_arg *fa;
|
||||||
const struct filter *f;
|
const struct filter *f;
|
||||||
struct f_tree *e;
|
struct f_tree *e;
|
||||||
struct f_trie *trie;
|
struct f_trie *trie;
|
||||||
|
@ -110,7 +111,7 @@ CF_DECLS
|
||||||
%type <i> expr bool pxlen4
|
%type <i> expr bool pxlen4
|
||||||
%type <time> expr_us time
|
%type <time> expr_us time
|
||||||
%type <a> ipa
|
%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 <net_ptr> net_ net_any net_vpn4_ net_vpn6_ net_vpn_ net_roa4_ net_roa6_ net_roa_ net_ip6_sadr_ net_mpls_
|
||||||
%type <mls> label_stack_start label_stack
|
%type <mls> label_stack_start label_stack
|
||||||
|
|
||||||
|
@ -152,14 +153,14 @@ conf: definition ;
|
||||||
definition:
|
definition:
|
||||||
DEFINE symbol '=' term ';' {
|
DEFINE symbol '=' term ';' {
|
||||||
struct f_val *val = cfg_allocz(sizeof(struct f_val));
|
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);
|
cf_define_symbol($2, SYM_CONSTANT | val->type, val, val);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
expr:
|
expr:
|
||||||
NUM
|
NUM
|
||||||
| '(' term ')' { $$ = f_eval_int(f_linearize($2)); }
|
| '(' term ')' { $$ = f_eval_int(f_linearize($2, 1)); }
|
||||||
| CF_SYM_KNOWN {
|
| CF_SYM_KNOWN {
|
||||||
if ($1->class != (SYM_CONSTANT | T_INT)) cf_error("Number constant expected");
|
if ($1->class != (SYM_CONSTANT | T_INT)) cf_error("Number constant expected");
|
||||||
$$ = SYM_VAL($1).i; }
|
$$ = SYM_VAL($1).i; }
|
||||||
|
@ -303,6 +304,15 @@ net_:
|
||||||
|
|
||||||
/* Networks - regular */
|
/* 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:
|
||||||
net_ip6_
|
net_ip6_
|
||||||
| CF_SYM_KNOWN {
|
| CF_SYM_KNOWN {
|
||||||
|
|
|
@ -142,7 +142,7 @@ flow_frag_opts:
|
||||||
;
|
;
|
||||||
|
|
||||||
flow4_item:
|
flow4_item:
|
||||||
flow_srcdst net_ip {
|
flow_srcdst net_ip4 {
|
||||||
flow_builder_set_type(this_flow, $1);
|
flow_builder_set_type(this_flow, $1);
|
||||||
flow_builder4_add_pfx(this_flow, (net_addr_ip4 *) &($2));
|
flow_builder4_add_pfx(this_flow, (net_addr_ip4 *) &($2));
|
||||||
}
|
}
|
||||||
|
@ -180,7 +180,7 @@ flow6_opts:
|
||||||
flow_builder_init:
|
flow_builder_init:
|
||||||
{
|
{
|
||||||
if (this_flow == NULL)
|
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
|
else
|
||||||
flow_builder_clear(this_flow);
|
flow_builder_clear(this_flow);
|
||||||
};
|
};
|
||||||
|
|
|
@ -238,6 +238,8 @@ else
|
||||||
;;
|
;;
|
||||||
openbsd*)
|
openbsd*)
|
||||||
sysdesc=bsd
|
sysdesc=bsd
|
||||||
|
CPPFLAGS="$CPPFLAGS -I/usr/local/include"
|
||||||
|
LDFLAGS="$LDFLAGS -L/usr/local/lib"
|
||||||
;;
|
;;
|
||||||
dragonfly*)
|
dragonfly*)
|
||||||
sysdesc=bsd
|
sysdesc=bsd
|
||||||
|
|
211
doc/bird.sgml
211
doc/bird.sgml
|
@ -684,6 +684,21 @@ to set options.
|
||||||
limit to the settle time from the initial ROA table change even if
|
limit to the settle time from the initial ROA table change even if
|
||||||
there are consecutive updates gradually renewing the settle time.
|
there are consecutive updates gradually renewing the settle time.
|
||||||
Default: 20 s.
|
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>
|
</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
|
Show the list of symbols defined in the configuration (names of
|
||||||
protocols, routing tables etc.).
|
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
|
Show contents of specified routing tables, that is routes, their metrics
|
||||||
and (in case the <cf/all/ switch is given) all their attributes.
|
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
|
Last, the set of default tables is used: <cf/master4/, <cf/master6/ and
|
||||||
each first table of any other network type.
|
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
|
<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/ }
|
a given filter (<cf>filter <m/name/</cf> or <cf>filter { <m/filter/ }
|
||||||
</cf> or matching a given condition (<cf>where <m/condition/</cf>).
|
</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
|
restarted otherwise. Changes in filters usually lead to restart of
|
||||||
affected protocols.
|
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
|
If <cf/soft/ option is used, changes in filters does not cause BIRD to
|
||||||
restart affected protocols, therefore already accepted routes (according
|
restart affected protocols, therefore already accepted routes (according
|
||||||
to old filters) would be still propagated, but new routes would be
|
to old filters) would be still propagated, but new routes would be
|
||||||
|
@ -1245,8 +1270,8 @@ this:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
filter not_too_far
|
filter not_too_far
|
||||||
int var;
|
|
||||||
{
|
{
|
||||||
|
int var;
|
||||||
if defined( rip_metric ) then
|
if defined( rip_metric ) then
|
||||||
var = rip_metric;
|
var = rip_metric;
|
||||||
else {
|
else {
|
||||||
|
@ -1275,9 +1300,9 @@ local variables. Recursion is not allowed. Function definitions look like this:
|
||||||
|
|
||||||
<code>
|
<code>
|
||||||
function name ()
|
function name ()
|
||||||
int local_variable;
|
|
||||||
{
|
{
|
||||||
local_variable = 5;
|
int local_variable;
|
||||||
|
int another_variable = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
function with_parameters (int parameter)
|
function with_parameters (int parameter)
|
||||||
|
@ -1286,16 +1311,19 @@ function with_parameters (int parameter)
|
||||||
}
|
}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<p>Unlike in C, variables are declared after the <cf/function/ line, but before
|
<p>Like in C programming language, variables are declared inside function body,
|
||||||
the first <cf/{/. You can't declare variables in nested blocks. Functions are
|
either at the beginning, or mixed with other statements. Declarations may
|
||||||
called like in C: <cf>name(); with_parameters(5);</cf>. Function may return
|
contain initialization. You can also declare variables in nested blocks, such
|
||||||
values using the <cf>return <m/[expr]/</cf> command. Returning a value exits
|
variables have scope restricted to such block. There is a deprecated syntax to
|
||||||
from current function (this is similar to C).
|
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
|
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
|
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.
|
filter, the route is rejected.
|
||||||
|
|
||||||
<p>A nice trick to debug filters is to use <cf>show route filter <m/name/</cf>
|
<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
|
<sect>Control structures
|
||||||
<label id="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/;
|
<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/;
|
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
|
omitted. If the <cf><m>boolean expression</m></cf> is true, <m/commandT/ is
|
||||||
executed, otherwise <m/commandF/ is executed.
|
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
|
<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/ ; [
|
<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
|
... ] }</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:
|
<p>Here is example that uses <cf/if/ and <cf/case/ structures:
|
||||||
|
|
||||||
<code>
|
<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 {
|
case arg1 {
|
||||||
2: print "two"; print "I can do more commands without {}";
|
2: print "two"; print "I can do more commands without {}";
|
||||||
3 .. 5: print "three to five";
|
3 .. 5: print "three to five";
|
||||||
else: print "something else";
|
else: print "something else";
|
||||||
}
|
}
|
||||||
|
|
||||||
if 1234 = i then printn "."; else {
|
|
||||||
print "not 1234";
|
|
||||||
print "You need {} around multiple commands";
|
|
||||||
}
|
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
|
||||||
|
@ -1747,7 +1789,7 @@ Common route attributes are:
|
||||||
|
|
||||||
<tag><label id="rta-source"><m/enum/ source</tag>
|
<tag><label id="rta-source"><m/enum/ source</tag>
|
||||||
what protocol has told me about this route. Possible values:
|
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_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/.
|
<cf/RTS_OSPF_EXT2/, <cf/RTS_BGP/, <cf/RTS_PIPE/, <cf/RTS_BABEL/.
|
||||||
|
|
||||||
|
@ -1865,8 +1907,9 @@ protocol babel [<name>] {
|
||||||
ipv4 { <channel config> };
|
ipv4 { <channel config> };
|
||||||
ipv6 [sadr] { <channel config> };
|
ipv6 [sadr] { <channel config> };
|
||||||
randomize router id <switch>;
|
randomize router id <switch>;
|
||||||
|
metric decay <time>;
|
||||||
interface <interface pattern> {
|
interface <interface pattern> {
|
||||||
type <wired|wireless>;
|
type <wired|wireless|tunnel>;
|
||||||
rxcost <number>;
|
rxcost <number>;
|
||||||
limit <number>;
|
limit <number>;
|
||||||
hello interval <time>;
|
hello interval <time>;
|
||||||
|
@ -1879,6 +1922,11 @@ protocol babel [<name>] {
|
||||||
check link <switch>;
|
check link <switch>;
|
||||||
next hop ipv4 <address>;
|
next hop ipv4 <address>;
|
||||||
next hop ipv6 <address>;
|
next hop ipv6 <address>;
|
||||||
|
rtt cost <number>;
|
||||||
|
rtt min <time>;
|
||||||
|
rtt max <time>;
|
||||||
|
rtt winlen <number>;
|
||||||
|
send timestamps <switch>;
|
||||||
authentication none|mac [permissive];
|
authentication none|mac [permissive];
|
||||||
password "<text>";
|
password "<text>";
|
||||||
password "<text>" {
|
password "<text>" {
|
||||||
|
@ -1909,15 +1957,27 @@ protocol babel [<name>] {
|
||||||
router ID every time it starts up, which avoids this problem at the cost
|
router ID every time it starts up, which avoids this problem at the cost
|
||||||
of not having stable router IDs in the network. Default: no.
|
of not having stable router IDs in the network. Default: no.
|
||||||
|
|
||||||
<tag><label id="babel-type">type wired|wireless </tag>
|
<tag><label id="babel-metric-decay">metric decay <m/time/ s</tag>
|
||||||
This option specifies the interface type: Wired or wireless. On wired
|
The metric smoothing decay time. When route metrics vary (because of
|
||||||
interfaces a neighbor is considered unreachable after a small number of
|
varying quality of a wireless link, or varying RTT when timestamps are
|
||||||
Hello packets are lost, as described by <cf/limit/ option. On wireless
|
enabled), Babel applies an exponential smoothing procedure to the metric
|
||||||
|
to dampen route oscillations. This parameter specifies the half-life of
|
||||||
|
the convergence of the smoothed metric to the actual metric of the route.
|
||||||
|
I.e., the distance between the smoothed and the actual metric will be
|
||||||
|
halfed for each time period specified here (until they converge). Set to 0
|
||||||
|
to disable metric smoothing; if set, the value must be in the interval
|
||||||
|
1-180 s. Default: 4 s
|
||||||
|
|
||||||
|
<tag><label id="babel-type">type wired|wireless|tunnel </tag>
|
||||||
|
This option specifies the interface type: Wired, wireless or tunnel. On
|
||||||
|
wired interfaces a neighbor is considered unreachable after a small number
|
||||||
|
of Hello packets are lost, as described by <cf/limit/ option. On wireless
|
||||||
interfaces the ETX link quality estimation technique is used to compute
|
interfaces the ETX link quality estimation technique is used to compute
|
||||||
the metrics of routes discovered over this interface. This technique will
|
the metrics of routes discovered over this interface. This technique will
|
||||||
gradually degrade the metric of routes when packets are lost rather than
|
gradually degrade the metric of routes when packets are lost rather than
|
||||||
the more binary up/down mechanism of wired type links. Default:
|
the more binary up/down mechanism of wired type links. A tunnel is like a
|
||||||
<cf/wired/.
|
wired interface, but turns on RTT-based metrics with a default cost of 96.
|
||||||
|
Default: <cf/wired/.
|
||||||
|
|
||||||
<tag><label id="babel-rxcost">rxcost <m/num/</tag>
|
<tag><label id="babel-rxcost">rxcost <m/num/</tag>
|
||||||
This option specifies the nominal RX cost of the interface. The effective
|
This option specifies the nominal RX cost of the interface. The effective
|
||||||
|
@ -1983,6 +2043,37 @@ protocol babel [<name>] {
|
||||||
source for Babel packets will be used. In normal operation, it should not
|
source for Babel packets will be used. In normal operation, it should not
|
||||||
be necessary to set this option.
|
be necessary to set this option.
|
||||||
|
|
||||||
|
<tag><label id="babel-rtt-cost">rtt cost <m/number/</tag>
|
||||||
|
The RTT-based cost that will be applied to all routes from each neighbour
|
||||||
|
based on the measured RTT to that neighbour. If this value is set,
|
||||||
|
timestamps will be included in generated Babel Hello and IHU messages, and
|
||||||
|
(if the neighbours also have timestamps enabled), the RTT to each
|
||||||
|
neighbour will be computed. An additional cost is added to a neighbour if
|
||||||
|
its RTT is above the <ref id="babel-rtt-min" name="rtt min"> value
|
||||||
|
configured on the interface. The added cost scales linearly from 0 up to
|
||||||
|
the RTT cost configured in this option; the full cost is applied if the
|
||||||
|
neighbour RTT reaches the RTT configured in the <ref id="babel-rtt-max"
|
||||||
|
name="rtt max"> option (and for all RTTs above this value). Default: 0
|
||||||
|
(disabled), except for tunnel interfaces, where it is 96.
|
||||||
|
|
||||||
|
<tag><label id="babel-rtt-min">rtt min <m/time/ s|ms</tag>
|
||||||
|
The minimum RTT above which the RTT cost will start to be applied (scaling
|
||||||
|
linearly from zero up to the full cost). Default: 10 ms
|
||||||
|
|
||||||
|
<tag><label id="babel-rtt-max">rtt max <m/time/ s|ms</tag>
|
||||||
|
The maximum RTT above which the full RTT cost will start be applied.
|
||||||
|
Default: 120 ms
|
||||||
|
|
||||||
|
<tag><label id="babel-rtt-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.
|
||||||
|
Sending the timestamps enables peers to calculate an RTT to this node,
|
||||||
|
even if no RTT cost is applied to the route metrics. Default: yes.
|
||||||
|
|
||||||
<tag><label id="babel-authentication">authentication none|mac [permissive]</tag>
|
<tag><label id="babel-authentication">authentication none|mac [permissive]</tag>
|
||||||
Selects authentication method to be used. <cf/none/ means that packets
|
Selects authentication method to be used. <cf/none/ means that packets
|
||||||
are not authenticated at all, <cf/mac/ means MAC authentication is
|
are not authenticated at all, <cf/mac/ means MAC authentication is
|
||||||
|
@ -2345,6 +2436,7 @@ avoid routing loops.
|
||||||
<item> <rfc id="8203"> - BGP Administrative Shutdown Communication
|
<item> <rfc id="8203"> - BGP Administrative Shutdown Communication
|
||||||
<item> <rfc id="8212"> - Default EBGP Route Propagation Behavior without Policies
|
<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="9117"> - Revised Validation Procedure for BGP Flow Specifications
|
||||||
|
<item> <rfc id="9234"> - Route Leak Prevention and Detection Using Roles
|
||||||
</itemize>
|
</itemize>
|
||||||
|
|
||||||
<sect1>Route selection rules
|
<sect1>Route selection rules
|
||||||
|
@ -2707,9 +2799,16 @@ using the following configuration parameters:
|
||||||
|
|
||||||
<tag><label id="bgp-hold-time">hold time <m/number/</tag>
|
<tag><label id="bgp-hold-time">hold time <m/number/</tag>
|
||||||
Time in seconds to wait for a Keepalive message from the other side
|
Time in seconds to wait for a Keepalive message from the other side
|
||||||
before considering the connection stale. Default: depends on agreement
|
before considering the connection stale. The effective value is
|
||||||
with the neighboring router, we prefer 240 seconds if the other side is
|
negotiated during session establishment and it is a minimum of this
|
||||||
willing to accept it.
|
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>
|
<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
|
Value of the hold timer used before the routers have a chance to exchange
|
||||||
|
@ -2717,8 +2816,15 @@ using the following configuration parameters:
|
||||||
|
|
||||||
<tag><label id="bgp-keepalive-time">keepalive time <m/number/</tag>
|
<tag><label id="bgp-keepalive-time">keepalive time <m/number/</tag>
|
||||||
Delay in seconds between sending of two consecutive Keepalive messages.
|
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.
|
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>
|
<tag><label id="bgp-connect-delay-time">connect delay time <m/number/</tag>
|
||||||
Delay in seconds between protocol startup and the first attempt to
|
Delay in seconds between protocol startup and the first attempt to
|
||||||
connect. Default: 5 seconds.
|
connect. Default: 5 seconds.
|
||||||
|
@ -2785,6 +2891,29 @@ using the following configuration parameters:
|
||||||
protocol itself (for example, if a route is received through eBGP and
|
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
|
therefore does not have such attribute). Default: 100 (0 in pre-1.2.0
|
||||||
versions of BIRD).
|
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>
|
</descrip>
|
||||||
|
|
||||||
<sect1>Channel configuration
|
<sect1>Channel configuration
|
||||||
|
@ -2855,6 +2984,20 @@ be used in explicit configuration.
|
||||||
BGP session (if acceptable), or the preferred address of an associated
|
BGP session (if acceptable), or the preferred address of an associated
|
||||||
interface.
|
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>
|
<tag><label id="bgp-gateway">gateway direct|recursive</tag>
|
||||||
For received routes, their <cf/gw/ (immediate next hop) attribute is
|
For received routes, their <cf/gw/ (immediate next hop) attribute is
|
||||||
computed from received <cf/bgp_next_hop/ attribute. This option
|
computed from received <cf/bgp_next_hop/ attribute. This option
|
||||||
|
@ -2888,6 +3031,13 @@ be used in explicit configuration.
|
||||||
be examined later by <cf/show route/, and can be used to reconfigure
|
be examined later by <cf/show route/, and can be used to reconfigure
|
||||||
import filters without full route refresh. Default: off.
|
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>
|
<tag><label id="bgp-export-table">export table <m/switch/</tag>
|
||||||
A BGP export table contains all routes sent to given BGP neighbor, after
|
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
|
application of export filters. It is also called <em/Adj-RIB-Out/ in BGP
|
||||||
|
@ -3092,6 +3242,11 @@ some of them (marked with `<tt/O/') are optional.
|
||||||
This attribute contains accumulated IGP metric, which is a total
|
This attribute contains accumulated IGP metric, which is a total
|
||||||
distance to the destination through multiple autonomous systems.
|
distance to the destination through multiple autonomous systems.
|
||||||
Currently, the attribute is not accessible from filters.
|
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>
|
</descrip>
|
||||||
|
|
||||||
<sect1>Example
|
<sect1>Example
|
||||||
|
|
193
filter/config.Y
193
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) \
|
#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)
|
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
|
* Sets and their items are during parsing handled as lists, linked
|
||||||
* through left ptr. The first item in a list also contains a pointer
|
* 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);
|
setter = f_new_inst(FI_VAR_SET, expr, lval->sym);
|
||||||
getter = f_new_inst(FI_VAR_GET, lval->sym);
|
getter = f_new_inst(FI_VAR_GET, lval->sym);
|
||||||
break;
|
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:
|
case F_LVAL_SA:
|
||||||
setter = f_new_inst(FI_RTA_SET, expr, lval->sa);
|
setter = f_new_inst(FI_RTA_SET, expr, lval->sa);
|
||||||
getter = f_new_inst(FI_RTA_GET, lval->sa);
|
getter = f_new_inst(FI_RTA_GET, lval->sa);
|
||||||
|
@ -266,7 +292,7 @@ assert_assign(struct f_lval *lval, struct f_inst *expr, const char *start, const
|
||||||
|
|
||||||
checker = f_new_inst(FI_EQ, expr, getter);
|
checker = f_new_inst(FI_EQ, expr, getter);
|
||||||
setter->next = checker;
|
setter->next = checker;
|
||||||
|
|
||||||
return assert_done(setter, start, end);
|
return assert_done(setter, start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,6 +303,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
||||||
INT, BOOL, IP, TYPE, PREFIX, RD, PAIR, QUAD, EC, LC,
|
INT, BOOL, IP, TYPE, PREFIX, RD, PAIR, QUAD, EC, LC,
|
||||||
SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
|
SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
|
||||||
IF, THEN, ELSE, CASE,
|
IF, THEN, ELSE, CASE,
|
||||||
|
FOR, IN, DO,
|
||||||
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
|
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
|
||||||
FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS,
|
FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS,
|
||||||
PREFERENCE,
|
PREFERENCE,
|
||||||
|
@ -296,21 +323,23 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
||||||
%nonassoc ELSE
|
%nonassoc ELSE
|
||||||
|
|
||||||
%type <xp> cmds_int cmd_prep
|
%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 <fda> dynamic_attr
|
||||||
%type <fsa> static_attr
|
%type <fsa> static_attr
|
||||||
%type <f> filter where_filter
|
%type <f> filter where_filter
|
||||||
%type <fl> filter_body function_body
|
%type <fl> filter_body function_body
|
||||||
%type <flv> lvalue
|
%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 <ecs> ec_kind
|
||||||
%type <fret> break_command
|
%type <fret> break_command
|
||||||
%type <i32> cnum
|
%type <i32> cnum
|
||||||
%type <e> pair_item ec_item lc_item set_item switch_item set_items switch_items switch_body
|
%type <e> pair_item ec_item lc_item set_item switch_item set_items switch_items switch_body
|
||||||
%type <trie> fprefix_set
|
%type <trie> fprefix_set
|
||||||
%type <v> set_atom switch_atom fipa
|
%type <v> set_atom switch_atom fipa
|
||||||
%type <px> fprefix
|
%type <px> fprefix
|
||||||
%type <t> get_cf_position
|
%type <t> get_cf_position
|
||||||
|
%type <s> for_var
|
||||||
|
|
||||||
CF_GRAMMAR
|
CF_GRAMMAR
|
||||||
|
|
||||||
|
@ -328,7 +357,7 @@ filter_def:
|
||||||
|
|
||||||
conf: filter_eval ;
|
conf: filter_eval ;
|
||||||
filter_eval:
|
filter_eval:
|
||||||
EVAL term { f_eval_int(f_linearize($2)); }
|
EVAL term { f_eval_int(f_linearize($2, 1)); }
|
||||||
;
|
;
|
||||||
|
|
||||||
conf: custom_attr ;
|
conf: custom_attr ;
|
||||||
|
@ -403,25 +432,28 @@ type:
|
||||||
;
|
;
|
||||||
|
|
||||||
function_argsn:
|
function_argsn:
|
||||||
/* EMPTY */
|
/* EMPTY */ { $$ = NULL; }
|
||||||
| function_argsn type symbol ';' {
|
| function_argsn type symbol ';' {
|
||||||
if ($3->scope->slots >= 0xfe) cf_error("Too many declarations, at most 255 allowed");
|
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:
|
function_args:
|
||||||
'(' ')' { $$ = 0; }
|
'(' ')' { $$ = NULL; }
|
||||||
| '(' function_argsn type symbol ')' {
|
| '(' function_argsn type symbol ')' {
|
||||||
cf_define_symbol($4, SYM_VARIABLE | $3, offset, $4->scope->slots++);
|
$$ = cfg_alloc(sizeof(struct f_arg));
|
||||||
$$ = $4->scope->slots;
|
$$->arg = cf_define_symbol($4, SYM_VARIABLE | $3, offset, sym_->scope->slots++);
|
||||||
|
$$->next = $2;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
function_vars:
|
function_vars:
|
||||||
/* EMPTY */ { $$ = 0; }
|
/* EMPTY */ { $$ = 0; }
|
||||||
| function_vars type symbol ';' {
|
| 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;
|
$$ = $1 + 1;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
@ -433,10 +465,12 @@ filter:
|
||||||
cf_assert_symbol($1, SYM_FILTER);
|
cf_assert_symbol($1, SYM_FILTER);
|
||||||
$$ = $1->filter;
|
$$ = $1->filter;
|
||||||
}
|
}
|
||||||
| filter_body {
|
| { cf_push_scope(NULL); } filter_body {
|
||||||
struct filter *f = cfg_alloc(sizeof(struct filter));
|
struct filter *f = cfg_alloc(sizeof(struct filter));
|
||||||
*f = (struct filter) { .root = $1 };
|
*f = (struct filter) { .root = $2 };
|
||||||
$$ = f;
|
$$ = f;
|
||||||
|
|
||||||
|
cf_pop_scope();
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -449,20 +483,35 @@ where_filter:
|
||||||
|
|
||||||
function_body:
|
function_body:
|
||||||
function_vars '{' cmds '}' {
|
function_vars '{' cmds '}' {
|
||||||
$$ = f_linearize($3);
|
$$ = f_linearize($3, 0);
|
||||||
$$->vars = $1;
|
$$->vars = $1;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
conf: function_def ;
|
conf: function_def ;
|
||||||
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);
|
$2 = cf_define_symbol($2, SYM_FUNCTION, function, NULL);
|
||||||
cf_push_scope($2);
|
cf_push_scope($2);
|
||||||
} function_args function_body {
|
} function_args {
|
||||||
DBG("Definition of function %s with %u args and %u local vars.\n", $2->name, $4, $5->vars);
|
/* Make dummy f_line for storing function prototype */
|
||||||
$5->args = $4;
|
struct f_line *dummy = cfg_allocz(sizeof(struct f_line));
|
||||||
$2->function = $5;
|
$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();
|
cf_pop_scope();
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
@ -473,7 +522,11 @@ cmds: /* EMPTY */ { $$ = NULL; }
|
||||||
| cmds_int { $$ = $1.begin; }
|
| 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;
|
$$.begin = $$.end = $1;
|
||||||
if ($1)
|
if ($1)
|
||||||
while ($$.end->next)
|
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
|
* Complex types, their bison value is struct f_val
|
||||||
*/
|
*/
|
||||||
|
@ -527,7 +571,7 @@ set_atom:
|
||||||
| VPN_RD { $$.type = T_RD; $$.val.ec = $1; }
|
| VPN_RD { $$.type = T_RD; $$.val.ec = $1; }
|
||||||
| ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
|
| ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
|
||||||
| '(' term ')' {
|
| '(' 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");
|
if (!f_valid_set_type($$.type)) cf_error("Set-incompatible type");
|
||||||
}
|
}
|
||||||
| CF_SYM_KNOWN {
|
| CF_SYM_KNOWN {
|
||||||
|
@ -539,13 +583,13 @@ set_atom:
|
||||||
|
|
||||||
switch_atom:
|
switch_atom:
|
||||||
NUM { $$.type = T_INT; $$.val.i = $1; }
|
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; }
|
| fipa { $$ = $1; }
|
||||||
| ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
|
| ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
|
||||||
;
|
;
|
||||||
|
|
||||||
cnum:
|
cnum:
|
||||||
term { $$ = f_eval_int(f_linearize($1)); }
|
term { $$ = f_eval_int(f_linearize($1, 1)); }
|
||||||
|
|
||||||
pair_item:
|
pair_item:
|
||||||
'(' cnum ',' cnum ')' { $$ = f_new_pair_item($2, $2, $4, $4); }
|
'(' cnum ',' cnum ')' { $$ = f_new_pair_item($2, $2, $4, $4); }
|
||||||
|
@ -629,19 +673,19 @@ fprefix_set:
|
||||||
;
|
;
|
||||||
|
|
||||||
switch_body: /* EMPTY */ { $$ = NULL; }
|
switch_body: /* EMPTY */ { $$ = NULL; }
|
||||||
| switch_body switch_items ':' cmds {
|
| switch_body switch_items ':' cmds_scoped {
|
||||||
/* Fill data fields */
|
/* Fill data fields */
|
||||||
struct f_tree *t;
|
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)
|
for (t = $2; t; t = t->left)
|
||||||
t->data = line;
|
t->data = line;
|
||||||
$$ = f_merge_items($1, $2);
|
$$ = f_merge_items($1, $2);
|
||||||
}
|
}
|
||||||
| switch_body ELSECOL cmds {
|
| switch_body ELSECOL cmds_scoped {
|
||||||
struct f_tree *t = f_new_tree();
|
struct f_tree *t = f_new_tree();
|
||||||
t->from.type = t->to.type = T_VOID;
|
t->from.type = t->to.type = T_VOID;
|
||||||
t->right = t;
|
t->right = t;
|
||||||
t->data = f_linearize($3);
|
t->data = f_linearize($3, 0);
|
||||||
$$ = f_merge_items($1, t);
|
$$ = f_merge_items($1, t);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
@ -658,6 +702,7 @@ bgp_path:
|
||||||
bgp_path_tail:
|
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 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; }
|
| 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 {
|
| '[' set_items ']' bgp_path_tail {
|
||||||
if ($2->from.type != T_INT) cf_error("Only integer sets allowed in path mask");
|
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;
|
$$ = 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); }
|
| fipa { $$ = f_new_inst(FI_CONSTANT, $1); }
|
||||||
| VPN_RD { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_RD, .val.ec = $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, }); }
|
| 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 ']' {
|
| '[' set_items ']' {
|
||||||
DBG( "We've got a set here..." );
|
DBG( "We've got a set here..." );
|
||||||
$$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_SET, .val.t = build_tree($2), });
|
$$ = 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; }
|
| var_list ',' term { $$ = $3; $$->next = $1; }
|
||||||
|
|
||||||
function_call:
|
function_call:
|
||||||
CF_SYM_KNOWN '(' var_list ')' {
|
CF_SYM_KNOWN '(' var_list ')'
|
||||||
|
{
|
||||||
if ($1->class != SYM_FUNCTION)
|
if ($1->class != SYM_FUNCTION)
|
||||||
cf_error("You can't call something which is not a function. Really.");
|
cf_error("You can't call something which is not a function. Really.");
|
||||||
|
|
||||||
struct f_inst *fc = f_new_inst(FI_CALL, $1);
|
/* Revert the var_list */
|
||||||
uint args = 0;
|
struct f_inst *args = NULL;
|
||||||
while ($3) {
|
while ($3) {
|
||||||
args++;
|
struct f_inst *tmp = $3;
|
||||||
struct f_inst *tmp = $3->next;
|
$3 = $3->next;
|
||||||
$3->next = fc;
|
|
||||||
|
|
||||||
fc = $3;
|
tmp->next = args;
|
||||||
$3 = tmp;
|
args = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args != $1->function->args)
|
$$ = f_new_inst(FI_CALL, args, $1);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -753,6 +794,7 @@ static_attr:
|
||||||
| IFNAME { $$ = f_new_static_attr(T_STRING, SA_IFNAME, 0); }
|
| IFNAME { $$ = f_new_static_attr(T_STRING, SA_IFNAME, 0); }
|
||||||
| IFINDEX { $$ = f_new_static_attr(T_INT, SA_IFINDEX, 1); }
|
| IFINDEX { $$ = f_new_static_attr(T_INT, SA_IFINDEX, 1); }
|
||||||
| WEIGHT { $$ = f_new_static_attr(T_INT, SA_WEIGHT, 0); }
|
| 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); }
|
| GW_MPLS { $$ = f_new_static_attr(T_INT, SA_GW_MPLS, 0); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
@ -779,8 +821,6 @@ term:
|
||||||
| constant { $$ = $1; }
|
| constant { $$ = $1; }
|
||||||
| constructor { $$ = $1; }
|
| constructor { $$ = $1; }
|
||||||
|
|
||||||
| PREFERENCE { $$ = f_new_inst(FI_PREF_GET); }
|
|
||||||
|
|
||||||
| static_attr { $$ = f_new_inst(FI_RTA_GET, $1); }
|
| static_attr { $$ = f_new_inst(FI_RTA_GET, $1); }
|
||||||
|
|
||||||
| dynamic_attr { $$ = f_new_inst(FI_EA_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:
|
cmd:
|
||||||
IF term THEN block {
|
'{' cmds_scoped '}' {
|
||||||
|
$$ = $2;
|
||||||
|
}
|
||||||
|
| IF term THEN cmd {
|
||||||
$$ = f_new_inst(FI_CONDITION, $2, $4, NULL);
|
$$ = 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);
|
$$ = 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 ';' {
|
| CF_SYM_KNOWN '=' term ';' {
|
||||||
switch ($1->class) {
|
switch ($1->class) {
|
||||||
case SYM_VARIABLE_RANGE:
|
case SYM_VARIABLE_RANGE:
|
||||||
|
@ -878,9 +949,6 @@ cmd:
|
||||||
cf_error( "This static attribute is read-only.");
|
cf_error( "This static attribute is read-only.");
|
||||||
$$ = f_new_inst(FI_RTA_SET, $3, $1);
|
$$ = f_new_inst(FI_RTA_SET, $3, $1);
|
||||||
}
|
}
|
||||||
| PREFERENCE '=' term ';' {
|
|
||||||
$$ = f_new_inst(FI_PREF_SET, $3);
|
|
||||||
}
|
|
||||||
| UNSET '(' dynamic_attr ')' ';' {
|
| UNSET '(' dynamic_attr ')' ';' {
|
||||||
$$ = f_new_inst(FI_EA_UNSET, $3);
|
$$ = f_new_inst(FI_EA_UNSET, $3);
|
||||||
}
|
}
|
||||||
|
@ -902,7 +970,7 @@ cmd:
|
||||||
| PRINTN print_list ';' {
|
| PRINTN print_list ';' {
|
||||||
$$ = f_new_inst(FI_PRINT, $2);
|
$$ = f_new_inst(FI_PRINT, $2);
|
||||||
}
|
}
|
||||||
| function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); }
|
| function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); }
|
||||||
| CASE term '{' switch_body '}' {
|
| CASE term '{' switch_body '}' {
|
||||||
$$ = f_new_inst(FI_SWITCH, $2, build_tree($4));
|
$$ = f_new_inst(FI_SWITCH, $2, build_tree($4));
|
||||||
}
|
}
|
||||||
|
@ -923,7 +991,6 @@ get_cf_position:
|
||||||
|
|
||||||
lvalue:
|
lvalue:
|
||||||
CF_SYM_KNOWN { cf_assert_symbol($1, SYM_VARIABLE); $$ = (struct f_lval) { .type = F_LVAL_VARIABLE, .sym = $1 }; }
|
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 }; }
|
| static_attr { $$ = (struct f_lval) { .type = F_LVAL_SA, .sa = $1 }; }
|
||||||
| dynamic_attr { $$ = (struct f_lval) { .type = F_LVAL_EA, .da = $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)
|
f_type_element_type(enum f_type t)
|
||||||
{
|
{
|
||||||
switch(t) {
|
switch(t) {
|
||||||
|
case T_PATH: return T_INT;
|
||||||
case T_CLIST: return T_PAIR;
|
case T_CLIST: return T_PAIR;
|
||||||
case T_ECLIST: return T_EC;
|
case T_ECLIST: return T_EC;
|
||||||
case T_LCLIST: return T_LC;
|
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 = {
|
const struct f_val f_const_empty_path = {
|
||||||
.type = T_PATH,
|
.type = T_PATH,
|
||||||
.val.ad = &null_adata,
|
.val.ad = &null_adata,
|
||||||
|
@ -91,6 +94,9 @@ const struct f_val f_const_empty_path = {
|
||||||
}, f_const_empty_lclist = {
|
}, f_const_empty_lclist = {
|
||||||
.type = T_LCLIST,
|
.type = T_LCLIST,
|
||||||
.val.ad = &null_adata,
|
.val.ad = &null_adata,
|
||||||
|
}, f_const_empty_prefix_set = {
|
||||||
|
.type = T_PREFIX_SET,
|
||||||
|
.val.ti = &f_const_empty_trie,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct adata *
|
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))
|
if (val_is_ip4(v1) && (v2->type == T_QUAD))
|
||||||
return uint_cmp(ipa_to_u32(v1->val.ip), v2->val.i);
|
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;
|
return F_CMP_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,6 +307,12 @@ val_same(const struct f_val *v1, const struct f_val *v2)
|
||||||
int
|
int
|
||||||
clist_set_type(const struct f_tree *set, struct f_val *v)
|
clist_set_type(const struct f_tree *set, struct f_val *v)
|
||||||
{
|
{
|
||||||
|
if (!set)
|
||||||
|
{
|
||||||
|
v->type = T_VOID;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
switch (set->from.type)
|
switch (set->from.type)
|
||||||
{
|
{
|
||||||
case T_PAIR:
|
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)
|
clist_match_set(const struct adata *clist, const struct f_tree *set)
|
||||||
{
|
{
|
||||||
if (!clist)
|
if (!clist)
|
||||||
|
@ -345,7 +357,7 @@ clist_match_set(const struct adata *clist, const struct f_tree *set)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
int
|
||||||
eclist_match_set(const struct adata *list, const struct f_tree *set)
|
eclist_match_set(const struct adata *list, const struct f_tree *set)
|
||||||
{
|
{
|
||||||
if (!list)
|
if (!list)
|
||||||
|
@ -369,7 +381,7 @@ eclist_match_set(const struct adata *list, const struct f_tree *set)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
int
|
||||||
lclist_match_set(const struct adata *list, const struct f_tree *set)
|
lclist_match_set(const struct adata *list, const struct f_tree *set)
|
||||||
{
|
{
|
||||||
if (!list)
|
if (!list)
|
||||||
|
@ -537,6 +549,9 @@ val_in_range(const struct f_val *v1, const struct f_val *v2)
|
||||||
if (v2->type != T_SET)
|
if (v2->type != T_SET)
|
||||||
return F_CMP_ERROR;
|
return F_CMP_ERROR;
|
||||||
|
|
||||||
|
if (!v2->val.t)
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* With integrated Quad<->IP implicit conversion */
|
/* With integrated Quad<->IP implicit conversion */
|
||||||
if ((v1->type == v2->val.t->from.type) ||
|
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))))
|
((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_IFNAME,
|
||||||
SA_IFINDEX,
|
SA_IFINDEX,
|
||||||
SA_WEIGHT,
|
SA_WEIGHT,
|
||||||
|
SA_PREF,
|
||||||
SA_GW_MPLS,
|
SA_GW_MPLS,
|
||||||
} PACKED;
|
} PACKED;
|
||||||
|
|
||||||
|
@ -191,6 +192,7 @@ struct f_tree *f_new_tree(void);
|
||||||
struct f_tree *build_tree(struct f_tree *);
|
struct f_tree *build_tree(struct f_tree *);
|
||||||
const struct f_tree *find_tree(const struct f_tree *t, const struct f_val *val);
|
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 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_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);
|
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);
|
int clist_set_type(const struct f_tree *set, struct f_val *v);
|
||||||
static inline int eclist_set_type(const struct f_tree *set)
|
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)
|
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 *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);
|
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);
|
(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);
|
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
|
# 101 content of per-inst struct
|
||||||
# 102 constructor arguments
|
# 102 constructor arguments
|
||||||
|
# 110 constructor attributes
|
||||||
# 103 constructor body
|
# 103 constructor body
|
||||||
# 104 dump line item content
|
# 104 dump line item content
|
||||||
# (there may be nothing in dump-line content and
|
# (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.
|
# Here are macros to allow you to _divert to the right directions.
|
||||||
m4_define(FID_STRUCT_IN, `m4_divert(101)')
|
m4_define(FID_STRUCT_IN, `m4_divert(101)')
|
||||||
m4_define(FID_NEW_ARGS, `m4_divert(102)')
|
m4_define(FID_NEW_ARGS, `m4_divert(102)')
|
||||||
|
m4_define(FID_NEW_ATTRIBUTES, `m4_divert(110)')
|
||||||
m4_define(FID_NEW_BODY, `m4_divert(103)')
|
m4_define(FID_NEW_BODY, `m4_divert(103)')
|
||||||
m4_define(FID_DUMP_BODY, `m4_divert(104)m4_define([[FID_DUMP_BODY_EXISTS]])')
|
m4_define(FID_DUMP_BODY, `m4_divert(104)m4_define([[FID_DUMP_BODY_EXISTS]])')
|
||||||
m4_define(FID_LINEARIZE_BODY, `m4_divert(105)')
|
m4_define(FID_LINEARIZE_BODY, `m4_divert(105)')
|
||||||
|
@ -106,15 +108,18 @@ FID_STRUCT_IN()m4_dnl
|
||||||
struct f_inst * f$1;
|
struct f_inst * f$1;
|
||||||
FID_NEW_ARGS()m4_dnl
|
FID_NEW_ARGS()m4_dnl
|
||||||
, struct f_inst * f$1
|
, struct f_inst * f$1
|
||||||
|
FID_NEW_ATTRIBUTES()m4_dnl
|
||||||
|
NONNULL(m4_eval($1+1))
|
||||||
FID_NEW_BODY()m4_dnl
|
FID_NEW_BODY()m4_dnl
|
||||||
whati->f$1 = f$1;
|
whati->f$1 = f$1;
|
||||||
for (const struct f_inst *child = f$1; child; child = child->next) {
|
const struct f_inst *child$1 = f$1;
|
||||||
what->size += child->size;
|
do {
|
||||||
|
what->size += child$1->size;
|
||||||
FID_IFCONST([[
|
FID_IFCONST([[
|
||||||
if (child->fi_code != FI_CONSTANT)
|
if (child$1->fi_code != FI_CONSTANT)
|
||||||
constargs = 0;
|
constargs = 0;
|
||||||
]])
|
]])
|
||||||
}
|
} while (child$1 = child$1->next);
|
||||||
FID_LINEARIZE_BODY
|
FID_LINEARIZE_BODY
|
||||||
pos = linearize(dest, whati->f$1, pos);
|
pos = linearize(dest, whati->f$1, pos);
|
||||||
FID_INTERPRET_BODY()')
|
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));
|
cf_error("Arguments $1 and $2 of %s must be of the same type", f_instruction_name(what->fi_code));
|
||||||
FID_INTERPRET_BODY()')
|
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
|
# Executing another filter line. This replaces the recursion
|
||||||
# that was needed in the former implementation.
|
# that was needed in the former implementation.
|
||||||
m4_define(LINEX, `FID_INTERPRET_EXEC()LINEX_($1)FID_INTERPRET_NEW()return $1 FID_INTERPRET_BODY()')
|
m4_define(LINEX, `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
|
FID_DUMP_BODY()m4_dnl
|
||||||
f_dump_line(item->fl$1, indent + 1);
|
f_dump_line(item->fl$1, indent + 1);
|
||||||
FID_LINEARIZE_BODY()m4_dnl
|
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
|
FID_SAME_BODY()m4_dnl
|
||||||
if (!f_same(f1->fl$1, f2->fl$1)) return 0;
|
if (!f_same(f1->fl$1, f2->fl$1)) return 0;
|
||||||
FID_ITERATE_BODY()m4_dnl
|
FID_ITERATE_BODY()m4_dnl
|
||||||
|
@ -238,9 +249,13 @@ m4_define(ERROR,
|
||||||
# This macro specifies result type and makes there are no conflicting definitions
|
# This macro specifies result type and makes there are no conflicting definitions
|
||||||
m4_define(RESULT_TYPE,
|
m4_define(RESULT_TYPE,
|
||||||
`m4_ifdef([[INST_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(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_, `
|
m4_define(RESULT_TYPE_, `
|
||||||
FID_NEW_BODY()m4_dnl
|
FID_NEW_BODY()m4_dnl
|
||||||
what->type = $1;
|
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.
|
# This macro does all the code wrapping. See inline comments.
|
||||||
m4_define(INST_FLUSH, `m4_ifdef([[INST_NAME]], [[
|
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 { ... }
|
FID_ENUM()m4_dnl Contents of enum fi_code { ... }
|
||||||
INST_NAME(),
|
INST_NAME(),
|
||||||
FID_ENUM_STR()m4_dnl Contents of const char * indexed by enum fi_code
|
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_NEW()m4_dnl Constructor and interpreter code together
|
||||||
FID_HIC(
|
FID_HIC(
|
||||||
[[m4_dnl Public declaration of constructor in H file
|
[[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_undivert(102)m4_dnl
|
||||||
);]],
|
);]],
|
||||||
[[m4_dnl The one case in The Big Switch inside interpreter
|
[[m4_dnl The one case in The Big Switch inside interpreter
|
||||||
|
@ -321,7 +339,9 @@ m4_undivert(102)m4_dnl
|
||||||
break;
|
break;
|
||||||
]],
|
]],
|
||||||
[[m4_dnl Constructor itself
|
[[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
|
m4_undivert(102)m4_dnl
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
@ -365,6 +385,7 @@ case INST_NAME(): {
|
||||||
#undef whati
|
#undef whati
|
||||||
#undef item
|
#undef item
|
||||||
dest->items[pos].fi_code = what->fi_code;
|
dest->items[pos].fi_code = what->fi_code;
|
||||||
|
dest->items[pos].flags = what->flags;
|
||||||
dest->items[pos].lineno = what->lineno;
|
dest->items[pos].lineno = what->lineno;
|
||||||
break;
|
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
|
INST_FLUSH()m4_dnl First, old data is flushed
|
||||||
m4_define([[INST_NAME]], [[$1]])m4_dnl Then we store instruction name,
|
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_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_NEVER_CONSTANT]])m4_dnl reset NEVER_CONSTANT trigger,
|
||||||
m4_undefine([[INST_RESULT_TYPE]])m4_dnl and reset RESULT_TYPE value.
|
m4_undefine([[INST_RESULT_TYPE]])m4_dnl and reset RESULT_TYPE value.
|
||||||
FID_INTERPRET_BODY()m4_dnl By default, every code is interpreter code.
|
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;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -550,7 +577,7 @@ FID_WR_PUT(8)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct f_line *
|
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;
|
uint len = 0;
|
||||||
for (uint i=0; i<count; i++)
|
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++)
|
for (uint i=0; i<count; i++)
|
||||||
out->len = linearize(out, inst[i], out->len);
|
out->len = linearize(out, inst[i], out->len);
|
||||||
|
|
||||||
|
out->results = results;
|
||||||
|
|
||||||
#ifdef LOCAL_DEBUG
|
#ifdef LOCAL_DEBUG
|
||||||
f_dump_line(out, 0);
|
f_dump_line(out, 0);
|
||||||
#endif
|
#endif
|
||||||
|
@ -630,6 +659,7 @@ FID_WR_PUT(4)m4_dnl
|
||||||
struct f_inst {
|
struct f_inst {
|
||||||
struct f_inst *next; /* Next instruction */
|
struct f_inst *next; /* Next instruction */
|
||||||
enum f_instruction_code fi_code; /* Instruction code */
|
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 */
|
enum f_type type; /* Type of returned value, if known */
|
||||||
int size; /* How many instructions are underneath */
|
int size; /* How many instructions are underneath */
|
||||||
int lineno; /* Line number */
|
int lineno; /* Line number */
|
||||||
|
|
291
filter/f-inst.c
291
filter/f-inst.c
|
@ -62,8 +62,9 @@
|
||||||
* m4_dnl INST(FI_NOP, in, out) { enum value, input args, output args
|
* 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(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_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 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 SYMBOL; symbol handed from config
|
||||||
* m4_dnl STATIC_ATTR; static attribute definition
|
* m4_dnl STATIC_ATTR; static attribute definition
|
||||||
* m4_dnl DYNAMIC_ATTR; dynamic attribute definition
|
* m4_dnl DYNAMIC_ATTR; dynamic attribute definition
|
||||||
|
@ -80,10 +81,17 @@
|
||||||
* m4_dnl )
|
* m4_dnl )
|
||||||
*
|
*
|
||||||
* m4_dnl RESULT(type, union-field, value); putting this on value stack
|
* 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_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 RESULT_VOID; return undef
|
||||||
* m4_dnl }
|
* 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.
|
* 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.
|
* 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
|
* What is really considered as one instruction is not the { ... } block
|
||||||
|
@ -91,6 +99,24 @@
|
||||||
*
|
*
|
||||||
* Other code is just copied into the interpreter part.
|
* 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
|
* 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.
|
* 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 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.
|
* 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 */
|
/* Binary operators */
|
||||||
|
@ -246,7 +303,7 @@
|
||||||
RESULT_TYPE(T_BOOL);
|
RESULT_TYPE(T_BOOL);
|
||||||
|
|
||||||
if (v1.val.i)
|
if (v1.val.i)
|
||||||
LINE(2,0);
|
LINE(2,1);
|
||||||
else
|
else
|
||||||
RESULT_VAL(v1);
|
RESULT_VAL(v1);
|
||||||
}
|
}
|
||||||
|
@ -256,7 +313,7 @@
|
||||||
RESULT_TYPE(T_BOOL);
|
RESULT_TYPE(T_BOOL);
|
||||||
|
|
||||||
if (!v1.val.i)
|
if (!v1.val.i)
|
||||||
LINE(2,0);
|
LINE(2,1);
|
||||||
else
|
else
|
||||||
RESULT_VAL(v1);
|
RESULT_VAL(v1);
|
||||||
}
|
}
|
||||||
|
@ -349,7 +406,7 @@
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case T_SET:
|
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");
|
runtime("Only integer sets allowed in path mask");
|
||||||
|
|
||||||
pm->item[i] = (struct f_path_mask_item) {
|
pm->item[i] = (struct f_path_mask_item) {
|
||||||
|
@ -371,12 +428,14 @@
|
||||||
INST(FI_NEQ, 2, 1) {
|
INST(FI_NEQ, 2, 1) {
|
||||||
ARG_ANY(1);
|
ARG_ANY(1);
|
||||||
ARG_ANY(2);
|
ARG_ANY(2);
|
||||||
|
ARG_PREFER_SAME_TYPE(1, 2);
|
||||||
RESULT(T_BOOL, i, !val_same(&v1, &v2));
|
RESULT(T_BOOL, i, !val_same(&v1, &v2));
|
||||||
}
|
}
|
||||||
|
|
||||||
INST(FI_EQ, 2, 1) {
|
INST(FI_EQ, 2, 1) {
|
||||||
ARG_ANY(1);
|
ARG_ANY(1);
|
||||||
ARG_ANY(2);
|
ARG_ANY(2);
|
||||||
|
ARG_PREFER_SAME_TYPE(1, 2);
|
||||||
RESULT(T_BOOL, i, val_same(&v1, &v2));
|
RESULT(T_BOOL, i, val_same(&v1, &v2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,6 +506,18 @@
|
||||||
RESULT(T_BOOL, i, ipa_is_ip4(v1.val.ip));
|
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 */
|
/* Set to indirect value prepared in v1 */
|
||||||
INST(FI_VAR_SET, 1, 0) {
|
INST(FI_VAR_SET, 1, 0) {
|
||||||
NEVER_CONSTANT;
|
NEVER_CONSTANT;
|
||||||
|
@ -477,12 +548,100 @@
|
||||||
RESULT_VAL(val);
|
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) {
|
INST(FI_CONDITION, 1, 0) {
|
||||||
ARG(1, T_BOOL);
|
ARG(1, T_BOOL);
|
||||||
if (v1.val.i)
|
if (v1.val.i)
|
||||||
LINE(2,0);
|
LINE(2,0);
|
||||||
else
|
else
|
||||||
LINE(3,1);
|
LINE(3,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
INST(FI_PRINT, 0, 0) {
|
INST(FI_PRINT, 0, 0) {
|
||||||
|
@ -526,13 +685,14 @@
|
||||||
case SA_FROM: RESULT(sa.f_type, ip, rta->from); break;
|
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_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_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_SOURCE: RESULT(sa.f_type, i, rta->source); break;
|
||||||
case SA_SCOPE: RESULT(sa.f_type, i, rta->scope); 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_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_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_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_WEIGHT: RESULT(sa.f_type, i, rta->nh.weight + 1); break;
|
||||||
|
case SA_PREF: RESULT(sa.f_type, i, rta->pref); break;
|
||||||
case SA_GW_MPLS: RESULT(sa.f_type, i, rta->nh.labels ? rta->nh.label[0] : MPLS_NULL); break;
|
case SA_GW_MPLS: RESULT(sa.f_type, i, rta->nh.labels ? rta->nh.label[0] : MPLS_NULL); break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -561,7 +721,7 @@
|
||||||
{
|
{
|
||||||
ip_addr ip = v1.val.ip;
|
ip_addr ip = v1.val.ip;
|
||||||
struct iface *ifa = ipa_is_link_local(ip) ? rta->nh.iface : NULL;
|
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))
|
if (!n || (n->scope == SCOPE_HOST))
|
||||||
runtime( "Invalid gw address" );
|
runtime( "Invalid gw address" );
|
||||||
|
|
||||||
|
@ -637,6 +797,10 @@
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SA_PREF:
|
||||||
|
rta->pref = v1.val.i;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code);
|
bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code);
|
||||||
}
|
}
|
||||||
|
@ -709,9 +873,6 @@
|
||||||
case EAF_TYPE_LC_SET:
|
case EAF_TYPE_LC_SET:
|
||||||
RESULT_(T_LCLIST, ad, e->u.ptr);
|
RESULT_(T_LCLIST, ad, e->u.ptr);
|
||||||
break;
|
break;
|
||||||
case EAF_TYPE_UNDEF:
|
|
||||||
RESULT_VOID;
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
bug("Unknown dynamic attribute type");
|
bug("Unknown dynamic attribute type");
|
||||||
}
|
}
|
||||||
|
@ -732,7 +893,10 @@
|
||||||
l->count = 1;
|
l->count = 1;
|
||||||
l->attrs[0].id = da.ea_code;
|
l->attrs[0].id = da.ea_code;
|
||||||
l->attrs[0].flags = 0;
|
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) {
|
switch (da.type) {
|
||||||
case EAF_TYPE_INT:
|
case EAF_TYPE_INT:
|
||||||
|
@ -787,35 +951,8 @@
|
||||||
ACCESS_RTE;
|
ACCESS_RTE;
|
||||||
ACCESS_EATTRS;
|
ACCESS_EATTRS;
|
||||||
|
|
||||||
{
|
f_rta_cow(fs);
|
||||||
struct ea_list *l = lp_alloc(fs->pool, sizeof(struct ea_list) + sizeof(eattr));
|
ea_unset_attr(fs->eattrs, fs->pool, 1, da.ea_code);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
INST(FI_LENGTH, 1, 1) { /* Get length of */
|
INST(FI_LENGTH, 1, 1) { /* Get length of */
|
||||||
|
@ -983,7 +1120,7 @@
|
||||||
RESULT(T_INT, i, v1.val.lc.ldp2);
|
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);
|
ARG_ANY(1);
|
||||||
RESULT_TYPE(f_type_element_type(v1.type));
|
RESULT_TYPE(f_type_element_type(v1.type));
|
||||||
switch(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);
|
ARG_ANY(1);
|
||||||
RESULT_TYPE(f_type_element_type(v1.type));
|
RESULT_TYPE(f_type_element_type(v1.type));
|
||||||
switch(v1.type)
|
switch(v1.type)
|
||||||
|
@ -1051,7 +1188,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
INST(FI_RETURN, 1, 1) {
|
INST(FI_RETURN, 1, 0) {
|
||||||
NEVER_CONSTANT;
|
NEVER_CONSTANT;
|
||||||
/* Acquire the return value */
|
/* Acquire the return value */
|
||||||
ARG_ANY(1);
|
ARG_ANY(1);
|
||||||
|
@ -1079,28 +1216,59 @@
|
||||||
|
|
||||||
INST(FI_CALL, 0, 1) {
|
INST(FI_CALL, 0, 1) {
|
||||||
NEVER_CONSTANT;
|
NEVER_CONSTANT;
|
||||||
|
VARARG;
|
||||||
SYMBOL;
|
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()
|
FID_SAME_BODY()
|
||||||
if (!(f1->sym->flags & SYM_FLAG_SAME))
|
if (!(f1->sym->flags & SYM_FLAG_SAME) && !(f1_->flags & FIF_RECURSIVE))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
FID_ITERATE_BODY()
|
FID_ITERATE_BODY()
|
||||||
|
if (!(what->flags & FIF_RECURSIVE))
|
||||||
BUFFER_PUSH(fit->lines) = whati->sym->function;
|
BUFFER_PUSH(fit->lines) = whati->sym->function;
|
||||||
|
|
||||||
FID_INTERPRET_BODY()
|
FID_INTERPRET_BODY()
|
||||||
|
|
||||||
/* Push the body on stack */
|
/* Push the body on stack */
|
||||||
LINEX(sym->function);
|
LINEX(sym->function);
|
||||||
|
curline.vbase = curline.ventry;
|
||||||
curline.emask |= FE_RETURN;
|
curline.emask |= FE_RETURN;
|
||||||
|
|
||||||
/* Before this instruction was called, there was the T_VOID
|
/* Arguments on stack */
|
||||||
* automatic return value pushed on value stack and also
|
fstk->vcnt += sym->function->args;
|
||||||
* 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;
|
|
||||||
|
|
||||||
/* Storage for local variables */
|
/* Storage for local variables */
|
||||||
memset(&(fstk->vstk[fstk->vcnt]), 0, sizeof(struct f_val) * sym->function->vars);
|
memset(&(fstk->vstk[fstk->vcnt]), 0, sizeof(struct f_val) * sym->function->vars);
|
||||||
|
@ -1213,17 +1381,10 @@
|
||||||
|
|
||||||
if (v1.type == T_PATH)
|
if (v1.type == T_PATH)
|
||||||
{
|
{
|
||||||
const struct f_tree *set = NULL;
|
if ((v2.type == T_SET) && path_set_type(v2.val.t) || (v2.type == T_INT))
|
||||||
u32 key = 0;
|
RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 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;
|
|
||||||
else
|
else
|
||||||
runtime("Can't delete non-integer (set)");
|
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)
|
else if (v1.type == T_CLIST)
|
||||||
|
@ -1275,10 +1436,8 @@
|
||||||
|
|
||||||
if (v1.type == T_PATH)
|
if (v1.type == T_PATH)
|
||||||
{
|
{
|
||||||
u32 key = 0;
|
if ((v2.type == T_SET) && path_set_type(v2.val.t))
|
||||||
|
RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, &v2, 1) ]]);
|
||||||
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) ]]);
|
|
||||||
else
|
else
|
||||||
runtime("Can't filter integer");
|
runtime("Can't filter integer");
|
||||||
}
|
}
|
||||||
|
@ -1368,7 +1527,7 @@
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
INST(FI_FORMAT, 1, 0) { /* Format */
|
INST(FI_FORMAT, 1, 1) { /* Format */
|
||||||
ARG_ANY(1);
|
ARG_ANY(1);
|
||||||
RESULT(T_STRING, s, val_format_str(fpool, &v1));
|
RESULT(T_STRING, s, val_format_str(fpool, &v1));
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
/* Flags for instructions */
|
/* Flags for instructions */
|
||||||
enum f_instruction_flags {
|
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;
|
} PACKED;
|
||||||
|
|
||||||
/* Include generated filter instruction declarations */
|
/* 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)
|
static inline const char *f_instruction_name(enum f_instruction_code fi)
|
||||||
{ return f_instruction_name_(fi) + 3; }
|
{ return f_instruction_name_(fi) + 3; }
|
||||||
|
|
||||||
|
struct f_arg {
|
||||||
|
struct symbol *arg;
|
||||||
|
struct f_arg *next;
|
||||||
|
};
|
||||||
|
|
||||||
/* Filter structures for execution */
|
/* Filter structures for execution */
|
||||||
/* Line of instructions to be unconditionally executed one after another */
|
/* Line of instructions to be unconditionally executed one after another */
|
||||||
struct f_line {
|
struct f_line {
|
||||||
uint len; /* Line length */
|
uint len; /* Line length */
|
||||||
u8 args; /* Function: Args required */
|
u8 args; /* Function: Args required */
|
||||||
u8 vars;
|
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 */
|
struct f_line_item items[0]; /* The items themselves */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Convert the f_inst infix tree to the f_line structures */
|
/* 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);
|
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)
|
static inline struct f_line *f_linearize(const struct f_inst *root, uint results)
|
||||||
{ return f_linearize_concat(&root, 1); }
|
{ return f_linearize_concat(&root, 1, results); }
|
||||||
|
|
||||||
void f_dump_line(const struct f_line *, uint indent);
|
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));
|
f_new_inst(FI_DIE, F_REJECT));
|
||||||
|
|
||||||
struct filter *f = cfg_allocz(sizeof(struct filter));
|
struct filter *f = cfg_allocz(sizeof(struct filter));
|
||||||
f->root = f_linearize(cond);
|
f->root = f_linearize(cond, 0);
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,11 +128,11 @@ ca_lookup(pool *p, const char *name, int f_type)
|
||||||
|
|
||||||
static int inited = 0;
|
static int inited = 0;
|
||||||
if (!inited) {
|
if (!inited) {
|
||||||
idm_init(&ca_idm, &root_pool, 8);
|
idm_init(&ca_idm, config_pool, 8);
|
||||||
HASH_INIT(ca_hash, &root_pool, CA_ORDER);
|
HASH_INIT(ca_hash, config_pool, CA_ORDER);
|
||||||
|
|
||||||
ca_storage_max = 256;
|
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++;
|
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);
|
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->fda = f_new_dynamic_attr(ea_type, f_type, EA_CUSTOM(id));
|
||||||
cas->uc = 1;
|
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. */
|
/* End of current line. Drop local variables before exiting. */
|
||||||
fstk->vcnt -= curline.line->vars;
|
fstk->vcnt = curline.ventry + curline.line->results;
|
||||||
fstk->vcnt -= curline.line->args;
|
|
||||||
fstk->ecnt--;
|
fstk->ecnt--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,9 +46,7 @@ run_function(const void *arg)
|
||||||
if (t->cmp)
|
if (t->cmp)
|
||||||
return t->result == f_same(t->fn, 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_linpool, NULL);
|
||||||
enum filter_return fret = f_eval(t->fn, tmp, NULL);
|
|
||||||
rfree(tmp);
|
|
||||||
|
|
||||||
return (fret < F_REJECT);
|
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()
|
function t_bool()
|
||||||
bool b;
|
|
||||||
{
|
{
|
||||||
b = true;
|
bool b = true;
|
||||||
bt_assert(b);
|
bt_assert(b);
|
||||||
bt_assert(!!b);
|
bt_assert(!!b);
|
||||||
|
|
||||||
|
@ -82,12 +81,11 @@ define xyzzy = (120+10);
|
||||||
define '1a-a1' = (xyzzy-100);
|
define '1a-a1' = (xyzzy-100);
|
||||||
|
|
||||||
function t_int()
|
function t_int()
|
||||||
int i;
|
|
||||||
{
|
{
|
||||||
bt_assert(xyzzy = 130);
|
bt_assert(xyzzy = 130);
|
||||||
bt_assert('1a-a1' = 30);
|
bt_assert('1a-a1' = 30);
|
||||||
|
|
||||||
i = four;
|
int i = four;
|
||||||
i = 12*100 + 60/2 + i;
|
i = 12*100 + 60/2 + i;
|
||||||
i = (i + 0);
|
i = (i + 0);
|
||||||
bt_assert(i = 1234);
|
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];
|
define is3 = [5, 17, 2, 11, 8, 15, 3, 19];
|
||||||
|
|
||||||
function t_int_set()
|
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(1 ~ [1,2,3]);
|
||||||
bt_assert(5 ~ [1..20]);
|
bt_assert(5 ~ [1..20]);
|
||||||
bt_assert(2 ~ [ 1, 2, 3 ]);
|
bt_assert(2 ~ [ 1, 2, 3 ]);
|
||||||
bt_assert(5 ~ [ 4 .. 7 ]);
|
bt_assert(5 ~ [ 4 .. 7 ]);
|
||||||
bt_assert(1 !~ [ 2, 3, 4 ]);
|
bt_assert(1 !~ [ 2, 3, 4 ]);
|
||||||
bt_assert(999 !~ [ 666, 333 ]);
|
bt_assert(999 !~ [ 666, 333 ]);
|
||||||
|
bt_assert(1 !~ []);
|
||||||
|
bt_assert(1 !~ is);
|
||||||
|
|
||||||
is = [ 2, 3, 4, 7..11 ];
|
is = [ 2, 3, 4, 7..11 ];
|
||||||
bt_assert(10 ~ is);
|
bt_assert(10 ~ is);
|
||||||
|
@ -170,6 +173,7 @@ int set is;
|
||||||
bt_assert([1,4..10,20] = [1,4..10,20]);
|
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([ 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");
|
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()
|
function t_string()
|
||||||
string st;
|
|
||||||
{
|
{
|
||||||
st = "Hello";
|
string st = "Hello";
|
||||||
bt_assert(format(st) = "Hello");
|
bt_assert(format(st) = "Hello");
|
||||||
bt_assert(st ~ "Hell*");
|
bt_assert(st ~ "Hell*");
|
||||||
bt_assert(st ~ "?ello");
|
bt_assert(st ~ "?ello");
|
||||||
|
@ -210,9 +213,8 @@ function 'mkpair-a'(int a)
|
||||||
}
|
}
|
||||||
|
|
||||||
function t_pair()
|
function t_pair()
|
||||||
pair pp;
|
|
||||||
{
|
{
|
||||||
pp = (1, 2);
|
pair pp = (1, 2);
|
||||||
bt_assert(format(pp) = "(1,2)");
|
bt_assert(format(pp) = "(1,2)");
|
||||||
bt_assert((1,2) = pp);
|
bt_assert((1,2) = pp);
|
||||||
bt_assert((1,1+1) = pp);
|
bt_assert((1,1+1) = pp);
|
||||||
|
@ -233,10 +235,11 @@ bt_test_suite(t_pair, "Testing pairs");
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function t_pair_set()
|
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)];
|
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(format(ps) = "[(1,2), (3,4)..(4,8), (5,0)..(5,65535), (6,3)..(6,6)]");
|
||||||
bt_assert(pp ~ ps);
|
bt_assert(pp ~ ps);
|
||||||
|
@ -253,6 +256,7 @@ pair set ps;
|
||||||
bt_assert((6,6+one) !~ ps);
|
bt_assert((6,6+one) !~ ps);
|
||||||
bt_assert(((one+6),2) !~ ps);
|
bt_assert(((one+6),2) !~ ps);
|
||||||
bt_assert((1,1) !~ ps);
|
bt_assert((1,1) !~ ps);
|
||||||
|
bt_assert(pp !~ []);
|
||||||
|
|
||||||
ps = [(20..150, 200..300), (50100..50200, 1000..50000), (*, 5+5)];
|
ps = [(20..150, 200..300), (50100..50200, 1000..50000), (*, 5+5)];
|
||||||
bt_assert((100,200) ~ ps);
|
bt_assert((100,200) ~ ps);
|
||||||
|
@ -304,6 +308,7 @@ quad qq;
|
||||||
qq = 1.2.3.4;
|
qq = 1.2.3.4;
|
||||||
bt_assert(qq ~ [1.2.3.4, 5.6.7.8]);
|
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 !~ [1.2.1.1, 1.2.3.5]);
|
||||||
|
bt_assert(qq !~ []);
|
||||||
}
|
}
|
||||||
|
|
||||||
bt_test_suite(t_quad_set, "Testing sets of quads");
|
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 ~ [ 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");
|
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()
|
function t_enum()
|
||||||
{
|
{
|
||||||
bt_assert(format(RTS_DUMMY) = "(enum 30)0");
|
|
||||||
bt_assert(format(RTS_STATIC) = "(enum 30)1");
|
bt_assert(format(RTS_STATIC) = "(enum 30)1");
|
||||||
bt_assert(format(NET_IP4) = "(enum 36)1");
|
bt_assert(format(NET_IP4) = "(enum 36)1");
|
||||||
bt_assert(format(NET_VPN6) = "(enum 36)4");
|
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/8 ~ [ 1.0.0.0/8+ ]);
|
||||||
bt_assert(1.0.0.0/9 !~ [ 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(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 } ]);
|
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()
|
function t_prefix_set()
|
||||||
prefix set pxs;
|
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} ];
|
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}]");
|
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()
|
function t_prefix6_set()
|
||||||
prefix set pxs;
|
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(1180::/16 ~ [ 1100::/8{15, 17} ]);
|
||||||
bt_assert(12::34 = 12::34);
|
bt_assert(12::34 = 12::34);
|
||||||
bt_assert(12::34 ~ [ 12::33..12::35 ]);
|
bt_assert(12::34 ~ [ 12::33..12::35 ]);
|
||||||
|
@ -681,6 +713,7 @@ int set set12;
|
||||||
bt_assert(3 ~ p2);
|
bt_assert(3 ~ p2);
|
||||||
bt_assert(p2 ~ [2, 10..20]);
|
bt_assert(p2 ~ [2, 10..20]);
|
||||||
bt_assert(p2 ~ [4, 10..20]);
|
bt_assert(p2 ~ [4, 10..20]);
|
||||||
|
bt_assert(p2 !~ []);
|
||||||
|
|
||||||
p2 = prepend(p2, 5);
|
p2 = prepend(p2, 5);
|
||||||
bt_assert(p2 !~ pm1);
|
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 [2, 4, 6] 3 [1..2] 1 =]);
|
||||||
bt_assert(p2 ~ [= 5 set35 3 set12 set12 =]);
|
bt_assert(p2 ~ [= 5 set35 3 set12 set12 =]);
|
||||||
bt_assert(p2 ~ mkpath(5, 4));
|
bt_assert(p2 ~ mkpath(5, 4));
|
||||||
|
bt_assert(p2 ~ [= * [3] * =]);
|
||||||
|
bt_assert(p2 !~ [= * [] * =]);
|
||||||
|
|
||||||
bt_assert(p2.len = 5);
|
bt_assert(p2.len = 5);
|
||||||
bt_assert(p2.first = 5);
|
bt_assert(p2.first = 5);
|
||||||
|
@ -699,6 +734,10 @@ int set set12;
|
||||||
bt_assert(p2.len = 5);
|
bt_assert(p2.len = 5);
|
||||||
bt_assert(delete(p2, 3) = prepend(prepend(prepend(prepend(+empty+, 1), 2), 4), 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(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( + empty +, 5 );
|
||||||
p2 = prepend( p2, 4 );
|
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(delete(p2, [4..5]) = prepend(prepend(prepend(prepend(+empty+, 3), 3), 2), 1));
|
||||||
|
|
||||||
bt_assert(format([= 1 2+ 3 =]) = "[= 1 2 + 3 =]");
|
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");
|
bt_test_suite(t_path, "Testing paths");
|
||||||
|
@ -759,6 +807,7 @@ clist r;
|
||||||
bt_assert(l ~ [(2,2..3)]);
|
bt_assert(l ~ [(2,2..3)]);
|
||||||
bt_assert(l ~ [(1,1..2)]);
|
bt_assert(l ~ [(1,1..2)]);
|
||||||
bt_assert(l ~ [(1,1)..(1,2)]);
|
bt_assert(l ~ [(1,1)..(1,2)]);
|
||||||
|
bt_assert(l !~ []);
|
||||||
|
|
||||||
l = add(l, (2,5));
|
l = add(l, (2,5));
|
||||||
l = add(l, (5,one));
|
l = add(l, (5,one));
|
||||||
|
@ -796,6 +845,9 @@ clist r;
|
||||||
bt_assert(l !~ [(*,(one+6))]);
|
bt_assert(l !~ [(*,(one+6))]);
|
||||||
bt_assert(l !~ [(*, (one+one+one))]);
|
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, [(*,(one+onef(3)))]);
|
||||||
l = delete(l, [(*,(4+one))]);
|
l = delete(l, [(*,(4+one))]);
|
||||||
bt_assert(l = add(-empty-, (3,1)));
|
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(format(r) = "(clist (2,1) (1,3) (2,2) (3,1) (2,3))");
|
||||||
bt_assert(r.min = (1,3));
|
bt_assert(r.min = (1,3));
|
||||||
bt_assert(r.max = (3,1));
|
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");
|
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((ro, 10.20.30.40, 100) !~ el);
|
||||||
bt_assert(el !~ [(rt, 10, 35..40)]);
|
bt_assert(el !~ [(rt, 10, 35..40)]);
|
||||||
bt_assert(el !~ [(ro, 10, *)]);
|
bt_assert(el !~ [(ro, 10, *)]);
|
||||||
|
bt_assert(el !~ []);
|
||||||
|
|
||||||
el = add(el, (rt, 10, 40));
|
el = add(el, (rt, 10, 40));
|
||||||
el2 = filter(el, [(rt, 10, 20..40)] );
|
el2 = filter(el, [(rt, 10, 20..40)] );
|
||||||
el2 = add(el2, (rt, 10, 50));
|
el2 = add(el2, (rt, 10, 50));
|
||||||
|
|
||||||
|
bt_assert(delete(el, []) = el);
|
||||||
|
bt_assert(filter(el, []) = --empty--);
|
||||||
|
|
||||||
# eclist A (1,30,40)
|
# eclist A (1,30,40)
|
||||||
bt_assert(el = add(add(add(--empty--, (rt, 10, 1)), (rt, 10, 30)), (rt, 10, 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))");
|
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(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.min = (rt, 1, 3));
|
||||||
bt_assert(r.max = (rt, 3, 1));
|
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");
|
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, (30, 30, 30));
|
||||||
ll2 = add(ll2, (40, 40, 40));
|
ll2 = add(ll2, (40, 40, 40));
|
||||||
|
|
||||||
|
bt_assert(delete(ll, []) = ll);
|
||||||
|
bt_assert(filter(ll, []) = ---empty---);
|
||||||
|
|
||||||
# lclist A (10, 20, 30)
|
# lclist A (10, 20, 30)
|
||||||
bt_assert(format(ll) = "(lclist (10, 10, 10) (20, 20, 20) (30, 30, 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(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.min = (1, 2, 3));
|
||||||
bt_assert(r.max = (3, 1, 2));
|
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");
|
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 !~ [(5,10,15), (10,21,30)]);
|
||||||
bt_assert(ll !~ [(10,21..25,*)]);
|
bt_assert(ll !~ [(10,21..25,*)]);
|
||||||
bt_assert(ll !~ [(11, *, *)]);
|
bt_assert(ll !~ [(11, *, *)]);
|
||||||
|
bt_assert(ll !~ []);
|
||||||
|
|
||||||
lls = [(10, 10, 10), (20, 20, 15..25), (30, 30, *), (40, 35..45, *), (50, *, *), (55..65, *, *)];
|
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)]");
|
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()
|
function t_rd_set()
|
||||||
rd set rds;
|
rd set rds;
|
||||||
{
|
{
|
||||||
|
rds = [];
|
||||||
|
bt_assert(rds = []);
|
||||||
|
bt_assert(10:20 !~ rds);
|
||||||
|
|
||||||
rds = [10:20, 100000:100..100000:200];
|
rds = [10:20, 100000:100..100000:200];
|
||||||
bt_assert(format(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:128 ~ rds);
|
||||||
bt_assert(100000:200 ~ rds);
|
bt_assert(100000:200 ~ rds);
|
||||||
bt_assert(100010:150 !~ rds);
|
bt_assert(100010:150 !~ rds);
|
||||||
|
bt_assert(100010:150 !~ []);
|
||||||
}
|
}
|
||||||
|
|
||||||
bt_test_suite(t_rd_set, "Testing sets of route distinguishers");
|
bt_test_suite(t_rd_set, "Testing sets of route distinguishers");
|
||||||
|
@ -1226,7 +1317,85 @@ function fifteen()
|
||||||
return 15;
|
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()
|
function t_call_function()
|
||||||
|
bgppath h_src;
|
||||||
{
|
{
|
||||||
bt_assert(fifteen() = 15);
|
bt_assert(fifteen() = 15);
|
||||||
|
|
||||||
|
@ -1238,6 +1407,17 @@ function t_call_function()
|
||||||
bt_assert(callme(4, 4) = 16);
|
bt_assert(callme(4, 4) = 16);
|
||||||
bt_assert(callme(7, 2) = 14);
|
bt_assert(callme(7, 2) = 14);
|
||||||
bt_assert(callmeagain(1, 2, 3) = 6);
|
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");
|
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.type != NET_IP6);
|
||||||
bt_assert(net.rd = 0:1:2);
|
bt_assert(net.rd = 0:1:2);
|
||||||
|
|
||||||
|
bool b = false;
|
||||||
case (net.type) {
|
case (net.type) {
|
||||||
NET_IP4: print "IPV4";
|
NET_IP4: print "IPV4";
|
||||||
NET_IP6: print "IPV6";
|
NET_IP6: print "IPV6";
|
||||||
|
else: b = true;
|
||||||
}
|
}
|
||||||
|
bt_assert(b);
|
||||||
|
|
||||||
bt_check_assign(from, 10.20.30.40);
|
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_community.add((3,5));
|
||||||
bgp_ext_community.add((ro, 135, 999));
|
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;
|
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
|
static void
|
||||||
tree_node_format(const struct f_tree *t, buffer *buf)
|
tree_node_format(const struct f_tree *t, buffer *buf)
|
||||||
|
|
|
@ -19,10 +19,7 @@ static void
|
||||||
start_conf_env(void)
|
start_conf_env(void)
|
||||||
{
|
{
|
||||||
bt_bird_init();
|
bt_bird_init();
|
||||||
|
cfg_mem = tmp_linpool;
|
||||||
pool *p = rp_new(&root_pool, "helper_pool");
|
|
||||||
linpool *l = lp_new_default(p);
|
|
||||||
cfg_mem = l;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct f_tree *
|
static struct f_tree *
|
||||||
|
|
|
@ -249,14 +249,14 @@ get_outer_net(net_addr *net, const struct f_prefix *src)
|
||||||
}
|
}
|
||||||
|
|
||||||
static list *
|
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);
|
init_list(prefixes);
|
||||||
|
|
||||||
for (int i = 0; i < num; i++)
|
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);
|
get_random_prefix(&px->prefix, v6, tight);
|
||||||
add_tail(prefixes, &px->n);
|
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 *
|
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;
|
struct f_prefix_node *n;
|
||||||
WALK_LIST(n, *prefixes)
|
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.
|
* Arg @plus means prefix should include all longer ones.
|
||||||
*/
|
*/
|
||||||
static list *
|
static list *
|
||||||
read_prefix_list(linpool *lp, FILE *f, int v6, int plus)
|
read_prefix_list(FILE *f, int v6, int plus)
|
||||||
{
|
{
|
||||||
ASSERT(!v6);
|
ASSERT(!v6);
|
||||||
|
|
||||||
|
@ -294,7 +294,7 @@ read_prefix_list(linpool *lp, FILE *f, int v6, int plus)
|
||||||
char s[32];
|
char s[32];
|
||||||
int n;
|
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);
|
init_list(pxlist);
|
||||||
|
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
@ -308,7 +308,7 @@ read_prefix_list(linpool *lp, FILE *f, int v6, int plus)
|
||||||
if (n != 5)
|
if (n != 5)
|
||||||
bt_abort_msg("Invalid content of trie_data");
|
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);
|
net_fill_ip4(&px->prefix.net, ip4_build(a0, a1, a2, a3), pl);
|
||||||
px->prefix.lo = pl;
|
px->prefix.lo = pl;
|
||||||
px->prefix.hi = plus ? IP4_MAX_PREFIX_LENGTH : 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
|
static int
|
||||||
read_prefix_file(const char *filename, int plus,
|
read_prefix_file(const char *filename, int plus,
|
||||||
linpool *lp0, linpool *lp1,
|
|
||||||
list *data[], struct f_trie *trie[])
|
list *data[], struct f_trie *trie[])
|
||||||
{
|
{
|
||||||
FILE *f = fopen(filename, "r");
|
FILE *f = fopen(filename, "r");
|
||||||
|
@ -339,10 +338,10 @@ read_prefix_file(const char *filename, int plus,
|
||||||
|
|
||||||
int n = 0;
|
int n = 0;
|
||||||
list *pxlist;
|
list *pxlist;
|
||||||
while (pxlist = read_prefix_list(lp0, f, 0, plus))
|
while (pxlist = read_prefix_list(f, 0, plus))
|
||||||
{
|
{
|
||||||
data[n] = pxlist;
|
data[n] = pxlist;
|
||||||
trie[n] = make_trie_from_prefix_list(lp1, pxlist);
|
trie[n] = make_trie_from_prefix_list(pxlist);
|
||||||
bt_debug("NEXT\n");
|
bt_debug("NEXT\n");
|
||||||
n++;
|
n++;
|
||||||
}
|
}
|
||||||
|
@ -437,11 +436,10 @@ t_match_random_net(void)
|
||||||
bt_config_parse(BT_CONFIG_SIMPLE);
|
bt_config_parse(BT_CONFIG_SIMPLE);
|
||||||
|
|
||||||
int v6 = 0;
|
int v6 = 0;
|
||||||
linpool *lp = lp_new_default(&root_pool);
|
|
||||||
for (int round = 0; round < TESTS_NUM; round++)
|
for (int round = 0; round < TESTS_NUM; round++)
|
||||||
{
|
{
|
||||||
list *prefixes = make_random_prefix_list(lp, PREFIXES_NUM, v6, 0);
|
list *prefixes = make_random_prefix_list(PREFIXES_NUM, v6, 0);
|
||||||
struct f_trie *trie = make_trie_from_prefix_list(lp, prefixes);
|
struct f_trie *trie = make_trie_from_prefix_list(prefixes);
|
||||||
|
|
||||||
for (int i = 0; i < PREFIX_TESTS_NUM; i++)
|
for (int i = 0; i < PREFIX_TESTS_NUM; i++)
|
||||||
{
|
{
|
||||||
|
@ -451,7 +449,7 @@ t_match_random_net(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
v6 = !v6;
|
v6 = !v6;
|
||||||
lp_flush(lp);
|
tmp_flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
bt_bird_cleanup();
|
bt_bird_cleanup();
|
||||||
|
@ -465,11 +463,10 @@ t_match_inner_net(void)
|
||||||
bt_config_parse(BT_CONFIG_SIMPLE);
|
bt_config_parse(BT_CONFIG_SIMPLE);
|
||||||
|
|
||||||
int v6 = 0;
|
int v6 = 0;
|
||||||
linpool *lp = lp_new_default(&root_pool);
|
|
||||||
for (int round = 0; round < TESTS_NUM; round++)
|
for (int round = 0; round < TESTS_NUM; round++)
|
||||||
{
|
{
|
||||||
list *prefixes = make_random_prefix_list(lp, PREFIXES_NUM, v6, 0);
|
list *prefixes = make_random_prefix_list(PREFIXES_NUM, v6, 0);
|
||||||
struct f_trie *trie = make_trie_from_prefix_list(lp, prefixes);
|
struct f_trie *trie = make_trie_from_prefix_list(prefixes);
|
||||||
|
|
||||||
struct f_prefix_node *n = HEAD(*prefixes);
|
struct f_prefix_node *n = HEAD(*prefixes);
|
||||||
for (int i = 0; i < PREFIX_TESTS_NUM; i++)
|
for (int i = 0; i < PREFIX_TESTS_NUM; i++)
|
||||||
|
@ -482,7 +479,7 @@ t_match_inner_net(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
v6 = !v6;
|
v6 = !v6;
|
||||||
lp_flush(lp);
|
tmp_flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
bt_bird_cleanup();
|
bt_bird_cleanup();
|
||||||
|
@ -496,11 +493,10 @@ t_match_outer_net(void)
|
||||||
bt_config_parse(BT_CONFIG_SIMPLE);
|
bt_config_parse(BT_CONFIG_SIMPLE);
|
||||||
|
|
||||||
int v6 = 0;
|
int v6 = 0;
|
||||||
linpool *lp = lp_new_default(&root_pool);
|
|
||||||
for (int round = 0; round < TESTS_NUM; round++)
|
for (int round = 0; round < TESTS_NUM; round++)
|
||||||
{
|
{
|
||||||
list *prefixes = make_random_prefix_list(lp, PREFIXES_NUM, v6, 0);
|
list *prefixes = make_random_prefix_list(PREFIXES_NUM, v6, 0);
|
||||||
struct f_trie *trie = make_trie_from_prefix_list(lp, prefixes);
|
struct f_trie *trie = make_trie_from_prefix_list(prefixes);
|
||||||
|
|
||||||
struct f_prefix_node *n = HEAD(*prefixes);
|
struct f_prefix_node *n = HEAD(*prefixes);
|
||||||
for (int i = 0; i < PREFIX_TESTS_NUM; i++)
|
for (int i = 0; i < PREFIX_TESTS_NUM; i++)
|
||||||
|
@ -513,7 +509,7 @@ t_match_outer_net(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
v6 = !v6;
|
v6 = !v6;
|
||||||
lp_flush(lp);
|
tmp_flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
v6 = !v6;
|
v6 = !v6;
|
||||||
|
@ -531,24 +527,22 @@ static int
|
||||||
benchmark_trie_dataset(const char *filename, int plus)
|
benchmark_trie_dataset(const char *filename, int plus)
|
||||||
{
|
{
|
||||||
int n = 0;
|
int n = 0;
|
||||||
linpool *lp0 = lp_new_default(&root_pool);
|
|
||||||
linpool *lp1 = lp_new_default(&root_pool);
|
|
||||||
list *data[TRIE_BUFFER_SIZE];
|
list *data[TRIE_BUFFER_SIZE];
|
||||||
struct f_trie *trie[TRIE_BUFFER_SIZE];
|
struct f_trie *trie[TRIE_BUFFER_SIZE];
|
||||||
net_addr *nets;
|
net_addr *nets;
|
||||||
|
|
||||||
bt_reset_suite_case_timer();
|
bt_reset_suite_case_timer();
|
||||||
bt_log_suite_case_result(1, "Reading %s", filename, n);
|
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);
|
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",
|
bt_log_suite_case_result(1, "Trie size %u.%03u MB",
|
||||||
(uint) (trie_size / 1000), (uint) (trie_size % 1000));
|
(uint) (trie_size / 1000), (uint) (trie_size % 1000));
|
||||||
|
|
||||||
int t = PREFIX_BENCH_NUM / n;
|
int t = PREFIX_BENCH_NUM / n;
|
||||||
int tb = MIN(t, TEST_BUFFER_SIZE);
|
int tb = MIN(t, TEST_BUFFER_SIZE);
|
||||||
nets = lp_alloc(lp0, tb * sizeof(net_addr));
|
nets = tmp_alloc(tb * sizeof(net_addr));
|
||||||
|
|
||||||
if (!plus)
|
if (!plus)
|
||||||
select_random_prefix_subset(data, nets, n, tb);
|
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);
|
bt_log_suite_case_result(1, "Matching done, %d / %d matches", match, t * n);
|
||||||
|
|
||||||
rfree(lp0);
|
tmp_flush();
|
||||||
rfree(lp1);
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -621,12 +613,11 @@ t_trie_same(void)
|
||||||
bt_config_parse(BT_CONFIG_SIMPLE);
|
bt_config_parse(BT_CONFIG_SIMPLE);
|
||||||
|
|
||||||
int v6 = 0;
|
int v6 = 0;
|
||||||
linpool *lp = lp_new_default(&root_pool);
|
|
||||||
for (int round = 0; round < TESTS_NUM*4; round++)
|
for (int round = 0; round < TESTS_NUM*4; round++)
|
||||||
{
|
{
|
||||||
list *prefixes = make_random_prefix_list(lp, 100 * PREFIXES_NUM, v6, 0);
|
list *prefixes = make_random_prefix_list(100 * PREFIXES_NUM, v6, 0);
|
||||||
struct f_trie *trie1 = f_new_trie(lp, 0);
|
struct f_trie *trie1 = f_new_trie(tmp_linpool, 0);
|
||||||
struct f_trie *trie2 = f_new_trie(lp, 0);
|
struct f_trie *trie2 = f_new_trie(tmp_linpool, 0);
|
||||||
|
|
||||||
struct f_prefix_node *n;
|
struct f_prefix_node *n;
|
||||||
WALK_LIST(n, *prefixes)
|
WALK_LIST(n, *prefixes)
|
||||||
|
@ -638,7 +629,7 @@ t_trie_same(void)
|
||||||
bt_assert(trie_same(trie1, trie2));
|
bt_assert(trie_same(trie1, trie2));
|
||||||
|
|
||||||
v6 = !v6;
|
v6 = !v6;
|
||||||
lp_flush(lp);
|
tmp_flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
bt_bird_cleanup();
|
bt_bird_cleanup();
|
||||||
|
@ -664,15 +655,14 @@ t_trie_walk(void)
|
||||||
bt_bird_init();
|
bt_bird_init();
|
||||||
bt_config_parse(BT_CONFIG_SIMPLE);
|
bt_config_parse(BT_CONFIG_SIMPLE);
|
||||||
|
|
||||||
linpool *lp = lp_new_default(&root_pool);
|
|
||||||
for (int round = 0; round < TESTS_NUM*8; round++)
|
for (int round = 0; round < TESTS_NUM*8; round++)
|
||||||
{
|
{
|
||||||
int level = round / TESTS_NUM;
|
int level = round / TESTS_NUM;
|
||||||
int v6 = level % 2;
|
int v6 = level % 2;
|
||||||
int num = PREFIXES_NUM * (int[]){1, 10, 100, 1000}[level / 2];
|
int num = PREFIXES_NUM * (int[]){1, 10, 100, 1000}[level / 2];
|
||||||
int pos = 0, end = 0;
|
int pos = 0, end = 0;
|
||||||
list *prefixes = make_random_prefix_list(lp, num, v6, 1);
|
list *prefixes = make_random_prefix_list(num, v6, 1);
|
||||||
struct f_trie *trie = make_trie_from_prefix_list(lp, prefixes);
|
struct f_trie *trie = make_trie_from_prefix_list(prefixes);
|
||||||
struct f_prefix *pxset = malloc((num + 1) * sizeof(struct f_prefix));
|
struct f_prefix *pxset = malloc((num + 1) * sizeof(struct f_prefix));
|
||||||
|
|
||||||
struct f_prefix_node *n;
|
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_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);
|
bt_debug("Subnet walk done for %s (found %d nets)\n", buf0, pos - p0);
|
||||||
|
|
||||||
lp_flush(lp);
|
tmp_flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
bt_bird_cleanup();
|
bt_bird_cleanup();
|
||||||
|
@ -815,7 +805,6 @@ t_trie_walk_to_root(void)
|
||||||
bt_bird_init();
|
bt_bird_init();
|
||||||
bt_config_parse(BT_CONFIG_SIMPLE);
|
bt_config_parse(BT_CONFIG_SIMPLE);
|
||||||
|
|
||||||
linpool *lp = lp_new_default(&root_pool);
|
|
||||||
for (int round = 0; round < TESTS_NUM * 4; round++)
|
for (int round = 0; round < TESTS_NUM * 4; round++)
|
||||||
{
|
{
|
||||||
int level = round / TESTS_NUM;
|
int level = round / TESTS_NUM;
|
||||||
|
@ -824,8 +813,8 @@ t_trie_walk_to_root(void)
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
int st = 0, sn = 0, sm = 0;
|
int st = 0, sn = 0, sm = 0;
|
||||||
|
|
||||||
list *prefixes = make_random_prefix_list(lp, num, v6, 1);
|
list *prefixes = make_random_prefix_list(num, v6, 1);
|
||||||
struct f_trie *trie = make_trie_from_prefix_list(lp, prefixes);
|
struct f_trie *trie = make_trie_from_prefix_list(prefixes);
|
||||||
struct f_prefix *pxset = malloc((num + 1) * sizeof(struct f_prefix));
|
struct f_prefix *pxset = malloc((num + 1) * sizeof(struct f_prefix));
|
||||||
|
|
||||||
struct f_prefix_node *pxn;
|
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);
|
bt_debug("Success in %d / %d, sum %d, max %d\n", sn, i, st, sm);
|
||||||
|
|
||||||
lp_flush(lp);
|
tmp_flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
bt_bird_cleanup();
|
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)
|
obj := $(src-o-files)
|
||||||
$(all-daemon)
|
$(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_targets := $(tests_targets) $(tests-target-files)
|
||||||
tests_objs := $(tests_objs) $(src-o-files)
|
tests_objs := $(tests_objs) $(src-o-files)
|
||||||
|
|
|
@ -24,7 +24,6 @@ t_bmap_set_clear_random(void)
|
||||||
{
|
{
|
||||||
struct bmap b;
|
struct bmap b;
|
||||||
|
|
||||||
resource_init();
|
|
||||||
bmap_init(&b, &root_pool, 1024);
|
bmap_init(&b, &root_pool, 1024);
|
||||||
|
|
||||||
char expected[MAX_NUM] = {};
|
char expected[MAX_NUM] = {};
|
||||||
|
@ -60,7 +59,6 @@ t_hmap_set_clear_random(void)
|
||||||
{
|
{
|
||||||
struct hmap b;
|
struct hmap b;
|
||||||
|
|
||||||
resource_init();
|
|
||||||
hmap_init(&b, &root_pool, 1024);
|
hmap_init(&b, &root_pool, 1024);
|
||||||
|
|
||||||
char expected[MAX_NUM] = {};
|
char expected[MAX_NUM] = {};
|
||||||
|
@ -119,7 +117,6 @@ t_hmap_set_clear_fill(void)
|
||||||
{
|
{
|
||||||
struct hmap b;
|
struct hmap b;
|
||||||
|
|
||||||
resource_init();
|
|
||||||
hmap_init(&b, &root_pool, 1024);
|
hmap_init(&b, &root_pool, 1024);
|
||||||
|
|
||||||
char expected[MAX_NUM] = {};
|
char expected[MAX_NUM] = {};
|
||||||
|
|
|
@ -41,7 +41,6 @@ fill_expected_array(void)
|
||||||
static void
|
static void
|
||||||
init_buffer(void)
|
init_buffer(void)
|
||||||
{
|
{
|
||||||
resource_init();
|
|
||||||
buffer_pool = &root_pool;
|
buffer_pool = &root_pool;
|
||||||
BUFFER_INIT(buf, buffer_pool, MAX_NUM);
|
BUFFER_INIT(buf, buffer_pool, MAX_NUM);
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,6 +157,7 @@ ev_run_list(event_list *l)
|
||||||
io_log_event(e->hook, e->data);
|
io_log_event(e->hook, e->data);
|
||||||
|
|
||||||
ev_run(e);
|
ev_run(e);
|
||||||
|
tmp_flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
return !EMPTY_LIST(*l);
|
return !EMPTY_LIST(*l);
|
||||||
|
@ -184,6 +185,7 @@ ev_run_list_limited(event_list *l, uint limit)
|
||||||
io_log_event(e->hook, e->data);
|
io_log_event(e->hook, e->data);
|
||||||
|
|
||||||
ev_run(e);
|
ev_run(e);
|
||||||
|
tmp_flush();
|
||||||
limit--;
|
limit--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,6 @@ t_ev_run_list(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
resource_init();
|
|
||||||
olock_init();
|
olock_init();
|
||||||
timer_init();
|
timer_init();
|
||||||
io_init();
|
io_init();
|
||||||
|
|
|
@ -446,10 +446,7 @@ t_validation6(void)
|
||||||
static int
|
static int
|
||||||
t_builder4(void)
|
t_builder4(void)
|
||||||
{
|
{
|
||||||
resource_init();
|
|
||||||
|
|
||||||
struct flow_builder *fb = flow_builder_init(&root_pool);
|
struct flow_builder *fb = flow_builder_init(&root_pool);
|
||||||
linpool *lp = lp_new_default(&root_pool);
|
|
||||||
|
|
||||||
/* Expectation */
|
/* Expectation */
|
||||||
|
|
||||||
|
@ -492,7 +489,7 @@ t_builder4(void)
|
||||||
flow_builder_set_type(fb, FLOW_TYPE_TCP_FLAGS);
|
flow_builder_set_type(fb, FLOW_TYPE_TCP_FLAGS);
|
||||||
flow_builder_add_op_val(fb, 0, 0x55);
|
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);
|
bt_assert(memcmp(res, expect, expect->length) == 0);
|
||||||
|
|
||||||
|
@ -529,8 +526,6 @@ t_builder6(void)
|
||||||
{
|
{
|
||||||
net_addr_ip6 ip;
|
net_addr_ip6 ip;
|
||||||
|
|
||||||
resource_init();
|
|
||||||
linpool *lp = lp_new_default(&root_pool);
|
|
||||||
struct flow_builder *fb = flow_builder_init(&root_pool);
|
struct flow_builder *fb = flow_builder_init(&root_pool);
|
||||||
fb->ipv6 = 1;
|
fb->ipv6 = 1;
|
||||||
|
|
||||||
|
@ -574,7 +569,7 @@ t_builder6(void)
|
||||||
flow_builder_set_type(fb, FLOW_TYPE_LABEL);
|
flow_builder_set_type(fb, FLOW_TYPE_LABEL);
|
||||||
flow_builder_add_op_val(fb, 0, 0x55);
|
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);
|
bt_assert(memcmp(res, expect, expect->length) == 0);
|
||||||
|
|
||||||
/* Reverse order */
|
/* Reverse order */
|
||||||
|
@ -601,7 +596,7 @@ t_builder6(void)
|
||||||
flow_builder_set_type(fb, FLOW_TYPE_DST_PREFIX);
|
flow_builder_set_type(fb, FLOW_TYPE_DST_PREFIX);
|
||||||
flow_builder6_add_pfx(fb, &ip, 61);
|
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);
|
bt_assert(memcmp(res, expect, expect->length) == 0);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -215,6 +215,12 @@ mem_hash_mix(u64 *h, const void *p, uint s)
|
||||||
*h = *h * multiplier + pp[i];
|
*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
|
static inline uint
|
||||||
mem_hash_value(u64 *h)
|
mem_hash_value(u64 *h)
|
||||||
{
|
{
|
||||||
|
|
|
@ -61,7 +61,6 @@ dump_nodes(void)
|
||||||
static void
|
static void
|
||||||
init_hash_(uint order)
|
init_hash_(uint order)
|
||||||
{
|
{
|
||||||
resource_init();
|
|
||||||
my_pool = rp_new(&root_pool, "Test pool");
|
my_pool = rp_new(&root_pool, "Test pool");
|
||||||
|
|
||||||
HASH_INIT(hash, my_pool, order);
|
HASH_INIT(hash, my_pool, order);
|
||||||
|
|
|
@ -42,6 +42,7 @@ typedef union list { /* In fact two overlayed nodes */
|
||||||
};
|
};
|
||||||
} list;
|
} list;
|
||||||
|
|
||||||
|
#define STATIC_LIST_INIT(name) name = { .head = &name.tail_node, .tail = &name.head_node, .null = NULL }
|
||||||
|
|
||||||
#define NODE (node *)
|
#define NODE (node *)
|
||||||
#define HEAD(list) ((void *)((list).head))
|
#define HEAD(list) ((void *)((list).head))
|
||||||
|
|
|
@ -27,21 +27,22 @@
|
||||||
|
|
||||||
struct lp_chunk {
|
struct lp_chunk {
|
||||||
struct lp_chunk *next;
|
struct lp_chunk *next;
|
||||||
uint size;
|
|
||||||
uintptr_t data_align[0];
|
uintptr_t data_align[0];
|
||||||
byte data[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 {
|
struct linpool {
|
||||||
resource r;
|
resource r;
|
||||||
byte *ptr, *end;
|
byte *ptr, *end;
|
||||||
struct lp_chunk *first, *current; /* Normal (reusable) chunks */
|
struct lp_chunk *first, *current; /* Normal (reusable) chunks */
|
||||||
struct lp_chunk *first_large; /* Large 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_free(resource *);
|
||||||
static void lp_dump(resource *);
|
static void lp_dump(resource *);
|
||||||
static resource *lp_lookup(resource *, unsigned long);
|
static resource *lp_lookup(resource *, unsigned long);
|
||||||
|
@ -59,19 +60,14 @@ static struct resclass lp_class = {
|
||||||
/**
|
/**
|
||||||
* lp_new - create a new linear memory pool
|
* lp_new - create a new linear memory pool
|
||||||
* @p: pool
|
* @p: pool
|
||||||
* @blk: block size
|
|
||||||
*
|
*
|
||||||
* lp_new() creates a new linear memory pool resource inside the pool @p.
|
* 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
|
* The linear pool consists of a list of memory chunks of page size.
|
||||||
* @blk.
|
|
||||||
*/
|
*/
|
||||||
linpool
|
linpool
|
||||||
*lp_new(pool *p, uint blk)
|
*lp_new(pool *p)
|
||||||
{
|
{
|
||||||
linpool *m = ralloc(p, &lp_class);
|
return ralloc(p, &lp_class);
|
||||||
m->chunk_size = blk;
|
|
||||||
m->threshold = 3*blk/4;
|
|
||||||
return m;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -102,14 +98,13 @@ lp_alloc(linpool *m, uint size)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
struct lp_chunk *c;
|
struct lp_chunk *c;
|
||||||
if (size >= m->threshold)
|
if (size > LP_DATA_SIZE)
|
||||||
{
|
{
|
||||||
/* Too large => allocate large chunk */
|
/* Too large => allocate large chunk */
|
||||||
c = xmalloc(sizeof(struct lp_chunk) + size);
|
c = xmalloc(sizeof(struct lp_chunk) + size);
|
||||||
m->total_large += size;
|
m->total_large += size;
|
||||||
c->next = m->first_large;
|
c->next = m->first_large;
|
||||||
m->first_large = c;
|
m->first_large = c;
|
||||||
c->size = size;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -121,10 +116,10 @@ lp_alloc(linpool *m, uint size)
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Need to allocate a new chunk */
|
/* Need to allocate a new chunk */
|
||||||
c = xmalloc(sizeof(struct lp_chunk) + m->chunk_size);
|
c = alloc_page();
|
||||||
m->total += m->chunk_size;
|
|
||||||
|
m->total += LP_DATA_SIZE;
|
||||||
c->next = NULL;
|
c->next = NULL;
|
||||||
c->size = m->chunk_size;
|
|
||||||
|
|
||||||
if (m->current)
|
if (m->current)
|
||||||
m->current->next = c;
|
m->current->next = c;
|
||||||
|
@ -133,7 +128,7 @@ lp_alloc(linpool *m, uint size)
|
||||||
}
|
}
|
||||||
m->current = c;
|
m->current = c;
|
||||||
m->ptr = c->data + size;
|
m->ptr = c->data + size;
|
||||||
m->end = c->data + m->chunk_size;
|
m->end = c->data + LP_DATA_SIZE;
|
||||||
}
|
}
|
||||||
return c->data;
|
return c->data;
|
||||||
}
|
}
|
||||||
|
@ -195,7 +190,7 @@ lp_flush(linpool *m)
|
||||||
/* Move ptr to the first chunk and free all large chunks */
|
/* Move ptr to the first chunk and free all large chunks */
|
||||||
m->current = c = m->first;
|
m->current = c = m->first;
|
||||||
m->ptr = c ? c->data : NULL;
|
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)
|
while (c = m->first_large)
|
||||||
{
|
{
|
||||||
|
@ -218,6 +213,7 @@ lp_save(linpool *m, lp_state *p)
|
||||||
{
|
{
|
||||||
p->current = m->current;
|
p->current = m->current;
|
||||||
p->large = m->first_large;
|
p->large = m->first_large;
|
||||||
|
p->total_large = m->total_large;
|
||||||
p->ptr = m->ptr;
|
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 */
|
/* Move ptr to the saved pos and free all newer large chunks */
|
||||||
m->current = c = p->current;
|
m->current = c = p->current;
|
||||||
m->ptr = p->ptr;
|
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))
|
while ((c = m->first_large) && (c != p->large))
|
||||||
{
|
{
|
||||||
m->first_large = c->next;
|
m->first_large = c->next;
|
||||||
m->total_large -= c->size;
|
|
||||||
xfree(c);
|
xfree(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -258,7 +254,7 @@ lp_free(resource *r)
|
||||||
for(d=m->first; d; d = c)
|
for(d=m->first; d; d = c)
|
||||||
{
|
{
|
||||||
c = d->next;
|
c = d->next;
|
||||||
xfree(d);
|
free_page(d);
|
||||||
}
|
}
|
||||||
for(d=m->first_large; d; d = c)
|
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++)
|
for(cntl=0, c=m->first_large; c; c=c->next, cntl++)
|
||||||
;
|
;
|
||||||
debug("(chunk=%d threshold=%d count=%d+%d total=%d+%d)\n",
|
debug("(count=%d+%d total=%d+%d)\n",
|
||||||
m->chunk_size,
|
|
||||||
m->threshold,
|
|
||||||
cnt,
|
cnt,
|
||||||
cntl,
|
cntl,
|
||||||
m->total,
|
m->total,
|
||||||
|
@ -291,19 +285,22 @@ static struct resmem
|
||||||
lp_memsize(resource *r)
|
lp_memsize(resource *r)
|
||||||
{
|
{
|
||||||
linpool *m = (linpool *) r;
|
linpool *m = (linpool *) r;
|
||||||
struct lp_chunk *c;
|
struct resmem sz = {
|
||||||
int cnt = 0;
|
.overhead = sizeof(struct linpool) + ALLOC_OVERHEAD,
|
||||||
|
.effective = m->total_large,
|
||||||
for(c=m->first; c; c=c->next)
|
|
||||||
cnt++;
|
|
||||||
for(c=m->first_large; c; c=c->next)
|
|
||||||
cnt++;
|
|
||||||
|
|
||||||
return (struct resmem) {
|
|
||||||
.effective = m->total + m->total_large,
|
|
||||||
.overhead = ALLOC_OVERHEAD + sizeof(struct linpool) +
|
|
||||||
cnt * (ALLOC_OVERHEAD + sizeof(struct lp_chunk)),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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;
|
struct lp_chunk *c;
|
||||||
|
|
||||||
for(c=m->first; c; c=c->next)
|
for(c=m->first; 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;
|
|
||||||
for(c=m->first_large; c; c=c->next)
|
|
||||||
if ((unsigned long) c->data <= a && (unsigned long) c->data + c->size > a)
|
|
||||||
return r;
|
return r;
|
||||||
return NULL;
|
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;
|
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;
|
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
|
static void
|
||||||
pool_free(resource *P)
|
pool_free(resource *P)
|
||||||
{
|
{
|
||||||
|
@ -270,9 +284,12 @@ rlookup(unsigned long a)
|
||||||
void
|
void
|
||||||
resource_init(void)
|
resource_init(void)
|
||||||
{
|
{
|
||||||
|
resource_sys_init();
|
||||||
|
|
||||||
root_pool.r.class = &pool_class;
|
root_pool.r.class = &pool_class;
|
||||||
root_pool.name = "Root";
|
root_pool.name = "Root";
|
||||||
init_list(&root_pool.inside);
|
init_list(&root_pool.inside);
|
||||||
|
tmp_init(&root_pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -407,21 +424,6 @@ mb_realloc(void *m, unsigned size)
|
||||||
return b->data;
|
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
|
* mb_free - free a memory block
|
||||||
|
|
|
@ -44,6 +44,7 @@ typedef struct pool pool;
|
||||||
|
|
||||||
void resource_init(void);
|
void resource_init(void);
|
||||||
pool *rp_new(pool *, const char *); /* Create new pool */
|
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 rfree(void *); /* Free single resource */
|
||||||
void rdump(void *); /* Dump to debug output */
|
void rdump(void *); /* Dump to debug output */
|
||||||
struct resmem rmemsize(void *res); /* Return size of memory used by the resource */
|
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_alloc(pool *, unsigned size);
|
||||||
void *mb_allocz(pool *, unsigned size);
|
void *mb_allocz(pool *, unsigned size);
|
||||||
void *mb_realloc(void *m, unsigned size);
|
void *mb_realloc(void *m, unsigned size);
|
||||||
void mb_move(void *, pool *);
|
|
||||||
void mb_free(void *);
|
void mb_free(void *);
|
||||||
|
|
||||||
/* Memory pools with linear allocation */
|
/* Memory pools with linear allocation */
|
||||||
|
@ -69,9 +69,10 @@ typedef struct linpool linpool;
|
||||||
typedef struct lp_state {
|
typedef struct lp_state {
|
||||||
void *current, *large;
|
void *current, *large;
|
||||||
byte *ptr;
|
byte *ptr;
|
||||||
|
uint total_large;
|
||||||
} lp_state;
|
} lp_state;
|
||||||
|
|
||||||
linpool *lp_new(pool *, unsigned blk);
|
linpool *lp_new(pool *);
|
||||||
void *lp_alloc(linpool *, unsigned size); /* Aligned */
|
void *lp_alloc(linpool *, unsigned size); /* Aligned */
|
||||||
void *lp_allocu(linpool *, unsigned size); /* Unaligned */
|
void *lp_allocu(linpool *, unsigned size); /* Unaligned */
|
||||||
void *lp_allocz(linpool *, unsigned size); /* With clear */
|
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_save(linpool *m, lp_state *p); /* Save state */
|
||||||
void lp_restore(linpool *m, lp_state *p); /* Restore state */
|
void lp_restore(linpool *m, lp_state *p); /* Restore state */
|
||||||
|
|
||||||
extern const int lp_chunk_size;
|
extern _Thread_local linpool *tmp_linpool; /* Temporary linpool autoflushed regularily */
|
||||||
#define LP_GAS 1024
|
|
||||||
#define LP_GOOD_SIZE(x) (((x + LP_GAS - 1) & (~(LP_GAS - 1))) - lp_chunk_size)
|
#define tmp_alloc(sz) lp_alloc(tmp_linpool, sz)
|
||||||
#define lp_new_default(p) lp_new(p, LP_GOOD_SIZE(LP_GAS*4))
|
#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 */
|
/* Slabs */
|
||||||
|
|
||||||
|
@ -91,7 +98,7 @@ typedef struct slab slab;
|
||||||
slab *sl_new(pool *, unsigned size);
|
slab *sl_new(pool *, unsigned size);
|
||||||
void *sl_alloc(slab *);
|
void *sl_alloc(slab *);
|
||||||
void *sl_allocz(slab *);
|
void *sl_allocz(slab *);
|
||||||
void sl_free(slab *, void *);
|
void sl_free(void *);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Low-level memory allocation functions, please don't use
|
* 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);
|
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. */
|
/* 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 *alloc_page(void);
|
||||||
void free_page(void *);
|
void free_page(void *);
|
||||||
extern uint pages_kept;
|
|
||||||
|
void resource_sys_init(void);
|
||||||
|
|
||||||
#ifdef HAVE_LIBDMALLOC
|
#ifdef HAVE_LIBDMALLOC
|
||||||
/*
|
/*
|
||||||
|
|
128
lib/slab.c
128
lib/slab.c
|
@ -32,6 +32,7 @@
|
||||||
#include "nest/bird.h"
|
#include "nest/bird.h"
|
||||||
#include "lib/resource.h"
|
#include "lib/resource.h"
|
||||||
#include "lib/string.h"
|
#include "lib/string.h"
|
||||||
|
#include "lib/tlists.h"
|
||||||
|
|
||||||
#undef FAKE_SLAB /* Turn on if you want to debug memory allocations */
|
#undef FAKE_SLAB /* Turn on if you want to debug memory allocations */
|
||||||
|
|
||||||
|
@ -98,7 +99,7 @@ sl_allocz(slab *s)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
sl_free(slab *s, void *oo)
|
sl_free(void *oo)
|
||||||
{
|
{
|
||||||
struct sl_obj *o = SKIP_BACK(struct sl_obj, data, 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
|
#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 {
|
struct slab {
|
||||||
resource r;
|
resource r;
|
||||||
uint obj_size, head_size, head_bitfield_len;
|
uint obj_size, head_size, head_bitfield_len;
|
||||||
uint objs_per_slab, num_empty_heads, data_size;
|
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 = {
|
static struct resclass sl_class = {
|
||||||
|
@ -169,18 +197,15 @@ static struct resclass sl_class = {
|
||||||
slab_memsize
|
slab_memsize
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sl_head {
|
#define SL_GET_HEAD(x) ((struct sl_head *) (((uintptr_t) (x)) & ~(page_size-1)))
|
||||||
node n;
|
|
||||||
u32 num_full;
|
|
||||||
u32 used_bits[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sl_alignment { /* Magic structure for testing of alignment */
|
#define SL_HEAD_CHANGE_STATE(_s, _h, _from, _to) ({ \
|
||||||
byte data;
|
ASSERT_DIE(_h->state == slh_##_from); \
|
||||||
int x[0];
|
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
|
* sl_new - create a new Slab
|
||||||
|
@ -202,7 +227,6 @@ sl_new(pool *p, uint size)
|
||||||
s->obj_size = size;
|
s->obj_size = size;
|
||||||
|
|
||||||
s->head_size = sizeof(struct sl_head);
|
s->head_size = sizeof(struct sl_head);
|
||||||
u64 page_size = get_page_size();
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
s->objs_per_slab = (page_size - s->head_size) / size;
|
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");
|
bug("Slab: object too large");
|
||||||
s->num_empty_heads = 0;
|
s->num_empty_heads = 0;
|
||||||
|
|
||||||
init_list(&s->empty_heads);
|
|
||||||
init_list(&s->partial_heads);
|
|
||||||
init_list(&s->full_heads);
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,8 +258,7 @@ sl_alloc(slab *s)
|
||||||
struct sl_head *h;
|
struct sl_head *h;
|
||||||
|
|
||||||
redo:
|
redo:
|
||||||
h = HEAD(s->partial_heads);
|
if (!(h = s->partial_heads.first))
|
||||||
if (!h->n.next)
|
|
||||||
goto no_partial;
|
goto no_partial;
|
||||||
okay:
|
okay:
|
||||||
for (uint i=0; i<s->head_bitfield_len; i++)
|
for (uint i=0; i<s->head_bitfield_len; i++)
|
||||||
|
@ -258,23 +278,27 @@ okay:
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
rem_node(&h->n);
|
SL_HEAD_CHANGE_STATE(s, h, partial, full);
|
||||||
add_tail(&s->full_heads, &h->n);
|
|
||||||
goto redo;
|
goto redo;
|
||||||
|
|
||||||
no_partial:
|
no_partial:
|
||||||
h = HEAD(s->empty_heads);
|
if (h = s->empty_heads.first)
|
||||||
if (h->n.next)
|
|
||||||
{
|
{
|
||||||
rem_node(&h->n);
|
SL_HEAD_CHANGE_STATE(s, h, empty, partial);
|
||||||
add_head(&s->partial_heads, &h->n);
|
|
||||||
s->num_empty_heads--;
|
s->num_empty_heads--;
|
||||||
goto okay;
|
goto okay;
|
||||||
}
|
}
|
||||||
|
|
||||||
h = alloc_page();
|
h = alloc_page();
|
||||||
ASSERT_DIE(SL_GET_HEAD(h) == h);
|
ASSERT_DIE(SL_GET_HEAD(h) == h);
|
||||||
|
|
||||||
|
#ifdef POISON
|
||||||
|
memset(h, 0xba, page_size);
|
||||||
|
#endif
|
||||||
|
|
||||||
memset(h, 0, s->head_size);
|
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;
|
goto okay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,9 +327,10 @@ sl_allocz(slab *s)
|
||||||
* and returns it back to the Slab @s.
|
* and returns it back to the Slab @s.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
sl_free(slab *s, void *oo)
|
sl_free(void *oo)
|
||||||
{
|
{
|
||||||
struct sl_head *h = SL_GET_HEAD(oo);
|
struct sl_head *h = SL_GET_HEAD(oo);
|
||||||
|
struct slab *s = h->slab;
|
||||||
|
|
||||||
#ifdef POISON
|
#ifdef POISON
|
||||||
memset(oo, 0xdb, s->data_size);
|
memset(oo, 0xdb, s->data_size);
|
||||||
|
@ -318,19 +343,22 @@ sl_free(slab *s, void *oo)
|
||||||
|
|
||||||
h->used_bits[pos / 32] &= ~(1 << (pos % 32));
|
h->used_bits[pos / 32] &= ~(1 << (pos % 32));
|
||||||
|
|
||||||
if (h->num_full-- == s->objs_per_slab)
|
if ((h->num_full-- == s->objs_per_slab) && (h->state == slh_full))
|
||||||
{
|
SL_HEAD_CHANGE_STATE(s, h, full, partial);
|
||||||
rem_node(&h->n);
|
|
||||||
add_head(&s->partial_heads, &h->n);
|
|
||||||
}
|
|
||||||
else if (!h->num_full)
|
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)
|
if (s->num_empty_heads >= MAX_EMPTY_HEADS)
|
||||||
|
{
|
||||||
|
#ifdef POISON
|
||||||
|
memset(h, 0xde, page_size);
|
||||||
|
#endif
|
||||||
free_page(h);
|
free_page(h);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
add_head(&s->empty_heads, &h->n);
|
sl_head_add_head(&s->empty_heads, h);
|
||||||
|
h->state = slh_empty;
|
||||||
s->num_empty_heads++;
|
s->num_empty_heads++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -340,13 +368,12 @@ static void
|
||||||
slab_free(resource *r)
|
slab_free(resource *r)
|
||||||
{
|
{
|
||||||
slab *s = (slab *) 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);
|
free_page(h);
|
||||||
WALK_LIST_DELSAFE(h, g, s->partial_heads)
|
WALK_TLIST_DELSAFE(sl_head, h, &s->partial_heads)
|
||||||
free_page(h);
|
free_page(h);
|
||||||
WALK_LIST_DELSAFE(h, g, s->full_heads)
|
WALK_TLIST_DELSAFE(sl_head, h, &s->full_heads)
|
||||||
free_page(h);
|
free_page(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -355,13 +382,12 @@ slab_dump(resource *r)
|
||||||
{
|
{
|
||||||
slab *s = (slab *) r;
|
slab *s = (slab *) r;
|
||||||
int ec=0, pc=0, fc=0;
|
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++;
|
ec++;
|
||||||
WALK_LIST(h, s->partial_heads)
|
WALK_TLIST(sl_head, h, &s->partial_heads)
|
||||||
pc++;
|
pc++;
|
||||||
WALK_LIST(h, s->full_heads)
|
WALK_TLIST(sl_head, h, &s->full_heads)
|
||||||
fc++;
|
fc++;
|
||||||
debug("(%de+%dp+%df blocks per %d objs per %d bytes)\n", ec, pc, fc, s->objs_per_slab, s->obj_size);
|
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;
|
slab *s = (slab *) r;
|
||||||
size_t heads = 0;
|
size_t heads = 0;
|
||||||
struct sl_head *h;
|
|
||||||
|
|
||||||
WALK_LIST(h, s->full_heads)
|
WALK_TLIST(sl_head, h, &s->full_heads)
|
||||||
heads++;
|
heads++;
|
||||||
|
|
||||||
size_t items = heads * s->objs_per_slab;
|
size_t items = heads * s->objs_per_slab;
|
||||||
|
|
||||||
WALK_LIST(h, s->partial_heads)
|
WALK_TLIST(sl_head, h, &s->partial_heads)
|
||||||
{
|
{
|
||||||
heads++;
|
heads++;
|
||||||
items += h->num_full;
|
items += h->num_full;
|
||||||
}
|
}
|
||||||
|
|
||||||
WALK_LIST(h, s->empty_heads)
|
WALK_TLIST(sl_head, h, &s->empty_heads)
|
||||||
heads++;
|
heads++;
|
||||||
|
|
||||||
size_t eff = items * s->obj_size;
|
size_t eff = items * s->data_size;
|
||||||
|
|
||||||
return (struct resmem) {
|
return (struct resmem) {
|
||||||
.effective = eff,
|
.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_lookup(resource *r, unsigned long a)
|
||||||
{
|
{
|
||||||
slab *s = (slab *) r;
|
slab *s = (slab *) r;
|
||||||
struct sl_head *h;
|
|
||||||
|
|
||||||
WALK_LIST(h, s->partial_heads)
|
WALK_TLIST(sl_head, h, &s->partial_heads)
|
||||||
if ((unsigned long) h < a && (unsigned long) h + get_page_size() < a)
|
if ((unsigned long) h < a && (unsigned long) h + page_size < a)
|
||||||
return r;
|
return r;
|
||||||
WALK_LIST(h, s->full_heads)
|
WALK_TLIST(sl_head, h, &s->full_heads)
|
||||||
if ((unsigned long) h < a && (unsigned long) h + get_page_size() < a)
|
if ((unsigned long) h < a && (unsigned long) h + page_size < a)
|
||||||
return r;
|
return r;
|
||||||
return NULL;
|
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 bsnprintf(char *str, int size, const char *fmt, ...);
|
||||||
int bvsnprintf(char *str, int size, const char *fmt, va_list args);
|
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_vprint(buffer *buf, const char *fmt, va_list args);
|
||||||
int buffer_print(buffer *buf, const char *fmt, ...);
|
int buffer_print(buffer *buf, const char *fmt, ...);
|
||||||
void buffer_puts(buffer *buf, const char *str);
|
void buffer_puts(buffer *buf, const char *str);
|
||||||
|
|
14
lib/timer.c
14
lib/timer.c
|
@ -76,6 +76,19 @@ current_time(void)
|
||||||
return timeloop_current()->last_time;
|
return timeloop_current()->last_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
btime
|
||||||
|
current_time_now(void)
|
||||||
|
{
|
||||||
|
struct timespec ts;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
rv = clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
|
if (rv < 0)
|
||||||
|
die("clock_gettime: %m");
|
||||||
|
|
||||||
|
return ts.tv_sec S + ts.tv_nsec NS;
|
||||||
|
}
|
||||||
|
|
||||||
btime
|
btime
|
||||||
current_real_time(void)
|
current_real_time(void)
|
||||||
{
|
{
|
||||||
|
@ -233,6 +246,7 @@ timers_fire(struct timeloop *loop)
|
||||||
io_log_event(t->hook, t->data);
|
io_log_event(t->hook, t->data);
|
||||||
|
|
||||||
t->hook(t);
|
t->hook(t);
|
||||||
|
tmp_flush();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ static inline timer *timers_first(struct timeloop *loop)
|
||||||
extern struct timeloop main_timeloop;
|
extern struct timeloop main_timeloop;
|
||||||
|
|
||||||
btime current_time(void);
|
btime current_time(void);
|
||||||
|
btime current_time_now(void);
|
||||||
btime current_real_time(void);
|
btime current_real_time(void);
|
||||||
|
|
||||||
//#define now (current_time() TO_S)
|
//#define now (current_time() TO_S)
|
||||||
|
|
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
|
Summary: BIRD Internet Routing Daemon
|
||||||
Name: bird
|
Name: bird
|
||||||
Version: 2.0.9
|
Version: 2.0.11
|
||||||
Release: 1
|
Release: 1
|
||||||
Copyright: GPL
|
Copyright: GPL
|
||||||
Group: Networking/Daemons
|
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)
|
obj := $(src-o-files)
|
||||||
$(all-daemon)
|
$(all-daemon)
|
||||||
$(cf-local)
|
$(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_src := a-set_test.c a-path_test.c
|
||||||
tests_targets := $(tests_targets) $(tests-target-files)
|
tests_targets := $(tests_targets) $(tests-target-files)
|
||||||
tests_objs := $(tests_objs) $(src-o-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;
|
p += 2;
|
||||||
for (i=0; i<n; i++)
|
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))
|
if (find_tree(set, &v))
|
||||||
return 1;
|
return 1;
|
||||||
p += BS;
|
p += BS;
|
||||||
|
@ -602,8 +602,10 @@ as_path_match_set(const struct adata *path, const struct f_tree *set)
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct adata *
|
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)
|
if (!path)
|
||||||
return NULL;
|
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);
|
u32 as = get_as(p);
|
||||||
int match;
|
int match;
|
||||||
|
|
||||||
if (set)
|
if (set->type == T_SET)
|
||||||
{
|
{
|
||||||
struct f_val v = {T_INT, .val.i = as};
|
struct f_val v = { .type = T_INT, .val.i = as};
|
||||||
match = !!find_tree(set, &v);
|
match = !!find_tree(set->val.t, &v);
|
||||||
}
|
}
|
||||||
else
|
else /* T_INT */
|
||||||
match = (as == key);
|
match = (as == set->val.i);
|
||||||
|
|
||||||
if (match == pos)
|
if (match == pos)
|
||||||
{
|
{
|
||||||
|
@ -667,6 +669,35 @@ as_path_filter(struct linpool *pool, const struct adata *path, const struct f_tr
|
||||||
return res;
|
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
|
struct pm_pos
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "nest/route.h"
|
#include "nest/route.h"
|
||||||
#include "nest/attrs.h"
|
#include "nest/attrs.h"
|
||||||
#include "lib/resource.h"
|
#include "lib/resource.h"
|
||||||
|
#include "filter/data.h"
|
||||||
|
|
||||||
#define TESTS_NUM 30
|
#define TESTS_NUM 30
|
||||||
#define AS_PATH_LENGTH 1000
|
#define AS_PATH_LENGTH 1000
|
||||||
|
@ -23,8 +24,6 @@
|
||||||
static int
|
static int
|
||||||
t_as_path_match(void)
|
t_as_path_match(void)
|
||||||
{
|
{
|
||||||
resource_init();
|
|
||||||
|
|
||||||
int round;
|
int round;
|
||||||
for (round = 0; round < TESTS_NUM; round++)
|
for (round = 0; round < TESTS_NUM; round++)
|
||||||
{
|
{
|
||||||
|
@ -32,14 +31,13 @@ t_as_path_match(void)
|
||||||
struct adata *as_path = &empty_as_path;
|
struct adata *as_path = &empty_as_path;
|
||||||
u32 first_prepended, last_prepended;
|
u32 first_prepended, last_prepended;
|
||||||
first_prepended = last_prepended = 0;
|
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));
|
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;
|
mask->len = AS_PATH_LENGTH;
|
||||||
for (int i = AS_PATH_LENGTH - 1; i >= 0; i--)
|
for (int i = AS_PATH_LENGTH - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
u32 val = bt_random();
|
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);
|
bt_debug("Prepending ASN: %10u \n", val);
|
||||||
|
|
||||||
if (i == 0)
|
if (i == 0)
|
||||||
|
@ -61,7 +59,7 @@ t_as_path_match(void)
|
||||||
bt_assert(as_path_get_last(as_path, &asn));
|
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");
|
bt_assert_msg(asn == first_prepended, "as_path_get_last() should return the first prepended ASN");
|
||||||
|
|
||||||
rfree(lp);
|
tmp_flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -70,16 +68,13 @@ t_as_path_match(void)
|
||||||
static int
|
static int
|
||||||
t_path_format(void)
|
t_path_format(void)
|
||||||
{
|
{
|
||||||
resource_init();
|
|
||||||
|
|
||||||
struct adata empty_as_path = {};
|
struct adata empty_as_path = {};
|
||||||
struct adata *as_path = &empty_as_path;
|
struct adata *as_path = &empty_as_path;
|
||||||
struct linpool *lp = lp_new_default(&root_pool);
|
|
||||||
|
|
||||||
uint i;
|
uint i;
|
||||||
for (i = 4294967285; i <= 4294967294; 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);
|
bt_debug("Prepending ASN: %10u \n", i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +92,7 @@ t_path_format(void)
|
||||||
as_path_format(as_path, buf2, SMALL_BUFFER_SIZE);
|
as_path_format(as_path, buf2, SMALL_BUFFER_SIZE);
|
||||||
bt_assert_msg(strcmp(buf2, "4294967294 42...") == 0, "Small Buffer(%zu): '%s'", strlen(buf2), buf2);
|
bt_assert_msg(strcmp(buf2, "4294967294 42...") == 0, "Small Buffer(%zu): '%s'", strlen(buf2), buf2);
|
||||||
|
|
||||||
rfree(lp);
|
tmp_flush();
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -116,11 +111,8 @@ count_asn_in_array(const u32 *array, u32 asn)
|
||||||
static int
|
static int
|
||||||
t_path_include(void)
|
t_path_include(void)
|
||||||
{
|
{
|
||||||
resource_init();
|
|
||||||
|
|
||||||
struct adata empty_as_path = {};
|
struct adata empty_as_path = {};
|
||||||
struct adata *as_path = &empty_as_path;
|
struct adata *as_path = &empty_as_path;
|
||||||
struct linpool *lp = lp_new_default(&root_pool);
|
|
||||||
|
|
||||||
u32 as_nums[AS_PATH_LENGTH] = {};
|
u32 as_nums[AS_PATH_LENGTH] = {};
|
||||||
int i;
|
int i;
|
||||||
|
@ -128,7 +120,7 @@ t_path_include(void)
|
||||||
{
|
{
|
||||||
u32 val = bt_random();
|
u32 val = bt_random();
|
||||||
as_nums[i] = val;
|
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++)
|
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]);
|
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_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);
|
struct f_val v = { .type = T_INT, .val.i = as_nums[i] };
|
||||||
bt_assert(as_path_filter(lp, as_path, NULL, as_nums[i], 1) != NULL);
|
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++)
|
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);
|
bt_assert_msg(result == 0, "As path should not contain the number %u", test_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
rfree(lp);
|
tmp_flush();
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -161,16 +154,13 @@ t_path_include(void)
|
||||||
static int
|
static int
|
||||||
t_as_path_converting(void)
|
t_as_path_converting(void)
|
||||||
{
|
{
|
||||||
resource_init();
|
|
||||||
|
|
||||||
struct adata empty_as_path = {};
|
struct adata empty_as_path = {};
|
||||||
struct adata *as_path = &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
|
#define AS_PATH_LENGTH_FOR_CONVERTING_TEST 10
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < AS_PATH_LENGTH_FOR_CONVERTING_TEST; 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);
|
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] };
|
*val = (lcomm) { res[0], res[1], res[2] };
|
||||||
return 1;
|
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
|
#define SET_SIZE_FOR_FORMAT_OUTPUT 10
|
||||||
|
|
||||||
struct linpool *lp;
|
|
||||||
|
|
||||||
enum set_type
|
enum set_type
|
||||||
{
|
{
|
||||||
SET_TYPE_INT,
|
SET_TYPE_INT,
|
||||||
|
@ -38,24 +36,23 @@ generate_set_sequence(enum set_type type, int len)
|
||||||
{
|
{
|
||||||
struct adata empty_as_path = {};
|
struct adata empty_as_path = {};
|
||||||
set_sequence = set_sequence_same = set_sequence_higher = set_random = &empty_as_path;
|
set_sequence = set_sequence_same = set_sequence_higher = set_random = &empty_as_path;
|
||||||
lp = lp_new_default(&root_pool);
|
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < len; i++)
|
for (i = 0; i < len; i++)
|
||||||
{
|
{
|
||||||
if (type == SET_TYPE_INT)
|
if (type == SET_TYPE_INT)
|
||||||
{
|
{
|
||||||
set_sequence = int_set_add(lp, set_sequence, i);
|
set_sequence = int_set_add(tmp_linpool, set_sequence, i);
|
||||||
set_sequence_same = int_set_add(lp, set_sequence_same, i);
|
set_sequence_same = int_set_add(tmp_linpool, set_sequence_same, i);
|
||||||
set_sequence_higher = int_set_add(lp, set_sequence_higher, i + SET_SIZE);
|
set_sequence_higher = int_set_add(tmp_linpool, set_sequence_higher, i + SET_SIZE);
|
||||||
set_random = int_set_add(lp, set_random, bt_random());
|
set_random = int_set_add(tmp_linpool, set_random, bt_random());
|
||||||
}
|
}
|
||||||
else if (type == SET_TYPE_EC)
|
else if (type == SET_TYPE_EC)
|
||||||
{
|
{
|
||||||
set_sequence = ec_set_add(lp, set_sequence, i);
|
set_sequence = ec_set_add(tmp_linpool, set_sequence, i);
|
||||||
set_sequence_same = ec_set_add(lp, set_sequence_same, i);
|
set_sequence_same = ec_set_add(tmp_linpool, set_sequence_same, i);
|
||||||
set_sequence_higher = ec_set_add(lp, set_sequence_higher, i + SET_SIZE);
|
set_sequence_higher = ec_set_add(tmp_linpool, set_sequence_higher, i + SET_SIZE);
|
||||||
set_random = ec_set_add(lp, set_random, (bt_random() << 32 | bt_random()));
|
set_random = ec_set_add(tmp_linpool, set_random, (bt_random() << 32 | bt_random()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
bt_abort_msg("This should be unreachable");
|
bt_abort_msg("This should be unreachable");
|
||||||
|
@ -71,7 +68,6 @@ t_set_int_contains(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
resource_init();
|
|
||||||
generate_set_sequence(SET_TYPE_INT, SET_SIZE);
|
generate_set_sequence(SET_TYPE_INT, SET_SIZE);
|
||||||
|
|
||||||
bt_assert(int_set_get_size(set_sequence) == 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++)
|
for (i = 0; i < SET_SIZE; i++)
|
||||||
bt_assert_msg(data[i] == i, "(data[i] = %d) == i = %d)", data[i], i);
|
bt_assert_msg(data[i] == i, "(data[i] = %d) == i = %d)", data[i], i);
|
||||||
|
|
||||||
rfree(lp);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
t_set_int_union(void)
|
t_set_int_union(void)
|
||||||
{
|
{
|
||||||
resource_init();
|
|
||||||
generate_set_sequence(SET_TYPE_INT, SET_SIZE);
|
generate_set_sequence(SET_TYPE_INT, SET_SIZE);
|
||||||
|
|
||||||
const struct adata *set_union;
|
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_get_size(set_union) == SET_SIZE);
|
||||||
bt_assert(int_set_format(set_union, 0, 2, buf, BUFFER_SIZE) == 0);
|
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_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);
|
bt_assert(int_set_format(set_union, 0, 2, buf, BUFFER_SIZE) == 0);
|
||||||
|
|
||||||
rfree(lp);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
t_set_int_format(void)
|
t_set_int_format(void)
|
||||||
{
|
{
|
||||||
resource_init();
|
|
||||||
generate_set_sequence(SET_TYPE_INT, SET_SIZE_FOR_FORMAT_OUTPUT);
|
generate_set_sequence(SET_TYPE_INT, SET_SIZE_FOR_FORMAT_OUTPUT);
|
||||||
|
|
||||||
bt_assert(int_set_format(set_sequence, 0, 0, buf, BUFFER_SIZE) == 0);
|
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(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);
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
t_set_int_delete(void)
|
t_set_int_delete(void)
|
||||||
{
|
{
|
||||||
resource_init();
|
|
||||||
generate_set_sequence(SET_TYPE_INT, SET_SIZE);
|
generate_set_sequence(SET_TYPE_INT, SET_SIZE);
|
||||||
|
|
||||||
const struct adata *deleting_sequence = set_sequence;
|
const struct adata *deleting_sequence = set_sequence;
|
||||||
u32 i;
|
u32 i;
|
||||||
for (i = 0; i < SET_SIZE; 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),
|
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) %d == SET_SIZE-1-i %d",
|
||||||
int_set_get_size(deleting_sequence),
|
int_set_get_size(deleting_sequence),
|
||||||
|
@ -160,7 +150,6 @@ t_set_ec_contains(void)
|
||||||
{
|
{
|
||||||
u32 i;
|
u32 i;
|
||||||
|
|
||||||
resource_init();
|
|
||||||
generate_set_sequence(SET_TYPE_EC, SET_SIZE);
|
generate_set_sequence(SET_TYPE_EC, SET_SIZE);
|
||||||
|
|
||||||
bt_assert(ec_set_get_size(set_sequence) == 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++)
|
// 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);
|
// 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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
t_set_ec_union(void)
|
t_set_ec_union(void)
|
||||||
{
|
{
|
||||||
resource_init();
|
|
||||||
generate_set_sequence(SET_TYPE_EC, SET_SIZE);
|
generate_set_sequence(SET_TYPE_EC, SET_SIZE);
|
||||||
|
|
||||||
const struct adata *set_union;
|
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_get_size(set_union) == SET_SIZE);
|
||||||
bt_assert(ec_set_format(set_union, 0, buf, BUFFER_SIZE) == 0);
|
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_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);
|
bt_assert(ec_set_format(set_union, 0, buf, BUFFER_SIZE) == 0);
|
||||||
|
|
||||||
rfree(lp);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
t_set_ec_format(void)
|
t_set_ec_format(void)
|
||||||
{
|
{
|
||||||
resource_init();
|
|
||||||
|
|
||||||
const struct adata empty_as_path = {};
|
const struct adata empty_as_path = {};
|
||||||
set_sequence = set_sequence_same = set_sequence_higher = set_random = &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;
|
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++)
|
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(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,
|
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);
|
"ec_set_format() returns '%s'", buf);
|
||||||
|
|
||||||
rfree(lp);
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
t_set_ec_delete(void)
|
t_set_ec_delete(void)
|
||||||
{
|
{
|
||||||
resource_init();
|
|
||||||
generate_set_sequence(SET_TYPE_EC, SET_SIZE);
|
generate_set_sequence(SET_TYPE_EC, SET_SIZE);
|
||||||
|
|
||||||
const struct adata *deleting_sequence = set_sequence;
|
const struct adata *deleting_sequence = set_sequence;
|
||||||
u32 i;
|
u32 i;
|
||||||
for (i = 0; i < SET_SIZE; 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),
|
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) %d == SET_SIZE-1-i %d",
|
||||||
ec_set_get_size(deleting_sequence), SET_SIZE-1-i);
|
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
|
* to 16bit slot (like in 16bit AS_PATH). See RFC 4893 for details
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
struct f_val;
|
||||||
struct f_tree;
|
struct f_tree;
|
||||||
|
|
||||||
int as_path_valid(byte *data, uint len, int bs, int sets, int confed, char *err, uint elen);
|
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);
|
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_contains(const struct adata *path, u32 as, int min);
|
||||||
int as_path_match_set(const struct adata *path, const struct f_tree *set);
|
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)
|
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); }
|
{ 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) */
|
/* Transitive bit (for first u32 half of EC) */
|
||||||
#define EC_TBIT 0x40000000
|
#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_hi(u64 ec) { return ec >> 32; }
|
||||||
static inline u32 ec_lo(u64 ec) { return ec; }
|
static inline u32 ec_lo(u64 ec) { return ec; }
|
||||||
|
|
||||||
static inline u64 ec_get(const u32 *l, int i)
|
static inline u64 ec_get(const u32 *l, int i)
|
||||||
{ return (((u64) l[i]) << 32) | l[i+1]; }
|
{ 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 */
|
/* RFC 4360 3.1. Two-Octet AS Specific Extended Community */
|
||||||
static inline u64 ec_as2(enum ec_subtype kind, u64 key, u64 val)
|
static inline u64 ec_as2(enum ec_subtype kind, u64 key, u64 val)
|
||||||
{ return (((u64) kind | 0x0000) << 48) | (key << 32) | 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 int_set_max(const struct adata *list, u32 *val);
|
||||||
int ec_set_max(const struct adata *list, u64 *val);
|
int ec_set_max(const struct adata *list, u64 *val);
|
||||||
int lc_set_max(const struct adata *list, lcomm *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 */
|
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 */
|
node n; /* Node in list of all log hooks */
|
||||||
pool *pool;
|
pool *pool;
|
||||||
void *priv; /* Private to sysdep layer */
|
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;
|
struct cli_out *tx_buf, *tx_pos, *tx_write;
|
||||||
event *event;
|
event *event;
|
||||||
void (*cont)(struct cli *c);
|
void (*cont)(struct cli *c);
|
||||||
|
|
|
@ -108,6 +108,7 @@ print_size(char *dsc, struct resmem vals)
|
||||||
|
|
||||||
extern pool *rt_table_pool;
|
extern pool *rt_table_pool;
|
||||||
extern pool *rta_pool;
|
extern pool *rta_pool;
|
||||||
|
extern uint *pages_kept;
|
||||||
|
|
||||||
void
|
void
|
||||||
cmd_show_memory(void)
|
cmd_show_memory(void)
|
||||||
|
@ -117,10 +118,11 @@ cmd_show_memory(void)
|
||||||
print_size("Routing tables:", rmemsize(rt_table_pool));
|
print_size("Routing tables:", rmemsize(rt_table_pool));
|
||||||
print_size("Route attributes:", rmemsize(rta_pool));
|
print_size("Route attributes:", rmemsize(rta_pool));
|
||||||
print_size("Protocols:", rmemsize(proto_pool));
|
print_size("Protocols:", rmemsize(proto_pool));
|
||||||
|
print_size("Current config:", rmemsize(config_pool));
|
||||||
struct resmem total = rmemsize(&root_pool);
|
struct resmem total = rmemsize(&root_pool);
|
||||||
#ifdef HAVE_MMAP
|
#ifdef HAVE_MMAP
|
||||||
print_size("Standby memory:", (struct resmem) { .overhead = get_page_size() * pages_kept });
|
print_size("Standby memory:", (struct resmem) { .overhead = page_size * *pages_kept });
|
||||||
total.overhead += get_page_size() * pages_kept;
|
total.overhead += page_size * *pages_kept;
|
||||||
#endif
|
#endif
|
||||||
print_size("Total:", total);
|
print_size("Total:", total);
|
||||||
cli_msg(0, "");
|
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(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
|
||||||
CF_KEYWORDS(MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE)
|
CF_KEYWORDS(MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE)
|
||||||
CF_KEYWORDS(CHECK, LINK)
|
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 */
|
/* 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_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)
|
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_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINED)
|
||||||
CF_ENUM(T_ENUM_RTD, RTD_, UNICAST, BLACKHOLE, UNREACHABLE, PROHIBIT)
|
CF_ENUM(T_ENUM_RTD, RTD_, UNICAST, BLACKHOLE, UNREACHABLE, PROHIBIT)
|
||||||
|
@ -165,7 +165,7 @@ rtrid:
|
||||||
|
|
||||||
idval:
|
idval:
|
||||||
NUM { $$ = $1; }
|
NUM { $$ = $1; }
|
||||||
| '(' term ')' { $$ = f_eval_int(f_linearize($2)); }
|
| '(' term ')' { $$ = f_eval_int(f_linearize($2, 1)); }
|
||||||
| IP4 { $$ = ip4_to_u32($1); }
|
| IP4 { $$ = ip4_to_u32($1); }
|
||||||
| CF_SYM_KNOWN {
|
| CF_SYM_KNOWN {
|
||||||
if ($1->class == (SYM_CONSTANT | T_INT) || $1->class == (SYM_CONSTANT | T_QUAD))
|
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; }
|
| MIN SETTLE TIME expr_us { this_table->min_settle_time = $4; }
|
||||||
| MAX SETTLE TIME expr_us { this_table->max_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:
|
table_opts:
|
||||||
|
@ -630,7 +632,7 @@ CF_CLI(SHOW INTERFACES SUMMARY,,, [[Show summary of network interfaces]])
|
||||||
{ if_show_summary(); } ;
|
{ if_show_summary(); } ;
|
||||||
|
|
||||||
CF_CLI_HELP(SHOW ROUTE, ..., [[Show routing table]])
|
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); } ;
|
{ rt_show($3); } ;
|
||||||
|
|
||||||
r_args:
|
r_args:
|
||||||
|
@ -858,7 +860,7 @@ CF_CLI(DUMP FILTER ALL,,, [[Dump all filters in linearized form]])
|
||||||
{ filters_dump_all(); cli_msg(0, ""); } ;
|
{ filters_dump_all(); cli_msg(0, ""); } ;
|
||||||
|
|
||||||
CF_CLI(EVAL, term, <expr>, [[Evaluate an expression]])
|
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_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]]) {
|
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->n);
|
||||||
rem_node(&n->if_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)
|
void dump(struct proto *p)
|
||||||
{ DUMMY; }
|
{ 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
|
* start - request instance startup
|
||||||
* @p: protocol instance
|
* @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)
|
void neigh_notify(neighbor *neigh)
|
||||||
{ DUMMY; }
|
{ 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
|
* preexport - pre-filtering decisions before route export
|
||||||
* @p: protocol instance the route is going to be exported to
|
* @p: protocol instance the route is going to be exported to
|
||||||
|
|
58
nest/proto.c
58
nest/proto.c
|
@ -23,9 +23,9 @@
|
||||||
#include "filter/f-inst.h"
|
#include "filter/f-inst.h"
|
||||||
|
|
||||||
pool *proto_pool;
|
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];
|
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); })
|
#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_error("Multiple %s channels", name);
|
||||||
|
|
||||||
cf->parent = proto;
|
cf->parent = proto;
|
||||||
|
cf->copy = 1;
|
||||||
return cf;
|
return cf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -829,6 +830,9 @@ static int reconfigure_type; /* Hack to propagate type info to channel_reconfig
|
||||||
int
|
int
|
||||||
channel_reconfigure(struct channel *c, struct channel_config *cf)
|
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 */
|
/* 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)))
|
if ((c->table != cf->table->table) || (cf->ra_mode && (c->ra_mode != cf->ra_mode)))
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1651,6 +1655,8 @@ proto_build(struct protocol *p)
|
||||||
/* FIXME: convert this call to some protocol hook */
|
/* FIXME: convert this call to some protocol hook */
|
||||||
extern void bfd_init_all(void);
|
extern void bfd_init_all(void);
|
||||||
|
|
||||||
|
void protos_build_gen(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* protos_build - build a protocol list
|
* protos_build - build a protocol list
|
||||||
*
|
*
|
||||||
|
@ -1663,44 +1669,7 @@ extern void bfd_init_all(void);
|
||||||
void
|
void
|
||||||
protos_build(void)
|
protos_build(void)
|
||||||
{
|
{
|
||||||
init_list(&proto_list);
|
protos_build_gen();
|
||||||
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
|
|
||||||
|
|
||||||
proto_pool = rp_new(&root_pool, "Protocols");
|
proto_pool = rp_new(&root_pool, "Protocols");
|
||||||
proto_shutdown_timer = tm_new(proto_pool);
|
proto_shutdown_timer = tm_new(proto_pool);
|
||||||
|
@ -2243,8 +2212,13 @@ proto_apply_cmd_symbol(const struct symbol *s, void (* cmd)(struct proto *, uint
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd(s->proto->proto, arg, 0);
|
if (s->proto->proto)
|
||||||
cli_msg(0, "");
|
{
|
||||||
|
cmd(s->proto->proto, arg, 0);
|
||||||
|
cli_msg(0, "");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cli_msg(9002, "%s does not exist", s->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
|
@ -74,7 +74,6 @@ struct protocol {
|
||||||
struct proto * (*init)(struct proto_config *); /* Create new instance */
|
struct proto * (*init)(struct proto_config *); /* Create new instance */
|
||||||
int (*reconfigure)(struct proto *, struct proto_config *); /* Try to reconfigure instance, returns success */
|
int (*reconfigure)(struct proto *, struct proto_config *); /* Try to reconfigure instance, returns success */
|
||||||
void (*dump)(struct proto *); /* Debugging dump */
|
void (*dump)(struct proto *); /* Debugging dump */
|
||||||
void (*dump_attrs)(struct rte *); /* Dump protocol-dependent attributes */
|
|
||||||
int (*start)(struct proto *); /* Start the instance */
|
int (*start)(struct proto *); /* Start the instance */
|
||||||
int (*shutdown)(struct proto *); /* Stop the instance */
|
int (*shutdown)(struct proto *); /* Stop the instance */
|
||||||
void (*cleanup)(struct proto *); /* Called after shutdown when protocol became hungry/down */
|
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 (*copy_config)(struct proto_config *, struct proto_config *); /* Copy config from given protocol instance */
|
||||||
};
|
};
|
||||||
|
|
||||||
void protos_build(void);
|
void protos_build(void); /* Called from sysdep to initialize protocols */
|
||||||
void proto_build(struct protocol *);
|
void proto_build(struct protocol *); /* Called from protocol to register itself */
|
||||||
void protos_preconfig(struct config *);
|
void protos_preconfig(struct config *);
|
||||||
void protos_commit(struct config *new, struct config *old, int force_restart, int type);
|
void protos_commit(struct config *new, struct config *old, int force_restart, int type);
|
||||||
struct proto * proto_spawn(struct proto_config *cf, uint disabled);
|
struct proto * proto_spawn(struct proto_config *cf, uint disabled);
|
||||||
|
@ -198,12 +197,11 @@ struct proto {
|
||||||
* ifa_notify Notify protocol about interface address changes.
|
* ifa_notify Notify protocol about interface address changes.
|
||||||
* rt_notify Notify protocol about routing table updates.
|
* rt_notify Notify protocol about routing table updates.
|
||||||
* neigh_notify Notify protocol about neighbor cache events.
|
* 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.
|
* preexport Called as the first step of the route exporting process.
|
||||||
* store_tmp_attrs Store private attrs back to rte and undef added attributes. The route and rta MUST NOT be cached.
|
* It can decide whether the route shall be exported:
|
||||||
* preexport Called as the first step of the route exporting process.
|
* -1 = reject,
|
||||||
* It can construct a new rte, add private attributes and
|
* 0 = continue to export filter
|
||||||
* decide whether the route shall be exported: 1=yes, -1=no,
|
* 1 = accept immediately
|
||||||
* 0=process it through the export filter set by the user.
|
|
||||||
* reload_routes Request channel to reload all its routes to the core
|
* reload_routes Request channel to reload all its routes to the core
|
||||||
* (using rte_update()). Returns: 0=reload cannot be done,
|
* (using rte_update()). Returns: 0=reload cannot be done,
|
||||||
* 1= reload is scheduled and will happen (asynchronously).
|
* 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 (*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 (*rt_notify)(struct proto *, struct channel *, struct network *net, struct rte *new, struct rte *old);
|
||||||
void (*neigh_notify)(struct neighbor *neigh);
|
void (*neigh_notify)(struct neighbor *neigh);
|
||||||
void (*make_tmp_attrs)(struct rte *rt, struct linpool *pool);
|
int (*preexport)(struct channel *, struct rte *rt);
|
||||||
void (*store_tmp_attrs)(struct rte *rt, struct linpool *pool);
|
|
||||||
int (*preexport)(struct proto *, struct rte **rt, struct linpool *pool);
|
|
||||||
void (*reload_routes)(struct channel *);
|
void (*reload_routes)(struct channel *);
|
||||||
void (*feed_begin)(struct channel *, int initial);
|
void (*feed_begin)(struct channel *, int initial);
|
||||||
void (*feed_end)(struct channel *);
|
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_recalculate)(struct rtable *, struct network *, struct rte *, struct rte *, struct rte *);
|
||||||
int (*rte_better)(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 *);
|
int (*rte_mergable)(struct rte *, struct rte *);
|
||||||
struct rte * (*rte_modify)(struct rte *, struct linpool *);
|
struct rte * (*rte_modify)(struct rte *, struct linpool *);
|
||||||
void (*rte_insert)(struct network *, struct rte *);
|
void (*rte_insert)(struct network *, struct rte *);
|
||||||
void (*rte_remove)(struct network *, struct rte *);
|
void (*rte_remove)(struct network *, struct rte *);
|
||||||
|
u32 (*rte_igp_metric)(struct rte *);
|
||||||
|
|
||||||
/* Hic sunt protocol-specific data */
|
/* Hic sunt protocol-specific data */
|
||||||
};
|
};
|
||||||
|
@ -469,7 +465,6 @@ struct channel_class {
|
||||||
|
|
||||||
|
|
||||||
void (*dump)(struct proto *); /* Debugging dump */
|
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_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) */
|
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_*) */
|
u8 ra_mode; /* Mode of received route advertisements (RA_*) */
|
||||||
u16 preference; /* Default route preference */
|
u16 preference; /* Default route preference */
|
||||||
u32 debug; /* Debugging flags (D_*) */
|
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 merge_limit; /* Maximal number of nexthops for RA_MERGED */
|
||||||
u8 in_keep_filtered; /* Routes rejected in import filter are kept */
|
u8 in_keep_filtered; /* Routes rejected in import filter are kept */
|
||||||
u8 rpki_reload; /* RPKI changes trigger channel reload */
|
u8 rpki_reload; /* RPKI changes trigger channel reload */
|
||||||
|
|
141
nest/route.h
141
nest/route.h
|
@ -148,8 +148,8 @@ struct rtable_config {
|
||||||
struct rtable *table;
|
struct rtable *table;
|
||||||
struct proto_config *krt_attached; /* Kernel syncer attached to this table */
|
struct proto_config *krt_attached; /* Kernel syncer attached to this table */
|
||||||
uint addr_type; /* Type of address data stored in table (NET_*) */
|
uint addr_type; /* Type of address data stored in table (NET_*) */
|
||||||
int gc_max_ops; /* Maximum number of operations before GC is run */
|
uint gc_threshold; /* Maximum number of operations before GC is run */
|
||||||
int gc_min_time; /* Minimum time between two consecutive GC runs */
|
uint gc_period; /* Approximate time between two consecutive GC runs */
|
||||||
byte sorted; /* Routes of network are sorted according to rte_better() */
|
byte sorted; /* Routes of network are sorted according to rte_better() */
|
||||||
byte internal; /* Internal table of a protocol */
|
byte internal; /* Internal table of a protocol */
|
||||||
byte trie_used; /* Rtable has attached trie */
|
byte trie_used; /* Rtable has attached trie */
|
||||||
|
@ -180,10 +180,11 @@ typedef struct rtable {
|
||||||
* obstacle from this routing table.
|
* obstacle from this routing table.
|
||||||
*/
|
*/
|
||||||
struct event *rt_event; /* Routing table event */
|
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 last_rt_change; /* Last time when route changed */
|
||||||
btime base_settle_time; /* Start time of rtable settling interval */
|
btime base_settle_time; /* Start time of rtable settling interval */
|
||||||
btime gc_time; /* Time of last GC */
|
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_state; /* Table prune state, 1 -> scheduled, 2-> running */
|
||||||
byte prune_trie; /* Prune prefix trie during next table prune */
|
byte prune_trie; /* Prune prefix trie during next table prune */
|
||||||
byte hcu_scheduled; /* Hostcache update is scheduled */
|
byte hcu_scheduled; /* Hostcache update is scheduled */
|
||||||
|
@ -255,50 +256,13 @@ struct hostentry {
|
||||||
typedef struct rte {
|
typedef struct rte {
|
||||||
struct rte *next;
|
struct rte *next;
|
||||||
net *net; /* Network this RTE belongs to */
|
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 channel *sender; /* Channel used to send the route to the routing table */
|
||||||
struct rta *attrs; /* Attributes of this route */
|
struct rta *attrs; /* Attributes of this route */
|
||||||
u32 id; /* Table specific route id */
|
u32 id; /* Table specific route id */
|
||||||
byte flags; /* Flags (REF_...) */
|
byte flags; /* Flags (REF_...) */
|
||||||
byte pflags; /* Protocol-specific flags */
|
byte pflags; /* Protocol-specific flags */
|
||||||
word pref; /* Route preference */
|
|
||||||
btime lastmod; /* Last modified */
|
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;
|
} rte;
|
||||||
|
|
||||||
#define REF_COW 1 /* Copy this rte on write */
|
#define REF_COW 1 /* Copy this rte on write */
|
||||||
|
@ -332,6 +296,7 @@ struct config;
|
||||||
|
|
||||||
void rt_init(void);
|
void rt_init(void);
|
||||||
void rt_preconfig(struct config *);
|
void rt_preconfig(struct config *);
|
||||||
|
void rt_postconfig(struct config *);
|
||||||
void rt_commit(struct config *new, struct config *old);
|
void rt_commit(struct config *new, struct config *old);
|
||||||
void rt_lock_table(rtable *);
|
void rt_lock_table(rtable *);
|
||||||
void rt_unlock_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);
|
net *net_route(rtable *tab, const net_addr *n);
|
||||||
int net_roa_check(rtable *tab, const net_addr *n, u32 asn);
|
int net_roa_check(rtable *tab, const net_addr *n, u32 asn);
|
||||||
rte *rte_find(net *net, struct rte_src *src);
|
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);
|
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 */
|
/* 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);
|
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_begin(rtable *t, struct channel *c);
|
||||||
void rt_refresh_end(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 *);
|
rte *rte_do_cow(rte *);
|
||||||
static inline rte * rte_cow(rte *r) { return (r->flags & REF_COW) ? rte_do_cow(r) : r; }
|
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);
|
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(rtable *);
|
||||||
void rt_dump_all(void);
|
void rt_dump_all(void);
|
||||||
int rt_feed_channel(struct channel *c);
|
int rt_feed_channel(struct channel *c);
|
||||||
|
@ -487,18 +448,17 @@ typedef struct rta {
|
||||||
u32 uc; /* Use count */
|
u32 uc; /* Use count */
|
||||||
u32 hash_key; /* Hash over important fields */
|
u32 hash_key; /* Hash over important fields */
|
||||||
struct ea_list *eattrs; /* Extended Attribute chain */
|
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 */
|
struct hostentry *hostentry; /* Hostentry for recursive next-hops */
|
||||||
ip_addr from; /* Advertising router */
|
ip_addr from; /* Advertising router */
|
||||||
u32 igp_metric; /* IGP metric to next hop (for iBGP routes) */
|
u32 igp_metric; /* IGP metric to next hop (for iBGP routes) */
|
||||||
u8 source; /* Route source (RTS_...) */
|
u16 cached:1; /* Are attributes cached? */
|
||||||
u8 scope; /* Route scope (SCOPE_... -- see ip.h) */
|
u16 source:7; /* Route source (RTS_...) */
|
||||||
u8 dest; /* Route destination type (RTD_...) */
|
u16 scope:4; /* Route scope (SCOPE_... -- see ip.h) */
|
||||||
u8 aflags;
|
u16 dest:4; /* Route destination type (RTD_...) */
|
||||||
|
word pref;
|
||||||
struct nexthop nh; /* Next hop */
|
struct nexthop nh; /* Next hop */
|
||||||
} rta;
|
} rta;
|
||||||
|
|
||||||
#define RTS_DUMMY 0 /* Dummy route to be removed soon */
|
|
||||||
#define RTS_STATIC 1 /* Normal static route */
|
#define RTS_STATIC 1 /* Normal static route */
|
||||||
#define RTS_INHERIT 2 /* Route inherited from kernel */
|
#define RTS_INHERIT 2 /* Route inherited from kernel */
|
||||||
#define RTS_DEVICE 3 /* Device route */
|
#define RTS_DEVICE 3 /* Device route */
|
||||||
|
@ -516,11 +476,6 @@ typedef struct rta {
|
||||||
#define RTS_PERF 15 /* Perf checker */
|
#define RTS_PERF 15 /* Perf checker */
|
||||||
#define RTS_MAX 16
|
#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_NONE 0 /* Undefined next hop */
|
||||||
#define RTD_UNICAST 1 /* Next hop is neighbor router */
|
#define RTD_UNICAST 1 /* Next hop is neighbor router */
|
||||||
#define RTD_BLACKHOLE 2 /* Silently drop packets */
|
#define RTD_BLACKHOLE 2 /* Silently drop packets */
|
||||||
|
@ -528,8 +483,6 @@ typedef struct rta {
|
||||||
#define RTD_PROHIBIT 4 /* Administratively prohibited */
|
#define RTD_PROHIBIT 4 /* Administratively prohibited */
|
||||||
#define RTD_MAX 5
|
#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
|
#define IGP_METRIC_UNKNOWN 0x80000000 /* Default igp_metric used when no other
|
||||||
protocol-specific metric is availabe */
|
protocol-specific metric is availabe */
|
||||||
|
|
||||||
|
@ -551,10 +504,13 @@ static inline int rte_is_reachable(rte *r)
|
||||||
typedef struct eattr {
|
typedef struct eattr {
|
||||||
word id; /* EA_CODE(PROTOCOL_..., protocol-dependent ID) */
|
word id; /* EA_CODE(PROTOCOL_..., protocol-dependent ID) */
|
||||||
byte flags; /* Protocol-dependent flags */
|
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 {
|
union {
|
||||||
u32 data;
|
uintptr_t data;
|
||||||
const struct adata *ptr; /* Attribute data elsewhere */
|
const struct adata *ptr; /* Attribute data elsewhere */
|
||||||
} u;
|
} u;
|
||||||
} eattr;
|
} eattr;
|
||||||
|
|
||||||
|
@ -562,7 +518,6 @@ typedef struct eattr {
|
||||||
#define EA_CODE(proto,id) (((proto) << 8) | (id))
|
#define EA_CODE(proto,id) (((proto) << 8) | (id))
|
||||||
#define EA_ID(ea) ((ea) & 0xff)
|
#define EA_ID(ea) ((ea) & 0xff)
|
||||||
#define EA_PROTO(ea) ((ea) >> 8)
|
#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_CUSTOM(id) ((id) | EA_CUSTOM_BIT)
|
||||||
#define EA_IS_CUSTOM(ea) ((ea) & EA_CUSTOM_BIT)
|
#define EA_IS_CUSTOM(ea) ((ea) & EA_CUSTOM_BIT)
|
||||||
#define EA_CUSTOM_ID(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_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_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_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_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_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 {
|
typedef struct adata {
|
||||||
uint length; /* Length of data */
|
uint length; /* Length of data */
|
||||||
|
@ -623,7 +576,6 @@ typedef struct ea_list {
|
||||||
#define EALF_SORTED 1 /* Attributes are sorted by code */
|
#define EALF_SORTED 1 /* Attributes are sorted by code */
|
||||||
#define EALF_BISECT 2 /* Use interval bisection for searching */
|
#define EALF_BISECT 2 /* Use interval bisection for searching */
|
||||||
#define EALF_CACHED 4 /* Attributes belonging to cached rta */
|
#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_find_source(struct proto *p, u32 id);
|
||||||
struct rte_src *rt_get_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_find(ea_list *, unsigned ea);
|
||||||
eattr *ea_walk(struct ea_walk_state *s, uint id, uint max);
|
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_dump(ea_list *);
|
||||||
void ea_sort(ea_list *); /* Sort entries in all sub-lists */
|
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 */
|
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; \
|
ea = NULL; \
|
||||||
} while(0) \
|
} while(0) \
|
||||||
|
|
||||||
|
struct ea_one_attr_list {
|
||||||
|
ea_list l;
|
||||||
|
eattr a;
|
||||||
|
};
|
||||||
|
|
||||||
static inline eattr *
|
static inline eattr *
|
||||||
ea_set_attr(ea_list **to, struct linpool *pool, uint id, uint flags, uint type, uintptr_t val)
|
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));
|
struct ea_one_attr_list *ea = lp_alloc(pool, sizeof(*ea));
|
||||||
eattr *e = &a->attrs[0];
|
*ea = (struct ea_one_attr_list) {
|
||||||
|
.l.flags = EALF_SORTED,
|
||||||
|
.l.count = 1,
|
||||||
|
.l.next = *to,
|
||||||
|
|
||||||
a->flags = EALF_SORTED;
|
.a.id = id,
|
||||||
a->count = 1;
|
.a.type = type,
|
||||||
a->next = *to;
|
.a.flags = flags,
|
||||||
*to = a;
|
};
|
||||||
|
|
||||||
e->id = id;
|
|
||||||
e->type = type;
|
|
||||||
e->flags = flags;
|
|
||||||
|
|
||||||
if (type & EAF_EMBEDDED)
|
if (type & EAF_EMBEDDED)
|
||||||
e->u.data = (u32) val;
|
ea->a.u.data = val;
|
||||||
else
|
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
|
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; }
|
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)
|
#define RTA_MAX_SIZE (sizeof(rta) + sizeof(u32)*MPLS_MAX_LABEL_STACK)
|
||||||
rta *rta_lookup(rta *); /* Get rta equivalent to this one, uc++ */
|
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; }
|
static inline rta *rta_clone(rta *r) { r->uc++; return r; }
|
||||||
void rta__free(rta *r);
|
void rta__free(rta *r);
|
||||||
static inline void rta_free(rta *r) { if (r && !--r->uc) rta__free(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 adata null_adata; /* adata of length 0 */
|
||||||
|
|
||||||
const char * const rta_src_names[RTS_MAX] = {
|
const char * const rta_src_names[RTS_MAX] = {
|
||||||
[RTS_DUMMY] = "",
|
|
||||||
[RTS_STATIC] = "static",
|
[RTS_STATIC] = "static",
|
||||||
[RTS_INHERIT] = "inherit",
|
[RTS_INHERIT] = "inherit",
|
||||||
[RTS_DEVICE] = "device",
|
[RTS_DEVICE] = "device",
|
||||||
|
@ -155,7 +154,7 @@ rt_prune_sources(void)
|
||||||
{
|
{
|
||||||
HASH_DO_REMOVE(src_hash, RSH, sp);
|
HASH_DO_REMOVE(src_hash, RSH, sp);
|
||||||
idm_free(&src_ids, src->global_id);
|
idm_free(&src_ids, src->global_id);
|
||||||
sl_free(rte_src_slab, src);
|
sl_free(src);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HASH_WALK_FILTER_END;
|
HASH_WALK_FILTER_END;
|
||||||
|
@ -392,7 +391,7 @@ nexthop_free(struct nexthop *o)
|
||||||
while (o)
|
while (o)
|
||||||
{
|
{
|
||||||
n = o->next;
|
n = o->next;
|
||||||
sl_free(nexthop_slab(o), o);
|
sl_free(o);
|
||||||
o = n;
|
o = n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -449,8 +448,7 @@ ea_find(ea_list *e, unsigned id)
|
||||||
{
|
{
|
||||||
eattr *a = ea__find(e, id & EA_CODE_MASK);
|
eattr *a = ea__find(e, id & EA_CODE_MASK);
|
||||||
|
|
||||||
if (a && (a->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF &&
|
if (a && a->undef && !(id & EA_ALLOW_UNDEF))
|
||||||
!(id & EA_ALLOW_UNDEF))
|
|
||||||
return NULL;
|
return NULL;
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
@ -517,7 +515,7 @@ ea_walk(struct ea_walk_state *s, uint id, uint max)
|
||||||
|
|
||||||
BIT32_SET(s->visited, n);
|
BIT32_SET(s->visited, n);
|
||||||
|
|
||||||
if ((a->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF)
|
if (a->undef)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
s->eattrs = e;
|
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
|
* by calling ea_find() to find the attribute, extracting its value or returning
|
||||||
* a provided default if no such attribute is present.
|
* a provided default if no such attribute is present.
|
||||||
*/
|
*/
|
||||||
int
|
uintptr_t
|
||||||
ea_get_int(ea_list *e, unsigned id, int def)
|
ea_get_int(ea_list *e, unsigned id, uintptr_t def)
|
||||||
{
|
{
|
||||||
eattr *a = ea_find(e, id);
|
eattr *a = ea_find(e, id);
|
||||||
if (!a)
|
if (!a)
|
||||||
|
@ -617,14 +615,17 @@ ea_do_prune(ea_list *e)
|
||||||
|
|
||||||
/* Now s0 is the most recent version, s[-1] the oldest one */
|
/* Now s0 is the most recent version, s[-1] the oldest one */
|
||||||
/* Drop undefs */
|
/* Drop undefs */
|
||||||
if ((s0->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF)
|
if (s0->undef)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Copy the newest version to destination */
|
/* Copy the newest version to destination */
|
||||||
*d = *s0;
|
*d = *s0;
|
||||||
|
|
||||||
/* Preserve info whether it originated locally */
|
/* 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 */
|
/* Next destination */
|
||||||
d++;
|
d++;
|
||||||
|
@ -738,6 +739,9 @@ ea_same(ea_list *x, ea_list *y)
|
||||||
if (a->id != b->id ||
|
if (a->id != b->id ||
|
||||||
a->flags != b->flags ||
|
a->flags != b->flags ||
|
||||||
a->type != b->type ||
|
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)))
|
((a->type & EAF_EMBEDDED) ? a->u.data != b->u.data : !adata_same(a->u.ptr, b->u.ptr)))
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -940,6 +944,10 @@ ea_show(struct cli *c, const eattr *e)
|
||||||
{
|
{
|
||||||
*pos++ = ':';
|
*pos++ = ':';
|
||||||
*pos++ = ' ';
|
*pos++ = ' ';
|
||||||
|
|
||||||
|
if (e->undef)
|
||||||
|
bsprintf(pos, "undefined");
|
||||||
|
else
|
||||||
switch (e->type & EAF_TYPE_MASK)
|
switch (e->type & EAF_TYPE_MASK)
|
||||||
{
|
{
|
||||||
case EAF_TYPE_INT:
|
case EAF_TYPE_INT:
|
||||||
|
@ -969,7 +977,6 @@ ea_show(struct cli *c, const eattr *e)
|
||||||
case EAF_TYPE_LC_SET:
|
case EAF_TYPE_LC_SET:
|
||||||
ea_show_lc_set(c, ad, pos, buf, end);
|
ea_show_lc_set(c, ad, pos, buf, end);
|
||||||
return;
|
return;
|
||||||
case EAF_TYPE_UNDEF:
|
|
||||||
default:
|
default:
|
||||||
bsprintf(pos, "<type %02x>", e->type);
|
bsprintf(pos, "<type %02x>", e->type);
|
||||||
}
|
}
|
||||||
|
@ -1005,7 +1012,7 @@ ea_dump(ea_list *e)
|
||||||
eattr *a = &e->attrs[i];
|
eattr *a = &e->attrs[i];
|
||||||
debug(" %02x:%02x.%02x", EA_PROTO(a->id), EA_ID(a->id), a->flags);
|
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]);
|
debug("=%c", "?iO?I?P???S?????" [a->type & EAF_TYPE_MASK]);
|
||||||
if (a->type & EAF_ORIGINATED)
|
if (a->originated)
|
||||||
debug("o");
|
debug("o");
|
||||||
if (a->type & EAF_EMBEDDED)
|
if (a->type & EAF_EMBEDDED)
|
||||||
debug(":%08x", a->u.data);
|
debug(":%08x", a->u.data);
|
||||||
|
@ -1104,13 +1111,14 @@ rta_hash(rta *a)
|
||||||
u64 h;
|
u64 h;
|
||||||
mem_hash_init(&h);
|
mem_hash_init(&h);
|
||||||
#define MIX(f) mem_hash_mix(&h, &(a->f), sizeof(a->f));
|
#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(hostentry);
|
||||||
MIX(from);
|
MIX(from);
|
||||||
MIX(igp_metric);
|
MIX(igp_metric);
|
||||||
MIX(source);
|
BMIX(source);
|
||||||
MIX(scope);
|
BMIX(scope);
|
||||||
MIX(dest);
|
BMIX(dest);
|
||||||
|
MIX(pref);
|
||||||
#undef MIX
|
#undef MIX
|
||||||
|
|
||||||
return mem_hash_value(&h) ^ nexthop_hash(&(a->nh)) ^ ea_hash(a->eattrs);
|
return mem_hash_value(&h) ^ nexthop_hash(&(a->nh)) ^ ea_hash(a->eattrs);
|
||||||
|
@ -1119,8 +1127,7 @@ rta_hash(rta *a)
|
||||||
static inline int
|
static inline int
|
||||||
rta_same(rta *x, rta *y)
|
rta_same(rta *x, rta *y)
|
||||||
{
|
{
|
||||||
return (x->src == y->src &&
|
return (x->source == y->source &&
|
||||||
x->source == y->source &&
|
|
||||||
x->scope == y->scope &&
|
x->scope == y->scope &&
|
||||||
x->dest == y->dest &&
|
x->dest == y->dest &&
|
||||||
x->igp_metric == y->igp_metric &&
|
x->igp_metric == y->igp_metric &&
|
||||||
|
@ -1198,7 +1205,7 @@ rta_lookup(rta *o)
|
||||||
rta *r;
|
rta *r;
|
||||||
uint h;
|
uint h;
|
||||||
|
|
||||||
ASSERT(!(o->aflags & RTAF_CACHED));
|
ASSERT(!o->cached);
|
||||||
if (o->eattrs)
|
if (o->eattrs)
|
||||||
ea_normalize(o->eattrs);
|
ea_normalize(o->eattrs);
|
||||||
|
|
||||||
|
@ -1209,8 +1216,7 @@ rta_lookup(rta *o)
|
||||||
|
|
||||||
r = rta_copy(o);
|
r = rta_copy(o);
|
||||||
r->hash_key = h;
|
r->hash_key = h;
|
||||||
r->aflags = RTAF_CACHED;
|
r->cached = 1;
|
||||||
rt_lock_source(r->src);
|
|
||||||
rt_lock_hostentry(r->hostentry);
|
rt_lock_hostentry(r->hostentry);
|
||||||
rta_insert(r);
|
rta_insert(r);
|
||||||
|
|
||||||
|
@ -1223,18 +1229,17 @@ rta_lookup(rta *o)
|
||||||
void
|
void
|
||||||
rta__free(rta *a)
|
rta__free(rta *a)
|
||||||
{
|
{
|
||||||
ASSERT(rta_cache_count && (a->aflags & RTAF_CACHED));
|
ASSERT(rta_cache_count && a->cached);
|
||||||
rta_cache_count--;
|
rta_cache_count--;
|
||||||
*a->pprev = a->next;
|
*a->pprev = a->next;
|
||||||
if (a->next)
|
if (a->next)
|
||||||
a->next->pprev = a->pprev;
|
a->next->pprev = a->pprev;
|
||||||
rt_unlock_hostentry(a->hostentry);
|
rt_unlock_hostentry(a->hostentry);
|
||||||
rt_unlock_source(a->src);
|
|
||||||
if (a->nh.next)
|
if (a->nh.next)
|
||||||
nexthop_free(a->nh.next);
|
nexthop_free(a->nh.next);
|
||||||
ea_free(a->eattrs);
|
ea_free(a->eattrs);
|
||||||
a->aflags = 0; /* Poison the entry */
|
a->cached = 0;
|
||||||
sl_free(rta_slab(a), a);
|
sl_free(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
rta *
|
rta *
|
||||||
|
@ -1248,7 +1253,7 @@ rta_do_cow(rta *o, linpool *lp)
|
||||||
memcpy(*nhn, nho, nexthop_size(nho));
|
memcpy(*nhn, nho, nexthop_size(nho));
|
||||||
nhn = &((*nhn)->next);
|
nhn = &((*nhn)->next);
|
||||||
}
|
}
|
||||||
r->aflags = 0;
|
r->cached = 0;
|
||||||
r->uc = 0;
|
r->uc = 0;
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
@ -1262,16 +1267,16 @@ rta_do_cow(rta *o, linpool *lp)
|
||||||
void
|
void
|
||||||
rta_dump(rta *a)
|
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_STAT_DEV", "RTS_REDIR", "RTS_RIP",
|
||||||
"RTS_OSPF", "RTS_OSPF_IA", "RTS_OSPF_EXT1",
|
"RTS_OSPF", "RTS_OSPF_IA", "RTS_OSPF_EXT1",
|
||||||
"RTS_OSPF_EXT2", "RTS_BGP", "RTS_PIPE", "RTS_BABEL" };
|
"RTS_OSPF_EXT2", "RTS_BGP", "RTS_PIPE", "RTS_BABEL" };
|
||||||
static char *rtd[] = { "", " DEV", " HOLE", " UNREACH", " PROHIBIT" };
|
static char *rtd[] = { "", " DEV", " HOLE", " UNREACH", " PROHIBIT" };
|
||||||
|
|
||||||
debug("p=%s uc=%d %s %s%s h=%04x",
|
debug("pref=%d uc=%d %s %s%s h=%04x",
|
||||||
a->src->proto->name, a->uc, rts[a->source], ip_scope_text(a->scope),
|
a->pref, a->uc, rts[a->source], ip_scope_text(a->scope),
|
||||||
rtd[a->dest], a->hash_key);
|
rtd[a->dest], a->hash_key);
|
||||||
if (!(a->aflags & RTAF_CACHED))
|
if (!a->cached)
|
||||||
debug(" !CACHED");
|
debug(" !CACHED");
|
||||||
debug(" <-%I", a->from);
|
debug(" <-%I", a->from);
|
||||||
if (a->dest == RTD_UNICAST)
|
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);
|
struct rte_src *src = rt_get_source(P, ad->iface->index);
|
||||||
|
|
||||||
rta a0 = {
|
rta a0 = {
|
||||||
.src = src,
|
.pref = c->preference,
|
||||||
.source = RTS_DEVICE,
|
.source = RTS_DEVICE,
|
||||||
.scope = SCOPE_UNIVERSE,
|
.scope = SCOPE_UNIVERSE,
|
||||||
.dest = RTD_UNICAST,
|
.dest = RTD_UNICAST,
|
||||||
|
@ -91,7 +91,7 @@ dev_ifa_notify(struct proto *P, uint flags, struct ifa *ad)
|
||||||
};
|
};
|
||||||
|
|
||||||
a = rta_lookup(&a0);
|
a = rta_lookup(&a0);
|
||||||
e = rte_get_temp(a);
|
e = rte_get_temp(a, src);
|
||||||
e->pflags = 0;
|
e->pflags = 0;
|
||||||
rte_update2(c, net, e, src);
|
rte_update2(c, net, e, src);
|
||||||
}
|
}
|
||||||
|
@ -195,3 +195,9 @@ struct protocol proto_device = {
|
||||||
.reconfigure = dev_reconfigure,
|
.reconfigure = dev_reconfigure,
|
||||||
.copy_config = dev_copy_config
|
.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)
|
if (f->fib_slab)
|
||||||
sl_free(f->fib_slab, E);
|
sl_free(E);
|
||||||
else
|
else
|
||||||
mb_free(E);
|
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)
|
if (d->verbose && !rta_is_cached(a) && a->eattrs)
|
||||||
ea_normalize(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)
|
if (get_route_info)
|
||||||
get_route_info(e, info);
|
get_route_info(e, info);
|
||||||
else
|
else
|
||||||
bsprintf(info, " (%d)", e->pref);
|
bsprintf(info, " (%d)", a->pref);
|
||||||
|
|
||||||
if (d->last_table != d->tab)
|
if (d->last_table != d->tab)
|
||||||
rt_show_table(c, d);
|
rt_show_table(c, d);
|
||||||
|
|
||||||
cli_printf(c, -1007, "%-20s %s [%s %s%s]%s%s", ia, rta_dest_name(a->dest),
|
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)
|
if (a->dest == RTD_UNICAST)
|
||||||
for (nh = &(a->nh); nh; nh = nh->next)
|
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;
|
continue;
|
||||||
|
|
||||||
ee = e;
|
ee = e;
|
||||||
rte_make_tmp_attrs(&e, c->show_pool, NULL);
|
|
||||||
|
|
||||||
/* Export channel is down, do not try to export routes to it */
|
/* Export channel is down, do not try to export routes to it */
|
||||||
if (ec && (ec->export_state == ES_DOWN))
|
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)
|
else if (d->export_mode)
|
||||||
{
|
{
|
||||||
struct proto *ep = ec->proto;
|
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)
|
if (ec->ra_mode == RA_OPTIMAL || ec->ra_mode == RA_MERGED)
|
||||||
pass = 1;
|
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;
|
goto skip;
|
||||||
|
|
||||||
if (f_run(d->filter, &e, c->show_pool, 0) > F_ACCEPT)
|
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++)
|
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);
|
rt_show_add_table(d, config->def_tables[i]->table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
430
nest/rt-table.c
430
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_prune_table(rtable *tab);
|
||||||
static inline void rt_schedule_notify(rtable *tab);
|
static inline void rt_schedule_notify(rtable *tab);
|
||||||
static void rt_flowspec_notify(rtable *tab, net *net);
|
static void rt_flowspec_notify(rtable *tab, net *net);
|
||||||
|
static void rt_kick_prune_timer(rtable *tab);
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -548,7 +549,7 @@ rte_find(net *net, struct rte_src *src)
|
||||||
{
|
{
|
||||||
rte *e = net->routes;
|
rte *e = net->routes;
|
||||||
|
|
||||||
while (e && e->attrs->src != src)
|
while (e && e->src != src)
|
||||||
e = e->next;
|
e = e->next;
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
@ -563,14 +564,14 @@ rte_find(net *net, struct rte_src *src)
|
||||||
* the protocol.
|
* the protocol.
|
||||||
*/
|
*/
|
||||||
rte *
|
rte *
|
||||||
rte_get_temp(rta *a)
|
rte_get_temp(rta *a, struct rte_src *src)
|
||||||
{
|
{
|
||||||
rte *e = sl_alloc(rte_slab);
|
rte *e = sl_alloc(rte_slab);
|
||||||
|
|
||||||
e->attrs = a;
|
e->attrs = a;
|
||||||
e->id = 0;
|
e->id = 0;
|
||||||
e->flags = 0;
|
e->flags = 0;
|
||||||
e->pref = 0;
|
rt_lock_source(e->src = src);
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -580,6 +581,8 @@ rte_do_cow(rte *r)
|
||||||
rte *e = sl_alloc(rte_slab);
|
rte *e = sl_alloc(rte_slab);
|
||||||
|
|
||||||
memcpy(e, r, sizeof(rte));
|
memcpy(e, r, sizeof(rte));
|
||||||
|
|
||||||
|
rt_lock_source(e->src);
|
||||||
e->attrs = rta_clone(r->attrs);
|
e->attrs = rta_clone(r->attrs);
|
||||||
e->flags = 0;
|
e->flags = 0;
|
||||||
return e;
|
return e;
|
||||||
|
@ -617,176 +620,6 @@ rte_cow_rta(rte *r, linpool *lp)
|
||||||
return r;
|
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 */
|
static int /* Actually better or at least as good as */
|
||||||
rte_better(rte *new, rte *old)
|
rte_better(rte *new, rte *old)
|
||||||
{
|
{
|
||||||
|
@ -797,20 +630,20 @@ rte_better(rte *new, rte *old)
|
||||||
if (!rte_is_valid(new))
|
if (!rte_is_valid(new))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (new->pref > old->pref)
|
if (new->attrs->pref > old->attrs->pref)
|
||||||
return 1;
|
return 1;
|
||||||
if (new->pref < old->pref)
|
if (new->attrs->pref < old->attrs->pref)
|
||||||
return 0;
|
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
|
* 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
|
* have the same preference, try to break the tie by comparing addresses. Not too
|
||||||
* useful, but keeps the ordering of routes unambiguous.
|
* 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 better(new, old);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -823,13 +656,13 @@ rte_mergable(rte *pri, rte *sec)
|
||||||
if (!rte_is_valid(pri) || !rte_is_valid(sec))
|
if (!rte_is_valid(pri) || !rte_is_valid(sec))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (pri->pref != sec->pref)
|
if (pri->attrs->pref != sec->attrs->pref)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (pri->attrs->src->proto->proto != sec->attrs->src->proto->proto)
|
if (pri->src->proto->proto != sec->src->proto->proto)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (mergable = pri->attrs->src->proto->rte_mergable)
|
if (mergable = pri->src->proto->rte_mergable)
|
||||||
return mergable(pri, sec);
|
return mergable(pri, sec);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -838,8 +671,8 @@ rte_mergable(rte *pri, rte *sec)
|
||||||
static void
|
static void
|
||||||
rte_trace(struct channel *c, rte *e, int dir, char *msg)
|
rte_trace(struct channel *c, rte *e, int dir, char *msg)
|
||||||
{
|
{
|
||||||
log(L_TRACE "%s.%s %c %s %N %s",
|
log(L_TRACE "%s.%s %c %s %N %uL %uG %s",
|
||||||
c->proto->name, c->name ?: "?", dir, msg, e->net->n.addr,
|
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));
|
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 = rt0;
|
||||||
*rt_free = NULL;
|
*rt_free = NULL;
|
||||||
|
|
||||||
v = p->preexport ? p->preexport(p, &rt, pool) : 0;
|
v = p->preexport ? p->preexport(c, rt) : 0;
|
||||||
if (v < 0)
|
if (v < 0)
|
||||||
{
|
{
|
||||||
if (silent)
|
if (silent)
|
||||||
|
@ -887,8 +720,6 @@ export_filter_(struct channel *c, rte *rt0, rte **rt_free, linpool *pool, int si
|
||||||
goto accept;
|
goto accept;
|
||||||
}
|
}
|
||||||
|
|
||||||
rte_make_tmp_attrs(&rt, pool, NULL);
|
|
||||||
|
|
||||||
v = filter && ((filter == FILTER_REJECT) ||
|
v = filter && ((filter == FILTER_REJECT) ||
|
||||||
(f_run(filter, &rt, pool,
|
(f_run(filter, &rt, pool,
|
||||||
(silent ? FF_SILENT : 0)) > F_ACCEPT));
|
(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;
|
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:
|
accept:
|
||||||
if (rt != rt0)
|
if (rt != rt0)
|
||||||
*rt_free = rt;
|
*rt_free = rt;
|
||||||
|
@ -1287,7 +1112,20 @@ rte_announce(rtable *tab, uint type, net *net, rte *new, rte *old,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RA_ACCEPTED:
|
case RA_ACCEPTED:
|
||||||
rt_notify_accepted(c, net, new, old, 0);
|
/*
|
||||||
|
* 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;
|
break;
|
||||||
|
|
||||||
case RA_MERGED:
|
case RA_MERGED:
|
||||||
|
@ -1350,16 +1188,18 @@ rte_validate(rte *e)
|
||||||
void
|
void
|
||||||
rte_free(rte *e)
|
rte_free(rte *e)
|
||||||
{
|
{
|
||||||
|
rt_unlock_source(e->src);
|
||||||
if (rta_is_cached(e->attrs))
|
if (rta_is_cached(e->attrs))
|
||||||
rta_free(e->attrs);
|
rta_free(e->attrs);
|
||||||
sl_free(rte_slab, e);
|
sl_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
rte_free_quick(rte *e)
|
rte_free_quick(rte *e)
|
||||||
{
|
{
|
||||||
|
rt_unlock_source(e->src);
|
||||||
rta_free(e->attrs);
|
rta_free(e->attrs);
|
||||||
sl_free(rte_slab, e);
|
sl_free(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -1369,8 +1209,7 @@ rte_same(rte *x, rte *y)
|
||||||
return
|
return
|
||||||
x->attrs == y->attrs &&
|
x->attrs == y->attrs &&
|
||||||
x->pflags == y->pflags &&
|
x->pflags == y->pflags &&
|
||||||
x->pref == y->pref &&
|
x->src == y->src &&
|
||||||
(!x->attrs->src->proto->rte_same || x->attrs->src->proto->rte_same(x, y)) &&
|
|
||||||
rte_is_filtered(x) == rte_is_filtered(y);
|
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 */
|
k = &net->routes; /* Find and remove original route from the same protocol */
|
||||||
while (old = *k)
|
while (old = *k)
|
||||||
{
|
{
|
||||||
if (old->attrs->src == src)
|
if (old->src == src)
|
||||||
{
|
{
|
||||||
/* If there is the same route in the routing table but from
|
/* If there is the same route in the routing table but from
|
||||||
* a different sender, then there are two paths from the
|
* 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);
|
rte_announce(table, RA_UNDEF, net, new, old, net->routes, old_best);
|
||||||
|
|
||||||
if (!net->routes &&
|
if (!net->routes &&
|
||||||
(table->gc_counter++ >= table->config->gc_max_ops) &&
|
(table->gc_counter++ >= table->config->gc_threshold))
|
||||||
(table->gc_time + table->config->gc_min_time <= current_time()))
|
rt_kick_prune_timer(table);
|
||||||
rt_schedule_prune(table);
|
|
||||||
|
|
||||||
if (old_ok && p->rte_remove)
|
if (old_ok && p->rte_remove)
|
||||||
p->rte_remove(net, old);
|
p->rte_remove(net, old);
|
||||||
|
@ -1674,26 +1512,6 @@ rte_update_unlock(void)
|
||||||
lp_flush(rte_update_pool);
|
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
|
* rte_update - enter a new update to a routing table
|
||||||
* @table: table to be updated
|
* @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 *p = c->proto;
|
||||||
struct proto_stats *stats = &c->stats;
|
struct proto_stats *stats = &c->stats;
|
||||||
const struct filter *filter = c->in_filter;
|
const struct filter *filter = c->in_filter;
|
||||||
rte *dummy = NULL;
|
|
||||||
net *nn;
|
net *nn;
|
||||||
|
|
||||||
ASSERT(c->channel_state == CS_UP);
|
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->net = nn;
|
||||||
new->sender = c;
|
new->sender = c;
|
||||||
|
|
||||||
if (!new->pref)
|
|
||||||
new->pref = c->preference;
|
|
||||||
|
|
||||||
stats->imp_updates_received++;
|
stats->imp_updates_received++;
|
||||||
if (!rte_validate(new))
|
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)
|
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);
|
int fr = f_run(filter, &new, rte_update_pool, 0);
|
||||||
if (fr > F_ACCEPT)
|
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");
|
rte_trace_in(D_FILTERS, c, new, "filtered out");
|
||||||
|
|
||||||
if (! c->in_keep_filtered)
|
if (! c->in_keep_filtered)
|
||||||
{
|
|
||||||
rta_free(old_attrs);
|
|
||||||
goto drop;
|
goto drop;
|
||||||
}
|
|
||||||
|
|
||||||
new->flags |= REF_FILTERED;
|
new->flags |= REF_FILTERED;
|
||||||
}
|
}
|
||||||
|
|
||||||
rte_store_tmp_attrs(new, rte_update_pool, old_attrs);
|
|
||||||
}
|
}
|
||||||
if (!rta_is_cached(new->attrs)) /* Need to copy attributes */
|
if (!rta_is_cached(new->attrs)) /* Need to copy attributes */
|
||||||
new->attrs = rta_lookup(new->attrs);
|
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:
|
recalc:
|
||||||
/* And recalculate the best route */
|
/* And recalculate the best route */
|
||||||
rte_hide_dummy_routes(nn, &dummy);
|
|
||||||
rte_recalculate(c, nn, new, src);
|
rte_recalculate(c, nn, new, src);
|
||||||
rte_unhide_dummy_routes(nn, &dummy);
|
|
||||||
|
|
||||||
rte_update_unlock();
|
rte_update_unlock();
|
||||||
return;
|
return;
|
||||||
|
@ -1855,7 +1659,7 @@ static inline void
|
||||||
rte_discard(rte *old) /* Non-filtered route deletion, used during garbage collection */
|
rte_discard(rte *old) /* Non-filtered route deletion, used during garbage collection */
|
||||||
{
|
{
|
||||||
rte_update_lock();
|
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();
|
rte_update_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1875,7 +1679,7 @@ rte_modify(rte *old)
|
||||||
new->flags = (old->flags & ~REF_MODIFY) | REF_COW;
|
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();
|
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 */
|
/* Check rtable for best route to given net whether it would be exported do p */
|
||||||
int
|
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);
|
net *n = net_find(t, a);
|
||||||
rte *rt = n ? n->routes : NULL;
|
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();
|
rte_update_lock();
|
||||||
|
|
||||||
/* Rest is stripped down export_filter() */
|
/* 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)
|
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);
|
v = (f_run(filter, &rt, rte_update_pool, FF_SILENT) <= F_ACCEPT);
|
||||||
}
|
|
||||||
|
|
||||||
/* Discard temporary rte */
|
/* Discard temporary rte */
|
||||||
if (rt != n->routes)
|
if (rt != n->routes)
|
||||||
|
@ -1999,10 +1801,8 @@ rte_dump(rte *e)
|
||||||
{
|
{
|
||||||
net *n = e->net;
|
net *n = e->net;
|
||||||
debug("%-1N ", n->n.addr);
|
debug("%-1N ", n->n.addr);
|
||||||
debug("PF=%02x pref=%d ", e->pflags, e->pref);
|
debug("PF=%02x ", e->pflags);
|
||||||
rta_dump(e->attrs);
|
rta_dump(e->attrs);
|
||||||
if (e->attrs->src->proto->proto->dump_attrs)
|
|
||||||
e->attrs->src->proto->proto->dump_attrs(e);
|
|
||||||
debug("\n");
|
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
|
static inline btime
|
||||||
rt_settled_time(rtable *tab)
|
rt_settled_time(rtable *tab)
|
||||||
{
|
{
|
||||||
|
@ -2299,12 +2122,7 @@ static struct resclass rt_class = {
|
||||||
rtable *
|
rtable *
|
||||||
rt_setup(pool *pp, struct rtable_config *cf)
|
rt_setup(pool *pp, struct rtable_config *cf)
|
||||||
{
|
{
|
||||||
int ns = strlen("Routing table ") + strlen(cf->name) + 1;
|
pool *p = rp_newf(pp, "Routing table %s", cf->name);
|
||||||
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);
|
|
||||||
|
|
||||||
rtable *t = ralloc(p, &rt_class);
|
rtable *t = ralloc(p, &rt_class);
|
||||||
t->rp = p;
|
t->rp = p;
|
||||||
|
@ -2333,6 +2151,7 @@ rt_setup(pool *pp, struct rtable_config *cf)
|
||||||
hmap_set(&t->id_map, 0);
|
hmap_set(&t->id_map, 0);
|
||||||
|
|
||||||
t->rt_event = ev_new_init(p, rt_event, t);
|
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();
|
t->last_rt_change = t->gc_time = current_time();
|
||||||
|
|
||||||
if (rt_is_flow(t))
|
if (rt_is_flow(t))
|
||||||
|
@ -2380,7 +2199,7 @@ static void
|
||||||
rt_prune_table(rtable *tab)
|
rt_prune_table(rtable *tab)
|
||||||
{
|
{
|
||||||
struct fib_iterator *fit = &tab->prune_fit;
|
struct fib_iterator *fit = &tab->prune_fit;
|
||||||
int limit = 512;
|
int limit = 2000;
|
||||||
|
|
||||||
struct channel *c;
|
struct channel *c;
|
||||||
node *n, *x;
|
node *n, *x;
|
||||||
|
@ -2403,6 +2222,9 @@ rt_prune_table(rtable *tab)
|
||||||
FIB_ITERATE_INIT(fit, &tab->fib);
|
FIB_ITERATE_INIT(fit, &tab->fib);
|
||||||
tab->prune_state = 2;
|
tab->prune_state = 2;
|
||||||
|
|
||||||
|
tab->gc_counter = 0;
|
||||||
|
tab->gc_time = current_time();
|
||||||
|
|
||||||
if (tab->prune_trie)
|
if (tab->prune_trie)
|
||||||
{
|
{
|
||||||
/* Init prefix trie pruning */
|
/* Init prefix trie pruning */
|
||||||
|
@ -2462,9 +2284,6 @@ again:
|
||||||
fib_check(&tab->fib);
|
fib_check(&tab->fib);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
tab->gc_counter = 0;
|
|
||||||
tab->gc_time = current_time();
|
|
||||||
|
|
||||||
/* state change 2->0, 3->1 */
|
/* state change 2->0, 3->1 */
|
||||||
tab->prune_state &= 1;
|
tab->prune_state &= 1;
|
||||||
|
|
||||||
|
@ -2591,6 +2410,20 @@ rt_preconfig(struct config *c)
|
||||||
rt_new_table(cf_get_symbol("master6"), NET_IP6);
|
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
|
* 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));
|
memcpy(mls.stack, &a->nh.label[a->nh.labels - mls.len], mls.len * sizeof(u32));
|
||||||
|
|
||||||
rta_apply_hostentry(a, old->attrs->hostentry, &mls);
|
rta_apply_hostentry(a, old->attrs->hostentry, &mls);
|
||||||
a->aflags = 0;
|
a->cached = 0;
|
||||||
|
|
||||||
rte *e = sl_alloc(rte_slab);
|
rte *e = sl_alloc(rte_slab);
|
||||||
memcpy(e, old, sizeof(rte));
|
memcpy(e, old, sizeof(rte));
|
||||||
e->attrs = rta_lookup(a);
|
e->attrs = rta_lookup(a);
|
||||||
|
rt_lock_source(e->src);
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
@ -2844,12 +2678,16 @@ static rte *
|
||||||
rt_flowspec_update_rte(rtable *tab, rte *r)
|
rt_flowspec_update_rte(rtable *tab, rte *r)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_BGP
|
#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;
|
return NULL;
|
||||||
|
|
||||||
const net_addr *n = r->net->n.addr;
|
const net_addr *n = r->net->n.addr;
|
||||||
struct bgp_proto *p = (void *) r->attrs->src->proto;
|
struct bgp_proto *p = (void *) r->src->proto;
|
||||||
int valid = rt_flowspec_check(r->u.bgp.base_table, tab, n, r->attrs, p->is_interior);
|
int valid = rt_flowspec_check(bc->base_table, tab, n, r->attrs, p->is_interior);
|
||||||
int dest = valid ? RTD_NONE : RTD_UNREACHABLE;
|
int dest = valid ? RTD_NONE : RTD_UNREACHABLE;
|
||||||
|
|
||||||
if (dest == r->attrs->dest)
|
if (dest == r->attrs->dest)
|
||||||
|
@ -2858,11 +2696,12 @@ rt_flowspec_update_rte(rtable *tab, rte *r)
|
||||||
rta *a = alloca(RTA_MAX_SIZE);
|
rta *a = alloca(RTA_MAX_SIZE);
|
||||||
memcpy(a, r->attrs, rta_size(r->attrs));
|
memcpy(a, r->attrs, rta_size(r->attrs));
|
||||||
a->dest = dest;
|
a->dest = dest;
|
||||||
a->aflags = 0;
|
a->cached = 0;
|
||||||
|
|
||||||
rte *new = sl_alloc(rte_slab);
|
rte *new = sl_alloc(rte_slab);
|
||||||
memcpy(new, r, sizeof(rte));
|
memcpy(new, r, sizeof(rte));
|
||||||
new->attrs = rta_lookup(a);
|
new->attrs = rta_lookup(a);
|
||||||
|
rt_lock_source(new->src);
|
||||||
|
|
||||||
return new;
|
return new;
|
||||||
#else
|
#else
|
||||||
|
@ -2898,8 +2737,8 @@ rt_next_hop_update_net(rtable *tab, net *n)
|
||||||
|
|
||||||
/* Call a pre-comparison hook */
|
/* Call a pre-comparison hook */
|
||||||
/* Not really an efficient way to compute this */
|
/* Not really an efficient way to compute this */
|
||||||
if (e->attrs->src->proto->rte_recalculate)
|
if (e->src->proto->rte_recalculate)
|
||||||
e->attrs->src->proto->rte_recalculate(tab, n, new, e, NULL);
|
e->src->proto->rte_recalculate(tab, n, new, e, NULL);
|
||||||
|
|
||||||
if (e != old_best)
|
if (e != old_best)
|
||||||
rte_free_quick(e);
|
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);
|
cf_define_symbol(s, SYM_TABLE, table, c);
|
||||||
c->name = s->name;
|
c->name = s->name;
|
||||||
c->addr_type = addr_type;
|
c->addr_type = addr_type;
|
||||||
c->gc_max_ops = 1000;
|
c->gc_threshold = 1000;
|
||||||
c->gc_min_time = 5;
|
c->gc_period = (uint) -1; /* set in rt_postconfig() */
|
||||||
c->min_settle_time = 1 S;
|
c->min_settle_time = 1 S;
|
||||||
c->max_settle_time = 20 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);
|
net = net_get(tab, n);
|
||||||
|
|
||||||
if (!new->pref)
|
|
||||||
new->pref = c->preference;
|
|
||||||
|
|
||||||
if (!rta_is_cached(new->attrs))
|
if (!rta_is_cached(new->attrs))
|
||||||
new->attrs = rta_lookup(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 */
|
/* Find the old rte */
|
||||||
for (pos = &net->routes; old = *pos; pos = &old->next)
|
for (pos = &net->routes; old = *pos; pos = &old->next)
|
||||||
if (old->attrs->src == src)
|
if (old->src == src)
|
||||||
{
|
{
|
||||||
if (new && rte_same(old, new))
|
if (new && rte_same(old, new))
|
||||||
{
|
{
|
||||||
|
@ -3353,7 +3189,7 @@ rt_reload_channel(struct channel *c)
|
||||||
return 0;
|
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;
|
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)
|
if (new)
|
||||||
{
|
{
|
||||||
net = net_get(tab, n);
|
net = net_get(tab, n);
|
||||||
src = new->attrs->src;
|
src = new->src;
|
||||||
|
|
||||||
rte_store_tmp_attrs(new, rte_update_pool, NULL);
|
|
||||||
|
|
||||||
if (!rta_is_cached(new->attrs))
|
if (!rta_is_cached(new->attrs))
|
||||||
new->attrs = rta_lookup(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
|
else
|
||||||
{
|
{
|
||||||
net = net_find(tab, n);
|
net = net_find(tab, n);
|
||||||
src = old0->attrs->src;
|
src = old0->src;
|
||||||
|
|
||||||
if (!net)
|
if (!net)
|
||||||
goto drop_withdraw;
|
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 */
|
/* Find the old rte */
|
||||||
for (pos = &net->routes; old = *pos; pos = &old->next)
|
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))
|
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);
|
rem_node(&he->ln);
|
||||||
hc_remove(hc, he);
|
hc_remove(hc, he);
|
||||||
sl_free(hc->slab, he);
|
sl_free(he);
|
||||||
|
|
||||||
hc->hash_items--;
|
hc->hash_items--;
|
||||||
if (hc->hash_items < hc->hash_min)
|
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_alloc_table(hc, tab->rp, HC_DEF_ORDER);
|
||||||
hc->slab = sl_new(tab->rp, sizeof(struct hostentry));
|
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);
|
hc->trie = f_new_trie(hc->lp, 0);
|
||||||
|
|
||||||
tab->hostcache = hc;
|
tab->hostcache = hc;
|
||||||
|
@ -3679,36 +3513,12 @@ rt_get_igp_metric(rte *rt)
|
||||||
if (ea)
|
if (ea)
|
||||||
return ea->u.data;
|
return ea->u.data;
|
||||||
|
|
||||||
rta *a = rt->attrs;
|
if (rt->attrs->source == RTS_DEVICE)
|
||||||
|
|
||||||
#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)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
if (rt->src->proto->rte_igp_metric)
|
||||||
|
return rt->src->proto->rte_igp_metric(rt);
|
||||||
|
|
||||||
return IGP_METRIC_UNKNOWN;
|
return IGP_METRIC_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3732,9 +3542,10 @@ rt_update_hostentry(rtable *tab, struct hostentry *he)
|
||||||
{
|
{
|
||||||
rte *e = n->routes;
|
rte *e = n->routes;
|
||||||
rta *a = e->attrs;
|
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 */
|
/* Recursive route should not depend on another recursive route */
|
||||||
log(L_WARN "Next hop address %I resolvable through recursive route for %N",
|
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;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pxlen = n->n.addr->pxlen;
|
||||||
|
|
||||||
if (a->dest == RTD_UNICAST)
|
if (a->dest == RTD_UNICAST)
|
||||||
{
|
{
|
||||||
for (struct nexthop *nh = &(a->nh); nh; nh = nh->next)
|
for (struct nexthop *nh = &(a->nh); nh; nh = nh->next)
|
||||||
|
@ -3803,6 +3616,7 @@ rt_update_hostcache(rtable *tab)
|
||||||
struct hostentry *
|
struct hostentry *
|
||||||
rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep)
|
rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep)
|
||||||
{
|
{
|
||||||
|
ip_addr link = ipa_zero(ll) ? a : ll;
|
||||||
struct hostentry *he;
|
struct hostentry *he;
|
||||||
|
|
||||||
if (!tab->hostcache)
|
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);
|
u32 k = hc_hash(a, dep);
|
||||||
struct hostcache *hc = tab->hostcache;
|
struct hostcache *hc = tab->hostcache;
|
||||||
for (he = hc->hash_table[k >> hc->hash_shift]; he != NULL; he = he->next)
|
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;
|
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);
|
rt_update_hostentry(tab, he);
|
||||||
return he;
|
return he;
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,7 +119,7 @@ babel_get_source(struct babel_proto *p, struct babel_entry *e, u64 router_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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;
|
struct babel_source *n, *nx;
|
||||||
btime now_ = current_time();
|
btime now_ = current_time();
|
||||||
|
@ -129,11 +129,108 @@ babel_expire_sources(struct babel_proto *p, struct babel_entry *e)
|
||||||
if (n->expires && n->expires <= now_)
|
if (n->expires && n->expires <= now_)
|
||||||
{
|
{
|
||||||
rem_node(NODE n);
|
rem_node(NODE n);
|
||||||
sl_free(p->source_slab, n);
|
sl_free(n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u16
|
||||||
|
babel_calc_smoothed_metric(struct babel_proto *p, struct babel_route *r, u8 update)
|
||||||
|
{
|
||||||
|
struct babel_config *cf = (void *) p->p.cf;
|
||||||
|
uint metric = r->metric, smoothed_metric = r->smoothed_metric;
|
||||||
|
btime smoothed_time = r->smoothed_time, now = current_time();
|
||||||
|
|
||||||
|
if (!cf->metric_decay || metric == BABEL_INFINITY ||
|
||||||
|
metric == smoothed_metric || !smoothed_time)
|
||||||
|
{
|
||||||
|
smoothed_metric = metric;
|
||||||
|
smoothed_time = now;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
int diff = metric - smoothed_metric;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The decay defines the half-life of the metric convergence, so first iterate
|
||||||
|
* in halving steps
|
||||||
|
*/
|
||||||
|
while (smoothed_time < now - cf->metric_decay && diff) {
|
||||||
|
smoothed_metric += diff/2;
|
||||||
|
smoothed_time += cf->metric_decay;
|
||||||
|
diff = metric - smoothed_metric;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Then, update remainder in BABEL_SMOOTHING_STEP intervals using the
|
||||||
|
* exponential function (approximated via the pre-computed reciprocal).
|
||||||
|
*/
|
||||||
|
while (smoothed_time < now - BABEL_SMOOTHING_STEP && diff) {
|
||||||
|
smoothed_metric += (BABEL_SMOOTHING_STEP * diff *
|
||||||
|
(cf->smooth_recp - BABEL_SMOOTHING_UNIT) / BABEL_SMOOTHING_UNIT);
|
||||||
|
smoothed_time += BABEL_SMOOTHING_STEP;
|
||||||
|
diff = metric - smoothed_metric;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Consider the metric converged once we're close enough */
|
||||||
|
if (ABS(diff) < BABEL_SMOOTHING_MIN_DIFF)
|
||||||
|
smoothed_metric = metric;
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (update) {
|
||||||
|
r->smoothed_metric = smoothed_metric;
|
||||||
|
r->smoothed_time = smoothed_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
return smoothed_metric;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u16
|
||||||
|
babel_update_smoothed_metric(struct babel_proto *p, struct babel_route *r)
|
||||||
|
{
|
||||||
|
if (!r->metric)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!r->smoothed_metric) {
|
||||||
|
r->smoothed_metric = r->metric;
|
||||||
|
r->smoothed_time = current_time();
|
||||||
|
return r->smoothed_metric;
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 smoothed = babel_calc_smoothed_metric(p, r, 1);
|
||||||
|
DBG("Updated smoothed metric for prefix %N: router-id %lR metric %d/%d\n",
|
||||||
|
r->e->n.addr, r->router_id, r->metric, smoothed);
|
||||||
|
|
||||||
|
return smoothed;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u16
|
||||||
|
babel_smoothed_metric(struct babel_proto *p, struct babel_route *r)
|
||||||
|
{
|
||||||
|
return babel_calc_smoothed_metric(p, r, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
babel_update_metric(struct babel_proto *p, struct babel_route *r, u16 metric)
|
||||||
|
{
|
||||||
|
babel_update_smoothed_metric(p, r);
|
||||||
|
r->metric = metric;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u8
|
||||||
|
babel_route_better(struct babel_proto *p, struct babel_route *mod,
|
||||||
|
struct babel_route *best)
|
||||||
|
{
|
||||||
|
if (!mod->feasible)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!best)
|
||||||
|
return mod->metric < BABEL_INFINITY;
|
||||||
|
|
||||||
|
return (mod->metric < best->metric &&
|
||||||
|
babel_smoothed_metric(p, mod) < babel_smoothed_metric(p, best));
|
||||||
|
}
|
||||||
|
|
||||||
static struct babel_route *
|
static struct babel_route *
|
||||||
babel_find_route(struct babel_entry *e, struct babel_neighbor *n)
|
babel_find_route(struct babel_entry *e, struct babel_neighbor *n)
|
||||||
{
|
{
|
||||||
|
@ -151,8 +248,10 @@ babel_get_route(struct babel_proto *p, struct babel_entry *e, struct babel_neigh
|
||||||
{
|
{
|
||||||
struct babel_route *r = babel_find_route(e, nbr);
|
struct babel_route *r = babel_find_route(e, nbr);
|
||||||
|
|
||||||
if (r)
|
if (r) {
|
||||||
|
babel_update_smoothed_metric(p, r);
|
||||||
return r;
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
r = sl_allocz(p->route_slab);
|
r = sl_allocz(p->route_slab);
|
||||||
|
|
||||||
|
@ -174,7 +273,7 @@ babel_retract_route(struct babel_proto *p, struct babel_route *r)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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",
|
DBG("Babel: Flush route %N router_id %lR neigh %I\n",
|
||||||
r->e->n.addr, r->router_id, r->neigh->addr);
|
r->e->n.addr, r->router_id, r->neigh->addr);
|
||||||
|
@ -185,7 +284,7 @@ babel_flush_route(struct babel_proto *p, struct babel_route *r)
|
||||||
if (r->e->selected == r)
|
if (r->e->selected == r)
|
||||||
r->e->selected = NULL;
|
r->e->selected = NULL;
|
||||||
|
|
||||||
sl_free(p->route_slab, r);
|
sl_free(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -304,7 +403,7 @@ babel_add_seqno_request(struct babel_proto *p, struct babel_entry *e,
|
||||||
struct babel_seqno_request *sr;
|
struct babel_seqno_request *sr;
|
||||||
|
|
||||||
WALK_LIST(sr, e->requests)
|
WALK_LIST(sr, e->requests)
|
||||||
if (sr->router_id == router_id)
|
if (sr->router_id == router_id && sr->nbr == nbr)
|
||||||
{
|
{
|
||||||
/* Found matching or newer */
|
/* Found matching or newer */
|
||||||
if (ge_mod64k(sr->seqno, seqno) && seqno_request_valid(sr))
|
if (ge_mod64k(sr->seqno, seqno) && seqno_request_valid(sr))
|
||||||
|
@ -312,7 +411,9 @@ babel_add_seqno_request(struct babel_proto *p, struct babel_entry *e,
|
||||||
|
|
||||||
/* Found older */
|
/* Found older */
|
||||||
rem_node(NODE sr);
|
rem_node(NODE sr);
|
||||||
rem_node(&sr->nbr_node);
|
|
||||||
|
if (sr->nbr)
|
||||||
|
rem_node(&sr->nbr_node);
|
||||||
|
|
||||||
goto found;
|
goto found;
|
||||||
}
|
}
|
||||||
|
@ -336,30 +437,33 @@ found:
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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)
|
if (sr->nbr)
|
||||||
rem_node(&sr->nbr_node);
|
rem_node(&sr->nbr_node);
|
||||||
|
|
||||||
rem_node(NODE sr);
|
rem_node(NODE sr);
|
||||||
sl_free(p->seqno_slab, sr);
|
sl_free(sr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
babel_satisfy_seqno_request(struct babel_proto *p, struct babel_entry *e,
|
babel_satisfy_seqno_request(struct babel_proto *p, struct babel_entry *e,
|
||||||
u64 router_id, u16 seqno)
|
u64 router_id, u16 seqno)
|
||||||
{
|
{
|
||||||
struct babel_seqno_request *sr;
|
struct babel_seqno_request *sr, *srx;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
WALK_LIST(sr, e->requests)
|
WALK_LIST_DELSAFE(sr, srx, e->requests)
|
||||||
if ((sr->router_id == router_id) && ge_mod64k(seqno, sr->seqno))
|
if ((sr->router_id == router_id) && ge_mod64k(seqno, sr->seqno))
|
||||||
{
|
{
|
||||||
/* Found the request, remove it */
|
/* Found a matching request, remove it; there may be multiple outstanding
|
||||||
|
* requests, so continue looping
|
||||||
|
*/
|
||||||
babel_remove_seqno_request(p, sr);
|
babel_remove_seqno_request(p, sr);
|
||||||
return 1;
|
ret = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -432,6 +536,10 @@ babel_get_neighbor(struct babel_iface *ifa, ip_addr addr)
|
||||||
init_list(&nbr->requests);
|
init_list(&nbr->requests);
|
||||||
add_tail(&ifa->neigh_list, NODE nbr);
|
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;
|
return nbr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -452,13 +560,12 @@ babel_flush_neighbor(struct babel_proto *p, struct babel_neighbor *nbr)
|
||||||
|
|
||||||
struct babel_seqno_request *sr;
|
struct babel_seqno_request *sr;
|
||||||
WALK_LIST_FIRST2(sr, nbr_node, nbr->requests)
|
WALK_LIST_FIRST2(sr, nbr_node, nbr->requests)
|
||||||
{
|
babel_remove_seqno_request(p, sr);
|
||||||
sr->nbr = NULL;
|
|
||||||
rem_node(&sr->nbr_node);
|
|
||||||
}
|
|
||||||
|
|
||||||
nbr->ifa = NULL;
|
nbr->ifa = NULL;
|
||||||
rem_node(NODE nbr);
|
rem_node(NODE nbr);
|
||||||
|
mb_free(nbr->srtt_pool);
|
||||||
|
mb_free(nbr->srtt_pool_sorted);
|
||||||
mb_free(nbr);
|
mb_free(nbr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -570,6 +677,7 @@ babel_update_cost(struct babel_neighbor *nbr)
|
||||||
switch (cf->type)
|
switch (cf->type)
|
||||||
{
|
{
|
||||||
case BABEL_IFACE_TYPE_WIRED:
|
case BABEL_IFACE_TYPE_WIRED:
|
||||||
|
case BABEL_IFACE_TYPE_TUNNEL:
|
||||||
/* k-out-of-j selection - Appendix 2.1 in the RFC. */
|
/* k-out-of-j selection - Appendix 2.1 in the RFC. */
|
||||||
|
|
||||||
/* Link is bad if less than cf->limit/16 of expected hellos were received */
|
/* Link is bad if less than cf->limit/16 of expected hellos were received */
|
||||||
|
@ -598,6 +706,25 @@ babel_update_cost(struct babel_neighbor *nbr)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cf->rtt_cost && nbr->srtt > cf->rtt_min)
|
||||||
|
{
|
||||||
|
uint rtt_cost = cf->rtt_cost;
|
||||||
|
|
||||||
|
if (nbr->srtt < cf->rtt_max)
|
||||||
|
{
|
||||||
|
uint rtt_interval = cf->rtt_max TO_US - cf->rtt_min TO_US;
|
||||||
|
uint rtt_diff = (nbr->srtt TO_US - cf->rtt_min TO_US);
|
||||||
|
|
||||||
|
rtt_cost = (rtt_cost * rtt_diff) / rtt_interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
txcost = MIN(txcost + rtt_cost, BABEL_INFINITY);
|
||||||
|
rxcost = MIN(rxcost + rtt_cost, BABEL_INFINITY);
|
||||||
|
|
||||||
|
TRACE(D_EVENTS, "Added RTT cost %u to nbr %I on %s with srtt %u.%03u ms",
|
||||||
|
rtt_cost, nbr->addr, nbr->ifa->iface->name, nbr->srtt/1000, nbr->srtt%1000);
|
||||||
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
/* If RX cost changed, send IHU with next Hello */
|
/* If RX cost changed, send IHU with next Hello */
|
||||||
if (rxcost != nbr->rxcost)
|
if (rxcost != nbr->rxcost)
|
||||||
|
@ -617,7 +744,7 @@ done:
|
||||||
struct babel_route *r; node *n;
|
struct babel_route *r; node *n;
|
||||||
WALK_LIST2(r, n, nbr->routes, neigh_route)
|
WALK_LIST2(r, n, nbr->routes, neigh_route)
|
||||||
{
|
{
|
||||||
r->metric = babel_compute_metric(nbr, r->advert_metric);
|
babel_update_metric(p, r, babel_compute_metric(nbr, r->advert_metric));
|
||||||
babel_select_route(p, r->e, r);
|
babel_select_route(p, r->e, r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -641,13 +768,36 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
|
||||||
if (r)
|
if (r)
|
||||||
{
|
{
|
||||||
rta a0 = {
|
rta a0 = {
|
||||||
.src = p->p.main_source,
|
|
||||||
.source = RTS_BABEL,
|
.source = RTS_BABEL,
|
||||||
.scope = SCOPE_UNIVERSE,
|
.scope = SCOPE_UNIVERSE,
|
||||||
.dest = RTD_UNICAST,
|
.dest = RTD_UNICAST,
|
||||||
|
.pref = c->preference,
|
||||||
.from = r->neigh->addr,
|
.from = r->neigh->addr,
|
||||||
.nh.gw = r->next_hop,
|
.nh.gw = r->next_hop,
|
||||||
.nh.iface = r->neigh->ifa->iface,
|
.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,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -659,11 +809,7 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
|
||||||
a0.nh.flags = RNF_ONLINK;
|
a0.nh.flags = RNF_ONLINK;
|
||||||
|
|
||||||
rta *a = rta_lookup(&a0);
|
rta *a = rta_lookup(&a0);
|
||||||
rte *rte = rte_get_temp(a);
|
rte *rte = rte_get_temp(a, p->p.main_source);
|
||||||
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);
|
|
||||||
|
|
||||||
e->unreachable = 0;
|
e->unreachable = 0;
|
||||||
rte_update2(c, e->n.addr, rte, p->p.main_source);
|
rte_update2(c, e->n.addr, rte, p->p.main_source);
|
||||||
|
@ -672,17 +818,15 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
|
||||||
{
|
{
|
||||||
/* Unreachable */
|
/* Unreachable */
|
||||||
rta a0 = {
|
rta a0 = {
|
||||||
.src = p->p.main_source,
|
|
||||||
.source = RTS_BABEL,
|
.source = RTS_BABEL,
|
||||||
.scope = SCOPE_UNIVERSE,
|
.scope = SCOPE_UNIVERSE,
|
||||||
.dest = RTD_UNREACHABLE,
|
.dest = RTD_UNREACHABLE,
|
||||||
|
.pref = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
rta *a = rta_lookup(&a0);
|
rta *a = rta_lookup(&a0);
|
||||||
rte *rte = rte_get_temp(a);
|
rte *rte = rte_get_temp(a, p->p.main_source);
|
||||||
memset(&rte->u.babel, 0, sizeof(rte->u.babel));
|
|
||||||
rte->pflags = 0;
|
rte->pflags = 0;
|
||||||
rte->pref = 1;
|
|
||||||
|
|
||||||
e->unreachable = 1;
|
e->unreachable = 1;
|
||||||
rte_update2(c, e->n.addr, rte, p->p.main_source);
|
rte_update2(c, e->n.addr, rte, p->p.main_source);
|
||||||
|
@ -741,8 +885,7 @@ babel_select_route(struct babel_proto *p, struct babel_entry *e, struct babel_ro
|
||||||
/* Shortcut if only non-best was modified */
|
/* Shortcut if only non-best was modified */
|
||||||
if (mod && (mod != best))
|
if (mod && (mod != best))
|
||||||
{
|
{
|
||||||
/* Either select modified route, or keep old best route */
|
if (babel_route_better(p, mod, best))
|
||||||
if ((mod->metric < (best ? best->metric : BABEL_INFINITY)) && mod->feasible)
|
|
||||||
best = mod;
|
best = mod;
|
||||||
else
|
else
|
||||||
return;
|
return;
|
||||||
|
@ -753,17 +896,24 @@ babel_select_route(struct babel_proto *p, struct babel_entry *e, struct babel_ro
|
||||||
if (!best || (best->metric == BABEL_INFINITY) || !best->feasible)
|
if (!best || (best->metric == BABEL_INFINITY) || !best->feasible)
|
||||||
best = NULL;
|
best = NULL;
|
||||||
|
|
||||||
|
/* best will be compared to many routes below, make sure it's up-to-date */
|
||||||
|
if (best)
|
||||||
|
babel_update_smoothed_metric(p, best);
|
||||||
|
|
||||||
/* Find the best feasible route from all routes */
|
/* Find the best feasible route from all routes */
|
||||||
WALK_LIST(r, e->routes)
|
WALK_LIST(r, e->routes)
|
||||||
if ((r->metric < (best ? best->metric : BABEL_INFINITY)) && r->feasible)
|
if (babel_route_better(p, r, best))
|
||||||
best = r;
|
best = r;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (best)
|
if (best)
|
||||||
{
|
{
|
||||||
if (best != e->selected)
|
if (best != e->selected)
|
||||||
TRACE(D_EVENTS, "Picked new route for prefix %N: router-id %lR metric %d",
|
{
|
||||||
e->n.addr, best->router_id, best->metric);
|
u16 smoothed = babel_update_smoothed_metric(p, best);
|
||||||
|
TRACE(D_EVENTS, "Picked new route for prefix %N: router-id %lR metric %d/%d",
|
||||||
|
e->n.addr, best->router_id, best->metric, smoothed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (e->selected)
|
else if (e->selected)
|
||||||
{
|
{
|
||||||
|
@ -812,6 +962,12 @@ babel_build_ihu(union babel_msg *msg, struct babel_iface *ifa, struct babel_neig
|
||||||
msg->ihu.rxcost = n->rxcost;
|
msg->ihu.rxcost = n->rxcost;
|
||||||
msg->ihu.interval = ifa->cf->ihu_interval;
|
msg->ihu.interval = ifa->cf->ihu_interval;
|
||||||
|
|
||||||
|
if (n->last_tstamp_rcvd && ifa->cf->rtt_send)
|
||||||
|
{
|
||||||
|
msg->ihu.tstamp = n->last_tstamp;
|
||||||
|
msg->ihu.tstamp_rcvd = n->last_tstamp_rcvd TO_US;
|
||||||
|
}
|
||||||
|
|
||||||
TRACE(D_PACKETS, "Sending IHU for %I with rxcost %d interval %t",
|
TRACE(D_PACKETS, "Sending IHU for %I with rxcost %d interval %t",
|
||||||
msg->ihu.addr, msg->ihu.rxcost, (btime) msg->ihu.interval);
|
msg->ihu.addr, msg->ihu.rxcost, (btime) msg->ihu.interval);
|
||||||
}
|
}
|
||||||
|
@ -851,6 +1007,9 @@ babel_send_hello(struct babel_iface *ifa, uint interval)
|
||||||
msg.hello.seqno = ifa->hello_seqno++;
|
msg.hello.seqno = ifa->hello_seqno++;
|
||||||
msg.hello.interval = interval ?: ifa->cf->hello_interval;
|
msg.hello.interval = interval ?: ifa->cf->hello_interval;
|
||||||
|
|
||||||
|
if (ifa->cf->rtt_send)
|
||||||
|
msg.hello.tstamp = 1; /* real timestamp will be set on TLV write */
|
||||||
|
|
||||||
TRACE(D_PACKETS, "Sending hello on %s with seqno %d interval %t",
|
TRACE(D_PACKETS, "Sending hello on %s with seqno %d interval %t",
|
||||||
ifa->ifname, msg.hello.seqno, (btime) msg.hello.interval);
|
ifa->ifname, msg.hello.seqno, (btime) msg.hello.interval);
|
||||||
|
|
||||||
|
@ -1146,14 +1305,41 @@ babel_handle_hello(union babel_msg *m, struct babel_iface *ifa)
|
||||||
msg->seqno, (btime) msg->interval);
|
msg->seqno, (btime) msg->interval);
|
||||||
|
|
||||||
struct babel_neighbor *n = babel_get_neighbor(ifa, msg->sender);
|
struct babel_neighbor *n = babel_get_neighbor(ifa, msg->sender);
|
||||||
|
struct babel_iface_config *cf = n->ifa->cf;
|
||||||
int first_hello = !n->hello_cnt;
|
int first_hello = !n->hello_cnt;
|
||||||
|
|
||||||
|
if (msg->tstamp)
|
||||||
|
{
|
||||||
|
n->last_tstamp = msg->tstamp;
|
||||||
|
n->last_tstamp_rcvd = msg->pkt_received;
|
||||||
|
}
|
||||||
babel_update_hello_history(n, msg->seqno, msg->interval);
|
babel_update_hello_history(n, msg->seqno, msg->interval);
|
||||||
babel_update_cost(n);
|
babel_update_cost(n);
|
||||||
|
|
||||||
/* Speed up session establishment by sending IHU immediately */
|
/* Speed up session establishment by sending IHU immediately */
|
||||||
if (first_hello)
|
if (first_hello)
|
||||||
babel_send_ihu(ifa, n);
|
{
|
||||||
|
/* if using RTT, all IHUs must be paired with hellos */
|
||||||
|
if(cf->rtt_send)
|
||||||
|
babel_send_hello(ifa, 0);
|
||||||
|
else
|
||||||
|
babel_send_ihu(ifa, n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
void
|
||||||
|
@ -1172,6 +1358,51 @@ babel_handle_ihu(union babel_msg *m, struct babel_iface *ifa)
|
||||||
struct babel_neighbor *n = babel_get_neighbor(ifa, msg->sender);
|
struct babel_neighbor *n = babel_get_neighbor(ifa, msg->sender);
|
||||||
n->txcost = msg->rxcost;
|
n->txcost = msg->rxcost;
|
||||||
n->ihu_expiry = current_time() + BABEL_IHU_EXPIRY_FACTOR(msg->interval);
|
n->ihu_expiry = current_time() + BABEL_IHU_EXPIRY_FACTOR(msg->interval);
|
||||||
|
|
||||||
|
if (msg->tstamp)
|
||||||
|
{
|
||||||
|
u32 rtt_sample = 0, pkt_received = msg->pkt_received TO_US;
|
||||||
|
int remote_time, full_time;
|
||||||
|
|
||||||
|
/* processing time reported by peer */
|
||||||
|
remote_time = (n->last_tstamp - msg->tstamp_rcvd);
|
||||||
|
/* time since we sent the last timestamp - RTT including remote time */
|
||||||
|
full_time = (pkt_received - msg->tstamp);
|
||||||
|
|
||||||
|
/* sanity checks */
|
||||||
|
if (remote_time < 0 || full_time < 0 ||
|
||||||
|
remote_time US_ > BABEL_RTT_MAX_VALUE || full_time US_ > BABEL_RTT_MAX_VALUE)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (remote_time < full_time)
|
||||||
|
rtt_sample = full_time - remote_time;
|
||||||
|
|
||||||
|
if (n->srtt)
|
||||||
|
{
|
||||||
|
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 = (*(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
babel_update_cost(n);
|
babel_update_cost(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1288,9 +1519,9 @@ babel_handle_update(union babel_msg *m, struct babel_iface *ifa)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Last paragraph above - update the entry */
|
/* Last paragraph above - update the entry */
|
||||||
|
babel_update_metric(p, r, metric);
|
||||||
r->feasible = feasible;
|
r->feasible = feasible;
|
||||||
r->seqno = msg->seqno;
|
r->seqno = msg->seqno;
|
||||||
r->metric = metric;
|
|
||||||
r->advert_metric = msg->metric;
|
r->advert_metric = msg->metric;
|
||||||
r->router_id = msg->router_id;
|
r->router_id = msg->router_id;
|
||||||
r->next_hop = msg->next_hop;
|
r->next_hop = msg->next_hop;
|
||||||
|
@ -2010,7 +2241,13 @@ babel_dump(struct proto *P)
|
||||||
static void
|
static void
|
||||||
babel_get_route_info(rte *rte, byte *buf)
|
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
|
static int
|
||||||
|
@ -2018,6 +2255,9 @@ babel_get_attr(const eattr *a, byte *buf, int buflen UNUSED)
|
||||||
{
|
{
|
||||||
switch (a->id)
|
switch (a->id)
|
||||||
{
|
{
|
||||||
|
case EA_BABEL_SEQNO:
|
||||||
|
return GA_FULL;
|
||||||
|
|
||||||
case EA_BABEL_METRIC:
|
case EA_BABEL_METRIC:
|
||||||
bsprintf(buf, "metric: %d", a->u.data);
|
bsprintf(buf, "metric: %d", a->u.data);
|
||||||
return GA_FULL;
|
return GA_FULL;
|
||||||
|
@ -2087,8 +2327,8 @@ babel_show_neighbors(struct proto *P, const char *iff)
|
||||||
}
|
}
|
||||||
|
|
||||||
cli_msg(-1024, "%s:", p->p.name);
|
cli_msg(-1024, "%s:", p->p.name);
|
||||||
cli_msg(-1024, "%-25s %-10s %6s %6s %6s %7s %4s",
|
cli_msg(-1024, "%-25s %-10s %6s %6s %6s %7s %4s %11s",
|
||||||
"IP address", "Interface", "Metric", "Routes", "Hellos", "Expires", "Auth");
|
"IP address", "Interface", "Metric", "Routes", "Hellos", "Expires", "Auth", "RTT");
|
||||||
|
|
||||||
WALK_LIST(ifa, p->interfaces)
|
WALK_LIST(ifa, p->interfaces)
|
||||||
{
|
{
|
||||||
|
@ -2103,9 +2343,10 @@ babel_show_neighbors(struct proto *P, const char *iff)
|
||||||
|
|
||||||
uint hellos = u32_popcount(n->hello_map);
|
uint hellos = u32_popcount(n->hello_map);
|
||||||
btime timer = (n->hello_expiry ?: n->init_expiry) - current_time();
|
btime timer = (n->hello_expiry ?: n->init_expiry) - current_time();
|
||||||
cli_msg(-1024, "%-25I %-10s %6u %6u %6u %7t %-4s",
|
cli_msg(-1024, "%-25I %-10s %6u %6u %6u %7t %-4s %5u.%03ums",
|
||||||
n->addr, ifa->iface->name, n->cost, rts, hellos, MAX(timer, 0),
|
n->addr, ifa->iface->name, n->cost, rts, hellos, MAX(timer, 0),
|
||||||
n->auth_passed ? "Yes" : "No");
|
n->auth_passed ? "Yes" : "No",
|
||||||
|
n->srtt/1000, n->srtt%1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2231,38 +2472,16 @@ babel_kick_timer(struct babel_proto *p)
|
||||||
|
|
||||||
|
|
||||||
static int
|
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 */
|
/* 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 -1;
|
||||||
|
|
||||||
return 0;
|
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),
|
* babel_rt_notify - core tells us about new route (possibly our own),
|
||||||
* so store it into our data structures.
|
* so store it into our data structures.
|
||||||
|
@ -2277,10 +2496,22 @@ babel_rt_notify(struct proto *P, struct channel *c UNUSED, struct network *net,
|
||||||
if (new)
|
if (new)
|
||||||
{
|
{
|
||||||
/* Update */
|
/* Update */
|
||||||
uint internal = (new->attrs->src->proto == P);
|
uint rt_seqno;
|
||||||
uint rt_seqno = internal ? new->u.babel.seqno : p->update_seqno;
|
|
||||||
uint rt_metric = ea_get_int(new->attrs->eattrs, EA_BABEL_METRIC, 0);
|
uint rt_metric = ea_get_int(new->attrs->eattrs, EA_BABEL_METRIC, 0);
|
||||||
u64 rt_router_id = 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)
|
if (rt_metric > BABEL_INFINITY)
|
||||||
{
|
{
|
||||||
|
@ -2323,15 +2554,16 @@ babel_rt_notify(struct proto *P, struct channel *c UNUSED, struct network *net,
|
||||||
static int
|
static int
|
||||||
babel_rte_better(struct rte *new, struct rte *old)
|
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
|
static u32
|
||||||
babel_rte_same(struct rte *new, struct rte *old)
|
babel_rte_igp_metric(struct rte *rt)
|
||||||
{
|
{
|
||||||
return ((new->u.babel.seqno == old->u.babel.seqno) &&
|
return ea_get_int(rt->attrs->eattrs, EA_BABEL_METRIC, BABEL_INFINITY);
|
||||||
(new->u.babel.metric == old->u.babel.metric) &&
|
|
||||||
(new->u.babel.router_id == old->u.babel.router_id));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2365,10 +2597,8 @@ babel_init(struct proto_config *CF)
|
||||||
P->if_notify = babel_if_notify;
|
P->if_notify = babel_if_notify;
|
||||||
P->rt_notify = babel_rt_notify;
|
P->rt_notify = babel_rt_notify;
|
||||||
P->preexport = babel_preexport;
|
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_better = babel_rte_better;
|
||||||
P->rte_same = babel_rte_same;
|
P->rte_igp_metric = babel_rte_igp_metric;
|
||||||
|
|
||||||
return P;
|
return P;
|
||||||
}
|
}
|
||||||
|
@ -2484,3 +2714,9 @@ struct protocol proto_babel = {
|
||||||
.get_route_info = babel_get_route_info,
|
.get_route_info = babel_get_route_info,
|
||||||
.get_attr = babel_get_attr
|
.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_METRIC EA_CODE(PROTOCOL_BABEL, 0)
|
||||||
#define EA_BABEL_ROUTER_ID EA_CODE(PROTOCOL_BABEL, 1)
|
#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_MAGIC 42
|
||||||
#define BABEL_VERSION 2
|
#define BABEL_VERSION 2
|
||||||
|
@ -50,10 +51,28 @@
|
||||||
#define BABEL_GARBAGE_INTERVAL (300 S_)
|
#define BABEL_GARBAGE_INTERVAL (300 S_)
|
||||||
#define BABEL_RXCOST_WIRED 96
|
#define BABEL_RXCOST_WIRED 96
|
||||||
#define BABEL_RXCOST_WIRELESS 256
|
#define BABEL_RXCOST_WIRELESS 256
|
||||||
|
#define BABEL_RXCOST_RTT 96
|
||||||
#define BABEL_INITIAL_HOP_COUNT 255
|
#define BABEL_INITIAL_HOP_COUNT 255
|
||||||
#define BABEL_MAX_SEND_INTERVAL 5 /* Unused ? */
|
#define BABEL_MAX_SEND_INTERVAL 5 /* Unused ? */
|
||||||
#define BABEL_INITIAL_NEIGHBOR_TIMEOUT (60 S_)
|
#define BABEL_INITIAL_NEIGHBOR_TIMEOUT (60 S_)
|
||||||
|
|
||||||
|
#define BABEL_RTT_MAX_VALUE (600 S_)
|
||||||
|
#define BABEL_RTT_MIN (10 MS_)
|
||||||
|
#define BABEL_RTT_MAX (120 MS_)
|
||||||
|
#define BABEL_RTT_WINLEN 100
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Constants for calculating metric smoothing. Chosen so that:
|
||||||
|
* log(2) = BABEL_SMOOTHING_CONSTANT / BABEL_SMOOTHING_UNIT, which means that
|
||||||
|
* log(2)/x can be calculated as BABEL_SMOOTHING_UNIT + BABEL_SMOOTHING_CONSTANT / x
|
||||||
|
*/
|
||||||
|
#define BABEL_SMOOTHING_UNIT 0x10000000
|
||||||
|
#define BABEL_SMOOTHING_CONSTANT 186065279
|
||||||
|
#define BABEL_SMOOTHING_STEP (1 S_) /* smoothing calculated in this step size */
|
||||||
|
#define BABEL_SMOOTHING_MIN_DIFF 4 /* metric diff beneath this is converged */
|
||||||
|
#define BABEL_SMOOTHING_DECAY (4 S_)
|
||||||
|
#define BABEL_SMOOTHING_DECAY_MAX (180 S_)
|
||||||
|
|
||||||
/* Max interval that will not overflow when carried as 16-bit centiseconds */
|
/* Max interval that will not overflow when carried as 16-bit centiseconds */
|
||||||
#define BABEL_TIME_UNITS 10000 /* On-wire times are counted in centiseconds */
|
#define BABEL_TIME_UNITS 10000 /* On-wire times are counted in centiseconds */
|
||||||
#define BABEL_MIN_INTERVAL (0x0001 * BABEL_TIME_UNITS)
|
#define BABEL_MIN_INTERVAL (0x0001 * BABEL_TIME_UNITS)
|
||||||
|
@ -93,6 +112,8 @@ enum babel_tlv_type {
|
||||||
enum babel_subtlv_type {
|
enum babel_subtlv_type {
|
||||||
BABEL_SUBTLV_PAD1 = 0,
|
BABEL_SUBTLV_PAD1 = 0,
|
||||||
BABEL_SUBTLV_PADN = 1,
|
BABEL_SUBTLV_PADN = 1,
|
||||||
|
BABEL_SUBTLV_DIVERSITY = 2, /* we don't support this */
|
||||||
|
BABEL_SUBTLV_TIMESTAMP = 3,
|
||||||
|
|
||||||
/* Mandatory subtlvs */
|
/* Mandatory subtlvs */
|
||||||
BABEL_SUBTLV_SOURCE_PREFIX = 128,
|
BABEL_SUBTLV_SOURCE_PREFIX = 128,
|
||||||
|
@ -103,6 +124,7 @@ enum babel_iface_type {
|
||||||
BABEL_IFACE_TYPE_UNDEF = 0,
|
BABEL_IFACE_TYPE_UNDEF = 0,
|
||||||
BABEL_IFACE_TYPE_WIRED = 1,
|
BABEL_IFACE_TYPE_WIRED = 1,
|
||||||
BABEL_IFACE_TYPE_WIRELESS = 2,
|
BABEL_IFACE_TYPE_WIRELESS = 2,
|
||||||
|
BABEL_IFACE_TYPE_TUNNEL = 3,
|
||||||
BABEL_IFACE_TYPE_MAX
|
BABEL_IFACE_TYPE_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -118,6 +140,8 @@ enum babel_ae_type {
|
||||||
struct babel_config {
|
struct babel_config {
|
||||||
struct proto_config c;
|
struct proto_config c;
|
||||||
list iface_list; /* List of iface configs (struct babel_iface_config) */
|
list iface_list; /* List of iface configs (struct babel_iface_config) */
|
||||||
|
btime metric_decay;
|
||||||
|
uint smooth_recp; /* Reciprocal for exponential metric smoothing */
|
||||||
uint hold_time; /* Time to hold stale entries and unreachable routes */
|
uint hold_time; /* Time to hold stale entries and unreachable routes */
|
||||||
u8 randomize_router_id;
|
u8 randomize_router_id;
|
||||||
|
|
||||||
|
@ -137,6 +161,12 @@ struct babel_iface_config {
|
||||||
uint ihu_interval; /* IHU interval, in us */
|
uint ihu_interval; /* IHU interval, in us */
|
||||||
uint update_interval; /* Update interval, in us */
|
uint update_interval; /* Update interval, in us */
|
||||||
|
|
||||||
|
btime rtt_min; /* rtt above which to start penalising metric */
|
||||||
|
btime rtt_max; /* max rtt metric penalty applied above this */
|
||||||
|
u16 rtt_cost; /* metric penalty to apply at rtt_max */
|
||||||
|
u16 rtt_win_len; /* smoothing windows length */
|
||||||
|
u8 rtt_send; /* whether to send timestamps on this interface */
|
||||||
|
|
||||||
u16 rx_buffer; /* RX buffer size, 0 for MTU */
|
u16 rx_buffer; /* RX buffer size, 0 for MTU */
|
||||||
u16 tx_length; /* TX packet length limit (including headers), 0 for MTU */
|
u16 tx_length; /* TX packet length limit (including headers), 0 for MTU */
|
||||||
int tx_tos;
|
int tx_tos;
|
||||||
|
@ -224,6 +254,14 @@ struct babel_neighbor {
|
||||||
u16 next_hello_seqno;
|
u16 next_hello_seqno;
|
||||||
uint last_hello_int;
|
uint last_hello_int;
|
||||||
|
|
||||||
|
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;
|
u32 auth_pc;
|
||||||
u8 auth_passed;
|
u8 auth_passed;
|
||||||
u8 auth_index_len;
|
u8 auth_index_len;
|
||||||
|
@ -261,9 +299,11 @@ struct babel_route {
|
||||||
u16 seqno;
|
u16 seqno;
|
||||||
u16 metric;
|
u16 metric;
|
||||||
u16 advert_metric;
|
u16 advert_metric;
|
||||||
|
u16 smoothed_metric;
|
||||||
u64 router_id;
|
u64 router_id;
|
||||||
ip_addr next_hop;
|
ip_addr next_hop;
|
||||||
btime refresh_time;
|
btime refresh_time;
|
||||||
|
btime smoothed_time;
|
||||||
btime expires;
|
btime expires;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -321,6 +361,8 @@ struct babel_msg_hello {
|
||||||
u16 seqno;
|
u16 seqno;
|
||||||
uint interval;
|
uint interval;
|
||||||
ip_addr sender;
|
ip_addr sender;
|
||||||
|
u32 tstamp;
|
||||||
|
btime pkt_received;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct babel_msg_ihu {
|
struct babel_msg_ihu {
|
||||||
|
@ -330,6 +372,9 @@ struct babel_msg_ihu {
|
||||||
uint interval;
|
uint interval;
|
||||||
ip_addr addr;
|
ip_addr addr;
|
||||||
ip_addr sender;
|
ip_addr sender;
|
||||||
|
u32 tstamp;
|
||||||
|
u32 tstamp_rcvd;
|
||||||
|
btime pkt_received;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct babel_msg_update {
|
struct babel_msg_update {
|
||||||
|
|
|
@ -23,9 +23,10 @@ CF_DEFINES
|
||||||
CF_DECLS
|
CF_DECLS
|
||||||
|
|
||||||
CF_KEYWORDS(BABEL, INTERFACE, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT,
|
CF_KEYWORDS(BABEL, INTERFACE, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT,
|
||||||
TYPE, WIRED, WIRELESS, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK, LINK,
|
TYPE, WIRED, WIRELESS, TUNNEL, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK,
|
||||||
NEXT, HOP, IPV4, IPV6, BABEL_METRIC, SHOW, INTERFACES, NEIGHBORS,
|
LINK, NEXT, HOP, IPV4, IPV6, BABEL_METRIC, SHOW, INTERFACES, NEIGHBORS,
|
||||||
ENTRIES, RANDOMIZE, ROUTER, ID, AUTHENTICATION, NONE, MAC, PERMISSIVE)
|
ENTRIES, RANDOMIZE, ROUTER, ID, AUTHENTICATION, NONE, MAC, PERMISSIVE,
|
||||||
|
RTT, MIN, MAX, DECAY, WINLEN, SEND, TIMESTAMPS)
|
||||||
|
|
||||||
CF_GRAMMAR
|
CF_GRAMMAR
|
||||||
|
|
||||||
|
@ -36,6 +37,13 @@ babel_proto_start: proto_start BABEL
|
||||||
this_proto = proto_config_new(&proto_babel, $1);
|
this_proto = proto_config_new(&proto_babel, $1);
|
||||||
init_list(&BABEL_CFG->iface_list);
|
init_list(&BABEL_CFG->iface_list);
|
||||||
BABEL_CFG->hold_time = 1 S_;
|
BABEL_CFG->hold_time = 1 S_;
|
||||||
|
BABEL_CFG->metric_decay = BABEL_SMOOTHING_DECAY;
|
||||||
|
};
|
||||||
|
|
||||||
|
babel_proto_finish:
|
||||||
|
{
|
||||||
|
if (BABEL_CFG->metric_decay)
|
||||||
|
BABEL_CFG->smooth_recp = BABEL_SMOOTHING_UNIT + BABEL_SMOOTHING_CONSTANT / BABEL_CFG->metric_decay;
|
||||||
};
|
};
|
||||||
|
|
||||||
babel_proto_item:
|
babel_proto_item:
|
||||||
|
@ -43,6 +51,7 @@ babel_proto_item:
|
||||||
| proto_channel
|
| proto_channel
|
||||||
| INTERFACE babel_iface
|
| INTERFACE babel_iface
|
||||||
| RANDOMIZE ROUTER ID bool { BABEL_CFG->randomize_router_id = $4; }
|
| RANDOMIZE ROUTER ID bool { BABEL_CFG->randomize_router_id = $4; }
|
||||||
|
| METRIC DECAY expr_us { BABEL_CFG->metric_decay = $3; if ($3 && (($3 < BABEL_SMOOTHING_STEP) || ($3 > BABEL_SMOOTHING_DECAY_MAX))) cf_error("Metric decay must be 0, or between 1-180s"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
babel_proto_opts:
|
babel_proto_opts:
|
||||||
|
@ -51,7 +60,7 @@ babel_proto_opts:
|
||||||
;
|
;
|
||||||
|
|
||||||
babel_proto:
|
babel_proto:
|
||||||
babel_proto_start proto_name '{' babel_proto_opts '}';
|
babel_proto_start proto_name '{' babel_proto_opts '}' babel_proto_finish;
|
||||||
|
|
||||||
|
|
||||||
babel_iface_start:
|
babel_iface_start:
|
||||||
|
@ -66,6 +75,10 @@ babel_iface_start:
|
||||||
BABEL_IFACE->limit = BABEL_HELLO_LIMIT;
|
BABEL_IFACE->limit = BABEL_HELLO_LIMIT;
|
||||||
BABEL_IFACE->tx_tos = IP_PREC_INTERNET_CONTROL;
|
BABEL_IFACE->tx_tos = IP_PREC_INTERNET_CONTROL;
|
||||||
BABEL_IFACE->tx_priority = sk_priority_control;
|
BABEL_IFACE->tx_priority = sk_priority_control;
|
||||||
|
BABEL_IFACE->rtt_min = BABEL_RTT_MIN;
|
||||||
|
BABEL_IFACE->rtt_max = BABEL_RTT_MAX;
|
||||||
|
BABEL_IFACE->rtt_win_len = BABEL_RTT_WINLEN;
|
||||||
|
BABEL_IFACE->rtt_send = 1;
|
||||||
BABEL_IFACE->check_link = 1;
|
BABEL_IFACE->check_link = 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -85,8 +98,16 @@ babel_iface_finish:
|
||||||
BABEL_IFACE->hello_interval = BABEL_HELLO_INTERVAL_WIRED;
|
BABEL_IFACE->hello_interval = BABEL_HELLO_INTERVAL_WIRED;
|
||||||
if (!BABEL_IFACE->rxcost)
|
if (!BABEL_IFACE->rxcost)
|
||||||
BABEL_IFACE->rxcost = BABEL_RXCOST_WIRED;
|
BABEL_IFACE->rxcost = BABEL_RXCOST_WIRED;
|
||||||
|
if (BABEL_IFACE->type == BABEL_IFACE_TYPE_TUNNEL && !BABEL_IFACE->rtt_cost)
|
||||||
|
BABEL_IFACE->rtt_cost = BABEL_RXCOST_RTT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (BABEL_IFACE->rtt_cost && !BABEL_IFACE->rtt_send)
|
||||||
|
cf_error("Can't set RTT cost when sending timestamps is disabled");
|
||||||
|
|
||||||
|
if (BABEL_IFACE->rtt_min >= BABEL_IFACE->rtt_max)
|
||||||
|
cf_error("Min RTT must be smaller than max RTT");
|
||||||
|
|
||||||
/* Make sure we do not overflow the 16-bit centisec fields */
|
/* Make sure we do not overflow the 16-bit centisec fields */
|
||||||
if (!BABEL_IFACE->update_interval)
|
if (!BABEL_IFACE->update_interval)
|
||||||
BABEL_IFACE->update_interval = MIN_(BABEL_IFACE->hello_interval*BABEL_UPDATE_INTERVAL_FACTOR, BABEL_MAX_INTERVAL);
|
BABEL_IFACE->update_interval = MIN_(BABEL_IFACE->hello_interval*BABEL_UPDATE_INTERVAL_FACTOR, BABEL_MAX_INTERVAL);
|
||||||
|
@ -134,6 +155,7 @@ babel_iface_item:
|
||||||
| LIMIT expr { BABEL_IFACE->limit = $2; if (($2<1) || ($2>16)) cf_error("Limit must be in range 1-16"); }
|
| LIMIT expr { BABEL_IFACE->limit = $2; if (($2<1) || ($2>16)) cf_error("Limit must be in range 1-16"); }
|
||||||
| TYPE WIRED { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRED; }
|
| TYPE WIRED { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRED; }
|
||||||
| TYPE WIRELESS { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRELESS; }
|
| TYPE WIRELESS { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRELESS; }
|
||||||
|
| TYPE TUNNEL { BABEL_IFACE->type = BABEL_IFACE_TYPE_TUNNEL; }
|
||||||
| HELLO INTERVAL expr_us { BABEL_IFACE->hello_interval = $3; if (($3<BABEL_MIN_INTERVAL) || ($3>BABEL_MAX_INTERVAL)) cf_error("Hello interval must be in range 10 ms - 655 s"); }
|
| HELLO INTERVAL expr_us { BABEL_IFACE->hello_interval = $3; if (($3<BABEL_MIN_INTERVAL) || ($3>BABEL_MAX_INTERVAL)) cf_error("Hello interval must be in range 10 ms - 655 s"); }
|
||||||
| UPDATE INTERVAL expr_us { BABEL_IFACE->update_interval = $3; if (($3<BABEL_MIN_INTERVAL) || ($3>BABEL_MAX_INTERVAL)) cf_error("Update interval must be in range 10 ms - 655 s"); }
|
| UPDATE INTERVAL expr_us { BABEL_IFACE->update_interval = $3; if (($3<BABEL_MIN_INTERVAL) || ($3>BABEL_MAX_INTERVAL)) cf_error("Update interval must be in range 10 ms - 655 s"); }
|
||||||
| RX BUFFER expr { BABEL_IFACE->rx_buffer = $3; if (($3<256) || ($3>65535)) cf_error("RX buffer must be in range 256-65535"); }
|
| RX BUFFER expr { BABEL_IFACE->rx_buffer = $3; if (($3<256) || ($3>65535)) cf_error("RX buffer must be in range 256-65535"); }
|
||||||
|
@ -146,6 +168,11 @@ babel_iface_item:
|
||||||
| AUTHENTICATION NONE { BABEL_IFACE->auth_type = BABEL_AUTH_NONE; }
|
| AUTHENTICATION NONE { BABEL_IFACE->auth_type = BABEL_AUTH_NONE; }
|
||||||
| AUTHENTICATION MAC { BABEL_IFACE->auth_type = BABEL_AUTH_MAC; BABEL_IFACE->auth_permissive = 0; }
|
| AUTHENTICATION MAC { BABEL_IFACE->auth_type = BABEL_AUTH_MAC; BABEL_IFACE->auth_permissive = 0; }
|
||||||
| AUTHENTICATION MAC PERMISSIVE { BABEL_IFACE->auth_type = BABEL_AUTH_MAC; BABEL_IFACE->auth_permissive = 1; }
|
| AUTHENTICATION MAC PERMISSIVE { BABEL_IFACE->auth_type = BABEL_AUTH_MAC; BABEL_IFACE->auth_permissive = 1; }
|
||||||
|
| RTT MIN expr_us { BABEL_IFACE->rtt_min = $3; }
|
||||||
|
| RTT MAX expr_us { BABEL_IFACE->rtt_max = $3; }
|
||||||
|
| RTT COST expr { BABEL_IFACE->rtt_cost = $3; if ($3 >= BABEL_INFINITY) cf_error("RTT cost must be < 65535"); }
|
||||||
|
| RTT 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
|
| password_list
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,13 @@ struct babel_tlv_ihu {
|
||||||
u8 addr[0];
|
u8 addr[0];
|
||||||
} PACKED;
|
} PACKED;
|
||||||
|
|
||||||
|
struct babel_subtlv_timestamp {
|
||||||
|
u8 type;
|
||||||
|
u8 length;
|
||||||
|
u32 tstamp;
|
||||||
|
u32 tstamp_rcvd; /* only used in IHU */
|
||||||
|
} PACKED;
|
||||||
|
|
||||||
struct babel_tlv_router_id {
|
struct babel_tlv_router_id {
|
||||||
u8 type;
|
u8 type;
|
||||||
u8 length;
|
u8 length;
|
||||||
|
@ -161,6 +168,7 @@ struct babel_parse_state {
|
||||||
const struct babel_tlv_data* (*get_subtlv_data)(u8 type);
|
const struct babel_tlv_data* (*get_subtlv_data)(u8 type);
|
||||||
struct babel_proto *proto;
|
struct babel_proto *proto;
|
||||||
struct babel_iface *ifa;
|
struct babel_iface *ifa;
|
||||||
|
btime received_time;
|
||||||
ip_addr saddr;
|
ip_addr saddr;
|
||||||
ip_addr next_hop_ip4;
|
ip_addr next_hop_ip4;
|
||||||
ip_addr next_hop_ip6;
|
ip_addr next_hop_ip6;
|
||||||
|
@ -170,6 +178,7 @@ struct babel_parse_state {
|
||||||
u8 router_id_seen; /* router_id field is valid */
|
u8 router_id_seen; /* router_id field is valid */
|
||||||
u8 def_ip6_prefix_seen; /* def_ip6_prefix is valid */
|
u8 def_ip6_prefix_seen; /* def_ip6_prefix is valid */
|
||||||
u8 def_ip4_prefix_seen; /* def_ip4_prefix is valid */
|
u8 def_ip4_prefix_seen; /* def_ip4_prefix is valid */
|
||||||
|
u8 hello_tstamp_seen; /* pkt contains a hello timestamp */
|
||||||
u8 current_tlv_endpos; /* End of self-terminating TLVs (offset from start) */
|
u8 current_tlv_endpos; /* End of self-terminating TLVs (offset from start) */
|
||||||
u8 sadr_enabled;
|
u8 sadr_enabled;
|
||||||
u8 is_unicast;
|
u8 is_unicast;
|
||||||
|
@ -334,6 +343,7 @@ static int babel_read_update(struct babel_tlv *hdr, union babel_msg *msg, struct
|
||||||
static int babel_read_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
|
static int babel_read_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
|
||||||
static int babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
|
static int babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
|
||||||
static int babel_read_source_prefix(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
|
static int babel_read_source_prefix(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
|
||||||
|
static int babel_read_timestamp(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state);
|
||||||
|
|
||||||
static uint babel_write_ack(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
|
static uint babel_write_ack(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
|
||||||
static uint babel_write_hello(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
|
static uint babel_write_hello(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
|
||||||
|
@ -342,6 +352,7 @@ static uint babel_write_update(struct babel_tlv *hdr, union babel_msg *msg, stru
|
||||||
static uint babel_write_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
|
static uint babel_write_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
|
||||||
static uint babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
|
static uint babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len);
|
||||||
static int babel_write_source_prefix(struct babel_tlv *hdr, net_addr *net, uint max_len);
|
static int babel_write_source_prefix(struct babel_tlv *hdr, net_addr *net, uint max_len);
|
||||||
|
static int babel_write_timestamp(struct babel_tlv *hdr, u32 tstamp, u32 tstamp_rcvd, uint max_len);
|
||||||
|
|
||||||
static const struct babel_tlv_data tlv_data[BABEL_TLV_MAX] = {
|
static const struct babel_tlv_data tlv_data[BABEL_TLV_MAX] = {
|
||||||
[BABEL_TLV_ACK_REQ] = {
|
[BABEL_TLV_ACK_REQ] = {
|
||||||
|
@ -417,6 +428,13 @@ static const struct babel_tlv_data *get_packet_tlv_data(u8 type)
|
||||||
return type < sizeof(tlv_data) / sizeof(*tlv_data) ? &tlv_data[type] : NULL;
|
return type < sizeof(tlv_data) / sizeof(*tlv_data) ? &tlv_data[type] : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct babel_tlv_data timestamp_tlv_data = {
|
||||||
|
sizeof(struct babel_subtlv_timestamp),
|
||||||
|
babel_read_timestamp,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
static const struct babel_tlv_data source_prefix_tlv_data = {
|
static const struct babel_tlv_data source_prefix_tlv_data = {
|
||||||
sizeof(struct babel_subtlv_source_prefix),
|
sizeof(struct babel_subtlv_source_prefix),
|
||||||
babel_read_source_prefix,
|
babel_read_source_prefix,
|
||||||
|
@ -428,6 +446,8 @@ static const struct babel_tlv_data *get_packet_subtlv_data(u8 type)
|
||||||
{
|
{
|
||||||
switch (type)
|
switch (type)
|
||||||
{
|
{
|
||||||
|
case BABEL_SUBTLV_TIMESTAMP:
|
||||||
|
return ×tamp_tlv_data;
|
||||||
case BABEL_SUBTLV_SOURCE_PREFIX:
|
case BABEL_SUBTLV_SOURCE_PREFIX:
|
||||||
return &source_prefix_tlv_data;
|
return &source_prefix_tlv_data;
|
||||||
|
|
||||||
|
@ -489,16 +509,34 @@ babel_read_hello(struct babel_tlv *hdr, union babel_msg *m,
|
||||||
|
|
||||||
static uint
|
static uint
|
||||||
babel_write_hello(struct babel_tlv *hdr, union babel_msg *m,
|
babel_write_hello(struct babel_tlv *hdr, union babel_msg *m,
|
||||||
struct babel_write_state *state UNUSED, uint max_len UNUSED)
|
struct babel_write_state *state UNUSED, uint max_len)
|
||||||
{
|
{
|
||||||
struct babel_tlv_hello *tlv = (void *) hdr;
|
struct babel_tlv_hello *tlv = (void *) hdr;
|
||||||
struct babel_msg_hello *msg = &m->hello;
|
struct babel_msg_hello *msg = &m->hello;
|
||||||
|
uint len = sizeof(struct babel_tlv_hello);
|
||||||
|
|
||||||
TLV_HDR0(tlv, BABEL_TLV_HELLO);
|
TLV_HDR0(tlv, BABEL_TLV_HELLO);
|
||||||
put_u16(&tlv->seqno, msg->seqno);
|
put_u16(&tlv->seqno, msg->seqno);
|
||||||
put_time16(&tlv->interval, msg->interval);
|
put_time16(&tlv->interval, msg->interval);
|
||||||
|
|
||||||
return sizeof(struct babel_tlv_hello);
|
if (msg->tstamp)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* There can be a substantial delay between when the babel_msg was created
|
||||||
|
* and when it is serialised. We don't want this included in the RTT
|
||||||
|
* measurement, so replace the timestamp with the current time to get as
|
||||||
|
* close as possible to on-wire time for the packet.
|
||||||
|
*/
|
||||||
|
u32 tstamp = current_time_now() TO_US;
|
||||||
|
|
||||||
|
int l = babel_write_timestamp(hdr, tstamp, 0, max_len);
|
||||||
|
if (l < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
len += l;
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -556,6 +594,7 @@ babel_write_ihu(struct babel_tlv *hdr, union babel_msg *m,
|
||||||
{
|
{
|
||||||
struct babel_tlv_ihu *tlv = (void *) hdr;
|
struct babel_tlv_ihu *tlv = (void *) hdr;
|
||||||
struct babel_msg_ihu *msg = &m->ihu;
|
struct babel_msg_ihu *msg = &m->ihu;
|
||||||
|
uint len = sizeof(*tlv);
|
||||||
|
|
||||||
if (ipa_is_link_local(msg->addr) && max_len < sizeof(struct babel_tlv_ihu) + 8)
|
if (ipa_is_link_local(msg->addr) && max_len < sizeof(struct babel_tlv_ihu) + 8)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -567,12 +606,24 @@ babel_write_ihu(struct babel_tlv *hdr, union babel_msg *m,
|
||||||
if (!ipa_is_link_local(msg->addr))
|
if (!ipa_is_link_local(msg->addr))
|
||||||
{
|
{
|
||||||
tlv->ae = BABEL_AE_WILDCARD;
|
tlv->ae = BABEL_AE_WILDCARD;
|
||||||
return sizeof(struct babel_tlv_ihu);
|
goto out;
|
||||||
}
|
}
|
||||||
put_ip6_ll(&tlv->addr, msg->addr);
|
put_ip6_ll(&tlv->addr, msg->addr);
|
||||||
tlv->ae = BABEL_AE_IP6_LL;
|
tlv->ae = BABEL_AE_IP6_LL;
|
||||||
hdr->length += 8;
|
hdr->length += 8;
|
||||||
return sizeof(struct babel_tlv_ihu) + 8;
|
len += 8;
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (msg->tstamp)
|
||||||
|
{
|
||||||
|
int l = babel_write_timestamp(hdr, msg->tstamp, msg->tstamp_rcvd, max_len);
|
||||||
|
if (l < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
len += l;
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -1200,6 +1251,66 @@ babel_write_source_prefix(struct babel_tlv *hdr, net_addr *n, uint max_len)
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
babel_read_timestamp(struct babel_tlv *hdr, union babel_msg *msg,
|
||||||
|
struct babel_parse_state *state)
|
||||||
|
{
|
||||||
|
struct babel_subtlv_timestamp *tlv = (void *) hdr;
|
||||||
|
|
||||||
|
switch (msg->type)
|
||||||
|
{
|
||||||
|
case BABEL_TLV_HELLO:
|
||||||
|
if (tlv->length < 4)
|
||||||
|
return PARSE_ERROR;
|
||||||
|
|
||||||
|
msg->hello.tstamp = get_u32(&tlv->tstamp);
|
||||||
|
msg->hello.pkt_received = state->received_time;
|
||||||
|
state->hello_tstamp_seen = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BABEL_TLV_IHU:
|
||||||
|
if (tlv->length < 8)
|
||||||
|
return PARSE_ERROR;
|
||||||
|
|
||||||
|
/* RTT calculation relies on a Hello always being present with an IHU */
|
||||||
|
if (!state->hello_tstamp_seen)
|
||||||
|
break;
|
||||||
|
|
||||||
|
msg->ihu.tstamp = get_u32(&tlv->tstamp);
|
||||||
|
msg->ihu.tstamp_rcvd = get_u32(&tlv->tstamp_rcvd);
|
||||||
|
msg->ihu.pkt_received = state->received_time;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return PARSE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PARSE_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
babel_write_timestamp(struct babel_tlv *hdr, u32 tstamp, u32 tstamp_rcvd, uint max_len)
|
||||||
|
{
|
||||||
|
struct babel_subtlv_timestamp *tlv = (void *) NEXT_TLV(hdr);
|
||||||
|
uint len = sizeof(*tlv);
|
||||||
|
|
||||||
|
if (hdr->type == BABEL_TLV_HELLO)
|
||||||
|
len -= 4;
|
||||||
|
|
||||||
|
if (len > max_len)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
TLV_HDR(tlv, BABEL_SUBTLV_TIMESTAMP, len);
|
||||||
|
hdr->length += len;
|
||||||
|
|
||||||
|
put_u32(&tlv->tstamp, tstamp);
|
||||||
|
|
||||||
|
if (hdr->type == BABEL_TLV_IHU)
|
||||||
|
put_u32(&tlv->tstamp_rcvd, tstamp_rcvd);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
babel_read_subtlvs(struct babel_tlv *hdr,
|
babel_read_subtlvs(struct babel_tlv *hdr,
|
||||||
union babel_msg *msg,
|
union babel_msg *msg,
|
||||||
|
@ -1318,7 +1429,6 @@ babel_send_to(struct babel_iface *ifa, ip_addr dest)
|
||||||
static uint
|
static uint
|
||||||
babel_write_queue(struct babel_iface *ifa, list *queue)
|
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 };
|
struct babel_write_state state = { .next_hop_ip6 = ifa->addr };
|
||||||
|
|
||||||
if (EMPTY_LIST(*queue))
|
if (EMPTY_LIST(*queue))
|
||||||
|
@ -1346,7 +1456,7 @@ babel_write_queue(struct babel_iface *ifa, list *queue)
|
||||||
|
|
||||||
pos += len;
|
pos += len;
|
||||||
rem_node(NODE msg);
|
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);
|
pos += babel_auth_add_tlvs(ifa, (struct babel_tlv *) pos, end - pos);
|
||||||
|
@ -1470,6 +1580,13 @@ babel_process_packet(struct babel_iface *ifa,
|
||||||
.saddr = saddr,
|
.saddr = saddr,
|
||||||
.next_hop_ip6 = saddr,
|
.next_hop_ip6 = saddr,
|
||||||
.sadr_enabled = babel_sadr_enabled(p),
|
.sadr_enabled = babel_sadr_enabled(p),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The core updates current_time() after returning from poll(), so this is
|
||||||
|
* actually the time the packet was received, even though there may have
|
||||||
|
* been a bit of delay before we got to process it
|
||||||
|
*/
|
||||||
|
.received_time = current_time(),
|
||||||
};
|
};
|
||||||
|
|
||||||
if ((pkt->magic != BABEL_MAGIC) || (pkt->version != BABEL_VERSION))
|
if ((pkt->magic != BABEL_MAGIC) || (pkt->version != BABEL_VERSION))
|
||||||
|
@ -1507,13 +1624,13 @@ babel_process_packet(struct babel_iface *ifa,
|
||||||
else if (res == PARSE_IGNORE)
|
else if (res == PARSE_IGNORE)
|
||||||
{
|
{
|
||||||
DBG("Babel: Ignoring TLV of type %d\n", tlv->type);
|
DBG("Babel: Ignoring TLV of type %d\n", tlv->type);
|
||||||
sl_free(p->msg_slab, msg);
|
sl_free(msg);
|
||||||
}
|
}
|
||||||
else /* PARSE_ERROR */
|
else /* PARSE_ERROR */
|
||||||
{
|
{
|
||||||
LOG_PKT("Bad TLV from %I via %s type %d pos %d - 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));
|
saddr, ifa->iface->name, tlv->type, (int) ((byte *)tlv - (byte *)pkt));
|
||||||
sl_free(p->msg_slab, msg);
|
sl_free(msg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1525,7 +1642,7 @@ babel_process_packet(struct babel_iface *ifa,
|
||||||
if (tlv_data[msg->msg.type].handle_tlv)
|
if (tlv_data[msg->msg.type].handle_tlv)
|
||||||
tlv_data[msg->msg.type].handle_tlv(&msg->msg, ifa);
|
tlv_data[msg->msg.type].handle_tlv(&msg->msg, ifa);
|
||||||
rem_node(NODE msg);
|
rem_node(NODE msg);
|
||||||
sl_free(p->msg_slab, msg);
|
sl_free(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,4 +3,4 @@ obj := $(src-o-files)
|
||||||
$(all-daemon)
|
$(all-daemon)
|
||||||
$(cf-local)
|
$(cf-local)
|
||||||
|
|
||||||
tests_objs := $(tests_objs) $(src-o-files)
|
tests_objs := $(tests_objs) $(src-o-files)
|
||||||
|
|
|
@ -113,8 +113,8 @@
|
||||||
#define HASH_IP_EQ(a1,n1,a2,n2) ipa_equal(a1, a2) && n1 == n2
|
#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)
|
#define HASH_IP_FN(a,n) ipa_hash(a) ^ u32_hash(n)
|
||||||
|
|
||||||
static list bfd_proto_list;
|
static list STATIC_LIST_INIT(bfd_proto_list);
|
||||||
static list bfd_wait_list;
|
static list STATIC_LIST_INIT(bfd_wait_list);
|
||||||
|
|
||||||
const char *bfd_state_names[] = { "AdminDown", "Down", "Init", "Up" };
|
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_id, HASH_ID, s);
|
||||||
HASH_REMOVE(p->session_hash_ip, HASH_IP, 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);
|
TRACE(D_EVENTS, "Session to %I removed", ip);
|
||||||
|
|
||||||
|
@ -1007,13 +1007,6 @@ bfd_notify_init(struct bfd_proto *p)
|
||||||
* BFD protocol glue
|
* BFD protocol glue
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void
|
|
||||||
bfd_init_all(void)
|
|
||||||
{
|
|
||||||
init_list(&bfd_proto_list);
|
|
||||||
init_list(&bfd_wait_list);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct proto *
|
static struct proto *
|
||||||
bfd_init(struct proto_config *c)
|
bfd_init(struct proto_config *c)
|
||||||
{
|
{
|
||||||
|
@ -1199,3 +1192,9 @@ struct protocol proto_bfd = {
|
||||||
.reconfigure = bfd_reconfigure,
|
.reconfigure = bfd_reconfigure,
|
||||||
.copy_config = bfd_copy_config,
|
.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);
|
birdloop_set_current(loop);
|
||||||
|
|
||||||
|
tmp_init(loop->pool);
|
||||||
|
|
||||||
pthread_mutex_lock(&loop->mutex);
|
pthread_mutex_lock(&loop->mutex);
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,4 +3,4 @@ obj := $(src-o-files)
|
||||||
$(all-daemon)
|
$(all-daemon)
|
||||||
$(cf-local)
|
$(cf-local)
|
||||||
|
|
||||||
tests_objs := $(tests_objs) $(src-o-files)
|
tests_objs := $(tests_objs) $(src-o-files)
|
||||||
|
|
|
@ -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; })
|
({ REPORT(msg, ## args); s->err_withdraw = 1; return; })
|
||||||
|
|
||||||
#define UNSET(a) \
|
#define UNSET(a) \
|
||||||
({ a->type = EAF_TYPE_UNDEF; return; })
|
({ a->undef = 1; return; })
|
||||||
|
|
||||||
#define REJECT(msg, args...) \
|
#define REJECT(msg, args...) \
|
||||||
({ log(L_ERR "%s: " msg, s->proto->p.name, ## args); s->err_reject = 1; return; })
|
({ 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;
|
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
|
* 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);
|
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
|
static void
|
||||||
bgp_export_mpls_label_stack(struct bgp_export_state *s, eattr *a)
|
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,
|
.encode = bgp_encode_u32s,
|
||||||
.decode = bgp_decode_large_community,
|
.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] = {
|
[BA_MPLS_LABEL_STACK] = {
|
||||||
.name = "mpls_label_stack",
|
.name = "mpls_label_stack",
|
||||||
.type = EAF_TYPE_INT_SET,
|
.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;
|
a->flags = (a->flags & BAF_PARTIAL) | desc->flags;
|
||||||
|
|
||||||
/* Set partial bit if new opt-trans attribute is attached to non-local route */
|
/* 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_OPTIONAL) && (a->flags & BAF_TRANSITIVE))
|
||||||
a->flags |= BAF_PARTIAL;
|
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);
|
CALL(desc->export, s, a);
|
||||||
|
|
||||||
/* Attribute might become undefined in hook */
|
/* Attribute might become undefined in hook */
|
||||||
if ((a->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF)
|
if (a->undef)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1445,6 +1471,29 @@ bgp_finish_attrs(struct bgp_parse_state *s, rta *a)
|
||||||
REPORT("Discarding AIGP attribute received on non-AIGP session");
|
REPORT("Discarding AIGP attribute received on non-AIGP session");
|
||||||
bgp_unset_attr(&a->eattrs, s->pool, BA_AIGP);
|
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_FN(a,h) h
|
||||||
|
|
||||||
#define RBH_REHASH bgp_rbh_rehash
|
#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)
|
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_FN(n,i,h) h
|
||||||
|
|
||||||
#define PXH_REHASH bgp_pxh_rehash
|
#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)
|
HASH_DEFINE_REHASH_FN(PXH, struct bgp_prefix)
|
||||||
|
@ -1626,7 +1675,8 @@ bgp_free_prefix_table(struct bgp_channel *c)
|
||||||
static struct bgp_prefix *
|
static struct bgp_prefix *
|
||||||
bgp_get_prefix(struct bgp_channel *c, net_addr *net, u32 path_id)
|
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);
|
struct bgp_prefix *px = HASH_FIND(c->prefix_hash, PXH, net, path_id, hash);
|
||||||
|
|
||||||
if (px)
|
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);
|
HASH_REMOVE2(c->prefix_hash, PXH, c->pool, px);
|
||||||
|
|
||||||
if (c->prefix_slab)
|
if (c->prefix_slab)
|
||||||
sl_free(c->prefix_slab, px);
|
sl_free(px);
|
||||||
else
|
else
|
||||||
mb_free(px);
|
mb_free(px);
|
||||||
}
|
}
|
||||||
|
@ -1668,12 +1718,16 @@ bgp_free_prefix(struct bgp_channel *c, struct bgp_prefix *px)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int
|
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->src->proto;
|
||||||
struct proto *SRC = e->attrs->src->proto;
|
struct bgp_proto *p = (struct bgp_proto *) C->proto;
|
||||||
struct bgp_proto *p = (struct bgp_proto *) P;
|
|
||||||
struct bgp_proto *src = (SRC->proto == &proto_bgp) ? (struct bgp_proto *) SRC : NULL;
|
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 */
|
/* Reject our routes */
|
||||||
if (src == p)
|
if (src == p)
|
||||||
|
@ -1701,11 +1755,11 @@ bgp_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle well-known communities, RFC 1997 */
|
/* Handle well-known communities, RFC 1997 */
|
||||||
struct eattr *c;
|
struct eattr *a;
|
||||||
if (p->cf->interpret_communities &&
|
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 */
|
/* Do not export anywhere */
|
||||||
if (int_set_contains(d, BGP_COMM_NO_ADVERTISE))
|
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;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ea_list *
|
static ea_list *
|
||||||
bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *attrs0, struct linpool *pool)
|
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_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 };
|
struct bgp_export_state s = { .proto = p, .channel = c, .pool = pool, .src = src, .route = e, .mpls = c->desc->mpls };
|
||||||
ea_list *attrs = attrs0;
|
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 */
|
/* MULTI_EXIT_DESC attribute - accept only if set in export filter */
|
||||||
a = bgp_find_attr(attrs0, BA_MULTI_EXIT_DISC);
|
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);
|
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
|
* Presence of mandatory attributes ORIGIN and AS_PATH is ensured by above
|
||||||
* conditions. Presence and validity of quasi-mandatory NEXT_HOP attribute
|
* 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;
|
struct bgp_prefix *px;
|
||||||
u32 path;
|
u32 path;
|
||||||
|
|
||||||
|
/* Ignore non-BGP channels */
|
||||||
|
if (C->channel != &channel_bgp)
|
||||||
|
return;
|
||||||
|
|
||||||
if (new)
|
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 */
|
/* Error during attribute processing */
|
||||||
if (!attrs)
|
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 */
|
/* If attributes are invalid, we fail back to withdraw */
|
||||||
buck = attrs ? bgp_get_bucket(c, attrs) : bgp_get_withdraw_bucket(c);
|
buck = attrs ? bgp_get_bucket(c, attrs) : bgp_get_withdraw_bucket(c);
|
||||||
path = new->attrs->src->global_id;
|
path = new->src->global_id;
|
||||||
|
|
||||||
lp_flush(bgp_linpool2);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
buck = bgp_get_withdraw_bucket(c);
|
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);
|
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;
|
return as;
|
||||||
|
|
||||||
/* If AS_PATH is not defined, we treat rte as locally originated */
|
/* 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;
|
return p->cf->confederation ?: p->local_as;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
rte_stale(rte *r)
|
rte_stale(rte *r)
|
||||||
{
|
{
|
||||||
if (r->u.bgp.stale < 0)
|
if (r->pflags & BGP_REF_STALE)
|
||||||
{
|
return 1;
|
||||||
/* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return r->u.bgp.stale;
|
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));
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
bgp_rte_better(rte *new, rte *old)
|
bgp_rte_better(rte *new, rte *old)
|
||||||
{
|
{
|
||||||
struct bgp_proto *new_bgp = (struct bgp_proto *) new->attrs->src->proto;
|
struct bgp_proto *new_bgp = (struct bgp_proto *) new->src->proto;
|
||||||
struct bgp_proto *old_bgp = (struct bgp_proto *) old->attrs->src->proto;
|
struct bgp_proto *old_bgp = (struct bgp_proto *) old->src->proto;
|
||||||
eattr *x, *y;
|
eattr *x, *y;
|
||||||
u32 n, o;
|
u32 n, o;
|
||||||
|
|
||||||
/* Skip suppressed routes (see bgp_rte_recalculate()) */
|
/* Skip suppressed routes (see bgp_rte_recalculate()) */
|
||||||
n = new->u.bgp.suppressed;
|
n = new->pflags & BGP_REF_SUPPRESSED;
|
||||||
o = old->u.bgp.suppressed;
|
o = old->pflags & BGP_REF_SUPPRESSED;
|
||||||
if (n > o)
|
if (n > o)
|
||||||
return 0;
|
return 0;
|
||||||
if (n < o)
|
if (n < o)
|
||||||
|
@ -2054,13 +2140,13 @@ bgp_rte_better(rte *new, rte *old)
|
||||||
int
|
int
|
||||||
bgp_rte_mergable(rte *pri, rte *sec)
|
bgp_rte_mergable(rte *pri, rte *sec)
|
||||||
{
|
{
|
||||||
struct bgp_proto *pri_bgp = (struct bgp_proto *) pri->attrs->src->proto;
|
struct bgp_proto *pri_bgp = (struct bgp_proto *) pri->src->proto;
|
||||||
struct bgp_proto *sec_bgp = (struct bgp_proto *) sec->attrs->src->proto;
|
struct bgp_proto *sec_bgp = (struct bgp_proto *) sec->src->proto;
|
||||||
eattr *x, *y;
|
eattr *x, *y;
|
||||||
u32 p, s;
|
u32 p, s;
|
||||||
|
|
||||||
/* Skip suppressed routes (see bgp_rte_recalculate()) */
|
/* 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;
|
return 0;
|
||||||
|
|
||||||
/* RFC 4271 9.1.2.1. Route resolvability test */
|
/* RFC 4271 9.1.2.1. Route resolvability test */
|
||||||
|
@ -2133,13 +2219,13 @@ bgp_rte_mergable(rte *pri, rte *sec)
|
||||||
static inline int
|
static inline int
|
||||||
same_group(rte *r, u32 lpref, u32 lasn)
|
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
|
static inline int
|
||||||
use_deterministic_med(rte *r)
|
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;
|
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 *r, *s;
|
||||||
rte *key = new ? new : old;
|
rte *key = new ? new : old;
|
||||||
u32 lpref = key->pref;
|
u32 lpref = key->attrs->pref;
|
||||||
u32 lasn = bgp_get_neighbor(key);
|
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
|
* 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)
|
if (new)
|
||||||
new->u.bgp.suppressed = 1;
|
new->pflags |= BGP_REF_SUPPRESSED;
|
||||||
|
|
||||||
if (old)
|
if (old)
|
||||||
{
|
{
|
||||||
old->u.bgp.suppressed = 1;
|
old->pflags |= BGP_REF_SUPPRESSED;
|
||||||
|
|
||||||
/* The fast case - replace not best with worse (or remove not best) */
|
/* The fast case - replace not best with worse (or remove not best) */
|
||||||
if (old_suppressed && !(new && bgp_rte_better(new, old)))
|
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)
|
for (s=net->routes; rte_is_valid(s); s=s->next)
|
||||||
if (use_deterministic_med(s) && same_group(s, lpref, lasn))
|
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))
|
if (!r || bgp_rte_better(s, r))
|
||||||
r = s;
|
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 */
|
/* Found if new is mergable with best-in-group */
|
||||||
if (new && (new != r) && bgp_rte_mergable(r, new))
|
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 */
|
/* Found all existing routes mergable with best-in-group */
|
||||||
for (s=net->routes; rte_is_valid(s); s=s->next)
|
for (s=net->routes; rte_is_valid(s); s=s->next)
|
||||||
if (use_deterministic_med(s) && same_group(s, lpref, lasn))
|
if (use_deterministic_med(s) && same_group(s, lpref, lasn))
|
||||||
if ((s != r) && bgp_rte_mergable(r, s))
|
if ((s != r) && bgp_rte_mergable(r, s))
|
||||||
s->u.bgp.suppressed = 0;
|
s->pflags &= ~BGP_REF_SUPPRESSED;
|
||||||
|
|
||||||
/* Found best-in-group */
|
/* Found best-in-group */
|
||||||
r->u.bgp.suppressed = 0;
|
r->pflags &= ~BGP_REF_SUPPRESSED;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There are generally two reasons why we have to force
|
* 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);
|
r = rte_cow_rta(r, pool);
|
||||||
bgp_set_attr_ptr(&(r->attrs->eattrs), pool, BA_COMMUNITY, flags,
|
bgp_set_attr_ptr(&(r->attrs->eattrs), pool, BA_COMMUNITY, flags,
|
||||||
int_set_add(pool, ad, BGP_COMM_LLGR_STALE));
|
int_set_add(pool, ad, BGP_COMM_LLGR_STALE));
|
||||||
r->u.bgp.stale = 1;
|
r->pflags |= BGP_REF_STALE;
|
||||||
|
|
||||||
return r;
|
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));
|
eattr *o = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_ORIGIN));
|
||||||
u32 origas;
|
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, "-");
|
buf += bsprintf(buf, "-");
|
||||||
|
|
||||||
if (rte_stale(e))
|
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 8212 - Default EBGP Route Propagation Behavior without Policies
|
||||||
* RFC 8654 - Extended Message Support for BGP
|
* RFC 8654 - Extended Message Support for BGP
|
||||||
* RFC 9117 - Revised Validation Procedure for BGP Flow Specifications
|
* 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-ietf-idr-ext-opt-param-07
|
||||||
* draft-uttaro-idr-bgp-persistence-04
|
* draft-uttaro-idr-bgp-persistence-04
|
||||||
* draft-walton-bgp-hostname-capability-02
|
* draft-walton-bgp-hostname-capability-02
|
||||||
|
@ -126,9 +127,7 @@
|
||||||
#include "bgp.h"
|
#include "bgp.h"
|
||||||
|
|
||||||
|
|
||||||
struct linpool *bgp_linpool; /* Global temporary pool */
|
static list STATIC_LIST_INIT(bgp_sockets); /* Global list of listening sockets */
|
||||||
struct linpool *bgp_linpool2; /* Global temporary pool for bgp_rt_notify() */
|
|
||||||
static list bgp_sockets; /* Global list of listening sockets */
|
|
||||||
|
|
||||||
|
|
||||||
static void bgp_connect(struct bgp_proto *p);
|
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 flags = p->cf->free_bind ? SKF_FREEBIND : 0;
|
||||||
uint flag_mask = SKF_FREEBIND;
|
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 */
|
/* We assume that cf->iface is defined iff cf->local_ip is link-local */
|
||||||
|
|
||||||
WALK_LIST(bs, bgp_sockets)
|
WALK_LIST(bs, bgp_sockets)
|
||||||
|
@ -204,12 +199,6 @@ bgp_open(struct bgp_proto *p)
|
||||||
|
|
||||||
add_tail(&bgp_sockets, &bs->n);
|
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;
|
return 0;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
@ -238,15 +227,6 @@ bgp_close(struct bgp_proto *p)
|
||||||
rfree(bs->sk);
|
rfree(bs->sk);
|
||||||
rem_node(&bs->n);
|
rem_node(&bs->n);
|
||||||
mb_free(bs);
|
mb_free(bs);
|
||||||
|
|
||||||
if (!EMPTY_LIST(bgp_sockets))
|
|
||||||
return;
|
|
||||||
|
|
||||||
rfree(bgp_linpool);
|
|
||||||
bgp_linpool = NULL;
|
|
||||||
|
|
||||||
rfree(bgp_linpool2);
|
|
||||||
bgp_linpool2 = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
|
@ -280,7 +260,7 @@ static inline struct bgp_channel *
|
||||||
bgp_find_channel(struct bgp_proto *p, u32 afi)
|
bgp_find_channel(struct bgp_proto *p, u32 afi)
|
||||||
{
|
{
|
||||||
struct bgp_channel *c;
|
struct bgp_channel *c;
|
||||||
WALK_LIST(c, p->p.channels)
|
BGP_WALK_CHANNELS(p, c)
|
||||||
if (c->afi == afi)
|
if (c->afi == afi)
|
||||||
return c;
|
return c;
|
||||||
|
|
||||||
|
@ -606,7 +586,7 @@ bgp_conn_enter_established_state(struct bgp_conn *conn)
|
||||||
/* Summary state of ADD_PATH RX for active channels */
|
/* Summary state of ADD_PATH RX for active channels */
|
||||||
uint summary_add_path_rx = 0;
|
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 *loc = bgp_find_af_caps(local, c->afi);
|
||||||
const struct bgp_af_caps *rem = bgp_find_af_caps(peer, 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->channel_count = num;
|
||||||
p->summary_add_path_rx = summary_add_path_rx;
|
p->summary_add_path_rx = summary_add_path_rx;
|
||||||
|
|
||||||
WALK_LIST(c, p->p.channels)
|
BGP_WALK_CHANNELS(p, c)
|
||||||
{
|
{
|
||||||
if (c->c.disabled)
|
if (c->c.disabled)
|
||||||
continue;
|
continue;
|
||||||
|
@ -767,7 +747,7 @@ bgp_handle_graceful_restart(struct bgp_proto *p)
|
||||||
p->gr_active_num = 0;
|
p->gr_active_num = 0;
|
||||||
|
|
||||||
struct bgp_channel *c;
|
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? */
|
/* FIXME: perhaps check for channel state instead of disabled flag? */
|
||||||
if (c->c.disabled)
|
if (c->c.disabled)
|
||||||
|
@ -862,7 +842,7 @@ bgp_graceful_restart_timeout(timer *t)
|
||||||
if (p->llgr_ready)
|
if (p->llgr_ready)
|
||||||
{
|
{
|
||||||
struct bgp_channel *c;
|
struct bgp_channel *c;
|
||||||
WALK_LIST(c, p->p.channels)
|
BGP_WALK_CHANNELS(p, c)
|
||||||
{
|
{
|
||||||
/* Channel is not in GR and is already flushed */
|
/* Channel is not in GR and is already flushed */
|
||||||
if (!c->gr_active)
|
if (!c->gr_active)
|
||||||
|
@ -1414,6 +1394,10 @@ bgp_reload_routes(struct channel *C)
|
||||||
struct bgp_proto *p = (void *) C->proto;
|
struct bgp_proto *p = (void *) C->proto;
|
||||||
struct bgp_channel *c = (void *) C;
|
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));
|
ASSERT(p->conn && (p->route_refresh || c->c.in_table));
|
||||||
|
|
||||||
if (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_proto *p = (void *) C->proto;
|
||||||
struct bgp_channel *c = (void *) C;
|
struct bgp_channel *c = (void *) C;
|
||||||
|
|
||||||
|
/* Ignore non-BGP channels */
|
||||||
|
if (C->channel != &channel_bgp)
|
||||||
|
return;
|
||||||
|
|
||||||
/* This should not happen */
|
/* This should not happen */
|
||||||
if (!p->conn)
|
if (!p->conn)
|
||||||
return;
|
return;
|
||||||
|
@ -1453,6 +1441,10 @@ bgp_feed_end(struct channel *C)
|
||||||
struct bgp_proto *p = (void *) C->proto;
|
struct bgp_proto *p = (void *) C->proto;
|
||||||
struct bgp_channel *c = (void *) C;
|
struct bgp_channel *c = (void *) C;
|
||||||
|
|
||||||
|
/* Ignore non-BGP channels */
|
||||||
|
if (C->channel != &channel_bgp)
|
||||||
|
return;
|
||||||
|
|
||||||
/* This should not happen */
|
/* This should not happen */
|
||||||
if (!p->conn)
|
if (!p->conn)
|
||||||
return;
|
return;
|
||||||
|
@ -1567,7 +1559,7 @@ bgp_start(struct proto *P)
|
||||||
if (p->p.gr_recovery && p->cf->gr_mode)
|
if (p->p.gr_recovery && p->cf->gr_mode)
|
||||||
{
|
{
|
||||||
struct bgp_channel *c;
|
struct bgp_channel *c;
|
||||||
WALK_LIST(c, p->p.channels)
|
BGP_WALK_CHANNELS(p, c)
|
||||||
channel_graceful_restart_lock(&c->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_mergable = bgp_rte_mergable;
|
||||||
P->rte_recalculate = cf->deterministic_med ? bgp_rte_recalculate : NULL;
|
P->rte_recalculate = cf->deterministic_med ? bgp_rte_recalculate : NULL;
|
||||||
P->rte_modify = bgp_rte_modify_stale;
|
P->rte_modify = bgp_rte_modify_stale;
|
||||||
|
P->rte_igp_metric = bgp_rte_igp_metric;
|
||||||
|
|
||||||
p->cf = cf;
|
p->cf = cf;
|
||||||
p->is_internal = (cf->local_as == cf->remote_as);
|
p->is_internal = (cf->local_as == cf->remote_as);
|
||||||
|
@ -1720,7 +1713,7 @@ bgp_init(struct proto_config *CF)
|
||||||
|
|
||||||
/* Add all channels */
|
/* Add all channels */
|
||||||
struct bgp_channel_config *cc;
|
struct bgp_channel_config *cc;
|
||||||
WALK_LIST(cc, CF->channels)
|
BGP_CF_WALK_CHANNELS(cf, cc)
|
||||||
proto_add_channel(P, &cc->c);
|
proto_add_channel(P, &cc->c);
|
||||||
|
|
||||||
return P;
|
return P;
|
||||||
|
@ -1866,7 +1859,7 @@ bgp_find_channel_config(struct bgp_config *cf, u32 afi)
|
||||||
{
|
{
|
||||||
struct bgp_channel_config *cc;
|
struct bgp_channel_config *cc;
|
||||||
|
|
||||||
WALK_LIST(cc, cf->c.channels)
|
BGP_CF_WALK_CHANNELS(cf, cc)
|
||||||
if (cc->afi == afi)
|
if (cc->afi == afi)
|
||||||
return cc;
|
return cc;
|
||||||
|
|
||||||
|
@ -1983,6 +1976,15 @@ bgp_postconfig(struct proto_config *CF)
|
||||||
if (internal && cf->rs_client)
|
if (internal && cf->rs_client)
|
||||||
cf_error("Only external neighbor can be 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)
|
if (!cf->confederation && cf->confederation_member)
|
||||||
cf_error("Confederation ID must be set for member sessions");
|
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)
|
if (internal && cf->enforce_first_as)
|
||||||
cf_error("Enforce first AS check is requires EBGP sessions");
|
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;
|
struct bgp_channel_config *cc;
|
||||||
WALK_LIST(cc, CF->channels)
|
BGP_CF_WALK_CHANNELS(cf, cc)
|
||||||
{
|
{
|
||||||
/* Handle undefined import filter */
|
/* Handle undefined import filter */
|
||||||
if (cc->c.in_filter == FILTER_UNDEF)
|
if (cc->c.in_filter == FILTER_UNDEF)
|
||||||
|
@ -2035,6 +2052,10 @@ bgp_postconfig(struct proto_config *CF)
|
||||||
if (!cc->gw_mode)
|
if (!cc->gw_mode)
|
||||||
cc->gw_mode = cf->multihop ? GW_RECURSIVE : GW_DIRECT;
|
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 */
|
/* Defaults based on proto config */
|
||||||
if (cc->gr_able == 0xff)
|
if (cc->gr_able == 0xff)
|
||||||
cc->gr_able = (cf->gr_mode == BGP_GR_ABLE);
|
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)
|
WALK_LIST(C, p->p.channels)
|
||||||
C->stale = 1;
|
C->stale = 1;
|
||||||
|
|
||||||
WALK_LIST(cc, new->c.channels)
|
BGP_CF_WALK_CHANNELS(new, cc)
|
||||||
{
|
{
|
||||||
C = (struct channel *) bgp_find_channel(p, cc->afi);
|
C = (struct channel *) bgp_find_channel(p, cc->afi);
|
||||||
same = proto_configure_channel(P, &C, &cc->c) && same;
|
same = proto_configure_channel(P, &C, &cc->c) && same;
|
||||||
|
|
||||||
if (C)
|
|
||||||
C->stale = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WALK_LIST_DELSAFE(C, C2, p->p.channels)
|
WALK_LIST_DELSAFE(C, C2, p->p.channels)
|
||||||
if (C->stale)
|
if (C->stale)
|
||||||
same = proto_configure_channel(P, &C, NULL) && same;
|
same = proto_configure_channel(P, &C, NULL) && same;
|
||||||
|
|
||||||
|
|
||||||
if (same && (p->start_state > BSS_PREPARE))
|
if (same && (p->start_state > BSS_PREPARE))
|
||||||
bgp_update_bfd(p, new->bfd);
|
bgp_update_bfd(p, new->bfd);
|
||||||
|
|
||||||
|
@ -2169,6 +2186,7 @@ bgp_channel_reconfigure(struct channel *C, struct channel_config *CC, int *impor
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if ((new->gw_mode != old->gw_mode) ||
|
if ((new->gw_mode != old->gw_mode) ||
|
||||||
|
(new->next_hop_prefer != old->next_hop_prefer) ||
|
||||||
(new->aigp != old->aigp) ||
|
(new->aigp != old->aigp) ||
|
||||||
(new->cost != old->cost))
|
(new->cost != old->cost))
|
||||||
{
|
{
|
||||||
|
@ -2345,6 +2363,15 @@ bgp_show_afis(int code, char *s, u32 *afis, uint count)
|
||||||
cli_msg(code, b.start);
|
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
|
static void
|
||||||
bgp_show_capabilities(struct bgp_proto *p UNUSED, struct bgp_caps *caps)
|
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)
|
if (caps->hostname)
|
||||||
cli_msg(-1006, " Hostname: %s", 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
|
static void
|
||||||
|
@ -2487,6 +2517,9 @@ bgp_show_proto_info(struct proto *P)
|
||||||
else
|
else
|
||||||
cli_msg(-1006, " Neighbor address: %I%J", p->remote_ip, p->cf->iface);
|
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, " Neighbor AS: %u", p->remote_as);
|
||||||
cli_msg(-1006, " Local AS: %u", p->cf->local_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);
|
channel_show_info(&c->c);
|
||||||
|
|
||||||
|
if (c->c.channel != &channel_bgp)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (p->gr_active_num)
|
if (p->gr_active_num)
|
||||||
cli_msg(-1006, " Neighbor GR: %s", bgp_gr_states[c->gr_active]);
|
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,
|
.get_route_info = bgp_get_route_info,
|
||||||
.show_proto_info = bgp_show_proto_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/hash.h"
|
||||||
#include "lib/socket.h"
|
#include "lib/socket.h"
|
||||||
|
|
||||||
struct linpool;
|
|
||||||
struct eattr;
|
struct eattr;
|
||||||
|
|
||||||
|
|
||||||
|
@ -114,13 +113,18 @@ struct bgp_config {
|
||||||
int gr_mode; /* Graceful restart mode (BGP_GR_*) */
|
int gr_mode; /* Graceful restart mode (BGP_GR_*) */
|
||||||
int llgr_mode; /* Long-lived graceful restart mode (BGP_LLGR_*) */
|
int llgr_mode; /* Long-lived graceful restart mode (BGP_LLGR_*) */
|
||||||
int setkey; /* Set MD5 password to system SA/SP database */
|
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 */
|
/* Times below are in seconds */
|
||||||
unsigned gr_time; /* Graceful restart timeout */
|
unsigned gr_time; /* Graceful restart timeout */
|
||||||
unsigned llgr_time; /* Long-lived graceful restart stale time */
|
unsigned llgr_time; /* Long-lived graceful restart stale time */
|
||||||
unsigned connect_delay_time; /* Minimum delay between connect attempts */
|
unsigned connect_delay_time; /* Minimum delay between connect attempts */
|
||||||
unsigned connect_retry_time; /* Timeout for 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 keepalive_time;
|
||||||
|
unsigned min_keepalive_time; /* Minimum accepted keepalive time */
|
||||||
unsigned error_amnesia_time; /* Errors are forgotten after */
|
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_min; /* Time to wait after an error is detected */
|
||||||
unsigned error_delay_time_max;
|
unsigned error_delay_time_max;
|
||||||
|
@ -144,6 +148,7 @@ struct bgp_channel_config {
|
||||||
ip_addr next_hop_addr; /* Local address for NEXT_HOP attribute */
|
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_self; /* Always set next hop to local IP address (NH_*) */
|
||||||
u8 next_hop_keep; /* Do not modify next hop attribute (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 mandatory; /* Channel is mandatory in capability negotiation */
|
||||||
u8 gw_mode; /* How we compute route gateway from next_hop attr, see GW_* */
|
u8 gw_mode; /* How we compute route gateway from next_hop attr, see GW_* */
|
||||||
u8 secondary; /* Accept also non-best routes (i.e. RA_ACCEPTED) */
|
u8 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_INTERNAL 1
|
||||||
#define BGP_PT_EXTERNAL 2
|
#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_NO 0
|
||||||
#define NH_ALL 1
|
#define NH_ALL 1
|
||||||
#define NH_IBGP 2
|
#define NH_IBGP 2
|
||||||
|
@ -179,6 +191,9 @@ struct bgp_channel_config {
|
||||||
#define GW_DIRECT 1
|
#define GW_DIRECT 1
|
||||||
#define GW_RECURSIVE 2
|
#define GW_RECURSIVE 2
|
||||||
|
|
||||||
|
#define NHP_GLOBAL 1
|
||||||
|
#define NHP_LOCAL 2
|
||||||
|
|
||||||
#define BGP_ADD_PATH_RX 1
|
#define BGP_ADD_PATH_RX 1
|
||||||
#define BGP_ADD_PATH_TX 2
|
#define BGP_ADD_PATH_TX 2
|
||||||
#define BGP_ADD_PATH_FULL 3
|
#define BGP_ADD_PATH_FULL 3
|
||||||
|
@ -203,6 +218,10 @@ struct bgp_channel_config {
|
||||||
|
|
||||||
#define BGP_BFD_GRACEFUL 2 /* BFD down triggers graceful restart */
|
#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 {
|
struct bgp_af_caps {
|
||||||
u32 afi;
|
u32 afi;
|
||||||
|
@ -223,6 +242,7 @@ struct bgp_caps {
|
||||||
u8 ext_messages; /* Extended message length, RFC draft */
|
u8 ext_messages; /* Extended message length, RFC draft */
|
||||||
u8 route_refresh; /* Route refresh capability, RFC 2918 */
|
u8 route_refresh; /* Route refresh capability, RFC 2918 */
|
||||||
u8 enhanced_refresh; /* Enhanced route refresh, RFC 7313 */
|
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_aware; /* Graceful restart capability, RFC 4724 */
|
||||||
u8 gr_flags; /* Graceful restart flags */
|
u8 gr_flags; /* Graceful restart flags */
|
||||||
|
@ -454,7 +474,6 @@ struct bgp_parse_state {
|
||||||
jmp_buf err_jmpbuf;
|
jmp_buf err_jmpbuf;
|
||||||
|
|
||||||
struct hostentry *hostentry;
|
struct hostentry *hostentry;
|
||||||
struct rtable *base_table;
|
|
||||||
adata *mpls_labels;
|
adata *mpls_labels;
|
||||||
|
|
||||||
/* Cached state for bgp_rte_update() */
|
/* Cached state for bgp_rte_update() */
|
||||||
|
@ -473,6 +492,9 @@ struct bgp_parse_state {
|
||||||
#define BGP_RX_BUFFER_EXT_SIZE 65535
|
#define BGP_RX_BUFFER_EXT_SIZE 65535
|
||||||
#define BGP_TX_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)
|
static inline int bgp_channel_is_ipv4(struct bgp_channel *c)
|
||||||
{ return BGP_AFI(c->afi) == BGP_AFI_IPV4; }
|
{ 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)
|
static inline int bgp_cc_is_ipv6(struct bgp_channel_config *c)
|
||||||
{ return BGP_AFI(c->afi) == BGP_AFI_IPV6; }
|
{ 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)
|
static inline uint bgp_max_packet_length(struct bgp_conn *conn)
|
||||||
{ return conn->ext_messages ? BGP_MAX_EXT_MSG_LENGTH : BGP_MAX_MESSAGE_LENGTH; }
|
{ 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);
|
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_start_timer(timer *t, uint value);
|
||||||
void bgp_check_config(struct bgp_config *c);
|
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);
|
bgp_set_attr(to, pool, code, flags, (uintptr_t) a);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
#define bgp_unset_attr(to, pool, code) ea_unset_attr(to, pool, 0, code)
|
||||||
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; }
|
|
||||||
|
|
||||||
int bgp_encode_mp_reach_mrt(struct bgp_write_state *s, eattr *a, byte *buf, uint size);
|
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_mergable(rte *pri, rte *sec);
|
||||||
int bgp_rte_recalculate(rtable *table, net *net, rte *new, rte *old, rte *old_best);
|
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);
|
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);
|
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);
|
int bgp_get_attr(const struct eattr *e, byte *buf, int buflen);
|
||||||
void bgp_get_route_info(struct rte *, byte *buf);
|
void bgp_get_route_info(struct rte *, byte *buf);
|
||||||
int bgp_total_aigp_metric_(rte *e, u64 *metric, const struct adata **ad);
|
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_AS4_AGGREGATOR 0x12 /* RFC 6793 */
|
||||||
#define BA_AIGP 0x1a /* RFC 7311 */
|
#define BA_AIGP 0x1a /* RFC 7311 */
|
||||||
#define BA_LARGE_COMMUNITY 0x20 /* RFC 8092 */
|
#define BA_LARGE_COMMUNITY 0x20 /* RFC 8092 */
|
||||||
|
#define BA_ONLY_TO_CUSTOMER 0x23 /* RFC 9234 */
|
||||||
|
|
||||||
/* Bird's private internal BGP attributes */
|
/* Bird's private internal BGP attributes */
|
||||||
#define BA_MPLS_LABEL_STACK 0xfe /* MPLS label stack transfer attribute */
|
#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,
|
STRICT, BIND, CONFEDERATION, MEMBER, MULTICAST, FLOW4, FLOW6, LONG,
|
||||||
LIVED, STALE, IMPORT, IBGP, EBGP, MANDATORY, INTERNAL, EXTERNAL, SETS,
|
LIVED, STALE, IMPORT, IBGP, EBGP, MANDATORY, INTERNAL, EXTERNAL, SETS,
|
||||||
DYNAMIC, RANGE, NAME, DIGITS, BGP_AIGP, AIGP, ORIGINATE, COST, ENFORCE,
|
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 <i> bgp_nh
|
||||||
%type <i32> bgp_afi
|
%type <i32> bgp_afi
|
||||||
|
@ -40,7 +41,7 @@ CF_KEYWORDS(CEASE, PREFIX, LIMIT, HIT, ADMINISTRATIVE, SHUTDOWN, RESET, PEER,
|
||||||
CONFIGURATION, CHANGE, DECONFIGURED, CONNECTION, REJECTED, COLLISION,
|
CONFIGURATION, CHANGE, DECONFIGURED, CONNECTION, REJECTED, COLLISION,
|
||||||
OUT, OF, RESOURCES)
|
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
|
CF_GRAMMAR
|
||||||
|
|
||||||
|
@ -72,6 +73,7 @@ bgp_proto_start: proto_start BGP {
|
||||||
BGP_CFG->llgr_mode = -1;
|
BGP_CFG->llgr_mode = -1;
|
||||||
BGP_CFG->llgr_time = 3600;
|
BGP_CFG->llgr_time = 3600;
|
||||||
BGP_CFG->setkey = 1;
|
BGP_CFG->setkey = 1;
|
||||||
|
BGP_CFG->local_role = BGP_ROLE_UNDEFINED;
|
||||||
BGP_CFG->dynamic_name = "dynbgp";
|
BGP_CFG->dynamic_name = "dynbgp";
|
||||||
BGP_CFG->check_link = -1;
|
BGP_CFG->check_link = -1;
|
||||||
}
|
}
|
||||||
|
@ -114,6 +116,14 @@ bgp_cease_flag:
|
||||||
| OUT OF RESOURCES { $$ = 1 << 8; }
|
| 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:
|
||||||
bgp_proto_start proto_name '{'
|
bgp_proto_start proto_name '{'
|
||||||
| bgp_proto proto_item ';'
|
| bgp_proto proto_item ';'
|
||||||
|
@ -143,7 +153,8 @@ bgp_proto:
|
||||||
| bgp_proto RS CLIENT bool ';' { BGP_CFG->rs_client = $4; }
|
| bgp_proto RS CLIENT bool ';' { BGP_CFG->rs_client = $4; }
|
||||||
| bgp_proto CONFEDERATION expr ';' { BGP_CFG->confederation = $3; }
|
| bgp_proto CONFEDERATION expr ';' { BGP_CFG->confederation = $3; }
|
||||||
| bgp_proto CONFEDERATION MEMBER bool ';' { BGP_CFG->confederation_member = $4; }
|
| 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 STARTUP HOLD TIME expr ';' { BGP_CFG->initial_hold_time = $5; }
|
||||||
| bgp_proto DIRECT ';' { BGP_CFG->multihop = 0; }
|
| bgp_proto DIRECT ';' { BGP_CFG->multihop = 0; }
|
||||||
| bgp_proto MULTIHOP ';' { BGP_CFG->multihop = 64; }
|
| 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 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 DELAY TIME expr ';' { BGP_CFG->connect_delay_time = $5; }
|
||||||
| bgp_proto CONNECT RETRY TIME expr ';' { BGP_CFG->connect_retry_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 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 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; }
|
| 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 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 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 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:
|
bgp_afi:
|
||||||
|
@ -251,6 +265,7 @@ bgp_channel_item:
|
||||||
| NEXT HOP ADDRESS ipa { BGP_CC->next_hop_addr = $4; }
|
| NEXT HOP ADDRESS ipa { BGP_CC->next_hop_addr = $4; }
|
||||||
| NEXT HOP SELF bgp_nh { BGP_CC->next_hop_self = $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 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; }
|
| MANDATORY bool { BGP_CC->mandatory = $2; }
|
||||||
| MISSING LLADDR bgp_lladdr { log(L_WARN "%s.%s: Missing lladdr option is deprecated and ignored, remove it", this_proto->name, this_channel->name); }
|
| MISSING LLADDR bgp_lladdr { 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; }
|
| 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)); } ;
|
{ $$ = f_new_dynamic_attr(EAF_TYPE_OPAQUE, T_ENUM_EMPTY, EA_CODE(PROTOCOL_BGP, BA_AIGP)); } ;
|
||||||
dynamic_attr: BGP_LARGE_COMMUNITY
|
dynamic_attr: BGP_LARGE_COMMUNITY
|
||||||
{ $$ = f_new_dynamic_attr(EAF_TYPE_LC_SET, T_LCLIST, EA_CODE(PROTOCOL_BGP, BA_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->ext_messages = p->cf->enable_extended_messages;
|
||||||
caps->route_refresh = p->cf->enable_refresh;
|
caps->route_refresh = p->cf->enable_refresh;
|
||||||
caps->enhanced_refresh = p->cf->enable_refresh;
|
caps->enhanced_refresh = p->cf->enable_refresh;
|
||||||
|
caps->role = p->cf->local_role;
|
||||||
|
|
||||||
if (caps->as4_support)
|
if (caps->as4_support)
|
||||||
caps->as4_number = p->public_as;
|
caps->as4_number = p->public_as;
|
||||||
|
@ -261,7 +262,7 @@ bgp_prepare_capabilities(struct bgp_conn *conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate and fill per-AF fields */
|
/* 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 = &caps->af_data[caps->af_count++];
|
||||||
ac->afi = c->afi;
|
ac->afi = c->afi;
|
||||||
|
@ -350,6 +351,13 @@ bgp_write_capabilities(struct bgp_conn *conn, byte *buf)
|
||||||
*buf++ = 0; /* Capability data length */
|
*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)
|
if (caps->gr_aware)
|
||||||
{
|
{
|
||||||
*buf++ = 64; /* Capability 64: Support for graceful restart */
|
*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_proto *p = conn->bgp;
|
||||||
struct bgp_caps *caps;
|
struct bgp_caps *caps;
|
||||||
struct bgp_af_caps *ac;
|
struct bgp_af_caps *ac;
|
||||||
|
uint err_subcode = 0;
|
||||||
int i, cl;
|
int i, cl;
|
||||||
u32 af;
|
u32 af;
|
||||||
|
|
||||||
if (!conn->remote_caps)
|
if (!conn->remote_caps)
|
||||||
|
{
|
||||||
caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps) + sizeof(struct bgp_af_caps));
|
caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps) + sizeof(struct bgp_af_caps));
|
||||||
|
caps->role = BGP_ROLE_UNDEFINED;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
caps = conn->remote_caps;
|
caps = conn->remote_caps;
|
||||||
|
@ -513,6 +525,21 @@ bgp_read_capabilities(struct bgp_conn *conn, byte *pos, int len)
|
||||||
caps->ext_messages = 1;
|
caps->ext_messages = 1;
|
||||||
break;
|
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 */
|
case 64: /* Graceful restart capability, RFC 4724 */
|
||||||
if (cl % 4 != 2)
|
if (cl % 4 != 2)
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -638,7 +665,7 @@ bgp_read_capabilities(struct bgp_conn *conn, byte *pos, int len)
|
||||||
|
|
||||||
err:
|
err:
|
||||||
mb_free(caps);
|
mb_free(caps);
|
||||||
bgp_error(conn, 2, 0, NULL, 0);
|
bgp_error(conn, 2, err_subcode, NULL, 0);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -654,7 +681,7 @@ bgp_check_capabilities(struct bgp_conn *conn)
|
||||||
/* This is partially overlapping with bgp_conn_enter_established_state(),
|
/* This is partially overlapping with bgp_conn_enter_established_state(),
|
||||||
but we need to run this just after we receive OPEN message */
|
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 *loc = bgp_find_af_caps(local, c->afi);
|
||||||
const struct bgp_af_caps *rem = bgp_find_af_caps(remote, 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)
|
if (bgp_read_options(conn, pkt+29, pkt[28], len-29) < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* RFC 4271 4.2 - hold time must be either 0 or at least 3 */
|
||||||
if (hold > 0 && hold < 3)
|
if (hold > 0 && hold < 3)
|
||||||
{ bgp_error(conn, 2, 6, pkt+22, 2); return; }
|
{ 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 */
|
/* RFC 6286 2.2 - router ID is nonzero and AS-wide unique */
|
||||||
if (!id || (p->is_internal && id == p->local_id))
|
if (!id || (p->is_internal && id == p->local_id))
|
||||||
{ bgp_error(conn, 2, 3, pkt+24, -4); return; }
|
{ 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;
|
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 */
|
/* Check the other connection */
|
||||||
other = (conn == &p->outgoing_conn) ? &p->incoming_conn : &p->outgoing_conn;
|
other = (conn == &p->outgoing_conn) ? &p->incoming_conn : &p->outgoing_conn;
|
||||||
switch (other->state)
|
switch (other->state)
|
||||||
|
@ -904,8 +963,8 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update our local variables */
|
/* Update our local variables */
|
||||||
conn->hold_time = MIN(hold, p->cf->hold_time);
|
conn->hold_time = hold_time;
|
||||||
conn->keepalive_time = p->cf->keepalive_time ? : conn->hold_time / 3;
|
conn->keepalive_time = keepalive_time;
|
||||||
conn->as4_session = conn->local_caps->as4_support && caps->as4_support;
|
conn->as4_session = conn->local_caps->as4_support && caps->as4_support;
|
||||||
conn->ext_messages = conn->local_caps->ext_messages && caps->ext_messages;
|
conn->ext_messages = conn->local_caps->ext_messages && caps->ext_messages;
|
||||||
p->remote_id = id;
|
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");
|
WITHDRAW(BAD_NEXT_HOP " - zero address");
|
||||||
|
|
||||||
rtable *tab = ipa_is_ip4(gw) ? c->igp_table_ip4 : c->igp_table_ip6;
|
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)
|
if (!s->mpls)
|
||||||
rta_apply_hostentry(a, s->hostentry, NULL);
|
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);
|
int valid = rt_flowspec_check(c->base_table, c->c.table, n, a, s->proto->is_interior);
|
||||||
a->dest = valid ? RTD_NONE : RTD_UNREACHABLE;
|
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 */
|
/* Invalidate cached rta if dest changes */
|
||||||
if (s->cached_rta && (s->cached_rta->dest != a->dest))
|
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;
|
return 1;
|
||||||
|
|
||||||
/* Keep it when explicitly set in export filter */
|
/* Keep it when explicitly set in export filter */
|
||||||
if (a->type & EAF_FRESH)
|
if (a->fresh)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/* Check for non-matching AF */
|
/* Check for non-matching AF */
|
||||||
if ((ipa_is_ip4(*nh) != bgp_channel_is_ipv4(c)) && !c->ext_next_hop)
|
if ((ipa_is_ip4(*nh) != bgp_channel_is_ipv4(c)) && !c->ext_next_hop)
|
||||||
return 0;
|
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 */
|
/* Keep it when exported to internal peers */
|
||||||
if (p->is_interior && ipa_nonzero(*nh))
|
if (p->is_interior && ipa_nonzero(*nh))
|
||||||
return 1;
|
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)
|
if ((ipa_is_ip4(ra->nh.gw) != bgp_channel_is_ipv4(c)) && !c->ext_next_hop)
|
||||||
return 0;
|
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 */
|
/* Use it when exported to internal peers */
|
||||||
if (p->is_interior)
|
if (p->is_interior)
|
||||||
return 1;
|
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;
|
uint lnum = ra->nh.labels ? ra->nh.labels : 1;
|
||||||
bgp_set_attr_data(to, s->pool, BA_MPLS_LABEL_STACK, 0, labels, lnum * 4);
|
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
|
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;
|
u32 implicit_null = BGP_MPLS_NULL;
|
||||||
bgp_set_attr_data(to, s->pool, BA_MPLS_LABEL_STACK, 0, &implicit_null, 4);
|
bgp_set_attr_data(to, 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 */
|
/* Prepare cached route attributes */
|
||||||
if (s->cached_rta == NULL)
|
if (s->cached_rta == NULL)
|
||||||
{
|
{
|
||||||
a0->src = s->last_src;
|
|
||||||
|
|
||||||
/* Workaround for rta_lookup() breaking eattrs */
|
/* Workaround for rta_lookup() breaking eattrs */
|
||||||
ea_list *ea = a0->eattrs;
|
ea_list *ea = a0->eattrs;
|
||||||
s->cached_rta = rta_lookup(a0);
|
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);
|
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->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);
|
rte_update3(&s->channel->c, n, e, s->last_src);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2331,11 +2395,14 @@ bgp_create_update(struct bgp_channel *c, byte *buf)
|
||||||
|
|
||||||
again: ;
|
again: ;
|
||||||
|
|
||||||
|
struct lp_state tmpp;
|
||||||
|
lp_save(tmp_linpool, &tmpp);
|
||||||
|
|
||||||
/* Initialize write state */
|
/* Initialize write state */
|
||||||
struct bgp_write_state s = {
|
struct bgp_write_state s = {
|
||||||
.proto = p,
|
.proto = p,
|
||||||
.channel = c,
|
.channel = c,
|
||||||
.pool = bgp_linpool,
|
.pool = tmp_linpool,
|
||||||
.mp_reach = (c->afi != BGP_AF_IPV4) || c->ext_next_hop,
|
.mp_reach = (c->afi != BGP_AF_IPV4) || c->ext_next_hop,
|
||||||
.as4_session = p->as4_session,
|
.as4_session = p->as4_session,
|
||||||
.add_path = c->add_path_tx,
|
.add_path = c->add_path_tx,
|
||||||
|
@ -2361,6 +2428,7 @@ again: ;
|
||||||
if (EMPTY_LIST(buck->prefixes))
|
if (EMPTY_LIST(buck->prefixes))
|
||||||
{
|
{
|
||||||
bgp_free_bucket(c, buck);
|
bgp_free_bucket(c, buck);
|
||||||
|
lp_restore(tmp_linpool, &tmpp);
|
||||||
goto again;
|
goto again;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2374,7 +2442,10 @@ again: ;
|
||||||
bgp_defer_bucket(c, buck);
|
bgp_defer_bucket(c, buck);
|
||||||
|
|
||||||
if (!res)
|
if (!res)
|
||||||
|
{
|
||||||
|
lp_restore(tmp_linpool, &tmpp);
|
||||||
goto again;
|
goto again;
|
||||||
|
}
|
||||||
|
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
@ -2385,7 +2456,7 @@ again: ;
|
||||||
done:
|
done:
|
||||||
BGP_TRACE_RL(&rl_snd_update, D_PACKETS, "Sending UPDATE");
|
BGP_TRACE_RL(&rl_snd_update, D_PACKETS, "Sending UPDATE");
|
||||||
p->stats.tx_updates++;
|
p->stats.tx_updates++;
|
||||||
lp_flush(s.pool);
|
lp_restore(tmp_linpool, &tmpp);
|
||||||
|
|
||||||
return res;
|
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_id = 0;
|
||||||
s->last_src = s->proto->p.main_source;
|
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
|
* 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
|
* 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->scope = SCOPE_UNIVERSE;
|
||||||
a->from = s->proto->remote_ip;
|
a->from = s->proto->remote_ip;
|
||||||
a->eattrs = ea;
|
a->eattrs = ea;
|
||||||
|
a->pref = c->c.preference;
|
||||||
|
|
||||||
c->desc->decode_next_hop(s, nh, nh_len, a);
|
c->desc->decode_next_hop(s, nh, nh_len, a);
|
||||||
bgp_finish_attrs(s, 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);
|
bgp_start_timer(conn->hold_timer, conn->hold_time);
|
||||||
|
|
||||||
|
struct lp_state tmpp;
|
||||||
|
lp_save(tmp_linpool, &tmpp);
|
||||||
|
|
||||||
/* Initialize parse state */
|
/* Initialize parse state */
|
||||||
struct bgp_parse_state s = {
|
struct bgp_parse_state s = {
|
||||||
.proto = p,
|
.proto = p,
|
||||||
.pool = bgp_linpool,
|
.pool = tmp_linpool,
|
||||||
.as4_session = p->as4_session,
|
.as4_session = p->as4_session,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2596,7 +2669,7 @@ bgp_rx_update(struct bgp_conn *conn, byte *pkt, uint len)
|
||||||
|
|
||||||
done:
|
done:
|
||||||
rta_free(s.cached_rta);
|
rta_free(s.cached_rta);
|
||||||
lp_flush(s.pool);
|
lp_restore(tmp_linpool, &tmpp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2984,6 +3057,7 @@ static struct {
|
||||||
{ 2, 6, "Unacceptable hold time" },
|
{ 2, 6, "Unacceptable hold time" },
|
||||||
{ 2, 7, "Required capability missing" }, /* [RFC5492] */
|
{ 2, 7, "Required capability missing" }, /* [RFC5492] */
|
||||||
{ 2, 8, "No supported AFI/SAFI" }, /* This error msg is nonstandard */
|
{ 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, 0, "Invalid UPDATE message" },
|
||||||
{ 3, 1, "Malformed attribute list" },
|
{ 3, 1, "Malformed attribute list" },
|
||||||
{ 3, 2, "Unrecognized well-known attribute" },
|
{ 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)
|
if (len)
|
||||||
{
|
{
|
||||||
/* Bad peer AS - we would like to print the AS */
|
/* Bad peer AS / unacceptable hold time - print the value as decimal number */
|
||||||
if ((code == 2) && (subcode == 2) && ((len == 2) || (len == 4)))
|
if ((code == 2) && ((subcode == 2) || (subcode == 6)) && ((len == 2) || (len == 4)))
|
||||||
{
|
{
|
||||||
t += bsprintf(t, ": %u", (len == 2) ? get_u16(data) : get_u32(data));
|
t += bsprintf(t, ": %u", (len == 2) ? get_u16(data) : get_u32(data));
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
@ -3,4 +3,4 @@ obj := $(src-o-files)
|
||||||
$(all-daemon)
|
$(all-daemon)
|
||||||
$(cf-local)
|
$(cf-local)
|
||||||
|
|
||||||
tests_objs := $(tests_objs) $(src-o-files)
|
tests_objs := $(tests_objs) $(src-o-files)
|
||||||
|
|
|
@ -472,9 +472,9 @@ mrt_rib_table_entry(struct mrt_table_dump_state *s, rte *r)
|
||||||
|
|
||||||
#ifdef CONFIG_BGP
|
#ifdef CONFIG_BGP
|
||||||
/* Find peer index */
|
/* 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 =
|
struct mrt_peer_entry *n =
|
||||||
HASH_FIND(s->peer_hash, PEER, p->remote_id, p->remote_as, p->remote_ip);
|
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 */
|
/* Path Identifier */
|
||||||
if (s->add_path)
|
if (s->add_path)
|
||||||
mrt_put_u32(b, r->attrs->src->private_id);
|
mrt_put_u32(b, r->src->private_id);
|
||||||
|
|
||||||
/* Route Attributes */
|
/* Route Attributes */
|
||||||
mrt_put_u16(b, 0);
|
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;
|
continue;
|
||||||
|
|
||||||
/* Skip routes that should be reported in the other phase */
|
/* 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;
|
s->want_add_path = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
rte_make_tmp_attrs(&rt, s->linpool, NULL);
|
|
||||||
|
|
||||||
if (f_run(s->filter, &rt, s->linpool, 0) <= F_ACCEPT)
|
if (f_run(s->filter, &rt, s->linpool, 0) <= F_ACCEPT)
|
||||||
mrt_rib_table_entry(s, rt);
|
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));
|
struct mrt_table_dump_state *s = mb_allocz(pool, sizeof(struct mrt_table_dump_state));
|
||||||
|
|
||||||
s->pool = pool;
|
s->pool = pool;
|
||||||
s->linpool = lp_new(pool, 4080);
|
s->linpool = lp_new(pool);
|
||||||
s->peer_lp = lp_new(pool, 4080);
|
s->peer_lp = lp_new(pool);
|
||||||
mrt_buffer_init(&s->buf, pool, 2 * MRT_ATTR_BUFFER_SIZE);
|
mrt_buffer_init(&s->buf, pool, 2 * MRT_ATTR_BUFFER_SIZE);
|
||||||
|
|
||||||
/* We lock the current config as we may reference it indirectly by filter */
|
/* We lock the current config as we may reference it indirectly by filter */
|
||||||
|
@ -918,3 +916,9 @@ struct protocol proto_mrt = {
|
||||||
.reconfigure = mrt_reconfigure,
|
.reconfigure = mrt_reconfigure,
|
||||||
.copy_config = mrt_copy_config,
|
.copy_config = mrt_copy_config,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
mrt_build(void)
|
||||||
|
{
|
||||||
|
proto_build(&proto_mrt);
|
||||||
|
}
|
||||||
|
|
|
@ -3,4 +3,4 @@ obj := $(src-o-files)
|
||||||
$(all-daemon)
|
$(all-daemon)
|
||||||
$(cf-local)
|
$(cf-local)
|
||||||
|
|
||||||
tests_objs := $(tests_objs) $(src-o-files)
|
tests_objs := $(tests_objs) $(src-o-files)
|
||||||
|
|
|
@ -522,7 +522,10 @@ static inline void
|
||||||
add_nbma_node(struct ospf_iface *ifa, struct nbma_node *src, int found)
|
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));
|
struct nbma_node *n = mb_alloc(ifa->pool, sizeof(struct nbma_node));
|
||||||
|
|
||||||
|
n->n = (node) {};
|
||||||
add_tail(&ifa->nbma_list, NODE n);
|
add_tail(&ifa->nbma_list, NODE n);
|
||||||
|
|
||||||
n->ip = src->ip;
|
n->ip = src->ip;
|
||||||
n->eligible = src->eligible;
|
n->eligible = src->eligible;
|
||||||
n->found = found;
|
n->found = found;
|
||||||
|
|
|
@ -107,12 +107,10 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "ospf.h"
|
#include "ospf.h"
|
||||||
|
|
||||||
static int ospf_preexport(struct proto *P, rte **new, struct linpool *pool);
|
static int ospf_preexport(struct channel *P, rte *new);
|
||||||
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 void ospf_reload_routes(struct channel *C);
|
static void ospf_reload_routes(struct channel *C);
|
||||||
static int ospf_rte_better(struct rte *new, struct rte *old);
|
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);
|
static void ospf_disp(timer *timer);
|
||||||
|
|
||||||
|
|
||||||
|
@ -301,7 +299,7 @@ ospf_start(struct proto *P)
|
||||||
p->lsab_size = 256;
|
p->lsab_size = 256;
|
||||||
p->lsab_used = 0;
|
p->lsab_used = 0;
|
||||||
p->lsab = mb_alloc(P->pool, p->lsab_size);
|
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->iface_list));
|
||||||
init_list(&(p->area_list));
|
init_list(&(p->area_list));
|
||||||
fib_init(&p->rtf, P->pool, ospf_get_af(p), sizeof(ort), OFFSETOF(ort, fn), 0, NULL);
|
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->reload_routes = ospf_reload_routes;
|
||||||
P->feed_begin = ospf_feed_begin;
|
P->feed_begin = ospf_feed_begin;
|
||||||
P->feed_end = ospf_feed_end;
|
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_better = ospf_rte_better;
|
||||||
P->rte_same = ospf_rte_same;
|
P->rte_igp_metric = ospf_rte_igp_metric;
|
||||||
|
|
||||||
return P;
|
return P;
|
||||||
}
|
}
|
||||||
|
@ -390,7 +386,9 @@ ospf_init(struct proto_config *CF)
|
||||||
static int
|
static int
|
||||||
ospf_rte_better(struct rte *new, struct rte *old)
|
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;
|
return 0;
|
||||||
|
|
||||||
if(new->attrs->source < old->attrs->source) return 1;
|
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->attrs->source == RTS_OSPF_EXT2)
|
||||||
{
|
{
|
||||||
if(new->u.ospf.metric2 < old->u.ospf.metric2) return 1;
|
u32 old_metric2 = ea_get_int(old->attrs->eattrs, EA_OSPF_METRIC2, LSINFINITY);
|
||||||
if(new->u.ospf.metric2 > old->u.ospf.metric2) return 0;
|
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 1;
|
||||||
|
|
||||||
return 0; /* Old is shorter or same */
|
return 0; /* Old is shorter or same */
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static u32
|
||||||
ospf_rte_same(struct rte *new, struct rte *old)
|
ospf_rte_igp_metric(struct rte *rt)
|
||||||
{
|
{
|
||||||
/* new->attrs == old->attrs always */
|
if (rt->attrs->source == RTS_OSPF_EXT2)
|
||||||
return
|
return IGP_METRIC_UNKNOWN;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return ea_get_int(rt->attrs->eattrs, EA_OSPF_METRIC1, LSINFINITY);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ospf_schedule_rtcalc(struct ospf_proto *p)
|
ospf_schedule_rtcalc(struct ospf_proto *p)
|
||||||
|
@ -484,14 +482,13 @@ ospf_disp(timer * timer)
|
||||||
* import to the filters.
|
* import to the filters.
|
||||||
*/
|
*/
|
||||||
static int
|
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);
|
struct ospf_area *oa = ospf_main_area(p);
|
||||||
rte *e = *new;
|
|
||||||
|
|
||||||
/* Reject our own routes */
|
/* Reject our own routes */
|
||||||
if (e->attrs->src->proto == P)
|
if (e->src->proto == &p->p)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* Do not export routes to stub areas */
|
/* Do not export routes to stub areas */
|
||||||
|
@ -501,26 +498,6 @@ ospf_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
|
||||||
return 0;
|
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
|
* ospf_shutdown - Finish of OSPF instance
|
||||||
* @P: OSPF protocol instance
|
* @P: OSPF protocol instance
|
||||||
|
@ -558,6 +535,9 @@ ospf_shutdown(struct proto *P)
|
||||||
}
|
}
|
||||||
FIB_WALK_END;
|
FIB_WALK_END;
|
||||||
|
|
||||||
|
if (tm_active(p->disp_timer))
|
||||||
|
tm_stop(p->disp_timer);
|
||||||
|
|
||||||
return PS_DOWN;
|
return PS_DOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -607,16 +587,20 @@ ospf_get_route_info(rte * rte, byte * buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
buf += bsprintf(buf, " %s", type);
|
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)
|
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, ")");
|
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
|
static int
|
||||||
|
@ -1550,3 +1534,9 @@ struct protocol proto_ospf = {
|
||||||
.get_attr = ospf_get_attr,
|
.get_attr = ospf_get_attr,
|
||||||
.get_route_info = ospf_get_route_info
|
.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;
|
int r;
|
||||||
|
|
||||||
if (old->type == RTS_DUMMY)
|
if (!old->type)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/* Prefer intra-area to inter-area to externals */
|
/* 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;
|
int r;
|
||||||
|
|
||||||
if (old->type == RTS_DUMMY)
|
if (!old->type)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (!p->rfc1583)
|
if (!p->rfc1583)
|
||||||
|
@ -225,7 +225,7 @@ orta_compare_ext(const struct ospf_proto *p, const orta *new, const orta *old)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (old->type == RTS_DUMMY)
|
if (!old->type)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/* 16.4 (6a) - prefer routes with lower type */
|
/* 16.4 (6a) - prefer routes with lower type */
|
||||||
|
@ -2053,32 +2053,54 @@ again1:
|
||||||
if (nf->n.type) /* Add the route */
|
if (nf->n.type) /* Add the route */
|
||||||
{
|
{
|
||||||
rta a0 = {
|
rta a0 = {
|
||||||
.src = p->p.main_source,
|
|
||||||
.source = nf->n.type,
|
.source = nf->n.type,
|
||||||
.scope = SCOPE_UNIVERSE,
|
.scope = SCOPE_UNIVERSE,
|
||||||
.dest = RTD_UNICAST,
|
.dest = RTD_UNICAST,
|
||||||
.nh = *(nf->n.nhs),
|
.nh = *(nf->n.nhs),
|
||||||
|
.pref = p->p.main_channel->preference,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (reload || ort_changed(nf, &a0))
|
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);
|
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);
|
rta_free(nf->old_rta);
|
||||||
nf->old_rta = rta_clone(a);
|
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",
|
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);
|
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)
|
if (*ee == e)
|
||||||
{
|
{
|
||||||
*ee = e->next;
|
*ee = e->next;
|
||||||
sl_free(f->hash_slab, e);
|
sl_free(e);
|
||||||
if (f->hash_entries-- < f->hash_entries_min)
|
if (f->hash_entries-- < f->hash_entries_min)
|
||||||
ospf_top_rehash(f, -HASH_LO_STEP);
|
ospf_top_rehash(f, -HASH_LO_STEP);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -143,10 +143,10 @@ perf_loop(void *data)
|
||||||
|
|
||||||
if (!p->attrs_per_rte || !(i % p->attrs_per_rte)) {
|
if (!p->attrs_per_rte || !(i % p->attrs_per_rte)) {
|
||||||
struct rta a0 = {
|
struct rta a0 = {
|
||||||
.src = p->p.main_source,
|
|
||||||
.source = RTS_PERF,
|
.source = RTS_PERF,
|
||||||
.scope = SCOPE_UNIVERSE,
|
.scope = SCOPE_UNIVERSE,
|
||||||
.dest = RTD_UNICAST,
|
.dest = RTD_UNICAST,
|
||||||
|
.pref = p->p.main_channel->preference,
|
||||||
.nh.iface = p->ifa->iface,
|
.nh.iface = p->ifa->iface,
|
||||||
.nh.gw = gw,
|
.nh.gw = gw,
|
||||||
.nh.weight = 1,
|
.nh.weight = 1,
|
||||||
|
@ -161,7 +161,7 @@ perf_loop(void *data)
|
||||||
clock_gettime(CLOCK_MONOTONIC, &ts_generated);
|
clock_gettime(CLOCK_MONOTONIC, &ts_generated);
|
||||||
|
|
||||||
for (uint i=0; i<N; i++) {
|
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;
|
e->pflags = 0;
|
||||||
|
|
||||||
rte_update(P, &(p->data[i].net), e);
|
rte_update(P, &(p->data[i].net), e);
|
||||||
|
@ -315,3 +315,9 @@ struct protocol proto_perf = {
|
||||||
.reconfigure = perf_reconfigure,
|
.reconfigure = perf_reconfigure,
|
||||||
.copy_config = perf_copy_config,
|
.copy_config = perf_copy_config,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
perf_build(void)
|
||||||
|
{
|
||||||
|
proto_build(&proto_perf);
|
||||||
|
}
|
||||||
|
|
|
@ -3,4 +3,4 @@ obj := $(src-o-files)
|
||||||
$(all-daemon)
|
$(all-daemon)
|
||||||
$(cf-local)
|
$(cf-local)
|
||||||
|
|
||||||
tests_objs := $(tests_objs) $(src-o-files)
|
tests_objs := $(tests_objs) $(src-o-files)
|
||||||
|
|
|
@ -43,6 +43,10 @@
|
||||||
|
|
||||||
#include "pipe.h"
|
#include "pipe.h"
|
||||||
|
|
||||||
|
#ifdef CONFIG_BGP
|
||||||
|
#include "proto/bgp/bgp.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
static void
|
static void
|
||||||
pipe_rt_notify(struct proto *P, struct channel *src_ch, net *n, rte *new, rte *old)
|
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)
|
if (new)
|
||||||
{
|
{
|
||||||
|
src = new->src;
|
||||||
|
|
||||||
a = alloca(rta_size(new->attrs));
|
a = alloca(rta_size(new->attrs));
|
||||||
memcpy(a, new->attrs, rta_size(new->attrs));
|
memcpy(a, new->attrs, rta_size(new->attrs));
|
||||||
|
|
||||||
a->aflags = 0;
|
a->cached = 0;
|
||||||
a->hostentry = NULL;
|
a->hostentry = NULL;
|
||||||
e = rte_get_temp(a);
|
e = rte_get_temp(a, src);
|
||||||
e->pflags = 0;
|
|
||||||
|
|
||||||
/* Copy protocol specific embedded attributes. */
|
|
||||||
memcpy(&(e->u), &(new->u), sizeof(e->u));
|
|
||||||
e->pref = new->pref;
|
|
||||||
e->pflags = new->pflags;
|
e->pflags = new->pflags;
|
||||||
|
|
||||||
#ifdef CONFIG_BGP
|
#ifdef CONFIG_BGP
|
||||||
/* Hack to cleanup cached value */
|
/* Hack to cleanup cached value */
|
||||||
if (e->attrs->src->proto->proto == &proto_bgp)
|
if (e->src->proto->proto == &proto_bgp)
|
||||||
{
|
e->pflags &= ~(BGP_REF_STALE | BGP_REF_NOT_STALE);
|
||||||
e->u.bgp.stale = -1;
|
|
||||||
e->u.bgp.base_table = NULL;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
src = a->src;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
e = NULL;
|
e = NULL;
|
||||||
src = old->attrs->src;
|
src = old->src;
|
||||||
}
|
}
|
||||||
|
|
||||||
src_ch->table->pipe_busy = 1;
|
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
|
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 -1; /* Avoid local loops automatically */
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -307,3 +303,9 @@ struct protocol proto_pipe = {
|
||||||
.get_status = pipe_get_status,
|
.get_status = pipe_get_status,
|
||||||
.show_proto_info = pipe_show_proto_info
|
.show_proto_info = pipe_show_proto_info
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
pipe_build(void)
|
||||||
|
{
|
||||||
|
proto_build(&proto_pipe);
|
||||||
|
}
|
||||||
|
|
|
@ -3,4 +3,4 @@ obj := $(src-o-files)
|
||||||
$(all-daemon)
|
$(all-daemon)
|
||||||
$(cf-local)
|
$(cf-local)
|
||||||
|
|
||||||
tests_objs := $(tests_objs) $(src-o-files)
|
tests_objs := $(tests_objs) $(src-o-files)
|
||||||
|
|
|
@ -391,12 +391,12 @@ radv_net_match_trigger(struct radv_config *cf, net *n)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
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_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;
|
return RIC_PROCESS;
|
||||||
|
|
||||||
if (cf->propagate_routes)
|
if (cf->propagate_routes)
|
||||||
|
@ -555,7 +555,7 @@ radv_check_active(struct radv_proto *p)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
struct channel *c = p->p.main_channel;
|
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
|
static void
|
||||||
|
@ -771,3 +771,9 @@ struct protocol proto_radv = {
|
||||||
.get_status = radv_get_status,
|
.get_status = radv_get_status,
|
||||||
.get_attr = radv_get_attr
|
.get_attr = radv_get_attr
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
radv_build(void)
|
||||||
|
{
|
||||||
|
proto_build(&proto_radv);
|
||||||
|
}
|
||||||
|
|
|
@ -3,4 +3,4 @@ obj := $(src-o-files)
|
||||||
$(all-daemon)
|
$(all-daemon)
|
||||||
$(cf-local)
|
$(cf-local)
|
||||||
|
|
||||||
tests_objs := $(tests_objs) $(src-o-files)
|
tests_objs := $(tests_objs) $(src-o-files)
|
||||||
|
|
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
|
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;
|
struct rip_rte *rt = *rp;
|
||||||
|
|
||||||
rip_unlock_neighbor(rt->from);
|
rip_unlock_neighbor(rt->from);
|
||||||
|
|
||||||
*rp = rt->next;
|
*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)
|
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)
|
static inline int rip_valid_rte(struct rip_rte *rt)
|
||||||
{ return rt->from->ifa != NULL; }
|
{ 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
|
* rip_announce_rte - announce route from RIP routing table to the core
|
||||||
* @p: RIP instance
|
* @p: RIP instance
|
||||||
|
@ -145,7 +150,7 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
|
||||||
{
|
{
|
||||||
/* Update */
|
/* Update */
|
||||||
rta a0 = {
|
rta a0 = {
|
||||||
.src = p->p.main_source,
|
.pref = p->p.main_channel->preference,
|
||||||
.source = RTS_RIP,
|
.source = RTS_RIP,
|
||||||
.scope = SCOPE_UNIVERSE,
|
.scope = SCOPE_UNIVERSE,
|
||||||
.dest = RTD_UNICAST,
|
.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;
|
a0.nh.iface = rt->from->ifa->iface;
|
||||||
}
|
}
|
||||||
|
|
||||||
rta *a = rta_lookup(&a0);
|
struct {
|
||||||
rte *e = rte_get_temp(a);
|
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;
|
a0.eattrs = &ea_block.l;
|
||||||
e->u.rip.metric = rt_metric;
|
|
||||||
e->u.rip.tag = rt_tag;
|
rta *a = rta_lookup(&a0);
|
||||||
e->pflags = EA_ID_FLAG(EA_RIP_METRIC) | EA_ID_FLAG(EA_RIP_TAG);
|
rte *e = rte_get_temp(a, p->p.main_source);
|
||||||
|
|
||||||
rte_update(&p->p, en->n.addr, e);
|
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)
|
if (new)
|
||||||
{
|
{
|
||||||
/* Update */
|
/* 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_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)
|
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->valid = RIP_ENTRY_VALID;
|
||||||
en->metric = rt_metric;
|
en->metric = rt_metric;
|
||||||
en->tag = rt_tag;
|
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->iface = new->attrs->nh.iface;
|
||||||
en->next_hop = new->attrs->nh.gw;
|
en->next_hop = new->attrs->nh.gw;
|
||||||
}
|
}
|
||||||
|
@ -1068,37 +1101,18 @@ rip_reload_routes(struct channel *C)
|
||||||
rip_kick_timer(p);
|
rip_kick_timer(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static u32
|
||||||
rip_make_tmp_attrs(struct rte *rt, struct linpool *pool)
|
rip_rte_igp_metric(struct rte *rt)
|
||||||
{
|
{
|
||||||
rte_init_tmp_attrs(rt, pool, 2);
|
return ea_get_int(rt->attrs->eattrs, EA_RIP_METRIC, IGP_METRIC_UNKNOWN);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
rip_rte_better(struct rte *new, struct rte *old)
|
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
|
static void
|
||||||
rip_postconfig(struct proto_config *CF)
|
rip_postconfig(struct proto_config *CF)
|
||||||
{
|
{
|
||||||
|
@ -1120,10 +1134,8 @@ rip_init(struct proto_config *CF)
|
||||||
P->rt_notify = rip_rt_notify;
|
P->rt_notify = rip_rt_notify;
|
||||||
P->neigh_notify = rip_neigh_notify;
|
P->neigh_notify = rip_neigh_notify;
|
||||||
P->reload_routes = rip_reload_routes;
|
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_better = rip_rte_better;
|
||||||
P->rte_same = rip_rte_same;
|
P->rte_igp_metric = rip_rte_igp_metric;
|
||||||
|
|
||||||
return P;
|
return P;
|
||||||
}
|
}
|
||||||
|
@ -1198,10 +1210,14 @@ rip_reconfigure(struct proto *P, struct proto_config *CF)
|
||||||
static void
|
static void
|
||||||
rip_get_route_info(rte *rte, byte *buf)
|
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)
|
buf += bsprintf(buf, " (%d/%d)", rte->attrs->pref, rt_metric);
|
||||||
bsprintf(buf, " [%04x]", rte->u.rip.tag);
|
|
||||||
|
if (rt_tag)
|
||||||
|
bsprintf(buf, " [%04x]", rt_tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -1338,3 +1354,9 @@ struct protocol proto_rip = {
|
||||||
.get_route_info = rip_get_route_info,
|
.get_route_info = rip_get_route_info,
|
||||||
.get_attr = rip_get_attr
|
.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_METRIC EA_CODE(PROTOCOL_RIP, 0)
|
||||||
#define EA_RIP_TAG EA_CODE(PROTOCOL_RIP, 1)
|
#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)
|
static inline int rip_is_v2(struct rip_proto *p)
|
||||||
{ return p->rip2; }
|
{ return p->rip2; }
|
||||||
|
|
|
@ -3,4 +3,4 @@ obj := $(src-o-files)
|
||||||
$(all-daemon)
|
$(all-daemon)
|
||||||
$(cf-local)
|
$(cf-local)
|
||||||
|
|
||||||
tests_objs := $(tests_objs) $(src-o-files)
|
tests_objs := $(tests_objs) $(src-o-files)
|
||||||
|
|
|
@ -121,18 +121,18 @@ rpki_table_add_roa(struct rpki_cache *cache, struct channel *channel, const net_
|
||||||
struct rpki_proto *p = cache->p;
|
struct rpki_proto *p = cache->p;
|
||||||
|
|
||||||
rta a0 = {
|
rta a0 = {
|
||||||
.src = p->p.main_source,
|
.pref = channel->preference,
|
||||||
.source = RTS_RPKI,
|
.source = RTS_RPKI,
|
||||||
.scope = SCOPE_UNIVERSE,
|
.scope = SCOPE_UNIVERSE,
|
||||||
.dest = RTD_NONE,
|
.dest = RTD_NONE,
|
||||||
};
|
};
|
||||||
|
|
||||||
rta *a = rta_lookup(&a0);
|
rta *a = rta_lookup(&a0);
|
||||||
rte *e = rte_get_temp(a);
|
rte *e = rte_get_temp(a, p->p.main_source);
|
||||||
|
|
||||||
e->pflags = 0;
|
e->pflags = 0;
|
||||||
|
|
||||||
rte_update2(channel, &pfxr->n, e, a0.src);
|
rte_update2(channel, &pfxr->n, e, e->src);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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:
|
case RPKI_CS_NO_INCR_UPDATE_AVAIL:
|
||||||
/* Server was unable to answer the last Serial Query and sent Cache Reset. */
|
/* 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:
|
case RPKI_CS_ERROR_NO_DATA_AVAIL:
|
||||||
/* No validation records are available on the cache server. */
|
/* No validation records are available on the cache server. */
|
||||||
rpki_cache_change_state(cache, RPKI_CS_RESET);
|
|
||||||
|
if (old_state == RPKI_CS_ESTABLISHED)
|
||||||
|
rpki_cache_change_state(cache, RPKI_CS_RESET);
|
||||||
|
else
|
||||||
|
rpki_schedule_next_retry(cache);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RPKI_CS_ERROR_FATAL:
|
case RPKI_CS_ERROR_FATAL:
|
||||||
|
@ -453,6 +454,11 @@ rpki_retry_hook(timer *tm)
|
||||||
}
|
}
|
||||||
break;
|
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:
|
default:
|
||||||
rpki_cache_change_state(cache, RPKI_CS_CONNECTING);
|
rpki_cache_change_state(cache, RPKI_CS_CONNECTING);
|
||||||
break;
|
break;
|
||||||
|
@ -828,16 +834,27 @@ rpki_show_proto_info(struct proto *P)
|
||||||
if (cache)
|
if (cache)
|
||||||
{
|
{
|
||||||
const char *transport_name = "---";
|
const char *transport_name = "---";
|
||||||
|
uint default_port = 0;
|
||||||
|
|
||||||
switch (cf->tr_config.type)
|
switch (cf->tr_config.type)
|
||||||
{
|
{
|
||||||
#if HAVE_LIBSSH
|
#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
|
#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);
|
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, " Status: %s", rpki_cache_state_to_str(cache->state));
|
||||||
cli_msg(-1006, " Transport: %s", transport_name);
|
cli_msg(-1006, " Transport: %s", transport_name);
|
||||||
cli_msg(-1006, " Protocol version: %u", cache->version);
|
cli_msg(-1006, " Protocol version: %u", cache->version);
|
||||||
|
@ -949,3 +966,9 @@ struct protocol proto_rpki = {
|
||||||
.reconfigure = rpki_reconfigure,
|
.reconfigure = rpki_reconfigure,
|
||||||
.get_status = rpki_get_status,
|
.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->rbsize = RPKI_RX_BUFFER_SIZE;
|
||||||
sk->tbsize = RPKI_TX_BUFFER_SIZE;
|
sk->tbsize = RPKI_TX_BUFFER_SIZE;
|
||||||
sk->tos = IP_PREC_INTERNET_CONTROL;
|
sk->tos = IP_PREC_INTERNET_CONTROL;
|
||||||
|
sk->vrf = cache->p->p.vrf;
|
||||||
|
|
||||||
if (ipa_zero(sk->daddr) && sk->host)
|
if (ipa_zero(sk->daddr) && sk->host)
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,4 +3,4 @@ obj := $(src-o-files)
|
||||||
$(all-daemon)
|
$(all-daemon)
|
||||||
$(cf-local)
|
$(cf-local)
|
||||||
|
|
||||||
tests_objs := $(tests_objs) $(src-o-files)
|
tests_objs := $(tests_objs) $(src-o-files)
|
||||||
|
|
|
@ -40,7 +40,7 @@ static_route_finish(void)
|
||||||
if (net_type_match(this_srt->net, NB_DEST) == !this_srt->dest)
|
if (net_type_match(this_srt->net, NB_DEST) == !this_srt->dest)
|
||||||
cf_error("Unexpected or missing nexthop/type");
|
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
|
CF_DECLS
|
||||||
|
|
|
@ -56,10 +56,11 @@ static void
|
||||||
static_announce_rte(struct static_proto *p, struct static_route *r)
|
static_announce_rte(struct static_proto *p, struct static_route *r)
|
||||||
{
|
{
|
||||||
rta *a = allocz(RTA_MAX_SIZE);
|
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->source = RTS_STATIC;
|
||||||
a->scope = SCOPE_UNIVERSE;
|
a->scope = SCOPE_UNIVERSE;
|
||||||
a->dest = r->dest;
|
a->dest = r->dest;
|
||||||
|
a->pref = p->p.main_channel->preference;
|
||||||
|
|
||||||
if (r->dest == RTD_UNICAST)
|
if (r->dest == RTD_UNICAST)
|
||||||
{
|
{
|
||||||
|
@ -102,7 +103,7 @@ static_announce_rte(struct static_proto *p, struct static_route *r)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* We skip rta_lookup() here */
|
/* We skip rta_lookup() here */
|
||||||
rte *e = rte_get_temp(a);
|
rte *e = rte_get_temp(a, src);
|
||||||
e->pflags = 0;
|
e->pflags = 0;
|
||||||
|
|
||||||
if (r->cmds)
|
if (r->cmds)
|
||||||
|
@ -119,7 +120,7 @@ static_announce_rte(struct static_proto *p, struct static_route *r)
|
||||||
e->net = NULL;
|
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;
|
r->state = SRS_CLEAN;
|
||||||
|
|
||||||
if (r->cmds)
|
if (r->cmds)
|
||||||
|
@ -131,7 +132,7 @@ withdraw:
|
||||||
if (r->state == SRS_DOWN)
|
if (r->state == SRS_DOWN)
|
||||||
return;
|
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;
|
r->state = SRS_DOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -485,7 +486,7 @@ static_start(struct proto *P)
|
||||||
struct static_route *r;
|
struct static_route *r;
|
||||||
|
|
||||||
if (!static_lp)
|
if (!static_lp)
|
||||||
static_lp = lp_new(&root_pool, LP_GOOD_SIZE(1024));
|
static_lp = lp_new(&root_pool);
|
||||||
|
|
||||||
if (p->igp_table_ip4)
|
if (p->igp_table_ip4)
|
||||||
rt_lock_table(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);
|
eattr *a = ea_find(rte->attrs->eattrs, EA_GEN_IGP_METRIC);
|
||||||
if (a)
|
if (a)
|
||||||
buf += bsprintf(buf, " (%d/%u)", rte->pref, a->u.data);
|
buf += bsprintf(buf, " (%d/%u)", rte->attrs->pref, a->u.data);
|
||||||
else
|
else
|
||||||
buf += bsprintf(buf, " (%d)", rte->pref);
|
buf += bsprintf(buf, " (%d)", rte->attrs->pref);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -792,3 +793,9 @@ struct protocol proto_static = {
|
||||||
.copy_config = static_copy_config,
|
.copy_config = static_copy_config,
|
||||||
.get_route_info = static_get_route_info,
|
.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)
|
sockaddr_fill_dl(struct sockaddr_dl *sa, struct iface *ifa)
|
||||||
{
|
{
|
||||||
uint len = OFFSETOF(struct sockaddr_dl, sdl_data);
|
uint len = OFFSETOF(struct sockaddr_dl, sdl_data);
|
||||||
|
|
||||||
|
/* Workaround for FreeBSD 13.0 */
|
||||||
|
len = MAX(len, sizeof(struct sockaddr));
|
||||||
|
|
||||||
memset(sa, 0, len);
|
memset(sa, 0, len);
|
||||||
sa->sdl_len = len;
|
sa->sdl_len = len;
|
||||||
sa->sdl_family = AF_LINK;
|
sa->sdl_family = AF_LINK;
|
||||||
|
@ -347,7 +351,7 @@ krt_send_route(struct krt_proto *p, int cmd, rte *e)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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;
|
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);
|
net = net_get(p->p.main_channel->table, &ndst);
|
||||||
|
|
||||||
rta a = {
|
rta a = {
|
||||||
.src = p->p.main_source,
|
|
||||||
.source = RTS_INHERIT,
|
.source = RTS_INHERIT,
|
||||||
.scope = SCOPE_UNIVERSE,
|
.scope = SCOPE_UNIVERSE,
|
||||||
};
|
};
|
||||||
|
@ -580,18 +583,23 @@ krt_read_route(struct ks_msg *msg, struct krt_proto *p, int scan)
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
e = rte_get_temp(&a);
|
e = rte_get_temp(&a, p->p.main_source);
|
||||||
e->net = net;
|
e->net = net;
|
||||||
e->u.krt.src = src;
|
|
||||||
e->u.krt.proto = src2;
|
ea_list *ea = alloca(sizeof(ea_list) + 1 * sizeof(eattr));
|
||||||
e->u.krt.seen = 0;
|
*ea = (ea_list) { .count = 1, .next = e->attrs->eattrs };
|
||||||
e->u.krt.best = 0;
|
e->attrs->eattrs = ea;
|
||||||
e->u.krt.metric = 0;
|
|
||||||
|
ea->attrs[0] = (eattr) {
|
||||||
|
.id = EA_KRT_SOURCE,
|
||||||
|
.type = EAF_TYPE_INT,
|
||||||
|
.u.data = src2,
|
||||||
|
};
|
||||||
|
|
||||||
if (scan)
|
if (scan)
|
||||||
krt_got_route(p, e);
|
krt_got_route(p, e, src);
|
||||||
else
|
else
|
||||||
krt_got_route_async(p, e, new);
|
krt_got_route_async(p, e, new, src);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -1198,7 +1206,7 @@ kif_update_sysdep_addr(struct iface *i)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
ip4_addr old = i->sysdep;
|
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);
|
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_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_SELF_CONSCIOUS We're able to recognize whether route was installed by us
|
||||||
CONFIG_MULTIPLE_TABLES The kernel supports multiple routing tables
|
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_SINGLE_ROUTE There is only one route per network
|
||||||
|
|
||||||
CONFIG_MC_PROPER_SRC Multicast packets have source address according to socket saddr field
|
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
|
||||||
|
*/
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue