Compare commits
218 Commits
v2.0.5
...
burble.dn4
Author | SHA1 | Date | |
---|---|---|---|
256e198fee | |||
18853a82c6 | |||
e6133456c1 | |||
b86d3f9a2e | |||
910f9127f0 | |||
ce7a2736e9 | |||
397c52070a | |||
|
4aecc5f8cd | ||
|
3155bb34be | ||
|
82f19ba95e | ||
|
f1ffe6a231 | ||
|
5a6e8380f8 | ||
|
454ae30445 | ||
|
0a3db4c680 | ||
|
2f98153490 | ||
|
ae9ae864d3 | ||
|
94abefc00b | ||
|
0d1a11cca3 | ||
|
6489a2450e | ||
|
7be3af7fa6 | ||
|
9cf3d53311 | ||
|
211fe69c98 | ||
|
d3782c72b9 | ||
|
77ce849ecf | ||
|
714238716e | ||
|
00b85905b9 | ||
|
d06a875b04 | ||
|
5d414309ec | ||
|
df83f62697 | ||
|
a40ddf5c61 | ||
|
d774f6d721 | ||
|
910adaa08b | ||
|
17663b6a7c | ||
|
2a8cc7259e | ||
|
a141959f07 | ||
|
7a1f4baac1 | ||
|
4155104c90 | ||
|
21f9acd2a0 | ||
|
455c13dc99 | ||
|
ea3c6c1a15 | ||
|
9e2635505a | ||
|
61dae32b29 | ||
|
8cc5bb09e3 | ||
|
2465867712 | ||
|
62d57b9bdf | ||
|
0ef082c51e | ||
|
30b8468269 | ||
|
1678bc0746 | ||
|
c9ae81656f | ||
|
db2d29073a | ||
|
3347aaafec | ||
|
df65d519d6 | ||
|
00ddd18b02 | ||
|
6ea8a46ccb | ||
|
b962967e20 | ||
|
4a42e7e925 | ||
|
3b56bf8849 | ||
|
99ad208dd7 | ||
|
9d3fc3062b | ||
|
fc1e3211b1 | ||
|
6c11dbcf28 | ||
|
14ce8904e7 | ||
|
600eb695b1 | ||
|
dc8d9dec4a | ||
|
c0e1f534c9 | ||
|
c26c6bc2d7 | ||
|
a948cf9a5c | ||
|
4ef0a96639 | ||
|
82937b465b | ||
|
71e08edd94 | ||
|
63451c1961 | ||
|
f1b5f179db | ||
|
19f8f17320 | ||
|
fae5448134 | ||
|
eee8af4db2 | ||
|
4e8f8afc68 | ||
|
c1632ad0f3 | ||
|
1ca7665fa4 | ||
|
b729e731f9 | ||
|
ec430a7fee | ||
|
5fc8407177 | ||
|
b8bbbbaf56 | ||
|
f7c34aa227 | ||
|
e6785c469b | ||
|
82bfee76f0 | ||
|
b12442c985 | ||
|
048eb2ddf1 | ||
|
59238768b3 | ||
|
ea259d6201 | ||
|
0c3b8ffe25 | ||
|
cdde3550dc | ||
|
9ac13d7af2 | ||
|
e26a5195dd | ||
|
3bb10b4d31 | ||
|
258be56539 | ||
|
a7d9b8f116 | ||
|
0fa8bf91cd | ||
|
bbe49ae569 | ||
|
a08853a269 | ||
|
5f60d14ede | ||
|
b748220906 | ||
|
9e64ac4b7c | ||
|
dccee40826 | ||
|
baac700906 | ||
|
a0d0a71a18 | ||
|
a1b61a271a | ||
|
d65a926a67 | ||
|
30ba7c1661 | ||
|
bf9486bf20 | ||
|
17de3a023f | ||
|
8029ae527e | ||
|
d607205486 | ||
|
124d860f64 | ||
|
59a86cbc7c | ||
|
b465604eb1 | ||
|
716e11a584 | ||
|
3c838ad9fd | ||
|
a6548d5b5b | ||
|
fd9f0c0640 | ||
|
a109056145 | ||
|
2928c5bcc7 | ||
|
c9d11e6230 | ||
|
2755002890 | ||
|
d516c68ad8 | ||
|
dc042d87cb | ||
|
e2630a494e | ||
|
1ad98965c5 | ||
|
ead531ffef | ||
|
e6746da6de | ||
|
78e4a123bb | ||
|
757cab18d6 | ||
|
22c3cf955d | ||
|
3343088a71 | ||
|
ab089f4fb5 | ||
|
027a3e66f7 | ||
|
4bbc10614f | ||
|
7f9adafc10 | ||
|
9f2670277c | ||
|
7d767c5a3d | ||
|
ef8c45749c | ||
|
cc75b3e1dc | ||
|
90a9c97e38 | ||
|
3dabf7b8d0 | ||
|
3232d17186 | ||
|
c132acae36 | ||
|
d3aa4f2aed | ||
|
dfb3eb7716 | ||
|
4ab54f1aef | ||
|
ff2ca10cba | ||
|
21d09632a5 | ||
|
92249894b3 | ||
|
0adfa0ec07 | ||
|
6a314d26cb | ||
|
148bd9ee92 | ||
|
faa43a755e | ||
|
5176455f1a | ||
|
5ea39eaa96 | ||
|
af02b83b88 | ||
|
d033e6327d | ||
|
0f88200247 | ||
|
53401bef63 | ||
|
0b228fca04 | ||
|
becda5638a | ||
|
d54a69ac7f | ||
|
10c4cd9677 | ||
|
87512e9751 | ||
|
c00c20a799 | ||
|
26194bd684 | ||
|
6fbcd8914a | ||
|
a52476c9be | ||
|
0edf0c8cd9 | ||
|
08c4c9a30b | ||
|
be7c1aef42 | ||
|
498d8145c0 | ||
|
ec331acf48 | ||
|
5ce881be82 | ||
|
f9eb9b4cab | ||
|
4e23b49969 | ||
|
b000a94275 | ||
|
6c9cda6f92 | ||
|
eeb2c61653 | ||
|
843b10c8b0 | ||
|
9eace84342 | ||
|
cb2b6e0494 | ||
|
09ee846d92 | ||
|
759b204be3 | ||
|
cc95b4594a | ||
|
d6eea6caee | ||
|
368f70604f | ||
|
6dda6931d1 | ||
|
15a7583787 | ||
|
c41a914d6e | ||
|
24493e9169 | ||
|
4821251ebb | ||
|
ca2dacfcee | ||
|
ea0917bcba | ||
|
9c79022153 | ||
|
3f477ccb03 | ||
|
eb1e43a9af | ||
|
5235c3f78d | ||
|
532471967e | ||
|
452e90ba72 | ||
|
1127887a8b | ||
|
8388f5a7e1 | ||
|
56d8b1e7f6 | ||
|
32a254050d | ||
|
5ab3447de1 | ||
|
4fa0e472cf | ||
|
524d253853 | ||
|
a297a4f044 | ||
|
b7d7599ce3 | ||
|
dfe63ed84d | ||
|
70a4320bdd | ||
|
9f3e098320 | ||
|
ef113c6f72 | ||
|
e2b530aa72 | ||
|
f6a6a77640 | ||
|
8c42205e35 |
57
.drone.yml
Normal file
57
.drone.yml
Normal file
@ -0,0 +1,57 @@
|
||||
---
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: deploy
|
||||
|
||||
steps:
|
||||
|
||||
- name: build alpine
|
||||
image: c8n.io/simonburblecom/bird-build:alpine
|
||||
commands:
|
||||
- /build.sh
|
||||
|
||||
- name: build ubuntu
|
||||
image: c8n.io/simonburblecom/bird-build:ubuntu
|
||||
commands:
|
||||
- /build.sh
|
||||
|
||||
- name: upload
|
||||
image: plugins/s3
|
||||
settings:
|
||||
bucket: artifacts
|
||||
access_key:
|
||||
from_secret: MINIO_ACCESS_KEY
|
||||
secret_key:
|
||||
from_secret: MINIO_SECRET_KEY
|
||||
endpoint: https://minio.burble.dn42
|
||||
region: uk-lon3
|
||||
path_style: true
|
||||
source: artifacts/**/*
|
||||
strip_prefix: artifacts/
|
||||
target: /bird/${DRONE_BRANCH}
|
||||
|
||||
|
||||
image_pull_secrets:
|
||||
- CONFIG_JSON
|
||||
|
||||
---
|
||||
kind: secret
|
||||
name: CONFIG_JSON
|
||||
get:
|
||||
path: burble.dn42/kv/data/drone/docker
|
||||
name: configjson
|
||||
|
||||
---
|
||||
kind: secret
|
||||
name: MINIO_ACCESS_KEY
|
||||
get:
|
||||
path: burble.dn42/kv/data/drone/minio
|
||||
name: ACCESS_KEY
|
||||
|
||||
---
|
||||
kind: secret
|
||||
name: MINIO_SECRET_KEY
|
||||
get:
|
||||
path: burble.dn42/kv/data/drone/minio
|
||||
name: SECRET_KEY
|
||||
|
456
.gitlab-ci.yml
456
.gitlab-ci.yml
@ -4,10 +4,12 @@ variables:
|
||||
GIT_STRATEGY: fetch
|
||||
DOCKER_CMD: docker --config="$HOME/.docker/$CI_JOB_ID/"
|
||||
IMG_BASE: registry.labs.nic.cz/labs/bird
|
||||
TOOLS_DIR: /var/lib/gitlab-runner/bird-tools
|
||||
|
||||
stages:
|
||||
- image
|
||||
- build
|
||||
- test
|
||||
|
||||
.docker: &docker_build
|
||||
stage: image
|
||||
@ -34,36 +36,46 @@ docker_debian-7-amd64:
|
||||
IMG_NAME: "debian-7-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-8-amd64:
|
||||
variables:
|
||||
IMG_NAME: "debian-8-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-9-amd64:
|
||||
variables:
|
||||
IMG_NAME: "debian-9-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-testing-amd64:
|
||||
variables:
|
||||
IMG_NAME: "debian-testing-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-7-i386:
|
||||
variables:
|
||||
IMG_NAME: "debian-7-i386"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-8-amd64:
|
||||
variables:
|
||||
IMG_NAME: "debian-8-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-8-i386:
|
||||
variables:
|
||||
IMG_NAME: "debian-8-i386"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-9-amd64:
|
||||
variables:
|
||||
IMG_NAME: "debian-9-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-9-i386:
|
||||
variables:
|
||||
IMG_NAME: "debian-9-i386"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-10-amd64:
|
||||
variables:
|
||||
IMG_NAME: "debian-10-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-10-i386:
|
||||
variables:
|
||||
IMG_NAME: "debian-10-i386"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-testing-amd64:
|
||||
variables:
|
||||
IMG_NAME: "debian-testing-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_debian-testing-i386:
|
||||
variables:
|
||||
IMG_NAME: "debian-testing-i386"
|
||||
@ -79,14 +91,39 @@ docker_fedora-26-amd64:
|
||||
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_centos-7-amd64:
|
||||
variables:
|
||||
IMG_NAME: "centos-7-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_opensuse-42_3-amd64:
|
||||
docker_centos-8-amd64:
|
||||
variables:
|
||||
IMG_NAME: "opensuse-42.3-amd64"
|
||||
IMG_NAME: "centos-8-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
docker_ubuntu-14_04-amd64:
|
||||
@ -99,105 +136,28 @@ docker_ubuntu-16_04-amd64:
|
||||
IMG_NAME: "ubuntu-16.04-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
.debian-7-i386: &debian-7-i386_env
|
||||
image: registry.labs.nic.cz/labs/bird:debian-7-i386
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
docker_ubuntu-18_04-amd64:
|
||||
variables:
|
||||
IMG_NAME: "ubuntu-18.04-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
.debian-8-i386: &debian-8-i386_env
|
||||
image: registry.labs.nic.cz/labs/bird:debian-8-i386
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
docker_ubuntu-19_10-amd64:
|
||||
variables:
|
||||
IMG_NAME: "ubuntu-19.10-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
.debian-9-i386: &debian-9-i386_env
|
||||
image: registry.labs.nic.cz/labs/bird:debian-9-i386
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
docker_opensuse-15.0-amd64:
|
||||
variables:
|
||||
IMG_NAME: "opensuse-15.0-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
.debian-testing-i386: &debian-testing-i386_env
|
||||
image: registry.labs.nic.cz/labs/bird:debian-testing-i386
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
docker_opensuse-15.1-amd64:
|
||||
variables:
|
||||
IMG_NAME: "opensuse-15.1-amd64"
|
||||
<<: *docker_build
|
||||
|
||||
.debian-7-amd64: &debian-7-amd64_env
|
||||
image: registry.labs.nic.cz/labs/bird:debian-7-amd64
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
.debian-8-amd64: &debian-8-amd64_env
|
||||
image: registry.labs.nic.cz/labs/bird:debian-8-amd64
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
.debian-9-amd64: &debian-9-amd64_env
|
||||
image: registry.labs.nic.cz/labs/bird:debian-9-amd64
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
.debian-testing-amd64: &debian-testing-amd64_env
|
||||
image: registry.labs.nic.cz/labs/bird:debian-testing-amd64
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
.fedora-25-amd64: &fedora-25-amd64_env
|
||||
image: registry.labs.nic.cz/labs/bird:fedora-25-amd64
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
.fedora-26-amd64: &fedora-26-amd64_env
|
||||
image: registry.labs.nic.cz/labs/bird:fedora-26-amd64
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
.centos-7-amd64: ¢os-7-amd64_env
|
||||
image: registry.labs.nic.cz/labs/bird:centos-7-amd64
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
.opensuse-42_3-amd64: &opensuse-42_3-amd64_env
|
||||
image: registry.labs.nic.cz/labs/bird:opensuse-42.3-amd64
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
.ubuntu-14_04-amd64: &ubuntu-14_04-amd64_env
|
||||
image: registry.labs.nic.cz/labs/bird:ubuntu-14.04-amd64
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
.ubuntu-16_04-amd64: &ubuntu-16_04-amd64_env
|
||||
image: registry.labs.nic.cz/labs/bird:ubuntu-16.04-amd64
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
# TODO We want to copy these BSDs to our own virtual machines, to make sure someone doesn't update them by accident.
|
||||
# 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
|
||||
@ -211,12 +171,8 @@ docker_ubuntu-16_04-amd64:
|
||||
tags:
|
||||
- freebsd
|
||||
- amd64
|
||||
#only:
|
||||
#- master
|
||||
#- triggers
|
||||
#- tags
|
||||
|
||||
.build: &build_job
|
||||
.build: &build-base
|
||||
stage: build
|
||||
script:
|
||||
- autoreconf
|
||||
@ -228,66 +184,232 @@ docker_ubuntu-16_04-amd64:
|
||||
# Run tests if they are available
|
||||
- $MAKE check
|
||||
|
||||
.build-linux: &build-linux
|
||||
<<: *build-base
|
||||
tags:
|
||||
- docker
|
||||
- linux
|
||||
- amd64
|
||||
|
||||
build-debian-7-amd64:
|
||||
<<: *debian-7-amd64_env
|
||||
<<: *build_job
|
||||
|
||||
build-debian-8-amd64:
|
||||
<<: *debian-8-amd64_env
|
||||
<<: *build_job
|
||||
|
||||
build-debian-9-amd64:
|
||||
<<: *debian-9-amd64_env
|
||||
<<: *build_job
|
||||
|
||||
build-debian-testing-amd64:
|
||||
<<: *debian-testing-amd64_env
|
||||
<<: *build_job
|
||||
|
||||
build-fedora-25-amd64:
|
||||
<<: *fedora-25-amd64_env
|
||||
<<: *build_job
|
||||
|
||||
build-fedora-26-amd64:
|
||||
<<: *fedora-26-amd64_env
|
||||
<<: *build_job
|
||||
|
||||
build-centos-7-amd64:
|
||||
<<: *centos-7-amd64_env
|
||||
<<: *build_job
|
||||
|
||||
build-opensuse-42_3-amd64:
|
||||
<<: *opensuse-42_3-amd64_env
|
||||
<<: *build_job
|
||||
|
||||
build-ubuntu-14_04-amd64:
|
||||
<<: *ubuntu-14_04-amd64_env
|
||||
<<: *build_job
|
||||
|
||||
build-ubuntu-16_04-amd64:
|
||||
<<: *ubuntu-16_04-amd64_env
|
||||
<<: *build_job
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:debian-7-amd64
|
||||
|
||||
build-debian-7-i386:
|
||||
<<: *debian-7-i386_env
|
||||
<<: *build_job
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:debian-7-i386
|
||||
|
||||
build-debian-8-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:debian-8-amd64
|
||||
|
||||
build-debian-8-i386:
|
||||
<<: *debian-8-i386_env
|
||||
<<: *build_job
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:debian-8-i386
|
||||
|
||||
build-debian-9-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:debian-9-amd64
|
||||
|
||||
build-debian-9-i386:
|
||||
<<: *debian-9-i386_env
|
||||
<<: *build_job
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:debian-9-i386
|
||||
|
||||
build-debian-10-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:debian-10-amd64
|
||||
|
||||
build-debian-10-i386:
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:debian-10-i386
|
||||
|
||||
build-debian-testing-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:debian-testing-amd64
|
||||
|
||||
build-debian-testing-i386:
|
||||
<<: *debian-testing-i386_env
|
||||
<<: *build_job
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:debian-testing-i386
|
||||
|
||||
build-fedora-25-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:fedora-25-amd64
|
||||
|
||||
build-fedora-26-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:fedora-26-amd64
|
||||
|
||||
build-fedora-27-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:fedora-27-amd64
|
||||
|
||||
build-fedora-28-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:fedora-28-amd64
|
||||
|
||||
build-fedora-29-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:fedora-29-amd64
|
||||
|
||||
build-fedora-30-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:fedora-30-amd64
|
||||
|
||||
build-fedora-31-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:fedora-31-amd64
|
||||
|
||||
build-centos-7-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:centos-7-amd64
|
||||
|
||||
build-centos-8-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:centos-8-amd64
|
||||
|
||||
build-ubuntu-14_04-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:ubuntu-14.04-amd64
|
||||
|
||||
build-ubuntu-16_04-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:ubuntu-16.04-amd64
|
||||
|
||||
build-ubuntu-18_04-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:ubuntu-18.04-amd64
|
||||
|
||||
build-ubuntu-19_04-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:ubuntu-19.04-amd64
|
||||
|
||||
build-opensuse-15.0-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:opensuse-15.0-amd64
|
||||
|
||||
build-opensuse-15.1-amd64:
|
||||
<<: *build-linux
|
||||
image: registry.labs.nic.cz/labs/bird:opensuse-15.1-amd64
|
||||
|
||||
build-freebsd-11-amd64:
|
||||
<<: *freebsd-11-amd64_env
|
||||
<<: *build_job
|
||||
<<: *build-base
|
||||
tags:
|
||||
- freebsd
|
||||
- amd64
|
||||
|
||||
build-freebsd-11-i386:
|
||||
<<: *freebsd-11-i386_env
|
||||
<<: *build_job
|
||||
<<: *build-base
|
||||
tags:
|
||||
- freebsd
|
||||
- i386
|
||||
|
||||
build-birdlab:
|
||||
stage: build
|
||||
tags:
|
||||
- birdlab
|
||||
- amd64
|
||||
script:
|
||||
- DIR=$(pwd)
|
||||
- autoreconf
|
||||
- ./configure
|
||||
- make
|
||||
- cd $TOOLS_DIR
|
||||
- sudo git clean -fx
|
||||
- git pull --ff-only
|
||||
- mv $DIR/bird $DIR/birdc netlab/common
|
||||
|
||||
.test: &test-base
|
||||
stage: test
|
||||
needs: [build-birdlab]
|
||||
tags:
|
||||
- birdlab
|
||||
- amd64
|
||||
script:
|
||||
- cd $TOOLS_DIR/netlab
|
||||
- sudo ./stop
|
||||
- sudo ./runtest -m check $TEST_NAME
|
||||
|
||||
test-ospf-base:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-ospf-base
|
||||
|
||||
test-ospf-default:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-ospf-default
|
||||
|
||||
test-ospf-priority:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-ospf-priority
|
||||
|
||||
test-ospf-nbma:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-ospf-nbma
|
||||
|
||||
test-ospf-ptmp:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-ospf-ptmp
|
||||
|
||||
test-ospf-authentication:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-ospf-authentication
|
||||
|
||||
test-ospf-bfd:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-ospf-bfd
|
||||
|
||||
test-ospf-custom:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-ospf-custom
|
||||
|
||||
test-ospf-vrf:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-ospf-vrf
|
||||
|
||||
test-bgp-base:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-bgp-base
|
||||
|
||||
test-bgp-auth:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-bgp-auth
|
||||
|
||||
test-bgp-int:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-bgp-int
|
||||
|
||||
test-bgp-merged:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-bgp-merged
|
||||
|
||||
test-ebgp-loop:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-ebgp-loop
|
||||
|
||||
test-ebgp-star:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-ebgp-star
|
||||
|
||||
test-ibgp-loop:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-ibgp-loop
|
||||
|
||||
test-ibgp-star:
|
||||
<<: *test-base
|
||||
variables:
|
||||
TEST_NAME: cf-ibgp-flat
|
||||
|
13
Makefile.in
13
Makefile.in
@ -184,11 +184,20 @@ check: tests tests_run
|
||||
tests: $(tests_targets)
|
||||
tests_run: $(tests_targets_ok)
|
||||
|
||||
STATIC_CHECKERS_ENABLE := nullability.NullableDereferenced nullability.NullablePassedToNonnull nullability.NullableReturnedFromNonnull optin.portability.UnixAPI valist.CopyToSelf valist.Uninitialized valist.Unterminated
|
||||
STATIC_CHECKERS_DISABLE := deadcode.DeadStores
|
||||
STATIC_SCAN_FLAGS := -o $(objdir)/static-scan/ $(addprefix -enable-checker ,$(STATIC_CHECKERS_ENABLE)) $(addprefix -disable-checker ,$(STATIC_CHECKERS_DISABLE))
|
||||
|
||||
static-scan:
|
||||
$(E)echo Running static code analysis
|
||||
$(Q)$(MAKE) clean
|
||||
$(Q)scan-build $(STATIC_SCAN_FLAGS) $(MAKE) -$(MAKEFLAGS)
|
||||
|
||||
tags:
|
||||
cd $(srcdir) ; etags -lc `find $(dirs) -name *.[chY]`
|
||||
cd $(srcdir) ; etags -lc `find $(dirs) -name '*.[chY]'`
|
||||
|
||||
cscope:
|
||||
cd $(srcdir) ; find $(dirs) -name *.[chY] > cscope.files ; cscope -b
|
||||
cd $(srcdir) ; find $(dirs) -name '*.[chY]' > cscope.files ; cscope -b
|
||||
|
||||
# Install
|
||||
|
||||
|
53
NEWS
53
NEWS
@ -1,3 +1,56 @@
|
||||
Version 2.0.8 (2021-03-18)
|
||||
o Automatic channel reloads based on RPKI changes
|
||||
o Multiple static routes with the same network
|
||||
o Use bitmaps to keep track of exported routes
|
||||
o Per-channel debug flags
|
||||
o CLI commands show info from multiple protocols
|
||||
o Linux: IPv4 routes with IPv6 nexthops
|
||||
o Filter: Optimized redesign of prefix sets
|
||||
o Filter: Improved type checking of user filters
|
||||
o Filter: New src/dst accessors for Flowspec and SADR
|
||||
o Filter: New 'weight' route attribute
|
||||
o Filter: BGP path mask loop operator
|
||||
o Filter: Remove quitbird command
|
||||
o RIP: Demand circuit support (RFC 2091)
|
||||
o BGP: New 'allow as sets' and 'enforce first as' options
|
||||
o BGP: Support for BGP hostname capability
|
||||
o BGP: Support for MD5SIG with dynamic BGP
|
||||
o BFD: Optional separation of IPv4 / IPv6 BFD instances
|
||||
o BFD: Per-peer session options
|
||||
o RPKI: Allow build without libSSH
|
||||
o RPKI: New 'ignore max length' option
|
||||
o OSPF: Redesign of handling of unnumbered PtPs
|
||||
o OSPF: Allow key id 0 in authentication
|
||||
o Babel: Use onlink flag for routes with unreachable next hop
|
||||
o Many bugfixes
|
||||
|
||||
Notes:
|
||||
|
||||
Automatic channel reloads based on RPKI changes are enabled by default,
|
||||
but require import table enabled when used in BGP import filter.
|
||||
|
||||
BIRD now uses bitmaps to keep track of exported routes instead of
|
||||
re-evaluation of export filters. That should improve speed and accuracy in
|
||||
route export handling during reconfiguration, but takes some more memory.
|
||||
|
||||
Per-channel debug logging and some CLI commands (like 'show ospf neighbors')
|
||||
defaulting to all protocol instances lead to some minor changes in log and
|
||||
CLI output. Caution is recommended when logs or CLI output are monitored by
|
||||
scripts.
|
||||
|
||||
|
||||
Version 2.0.7 (2019-10-11)
|
||||
o BGP: Accumulated IGP metric (RFC 7311)
|
||||
o Important filter reconfiguration bugfix
|
||||
o Several other bugfixes
|
||||
|
||||
Version 2.0.6 (2019-09-10)
|
||||
o RAdv: Solicited unicast RAs
|
||||
o BGP: Optional Adj-RIB-Out
|
||||
o BGP: Extended optional parameters length
|
||||
o Filter: Sets and set expressions in path masks
|
||||
o Several important bugfixes
|
||||
|
||||
Version 2.0.5 (2019-08-01)
|
||||
o OSPF Graceful restart (RFC 3623, RFC 5187)
|
||||
o BGP: Dynamic BGP
|
||||
|
@ -30,6 +30,7 @@ class BIRDFValPrinter(BIRDPrinter):
|
||||
"T_ENUM_ROA": "i",
|
||||
"T_ENUM_NETTYPE": "i",
|
||||
"T_ENUM_RA_PREFERENCE": "i",
|
||||
"T_ENUM_AF": "i",
|
||||
"T_IP": "ip",
|
||||
"T_NET": "net",
|
||||
"T_STRING": "s",
|
||||
|
@ -169,7 +169,7 @@ WHITE [ \t]
|
||||
|
||||
errno = 0;
|
||||
l = bstrtoul10(yytext, &e);
|
||||
if (e && (*e != ':') || (errno == ERANGE) || (l >> 32))
|
||||
if (!e || (*e != ':') || (errno == ERANGE) || (l >> 32))
|
||||
cf_error("ASN out of range");
|
||||
|
||||
if (l >> 16)
|
||||
@ -187,7 +187,7 @@ WHITE [ \t]
|
||||
|
||||
errno = 0;
|
||||
l = bstrtoul10(e+1, &e);
|
||||
if (e && *e || (errno == ERANGE) || (l >> len2))
|
||||
if (!e || *e || (errno == ERANGE) || (l >> len2))
|
||||
cf_error("Number out of range");
|
||||
cf_lval.i64 |= l;
|
||||
|
||||
@ -214,13 +214,13 @@ WHITE [ \t]
|
||||
|
||||
errno = 0;
|
||||
l = bstrtoul10(yytext+2, &e);
|
||||
if (e && (*e != ':') || (errno == ERANGE) || (l >> len1))
|
||||
if (!e || (*e != ':') || (errno == ERANGE) || (l >> len1))
|
||||
cf_error("ASN out of range");
|
||||
cf_lval.i64 |= ((u64) l) << len2;
|
||||
|
||||
errno = 0;
|
||||
l = bstrtoul10(e+1, &e);
|
||||
if (e && *e || (errno == ERANGE) || (l >> len2))
|
||||
if (!e || *e || (errno == ERANGE) || (l >> len2))
|
||||
cf_error("Number out of range");
|
||||
cf_lval.i64 |= l;
|
||||
|
||||
@ -242,7 +242,7 @@ WHITE [ \t]
|
||||
|
||||
errno = 0;
|
||||
l = bstrtoul10(e, &e);
|
||||
if (e && *e || (errno == ERANGE) || (l >> 16))
|
||||
if (!e || *e || (errno == ERANGE) || (l >> 16))
|
||||
cf_error("Number out of range");
|
||||
cf_lval.i64 |= l;
|
||||
|
||||
@ -266,7 +266,7 @@ WHITE [ \t]
|
||||
unsigned long int l;
|
||||
errno = 0;
|
||||
l = bstrtoul16(yytext+2, &e);
|
||||
if (e && *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l)
|
||||
if (!e || *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l)
|
||||
cf_error("Number out of range");
|
||||
cf_lval.i = l;
|
||||
return NUM;
|
||||
@ -277,7 +277,7 @@ WHITE [ \t]
|
||||
unsigned long int l;
|
||||
errno = 0;
|
||||
l = bstrtoul10(yytext, &e);
|
||||
if (e && *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l)
|
||||
if (!e || *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l)
|
||||
cf_error("Number out of range");
|
||||
cf_lval.i = l;
|
||||
return NUM;
|
||||
@ -737,7 +737,7 @@ cf_lex_init(int is_cli, struct config *c)
|
||||
void
|
||||
cf_push_scope(struct symbol *sym)
|
||||
{
|
||||
struct sym_scope *s = cfg_alloc(sizeof(struct sym_scope));
|
||||
struct sym_scope *s = cfg_allocz(sizeof(struct sym_scope));
|
||||
|
||||
s->next = conf_this_scope;
|
||||
conf_this_scope = s;
|
||||
|
10
conf/conf.c
10
conf/conf.c
@ -55,6 +55,7 @@
|
||||
#include "lib/timer.h"
|
||||
#include "conf/conf.h"
|
||||
#include "filter/filter.h"
|
||||
#include "sysdep/unix/unix.h"
|
||||
|
||||
|
||||
static jmp_buf conf_jmpbuf;
|
||||
@ -217,6 +218,14 @@ config_del_obstacle(struct config *c)
|
||||
static int
|
||||
global_commit(struct config *new, struct config *old)
|
||||
{
|
||||
if (!new->hostname)
|
||||
{
|
||||
new->hostname = get_hostname(new->mem);
|
||||
|
||||
if (!new->hostname)
|
||||
log(L_WARN "Cannot determine hostname");
|
||||
}
|
||||
|
||||
if (!old)
|
||||
return 0;
|
||||
|
||||
@ -573,6 +582,7 @@ cfg_copy_list(list *dest, list *src, unsigned node_size)
|
||||
{
|
||||
dn = cfg_alloc(node_size);
|
||||
memcpy(dn, sn, node_size);
|
||||
memset(dn, 0, sizeof(node));
|
||||
add_tail(dest, dn);
|
||||
}
|
||||
}
|
||||
|
18
conf/conf.h
18
conf/conf.h
@ -27,18 +27,20 @@ struct config {
|
||||
list symbols; /* Configured symbols in config order */
|
||||
|
||||
int mrtdump_file; /* Configured MRTDump file (sysdep, fd in unix) */
|
||||
char *syslog_name; /* Name used for syslog (NULL -> no syslog) */
|
||||
const char *syslog_name; /* Name used for syslog (NULL -> no syslog) */
|
||||
struct rtable_config *def_tables[NET_MAX]; /* Default routing tables for each network */
|
||||
struct iface_patt *router_id_from; /* Configured list of router ID iface patterns */
|
||||
|
||||
u32 router_id; /* Our Router ID */
|
||||
unsigned proto_default_debug; /* Default protocol debug mask */
|
||||
unsigned proto_default_mrtdump; /* Default protocol mrtdump mask */
|
||||
u32 proto_default_debug; /* Default protocol debug mask */
|
||||
u32 proto_default_mrtdump; /* Default protocol mrtdump mask */
|
||||
u32 channel_default_debug; /* Default channel debug mask */
|
||||
struct timeformat tf_route; /* Time format for 'show route' */
|
||||
struct timeformat tf_proto; /* Time format for 'show protocol' */
|
||||
struct timeformat tf_log; /* Time format for the logfile */
|
||||
struct timeformat tf_base; /* Time format for other purposes */
|
||||
u32 gr_wait; /* Graceful restart wait timeout (sec) */
|
||||
const char *hostname; /* Hostname */
|
||||
|
||||
int cli_debug; /* Tracing of CLI connections and commands */
|
||||
int latency_debug; /* I/O loop tracks duration of each event */
|
||||
@ -198,11 +200,11 @@ struct symbol *cf_localize_symbol(struct symbol *sym);
|
||||
* Result: Pointer to the newly defined symbol. If we are in the top-level
|
||||
* scope, it's the same @sym as passed to the function.
|
||||
*/
|
||||
#define cf_define_symbol(sym_, type_, var_, def_) ({ \
|
||||
struct symbol *sym = cf_localize_symbol(sym_); \
|
||||
sym->class = type_; \
|
||||
sym->var_ = def_; \
|
||||
sym; })
|
||||
#define cf_define_symbol(osym_, type_, var_, def_) ({ \
|
||||
struct symbol *sym_ = cf_localize_symbol(osym_); \
|
||||
sym_->class = type_; \
|
||||
sym_->var_ = def_; \
|
||||
sym_; })
|
||||
|
||||
void cf_push_scope(struct symbol *);
|
||||
void cf_pop_scope(void);
|
||||
|
@ -19,6 +19,7 @@ CF_HDR
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/iface.h"
|
||||
#include "nest/route.h"
|
||||
#include "nest/bfd.h"
|
||||
#include "nest/cli.h"
|
||||
#include "filter/filter.h"
|
||||
|
||||
@ -60,9 +61,10 @@ CF_DECLS
|
||||
net_addr net;
|
||||
net_addr *net_ptr;
|
||||
struct symbol *s;
|
||||
char *t;
|
||||
const char *t;
|
||||
struct rtable_config *r;
|
||||
struct channel_config *cc;
|
||||
struct channel *c;
|
||||
struct f_inst *x;
|
||||
struct {
|
||||
struct f_inst *begin, *end;
|
||||
@ -147,7 +149,7 @@ conf: definition ;
|
||||
|
||||
definition:
|
||||
DEFINE symbol '=' term ';' {
|
||||
struct f_val *val = cfg_alloc(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");
|
||||
cf_define_symbol($2, SYM_CONSTANT | val->type, val, val);
|
||||
}
|
||||
|
@ -34,9 +34,11 @@ m4_define(CF_CLI, `CF_KEYWORDS(m4_translit($1, [[ ]], [[,]]))
|
||||
')
|
||||
|
||||
# Enums are translated to C initializers: use CF_ENUM(typename, prefix, values)
|
||||
m4_define(CF_enum, `m4_divert(1){ "CF_enum_prefix[[]]$1", -((CF_enum_type<<16) | CF_enum_prefix[[]]$1), NULL },
|
||||
# For different prefix: CF_ENUM_PX(typename, external prefix, C prefix, values)
|
||||
m4_define(CF_enum, `m4_divert(1){ "CF_enum_prefix_ext[[]]$1", -((CF_enum_type<<16) | CF_enum_prefix_int[[]]$1), NULL },
|
||||
m4_divert(-1)')
|
||||
m4_define(CF_ENUM, `m4_define([[CF_enum_type]],$1)m4_define([[CF_enum_prefix]],$2)CF_iterate([[CF_enum]], [[m4_shift(m4_shift($@))]])DNL')
|
||||
m4_define(CF_ENUM, `m4_define([[CF_enum_type]],$1)m4_define([[CF_enum_prefix_ext]],$2)m4_define([[CF_enum_prefix_int]],$2)CF_iterate([[CF_enum]], [[m4_shift(m4_shift($@))]])DNL')
|
||||
m4_define(CF_ENUM_PX, `m4_define([[CF_enum_type]],$1)m4_define([[CF_enum_prefix_ext]],$2)m4_define([[CF_enum_prefix_int]],$3)CF_iterate([[CF_enum]], [[m4_shift(m4_shift(m4_shift($@)))]])DNL')
|
||||
|
||||
# After all configuration templates end, we generate the
|
||||
m4_m4wrap(`
|
||||
|
@ -44,6 +44,7 @@ m4_define(CF_CLI_HELP, `')
|
||||
|
||||
# ENUM declarations are ignored
|
||||
m4_define(CF_ENUM, `')
|
||||
m4_define(CF_ENUM_PX, `')
|
||||
|
||||
# After all configuration templates end, we finally generate the grammar file.
|
||||
m4_m4wrap(`
|
||||
|
48
configure.ac
48
configure.ac
@ -24,6 +24,12 @@ AC_ARG_ENABLE([debug-generated],
|
||||
[enable_debug_generated=no]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE([debug-expensive],
|
||||
[AS_HELP_STRING([--enable-debug-expensive], [enable expensive consistency checks (implies --enable-debug) @<:@no@:>@])],
|
||||
[],
|
||||
[enable_debug_expensive=no]
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE([memcheck],
|
||||
[AS_HELP_STRING([--enable-memcheck], [check memory allocations when debugging @<:@yes@:>@])],
|
||||
[],
|
||||
@ -37,7 +43,7 @@ AC_ARG_ENABLE([pthreads],
|
||||
)
|
||||
|
||||
AC_ARG_ENABLE([libssh],
|
||||
[AS_HELP_STRING([--enable-libssh], [enable LibSSH support together with RPKI @<:@try@:>@])],
|
||||
[AS_HELP_STRING([--enable-libssh], [enable LibSSH support in RPKI @<:@try@:>@])],
|
||||
[],
|
||||
[enable_libssh=try]
|
||||
)
|
||||
@ -72,6 +78,9 @@ AC_ARG_VAR([FLEX], [location of the Flex program])
|
||||
AC_ARG_VAR([BISON], [location of the Bison program])
|
||||
AC_ARG_VAR([M4], [location of the M4 program])
|
||||
|
||||
if test "$enable_debug_expensive" = yes; then
|
||||
enable_debug=yes
|
||||
fi
|
||||
|
||||
if test "$srcdir" = . ; then
|
||||
# Building in current directory => create obj directory holding all objects
|
||||
@ -137,27 +146,29 @@ if test "$enable_pthreads" != no ; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# This is assumed to be necessary for proper BIRD build
|
||||
CFLAGS="$CFLAGS -fno-strict-aliasing -fno-strict-overflow"
|
||||
|
||||
if test "$bird_cflags_default" = yes ; then
|
||||
BIRD_CHECK_GCC_OPTION([bird_cv_c_option_wno_pointer_sign], [-Wno-pointer-sign], [-Wall])
|
||||
BIRD_CHECK_GCC_OPTION([bird_cv_c_option_wno_missing_init], [-Wno-missing-field-initializers], [-Wall -Wextra])
|
||||
BIRD_CHECK_GCC_OPTION([bird_cv_c_option_fno_strict_aliasing], [-fno-strict-aliasing])
|
||||
BIRD_CHECK_GCC_OPTION([bird_cv_c_option_fno_strict_overflow], [-fno-strict-overflow])
|
||||
|
||||
if test "$enable_debug" = no; then
|
||||
BIRD_CHECK_LTO
|
||||
fi
|
||||
|
||||
if test "$bird_cv_c_lto" = yes; then
|
||||
CFLAGS="$CFLAGS -flto"
|
||||
LDFLAGS="$LDFLAGS -flto=4 -g"
|
||||
else
|
||||
LDFLAGS="$LDFLAGS -g"
|
||||
fi
|
||||
|
||||
CFLAGS="$CFLAGS -Wall -Wextra -Wstrict-prototypes -Wno-parentheses"
|
||||
BIRD_ADD_GCC_OPTION([bird_cv_c_option_wno_pointer_sign], [-Wno-pointer-sign])
|
||||
BIRD_ADD_GCC_OPTION([bird_cv_c_option_wno_missing_init], [-Wno-missing-field-initializers])
|
||||
BIRD_ADD_GCC_OPTION([bird_cv_c_option_fno_strict_aliasing], [-fno-strict-aliasing])
|
||||
BIRD_ADD_GCC_OPTION([bird_cv_c_option_fno_strict_overflow], [-fno-strict-overflow])
|
||||
fi
|
||||
|
||||
if test "$enable_debug" = no; then
|
||||
BIRD_CHECK_LTO
|
||||
fi
|
||||
|
||||
if test "$bird_cv_c_lto" = yes; then
|
||||
CFLAGS="$CFLAGS -flto"
|
||||
LDFLAGS="$LDFLAGS -flto=4"
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING([CFLAGS])
|
||||
AC_MSG_RESULT([$CFLAGS])
|
||||
@ -269,7 +280,6 @@ if test "$enable_libssh" != no ; then
|
||||
if test "$fail" != yes ; then
|
||||
AC_DEFINE([HAVE_LIBSSH], [1], [Define to 1 if you have the `ssh' library (-lssh).])
|
||||
DAEMON_LIBS="-lssh $DAEMON_LIBS"
|
||||
proto_rpki=rpki
|
||||
enable_libssh=yes
|
||||
else
|
||||
if test "$enable_libssh" = yes ; then
|
||||
@ -294,7 +304,7 @@ if test "$enable_mpls_kernel" != no ; then
|
||||
fi
|
||||
fi
|
||||
|
||||
all_protocols="$proto_bfd babel bgp mrt ospf perf pipe radv rip $proto_rpki static"
|
||||
all_protocols="$proto_bfd babel bgp mrt ospf perf pipe radv rip rpki static"
|
||||
|
||||
all_protocols=`echo $all_protocols | sed 's/ /,/g'`
|
||||
|
||||
@ -387,9 +397,10 @@ if test "$enable_debug" = yes ; then
|
||||
AC_CHECK_LIB([efence], [malloc])
|
||||
fi
|
||||
fi
|
||||
else
|
||||
BIRD_CHECK_GCC_OPTION([bird_cv_c_option_wno_implicit_fallthrough], [-Wno-implicit-fallthrough])
|
||||
BIRD_ADD_GCC_OPTION([bird_cv_c_option_wno_implicit_fallthrough], [-Wno-implicit-fallthrough])
|
||||
|
||||
if test "enable_debug_expensive" = yes ; then
|
||||
AC_DEFINE([ENABLE_EXPENSIVE_CHECKS], [1], [Define to 1 if you want to run expensive consistency checks.])
|
||||
fi
|
||||
fi
|
||||
|
||||
CLIENT=birdcl
|
||||
@ -454,6 +465,7 @@ AC_MSG_RESULT([ System configuration: $sysdesc])
|
||||
AC_MSG_RESULT([ Debugging: $enable_debug])
|
||||
AC_MSG_RESULT([ POSIX threads: $enable_pthreads])
|
||||
AC_MSG_RESULT([ Routing protocols: $protocols])
|
||||
AC_MSG_RESULT([ LibSSH support in RPKI: $enable_libssh])
|
||||
AC_MSG_RESULT([ Kernel MPLS support: $enable_mpls_kernel])
|
||||
AC_MSG_RESULT([ Client: $enable_client])
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* This is an example configuration file for MB-BGP setting
|
||||
* This is an example configuration file for MP-BGP setting
|
||||
*/
|
||||
|
||||
|
||||
|
441
doc/bird.sgml
441
doc/bird.sgml
@ -430,11 +430,11 @@ a comment, whitespace characters are treated as a single space. If there's a
|
||||
variable number of options, they are grouped using the <cf/{ }/ brackets. Each
|
||||
option is terminated by a <cf/;/. Configuration is case sensitive. There are two
|
||||
ways how to name symbols (like protocol names, filter names, constants etc.).
|
||||
You can either use a simple string starting with a letter followed by any
|
||||
combination of letters and numbers (e.g. <cf/R123/, <cf/myfilter/, <cf/bgp5/) or
|
||||
you can enclose the name into apostrophes (<cf/'/) and than you can use any
|
||||
combination of numbers, letters. hyphens, dots and colons (e.g.
|
||||
<cf/'1:strange-name'/, <cf/'-NAME-'/, <cf/'cool::name'/).
|
||||
You can either use a simple string starting with a letter (or underscore)
|
||||
followed by any combination of letters, numbers and underscores (e.g. <cf/R123/,
|
||||
<cf/my_filter/, <cf/bgp5/) or you can enclose the name into apostrophes (<cf/'/)
|
||||
and than you can use any combination of numbers, letters, underscores, hyphens,
|
||||
dots and colons (e.g. <cf/'1:strange-name'/, <cf/'-NAME-'/, <cf/'cool::name'/).
|
||||
|
||||
<p>Here is an example of a simple config file. It enables synchronization of
|
||||
routing tables with OS kernel, learns network interfaces and runs RIP on all
|
||||
@ -504,8 +504,14 @@ include "tablename.conf";;
|
||||
command-line option.
|
||||
|
||||
<tag><label id="opt-debug-protocols">debug protocols all|off|{ states|routes|filters|interfaces|events|packets [, <m/.../] }</tag>
|
||||
Set global defaults of protocol debugging options. See <cf/debug/ in the
|
||||
following section. Default: off.
|
||||
Set global defaults of protocol debugging options.
|
||||
See <ref id="proto-debug" name="debug"> in the following section.
|
||||
Default: off.
|
||||
|
||||
<tag><label id="opt-debug-channels">debug channels all|off|{ states|routes|filters|events [, <m/.../] }</tag>
|
||||
Set global defaults of channel debugging options.
|
||||
See <ref id="channel-debug" name="debug"> in the channel section.
|
||||
Default: off.
|
||||
|
||||
<tag><label id="opt-debug-commands">debug commands <m/number/</tag>
|
||||
Control logging of client connections (0 for no logging, 1 for logging
|
||||
@ -570,7 +576,7 @@ include "tablename.conf";;
|
||||
|
||||
<tag><label id="opt-attribute">attribute <m/type/ <m/name/</tag>
|
||||
Declare a custom route attribute. You can set and get it in filters like
|
||||
any other route atribute. This feature is intended for marking routes
|
||||
any other route attribute. This feature is intended for marking routes
|
||||
in import filters for export filtering purposes instead of locally
|
||||
assigned BGP communities which have to be deleted in export filters.
|
||||
|
||||
@ -585,6 +591,9 @@ include "tablename.conf";;
|
||||
See <ref id="proto-iface" name="interface"> section for detailed
|
||||
description of interface patterns with extended clauses.
|
||||
|
||||
<tag><label id="opt-hostname">hostname "<m/name/"</tag>
|
||||
Set hostname. Default: node name as returned by `uname -n'.
|
||||
|
||||
<tag><label id="opt-graceful-restart">graceful restart wait <m/number/</tag>
|
||||
During graceful restart recovery, BIRD waits for convergence of routing
|
||||
protocols. This option allows to specify a timeout for the recovery to
|
||||
@ -654,12 +663,14 @@ agreement").
|
||||
Set protocol debugging options. If asked, each protocol is capable of
|
||||
writing trace messages about its work to the log (with category
|
||||
<cf/trace/). You can either request printing of <cf/all/ trace messages
|
||||
or only of the types selected: <cf/states/ for protocol state changes
|
||||
or only of the selected types: <cf/states/ for protocol state changes
|
||||
(protocol going up, down, starting, stopping etc.), <cf/routes/ for
|
||||
routes exchanged with the routing table, <cf/filters/ for details on
|
||||
route filtering, <cf/interfaces/ for interface change events sent to the
|
||||
protocol, <cf/events/ for events internal to the protocol and <cf/packets/
|
||||
for packets sent and received by the protocol. Default: off.
|
||||
route filtering, <cf/interfaces/ for interface change events sent to
|
||||
the protocol, <cf/events/ for events internal to the protocol and
|
||||
<cf/packets/ for packets sent and received by the protocol. Classes
|
||||
<cf/routes/ and <cf/filters/ can be also set per-channel using
|
||||
<ref id="channel-debug" name="channel debugging option">) Default: off.
|
||||
|
||||
<tag><label id="proto-mrtdump">mrtdump all|off|{ states|messages [, <m/.../] }</tag>
|
||||
Set protocol MRTdump flags. MRTdump is a standard binary format for
|
||||
@ -748,10 +759,6 @@ agreement").
|
||||
on all interfaces that have address from 192.168.0.0/16, but not from
|
||||
192.168.1.0/24.
|
||||
|
||||
<cf>interface -192.168.1.0/24, 192.168.0.0/16;</cf> - start the protocol
|
||||
on all interfaces that have address from 192.168.0.0/16, but not from
|
||||
192.168.1.0/24.
|
||||
|
||||
<cf>interface "eth*" 192.168.1.0/24;</cf> - start the protocol on all
|
||||
ethernet interfaces that have address from 192.168.1.0/24.
|
||||
|
||||
@ -787,11 +794,12 @@ agreement").
|
||||
|
||||
<descrip>
|
||||
<tag><label id="proto-pass-id">id <M>num</M></tag>
|
||||
ID of the password, (1-255). If it is not used, BIRD will choose ID based
|
||||
on an order of the password item in the interface. For example, second
|
||||
password item in one interface will have default ID 2. ID is used by
|
||||
some routing protocols to identify which password was used to
|
||||
authenticate protocol packets.
|
||||
ID of the password, (0-255). If it is not specified, BIRD will choose ID
|
||||
based on an order of the password item in the interface, starting from
|
||||
1. For example, second password item in one interface will have default
|
||||
ID 2. ID 0 is allowed by BIRD, but some other implementations may not
|
||||
allow it. ID is used by some routing protocols to identify which
|
||||
password was used to authenticate protocol packets.
|
||||
|
||||
<tag><label id="proto-pass-gen-from">generate from "<m/time/"</tag>
|
||||
The start time of the usage of the password for packet signing.
|
||||
@ -831,6 +839,16 @@ templates. Multiple definitions of the same channel are forbidden, but channels
|
||||
inherited from templates can be updated by new definitions.
|
||||
|
||||
<descrip>
|
||||
<tag><label id="channel-debug">debug all|off|{ states|routes|filters [, <m/.../] }</tag>
|
||||
Set channel debugging options. Like in <ref id="proto-debug"
|
||||
name="protocol debugging">, channels are capable of writing trace
|
||||
messages about its work to the log (with category <cf/trace/). You can
|
||||
either request printing of <cf/all/ trace messages or only of the
|
||||
selected types: <cf/states/ for channel state changes (channel going up,
|
||||
down, feeding, reloading etc.), <cf/routes/ for routes propagated
|
||||
through the channel, <cf/filters/ for details on route filtering,
|
||||
remaining debug flags are not used in channel debug. Default: off.
|
||||
|
||||
<tag><label id="proto-table">table <m/name/</tag>
|
||||
Specify a table to which the channel is connected. Default: the first
|
||||
table of given nettype.
|
||||
@ -857,6 +875,19 @@ inherited from templates can be updated by new definitions.
|
||||
possible to show them using <cf/show route filtered/. Note that this
|
||||
option does not work for the pipe protocol. Default: off.
|
||||
|
||||
<tag><label id="proto-rpki-reload">rpki reload <m/switch/</tag>
|
||||
Import or export filters may depend on route RPKI status (using
|
||||
<cf/roa_check()/ operator). In contrast to to other filter operators,
|
||||
this status for the same route may change as the content of ROA tables
|
||||
changes. When this option is active, BIRD activates automatic reload of
|
||||
affected channels whenever ROA tables are updated (after a short settle
|
||||
time). When disabled, route reloads have to be requested manually. The
|
||||
option is ignored if <cf/roa_check()/ is not used in channel filters.
|
||||
Note that for BGP channels, automatic reload requires
|
||||
<ref id="bgp-import-table" name="import table"> or
|
||||
<ref id="bgp-export-table" name="export table"> (for respective
|
||||
direction). Default: on.
|
||||
|
||||
<tag><label id="proto-import-limit">import limit [<m/number/ | off ] [action warn | block | restart | disable]</tag>
|
||||
Specify an import route limit (a maximum number of routes imported from
|
||||
the protocol) and optionally the action to be taken when the limit is
|
||||
@ -1238,8 +1269,8 @@ bird>
|
||||
<label id="data-types">
|
||||
|
||||
<p>Each variable and each value has certain type. Booleans, integers and enums
|
||||
are incompatible with each other (that is to prevent you from shooting in the
|
||||
foot).
|
||||
are incompatible with each other (that is to prevent you from shooting oneself
|
||||
in the foot).
|
||||
|
||||
<descrip>
|
||||
<tag><label id="type-bool">bool</tag>
|
||||
@ -1275,8 +1306,8 @@ foot).
|
||||
<tag><label id="type-ip">ip</tag>
|
||||
This type can hold a single IP address. The IPv4 addresses are stored as
|
||||
IPv4-Mapped IPv6 addresses so one data type for both of them is used.
|
||||
Whether the address is IPv4 or not may be checked by <cf>.is_ip4</cf>
|
||||
which returns <cf/bool/. IP addresses are written in the standard
|
||||
Whether the address is IPv4 or not may be checked by <cf>.is_v4</cf>
|
||||
which returns a <cf/bool/. IP addresses are written in the standard
|
||||
notation (<cf/10.20.30.40/ or <cf/fec0:3:4::1/). You can apply special
|
||||
operator <cf>.mask(<M>num</M>)</cf> on values of type ip. It masks out
|
||||
all but first <cf><M>num</M></cf> bits from the IP address. So
|
||||
@ -1299,7 +1330,9 @@ foot).
|
||||
prefix. The literals are written as <cf><m/ipaddress//<m/pxlen/ from
|
||||
<m/ipaddress//<m/pxlen/</cf>, where the first part is the destination
|
||||
prefix and the second art is the source prefix. They support the same
|
||||
operators as IP prefixes, but just for the destination part.
|
||||
operators as IP prefixes, but just for the destination part. They also
|
||||
support <cf/.src/ and <cf/.dst/ operators to get respective parts of the
|
||||
address as separate <cf/NET_IP6/ values.
|
||||
|
||||
<cf/NET_VPN4/ and <cf/NET_VPN6/ prefixes hold an IP prefix with VPN
|
||||
Route Distinguisher (<rfc id="4364">). They support the same special
|
||||
@ -1313,7 +1346,9 @@ foot).
|
||||
and <cf/.asn/ which extracts the ASN.
|
||||
|
||||
<cf/NET_FLOW4/ and <cf/NET_FLOW6/ hold an IP prefix together with a
|
||||
flowspec rule. Filters currently don't support flowspec parsing.
|
||||
flowspec rule. Filters currently do not support much flowspec parsing,
|
||||
only <cf/.src/ and <cf/.dst/ operators to get source and destination
|
||||
parts of the flowspec as separate <cf/NET_IP4/ / <cf/NET_IP6/ values.
|
||||
|
||||
<cf/NET_MPLS/ holds a single MPLS label and its handling is currently
|
||||
not implemented.
|
||||
@ -1419,9 +1454,10 @@ foot).
|
||||
<cf>192.168.0.0/16{16,24}</cf> and <cf>192.168.0.0/16 ge 24</cf> as
|
||||
<cf>192.168.0.0/16{24,32}</cf>.
|
||||
|
||||
It is possible to mix IPv4 and IPv6 prefixes/addresses in a prefix/ip set
|
||||
but its behavior may change between versions without any warning; don't do
|
||||
it unless you are more than sure what you are doing. (Really, don't do it.)
|
||||
It is not possible to mix IPv4 and IPv6 prefixes in a prefix set. It is
|
||||
currently possible to mix IPv4 and IPv6 addresses in an ip set, but that
|
||||
behavior may change between versions without any warning; don't do it
|
||||
unless you are more than sure what you are doing. (Really, don't do it.)
|
||||
|
||||
<tag><label id="type-enum">enum</tag>
|
||||
Enumeration types are fixed sets of possibilities. You can't define your
|
||||
@ -1470,10 +1506,16 @@ foot).
|
||||
<cf/*/ matches any (even empty) sequence of arbitrary AS numbers and
|
||||
<cf/?/ matches one arbitrary AS number. For example, if <cf>bgp_path</cf>
|
||||
is 4 3 2 1, then: <tt>bgp_path ˜ [= * 4 3 * =]</tt> is true,
|
||||
but <tt>bgp_path ˜ [= * 4 5 * =]</tt> is false. BGP mask
|
||||
expressions can also contain integer expressions enclosed in parenthesis
|
||||
and integer variables, for example <tt>[= * 4 (1+2) a =]</tt>. You can
|
||||
also use ranges, for example <tt>[= * 3..5 2 100..200 * =]</tt>.
|
||||
but <tt>bgp_path ˜ [= * 4 5 * =]</tt> is false. There is also
|
||||
<cf/+/ operator which matches one or multiple instances of previous
|
||||
expression, e.g. <tt>[= 1 2+ 3 =]</tt> matches both path 1 2 3 and path
|
||||
1 2 2 2 3, but not 1 3 nor 1 2 4 3. Note that while <cf/*/ and <cf/?/
|
||||
are wildcard-style operators, <cf/+/ is regex-style operator.
|
||||
|
||||
BGP mask expressions can also contain integer expressions enclosed in
|
||||
parenthesis and integer variables, for example <tt>[= * 4 (1+2) a =]</tt>.
|
||||
You can also use ranges (e.g. <tt>[= * 3..5 2 100..200 * =]</tt>)
|
||||
and sets (e.g. <tt>[= 1 2 [3, 5, 7] * =]</tt>).
|
||||
|
||||
<tag><label id="type-clist">clist</tag>
|
||||
Clist is similar to a set, except that unlike other sets, it can be
|
||||
@ -1513,7 +1555,7 @@ foot).
|
||||
<cf/!˜/ membership operators) can be used to modify or test
|
||||
eclists, with ECs instead of pairs as arguments.
|
||||
|
||||
<tag><label id="type-lclist">lclist/</tag>
|
||||
<tag><label id="type-lclist">lclist</tag>
|
||||
Lclist is a data type used for BGP large community lists. Like eclists,
|
||||
lclists are very similar to clists, but they are sets of LCs instead of
|
||||
pairs. The same operations (like <cf/add/, <cf/delete/ or <cf/˜/
|
||||
@ -1545,8 +1587,8 @@ the clist that is also a member of the pair/quad set).
|
||||
<p>There is one operator related to ROA infrastructure - <cf/roa_check()/. It
|
||||
examines a ROA table and does <rfc id="6483"> route origin validation for a
|
||||
given network prefix. The basic usage is <cf>roa_check(<m/table/)</cf>, which
|
||||
checks current route (which should be from BGP to have AS_PATH argument) in the
|
||||
specified ROA table and returns ROA_UNKNOWN if there is no relevant ROA,
|
||||
checks the current route (which should be from BGP to have AS_PATH argument) in
|
||||
the specified ROA table and returns ROA_UNKNOWN if there is no relevant ROA,
|
||||
ROA_VALID if there is a matching ROA, or ROA_INVALID if there are some relevant
|
||||
ROAs but none of them match. There is also an extended variant
|
||||
<cf>roa_check(<m/table/, <m/prefix/, <m/asn/)</cf>, which allows to specify a
|
||||
@ -1639,9 +1681,8 @@ Common route attributes are:
|
||||
<tag><label id="rta-source"><m/enum/ source</tag>
|
||||
what protocol has told me about this route. Possible values:
|
||||
<cf/RTS_DUMMY/, <cf/RTS_STATIC/, <cf/RTS_INHERIT/, <cf/RTS_DEVICE/,
|
||||
<cf/RTS_STATIC_DEVICE/, <cf/RTS_REDIRECT/, <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_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/.
|
||||
|
||||
<tag><label id="rta-dest"><m/enum/ dest</tag>
|
||||
Type of destination the packets should be sent to
|
||||
@ -1666,6 +1707,15 @@ Common route attributes are:
|
||||
creation/removal. Zero is returned for routes with undefined outgoing
|
||||
interfaces. Read-only.
|
||||
|
||||
<tag><label id="rta-weight"><m/int/ weight</tag>
|
||||
Multipath weight of route next hops. Valid values are 1-256. Reading
|
||||
returns the weight of the first next hop, setting it sets weights of all
|
||||
next hops to the specified value. Therefore, this attribute is not much
|
||||
useful for manipulating individual next hops of an ECMP route, but can
|
||||
be used in BGP multipath setup to set weights of individual routes that
|
||||
are merged to one ECMP route during export to the Kernel protocol
|
||||
(with active <ref id="krt-merge-paths" name="marge paths"> option).
|
||||
|
||||
<tag><label id="rta-igp-metric"><m/int/ igp_metric</tag>
|
||||
The optional attribute that can be used to specify a distance to the
|
||||
network for routes that do not have a native protocol metric attribute
|
||||
@ -1696,9 +1746,6 @@ protocol sections.
|
||||
<tag><label id="print">print|printn <m/expr/ [<m/, expr.../]</tag>
|
||||
Prints given expressions; useful mainly while debugging filters. The
|
||||
<cf/printn/ variant does not terminate the line.
|
||||
|
||||
<tag><label id="quitbird">quitbird</tag>
|
||||
Terminates BIRD. Useful when debugging the filter interpreter.
|
||||
</descrip>
|
||||
|
||||
|
||||
@ -1938,6 +1985,8 @@ configuration is often sufficient.
|
||||
|
||||
<p>Note that to use BFD for other protocols like OSPF or BGP, these protocols
|
||||
also have to be configured to request BFD sessions, usually by <cf/bfd/ option.
|
||||
In BGP case, it is also possible to specify per-peer BFD session options (e.g.
|
||||
rx/tx intervals) as a part of the <cf/bfd/ option.
|
||||
|
||||
<p>A BFD instance not associated with any VRF handles session requests from all
|
||||
other protocols, even ones associated with a VRF. Such setup would work for
|
||||
@ -1954,6 +2003,7 @@ milliseconds.
|
||||
|
||||
<code>
|
||||
protocol bfd [<name>] {
|
||||
accept [ipv4|ipv6] [direct|multihop];
|
||||
interface <interface pattern> {
|
||||
interval <time>;
|
||||
min rx interval <time>;
|
||||
@ -1988,6 +2038,14 @@ protocol bfd [<name>] {
|
||||
</code>
|
||||
|
||||
<descrip>
|
||||
<tag><label id="bfd-accept">accept [ipv4|ipv6] [direct|multihop]</tag>
|
||||
A BFD protocol instance accepts (by default) all BFD session requests
|
||||
(with regard to VRF restrictions, see above). This option controls
|
||||
whether IPv4 / IPv6 and direct / multihop session requests are accepted
|
||||
(and which listening sockets are opened). It can be used, for example,
|
||||
to configure separate BFD protocol instances for IPv4 and for IPv6
|
||||
sessions.
|
||||
|
||||
<tag><label id="bfd-iface">interface <m/pattern/ [, <m/.../] { <m/options/ }</tag>
|
||||
Interface definitions allow to specify options for sessions associated
|
||||
with such interfaces and also may contain interface specific options.
|
||||
@ -2164,6 +2222,7 @@ avoid routing loops.
|
||||
<item> <rfc id="6286"> - AS-Wide Unique BGP Identifier
|
||||
<item> <rfc id="6608"> - Subcodes for BGP Finite State Machine Error
|
||||
<item> <rfc id="6793"> - BGP Support for 4-Octet AS Numbers
|
||||
<item> <rfc id="7311"> - Accumulated IGP Metric Attribute for BGP
|
||||
<item> <rfc id="7313"> - Enhanced Route Refresh Capability for BGP
|
||||
<item> <rfc id="7606"> - Revised Error Handling for BGP UPDATE Messages
|
||||
<item> <rfc id="7911"> - Advertisement of Multiple Paths in BGP
|
||||
@ -2300,14 +2359,17 @@ using the following configuration parameters:
|
||||
immediately shut down. Note that this option cannot be used with
|
||||
multihop BGP. Default: enabled for direct BGP, disabled otherwise.
|
||||
|
||||
<tag><label id="bgp-bfd">bfd <M>switch</M>|graceful</tag>
|
||||
<tag><label id="bgp-bfd">bfd <M>switch</M>|graceful| { <m/options/ }</tag>
|
||||
BGP could use BFD protocol as an advisory mechanism for neighbor
|
||||
liveness and failure detection. If enabled, BIRD setups a BFD session
|
||||
for the BGP neighbor and tracks its liveness by it. This has an
|
||||
advantage of an order of magnitude lower detection times in case of
|
||||
failure. When a neighbor failure is detected, the BGP session is
|
||||
restarted. Optionally, it can be configured (by <cf/graceful/ argument)
|
||||
to trigger graceful restart instead of regular restart. Note that BFD
|
||||
to trigger graceful restart instead of regular restart. It is also
|
||||
possible to specify section with per-peer BFD session options instead of
|
||||
just switch argument. Most BFD session specific options are allowed here
|
||||
with the exception of authentication options. here Note that BFD
|
||||
protocol also has to be configured, see <ref id="bfd" name="BFD">
|
||||
section for details. Default: disabled.
|
||||
|
||||
@ -2397,6 +2459,25 @@ using the following configuration parameters:
|
||||
completely disabled and you should ensure loop-free behavior by some
|
||||
other means. Default: 0 (no local AS number allowed).
|
||||
|
||||
<tag><label id="bgp-allow-as-sets">allow as sets [<m/switch/]</tag>
|
||||
AS path attribute received with BGP routes may contain not only
|
||||
sequences of AS numbers, but also sets of AS numbers. These rarely used
|
||||
artifacts are results of inter-AS route aggregation. AS sets are
|
||||
deprecated (<rfc id="6472">), and likely to be rejected in the future,
|
||||
as they complicate security features like RPKI validation. When this
|
||||
option is disabled, then received AS paths with AS sets are rejected as
|
||||
malformed and corresponding BGP updates are treated as withdraws.
|
||||
Default: on.
|
||||
|
||||
<tag><label id="bgp-enforce-first-as">enforce first as [<m/switch/]</tag>
|
||||
Routes received from an EBGP neighbor are generally expected to have the
|
||||
first (leftmost) AS number in their AS path equal to the neighbor AS
|
||||
number. This is not enforced by default as there are legitimate cases
|
||||
where it is not true, e.g. connections to route servers. When this
|
||||
option is enabled, routes with non-matching first AS number are rejected
|
||||
and corresponding updates are treated as withdraws. The option is valid
|
||||
on EBGP sessions only. Default: off.
|
||||
|
||||
<tag><label id="bgp-enable-route-refresh">enable route refresh <m/switch/</tag>
|
||||
After the initial route exchange, BGP protocol uses incremental updates
|
||||
to keep BGP speakers synchronized. Sometimes (e.g., if BGP speaker
|
||||
@ -2471,8 +2552,8 @@ using the following configuration parameters:
|
||||
|
||||
<tag><label id="bgp-enable-extended-messages">enable extended messages <m/switch/</tag>
|
||||
The BGP protocol uses maximum message length of 4096 bytes. This option
|
||||
provides an extension to allow extended messages with length up
|
||||
to 65535 bytes. Default: off.
|
||||
provides an extension (<rfc id="8654">) to allow extended messages with
|
||||
length up to 65535 bytes. Default: off.
|
||||
|
||||
<tag><label id="bgp-capabilities">capabilities <m/switch/</tag>
|
||||
Use capability advertisement to advertise optional capabilities. This is
|
||||
@ -2489,6 +2570,9 @@ using the following configuration parameters:
|
||||
This option is relevant to IPv4 mode with enabled capability
|
||||
advertisement only. Default: on.
|
||||
|
||||
<tag><label id="bgp-advertise-hostname">advertise hostname <m/switch/</tag>
|
||||
Advertise hostname capability along with the hostname. Default: off.
|
||||
|
||||
<tag><label id="bgp-disable-after-error">disable after error <m/switch/</tag>
|
||||
When an error is encountered (either locally or by the other side),
|
||||
disable the instance automatically and wait for an administrator to fix
|
||||
@ -2657,30 +2741,15 @@ be used in explicit configuration.
|
||||
BGP session (if acceptable), or the preferred address of an associated
|
||||
interface.
|
||||
|
||||
<tag><label id="bgp-missing-lladdr">missing lladdr self|drop|ignore</tag>
|
||||
Next Hop attribute in BGP-IPv6 sometimes contains just the global IPv6
|
||||
address, but sometimes it has to contain both global and link-local IPv6
|
||||
addresses. This option specifies what to do if BIRD have to send both
|
||||
addresses but does not know link-local address. This situation might
|
||||
happen when routes from other protocols are exported to BGP, or when
|
||||
improper updates are received from BGP peers. <cf/self/ means that BIRD
|
||||
advertises its own local address instead. <cf/drop/ means that BIRD
|
||||
skips that prefixes and logs error. <cf/ignore/ means that BIRD ignores
|
||||
the problem and sends just the global address (and therefore forms
|
||||
improper BGP update). Default: <cf/self/, unless BIRD is configured as a
|
||||
route server (option <cf/rs client/), in that case default is <cf/ignore/,
|
||||
because route servers usually do not forward packets themselves.
|
||||
|
||||
<tag><label id="bgp-gateway">gateway direct|recursive</tag>
|
||||
For received routes, their <cf/gw/ (immediate next hop) attribute is
|
||||
computed from received <cf/bgp_next_hop/ attribute. This option
|
||||
specifies how it is computed. Direct mode means that the IP address from
|
||||
<cf/bgp_next_hop/ is used if it is directly reachable, otherwise the
|
||||
neighbor IP address is used. Recursive mode means that the gateway is
|
||||
computed by an IGP routing table lookup for the IP address from
|
||||
<cf/bgp_next_hop/. Note that there is just one level of indirection in
|
||||
recursive mode - the route obtained by the lookup must not be recursive
|
||||
itself, to prevent mutually recursive routes.
|
||||
<cf/bgp_next_hop/ is used and must be directly reachable. Recursive mode
|
||||
means that the gateway is computed by an IGP routing table lookup for
|
||||
the IP address from <cf/bgp_next_hop/. Note that there is just one level
|
||||
of indirection in recursive mode - the route obtained by the lookup must
|
||||
not be recursive itself, to prevent mutually recursive routes.
|
||||
|
||||
Recursive mode is the behavior specified by the BGP
|
||||
standard. Direct mode is simpler, does not require any routes in a
|
||||
@ -2705,6 +2774,16 @@ be used in explicit configuration.
|
||||
be examined later by <cf/show route/, and can be used to reconfigure
|
||||
import filters without full route refresh. Default: off.
|
||||
|
||||
<tag><label id="bgp-export-table">export table <m/switch/</tag>
|
||||
A BGP export table contains all routes sent to given BGP neighbor, after
|
||||
application of export filters. It is also called <em/Adj-RIB-Out/ in BGP
|
||||
terminology. BIRD BGP by default operates without export tables, in
|
||||
which case routes from master table are just processed by export filters
|
||||
and then announced by BGP. Enabling <cf/export table/ allows to store
|
||||
routes after export filter processing, so they can be examined later by
|
||||
<cf/show route/, and can be used to eliminate unnecessary updates or
|
||||
withdraws. Default: off.
|
||||
|
||||
<tag><label id="bgp-secondary">secondary <m/switch/</tag>
|
||||
Usually, if an export filter rejects a selected route, no other route is
|
||||
propagated for that network. This option allows to try the next route in
|
||||
@ -2732,6 +2811,36 @@ be used in explicit configuration.
|
||||
TX direction. When active, all available routes accepted by the export
|
||||
filter are advertised to the neighbor. Default: off.
|
||||
|
||||
<tag><label id="bgp-aigp">aigp <m/switch/|originate</tag>
|
||||
The BGP protocol does not use a common metric like other routing
|
||||
protocols, instead it uses a set of criteria for route selection
|
||||
consisting both overall AS path length and a distance to the nearest AS
|
||||
boundary router. Assuming that metrics of different autonomous systems
|
||||
are incomparable, once a route is propagated from an AS to a next one,
|
||||
the distance in the old AS does not matter.
|
||||
|
||||
The AIGP extension (<rfc id="7311">) allows to propagate accumulated
|
||||
IGP metric (in the AIGP attribute) through both IBGP and EBGP links,
|
||||
computing total distance through multiple autonomous systems (assuming
|
||||
they use comparable IGP metric). The total AIGP metric is compared in
|
||||
the route selection process just after Local Preference comparison (and
|
||||
before AS path length comparison).
|
||||
|
||||
This option controls whether AIGP attribute propagation is allowed on
|
||||
the session. Optionally, it can be set to <cf/originate/, which not only
|
||||
allows AIGP attribute propagation, but also new AIGP attributes are
|
||||
automatically attached to non-BGP routes with valid IGP metric (e.g.
|
||||
<cf/ospf_metric1/) as they are exported to the BGP session. Default:
|
||||
enabled for IBGP (and intra-confederation EBGP), disabled for regular
|
||||
EBGP.
|
||||
|
||||
<tag><label id="bgp-cost">cost <m/number/</tag>
|
||||
When BGP <ref id="bgp-gateway" name="gateway mode"> is <cf/recursive/
|
||||
(mainly multihop IBGP sessions), then the distance to BGP next hop is
|
||||
based on underlying IGP metric. This option specifies the distance to
|
||||
BGP next hop for BGP sessions in direct gateway mode (mainly direct
|
||||
EBGP sessions).
|
||||
|
||||
<tag><label id="bgp-graceful-restart-c">graceful restart <m/switch/</tag>
|
||||
Although BGP graceful restart is configured mainly by protocol-wide
|
||||
<ref id="bgp-graceful-restart" name="options">, it is possible to
|
||||
@ -2800,9 +2909,11 @@ some of them (marked with `<tt/O/') are optional.
|
||||
presence of which indicates that the route has been aggregated from
|
||||
multiple routes by some router on the path from the originator.
|
||||
|
||||
<!-- we don't handle aggregators right since they are of a very obscure type
|
||||
<tag>bgp_aggregator</tag>
|
||||
-->
|
||||
<tag><label id="rta-bgp-aggregator">void bgp_aggregator [O]</tag>
|
||||
This is an optional attribute specifying AS number and IP address of the
|
||||
BGP router that created the route by aggregating multiple BGP routes.
|
||||
Currently, the attribute is not accessible from filters.
|
||||
|
||||
<tag><label id="rta-bgp-community">clist bgp_community [O]</tag>
|
||||
List of community values associated with the route. Each such value is a
|
||||
pair (represented as a <cf/pair/ data type inside the filters) of 16-bit
|
||||
@ -2837,6 +2948,11 @@ some of them (marked with `<tt/O/') are optional.
|
||||
<tag><label id="rta-bgp-cluster-list">clist bgp_cluster_list [I, O]</tag>
|
||||
This attribute contains a list of cluster IDs of route reflectors. Each
|
||||
route reflector prepends its cluster ID when reflecting the route.
|
||||
|
||||
<tag><label id="rta-bgp-aigp">void bgp_aigp [O]</tag>
|
||||
This attribute contains accumulated IGP metric, which is a total
|
||||
distance to the destination through multiple autonomous systems.
|
||||
Currently, the attribute is not accessible from filters.
|
||||
</descrip>
|
||||
|
||||
<sect1>Example
|
||||
@ -3313,18 +3429,18 @@ protocol ospf [v2|v3] <name> {
|
||||
networks {
|
||||
<prefix>;
|
||||
<prefix> hidden;
|
||||
}
|
||||
};
|
||||
external {
|
||||
<prefix>;
|
||||
<prefix> hidden;
|
||||
<prefix> tag <num>;
|
||||
}
|
||||
};
|
||||
stubnet <prefix>;
|
||||
stubnet <prefix> {
|
||||
hidden <switch>;
|
||||
summary <switch>;
|
||||
cost <num>;
|
||||
}
|
||||
};
|
||||
interface <interface pattern> [instance <num>] {
|
||||
cost <num>;
|
||||
stub <switch>;
|
||||
@ -3344,6 +3460,7 @@ protocol ospf [v2|v3] <name> {
|
||||
strict nonbroadcast <switch>;
|
||||
real broadcast <switch>;
|
||||
ptp netmask <switch>;
|
||||
ptp address <switch>;
|
||||
check link <switch>;
|
||||
bfd <switch>;
|
||||
ecmp weight <num>;
|
||||
@ -3680,11 +3797,28 @@ protocol ospf [v2|v3] <name> {
|
||||
In <cf/type ptp/ network configurations, OSPFv2 implementations should
|
||||
ignore received netmask field in hello packets and should send hello
|
||||
packets with zero netmask field on unnumbered PtP links. But some OSPFv2
|
||||
implementations perform netmask checking even for PtP links. This option
|
||||
specifies whether real netmask will be used in hello packets on <cf/type
|
||||
ptp/ interfaces. You should ignore this option unless you meet some
|
||||
compatibility problems related to this issue. Default value is no for
|
||||
unnumbered PtP links, yes otherwise.
|
||||
implementations perform netmask checking even for PtP links.
|
||||
|
||||
This option specifies whether real netmask will be used in hello packets
|
||||
on <cf/type ptp/ interfaces. You should ignore this option unless you
|
||||
meet some compatibility problems related to this issue. Default value is
|
||||
no for unnumbered PtP links, yes otherwise.
|
||||
|
||||
<tag><label id="ospf-ptp-address">ptp address <m/switch/</tag>
|
||||
In <cf/type ptp/ network configurations, OSPFv2 implementations should
|
||||
use IP address for regular PtP links and interface id for unnumbered PtP
|
||||
links in data field of link description records in router LSA. This data
|
||||
field has only local meaning for PtP links, but some broken OSPFv2
|
||||
implementations assume there is an IP address and use it as a next hop
|
||||
in SPF calculations. Note that interface id for unnumbered PtP links is
|
||||
necessary when graceful restart is enabled to distinguish PtP links with
|
||||
the same local IP address.
|
||||
|
||||
This option specifies whether an IP address will be used in data field
|
||||
for <cf/type ptp/ interfaces, it is ignored for other interfaces. You
|
||||
should ignore this option unless you meet some compatibility problems
|
||||
related to this issue. Default value is no for unnumbered PtP links when
|
||||
graceful restart is enabled, yes otherwise.
|
||||
|
||||
<tag><label id="ospf-check-link">check link <M>switch</M></tag>
|
||||
If set, a hardware link state (reported by OS) is taken into consideration.
|
||||
@ -3831,7 +3965,7 @@ protocol ospf MyOSPF {
|
||||
networks {
|
||||
172.16.1.0/24;
|
||||
172.16.2.0/24 hidden;
|
||||
}
|
||||
};
|
||||
interface "-arc0" , "arc*" {
|
||||
type nonbroadcast;
|
||||
authentication none;
|
||||
@ -4121,6 +4255,12 @@ definitions, prefix definitions and DNS definitions:
|
||||
The minimum delay between two consecutive router advertisements, in
|
||||
seconds. Default: 3
|
||||
|
||||
<tag><label id="radv-solicited-ra-unicast">solicited ra unicast <m/switch/</tag>
|
||||
Solicited router advertisements are usually sent to all-nodes multicast
|
||||
group like unsolicited ones, but the router can be configured to send
|
||||
them as unicast directly to soliciting nodes instead. This is especially
|
||||
useful on wireless networks (see <rfc id="7772">). Default: no
|
||||
|
||||
<tag><label id="radv-iface-managed">managed <m/switch/</tag>
|
||||
This option specifies whether hosts should use DHCPv6 for IP address
|
||||
configuration. Default: no
|
||||
@ -4358,7 +4498,8 @@ you can't use RIP on networks where maximal distance is higher than 15
|
||||
hosts.
|
||||
|
||||
<p>BIRD supports RIPv1 (<rfc id="1058">), RIPv2 (<rfc id="2453">), RIPng (<rfc
|
||||
id="2080">), and RIP cryptographic authentication (<rfc id="4822">).
|
||||
id="2080">), Triggered RIP for demand circuits (<rfc id="2091">), and RIP
|
||||
cryptographic authentication (<rfc id="4822">).
|
||||
|
||||
<p>RIP is a very simple protocol, and it has a lot of shortcomings. Slow
|
||||
convergence, big network load and inability to handle larger networks makes it
|
||||
@ -4388,6 +4529,7 @@ protocol rip [ng] [<name>] {
|
||||
version 1|2;
|
||||
split horizon <switch>;
|
||||
poison reverse <switch>;
|
||||
demand circuit <switch>;
|
||||
check zero <switch>;
|
||||
update time <number>;
|
||||
timeout time <number>;
|
||||
@ -4492,6 +4634,16 @@ protocol rip [ng] [<name>] {
|
||||
used. The poisoned reverse has some advantages in faster convergence,
|
||||
but uses more network traffic. Default: yes.
|
||||
|
||||
<tag><label id="rip-iface-demand-circuit">demand circuit <m/switch/</tag>
|
||||
Regular RIP sends periodic full updates on an interface. There is the
|
||||
Triggered RIP extension for demand circuits (<rfc id="2091">), which
|
||||
removes periodic updates and introduces update acknowledgments. When
|
||||
enabled, there is no RIP communication in steady-state network. Note
|
||||
that in order to work, it must be enabled on both sides. As there are
|
||||
no hello packets, it depends on hardware link state to detect neighbor
|
||||
failures. Also, it is designed for PtP links and it does not work
|
||||
properly with multiple RIP neighbors on an interface. Default: no.
|
||||
|
||||
<tag><label id="rip-iface-check-zero">check zero <m/switch/</tag>
|
||||
Received RIPv1 packets with non-zero values in reserved fields should
|
||||
be discarded. This option specifies whether the check is performed or
|
||||
@ -4622,21 +4774,21 @@ protocol rip {
|
||||
<sect1>Introduction
|
||||
|
||||
<p>The Resource Public Key Infrastructure (RPKI) is mechanism for origin
|
||||
validation of BGP routes (RFC 6480). BIRD supports only so-called RPKI-based
|
||||
origin validation. There is implemented RPKI to Router (RPKI-RTR) protocol (RFC
|
||||
6810). It uses some of the RPKI data to allow a router to verify that the
|
||||
autonomous system announcing an IP address prefix is in fact authorized to do
|
||||
so. This is not crypto checked so can be violated. But it should prevent the
|
||||
vast majority of accidental hijackings on the Internet today, e.g. the famous
|
||||
Pakastani accidental announcement of YouTube's address space.
|
||||
validation of BGP routes (<rfc id="6480">). BIRD supports only so-called
|
||||
RPKI-based origin validation. There is implemented RPKI to Router (RPKI-RTR)
|
||||
protocol (<rfc id="6810">). It uses some of the RPKI data to allow a router to
|
||||
verify that the autonomous system announcing an IP address prefix is in fact
|
||||
authorized to do so. This is not crypto checked so can be violated. But it
|
||||
should prevent the vast majority of accidental hijackings on the Internet today,
|
||||
e.g. the famous Pakistani accidental announcement of YouTube's address space.
|
||||
|
||||
<p>The RPKI-RTR protocol receives and maintains a set of ROAs from a cache
|
||||
server (also called validator). You can validate routes (RFC 6483) using
|
||||
function <cf/roa_check()/ in filter and set it as import filter at the BGP
|
||||
protocol. BIRD should re-validate all of affected routes after RPKI update by
|
||||
RFC 6811, but we don't support it yet! You can use a BIRD's client command
|
||||
<cf>reload in <m/bgp_protocol_name/</cf> for manual call of revalidation of all
|
||||
routes.
|
||||
server (also called validator). You can validate routes (<rfc id="6483">,
|
||||
<rfc id="6811">) using function <cf/roa_check()/ in filter and set it as import
|
||||
filter at the BGP protocol. BIRD offers crude automatic re-validating of
|
||||
affected routes after RPKI update, see option <ref id="proto-rpki-reload"
|
||||
name="rpki reload">. Or you can use a BIRD client command <cf>reload in
|
||||
<m/bgp_protocol_name/</cf> for manual call of revalidation of all routes.
|
||||
|
||||
<sect1>Supported transports
|
||||
<p>
|
||||
@ -4710,6 +4862,11 @@ specify both channels.
|
||||
suppresses updating this value by a cache server.
|
||||
Default: 7200 seconds
|
||||
|
||||
<tag>ignore max length <m/switch/</tag>
|
||||
Ignore received max length in ROA records and use max value (32 or 128)
|
||||
instead. This may be useful for implementing loose RPKI check for
|
||||
blackholes. Default: disabled.
|
||||
|
||||
<tag>transport tcp</tag> Unprotected transport over TCP. It's a default
|
||||
transport. Should be used only on secure private networks.
|
||||
Default: tcp
|
||||
@ -4758,7 +4915,7 @@ protocol rpki {
|
||||
filter peer_in_v4 {
|
||||
if (roa_check(r4, net, bgp_path.last) = ROA_INVALID) then
|
||||
{
|
||||
print "Ignore invalid ROA ", net, " for ASN ", bgp_path.last;
|
||||
print "Ignore RPKI invalid ", net, " for ASN ", bgp_path.last;
|
||||
reject;
|
||||
}
|
||||
accept;
|
||||
@ -4812,8 +4969,15 @@ default route to prevent routing loops).
|
||||
|
||||
<p>There are three classes of definitions in Static protocol configuration --
|
||||
global options, static route definitions, and per-route options. Usually, the
|
||||
definition of the protocol contains mainly a list of static routes.
|
||||
Static routes have no specific attributes.
|
||||
definition of the protocol contains mainly a list of static routes. Static
|
||||
routes have no specific attributes, but <ref id="rta-igp-metric" name="igp_metric">
|
||||
attribute is used to compare static routes with the same preference.
|
||||
|
||||
<p>The list of static routes may contain multiple routes for the same network
|
||||
(usually, but not necessary, distinquished by <cf/preference/ or <cf/igp_metric/),
|
||||
but only routes of the same network type are allowed, as the static protocol
|
||||
has just one channel. E.g., to have both IPv4 and IPv6 static routes, define two
|
||||
static protocols, each with appropriate routes and channel.
|
||||
|
||||
<p>Global options:
|
||||
|
||||
@ -4838,8 +5002,8 @@ Static routes have no specific attributes.
|
||||
<ref id="type-prefix" name="dependent on network type">.
|
||||
|
||||
<descrip>
|
||||
<tag>route <m/prefix/ via <m/ip/|<m/"interface"/ [mpls <m/num/[/<m/num/[/<m/num/[...]]]]</tag>
|
||||
Next hop routes may bear one or more <ref id="route-next-hop" name="next hops">.
|
||||
<tag>route <m/prefix/ via <m/ip/|<m/"interface"/ [<m/per-nexthop options/] [via ...]</tag>
|
||||
Regular routes may bear one or more <ref id="route-next-hop" name="next hops">.
|
||||
Every next hop is preceded by <cf/via/ and configured as shown.
|
||||
|
||||
<tag>route <m/prefix/ recursive <m/ip/ [mpls <m/num/[/<m/num/[/<m/num/[...]]]]</tag>
|
||||
@ -4858,6 +5022,46 @@ the next hop of the route is not a neighbor at the moment), Static just
|
||||
uninstalls the route from the table it is connected to and adds it again as soon
|
||||
as the destination becomes adjacent again.
|
||||
|
||||
<sect2>Per-nexthop options
|
||||
|
||||
<p>There are several options that in a case of multipath route are per-nexthop
|
||||
(i.e., they can be used multiple times for a route, one time for each nexthop).
|
||||
Syntactically, they are not separate options but just parts of <cf/route/
|
||||
statement after each <cf/via/ statement, not separated by semicolons. E.g.,
|
||||
statement <cf/route 10.0.0.0/8 via 192.0.2.1 bfd weight 1 via 192.0.2.2 weight
|
||||
2;/ describes a route with two nexthops, the first nexthop has two per-nexthop
|
||||
options (<cf/bfd/ and <cf/weight 1/), the second nexthop has just <cf/weight 2/.
|
||||
|
||||
<descrip>
|
||||
<tag><label id="static-route-bfd">bfd <m/switch/</tag>
|
||||
The Static protocol could use BFD protocol for next hop liveness
|
||||
detection. If enabled, a BFD session to the route next hop is created
|
||||
and the static route is BFD-controlled -- the static route is announced
|
||||
only if the next hop liveness is confirmed by BFD. If the BFD session
|
||||
fails, the static route (or just the affected nexthop from multiple
|
||||
ones) is removed. Note that this is a bit different compared to other
|
||||
protocols, which may use BFD as an advisory mechanism for fast failure
|
||||
detection but ignore it if a BFD session is not even established. Note
|
||||
that BFD protocol also has to be configured, see <ref id="bfd" name="BFD">
|
||||
section for details. Default value is no.
|
||||
|
||||
<tag><label id="static-route-mpls">mpls <m/num/[/<m/num/[/<m/num/[...]]]</tag>
|
||||
MPLS labels that should be pushed to packets forwarded by the route.
|
||||
The option could be used for both IP routes (on MPLS ingress routers)
|
||||
and MPLS switching rules (on MPLS transit routers). Default value is
|
||||
no labels.
|
||||
|
||||
<tag><label id="static-route-onlink">onlink <m/switch/</tag>
|
||||
Onlink flag means that the specified nexthop is accessible on the
|
||||
(specified) interface regardless of IP prefixes of the interface. The
|
||||
interface must be attached to nexthop IP address using link-local-scope
|
||||
format (e.g. <cf/192.0.2.1%eth0/). Default value is no.
|
||||
|
||||
<tag><label id="static-route-weight">weight <m/switch/</tag>
|
||||
For multipath routes, this value specifies a relative weight of the
|
||||
nexthop. Allowed values are 1-256. Default value is 1.
|
||||
</descrip>
|
||||
|
||||
<sect1>Route Origin Authorization
|
||||
|
||||
<p>The ROA config is just <cf>route <m/prefix/ max <m/int/ as <m/int/</cf> with no nexthop.
|
||||
@ -4996,21 +5200,6 @@ protocol static {
|
||||
<sect1>Per-route options
|
||||
<p>
|
||||
<descrip>
|
||||
<tag><label id="static-route-bfd">bfd <m/switch/</tag>
|
||||
The Static protocol could use BFD protocol for next hop liveness
|
||||
detection. If enabled, a BFD session to the route next hop is created
|
||||
and the static route is BFD-controlled -- the static route is announced
|
||||
only if the next hop liveness is confirmed by BFD. If the BFD session
|
||||
fails, the static route is removed. Note that this is a bit different
|
||||
compared to other protocols, which may use BFD as an advisory mechanism
|
||||
for fast failure detection but ignores it if a BFD session is not even
|
||||
established.
|
||||
|
||||
This option can be used for static routes with a direct next hop, or
|
||||
also for for individual next hops in a static multipath route (see
|
||||
above). Note that BFD protocol also has to be configured, see
|
||||
<ref id="bfd" name="BFD"> section for details. Default value is no.
|
||||
|
||||
<tag><label id="static-route-filter"><m/filter expression/</tag>
|
||||
This is a special option that allows filter expressions to be configured
|
||||
on per-route basis. Can be used multiple times. These expressions are
|
||||
@ -5020,7 +5209,8 @@ protocol static {
|
||||
exported to the OSPF protocol.
|
||||
</descrip>
|
||||
|
||||
<sect1>Example static config
|
||||
<sect1>Example static configs
|
||||
<label id="static-example">
|
||||
|
||||
<p><code>
|
||||
protocol static {
|
||||
@ -5031,16 +5221,29 @@ protocol static {
|
||||
via 198.51.100.10 weight 2
|
||||
via 198.51.100.20 bfd # BFD-controlled next hop
|
||||
via 192.0.2.1;
|
||||
route 203.0.113.0/24 unreachable; # Sink route
|
||||
route 203.0.113.0/24 blackhole; # Sink route
|
||||
route 10.2.0.0/24 via "arc0"; # Secondary network
|
||||
route 192.168.10.0/24 via 198.51.100.100 {
|
||||
ospf_metric1 = 20; # Set extended attribute
|
||||
}
|
||||
route 192.168.10.0/24 via 198.51.100.100 {
|
||||
};
|
||||
route 192.168.11.0/24 via 198.51.100.100 {
|
||||
ospf_metric2 = 100; # Set extended attribute
|
||||
ospf_tag = 2; # Set extended attribute
|
||||
bfd; # BFD-controlled route
|
||||
}
|
||||
};
|
||||
route 192.168.12.0/24 via 198.51.100.100 {
|
||||
bgp_community.add((65535, 65281)); # Set extended BGP attribute
|
||||
bgp_large_community.add((64512, 1, 1)); # Set extended BGP attribute
|
||||
};
|
||||
}
|
||||
|
||||
protocol static {
|
||||
ipv6; # Channel is mandatory
|
||||
route 2001:db8:10::/48 via 2001:db8:1::1; # Route with global nexthop
|
||||
route 2001:db8:20::/48 via fe80::10%eth0; # Route with link-local nexthop
|
||||
route 2001:db8:30::/48 via fe80::20%'eth1.60'; # Iface with non-alphanumeric characters
|
||||
route 2001:db8:40::/48 via "eth2"; # Direct route to eth2
|
||||
route 2001:db8::/32 unreachable; # Unreachable route
|
||||
route ::/0 via 2001:db8:1::1 bfd; # BFD-controlled default route
|
||||
}
|
||||
</code>
|
||||
|
||||
|
203
filter/config.Y
203
filter/config.Y
@ -185,159 +185,6 @@ f_generate_empty(struct f_dynamic_attr dyn)
|
||||
return f_new_inst(FI_EA_SET, f_new_inst(FI_CONSTANT, empty), dyn);
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
static inline struct f_inst *
|
||||
f_generate_dpair(struct f_inst *t1, struct f_inst *t2)
|
||||
{
|
||||
struct f_inst *rv;
|
||||
|
||||
if ((t1->fi_code == FI_CONSTANT) && (t2->fi_code == FI_CONSTANT)) {
|
||||
if ((t1->val.type != T_INT) || (t2->val.type != T_INT))
|
||||
cf_error( "Can't operate with value of non-integer type in pair constructor");
|
||||
|
||||
check_u16(t1->a[1].i);
|
||||
check_u16(t2->a[1].i);
|
||||
|
||||
rv = f_new_inst(FI_CONSTANT);
|
||||
rv->val = (struct f_val) {
|
||||
.type = T_PAIR,
|
||||
.val.i = pair(t1->a[1].i, t2->a[1].i),
|
||||
};
|
||||
}
|
||||
else {
|
||||
rv = f_new_inst(FI_PAIR_CONSTRUCT);
|
||||
rv->a[0].p = t1;
|
||||
rv->a[1].p = t2;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static inline struct f_inst *
|
||||
f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv)
|
||||
{
|
||||
struct f_inst *rv;
|
||||
int c1 = 0, c2 = 0, ipv4_used = 0;
|
||||
u32 key = 0, val2 = 0;
|
||||
|
||||
if (tk->fi_code == FI_CONSTANT) {
|
||||
c1 = 1;
|
||||
struct f_val *val = &(tk->val);
|
||||
|
||||
if (val->type == T_INT) {
|
||||
ipv4_used = 0; key = val->val.i;
|
||||
}
|
||||
else if (tk->val.type == T_QUAD) {
|
||||
ipv4_used = 1; key = val->val.i;
|
||||
}
|
||||
else if ((val->type == T_IP) && ipa_is_ip4(val->val.ip)) {
|
||||
ipv4_used = 1; key = ipa_to_u32(val->val.ip);
|
||||
}
|
||||
else
|
||||
cf_error("Can't operate with key of non-integer/IPv4 type in EC constructor");
|
||||
}
|
||||
|
||||
if (tv->fi_code == FI_CONSTANT) {
|
||||
if (tv->val.type != T_INT)
|
||||
cf_error("Can't operate with value of non-integer type in EC constructor");
|
||||
c2 = 1;
|
||||
val2 = tv->val.val.i;
|
||||
}
|
||||
|
||||
if (c1 && c2) {
|
||||
u64 ec;
|
||||
|
||||
if (kind == EC_GENERIC) {
|
||||
ec = ec_generic(key, val2);
|
||||
}
|
||||
else if (ipv4_used) {
|
||||
check_u16(val2);
|
||||
ec = ec_ip4(kind, key, val2);
|
||||
}
|
||||
else if (key < 0x10000) {
|
||||
ec = ec_as2(kind, key, val2);
|
||||
}
|
||||
else {
|
||||
check_u16(val2);
|
||||
ec = ec_as4(kind, key, val2);
|
||||
}
|
||||
|
||||
rv = f_new_inst(FI_CONSTANT);
|
||||
rv->val = (struct f_val) {
|
||||
.type = T_EC,
|
||||
.val.ec = ec,
|
||||
};
|
||||
}
|
||||
else {
|
||||
rv = f_new_inst(FI_EC_CONSTRUCT);
|
||||
rv->aux = kind;
|
||||
rv->a[0].p = tk;
|
||||
rv->a[1].p = tv;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static inline struct f_inst *
|
||||
f_generate_lc(struct f_inst *t1, struct f_inst *t2, struct f_inst *t3)
|
||||
{
|
||||
struct f_inst *rv;
|
||||
|
||||
if ((t1->fi_code == FI_CONSTANT) && (t2->fi_code == FI_CONSTANT) && (t3->fi_code == FI_CONSTANT)) {
|
||||
if ((t1->val.type != T_INT) || (t2->val.type != T_INT) || (t3->val.type != T_INT))
|
||||
cf_error( "LC - Can't operate with value of non-integer type in tuple constructor");
|
||||
|
||||
rv = f_new_inst(FI_CONSTANT);
|
||||
rv->val = (struct f_val) {
|
||||
.type = T_LC,
|
||||
.val.lc = (lcomm) { t1->a[1].i, t2->a[1].i, t3->a[1].i },
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
rv = f_new_inst(FI_LC_CONSTRUCT);
|
||||
rv->a[0].p = t1;
|
||||
rv->a[1].p = t2;
|
||||
rv->a[2].p = t3;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static inline struct f_inst *
|
||||
f_generate_path_mask(struct f_inst *t)
|
||||
{
|
||||
uint len = 0;
|
||||
uint dyn = 0;
|
||||
for (const struct f_inst *tt = t; tt; tt = tt->next) {
|
||||
if (tt->fi_code != FI_CONSTANT)
|
||||
dyn++;
|
||||
len++;
|
||||
}
|
||||
|
||||
if (dyn) {
|
||||
struct f_inst *pmc = f_new_inst(FI_PATHMASK_CONSTRUCT);
|
||||
pmc->a[0].p = t;
|
||||
pmc->a[1].i = len;
|
||||
return pmc;
|
||||
}
|
||||
|
||||
struct f_path_mask *pm = cfg_allocz(sizeof(struct f_path_mask) + len * sizeof(struct f_path_mask_item));
|
||||
|
||||
uint i = 0;
|
||||
for (const struct f_inst *tt = t; tt; tt = tt->next)
|
||||
pm->item[i++] = tt->val.val.pmi;
|
||||
|
||||
pm->len = i;
|
||||
struct f_inst *pmc = f_new_inst(FI_CONSTANT);
|
||||
pmc->val = (struct f_val) { .type = T_PATH_MASK, .val.path_mask = pm, };
|
||||
|
||||
return pmc;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Remove all new lines and doubled whitespaces
|
||||
* and convert all tabulators to spaces
|
||||
@ -426,14 +273,14 @@ assert_assign(struct f_lval *lval, struct f_inst *expr, const char *start, const
|
||||
CF_DECLS
|
||||
|
||||
CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
||||
ACCEPT, REJECT, ERROR, QUITBIRD,
|
||||
ACCEPT, REJECT, ERROR,
|
||||
INT, BOOL, IP, TYPE, PREFIX, RD, PAIR, QUAD, EC, LC,
|
||||
SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
|
||||
IF, THEN, ELSE, CASE,
|
||||
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
|
||||
FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX,
|
||||
FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT,
|
||||
PREFERENCE,
|
||||
ROA_CHECK, ASN, SRC,
|
||||
ROA_CHECK, ASN, SRC, DST,
|
||||
IS_V4, IS_V6,
|
||||
LEN, MAXLEN,
|
||||
DEFINED,
|
||||
@ -446,7 +293,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
|
||||
%nonassoc THEN
|
||||
%nonassoc ELSE
|
||||
|
||||
%type <xp> cmds_int
|
||||
%type <xp> cmds_int cmd_prep
|
||||
%type <x> term block cmd cmds constant constructor print_list var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail
|
||||
%type <fda> dynamic_attr
|
||||
%type <fsa> static_attr
|
||||
@ -624,17 +471,25 @@ cmds: /* EMPTY */ { $$ = NULL; }
|
||||
| cmds_int { $$ = $1.begin; }
|
||||
;
|
||||
|
||||
cmds_int: cmd {
|
||||
cmd_prep: cmd {
|
||||
$$.begin = $$.end = $1;
|
||||
while ($$.end->next)
|
||||
$$.end = $$.end->next;
|
||||
if ($1)
|
||||
while ($$.end->next)
|
||||
$$.end = $$.end->next;
|
||||
}
|
||||
;
|
||||
|
||||
cmds_int: cmd_prep
|
||||
| cmds_int cmd_prep {
|
||||
if (!$1.begin)
|
||||
$$ = $2;
|
||||
else if (!$2.begin)
|
||||
$$ = $1;
|
||||
else {
|
||||
$$.begin = $1.begin;
|
||||
$$.end = $2.end;
|
||||
$1.end->next = $2.begin;
|
||||
}
|
||||
| cmds_int cmd {
|
||||
$$.begin = $1.begin;
|
||||
$1.end->next = $2;
|
||||
$$.end = $2;
|
||||
while ($$.end->next)
|
||||
$$.end = $$.end->next;
|
||||
}
|
||||
;
|
||||
|
||||
@ -767,8 +622,8 @@ fprefix:
|
||||
;
|
||||
|
||||
fprefix_set:
|
||||
fprefix { $$ = f_new_trie(cfg_mem, sizeof(struct f_trie_node)); trie_add_prefix($$, &($1.net), $1.lo, $1.hi); }
|
||||
| fprefix_set ',' fprefix { $$ = $1; trie_add_prefix($$, &($3.net), $3.lo, $3.hi); }
|
||||
fprefix { $$ = f_new_trie(cfg_mem, 0); trie_add_prefix($$, &($1.net), $1.lo, $1.hi); }
|
||||
| fprefix_set ',' fprefix { $$ = $1; if (!trie_add_prefix($$, &($3.net), $3.lo, $3.hi)) cf_error("Mixed IPv4/IPv6 prefixes in prefix set"); }
|
||||
;
|
||||
|
||||
switch_body: /* EMPTY */ { $$ = NULL; }
|
||||
@ -801,8 +656,13 @@ bgp_path:
|
||||
bgp_path_tail:
|
||||
NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .asn = $1, .kind = PM_ASN, }, }); $$->next = $2; }
|
||||
| NUM DDOT NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .from = $1, .to = $3, .kind = PM_ASN_RANGE }, }); $$->next = $4; }
|
||||
| '[' set_items ']' bgp_path_tail {
|
||||
if ($2->from.type != T_INT) cf_error("Only integer sets allowed in path mask");
|
||||
$$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .set = build_tree($2), .kind = PM_ASN_SET }, }); $$->next = $4;
|
||||
}
|
||||
| '*' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_ASTERISK }, }); $$->next = $2; }
|
||||
| '?' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_QUESTION }, }); $$->next = $2; }
|
||||
| '+' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .kind = PM_LOOP }, }); $$->next = $2; }
|
||||
| bgp_path_expr bgp_path_tail { $$ = $1; $$->next = $2; }
|
||||
| { $$ = NULL; }
|
||||
;
|
||||
@ -890,6 +750,7 @@ static_attr:
|
||||
| DEST { $$ = f_new_static_attr(T_ENUM_RTD, SA_DEST, 0); }
|
||||
| IFNAME { $$ = f_new_static_attr(T_STRING, SA_IFNAME, 0); }
|
||||
| IFINDEX { $$ = f_new_static_attr(T_INT, SA_IFINDEX, 1); }
|
||||
| WEIGHT { $$ = f_new_static_attr(T_INT, SA_WEIGHT, 0); }
|
||||
;
|
||||
|
||||
term:
|
||||
@ -928,7 +789,8 @@ term:
|
||||
| term '.' LEN { $$ = f_new_inst(FI_LENGTH, $1); }
|
||||
| term '.' MAXLEN { $$ = f_new_inst(FI_ROA_MAXLEN, $1); }
|
||||
| term '.' ASN { $$ = f_new_inst(FI_ROA_ASN, $1); }
|
||||
| term '.' SRC { $$ = f_new_inst(FI_SADR_SRC, $1); }
|
||||
| term '.' SRC { $$ = f_new_inst(FI_NET_SRC, $1); }
|
||||
| term '.' DST { $$ = f_new_inst(FI_NET_DST, $1); }
|
||||
| term '.' MASK '(' term ')' { $$ = f_new_inst(FI_IP_MASK, $1, $5); }
|
||||
| term '.' FIRST { $$ = f_new_inst(FI_AS_PATH_FIRST, $1); }
|
||||
| term '.' LAST { $$ = f_new_inst(FI_AS_PATH_LAST, $1); }
|
||||
@ -962,8 +824,7 @@ term:
|
||||
;
|
||||
|
||||
break_command:
|
||||
QUITBIRD { $$ = F_QUITBIRD; }
|
||||
| ACCEPT { $$ = F_ACCEPT; }
|
||||
ACCEPT { $$ = F_ACCEPT; }
|
||||
| REJECT { $$ = F_REJECT; }
|
||||
| ERROR { $$ = F_ERROR; }
|
||||
;
|
||||
|
@ -25,6 +25,49 @@
|
||||
#include "filter/f-inst.h"
|
||||
#include "filter/data.h"
|
||||
|
||||
static const char * const f_type_str[] = {
|
||||
[T_VOID] = "void",
|
||||
|
||||
[T_INT] = "int",
|
||||
[T_BOOL] = "bool",
|
||||
[T_PAIR] = "pair",
|
||||
[T_QUAD] = "quad",
|
||||
|
||||
[T_ENUM_RTS] = "enum rts",
|
||||
[T_ENUM_BGP_ORIGIN] = "enum bgp_origin",
|
||||
[T_ENUM_SCOPE] = "enum scope",
|
||||
[T_ENUM_RTC] = "enum rtc",
|
||||
[T_ENUM_RTD] = "enum rtd",
|
||||
[T_ENUM_ROA] = "enum roa",
|
||||
[T_ENUM_NETTYPE] = "enum nettype",
|
||||
[T_ENUM_RA_PREFERENCE] = "enum ra_preference",
|
||||
[T_ENUM_AF] = "enum af",
|
||||
|
||||
[T_IP] = "ip",
|
||||
[T_NET] = "prefix",
|
||||
[T_STRING] = "string",
|
||||
[T_PATH_MASK] = "bgpmask",
|
||||
[T_PATH] = "bgppath",
|
||||
[T_CLIST] = "clist",
|
||||
[T_EC] = "ec",
|
||||
[T_ECLIST] = "eclist",
|
||||
[T_LC] = "lc",
|
||||
[T_LCLIST] = "lclist",
|
||||
[T_RD] = "rd",
|
||||
};
|
||||
|
||||
const char *
|
||||
f_type_name(enum f_type t)
|
||||
{
|
||||
if (t < ARRAY_SIZE(f_type_str))
|
||||
return f_type_str[t] ?: "?";
|
||||
|
||||
if ((t == T_SET) || (t == T_PREFIX_SET))
|
||||
return "set";
|
||||
|
||||
return "?";
|
||||
}
|
||||
|
||||
const struct f_val f_const_empty_path = {
|
||||
.type = T_PATH,
|
||||
.val.ad = &null_adata,
|
||||
@ -50,6 +93,8 @@ adata_empty(struct linpool *pool, int l)
|
||||
static void
|
||||
pm_format(const struct f_path_mask *p, buffer *buf)
|
||||
{
|
||||
int loop = 0;
|
||||
|
||||
buffer_puts(buf, "[= ");
|
||||
|
||||
for (uint i=0; i<p->len; i++)
|
||||
@ -68,14 +113,28 @@ pm_format(const struct f_path_mask *p, buffer *buf)
|
||||
buffer_puts(buf, "* ");
|
||||
break;
|
||||
|
||||
case PM_LOOP:
|
||||
loop = 1;
|
||||
break;
|
||||
|
||||
case PM_ASN_RANGE:
|
||||
buffer_print(buf, "%u..%u ", p->item[i].from, p->item[i].to);
|
||||
break;
|
||||
|
||||
case PM_ASN_SET:
|
||||
tree_format(p->item[i].set, buf);
|
||||
buffer_puts(buf, " ");
|
||||
break;
|
||||
|
||||
case PM_ASN_EXPR:
|
||||
ASSERT(0);
|
||||
}
|
||||
|
||||
if (loop && (p->item[i].kind != PM_LOOP))
|
||||
{
|
||||
buffer_puts(buf, "+ ");
|
||||
loop = 0;
|
||||
}
|
||||
}
|
||||
|
||||
buffer_puts(buf, "=]");
|
||||
@ -167,6 +226,10 @@ pmi_same(const struct f_path_mask_item *mi1, const struct f_path_mask_item *mi2)
|
||||
if (mi1->to != mi2->to)
|
||||
return 0;
|
||||
break;
|
||||
case PM_ASN_SET:
|
||||
if (!same_tree(mi1->set, mi2->set))
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
@ -176,6 +239,7 @@ static int
|
||||
pm_same(const struct f_path_mask *m1, const struct f_path_mask *m2)
|
||||
{
|
||||
if (m1->len != m2->len)
|
||||
return 0;
|
||||
|
||||
for (uint i=0; i<m1->len; i++)
|
||||
if (!pmi_same(&(m1->item[i]), &(m2->item[i])))
|
||||
|
@ -38,6 +38,7 @@ enum f_type {
|
||||
T_ENUM_ROA = 0x35,
|
||||
T_ENUM_NETTYPE = 0x36,
|
||||
T_ENUM_RA_PREFERENCE = 0x37,
|
||||
T_ENUM_AF = 0x38,
|
||||
|
||||
/* new enums go here */
|
||||
T_ENUM_EMPTY = 0x3f, /* Special hack for atomic_aggr */
|
||||
@ -71,7 +72,7 @@ struct f_val {
|
||||
lcomm lc;
|
||||
ip_addr ip;
|
||||
const net_addr *net;
|
||||
char *s;
|
||||
const char *s;
|
||||
const struct f_tree *t;
|
||||
const struct f_trie *ti;
|
||||
const struct adata *ad;
|
||||
@ -98,6 +99,7 @@ enum f_sa_code {
|
||||
SA_DEST,
|
||||
SA_IFNAME,
|
||||
SA_IFINDEX,
|
||||
SA_WEIGHT,
|
||||
} PACKED;
|
||||
|
||||
/* Static attribute definition (members of struct rta) */
|
||||
@ -137,19 +139,35 @@ struct f_tree {
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct f_trie_node4
|
||||
{
|
||||
ip4_addr addr, mask, accept;
|
||||
uint plen;
|
||||
struct f_trie_node4 *c[2];
|
||||
};
|
||||
|
||||
struct f_trie_node6
|
||||
{
|
||||
ip6_addr addr, mask, accept;
|
||||
uint plen;
|
||||
struct f_trie_node6 *c[2];
|
||||
};
|
||||
|
||||
struct f_trie_node
|
||||
{
|
||||
ip_addr addr, mask, accept;
|
||||
uint plen;
|
||||
struct f_trie_node *c[2];
|
||||
union {
|
||||
struct f_trie_node4 v4;
|
||||
struct f_trie_node6 v6;
|
||||
};
|
||||
};
|
||||
|
||||
struct f_trie
|
||||
{
|
||||
linpool *lp;
|
||||
int zero;
|
||||
uint node_size;
|
||||
struct f_trie_node root[0]; /* Root trie node follows */
|
||||
u8 zero;
|
||||
s8 ipv4; /* -1 for undefined / empty */
|
||||
u16 data_size; /* Additional data for each trie node */
|
||||
struct f_trie_node root; /* Root trie node */
|
||||
};
|
||||
|
||||
struct f_tree *f_new_tree(void);
|
||||
@ -157,8 +175,9 @@ struct f_tree *build_tree(struct f_tree *);
|
||||
const struct f_tree *find_tree(const struct f_tree *t, const struct f_val *val);
|
||||
int same_tree(const struct f_tree *t0, const struct f_tree *t2);
|
||||
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);
|
||||
|
||||
struct f_trie *f_new_trie(linpool *lp, uint node_size);
|
||||
struct f_trie *f_new_trie(linpool *lp, uint data_size);
|
||||
void *trie_add_prefix(struct f_trie *t, const net_addr *n, uint l, uint h);
|
||||
int trie_match_net(const struct f_trie *t, const net_addr *n);
|
||||
int trie_same(const struct f_trie *t1, const struct f_trie *t2);
|
||||
@ -166,6 +185,8 @@ void trie_format(const struct f_trie *t, buffer *buf);
|
||||
|
||||
#define F_CMP_ERROR 999
|
||||
|
||||
const char *f_type_name(enum f_type t);
|
||||
|
||||
int val_same(const struct f_val *v1, const struct f_val *v2);
|
||||
int val_compare(const struct f_val *v1, const struct f_val *v2);
|
||||
void val_format(const struct f_val *v, buffer *buf);
|
||||
|
110
filter/decl.m4
110
filter/decl.m4
@ -40,6 +40,7 @@ m4_divert(-1)m4_dnl
|
||||
# 106 comparator body
|
||||
# 107 struct f_line_item content
|
||||
# 108 interpreter body
|
||||
# 109 iterator body
|
||||
#
|
||||
# Here are macros to allow you to _divert to the right directions.
|
||||
m4_define(FID_STRUCT_IN, `m4_divert(101)')
|
||||
@ -50,6 +51,7 @@ m4_define(FID_LINEARIZE_BODY, `m4_divert(105)')
|
||||
m4_define(FID_SAME_BODY, `m4_divert(106)')
|
||||
m4_define(FID_LINE_IN, `m4_divert(107)')
|
||||
m4_define(FID_INTERPRET_BODY, `m4_divert(108)')
|
||||
m4_define(FID_ITERATE_BODY, `m4_divert(109)')
|
||||
|
||||
# Sometimes you want slightly different code versions in different
|
||||
# outputs.
|
||||
@ -104,7 +106,7 @@ FID_STRUCT_IN()m4_dnl
|
||||
struct f_inst * f$1;
|
||||
FID_NEW_ARGS()m4_dnl
|
||||
, struct f_inst * f$1
|
||||
FID_NEW_BODY
|
||||
FID_NEW_BODY()m4_dnl
|
||||
whati->f$1 = f$1;
|
||||
for (const struct f_inst *child = f$1; child; child = child->next) {
|
||||
what->size += child->size;
|
||||
@ -138,7 +140,7 @@ FID_IFCONST([[
|
||||
}
|
||||
FID_IFCONST([[
|
||||
const struct f_inst **items = NULL;
|
||||
if (constargs) {
|
||||
if (constargs && whati->varcount) {
|
||||
items = alloca(whati->varcount * sizeof(struct f_inst *));
|
||||
const struct f_inst *child = fvar;
|
||||
for (uint i=0; child; i++)
|
||||
@ -160,9 +162,28 @@ FID_HIC(,[[
|
||||
')
|
||||
|
||||
# Some arguments need to check their type. After that, ARG_ANY is called.
|
||||
m4_define(ARG, `ARG_ANY($1)
|
||||
m4_define(ARG, `ARG_ANY($1) ARG_TYPE($1,$2)')
|
||||
m4_define(ARG_TYPE, `ARG_TYPE_STATIC($1,$2) ARG_TYPE_DYNAMIC($1,$2)')
|
||||
|
||||
m4_define(ARG_TYPE_STATIC, `
|
||||
FID_NEW_BODY()m4_dnl
|
||||
if (f$1->type && (f$1->type != ($2)) && !f_const_promotion(f$1, ($2)))
|
||||
cf_error("Argument $1 of %s must be of type %s, got type %s",
|
||||
f_instruction_name(what->fi_code), f_type_name($2), f_type_name(f$1->type));
|
||||
FID_INTERPRET_BODY()')
|
||||
|
||||
m4_define(ARG_TYPE_DYNAMIC, `
|
||||
FID_INTERPRET_EXEC()m4_dnl
|
||||
if (v$1.type != $2) runtime("Argument $1 of instruction %s must be of type $2, got 0x%02x", f_instruction_name(what->fi_code), v$1.type)m4_dnl
|
||||
if (v$1.type != ($2))
|
||||
runtime("Argument $1 of %s must be of type %s, got type %s",
|
||||
f_instruction_name(what->fi_code), f_type_name($2), f_type_name(v$1.type));
|
||||
FID_INTERPRET_BODY()')
|
||||
|
||||
m4_define(ARG_SAME_TYPE, `
|
||||
FID_NEW_BODY()m4_dnl
|
||||
if (f$1->type && f$2->type && (f$1->type != f$2->type) &&
|
||||
!f_const_promotion(f$2, f$1->type) && !f_const_promotion(f$1, f$2->type))
|
||||
cf_error("Arguments $1 and $2 of %s must be of the same type", f_instruction_name(what->fi_code));
|
||||
FID_INTERPRET_BODY()')
|
||||
|
||||
# Executing another filter line. This replaces the recursion
|
||||
@ -192,6 +213,8 @@ FID_LINEARIZE_BODY()m4_dnl
|
||||
item->fl$1 = f_linearize(whati->f$1);
|
||||
FID_SAME_BODY()m4_dnl
|
||||
if (!f_same(f1->fl$1, f2->fl$1)) return 0;
|
||||
FID_ITERATE_BODY()m4_dnl
|
||||
if (whati->fl$1) BUFFER_PUSH(fit->lines) = whati->fl$1;
|
||||
FID_INTERPRET_EXEC()m4_dnl
|
||||
do { if (whati->fl$1) {
|
||||
LINEX_(whati->fl$1);
|
||||
@ -202,11 +225,27 @@ FID_INTERPRET_BODY()')
|
||||
|
||||
# Some of the instructions have a result. These constructions
|
||||
# state the result and put it to the right place.
|
||||
m4_define(RESULT, `RESULT_VAL([[ (struct f_val) { .type = $1, .val.$2 = $3 } ]])')
|
||||
m4_define(RESULT, `RESULT_TYPE([[$1]]) RESULT_([[$1]],[[$2]],[[$3]])')
|
||||
m4_define(RESULT_, `RESULT_VAL([[ (struct f_val) { .type = $1, .val.$2 = $3 } ]])')
|
||||
m4_define(RESULT_VAL, `FID_HIC(, [[do { res = $1; fstk->vcnt++; } while (0)]],
|
||||
[[return fi_constant(what, $1)]])')
|
||||
m4_define(RESULT_VOID, `RESULT_VAL([[ (struct f_val) { .type = T_VOID } ]])')
|
||||
|
||||
m4_define(ERROR,
|
||||
`m4_errprint(m4___file__:m4___line__: $*
|
||||
)m4_m4exit(1)')
|
||||
|
||||
# This macro specifies result type and makes there are no conflicting definitions
|
||||
m4_define(RESULT_TYPE,
|
||||
`m4_ifdef([[INST_RESULT_TYPE]],
|
||||
[[m4_ifelse(INST_RESULT_TYPE,$1,,[[ERROR([[Multiple type definitons]])]])]],
|
||||
[[m4_define(INST_RESULT_TYPE,$1) RESULT_TYPE_($1)]])')
|
||||
|
||||
m4_define(RESULT_TYPE_, `
|
||||
FID_NEW_BODY()m4_dnl
|
||||
what->type = $1;
|
||||
FID_INTERPRET_BODY()')
|
||||
|
||||
# Some common filter instruction members
|
||||
m4_define(SYMBOL, `FID_MEMBER(struct symbol *, sym, [[strcmp(f1->sym->name, f2->sym->name) || (f1->sym->class != f2->sym->class)]], "symbol %s", item->sym->name)')
|
||||
m4_define(RTC, `FID_MEMBER(struct rtable_config *, rtc, [[strcmp(f1->rtc->name, f2->rtc->name)]], "route table %s", item->rtc->name)')
|
||||
@ -230,6 +269,7 @@ m4_define(ACCESS_RTE, `FID_HIC(,[[do { if (!fs->rte) runtime("No route to access
|
||||
# 7 dump line item callers
|
||||
# 8 linearize
|
||||
# 9 same (filter comparator)
|
||||
# 10 iterate
|
||||
# 1 union in struct f_inst
|
||||
# 3 constructors + interpreter
|
||||
#
|
||||
@ -250,6 +290,7 @@ m4_define(FID_DUMP, `FID_ZONE(6, Dump line)')
|
||||
m4_define(FID_DUMP_CALLER, `FID_ZONE(7, Dump line caller)')
|
||||
m4_define(FID_LINEARIZE, `FID_ZONE(8, Linearize)')
|
||||
m4_define(FID_SAME, `FID_ZONE(9, Comparison)')
|
||||
m4_define(FID_ITERATE, `FID_ZONE(10, Iteration)')
|
||||
|
||||
# This macro does all the code wrapping. See inline comments.
|
||||
m4_define(INST_FLUSH, `m4_ifdef([[INST_NAME]], [[
|
||||
@ -337,14 +378,22 @@ m4_undivert(106)m4_dnl
|
||||
#undef f2
|
||||
break;
|
||||
|
||||
FID_ITERATE()m4_dnl The iterator
|
||||
case INST_NAME():
|
||||
#define whati (&(what->i_]]INST_NAME()[[))
|
||||
m4_undivert(109)m4_dnl
|
||||
#undef whati
|
||||
break;
|
||||
|
||||
m4_divert(-1)FID_FLUSH(101,200)m4_dnl And finally this flushes all the unused diversions
|
||||
]])')
|
||||
|
||||
m4_define(INST, `m4_dnl This macro is called on beginning of each instruction.
|
||||
INST_FLUSH()m4_dnl First, old data is flushed
|
||||
m4_define([[INST_NAME]], [[$1]])m4_dnl Then we store instruction name,
|
||||
m4_define([[INST_INVAL]], [[$2]])m4_dnl instruction input value count
|
||||
m4_undefine([[INST_NEVER_CONSTANT]])m4_dnl and reset NEVER_CONSTANT trigger.
|
||||
m4_define([[INST_INVAL]], [[$2]])m4_dnl instruction input value count,
|
||||
m4_undefine([[INST_NEVER_CONSTANT]])m4_dnl reset NEVER_CONSTANT trigger,
|
||||
m4_undefine([[INST_RESULT_TYPE]])m4_dnl and reset RESULT_TYPE value.
|
||||
FID_INTERPRET_BODY()m4_dnl By default, every code is interpreter code.
|
||||
')
|
||||
|
||||
@ -404,7 +453,7 @@ FID_WR_PUT(5)
|
||||
};
|
||||
|
||||
const char *
|
||||
f_instruction_name(enum f_instruction_code fi)
|
||||
f_instruction_name_(enum f_instruction_code fi)
|
||||
{
|
||||
if (fi < (sizeof(f_instruction_name_str) / sizeof(f_instruction_name_str[0])))
|
||||
return f_instruction_name_str[fi];
|
||||
@ -430,6 +479,25 @@ fi_constant(struct f_inst *what, struct f_val val)
|
||||
return what;
|
||||
}
|
||||
|
||||
static int
|
||||
f_const_promotion(struct f_inst *arg, enum f_type want)
|
||||
{
|
||||
if (arg->fi_code != FI_CONSTANT)
|
||||
return 0;
|
||||
|
||||
struct f_val *c = &arg->i_FI_CONSTANT.val;
|
||||
|
||||
if ((c->type == T_IP) && ipa_is_ip4(c->val.ip) && (want == T_QUAD)) {
|
||||
*c = (struct f_val) {
|
||||
.type = T_QUAD,
|
||||
.val.i = ipa_to_u32(c->val.ip),
|
||||
};
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define v1 whati->f1->i_FI_CONSTANT.val
|
||||
#define v2 whati->f2->i_FI_CONSTANT.val
|
||||
#define v3 whati->f3->i_FI_CONSTANT.val
|
||||
@ -459,7 +527,7 @@ void f_dump_line(const struct f_line *dest, uint indent)
|
||||
debug("%sFilter line %p (len=%u)\n", INDENT, dest, dest->len);
|
||||
for (uint i=0; i<dest->len; i++) {
|
||||
const struct f_line_item *item = &dest->items[i];
|
||||
debug("%sInstruction %s at line %u\n", INDENT, f_instruction_name(item->fi_code), item->lineno);
|
||||
debug("%sInstruction %s at line %u\n", INDENT, f_instruction_name_(item->fi_code), item->lineno);
|
||||
switch (item->fi_code) {
|
||||
FID_WR_PUT(7)
|
||||
default: bug("Unknown instruction %x in f_dump_line", item->fi_code);
|
||||
@ -494,7 +562,7 @@ f_linearize_concat(const struct f_inst * const inst[], uint count)
|
||||
for (uint i=0; i<count; i++)
|
||||
out->len = linearize(out, inst[i], out->len);
|
||||
|
||||
#if DEBUGGING
|
||||
#ifdef LOCAL_DEBUG
|
||||
f_dump_line(out, 0);
|
||||
#endif
|
||||
return out;
|
||||
@ -527,6 +595,27 @@ FID_WR_PUT(9)
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Part of FI_SWITCH filter iterator */
|
||||
static void
|
||||
f_add_tree_lines(const struct f_tree *t, void *fit_)
|
||||
{
|
||||
struct filter_iterator * fit = fit_;
|
||||
|
||||
if (t->data)
|
||||
BUFFER_PUSH(fit->lines) = t->data;
|
||||
}
|
||||
|
||||
/* Filter line iterator */
|
||||
void
|
||||
f_add_lines(const struct f_line_item *what, struct filter_iterator *fit)
|
||||
{
|
||||
switch(what->fi_code) {
|
||||
FID_WR_PUT(10)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ >= 6
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
@ -541,6 +630,7 @@ FID_WR_PUT(4)m4_dnl
|
||||
struct f_inst {
|
||||
struct f_inst *next; /* Next instruction */
|
||||
enum f_instruction_code fi_code; /* Instruction code */
|
||||
enum f_type type; /* Type of returned value, if known */
|
||||
int size; /* How many instructions are underneath */
|
||||
int lineno; /* Line number */
|
||||
union {
|
||||
|
328
filter/f-inst.c
328
filter/f-inst.c
@ -172,6 +172,22 @@
|
||||
* m4_dnl use macros f1 and f2.
|
||||
* m4_dnl For writing directly here, use FID_SAME_BODY.
|
||||
*
|
||||
* m4_dnl f_add_lines(...)
|
||||
* m4_dnl {
|
||||
* m4_dnl switch (what_->fi_code) {
|
||||
* m4_dnl case FI_EXAMPLE:
|
||||
* m4_dnl (109) [[ put it here ]]
|
||||
* m4_dnl break;
|
||||
* m4_dnl }
|
||||
* m4_dnl }
|
||||
* m4_dnl This code adds new filter lines reachable from the instruction
|
||||
* m4_dnl to the filter iterator line buffer. This is for instructions
|
||||
* m4_dnl that changes conrol flow, like FI_CONDITION or FI_CALL, most
|
||||
* m4_dnl instructions do not need to update it. It is used in generic
|
||||
* m4_dnl filter iteration code (FILTER_ITERATE*). For accessing your
|
||||
* m4_dnl custom instruction data, use macros f1 and f2. For writing
|
||||
* m4_dnl directly here, use FID_ITERATE_BODY.
|
||||
*
|
||||
* m4_dnl interpret(...)
|
||||
* m4_dnl {
|
||||
* m4_dnl switch (what->fi_code) {
|
||||
@ -226,6 +242,9 @@
|
||||
}
|
||||
INST(FI_AND, 1, 1) {
|
||||
ARG(1,T_BOOL);
|
||||
ARG_TYPE_STATIC(2,T_BOOL);
|
||||
RESULT_TYPE(T_BOOL);
|
||||
|
||||
if (v1.val.i)
|
||||
LINE(2,0);
|
||||
else
|
||||
@ -233,6 +252,9 @@
|
||||
}
|
||||
INST(FI_OR, 1, 1) {
|
||||
ARG(1,T_BOOL);
|
||||
ARG_TYPE_STATIC(2,T_BOOL);
|
||||
RESULT_TYPE(T_BOOL);
|
||||
|
||||
if (!v1.val.i)
|
||||
LINE(2,0);
|
||||
else
|
||||
@ -255,7 +277,7 @@
|
||||
|
||||
FID_MEMBER(enum ec_subtype, ecs, f1->ecs != f2->ecs, "ec subtype %s", ec_subtype_str(item->ecs));
|
||||
|
||||
int check, ipv4_used;
|
||||
int ipv4_used;
|
||||
u32 key, val;
|
||||
|
||||
if (v1.type == T_INT) {
|
||||
@ -273,21 +295,20 @@
|
||||
|
||||
val = v2.val.i;
|
||||
|
||||
if (ecs == EC_GENERIC) {
|
||||
check = 0; RESULT(T_EC, ec, ec_generic(key, val));
|
||||
}
|
||||
else if (ipv4_used) {
|
||||
check = 1; RESULT(T_EC, ec, ec_ip4(ecs, key, val));
|
||||
}
|
||||
else if (key < 0x10000) {
|
||||
check = 0; RESULT(T_EC, ec, ec_as2(ecs, key, val));
|
||||
}
|
||||
else {
|
||||
check = 1; RESULT(T_EC, ec, ec_as4(ecs, key, val));
|
||||
}
|
||||
|
||||
if (check && (val > 0xFFFF))
|
||||
runtime("Value %u > %u out of bounds in EC constructor", val, 0xFFFF);
|
||||
if (ecs == EC_GENERIC)
|
||||
RESULT(T_EC, ec, ec_generic(key, val));
|
||||
else if (ipv4_used)
|
||||
if (val <= 0xFFFF)
|
||||
RESULT(T_EC, ec, ec_ip4(ecs, key, val));
|
||||
else
|
||||
runtime("4-byte value %u can't be used with IP-address key in extended community", val);
|
||||
else if (key < 0x10000)
|
||||
RESULT(T_EC, ec, ec_as2(ecs, key, val));
|
||||
else
|
||||
if (val <= 0xFFFF)
|
||||
RESULT(T_EC, ec, ec_as4(ecs, key, val));
|
||||
else
|
||||
runtime("4-byte value %u can't be used with 4-byte ASN in extended community", val);
|
||||
}
|
||||
|
||||
INST(FI_LC_CONSTRUCT, 3, 1) {
|
||||
@ -306,14 +327,37 @@
|
||||
for (uint i=0; i<whati->varcount; i++) {
|
||||
switch (vv(i).type) {
|
||||
case T_PATH_MASK_ITEM:
|
||||
if (vv(i).val.pmi.kind == PM_LOOP)
|
||||
{
|
||||
if (i == 0)
|
||||
runtime("Path mask iterator '+' cannot be first");
|
||||
|
||||
/* We want PM_LOOP as prefix operator */
|
||||
pm->item[i] = pm->item[i - 1];
|
||||
pm->item[i - 1] = vv(i).val.pmi;
|
||||
break;
|
||||
}
|
||||
|
||||
pm->item[i] = vv(i).val.pmi;
|
||||
break;
|
||||
|
||||
case T_INT:
|
||||
pm->item[i] = (struct f_path_mask_item) {
|
||||
.asn = vv(i).val.i,
|
||||
.kind = PM_ASN,
|
||||
};
|
||||
break;
|
||||
|
||||
case T_SET:
|
||||
if (vv(i).val.t->from.type != T_INT)
|
||||
runtime("Only integer sets allowed in path mask");
|
||||
|
||||
pm->item[i] = (struct f_path_mask_item) {
|
||||
.set = vv(i).val.t,
|
||||
.kind = PM_ASN_SET,
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
runtime( "Error resolving path mask template: value not an integer" );
|
||||
}
|
||||
@ -339,6 +383,8 @@
|
||||
INST(FI_LT, 2, 1) {
|
||||
ARG_ANY(1);
|
||||
ARG_ANY(2);
|
||||
ARG_SAME_TYPE(1, 2);
|
||||
|
||||
int i = val_compare(&v1, &v2);
|
||||
if (i == F_CMP_ERROR)
|
||||
runtime( "Can't compare values of incompatible types" );
|
||||
@ -348,6 +394,8 @@
|
||||
INST(FI_LTE, 2, 1) {
|
||||
ARG_ANY(1);
|
||||
ARG_ANY(2);
|
||||
ARG_SAME_TYPE(1, 2);
|
||||
|
||||
int i = val_compare(&v1, &v2);
|
||||
if (i == F_CMP_ERROR)
|
||||
runtime( "Can't compare values of incompatible types" );
|
||||
@ -404,18 +452,7 @@
|
||||
NEVER_CONSTANT;
|
||||
ARG_ANY(1);
|
||||
SYMBOL;
|
||||
|
||||
if ((sym->class != (SYM_VARIABLE | v1.type)) && (v1.type != T_VOID))
|
||||
{
|
||||
/* IP->Quad implicit conversion */
|
||||
if ((sym->class == (SYM_VARIABLE | T_QUAD)) && val_is_ip4(&v1))
|
||||
v1 = (struct f_val) {
|
||||
.type = T_QUAD,
|
||||
.val.i = ipa_to_u32(v1.val.ip),
|
||||
};
|
||||
else
|
||||
runtime( "Assigning to variable of incompatible type" );
|
||||
}
|
||||
ARG_TYPE(1, sym->class & 0xff);
|
||||
|
||||
fstk->vstk[curline.vbase + sym->offset] = v1;
|
||||
}
|
||||
@ -423,6 +460,7 @@
|
||||
INST(FI_VAR_GET, 0, 1) {
|
||||
SYMBOL;
|
||||
NEVER_CONSTANT;
|
||||
RESULT_TYPE(sym->class & 0xff);
|
||||
RESULT_VAL(fstk->vstk[curline.vbase + sym->offset]);
|
||||
}
|
||||
|
||||
@ -435,6 +473,7 @@
|
||||
val_dump(&(item->val))
|
||||
);
|
||||
|
||||
RESULT_TYPE(val.type);
|
||||
RESULT_VAL(val);
|
||||
}
|
||||
|
||||
@ -467,8 +506,6 @@
|
||||
FID_MEMBER(enum filter_return, fret, f1->fret != f2->fret, "%s", filter_return_str(item->fret));
|
||||
|
||||
switch (whati->fret) {
|
||||
case F_QUITBIRD:
|
||||
die( "Filter asked me to die" );
|
||||
case F_ACCEPT: /* Should take care about turning ACCEPT into MODIFY */
|
||||
case F_ERROR:
|
||||
case F_REJECT: /* Maybe print complete route along with reason to reject route? */
|
||||
@ -495,6 +532,7 @@
|
||||
case SA_DEST: RESULT(sa.f_type, i, rta->dest); break;
|
||||
case SA_IFNAME: RESULT(sa.f_type, s, rta->nh.iface ? rta->nh.iface->name : ""); break;
|
||||
case SA_IFINDEX: RESULT(sa.f_type, i, rta->nh.iface ? rta->nh.iface->index : 0); break;
|
||||
case SA_WEIGHT: RESULT(sa.f_type, i, rta->nh.weight + 1); break;
|
||||
|
||||
default:
|
||||
bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code);
|
||||
@ -506,8 +544,7 @@
|
||||
ACCESS_RTE;
|
||||
ARG_ANY(1);
|
||||
STATIC_ATTR;
|
||||
if (sa.f_type != v1.type)
|
||||
runtime( "Attempt to set static attribute to incompatible type" );
|
||||
ARG_TYPE(1, sa.f_type);
|
||||
|
||||
f_rta_cow(fs);
|
||||
{
|
||||
@ -522,7 +559,8 @@
|
||||
case SA_GW:
|
||||
{
|
||||
ip_addr ip = v1.val.ip;
|
||||
neighbor *n = neigh_find(rta->src->proto, ip, NULL, 0);
|
||||
struct iface *ifa = ipa_is_link_local(ip) ? rta->nh.iface : NULL;
|
||||
neighbor *n = neigh_find(rta->src->proto, ip, ifa, 0);
|
||||
if (!n || (n->scope == SCOPE_HOST))
|
||||
runtime( "Invalid gw address" );
|
||||
|
||||
@ -566,6 +604,20 @@
|
||||
}
|
||||
break;
|
||||
|
||||
case SA_WEIGHT:
|
||||
{
|
||||
int i = v1.val.i;
|
||||
if (i < 1 || i > 256)
|
||||
runtime( "Setting weight value out of bounds" );
|
||||
if (rta->dest != RTD_UNICAST)
|
||||
runtime( "Setting weight needs regular nexthop " );
|
||||
|
||||
/* Set weight on all next hops */
|
||||
for (struct nexthop *nh = &rta->nh; nh; nh = nh->next)
|
||||
nh->weight = i - 1;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code);
|
||||
}
|
||||
@ -576,31 +628,32 @@
|
||||
DYNAMIC_ATTR;
|
||||
ACCESS_RTE;
|
||||
ACCESS_EATTRS;
|
||||
RESULT_TYPE(da.f_type);
|
||||
{
|
||||
eattr *e = ea_find(*fs->eattrs, da.ea_code);
|
||||
|
||||
if (!e) {
|
||||
/* A special case: undefined as_path looks like empty as_path */
|
||||
if (da.type == EAF_TYPE_AS_PATH) {
|
||||
RESULT(T_PATH, ad, &null_adata);
|
||||
RESULT_(T_PATH, ad, &null_adata);
|
||||
break;
|
||||
}
|
||||
|
||||
/* The same special case for int_set */
|
||||
if (da.type == EAF_TYPE_INT_SET) {
|
||||
RESULT(T_CLIST, ad, &null_adata);
|
||||
RESULT_(T_CLIST, ad, &null_adata);
|
||||
break;
|
||||
}
|
||||
|
||||
/* The same special case for ec_set */
|
||||
if (da.type == EAF_TYPE_EC_SET) {
|
||||
RESULT(T_ECLIST, ad, &null_adata);
|
||||
RESULT_(T_ECLIST, ad, &null_adata);
|
||||
break;
|
||||
}
|
||||
|
||||
/* The same special case for lc_set */
|
||||
if (da.type == EAF_TYPE_LC_SET) {
|
||||
RESULT(T_LCLIST, ad, &null_adata);
|
||||
RESULT_(T_LCLIST, ad, &null_adata);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -611,31 +664,31 @@
|
||||
|
||||
switch (e->type & EAF_TYPE_MASK) {
|
||||
case EAF_TYPE_INT:
|
||||
RESULT(da.f_type, i, e->u.data);
|
||||
RESULT_(da.f_type, i, e->u.data);
|
||||
break;
|
||||
case EAF_TYPE_ROUTER_ID:
|
||||
RESULT(T_QUAD, i, e->u.data);
|
||||
RESULT_(T_QUAD, i, e->u.data);
|
||||
break;
|
||||
case EAF_TYPE_OPAQUE:
|
||||
RESULT(T_ENUM_EMPTY, i, 0);
|
||||
RESULT_(T_ENUM_EMPTY, i, 0);
|
||||
break;
|
||||
case EAF_TYPE_IP_ADDRESS:
|
||||
RESULT(T_IP, ip, *((ip_addr *) e->u.ptr->data));
|
||||
RESULT_(T_IP, ip, *((ip_addr *) e->u.ptr->data));
|
||||
break;
|
||||
case EAF_TYPE_AS_PATH:
|
||||
RESULT(T_PATH, ad, e->u.ptr);
|
||||
RESULT_(T_PATH, ad, e->u.ptr);
|
||||
break;
|
||||
case EAF_TYPE_BITFIELD:
|
||||
RESULT(T_BOOL, i, !!(e->u.data & (1u << da.bit)));
|
||||
RESULT_(T_BOOL, i, !!(e->u.data & (1u << da.bit)));
|
||||
break;
|
||||
case EAF_TYPE_INT_SET:
|
||||
RESULT(T_CLIST, ad, e->u.ptr);
|
||||
RESULT_(T_CLIST, ad, e->u.ptr);
|
||||
break;
|
||||
case EAF_TYPE_EC_SET:
|
||||
RESULT(T_ECLIST, ad, e->u.ptr);
|
||||
RESULT_(T_ECLIST, ad, e->u.ptr);
|
||||
break;
|
||||
case EAF_TYPE_LC_SET:
|
||||
RESULT(T_LCLIST, ad, e->u.ptr);
|
||||
RESULT_(T_LCLIST, ad, e->u.ptr);
|
||||
break;
|
||||
case EAF_TYPE_UNDEF:
|
||||
RESULT_VOID;
|
||||
@ -651,6 +704,7 @@
|
||||
ACCESS_EATTRS;
|
||||
ARG_ANY(1);
|
||||
DYNAMIC_ATTR;
|
||||
ARG_TYPE(1, da.f_type);
|
||||
{
|
||||
struct ea_list *l = lp_alloc(fs->pool, sizeof(struct ea_list) + sizeof(eattr));
|
||||
|
||||
@ -663,20 +717,7 @@
|
||||
|
||||
switch (da.type) {
|
||||
case EAF_TYPE_INT:
|
||||
if (v1.type != da.f_type)
|
||||
runtime( "Setting int attribute to non-int value" );
|
||||
l->attrs[0].u.data = v1.val.i;
|
||||
break;
|
||||
|
||||
case EAF_TYPE_ROUTER_ID:
|
||||
/* IP->Quad implicit conversion */
|
||||
if (val_is_ip4(&v1)) {
|
||||
l->attrs[0].u.data = ipa_to_u32(v1.val.ip);
|
||||
break;
|
||||
}
|
||||
/* T_INT for backward compatibility */
|
||||
if ((v1.type != T_QUAD) && (v1.type != T_INT))
|
||||
runtime( "Setting quad attribute to non-quad value" );
|
||||
l->attrs[0].u.data = v1.val.i;
|
||||
break;
|
||||
|
||||
@ -684,9 +725,7 @@
|
||||
runtime( "Setting opaque attribute is not allowed" );
|
||||
break;
|
||||
|
||||
case EAF_TYPE_IP_ADDRESS:
|
||||
if (v1.type != T_IP)
|
||||
runtime( "Setting ip attribute to non-ip value" );
|
||||
case EAF_TYPE_IP_ADDRESS:;
|
||||
int len = sizeof(ip_addr);
|
||||
struct adata *ad = lp_alloc(fs->pool, sizeof(struct adata) + len);
|
||||
ad->length = len;
|
||||
@ -695,14 +734,13 @@
|
||||
break;
|
||||
|
||||
case EAF_TYPE_AS_PATH:
|
||||
if (v1.type != T_PATH)
|
||||
runtime( "Setting path attribute to non-path value" );
|
||||
case EAF_TYPE_INT_SET:
|
||||
case EAF_TYPE_EC_SET:
|
||||
case EAF_TYPE_LC_SET:
|
||||
l->attrs[0].u.ptr = v1.val.ad;
|
||||
break;
|
||||
|
||||
case EAF_TYPE_BITFIELD:
|
||||
if (v1.type != T_BOOL)
|
||||
runtime( "Setting bit in bitfield attribute to non-bool value" );
|
||||
{
|
||||
/* First, we have to find the old value */
|
||||
eattr *e = ea_find(*fs->eattrs, da.ea_code);
|
||||
@ -715,24 +753,6 @@
|
||||
}
|
||||
break;
|
||||
|
||||
case EAF_TYPE_INT_SET:
|
||||
if (v1.type != T_CLIST)
|
||||
runtime( "Setting clist attribute to non-clist value" );
|
||||
l->attrs[0].u.ptr = v1.val.ad;
|
||||
break;
|
||||
|
||||
case EAF_TYPE_EC_SET:
|
||||
if (v1.type != T_ECLIST)
|
||||
runtime( "Setting eclist attribute to non-eclist value" );
|
||||
l->attrs[0].u.ptr = v1.val.ad;
|
||||
break;
|
||||
|
||||
case EAF_TYPE_LC_SET:
|
||||
if (v1.type != T_LCLIST)
|
||||
runtime( "Setting lclist attribute to non-lclist value" );
|
||||
l->attrs[0].u.ptr = v1.val.ad;
|
||||
break;
|
||||
|
||||
default:
|
||||
bug("Unknown dynamic attribute type");
|
||||
}
|
||||
@ -791,18 +811,76 @@
|
||||
}
|
||||
}
|
||||
|
||||
INST(FI_SADR_SRC, 1, 1) { /* Get SADR src prefix */
|
||||
INST(FI_NET_SRC, 1, 1) { /* Get src prefix */
|
||||
ARG(1, T_NET);
|
||||
if (!net_is_sadr(v1.val.net))
|
||||
runtime( "SADR expected" );
|
||||
|
||||
net_addr_ip6_sadr *net = (void *) v1.val.net;
|
||||
net_addr_union *net = (void *) v1.val.net;
|
||||
net_addr *src = falloc(sizeof(net_addr_ip6));
|
||||
net_fill_ip6(src, net->src_prefix, net->src_pxlen);
|
||||
const byte *part;
|
||||
|
||||
switch(v1.val.net->type) {
|
||||
case NET_FLOW4:
|
||||
part = flow4_get_part(&net->flow4, FLOW_TYPE_SRC_PREFIX);
|
||||
if (part)
|
||||
net_fill_ip4(src, flow_read_ip4_part(part), flow_read_pxlen(part));
|
||||
else
|
||||
net_fill_ip4(src, IP4_NONE, 0);
|
||||
break;
|
||||
|
||||
case NET_FLOW6:
|
||||
part = flow6_get_part(&net->flow6, FLOW_TYPE_SRC_PREFIX);
|
||||
if (part)
|
||||
net_fill_ip6(src, flow_read_ip6_part(part), flow_read_pxlen(part));
|
||||
else
|
||||
net_fill_ip6(src, IP6_NONE, 0);
|
||||
break;
|
||||
|
||||
case NET_IP6_SADR:
|
||||
net_fill_ip6(src, net->ip6_sadr.src_prefix, net->ip6_sadr.src_pxlen);
|
||||
break;
|
||||
|
||||
default:
|
||||
runtime( "Flow or SADR expected" );
|
||||
}
|
||||
|
||||
RESULT(T_NET, net, src);
|
||||
}
|
||||
|
||||
INST(FI_NET_DST, 1, 1) { /* Get dst prefix */
|
||||
ARG(1, T_NET);
|
||||
|
||||
net_addr_union *net = (void *) v1.val.net;
|
||||
net_addr *dst = falloc(sizeof(net_addr_ip6));
|
||||
const byte *part;
|
||||
|
||||
switch(v1.val.net->type) {
|
||||
case NET_FLOW4:
|
||||
part = flow4_get_part(&net->flow4, FLOW_TYPE_DST_PREFIX);
|
||||
if (part)
|
||||
net_fill_ip4(dst, flow_read_ip4_part(part), flow_read_pxlen(part));
|
||||
else
|
||||
net_fill_ip4(dst, IP4_NONE, 0);
|
||||
break;
|
||||
|
||||
case NET_FLOW6:
|
||||
part = flow6_get_part(&net->flow6, FLOW_TYPE_DST_PREFIX);
|
||||
if (part)
|
||||
net_fill_ip6(dst, flow_read_ip6_part(part), flow_read_pxlen(part));
|
||||
else
|
||||
net_fill_ip6(dst, IP6_NONE, 0);
|
||||
break;
|
||||
|
||||
case NET_IP6_SADR:
|
||||
net_fill_ip6(dst, net->ip6_sadr.dst_prefix, net->ip6_sadr.dst_pxlen);
|
||||
break;
|
||||
|
||||
default:
|
||||
runtime( "Flow or SADR expected" );
|
||||
}
|
||||
|
||||
RESULT(T_NET, net, dst);
|
||||
}
|
||||
|
||||
INST(FI_ROA_MAXLEN, 1, 1) { /* Get ROA max prefix length */
|
||||
ARG(1, T_NET);
|
||||
if (!net_is_roa(v1.val.net))
|
||||
@ -837,14 +915,14 @@
|
||||
|
||||
INST(FI_AS_PATH_FIRST, 1, 1) { /* Get first ASN from AS PATH */
|
||||
ARG(1, T_PATH);
|
||||
int as = 0;
|
||||
u32 as = 0;
|
||||
as_path_get_first(v1.val.ad, &as);
|
||||
RESULT(T_INT, i, as);
|
||||
}
|
||||
|
||||
INST(FI_AS_PATH_LAST, 1, 1) { /* Get last ASN from AS PATH */
|
||||
ARG(1, T_PATH);
|
||||
int as = 0;
|
||||
u32 as = 0;
|
||||
as_path_get_last(v1.val.ad, &as);
|
||||
RESULT(T_INT, i, as);
|
||||
}
|
||||
@ -861,18 +939,17 @@
|
||||
uint retpos = fstk->vcnt;
|
||||
|
||||
/* Drop every sub-block including ourselves */
|
||||
while ((fstk->ecnt-- > 0) && !(fstk->estk[fstk->ecnt].emask & FE_RETURN))
|
||||
;
|
||||
do fstk->ecnt--;
|
||||
while ((fstk->ecnt > 0) && !(fstk->estk[fstk->ecnt].emask & FE_RETURN));
|
||||
|
||||
/* Now we are at the caller frame; if no such, try to convert to accept/reject. */
|
||||
if (!fstk->ecnt)
|
||||
{
|
||||
if (fstk->vstk[retpos].type == T_BOOL)
|
||||
if (fstk->vstk[retpos].val.i)
|
||||
return F_ACCEPT;
|
||||
else
|
||||
return F_REJECT;
|
||||
return (fstk->vstk[retpos].val.i) ? F_ACCEPT : F_REJECT;
|
||||
else
|
||||
runtime("Can't return non-bool from non-function");
|
||||
}
|
||||
|
||||
/* Set the value stack position, overwriting the former implicit void */
|
||||
fstk->vcnt = fstk->estk[fstk->ecnt].ventry - 1;
|
||||
@ -885,6 +962,15 @@
|
||||
NEVER_CONSTANT;
|
||||
SYMBOL;
|
||||
|
||||
FID_SAME_BODY()
|
||||
if (!(f1->sym->flags & SYM_FLAG_SAME))
|
||||
return 0;
|
||||
|
||||
FID_ITERATE_BODY()
|
||||
BUFFER_PUSH(fit->lines) = whati->sym->function;
|
||||
|
||||
FID_INTERPRET_BODY()
|
||||
|
||||
/* Push the body on stack */
|
||||
LINEX(sym->function);
|
||||
curline.emask |= FE_RETURN;
|
||||
@ -912,6 +998,10 @@
|
||||
|
||||
FID_MEMBER(struct f_tree *, tree, [[!same_tree(f1->tree, f2->tree)]], "tree %p", item->tree);
|
||||
|
||||
FID_ITERATE_BODY()
|
||||
tree_walk(whati->tree, f_add_tree_lines, fit);
|
||||
|
||||
FID_INTERPRET_BODY()
|
||||
const struct f_tree *t = find_tree(tree, &v1);
|
||||
if (!t) {
|
||||
v1.type = T_VOID;
|
||||
@ -943,6 +1033,8 @@
|
||||
INST(FI_CLIST_ADD, 2, 1) { /* (Extended) Community list add */
|
||||
ARG_ANY(1);
|
||||
ARG_ANY(2);
|
||||
RESULT_TYPE(f1->type);
|
||||
|
||||
if (v1.type == T_PATH)
|
||||
runtime("Can't add to path");
|
||||
|
||||
@ -952,14 +1044,14 @@
|
||||
struct f_val dummy;
|
||||
|
||||
if ((v2.type == T_PAIR) || (v2.type == T_QUAD))
|
||||
RESULT(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, v2.val.i) ]]);
|
||||
RESULT_(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, v2.val.i) ]]);
|
||||
/* IP->Quad implicit conversion */
|
||||
else if (val_is_ip4(&v2))
|
||||
RESULT(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]);
|
||||
RESULT_(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]);
|
||||
else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy))
|
||||
runtime("Can't add set");
|
||||
else if (v2.type == T_CLIST)
|
||||
RESULT(T_CLIST, ad, [[ int_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
|
||||
RESULT_(T_CLIST, ad, [[ int_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
|
||||
else
|
||||
runtime("Can't add non-pair");
|
||||
}
|
||||
@ -970,11 +1062,11 @@
|
||||
if ((v2.type == T_SET) && eclist_set_type(v2.val.t))
|
||||
runtime("Can't add set");
|
||||
else if (v2.type == T_ECLIST)
|
||||
RESULT(T_ECLIST, ad, [[ ec_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
|
||||
RESULT_(T_ECLIST, ad, [[ ec_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
|
||||
else if (v2.type != T_EC)
|
||||
runtime("Can't add non-ec");
|
||||
else
|
||||
RESULT(T_ECLIST, ad, [[ ec_set_add(fpool, v1.val.ad, v2.val.ec) ]]);
|
||||
RESULT_(T_ECLIST, ad, [[ ec_set_add(fpool, v1.val.ad, v2.val.ec) ]]);
|
||||
}
|
||||
|
||||
else if (v1.type == T_LCLIST)
|
||||
@ -983,11 +1075,11 @@
|
||||
if ((v2.type == T_SET) && lclist_set_type(v2.val.t))
|
||||
runtime("Can't add set");
|
||||
else if (v2.type == T_LCLIST)
|
||||
RESULT(T_LCLIST, ad, [[ lc_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
|
||||
RESULT_(T_LCLIST, ad, [[ lc_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
|
||||
else if (v2.type != T_LC)
|
||||
runtime("Can't add non-lc");
|
||||
else
|
||||
RESULT(T_LCLIST, ad, [[ lc_set_add(fpool, v1.val.ad, v2.val.lc) ]]);
|
||||
RESULT_(T_LCLIST, ad, [[ lc_set_add(fpool, v1.val.ad, v2.val.lc) ]]);
|
||||
|
||||
}
|
||||
|
||||
@ -998,6 +1090,8 @@
|
||||
INST(FI_CLIST_DEL, 2, 1) { /* (Extended) Community list add or delete */
|
||||
ARG_ANY(1);
|
||||
ARG_ANY(2);
|
||||
RESULT_TYPE(f1->type);
|
||||
|
||||
if (v1.type == T_PATH)
|
||||
{
|
||||
const struct f_tree *set = NULL;
|
||||
@ -1010,7 +1104,7 @@
|
||||
else
|
||||
runtime("Can't delete non-integer (set)");
|
||||
|
||||
RESULT(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, set, key, 0) ]]);
|
||||
RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, set, key, 0) ]]);
|
||||
}
|
||||
|
||||
else if (v1.type == T_CLIST)
|
||||
@ -1019,12 +1113,12 @@
|
||||
struct f_val dummy;
|
||||
|
||||
if ((v2.type == T_PAIR) || (v2.type == T_QUAD))
|
||||
RESULT(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, v2.val.i) ]]);
|
||||
RESULT_(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, v2.val.i) ]]);
|
||||
/* IP->Quad implicit conversion */
|
||||
else if (val_is_ip4(&v2))
|
||||
RESULT(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]);
|
||||
RESULT_(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]);
|
||||
else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy) || (v2.type == T_CLIST))
|
||||
RESULT(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 0) ]]);
|
||||
RESULT_(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 0) ]]);
|
||||
else
|
||||
runtime("Can't delete non-pair");
|
||||
}
|
||||
@ -1033,22 +1127,22 @@
|
||||
{
|
||||
/* v2.val is either EC or EC-set */
|
||||
if ((v2.type == T_SET) && eclist_set_type(v2.val.t) || (v2.type == T_ECLIST))
|
||||
RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
|
||||
RESULT_(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
|
||||
else if (v2.type != T_EC)
|
||||
runtime("Can't delete non-ec");
|
||||
else
|
||||
RESULT(T_ECLIST, ad, [[ ec_set_del(fpool, v1.val.ad, v2.val.ec) ]]);
|
||||
RESULT_(T_ECLIST, ad, [[ ec_set_del(fpool, v1.val.ad, v2.val.ec) ]]);
|
||||
}
|
||||
|
||||
else if (v1.type == T_LCLIST)
|
||||
{
|
||||
/* v2.val is either LC or LC-set */
|
||||
if ((v2.type == T_SET) && lclist_set_type(v2.val.t) || (v2.type == T_LCLIST))
|
||||
RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
|
||||
RESULT_(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
|
||||
else if (v2.type != T_LC)
|
||||
runtime("Can't delete non-lc");
|
||||
else
|
||||
RESULT(T_LCLIST, ad, [[ lc_set_del(fpool, v1.val.ad, v2.val.lc) ]]);
|
||||
RESULT_(T_LCLIST, ad, [[ lc_set_del(fpool, v1.val.ad, v2.val.lc) ]]);
|
||||
}
|
||||
|
||||
else
|
||||
@ -1058,12 +1152,14 @@
|
||||
INST(FI_CLIST_FILTER, 2, 1) { /* (Extended) Community list add or delete */
|
||||
ARG_ANY(1);
|
||||
ARG_ANY(2);
|
||||
RESULT_TYPE(f1->type);
|
||||
|
||||
if (v1.type == T_PATH)
|
||||
{
|
||||
u32 key = 0;
|
||||
|
||||
if ((v2.type == T_SET) && (v2.val.t->from.type == T_INT))
|
||||
RESULT(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, v2.val.t, key, 1) ]]);
|
||||
RESULT_(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, v2.val.t, key, 1) ]]);
|
||||
else
|
||||
runtime("Can't filter integer");
|
||||
}
|
||||
@ -1074,7 +1170,7 @@
|
||||
struct f_val dummy;
|
||||
|
||||
if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy) || (v2.type == T_CLIST))
|
||||
RESULT(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 1) ]]);
|
||||
RESULT_(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 1) ]]);
|
||||
else
|
||||
runtime("Can't filter pair");
|
||||
}
|
||||
@ -1083,7 +1179,7 @@
|
||||
{
|
||||
/* v2.val is either EC or EC-set */
|
||||
if ((v2.type == T_SET) && eclist_set_type(v2.val.t) || (v2.type == T_ECLIST))
|
||||
RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
|
||||
RESULT_(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
|
||||
else
|
||||
runtime("Can't filter ec");
|
||||
}
|
||||
@ -1092,7 +1188,7 @@
|
||||
{
|
||||
/* v2.val is either LC or LC-set */
|
||||
if ((v2.type == T_SET) && lclist_set_type(v2.val.t) || (v2.type == T_LCLIST))
|
||||
RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
|
||||
RESULT_(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
|
||||
else
|
||||
runtime("Can't filter lc");
|
||||
}
|
||||
|
@ -17,6 +17,8 @@
|
||||
#include "conf/conf.h"
|
||||
#include "filter/filter.h"
|
||||
#include "filter/data.h"
|
||||
#include "lib/buffer.h"
|
||||
#include "lib/flowspec.h"
|
||||
|
||||
/* Flags for instructions */
|
||||
enum f_instruction_flags {
|
||||
@ -29,7 +31,9 @@ enum f_instruction_flags {
|
||||
#define f_new_inst(...) MACRO_CONCAT_AFTER(f_new_inst_, MACRO_FIRST(__VA_ARGS__))(__VA_ARGS__)
|
||||
|
||||
/* Convert the instruction back to the enum name */
|
||||
const char *f_instruction_name(enum f_instruction_code fi);
|
||||
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; }
|
||||
|
||||
/* Filter structures for execution */
|
||||
/* Line of instructions to be unconditionally executed one after another */
|
||||
@ -47,6 +51,41 @@ static inline struct f_line *f_linearize(const struct f_inst *root)
|
||||
|
||||
void f_dump_line(const struct f_line *, uint indent);
|
||||
|
||||
|
||||
/* Recursive iteration over filter instructions */
|
||||
|
||||
struct filter_iterator {
|
||||
BUFFER_(const struct f_line *) lines;
|
||||
};
|
||||
|
||||
void f_add_lines(const struct f_line_item *what, struct filter_iterator *fit);
|
||||
|
||||
#define FILTER_ITERATE_INIT(fit, filter, pool) \
|
||||
({ \
|
||||
BUFFER_INIT((fit)->lines, (pool), 32); \
|
||||
BUFFER_PUSH((fit)->lines) = (filter)->root; \
|
||||
})
|
||||
|
||||
#define FILTER_ITERATE(fit, fi) ({ \
|
||||
const struct f_line *fl_; \
|
||||
while (!BUFFER_EMPTY((fit)->lines)) \
|
||||
{ \
|
||||
BUFFER_POP((fit)->lines); \
|
||||
fl_ = (fit)->lines.data[(fit)->lines.used]; \
|
||||
for (uint i_ = 0; i_ < fl_->len; i_++) \
|
||||
{ \
|
||||
const struct f_line_item *fi = &fl_->items[i_]; \
|
||||
f_add_lines(fi, (fit));
|
||||
|
||||
#define FILTER_ITERATE_END } } })
|
||||
|
||||
#define FILTER_ITERATE_CLEANUP(fit) \
|
||||
({ \
|
||||
mb_free((fit)->lines.data); \
|
||||
memset((fit), 0, sizeof(struct filter_iterator)); \
|
||||
})
|
||||
|
||||
|
||||
struct filter *f_new_where(struct f_inst *);
|
||||
static inline struct f_dynamic_attr f_new_dynamic_attr(u8 type, enum f_type f_type, uint code) /* Type as core knows it, type as filters know it, and code of dynamic attribute */
|
||||
{ return (struct f_dynamic_attr) { .type = type, .f_type = f_type, .ea_code = code }; } /* f_type currently unused; will be handy for static type checking */
|
||||
|
@ -32,33 +32,12 @@ filter_name(const struct filter *filter)
|
||||
|
||||
struct filter *f_new_where(struct f_inst *where)
|
||||
{
|
||||
struct f_inst acc = {
|
||||
.fi_code = FI_DIE,
|
||||
.lineno = ifs->lino,
|
||||
.size = 1,
|
||||
.i_FI_DIE = { .fret = F_ACCEPT, },
|
||||
};
|
||||
|
||||
struct f_inst rej = {
|
||||
.fi_code = FI_DIE,
|
||||
.lineno = ifs->lino,
|
||||
.size = 1,
|
||||
.i_FI_DIE = { .fret = F_REJECT, },
|
||||
};
|
||||
|
||||
struct f_inst i = {
|
||||
.fi_code = FI_CONDITION,
|
||||
.lineno = ifs->lino,
|
||||
.size = 3 + where->size,
|
||||
.i_FI_CONDITION = {
|
||||
.f1 = where,
|
||||
.f2 = &acc,
|
||||
.f3 = &rej,
|
||||
},
|
||||
};
|
||||
struct f_inst *cond = f_new_inst(FI_CONDITION, where,
|
||||
f_new_inst(FI_DIE, F_ACCEPT),
|
||||
f_new_inst(FI_DIE, F_REJECT));
|
||||
|
||||
struct filter *f = cfg_allocz(sizeof(struct filter));
|
||||
f->root = f_linearize(&i);
|
||||
f->root = f_linearize(cond);
|
||||
return f;
|
||||
}
|
||||
|
||||
|
@ -32,8 +32,9 @@
|
||||
#include "lib/socket.h"
|
||||
#include "lib/string.h"
|
||||
#include "lib/unaligned.h"
|
||||
#include "lib/net.h"
|
||||
#include "lib/ip.h"
|
||||
#include "lib/net.h"
|
||||
#include "lib/flowspec.h"
|
||||
#include "nest/route.h"
|
||||
#include "nest/protocol.h"
|
||||
#include "nest/iface.h"
|
||||
@ -174,7 +175,7 @@ interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val)
|
||||
|
||||
#define curline fstk->estk[fstk->ecnt-1]
|
||||
|
||||
#if DEBUGGING
|
||||
#ifdef LOCAL_DEBUG
|
||||
debug("Interpreting line.");
|
||||
f_dump_line(line, 1);
|
||||
#endif
|
||||
@ -407,7 +408,7 @@ f_eval_buf(const struct f_line *expr, struct linpool *tmp_pool, buffer *buf)
|
||||
{
|
||||
struct f_val val;
|
||||
enum filter_return fret = f_eval(expr, tmp_pool, &val);
|
||||
if (fret > F_RETURN)
|
||||
if (fret <= F_RETURN)
|
||||
val_format(&val, buf);
|
||||
return fret;
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ enum filter_return {
|
||||
F_ACCEPT, /* Need to preserve ordering: accepts < rejects! */
|
||||
F_REJECT,
|
||||
F_ERROR,
|
||||
F_QUITBIRD,
|
||||
};
|
||||
|
||||
static inline const char *filter_return_str(const enum filter_return fret) {
|
||||
@ -36,7 +35,6 @@ static inline const char *filter_return_str(const enum filter_return fret) {
|
||||
FRS(F_ACCEPT);
|
||||
FRS(F_REJECT);
|
||||
FRS(F_ERROR);
|
||||
FRS(F_QUITBIRD);
|
||||
#undef FRS
|
||||
default: bug("This shall not happen");
|
||||
}
|
||||
|
@ -24,21 +24,18 @@
|
||||
#define BT_CONFIG_FILE "filter/test.conf"
|
||||
|
||||
|
||||
struct parse_config_file_arg {
|
||||
struct config **cp;
|
||||
const char *filename;
|
||||
};
|
||||
|
||||
static int
|
||||
parse_config_file(const void *argv)
|
||||
t_reconfig(void)
|
||||
{
|
||||
const struct parse_config_file_arg *arg = argv;
|
||||
size_t fn_size = strlen(arg->filename) + 1;
|
||||
char *filename = alloca(fn_size);
|
||||
memcpy(filename, arg->filename, fn_size);
|
||||
if (!bt_config_file_parse(BT_CONFIG_FILE))
|
||||
return 0;
|
||||
|
||||
*(arg->cp) = bt_config_file_parse(filename);
|
||||
return !!*(arg->cp);
|
||||
struct symbol *s;
|
||||
WALK_LIST(s, config->symbols)
|
||||
if ((s->class == SYM_FUNCTION) || (s->class == SYM_FILTER))
|
||||
bt_assert_msg((s->flags & SYM_FLAG_SAME), "Symbol %s same check", s->name);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -49,12 +46,6 @@ run_function(const void *arg)
|
||||
if (t->cmp)
|
||||
return t->result == f_same(t->fn, t->cmp);
|
||||
|
||||
if (!f_same(t->fn, t->fn)) {
|
||||
bt_result = bt_suite_result = 0;
|
||||
bt_log_suite_case_result(0, "The function doesn't compare to itself as the same");
|
||||
return 0;
|
||||
}
|
||||
|
||||
linpool *tmp = lp_new_default(&root_pool);
|
||||
enum filter_return fret = f_eval(t->fn, tmp, NULL);
|
||||
rfree(tmp);
|
||||
@ -85,19 +76,16 @@ main(int argc, char *argv[])
|
||||
|
||||
bt_assert_hook = bt_assert_filter;
|
||||
|
||||
struct config *c = NULL;
|
||||
struct parse_config_file_arg pcfa = { .cp = &c, .filename = BT_CONFIG_FILE };
|
||||
bt_test_suite_base(parse_config_file, "conf", (const void *) &pcfa, 0, 0, "parse config file");
|
||||
bt_test_suite_base(parse_config_file, "reconf", (const void *) &pcfa, 0, 0, "reconfigure with the same file");
|
||||
/* Initial test.conf parsing, must be done here */
|
||||
if (!bt_config_file_parse(BT_CONFIG_FILE))
|
||||
abort();
|
||||
|
||||
bt_test_suite(t_reconfig, "Testing reconfiguration");
|
||||
|
||||
struct f_bt_test_suite *t;
|
||||
WALK_LIST(t, config->tests)
|
||||
bt_test_suite_base(run_function, t->fn_name, t, BT_FORKING, BT_TIMEOUT, "%s", t->dsc);
|
||||
|
||||
bt_bird_cleanup();
|
||||
|
||||
if (c)
|
||||
{
|
||||
struct f_bt_test_suite *t;
|
||||
WALK_LIST(t, c->tests)
|
||||
bt_test_suite_base(run_function, t->fn_name, t, BT_FORKING, BT_TIMEOUT, "%s", t->dsc);
|
||||
}
|
||||
|
||||
return bt_exit_value();
|
||||
}
|
||||
|
@ -63,8 +63,8 @@ bool b;
|
||||
bt_assert(! false && ! false && true);
|
||||
bt_assert(1 < 2 && 1 != 3);
|
||||
bt_assert(true && true && ! false);
|
||||
bt_assert(true || 1+"a");
|
||||
bt_assert(!(false && 1+"a"));
|
||||
# bt_assert(true || 1+"a");
|
||||
# bt_assert(!(false && 1+"a"));
|
||||
bt_assert(!(true && false));
|
||||
}
|
||||
|
||||
@ -380,6 +380,9 @@ function t_enum()
|
||||
{
|
||||
bt_assert(format(RTS_DUMMY) = "(enum 30)0");
|
||||
bt_assert(format(RTS_STATIC) = "(enum 30)1");
|
||||
bt_assert(format(NET_IP4) = "(enum 36)1");
|
||||
bt_assert(format(NET_VPN6) = "(enum 36)4");
|
||||
|
||||
bt_assert(RTS_STATIC ~ [RTS_STATIC, RTS_DEVICE]);
|
||||
bt_assert(RTS_BGP !~ [RTS_STATIC, RTS_DEVICE]);
|
||||
}
|
||||
@ -458,7 +461,8 @@ function t_prefix_set()
|
||||
prefix set pxs;
|
||||
{
|
||||
pxs = [ 1.2.0.0/16, 1.4.0.0/16+, 44.66.88.64/30{24,28}, 12.34.56.0/24{8,16} ];
|
||||
bt_assert(format(pxs) = "[1.2.0.0/112{::0.1.0.0}, 1.4.0.0/112{::0.1.255.255}, 12.34.0.0/112{::1.255.0.0}, 44.66.88.64/124{::1f0}]");
|
||||
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(1.2.0.0/16 ~ pxs);
|
||||
bt_assert(1.4.0.0/16 ~ pxs);
|
||||
bt_assert(1.4.0.0/18 ~ pxs);
|
||||
@ -597,11 +601,15 @@ function mkpath(int a; int b)
|
||||
return [= a b 3 2 1 =];
|
||||
}
|
||||
|
||||
define set35 = [3 .. 5];
|
||||
|
||||
function t_path()
|
||||
bgpmask pm1;
|
||||
bgppath p2;
|
||||
int set set12;
|
||||
{
|
||||
pm1 = [= 4 3 2 1 =];
|
||||
set12 = [1, 2];
|
||||
|
||||
bt_assert(format(pm1) = "[= 4 3 2 1 =]");
|
||||
|
||||
@ -626,6 +634,8 @@ bgppath p2;
|
||||
bt_assert(p2 !~ [8, ten..(2*ten)]);
|
||||
bt_assert(p2 ~ [= * 4 3 * 1 =]);
|
||||
bt_assert(p2 ~ [= (3+2) (2*2) 3 2 1 =]);
|
||||
bt_assert(p2 ~ [= 5 [2, 4, 6] 3 [1..2] 1 =]);
|
||||
bt_assert(p2 ~ [= 5 set35 3 set12 set12 =]);
|
||||
bt_assert(p2 ~ mkpath(5, 4));
|
||||
|
||||
bt_assert(p2.len = 5);
|
||||
@ -636,7 +646,6 @@ bgppath p2;
|
||||
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));
|
||||
|
||||
pm1 = [= 1 2 * 3 4 5 =];
|
||||
p2 = prepend( + empty +, 5 );
|
||||
p2 = prepend( p2, 4 );
|
||||
p2 = prepend( p2, 3 );
|
||||
@ -644,9 +653,17 @@ bgppath p2;
|
||||
p2 = prepend( p2, 2 );
|
||||
p2 = prepend( p2, 1 );
|
||||
|
||||
bt_assert(p2 ~ pm1);
|
||||
bt_assert(p2 !~ [= 1 2 3 4 5 =]);
|
||||
bt_assert(p2 ~ [= 1 2 * 4 5 =]);
|
||||
bt_assert(p2 ~ [= 1 2 * 3 4 5 =]);
|
||||
bt_assert(p2 ~ [= 1 2 3+ 4 5 =]);
|
||||
bt_assert(p2 ~ [= 1 2 3+ 4+ 5 =]);
|
||||
bt_assert(p2 !~ [= 1 2 3+ 5+ 4 5 =]);
|
||||
bt_assert(p2 !~ [= 1 2 3 3 5+ 4 5 =]);
|
||||
bt_assert(delete(p2, 3) = prepend(prepend(prepend(prepend(+empty+, 5), 4), 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_test_suite(t_path, "Testing paths");
|
||||
@ -1032,6 +1049,7 @@ rd x;
|
||||
bt_assert(x != 2:12345:20000);
|
||||
bt_assert(!(x > 12345:200010));
|
||||
|
||||
bt_assert(format(0:1:2) = "1:2");
|
||||
bt_assert(format(10.0.0.1:1000) = "10.0.0.1:1000");
|
||||
bt_assert(format(100000:20000) = "100000:20000");
|
||||
bt_assert(format(2:100000:20000) = "100000:20000");
|
||||
@ -1172,6 +1190,10 @@ bt_test_suite(t_include, "Testing including another config file");
|
||||
function t_if_else()
|
||||
int i;
|
||||
{
|
||||
/* Empty blocks regression test */
|
||||
if true then {}
|
||||
else {}
|
||||
|
||||
if true then
|
||||
bt_assert(true);
|
||||
|
||||
@ -1181,6 +1203,10 @@ int i;
|
||||
bt_assert(true);
|
||||
else
|
||||
bt_assert(false);
|
||||
|
||||
/* Empty blocks regression test */
|
||||
if true then {}
|
||||
else {}
|
||||
}
|
||||
|
||||
bt_test_suite(t_if_else, "Testing if-else statement");
|
||||
@ -1233,7 +1259,7 @@ int j;
|
||||
|
||||
filter roa_filter
|
||||
{
|
||||
if net ~ [ 10.0.0.0/8{16,24}, 2000::/3{16,96} ] then {
|
||||
if net ~ [ 10.0.0.0/8{16,24} ] || net ~ [ 2000::/3{16,96} ] then {
|
||||
accept;
|
||||
}
|
||||
reject;
|
||||
@ -1257,10 +1283,9 @@ protocol static
|
||||
route 2001:0db8:85a3:8a2e::/64 max 96 as 1000;
|
||||
}
|
||||
|
||||
function test_roa_check()
|
||||
function t_roa_check()
|
||||
prefix pfx;
|
||||
{
|
||||
# cannot be tested in __startup(), sorry
|
||||
bt_assert(roa_check(r4, 10.10.0.0/16, 1000) = ROA_UNKNOWN);
|
||||
bt_assert(roa_check(r4, 10.0.0.0/8, 1000) = ROA_UNKNOWN);
|
||||
bt_assert(roa_check(r4, 10.110.0.0/16, 1000) = ROA_VALID);
|
||||
@ -1315,39 +1340,9 @@ prefix pfx;
|
||||
bt_assert(pfx.asn = 1234);
|
||||
}
|
||||
|
||||
bt_test_suite(test_roa_check, "Testing ROA");
|
||||
bt_test_suite(t_roa_check, "Testing ROA");
|
||||
|
||||
/*
|
||||
* Testing Mixed Net Types
|
||||
* -----------------------
|
||||
*/
|
||||
|
||||
function t_mixed_prefix()
|
||||
prefix set pxs;
|
||||
prefix set pxt;
|
||||
{
|
||||
pxs = [ 98.45.0.0/16, 128.128.0.0/12+, 2200::/42-, ::ffff:d000:0/100{98,102}];
|
||||
bt_assert(format(pxs) = "[::/0, ::/2{c000::}, 98.45.0.0/112{::0.1.0.0}, 128.128.0.0/108{::0.31.255.255}, 208.0.0.0/100{::124.0.0.0}, 2200::/42{ffff:ffff:ffc0::}]");
|
||||
bt_assert(::fe00:0:0/88 !~ pxs);
|
||||
bt_assert(::fffe:0:0/95 !~ pxs);
|
||||
bt_assert(::ffff:d800:0/101 ~ pxs);
|
||||
bt_assert(216.0.0.0/5 ~ pxs);
|
||||
bt_assert(212.0.0.0/6 ~ pxs);
|
||||
bt_assert(212.0.0.0/7 !~ pxs);
|
||||
bt_assert(::ffff:8080:8080/121 ~ pxs);
|
||||
bt_assert(::/0 ~ pxs);
|
||||
bt_assert(0.0.0.0/0 !~ pxs);
|
||||
bt_assert(128.135.64.17/32 ~ pxs);
|
||||
|
||||
# pxt = [ 0:1:2 10.1.10.0/24, 0:5:10000 10.1.10.0/24 ];
|
||||
# print pxt;
|
||||
|
||||
bt_assert(format(NET_IP4) = "(enum 36)1"); ## if (net.type = NET_IP4) ...
|
||||
bt_assert(format(NET_VPN6) = "(enum 36)4");
|
||||
bt_assert(format(0:1:2) = "1:2");
|
||||
}
|
||||
|
||||
bt_test_suite(t_mixed_prefix, "Testing mixed net types");
|
||||
|
||||
|
||||
filter vpn_filter
|
||||
|
@ -43,7 +43,6 @@ protocol static {
|
||||
print scope;
|
||||
if !(scope ~ [ SCOPE_HOST, SCOPE_SITE ]) then {
|
||||
print "Failed in test";
|
||||
quitbird;
|
||||
}
|
||||
|
||||
preference = 15;
|
||||
|
@ -103,12 +103,7 @@ build_tree(struct f_tree *from)
|
||||
struct f_tree *
|
||||
f_new_tree(void)
|
||||
{
|
||||
struct f_tree * ret;
|
||||
ret = cfg_alloc(sizeof(struct f_tree));
|
||||
ret->left = ret->right = NULL;
|
||||
ret->from.type = ret->to.type = T_VOID;
|
||||
ret->from.val.i = ret->to.val.i = 0;
|
||||
ret->data = NULL;
|
||||
struct f_tree *ret = cfg_allocz(sizeof(struct f_tree));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -175,3 +170,14 @@ tree_format(const struct f_tree *t, buffer *buf)
|
||||
|
||||
buffer_puts(buf, "]");
|
||||
}
|
||||
|
||||
void
|
||||
tree_walk(const struct f_tree *t, void (*hook)(const struct f_tree *, void *), void *data)
|
||||
{
|
||||
if (!t)
|
||||
return;
|
||||
|
||||
tree_walk(t->left, hook, data);
|
||||
hook(t, data);
|
||||
tree_walk(t->right, hook, data);
|
||||
}
|
||||
|
293
filter/trie.c
293
filter/trie.c
@ -77,9 +77,10 @@
|
||||
|
||||
|
||||
/*
|
||||
* In the trie code, the prefix length is internally treated as for the whole
|
||||
* ip_addr, regardless whether it contains an IPv4 or IPv6 address. Therefore,
|
||||
* remaining definitions make sense.
|
||||
* In the trie_add_prefix(), we use ip_addr (assuming that it is the same as
|
||||
* ip6_addr) to handle both IPv4 and IPv6 prefixes. In contrast to rest of the
|
||||
* BIRD, IPv4 addresses are just zero-padded from right. That is why we have
|
||||
* ipt_from_ip4() and ipt_to_ip4() macros below.
|
||||
*/
|
||||
|
||||
#define ipa_mkmask(x) ip6_mkmask(x)
|
||||
@ -87,26 +88,30 @@
|
||||
#define ipa_pxlen(x,y) ip6_pxlen(x,y)
|
||||
#define ipa_getbit(x,n) ip6_getbit(x,n)
|
||||
|
||||
#define ipt_from_ip4(x) _MI6(_I(x), 0, 0, 0)
|
||||
#define ipt_to_ip4(x) _MI4(_I0(x))
|
||||
|
||||
|
||||
/**
|
||||
* f_new_trie - allocates and returns a new empty trie
|
||||
* @lp: linear pool to allocate items from
|
||||
* @node_size: node size to be used (&f_trie_node and user data)
|
||||
* @data_size: user data attached to node
|
||||
*/
|
||||
struct f_trie *
|
||||
f_new_trie(linpool *lp, uint node_size)
|
||||
f_new_trie(linpool *lp, uint data_size)
|
||||
{
|
||||
struct f_trie * ret;
|
||||
ret = lp_allocz(lp, sizeof(struct f_trie) + node_size);
|
||||
ret = lp_allocz(lp, sizeof(struct f_trie) + data_size);
|
||||
ret->lp = lp;
|
||||
ret->node_size = node_size;
|
||||
ret->ipv4 = -1;
|
||||
ret->data_size = data_size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline struct f_trie_node *
|
||||
new_node(struct f_trie *t, int plen, ip_addr paddr, ip_addr pmask, ip_addr amask)
|
||||
static inline struct f_trie_node4 *
|
||||
new_node4(struct f_trie *t, int plen, ip4_addr paddr, ip4_addr pmask, ip4_addr amask)
|
||||
{
|
||||
struct f_trie_node *n = lp_allocz(t->lp, t->node_size);
|
||||
struct f_trie_node4 *n = lp_allocz(t->lp, sizeof(struct f_trie_node4) + t->data_size);
|
||||
n->plen = plen;
|
||||
n->addr = paddr;
|
||||
n->mask = pmask;
|
||||
@ -114,12 +119,51 @@ new_node(struct f_trie *t, int plen, ip_addr paddr, ip_addr pmask, ip_addr amask
|
||||
return n;
|
||||
}
|
||||
|
||||
static inline void
|
||||
attach_node(struct f_trie_node *parent, struct f_trie_node *child)
|
||||
static inline struct f_trie_node6 *
|
||||
new_node6(struct f_trie *t, int plen, ip6_addr paddr, ip6_addr pmask, ip6_addr amask)
|
||||
{
|
||||
parent->c[ipa_getbit(child->addr, parent->plen) ? 1 : 0] = child;
|
||||
struct f_trie_node6 *n = lp_allocz(t->lp, sizeof(struct f_trie_node6) + t->data_size);
|
||||
n->plen = plen;
|
||||
n->addr = paddr;
|
||||
n->mask = pmask;
|
||||
n->accept = amask;
|
||||
return n;
|
||||
}
|
||||
|
||||
static inline struct f_trie_node *
|
||||
new_node(struct f_trie *t, int plen, ip_addr paddr, ip_addr pmask, ip_addr amask)
|
||||
{
|
||||
if (t->ipv4)
|
||||
return (struct f_trie_node *) new_node4(t, plen, ipt_to_ip4(paddr), ipt_to_ip4(pmask), ipt_to_ip4(amask));
|
||||
else
|
||||
return (struct f_trie_node *) new_node6(t, plen, ipa_to_ip6(paddr), ipa_to_ip6(pmask), ipa_to_ip6(amask));
|
||||
}
|
||||
|
||||
static inline void
|
||||
attach_node4(struct f_trie_node4 *parent, struct f_trie_node4 *child)
|
||||
{
|
||||
parent->c[ip4_getbit(child->addr, parent->plen) ? 1 : 0] = child;
|
||||
}
|
||||
|
||||
static inline void
|
||||
attach_node6(struct f_trie_node6 *parent, struct f_trie_node6 *child)
|
||||
{
|
||||
parent->c[ip6_getbit(child->addr, parent->plen) ? 1 : 0] = child;
|
||||
}
|
||||
|
||||
static inline void
|
||||
attach_node(struct f_trie_node *parent, struct f_trie_node *child, int v4)
|
||||
{
|
||||
if (v4)
|
||||
attach_node4(&parent->v4, &child->v4);
|
||||
else
|
||||
attach_node6(&parent->v6, &child->v6);
|
||||
}
|
||||
|
||||
#define GET_ADDR(N,F,X) ((X) ? ipt_from_ip4((N)->v4.F) : ipa_from_ip6((N)->v6.F))
|
||||
#define SET_ADDR(N,F,X,V) ({ if (X) (N)->v4.F =ipt_to_ip4(V); else (N)->v6.F =ipa_to_ip6(V); })
|
||||
|
||||
#define GET_CHILD(N,F,X,I) ((X) ? (struct f_trie_node *) (N)->v4.c[I] : (struct f_trie_node *) (N)->v6.c[I])
|
||||
/**
|
||||
* trie_add_prefix
|
||||
* @t: trie to add to
|
||||
@ -133,21 +177,30 @@ attach_node(struct f_trie_node *parent, struct f_trie_node *child)
|
||||
*
|
||||
* Returns a pointer to the allocated node. The function can return a pointer to
|
||||
* an existing node if @px and @plen are the same. If px/plen == 0/0 (or ::/0),
|
||||
* a pointer to the root node is returned.
|
||||
* a pointer to the root node is returned. Returns NULL when called with
|
||||
* mismatched IPv4/IPv6 net type.
|
||||
*/
|
||||
|
||||
void *
|
||||
trie_add_prefix(struct f_trie *t, const net_addr *net, uint l, uint h)
|
||||
{
|
||||
ip_addr px = net_prefix(net);
|
||||
uint plen = net_pxlen(net);
|
||||
ip_addr px;
|
||||
int v4;
|
||||
|
||||
if (net->type == NET_IP4)
|
||||
switch (net->type)
|
||||
{
|
||||
const uint delta = IP6_MAX_PREFIX_LENGTH - IP4_MAX_PREFIX_LENGTH;
|
||||
plen += delta;
|
||||
l += delta;
|
||||
h += delta;
|
||||
case NET_IP4: px = ipt_from_ip4(net4_prefix(net)); v4 = 1; break;
|
||||
case NET_IP6: px = ipa_from_ip6(net6_prefix(net)); v4 = 0; break;
|
||||
default: bug("invalid type");
|
||||
}
|
||||
|
||||
if (t->ipv4 != v4)
|
||||
{
|
||||
if (t->ipv4 < 0)
|
||||
t->ipv4 = v4;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (l == 0)
|
||||
@ -162,95 +215,136 @@ trie_add_prefix(struct f_trie *t, const net_addr *net, uint l, uint h)
|
||||
ip_addr pmask = ipa_mkmask(plen);
|
||||
ip_addr paddr = ipa_and(px, pmask);
|
||||
struct f_trie_node *o = NULL;
|
||||
struct f_trie_node *n = t->root;
|
||||
struct f_trie_node *n = &t->root;
|
||||
|
||||
while (n)
|
||||
{
|
||||
ip_addr cmask = ipa_and(n->mask, pmask);
|
||||
ip_addr naddr = GET_ADDR(n, addr, v4);
|
||||
ip_addr nmask = GET_ADDR(n, mask, v4);
|
||||
ip_addr accept = GET_ADDR(n, accept, v4);
|
||||
ip_addr cmask = ipa_and(nmask, pmask);
|
||||
uint nlen = v4 ? n->v4.plen : n->v6.plen;
|
||||
|
||||
if (ipa_compare(ipa_and(paddr, cmask), ipa_and(n->addr, cmask)))
|
||||
{
|
||||
if (ipa_compare(ipa_and(paddr, cmask), ipa_and(naddr, cmask)))
|
||||
{
|
||||
/* We are out of path - we have to add branching node 'b'
|
||||
between node 'o' and node 'n', and attach new node 'a'
|
||||
as the other child of 'b'. */
|
||||
int blen = ipa_pxlen(paddr, n->addr);
|
||||
int blen = ipa_pxlen(paddr, naddr);
|
||||
ip_addr bmask = ipa_mkmask(blen);
|
||||
ip_addr baddr = ipa_and(px, bmask);
|
||||
|
||||
/* Merge accept masks from children to get accept mask for node 'b' */
|
||||
ip_addr baccm = ipa_and(ipa_or(amask, n->accept), bmask);
|
||||
ip_addr baccm = ipa_and(ipa_or(amask, accept), bmask);
|
||||
|
||||
struct f_trie_node *a = new_node(t, plen, paddr, pmask, amask);
|
||||
struct f_trie_node *b = new_node(t, blen, baddr, bmask, baccm);
|
||||
attach_node(o, b);
|
||||
attach_node(b, n);
|
||||
attach_node(b, a);
|
||||
attach_node(o, b, v4);
|
||||
attach_node(b, n, v4);
|
||||
attach_node(b, a, v4);
|
||||
return a;
|
||||
}
|
||||
|
||||
if (plen < n->plen)
|
||||
if (plen < nlen)
|
||||
{
|
||||
/* We add new node 'a' between node 'o' and node 'n' */
|
||||
amask = ipa_or(amask, ipa_and(n->accept, pmask));
|
||||
amask = ipa_or(amask, ipa_and(accept, pmask));
|
||||
struct f_trie_node *a = new_node(t, plen, paddr, pmask, amask);
|
||||
attach_node(o, a);
|
||||
attach_node(a, n);
|
||||
attach_node(o, a, v4);
|
||||
attach_node(a, n, v4);
|
||||
return a;
|
||||
}
|
||||
|
||||
if (plen == n->plen)
|
||||
if (plen == nlen)
|
||||
{
|
||||
/* We already found added node in trie. Just update accept mask */
|
||||
n->accept = ipa_or(n->accept, amask);
|
||||
accept = ipa_or(accept, amask);
|
||||
SET_ADDR(n, accept, v4, accept);
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Update accept mask part M2 and go deeper */
|
||||
n->accept = ipa_or(n->accept, ipa_and(amask, n->mask));
|
||||
accept = ipa_or(accept, ipa_and(amask, nmask));
|
||||
SET_ADDR(n, accept, v4, accept);
|
||||
|
||||
/* n->plen < plen and plen <= 32 (128) */
|
||||
o = n;
|
||||
n = n->c[ipa_getbit(paddr, n->plen) ? 1 : 0];
|
||||
n = GET_CHILD(n, c, v4, ipa_getbit(paddr, nlen) ? 1 : 0);
|
||||
}
|
||||
|
||||
/* We add new tail node 'a' after node 'o' */
|
||||
struct f_trie_node *a = new_node(t, plen, paddr, pmask, amask);
|
||||
attach_node(o, a);
|
||||
attach_node(o, a, v4);
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
static int
|
||||
trie_match_prefix(const struct f_trie *t, ip_addr px, uint plen)
|
||||
trie_match_net4(const struct f_trie *t, ip4_addr px, uint plen)
|
||||
{
|
||||
ip_addr pmask = ipa_mkmask(plen);
|
||||
ip_addr paddr = ipa_and(px, pmask);
|
||||
ip4_addr pmask = ip4_mkmask(plen);
|
||||
ip4_addr paddr = ip4_and(px, pmask);
|
||||
|
||||
if (plen == 0)
|
||||
return t->zero;
|
||||
|
||||
int plentest = plen - 1;
|
||||
const struct f_trie_node *n = t->root;
|
||||
const struct f_trie_node4 *n = &t->root.v4;
|
||||
|
||||
while(n)
|
||||
{
|
||||
ip_addr cmask = ipa_and(n->mask, pmask);
|
||||
while (n)
|
||||
{
|
||||
ip4_addr cmask = ip4_and(n->mask, pmask);
|
||||
|
||||
/* We are out of path */
|
||||
if (ipa_compare(ipa_and(paddr, cmask), ipa_and(n->addr, cmask)))
|
||||
return 0;
|
||||
/* We are out of path */
|
||||
if (ip4_compare(ip4_and(paddr, cmask), ip4_and(n->addr, cmask)))
|
||||
return 0;
|
||||
|
||||
/* Check accept mask */
|
||||
if (ipa_getbit(n->accept, plentest))
|
||||
return 1;
|
||||
/* Check accept mask */
|
||||
if (ip4_getbit(n->accept, plentest))
|
||||
return 1;
|
||||
|
||||
/* We finished trie walk and still no match */
|
||||
if (plen <= n->plen)
|
||||
return 0;
|
||||
/* We finished trie walk and still no match */
|
||||
if (plen <= n->plen)
|
||||
return 0;
|
||||
|
||||
/* Choose children */
|
||||
n = n->c[(ipa_getbit(paddr, n->plen)) ? 1 : 0];
|
||||
}
|
||||
/* Choose children */
|
||||
n = n->c[(ip4_getbit(paddr, n->plen)) ? 1 : 0];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
trie_match_net6(const struct f_trie *t, ip6_addr px, uint plen)
|
||||
{
|
||||
ip6_addr pmask = ip6_mkmask(plen);
|
||||
ip6_addr paddr = ip6_and(px, pmask);
|
||||
|
||||
if (plen == 0)
|
||||
return t->zero;
|
||||
|
||||
int plentest = plen - 1;
|
||||
const struct f_trie_node6 *n = &t->root.v6;
|
||||
|
||||
while (n)
|
||||
{
|
||||
ip6_addr cmask = ip6_and(n->mask, pmask);
|
||||
|
||||
/* We are out of path */
|
||||
if (ip6_compare(ip6_and(paddr, cmask), ip6_and(n->addr, cmask)))
|
||||
return 0;
|
||||
|
||||
/* Check accept mask */
|
||||
if (ip6_getbit(n->accept, plentest))
|
||||
return 1;
|
||||
|
||||
/* We finished trie walk and still no match */
|
||||
if (plen <= n->plen)
|
||||
return 0;
|
||||
|
||||
/* Choose children */
|
||||
n = n->c[(ip6_getbit(paddr, n->plen)) ? 1 : 0];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -267,20 +361,25 @@ trie_match_prefix(const struct f_trie *t, ip_addr px, uint plen)
|
||||
int
|
||||
trie_match_net(const struct f_trie *t, const net_addr *n)
|
||||
{
|
||||
uint add = 0;
|
||||
switch (n->type)
|
||||
{
|
||||
case NET_IP4:
|
||||
case NET_VPN4:
|
||||
case NET_ROA4:
|
||||
return t->ipv4 ? trie_match_net4(t, net4_prefix(n), net_pxlen(n)) : 0;
|
||||
|
||||
switch (n->type) {
|
||||
case NET_IP4:
|
||||
case NET_VPN4:
|
||||
case NET_ROA4:
|
||||
add = IP6_MAX_PREFIX_LENGTH - IP4_MAX_PREFIX_LENGTH;
|
||||
case NET_IP6:
|
||||
case NET_VPN6:
|
||||
case NET_ROA6:
|
||||
return !t->ipv4 ? trie_match_net6(t, net6_prefix(n), net_pxlen(n)) : 0;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return trie_match_prefix(t, net_prefix(n), net_pxlen(n) + add);
|
||||
}
|
||||
|
||||
static int
|
||||
trie_node_same(const struct f_trie_node *t1, const struct f_trie_node *t2)
|
||||
trie_node_same4(const struct f_trie_node4 *t1, const struct f_trie_node4 *t2)
|
||||
{
|
||||
if ((t1 == NULL) && (t2 == NULL))
|
||||
return 1;
|
||||
@ -289,11 +388,28 @@ trie_node_same(const struct f_trie_node *t1, const struct f_trie_node *t2)
|
||||
return 0;
|
||||
|
||||
if ((t1->plen != t2->plen) ||
|
||||
(! ipa_equal(t1->addr, t2->addr)) ||
|
||||
(! ipa_equal(t1->accept, t2->accept)))
|
||||
(! ip4_equal(t1->addr, t2->addr)) ||
|
||||
(! ip4_equal(t1->accept, t2->accept)))
|
||||
return 0;
|
||||
|
||||
return trie_node_same(t1->c[0], t2->c[0]) && trie_node_same(t1->c[1], t2->c[1]);
|
||||
return trie_node_same4(t1->c[0], t2->c[0]) && trie_node_same4(t1->c[1], t2->c[1]);
|
||||
}
|
||||
|
||||
static int
|
||||
trie_node_same6(const struct f_trie_node6 *t1, const struct f_trie_node6 *t2)
|
||||
{
|
||||
if ((t1 == NULL) && (t2 == NULL))
|
||||
return 1;
|
||||
|
||||
if ((t1 == NULL) || (t2 == NULL))
|
||||
return 0;
|
||||
|
||||
if ((t1->plen != t2->plen) ||
|
||||
(! ip6_equal(t1->addr, t2->addr)) ||
|
||||
(! ip6_equal(t1->accept, t2->accept)))
|
||||
return 0;
|
||||
|
||||
return trie_node_same6(t1->c[0], t2->c[0]) && trie_node_same6(t1->c[1], t2->c[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -306,20 +422,39 @@ trie_node_same(const struct f_trie_node *t1, const struct f_trie_node *t2)
|
||||
int
|
||||
trie_same(const struct f_trie *t1, const struct f_trie *t2)
|
||||
{
|
||||
return (t1->zero == t2->zero) && trie_node_same(t1->root, t2->root);
|
||||
if ((t1->zero != t2->zero) || (t1->ipv4 != t2->ipv4))
|
||||
return 0;
|
||||
|
||||
if (t1->ipv4)
|
||||
return trie_node_same4(&t1->root.v4, &t2->root.v4);
|
||||
else
|
||||
return trie_node_same6(&t1->root.v6, &t2->root.v6);
|
||||
}
|
||||
|
||||
static void
|
||||
trie_node_format(const struct f_trie_node *t, buffer *buf)
|
||||
trie_node_format4(const struct f_trie_node4 *t, buffer *buf)
|
||||
{
|
||||
if (t == NULL)
|
||||
return;
|
||||
|
||||
if (ipa_nonzero(t->accept))
|
||||
buffer_print(buf, "%I/%d{%I}, ", t->addr, t->plen, t->accept);
|
||||
if (ip4_nonzero(t->accept))
|
||||
buffer_print(buf, "%I4/%d{%I4}, ", t->addr, t->plen, t->accept);
|
||||
|
||||
trie_node_format(t->c[0], buf);
|
||||
trie_node_format(t->c[1], buf);
|
||||
trie_node_format4(t->c[0], buf);
|
||||
trie_node_format4(t->c[1], buf);
|
||||
}
|
||||
|
||||
static void
|
||||
trie_node_format6(const struct f_trie_node6 *t, buffer *buf)
|
||||
{
|
||||
if (t == NULL)
|
||||
return;
|
||||
|
||||
if (ip6_nonzero(t->accept))
|
||||
buffer_print(buf, "%I6/%d{%I6}, ", t->addr, t->plen, t->accept);
|
||||
|
||||
trie_node_format6(t->c[0], buf);
|
||||
trie_node_format6(t->c[1], buf);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -335,8 +470,12 @@ trie_format(const struct f_trie *t, buffer *buf)
|
||||
buffer_puts(buf, "[");
|
||||
|
||||
if (t->zero)
|
||||
buffer_print(buf, "%I/%d, ", IPA_NONE, 0);
|
||||
trie_node_format(t->root, buf);
|
||||
buffer_print(buf, "%I/%d, ", t->ipv4 ? IPA_NONE4 : IPA_NONE6, 0);
|
||||
|
||||
if (t->ipv4)
|
||||
trie_node_format4(&t->root.v4, buf);
|
||||
else
|
||||
trie_node_format6(&t->root.v6, buf);
|
||||
|
||||
if (buf->pos == buf->end)
|
||||
return;
|
||||
|
@ -103,7 +103,7 @@ t_match_net(void)
|
||||
{
|
||||
list prefixes; /* of structs f_extended_prefix */
|
||||
init_list(&prefixes);
|
||||
struct f_trie *trie = f_new_trie(config->mem, sizeof(struct f_trie_node));
|
||||
struct f_trie *trie = f_new_trie(config->mem, 0);
|
||||
|
||||
generate_random_ipv6_prefixes(&prefixes);
|
||||
struct f_prefix_node *n;
|
||||
@ -143,8 +143,8 @@ t_trie_same(void)
|
||||
int round;
|
||||
for (round = 0; round < TESTS_NUM*4; round++)
|
||||
{
|
||||
struct f_trie * trie1 = f_new_trie(config->mem, sizeof(struct f_trie_node));
|
||||
struct f_trie * trie2 = f_new_trie(config->mem, sizeof(struct f_trie_node));
|
||||
struct f_trie * trie1 = f_new_trie(config->mem, 0);
|
||||
struct f_trie * trie2 = f_new_trie(config->mem, 0);
|
||||
|
||||
list prefixes; /* a list of f_extended_prefix structures */
|
||||
init_list(&prefixes);
|
||||
|
@ -1,7 +1,7 @@
|
||||
src := bitops.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c strtoul.c tbf.c timer.c xmalloc.c
|
||||
src := bitmap.c bitops.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c strtoul.c tbf.c timer.c xmalloc.c
|
||||
obj := $(src-o-files)
|
||||
$(all-daemon)
|
||||
|
||||
tests_src := 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
|
||||
tests_targets := $(tests_targets) $(tests-target-files)
|
||||
tests_objs := $(tests_objs) $(src-o-files)
|
||||
|
@ -38,7 +38,7 @@ struct align_probe { char x; long int y; };
|
||||
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a)))
|
||||
#define BYTES(n) ((((uint) (n)) + 7) / 8)
|
||||
#define CALL(fn, args...) ({ if (fn) fn(args); })
|
||||
#define ADVANCE(w, r, l) ({ r -= l; w += l; })
|
||||
#define ADVANCE(w, r, l) ({ r -= (l); w += (l); })
|
||||
|
||||
static inline int uint_cmp(uint i1, uint i2)
|
||||
{ return (int)(i1 > i2) - (int)(i1 < i2); }
|
||||
@ -72,6 +72,7 @@ static inline int u64_cmp(u64 i1, u64 i2)
|
||||
#define NORET __attribute__((noreturn))
|
||||
#define UNUSED __attribute__((unused))
|
||||
#define PACKED __attribute__((packed))
|
||||
#define NONNULL(...) __attribute__((nonnull((__VA_ARGS__))))
|
||||
|
||||
#ifndef HAVE_THREAD_LOCAL
|
||||
#define _Thread_local
|
||||
@ -162,12 +163,23 @@ void debug(const char *msg, ...); /* Printf to debug output */
|
||||
#define DBG(x, y...) do { } while(0)
|
||||
#endif
|
||||
|
||||
#define ASSERT_DIE(x) do { if (!(x)) bug("Assertion '%s' failed at %s:%d", #x, __FILE__, __LINE__); } while(0)
|
||||
|
||||
#define EXPENSIVE_CHECK(x) /* intentionally left blank */
|
||||
|
||||
#ifdef DEBUGGING
|
||||
#define ASSERT(x) do { if (!(x)) bug("Assertion '%s' failed at %s:%d", #x, __FILE__, __LINE__); } while(0)
|
||||
#define ASSERT(x) ASSERT_DIE(x)
|
||||
#define ASSUME(x) ASSERT_DIE(x)
|
||||
#ifdef ENABLE_EXPENSIVE_CHECKS
|
||||
#undef EXPENSIVE_CHECK
|
||||
#define EXPENSIVE_CHECK(x) ASSERT_DIE(x)
|
||||
#endif
|
||||
#else
|
||||
#define ASSERT(x) do { if (!(x)) log(L_BUG "Assertion '%s' failed at %s:%d", #x, __FILE__, __LINE__); } while(0)
|
||||
#define ASSUME(x) /* intentionally left blank */
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef DEBUGGING
|
||||
asm(
|
||||
".pushsection \".debug_gdb_scripts\", \"MS\",@progbits,1\n"
|
||||
|
197
lib/bitmap.c
Normal file
197
lib/bitmap.c
Normal file
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* BIRD Library -- Bitmaps
|
||||
*
|
||||
* (c) 2019 Ondrej Zajicek <santiago@crfreenet.org>
|
||||
* (c) 2019 CZ.NIC z.s.p.o.
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "nest/bird.h"
|
||||
#include "lib/bitmap.h"
|
||||
#include "lib/bitops.h"
|
||||
#include "lib/resource.h"
|
||||
|
||||
|
||||
/*
|
||||
* Basic bitmap
|
||||
*/
|
||||
|
||||
void
|
||||
bmap_init(struct bmap *b, pool *p, uint size)
|
||||
{
|
||||
b->size = BIRD_ALIGN(size, 4);
|
||||
b->data = mb_allocz(p, b->size);
|
||||
}
|
||||
|
||||
void
|
||||
bmap_reset(struct bmap *b, uint size)
|
||||
{
|
||||
b->size = BIRD_ALIGN(size, 4);
|
||||
memset(b->data, 0, b->size);
|
||||
}
|
||||
|
||||
void
|
||||
bmap_grow(struct bmap *b, uint need)
|
||||
{
|
||||
uint size = b->size * 2;
|
||||
while (size < need)
|
||||
size *= 2;
|
||||
|
||||
uint old_size = b->size;
|
||||
b->size = size;
|
||||
b->data = mb_realloc(b->data, b->size);
|
||||
|
||||
ASSERT(size >= old_size);
|
||||
memset(b->data + (old_size / 4), 0, size - old_size);
|
||||
}
|
||||
|
||||
void
|
||||
bmap_free(struct bmap *b)
|
||||
{
|
||||
mb_free(b->data);
|
||||
b->size = 0;
|
||||
b->data = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Hierarchical bitmap
|
||||
*/
|
||||
|
||||
#define B256_SIZE(b) BIRD_ALIGN(b, 32)
|
||||
#define B256_STEP(b) (BIRD_ALIGN(b, 8192) >> 8)
|
||||
|
||||
void
|
||||
hmap_init(struct hmap *b, pool *p, uint size)
|
||||
{
|
||||
b->size[0] = B256_SIZE(size);
|
||||
b->size[1] = B256_STEP(b->size[0]);
|
||||
b->size[2] = B256_STEP(b->size[1]);
|
||||
b->size[3] = sizeof(b->root);
|
||||
|
||||
b->data[0] = mb_allocz(p, b->size[0]);
|
||||
b->data[1] = mb_allocz(p, b->size[1]);
|
||||
b->data[2] = mb_allocz(p, b->size[2]);
|
||||
b->data[3] = b->root;
|
||||
|
||||
memset(b->root, 0, sizeof(b->root));
|
||||
}
|
||||
|
||||
static void
|
||||
hmap_grow(struct hmap *b, uint need)
|
||||
{
|
||||
uint size = b->size[0] * 2;
|
||||
while (size < need)
|
||||
size *= 2;
|
||||
|
||||
for (uint i = 0; i < 3; i++)
|
||||
{
|
||||
uint old_size = b->size[i];
|
||||
b->size[i] = size;
|
||||
b->data[i] = mb_realloc(b->data[i], b->size[i]);
|
||||
|
||||
ASSERT(size >= old_size);
|
||||
memset(b->data[i] + (old_size / 4), 0, size - old_size);
|
||||
|
||||
size = B256_STEP(size);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
hmap_free(struct hmap *b)
|
||||
{
|
||||
mb_free(b->data[0]);
|
||||
mb_free(b->data[1]);
|
||||
mb_free(b->data[2]);
|
||||
|
||||
memset(b, 0, sizeof(struct hmap));
|
||||
}
|
||||
|
||||
static inline int
|
||||
b256_and(u32 *p)
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
if (~p[i])
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
hmap_set(struct hmap *b, uint n)
|
||||
{
|
||||
if (n >= hmap_max(b))
|
||||
hmap_grow(b, n/8 + 1);
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
BIT32_SET(b->data[i], n);
|
||||
n = n >> 8;
|
||||
|
||||
/* Continue if all bits in 256-bit block are set */
|
||||
if (! b256_and(b->data[i] + 8*n))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
hmap_clear(struct hmap *b, uint n)
|
||||
{
|
||||
if (n >= hmap_max(b))
|
||||
return;
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
BIT32_CLR(b->data[i], n);
|
||||
n = n >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int
|
||||
b256_first_zero(u32 *p)
|
||||
{
|
||||
for (int i = 0; i < 8; i++)
|
||||
if (~p[i])
|
||||
return 32*i + u32_ctz(~p[i]);
|
||||
|
||||
return 256;
|
||||
}
|
||||
|
||||
u32
|
||||
hmap_first_zero(struct hmap *b)
|
||||
{
|
||||
u32 n = 0;
|
||||
|
||||
for (int i = 3; i >= 0; i--)
|
||||
{
|
||||
if (32*n >= b->size[i])
|
||||
return hmap_max(b);
|
||||
|
||||
u32 *p = b->data[i] + 8*n;
|
||||
|
||||
n = (n << 8) + b256_first_zero(p);
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void
|
||||
hmap_check(struct hmap *b)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
int max = b->size[i] / 32;
|
||||
|
||||
for (int j = 0; j < max; j++)
|
||||
{
|
||||
int x = b256_and(b->data[i] + 8*j);
|
||||
int y = !!BIT32_TEST(b->data[i+1], j);
|
||||
if (x != y)
|
||||
bug("Inconsistent data on %d:%d (%d vs %d)", i, j, x, y);
|
||||
}
|
||||
}
|
||||
}
|
63
lib/bitmap.h
Normal file
63
lib/bitmap.h
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* BIRD Library -- Bitmaps
|
||||
*
|
||||
* (c) 2019 Ondrej Zajicek <santiago@crfreenet.org>
|
||||
* (c) 2019 CZ.NIC z.s.p.o.
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#ifndef _BIRD_BITMAP_H_
|
||||
#define _BIRD_BITMAP_H_
|
||||
|
||||
struct bmap
|
||||
{
|
||||
u32 size;
|
||||
u32 *data;
|
||||
};
|
||||
|
||||
void bmap_init(struct bmap *b, pool *p, uint size);
|
||||
void bmap_reset(struct bmap *b, uint size);
|
||||
void bmap_grow(struct bmap *b, uint need);
|
||||
void bmap_free(struct bmap *b);
|
||||
|
||||
static inline uint bmap_max(struct bmap *b)
|
||||
{ return 8 * b->size; }
|
||||
|
||||
static inline int bmap_test(struct bmap *b, uint n)
|
||||
{ return (n < bmap_max(b)) && BIT32_TEST(b->data, n); }
|
||||
|
||||
static inline void bmap_set(struct bmap *b, uint n)
|
||||
{
|
||||
if (n >= bmap_max(b)) bmap_grow(b, n/8 + 1);
|
||||
BIT32_SET(b->data, n);
|
||||
}
|
||||
|
||||
static inline void bmap_clear(struct bmap *b, uint n)
|
||||
{
|
||||
if (n >= bmap_max(b)) return;
|
||||
BIT32_CLR(b->data, n);
|
||||
}
|
||||
|
||||
|
||||
struct hmap
|
||||
{
|
||||
u32 size[4];
|
||||
u32 *data[4];
|
||||
u32 root[8];
|
||||
};
|
||||
|
||||
static inline uint hmap_max(struct hmap *b)
|
||||
{ return 8 * b->size[0]; }
|
||||
|
||||
static inline int hmap_test(struct hmap *b, uint n)
|
||||
{ return (n < hmap_max(b)) && BIT32_TEST(b->data[0], n); }
|
||||
|
||||
void hmap_init(struct hmap *b, pool *p, uint size);
|
||||
void hmap_free(struct hmap *b);
|
||||
void hmap_set(struct hmap *b, uint n);
|
||||
void hmap_clear(struct hmap *b, uint n);
|
||||
u32 hmap_first_zero(struct hmap *b);
|
||||
void hmap_check(struct hmap *b);
|
||||
|
||||
#endif
|
186
lib/bitmap_test.c
Normal file
186
lib/bitmap_test.c
Normal file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
* BIRD Library -- Bitmap Tests
|
||||
*
|
||||
* (c) 2019 Ondrej Zajicek <santiago@crfreenet.org>
|
||||
* (c) 2019 CZ.NIC z.s.p.o.
|
||||
*
|
||||
* Can be freely distributed and used under the terms of the GNU GPL.
|
||||
*/
|
||||
|
||||
#include "test/birdtest.h"
|
||||
#include "sysdep/config.h"
|
||||
#include "lib/bitmap.h"
|
||||
|
||||
#define MAX_NUM (1 << 20)
|
||||
#define MAX_SET (1 << 19)
|
||||
#define MAX_CLR (1 << 17)
|
||||
|
||||
#define STEP_NUM 1000
|
||||
#define STEP_SET 1000
|
||||
#define STEP_CLR 500
|
||||
|
||||
static int
|
||||
t_bmap_set_clear_random(void)
|
||||
{
|
||||
struct bmap b;
|
||||
|
||||
resource_init();
|
||||
bmap_init(&b, &root_pool, 1024);
|
||||
|
||||
char expected[MAX_NUM] = {};
|
||||
uint i, n;
|
||||
|
||||
for (i = 0; i < MAX_SET; i++)
|
||||
{
|
||||
do n = bt_random() % MAX_NUM;
|
||||
while (expected[n]);
|
||||
|
||||
bmap_set(&b, n);
|
||||
expected[n] = 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_CLR; i++)
|
||||
{
|
||||
do n = bt_random() % MAX_NUM;
|
||||
while (!expected[n]);
|
||||
|
||||
bmap_clear(&b, n);
|
||||
expected[n] = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_NUM; i++)
|
||||
if (bmap_test(&b, i) != expected[i])
|
||||
bt_abort_msg("Bitmap mismatch on %d (should be %d %d)", i, bmap_test(&b, i), expected[i]);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
t_hmap_set_clear_random(void)
|
||||
{
|
||||
struct hmap b;
|
||||
|
||||
resource_init();
|
||||
hmap_init(&b, &root_pool, 1024);
|
||||
|
||||
char expected[MAX_NUM] = {};
|
||||
uint i, n;
|
||||
|
||||
for (i = 0; i < MAX_SET; i++)
|
||||
{
|
||||
do n = bt_random() % MAX_NUM;
|
||||
while (expected[n]);
|
||||
|
||||
hmap_set(&b, n);
|
||||
expected[n] = 1;
|
||||
}
|
||||
|
||||
hmap_check(&b);
|
||||
|
||||
for (i = 0; i < MAX_CLR; i++)
|
||||
{
|
||||
do n = bt_random() % MAX_NUM;
|
||||
while (!expected[n]);
|
||||
|
||||
hmap_clear(&b, n);
|
||||
expected[n] = 0;
|
||||
}
|
||||
|
||||
hmap_check(&b);
|
||||
|
||||
for (i = 0; i < MAX_NUM; i++)
|
||||
if (hmap_test(&b, i) != expected[i])
|
||||
bt_abort_msg("Bitmap mismatch on %d (should be %d %d)", i, hmap_test(&b, i), expected[i]);
|
||||
|
||||
for (i = 0; 1; i++)
|
||||
{
|
||||
n = hmap_first_zero(&b);
|
||||
bt_assert(n >= i);
|
||||
bt_assert(n <= MAX_NUM);
|
||||
|
||||
for (; i < n; i++)
|
||||
bt_assert(expected[i]);
|
||||
|
||||
if (n == MAX_NUM)
|
||||
break;
|
||||
|
||||
bt_assert(!expected[i]);
|
||||
|
||||
hmap_set(&b, n);
|
||||
}
|
||||
|
||||
hmap_check(&b);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
t_hmap_set_clear_fill(void)
|
||||
{
|
||||
struct hmap b;
|
||||
|
||||
resource_init();
|
||||
hmap_init(&b, &root_pool, 1024);
|
||||
|
||||
char expected[MAX_NUM] = {};
|
||||
uint i, j, n, max = 0;
|
||||
|
||||
for (i = 0; i < STEP_NUM; i++)
|
||||
{
|
||||
uint last = 0;
|
||||
uint step_set = bt_random() % STEP_SET;
|
||||
uint step_clr = bt_random() % STEP_CLR;
|
||||
|
||||
for (j = 0; j < step_set; j++)
|
||||
{
|
||||
n = hmap_first_zero(&b);
|
||||
bt_assert(n > last || !last);
|
||||
bt_assert(n < MAX_NUM);
|
||||
|
||||
if (!last)
|
||||
last = n;
|
||||
|
||||
for (; last < n; last++)
|
||||
bt_assert(expected[last]);
|
||||
|
||||
bt_assert(!expected[n]);
|
||||
|
||||
hmap_set(&b, n);
|
||||
expected[n] = 1;
|
||||
max = MAX(max, n);
|
||||
}
|
||||
|
||||
for (j = 0; j < step_clr; j++)
|
||||
{
|
||||
uint k = 0;
|
||||
do n = bt_random() % max;
|
||||
while (!expected[n] && (k++ < 8));
|
||||
|
||||
if (!expected[n])
|
||||
continue;
|
||||
|
||||
hmap_clear(&b, n);
|
||||
expected[n] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_NUM; i++)
|
||||
if (hmap_test(&b, i) != expected[i])
|
||||
bt_abort_msg("Bitmap mismatch on %d (should be %d %d)", i, hmap_test(&b, i), expected[i]);
|
||||
|
||||
hmap_check(&b);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
bt_init(argc, argv);
|
||||
|
||||
bt_test_suite(t_bmap_set_clear_random, "BMap - random sequence of sets / clears");
|
||||
bt_test_suite(t_hmap_set_clear_random, "HMap - random sequence of sets / clears");
|
||||
bt_test_suite(t_hmap_set_clear_fill, "HMap - linear sets and random clears");
|
||||
|
||||
return bt_exit_value();
|
||||
}
|
@ -29,4 +29,9 @@ static inline u32 u32_hash(u32 v) { return v * 2902958171u; }
|
||||
|
||||
static inline u8 u32_popcount(u32 v) { return __builtin_popcount(v); }
|
||||
|
||||
static inline int u32_clz(u32 v) { return __builtin_clz(v); }
|
||||
static inline int u32_ctz(u32 v) { return __builtin_ctz(v); }
|
||||
|
||||
static inline int uint_is_pow2(uint n) { return n && !(n & (n-1)); }
|
||||
|
||||
#endif
|
||||
|
@ -50,6 +50,8 @@
|
||||
|
||||
#define BUFFER_FLUSH(v) ({ (v).used = 0; })
|
||||
|
||||
#define BUFFER_EMPTY(v) ({ (v).used == 0; })
|
||||
|
||||
#define BUFFER_WALK(v,n) \
|
||||
for (BUFFER_TYPE(v) *_n = (v).data, n; _n < ((v).data + (v).used) && (n = *_n, 1); _n++)
|
||||
|
||||
|
56
lib/event.c
56
lib/event.c
@ -23,6 +23,7 @@
|
||||
#include "lib/event.h"
|
||||
|
||||
event_list global_event_list;
|
||||
event_list global_work_list;
|
||||
|
||||
inline void
|
||||
ev_postpone(event *e)
|
||||
@ -114,6 +115,22 @@ ev_schedule(event *e)
|
||||
ev_enqueue(&global_event_list, e);
|
||||
}
|
||||
|
||||
/**
|
||||
* ev_schedule_work - schedule a work-event.
|
||||
* @e: an event
|
||||
*
|
||||
* This function schedules an event by enqueueing it to a system-wide work-event
|
||||
* list which is run by the platform dependent code whenever appropriate. This
|
||||
* is designated for work-events instead of regular events. They are executed
|
||||
* less often in order to not clog I/O loop.
|
||||
*/
|
||||
void
|
||||
ev_schedule_work(event *e)
|
||||
{
|
||||
if (!ev_active(e))
|
||||
add_tail(&global_work_list, &e->n);
|
||||
}
|
||||
|
||||
void io_log_event(void *hook, void *data);
|
||||
|
||||
/**
|
||||
@ -136,10 +153,47 @@ ev_run_list(event_list *l)
|
||||
event *e = SKIP_BACK(event, n, n);
|
||||
|
||||
/* This is ugly hack, we want to log just events executed from the main I/O loop */
|
||||
if (l == &global_event_list)
|
||||
if ((l == &global_event_list) || (l == &global_work_list))
|
||||
io_log_event(e->hook, e->data);
|
||||
|
||||
ev_run(e);
|
||||
}
|
||||
|
||||
return !EMPTY_LIST(*l);
|
||||
}
|
||||
|
||||
int
|
||||
ev_run_list_limited(event_list *l, uint limit)
|
||||
{
|
||||
node *n;
|
||||
list tmp_list;
|
||||
|
||||
init_list(&tmp_list);
|
||||
add_tail_list(&tmp_list, l);
|
||||
init_list(l);
|
||||
|
||||
WALK_LIST_FIRST(n, tmp_list)
|
||||
{
|
||||
event *e = SKIP_BACK(event, n, n);
|
||||
|
||||
if (!limit)
|
||||
break;
|
||||
|
||||
/* This is ugly hack, we want to log just events executed from the main I/O loop */
|
||||
if ((l == &global_event_list) || (l == &global_work_list))
|
||||
io_log_event(e->hook, e->data);
|
||||
|
||||
ev_run(e);
|
||||
limit--;
|
||||
}
|
||||
|
||||
if (!EMPTY_LIST(tmp_list))
|
||||
{
|
||||
/* Attach new items after the unprocessed old items */
|
||||
add_tail_list(&tmp_list, l);
|
||||
init_list(l);
|
||||
add_tail_list(l, &tmp_list);
|
||||
}
|
||||
|
||||
return !EMPTY_LIST(*l);
|
||||
}
|
||||
|
@ -21,14 +21,17 @@ typedef struct event {
|
||||
typedef list event_list;
|
||||
|
||||
extern event_list global_event_list;
|
||||
extern event_list global_work_list;
|
||||
|
||||
event *ev_new(pool *);
|
||||
void ev_run(event *);
|
||||
#define ev_init_list(el) init_list(el)
|
||||
void ev_enqueue(event_list *, event *);
|
||||
void ev_schedule(event *);
|
||||
void ev_schedule_work(event *);
|
||||
void ev_postpone(event *);
|
||||
int ev_run_list(event_list *);
|
||||
int ev_run_list_limited(event_list *, uint);
|
||||
|
||||
static inline int
|
||||
ev_active(event *e)
|
||||
|
@ -24,24 +24,33 @@ straightforward_fletcher16_compute(const char *data)
|
||||
sum2 = (sum2 + sum1) % 255;
|
||||
}
|
||||
|
||||
return (sum2 << 8) | sum1;
|
||||
sum2 = (sum2 + sum1) % 255;
|
||||
sum2 = (sum2 + sum1) % 255;
|
||||
|
||||
return (sum1 << 8) | sum2;
|
||||
}
|
||||
|
||||
static u16
|
||||
straightforward_fletcher16_checksum(const char *data)
|
||||
{
|
||||
u16 csum;
|
||||
u8 c0,c1,f0,f1;
|
||||
u16 c0,c1,x,y;
|
||||
|
||||
csum = straightforward_fletcher16_compute(data);
|
||||
f0 = csum & 0xff;
|
||||
f1 = (csum >> 8) & 0xff;
|
||||
c0 = 0xff - ((f0 + f1) % 0xff);
|
||||
c1 = 0xff - ((f0 + c0) % 0xff);
|
||||
c0 = (csum >> 8) & 0xff;
|
||||
c1 = csum & 0xff;
|
||||
|
||||
return (c1 << 8) | c0;
|
||||
x = (255 + c0 - c1) % 255;
|
||||
y = (510 - 2*c0 + c1) % 255;
|
||||
|
||||
if (!x) x = 255;
|
||||
if (!y) y = 255;
|
||||
|
||||
return (x << 8) | y;
|
||||
}
|
||||
|
||||
const u8 zero16[2] = {};
|
||||
|
||||
static int
|
||||
test_fletcher16(void *out_, const void *in_, const void *expected_out_)
|
||||
{
|
||||
@ -53,7 +62,8 @@ test_fletcher16(void *out_, const void *in_, const void *expected_out_)
|
||||
|
||||
fletcher16_init(&ctxt);
|
||||
fletcher16_update(&ctxt, in, strlen(in));
|
||||
put_u16(out, fletcher16_compute(&ctxt));
|
||||
fletcher16_update(&ctxt, zero16, 2);
|
||||
*out = fletcher16_compute(&ctxt);
|
||||
|
||||
return *out == *expected_out;
|
||||
}
|
||||
@ -70,7 +80,8 @@ test_fletcher16_checksum(void *out_, const void *in_, const void *expected_out_)
|
||||
|
||||
fletcher16_init(&ctxt);
|
||||
fletcher16_update(&ctxt, in, len);
|
||||
put_u16(out, fletcher16_final(&ctxt, len, len));
|
||||
fletcher16_update(&ctxt, zero16, 2);
|
||||
*out = fletcher16_final(&ctxt, len+2, len);
|
||||
|
||||
return *out == *expected_out;
|
||||
}
|
||||
@ -81,7 +92,7 @@ t_fletcher16_compute(void)
|
||||
struct bt_pair test_vectors[] = {
|
||||
{
|
||||
.in = "\001\002",
|
||||
.out = & (const u16) { 0x0403 },
|
||||
.out = & ((const u16) { straightforward_fletcher16_compute("\001\002") }),
|
||||
},
|
||||
{
|
||||
.in = "",
|
||||
|
113
lib/flowspec.c
113
lib/flowspec.c
@ -112,7 +112,6 @@ get_value_length(const byte *op)
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Flowspec iterators
|
||||
*/
|
||||
@ -244,6 +243,69 @@ flow6_next_part(const byte *pos, const byte *end)
|
||||
return flow_next_part(pos, end, 1);
|
||||
}
|
||||
|
||||
static const byte *
|
||||
flow_get_part(const byte *data, uint dlen, uint type, int ipv6)
|
||||
{
|
||||
const byte *part;
|
||||
|
||||
for (part = flow_first_part(data);
|
||||
part && (part[0] <= type);
|
||||
part = flow_next_part(part, data+dlen, ipv6))
|
||||
if (part[0] == type)
|
||||
return part;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const byte *
|
||||
flow4_get_part(const net_addr_flow4 *f, uint type)
|
||||
{
|
||||
return flow_get_part(f->data, f->length - sizeof(net_addr_flow4), type, 0);
|
||||
}
|
||||
|
||||
const byte *
|
||||
flow6_get_part(const net_addr_flow6 *f, uint type)
|
||||
{
|
||||
return flow_get_part(f->data, f->length - sizeof(net_addr_flow6), type, 1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Flowspec accessors
|
||||
*/
|
||||
|
||||
static inline ip4_addr
|
||||
flow_read_ip4(const byte *px, uint pxlen)
|
||||
{
|
||||
ip4_addr ip = IP4_NONE;
|
||||
memcpy(&ip, px, BYTES(pxlen));
|
||||
return ip4_ntoh(ip);
|
||||
}
|
||||
|
||||
ip4_addr
|
||||
flow_read_ip4_part(const byte *part)
|
||||
{
|
||||
return flow_read_ip4(part + 2, part[1]);
|
||||
}
|
||||
|
||||
static inline ip6_addr
|
||||
flow_read_ip6(const byte *px, uint pxlen, uint pxoffset)
|
||||
{
|
||||
uint floor_offset = BYTES(pxoffset - (pxoffset % 8));
|
||||
uint ceil_len = BYTES(pxlen);
|
||||
ip6_addr ip = IP6_NONE;
|
||||
|
||||
memcpy(((byte *) &ip) + floor_offset, px, ceil_len - floor_offset);
|
||||
|
||||
return ip6_ntoh(ip);
|
||||
}
|
||||
|
||||
ip6_addr
|
||||
flow_read_ip6_part(const byte *part)
|
||||
{
|
||||
return flow_read_ip6(part + 3, part[1], part[2]);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Flowspec validation
|
||||
@ -374,7 +436,6 @@ flow_validate(const byte *nlri, uint len, int ipv6)
|
||||
enum flow_type type = 0;
|
||||
const byte *pos = nlri;
|
||||
const byte *end = nlri + len;
|
||||
int met_dst_pfx = 0;
|
||||
|
||||
while (pos < end)
|
||||
{
|
||||
@ -386,8 +447,6 @@ flow_validate(const byte *nlri, uint len, int ipv6)
|
||||
switch (type)
|
||||
{
|
||||
case FLOW_TYPE_DST_PREFIX:
|
||||
met_dst_pfx = 1;
|
||||
/* Fall through */
|
||||
case FLOW_TYPE_SRC_PREFIX:
|
||||
{
|
||||
uint pxlen = *pos++;
|
||||
@ -494,9 +553,6 @@ flow_validate(const byte *nlri, uint len, int ipv6)
|
||||
if (pos != end)
|
||||
return FLOW_ST_NOT_COMPLETE;
|
||||
|
||||
if (!ipv6 && !met_dst_pfx)
|
||||
return FLOW_ST_DEST_PREFIX_REQUIRED;
|
||||
|
||||
return FLOW_ST_VALID;
|
||||
}
|
||||
|
||||
@ -779,26 +835,6 @@ flow_builder_set_type(struct flow_builder *fb, enum flow_type type)
|
||||
fb->this_type = type;
|
||||
}
|
||||
|
||||
static ip4_addr
|
||||
flow_read_ip4(const byte *px, uint pxlen)
|
||||
{
|
||||
ip4_addr ip = IP4_NONE;
|
||||
memcpy(&ip, px, BYTES(pxlen));
|
||||
return ip4_ntoh(ip);
|
||||
}
|
||||
|
||||
static ip6_addr
|
||||
flow_read_ip6(const byte *px, uint pxlen, uint pxoffset)
|
||||
{
|
||||
uint floor_offset = BYTES(pxoffset - (pxoffset % 8));
|
||||
uint ceil_len = BYTES(pxlen);
|
||||
ip6_addr ip = IP6_NONE;
|
||||
|
||||
memcpy(((byte *) &ip) + floor_offset, px, ceil_len - floor_offset);
|
||||
|
||||
return ip6_ntoh(ip);
|
||||
}
|
||||
|
||||
static void
|
||||
builder_write_parts(struct flow_builder *fb, byte *buf)
|
||||
{
|
||||
@ -831,9 +867,9 @@ flow_builder4_finalize(struct flow_builder *fb, linpool *lpool)
|
||||
|
||||
if (fb->parts[FLOW_TYPE_DST_PREFIX].length)
|
||||
{
|
||||
byte *p = fb->data.data + fb->parts[FLOW_TYPE_DST_PREFIX].offset + 1;
|
||||
pxlen = *p++;
|
||||
prefix = flow_read_ip4(p, pxlen);
|
||||
byte *part = fb->data.data + fb->parts[FLOW_TYPE_DST_PREFIX].offset;
|
||||
prefix = flow_read_ip4_part(part);
|
||||
pxlen = flow_read_pxlen(part);
|
||||
}
|
||||
*f = NET_ADDR_FLOW4(prefix, pxlen, data_len);
|
||||
|
||||
@ -861,10 +897,9 @@ flow_builder6_finalize(struct flow_builder *fb, linpool *lpool)
|
||||
|
||||
if (fb->parts[FLOW_TYPE_DST_PREFIX].length)
|
||||
{
|
||||
byte *p = fb->data.data + fb->parts[FLOW_TYPE_DST_PREFIX].offset + 1;
|
||||
pxlen = *p++;
|
||||
uint pxoffset = *p++;
|
||||
prefix = flow_read_ip6(p, pxlen, pxoffset);
|
||||
byte *part = fb->data.data + fb->parts[FLOW_TYPE_DST_PREFIX].offset;
|
||||
prefix = flow_read_ip6_part(part);
|
||||
pxlen = flow_read_pxlen(part);
|
||||
}
|
||||
*n = NET_ADDR_FLOW6(prefix, pxlen, data_len);
|
||||
|
||||
@ -947,18 +982,18 @@ fragment_val_str(u8 val)
|
||||
static void
|
||||
net_format_flow_ip(buffer *b, const byte *part, int ipv6)
|
||||
{
|
||||
uint pxlen = *(part+1);
|
||||
uint pxlen = part[1];
|
||||
if (ipv6)
|
||||
{
|
||||
uint pxoffset = *(part+2);
|
||||
uint pxoffset = part[2];
|
||||
if (pxoffset)
|
||||
buffer_print(b, "%I6/%u offset %u; ", flow_read_ip6(part+3,pxlen,pxoffset), pxlen, pxoffset);
|
||||
buffer_print(b, "%I6/%u offset %u; ", flow_read_ip6_part(part), pxlen, pxoffset);
|
||||
else
|
||||
buffer_print(b, "%I6/%u; ", flow_read_ip6(part+3,pxlen,0), pxlen);
|
||||
buffer_print(b, "%I6/%u; ", flow_read_ip6_part(part), pxlen);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer_print(b, "%I4/%u; ", flow_read_ip4(part+2,pxlen), pxlen);
|
||||
buffer_print(b, "%I4/%u; ", flow_read_ip4_part(part), pxlen);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -83,6 +83,17 @@ const byte *flow4_first_part(const net_addr_flow4 *f);
|
||||
const byte *flow6_first_part(const net_addr_flow6 *f);
|
||||
const byte *flow4_next_part(const byte *pos, const byte *end);
|
||||
const byte *flow6_next_part(const byte *pos, const byte *end);
|
||||
const byte *flow4_get_part(const net_addr_flow4 *f, uint type);
|
||||
const byte *flow6_get_part(const net_addr_flow6 *f, uint type);
|
||||
|
||||
|
||||
/*
|
||||
* Flowspec accessors
|
||||
*/
|
||||
|
||||
ip4_addr flow_read_ip4_part(const byte *part);
|
||||
ip6_addr flow_read_ip6_part(const byte *part);
|
||||
static inline int flow_read_pxlen(const byte *part) { return part[1]; }
|
||||
|
||||
|
||||
/*
|
||||
|
@ -137,7 +137,7 @@ static int
|
||||
t_iterators6(void)
|
||||
{
|
||||
net_addr_flow6 *f;
|
||||
NET_ADDR_FLOW6_(f, ip6_build(0,0,0x12345678,0x9a000000), 64, ((byte[]) {
|
||||
NET_ADDR_FLOW6_(f, ip6_build(0,0,0x12345678,0x9a000000), 0x68, ((byte[]) {
|
||||
26, /* Length */
|
||||
FLOW_TYPE_DST_PREFIX, 0x68, 0x40, 0x12, 0x34, 0x56, 0x78, 0x9a,
|
||||
FLOW_TYPE_SRC_PREFIX, 0x08, 0x0, 0xc0,
|
||||
@ -166,6 +166,56 @@ t_iterators6(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
t_accessors4(void)
|
||||
{
|
||||
net_addr_flow4 *f;
|
||||
NET_ADDR_FLOW4_(f, ip4_build(5,6,7,0), 24, ((byte[]) {
|
||||
25, /* Length */
|
||||
FLOW_TYPE_DST_PREFIX, 24, 5, 6, 7,
|
||||
FLOW_TYPE_SRC_PREFIX, 32, 10, 11, 12, 13,
|
||||
FLOW_TYPE_IP_PROTOCOL, 0x81, 0x06,
|
||||
FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
|
||||
FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
|
||||
}));
|
||||
|
||||
const byte *p1_dst_px = &f->data[1];
|
||||
const ip4_addr p1_dst_addr = ip4_build(5,6,7,0);
|
||||
|
||||
const byte *p2_src_px = &f->data[6];
|
||||
const ip4_addr p2_src_addr = ip4_build(10,11,12,13);
|
||||
|
||||
bt_assert(ip4_equal(flow_read_ip4_part(p1_dst_px), p1_dst_addr));
|
||||
bt_assert(ip4_equal(flow_read_ip4_part(p2_src_px), p2_src_addr));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
t_accessors6(void)
|
||||
{
|
||||
net_addr_flow6 *f;
|
||||
NET_ADDR_FLOW6_(f, ip6_build(0,0,0x12345678,0x9a000000), 0x68, ((byte[]) {
|
||||
26, /* Length */
|
||||
FLOW_TYPE_DST_PREFIX, 0x68, 0x40, 0x12, 0x34, 0x56, 0x78, 0x9a,
|
||||
FLOW_TYPE_SRC_PREFIX, 0x08, 0x0, 0xc0,
|
||||
FLOW_TYPE_NEXT_HEADER, 0x81, 0x06,
|
||||
FLOW_TYPE_PORT, 0x03, 0x89, 0x45, 0x8b, 0x91, 0x1f, 0x90,
|
||||
FLOW_TYPE_LABEL, 0x80, 0x55,
|
||||
}));
|
||||
|
||||
const byte *p1_dst_px = &f->data[1];
|
||||
const ip6_addr p1_dst_addr = ip6_build(0,0,0x12345678,0x9a000000);
|
||||
|
||||
const byte *p2_src_px = &f->data[9];
|
||||
const ip6_addr p2_src_addr = ip6_build(0xc0000000, 0, 0, 0);
|
||||
|
||||
bt_assert(ip6_equal(flow_read_ip6_part(p1_dst_px), p1_dst_addr));
|
||||
bt_assert(ip6_equal(flow_read_ip6_part(p2_src_px), p2_src_addr));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
t_validation4(void)
|
||||
{
|
||||
@ -179,11 +229,9 @@ t_validation4(void)
|
||||
FLOW_TYPE_TCP_FLAGS, 0x80, 0x55,
|
||||
};
|
||||
|
||||
/* Isn't included destination prefix */
|
||||
/* Empty NLRI */
|
||||
res = flow4_validate(nlri1, 0);
|
||||
bt_assert(res == FLOW_ST_DEST_PREFIX_REQUIRED);
|
||||
res = flow4_validate(&nlri1[5], sizeof(nlri1)-5);
|
||||
bt_assert(res == FLOW_ST_DEST_PREFIX_REQUIRED);
|
||||
bt_assert(res == FLOW_ST_VALID);
|
||||
|
||||
/* Valid / Not Complete testing */
|
||||
uint valid_sizes[] = {5, 11, 14, 22, 25, 0};
|
||||
@ -628,6 +676,8 @@ main(int argc, char *argv[])
|
||||
bt_test_suite(t_first_part, "Searching first part in net_addr_flow");
|
||||
bt_test_suite(t_iterators4, "Testing iterators (IPv4)");
|
||||
bt_test_suite(t_iterators6, "Testing iterators (IPv6)");
|
||||
bt_test_suite(t_accessors4, "Testing accessors (IPv4)");
|
||||
bt_test_suite(t_accessors6, "Testing accessors (IPv6)");
|
||||
bt_test_suite(t_validation4, "Testing validation (IPv4)");
|
||||
bt_test_suite(t_validation6, "Testing validation (IPv6)");
|
||||
bt_test_suite(t_builder4, "Inserting components into existing Flow Specification (IPv4)");
|
||||
|
5
lib/ip.c
5
lib/ip.c
@ -264,6 +264,9 @@ ip6_pton(const char *a, ip6_addr *o)
|
||||
int i, j, k, l, hfil;
|
||||
const char *start;
|
||||
|
||||
if (!a[0]) /* Empty string check */
|
||||
return 0;
|
||||
|
||||
if (a[0] == ':') /* Leading :: */
|
||||
{
|
||||
if (a[1] != ':')
|
||||
@ -333,6 +336,8 @@ ip6_pton(const char *a, ip6_addr *o)
|
||||
for (; i>=hfil; i--)
|
||||
words[i] = 0;
|
||||
}
|
||||
else if (i != 8) /* Incomplete address */
|
||||
return 0;
|
||||
|
||||
/* Convert the address to ip6_addr format */
|
||||
for (i=0; i<4; i++)
|
||||
|
7
lib/ip.h
7
lib/ip.h
@ -48,6 +48,13 @@
|
||||
#define UDP_HEADER_LENGTH 8
|
||||
|
||||
|
||||
/* IANA Address Family Numbers */
|
||||
/* https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xhtml */
|
||||
/* Would use AF_ prefix, but that collides with POSIX address family numbers */
|
||||
#define AFI_IPV4 1
|
||||
#define AFI_IPV6 2
|
||||
|
||||
|
||||
#ifdef DEBUGGING
|
||||
|
||||
typedef struct ip4_addr {
|
||||
|
@ -13,25 +13,38 @@
|
||||
#define IP4_MAX_LEN 16
|
||||
|
||||
static int
|
||||
test_ipa_pton(void *out_, const void *in_, const void *expected_out_)
|
||||
test_ip4_pton(void *out_, const void *in_, const void *expected_out_)
|
||||
{
|
||||
ip_addr *out = out_;
|
||||
const char *in = in_;
|
||||
const ip_addr *expected_out = expected_out_;
|
||||
ip4_addr ip4;
|
||||
|
||||
if (expected_out)
|
||||
{
|
||||
bt_assert(ip4_pton(in, &ip4));
|
||||
*out = ipa_from_ip4(ip4);
|
||||
return ipa_equal(*out, *expected_out);
|
||||
}
|
||||
else
|
||||
return !ip4_pton(in, &ip4);
|
||||
|
||||
}
|
||||
|
||||
static int
|
||||
test_ip6_pton(void *out_, const void *in_, const void *expected_out_)
|
||||
{
|
||||
ip_addr *out = out_;
|
||||
const char *in = in_;
|
||||
const ip_addr *expected_out = expected_out_;
|
||||
|
||||
if (ipa_is_ip4(*expected_out))
|
||||
{
|
||||
ip4_addr ip4;
|
||||
bt_assert(ip4_pton(in, &ip4));
|
||||
*out = ipa_from_ip4(ip4);
|
||||
}
|
||||
else
|
||||
if (expected_out)
|
||||
{
|
||||
bt_assert(ip6_pton(in, out));
|
||||
/* ip_addr == ip6_addr */
|
||||
return ipa_equal(*out, *expected_out);
|
||||
}
|
||||
|
||||
return ipa_equal(*out, *expected_out);
|
||||
else
|
||||
return !ip6_pton(in, out);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -52,7 +65,7 @@ t_ip4_pton(void)
|
||||
},
|
||||
};
|
||||
|
||||
return bt_assert_batch(test_vectors, test_ipa_pton, bt_fmt_str, bt_fmt_ipa);
|
||||
return bt_assert_batch(test_vectors, test_ip4_pton, bt_fmt_str, bt_fmt_ipa);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -87,9 +100,17 @@ t_ip6_pton(void)
|
||||
.in = "2605:2700:0:3::4713:93e3",
|
||||
.out = & ipa_build6(0x26052700, 0x00000003, 0x00000000, 0x471393E3),
|
||||
},
|
||||
{
|
||||
.in = "2605:2700:0:3:4713:93e3",
|
||||
.out = NULL,
|
||||
},
|
||||
{
|
||||
.in = "2",
|
||||
.out = NULL,
|
||||
},
|
||||
};
|
||||
|
||||
return bt_assert_batch(test_vectors, test_ipa_pton, bt_fmt_str, bt_fmt_ipa);
|
||||
return bt_assert_batch(test_vectors, test_ip6_pton, bt_fmt_str, bt_fmt_ipa);
|
||||
}
|
||||
|
||||
static int
|
||||
|
79
lib/lists.c
79
lib/lists.c
@ -29,6 +29,42 @@
|
||||
#include "nest/bird.h"
|
||||
#include "lib/lists.h"
|
||||
|
||||
LIST_INLINE int
|
||||
check_list(list *l, node *n)
|
||||
{
|
||||
if (!l)
|
||||
{
|
||||
ASSERT_DIE(n);
|
||||
ASSERT_DIE(n->prev);
|
||||
|
||||
do { n = n->prev; } while (n->prev);
|
||||
|
||||
l = SKIP_BACK(list, head_node, n);
|
||||
}
|
||||
|
||||
int seen = 0;
|
||||
|
||||
ASSERT_DIE(l->null == NULL);
|
||||
ASSERT_DIE(l->head != NULL);
|
||||
ASSERT_DIE(l->tail != NULL);
|
||||
|
||||
node *prev = &l->head_node, *cur = l->head, *next = l->head->next;
|
||||
while (next)
|
||||
{
|
||||
if (cur == n)
|
||||
seen++;
|
||||
ASSERT_DIE(cur->prev == prev);
|
||||
prev = cur;
|
||||
cur = next;
|
||||
next = next->next;
|
||||
}
|
||||
|
||||
ASSERT_DIE(cur == &(l->tail_node));
|
||||
ASSERT_DIE(!n || (seen == 1));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* add_tail - append a node to a list
|
||||
* @l: linked list
|
||||
@ -39,6 +75,10 @@
|
||||
LIST_INLINE void
|
||||
add_tail(list *l, node *n)
|
||||
{
|
||||
EXPENSIVE_CHECK(check_list(l, NULL));
|
||||
ASSUME(n->prev == NULL);
|
||||
ASSUME(n->next == NULL);
|
||||
|
||||
node *z = l->tail;
|
||||
|
||||
n->next = &l->tail_node;
|
||||
@ -57,6 +97,10 @@ add_tail(list *l, node *n)
|
||||
LIST_INLINE void
|
||||
add_head(list *l, node *n)
|
||||
{
|
||||
EXPENSIVE_CHECK(check_list(l, NULL));
|
||||
ASSUME(n->prev == NULL);
|
||||
ASSUME(n->next == NULL);
|
||||
|
||||
node *z = l->head;
|
||||
|
||||
n->next = z;
|
||||
@ -76,6 +120,10 @@ add_head(list *l, node *n)
|
||||
LIST_INLINE void
|
||||
insert_node(node *n, node *after)
|
||||
{
|
||||
EXPENSIVE_CHECK(check_list(l, after));
|
||||
ASSUME(n->prev == NULL);
|
||||
ASSUME(n->next == NULL);
|
||||
|
||||
node *z = after->next;
|
||||
|
||||
n->next = z;
|
||||
@ -93,6 +141,8 @@ insert_node(node *n, node *after)
|
||||
LIST_INLINE void
|
||||
rem_node(node *n)
|
||||
{
|
||||
EXPENSIVE_CHECK(check_list(NULL, n));
|
||||
|
||||
node *z = n->prev;
|
||||
node *x = n->next;
|
||||
|
||||
@ -103,24 +153,20 @@ rem_node(node *n)
|
||||
}
|
||||
|
||||
/**
|
||||
* replace_node - replace a node in a list with another one
|
||||
* @old: node to be removed
|
||||
* @new: node to be inserted
|
||||
* update_node - update node after calling realloc on it
|
||||
* @n: node to be updated
|
||||
*
|
||||
* Replaces node @old in the list it's linked in with node @new. Node
|
||||
* @old may be a copy of the original node, which is not accessed
|
||||
* through the list. The function could be called with @old == @new,
|
||||
* which just fixes neighbors' pointers in the case that the node
|
||||
* was reallocated.
|
||||
* Fixes neighbor pointers.
|
||||
*/
|
||||
LIST_INLINE void
|
||||
replace_node(node *old, node *new)
|
||||
update_node(node *n)
|
||||
{
|
||||
old->next->prev = new;
|
||||
old->prev->next = new;
|
||||
ASSUME(n->next->prev == n->prev->next);
|
||||
|
||||
new->prev = old->prev;
|
||||
new->next = old->next;
|
||||
n->next->prev = n;
|
||||
n->prev->next = n;
|
||||
|
||||
EXPENSIVE_CHECK(check_list(NULL, n));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -149,6 +195,9 @@ init_list(list *l)
|
||||
LIST_INLINE void
|
||||
add_tail_list(list *to, list *l)
|
||||
{
|
||||
EXPENSIVE_CHECK(check_list(to, NULL));
|
||||
EXPENSIVE_CHECK(check_list(l, NULL));
|
||||
|
||||
node *p = to->tail;
|
||||
node *q = l->head;
|
||||
|
||||
@ -157,6 +206,8 @@ add_tail_list(list *to, list *l)
|
||||
q = l->tail;
|
||||
q->next = &to->tail_node;
|
||||
to->tail = q;
|
||||
|
||||
EXPENSIVE_CHECK(check_list(to, NULL));
|
||||
}
|
||||
|
||||
LIST_INLINE uint
|
||||
@ -165,6 +216,8 @@ list_length(list *l)
|
||||
uint len = 0;
|
||||
node *n;
|
||||
|
||||
EXPENSIVE_CHECK(check_list(l, NULL));
|
||||
|
||||
WALK_LIST(n, *l)
|
||||
len++;
|
||||
|
||||
|
@ -222,26 +222,29 @@ t_remove_node(void)
|
||||
}
|
||||
|
||||
static int
|
||||
t_replace_node(void)
|
||||
t_update_node(void)
|
||||
{
|
||||
node head, inside, tail;
|
||||
|
||||
init_list_();
|
||||
fill_list();
|
||||
|
||||
replace_node(&nodes[0], &head);
|
||||
head = nodes[0];
|
||||
update_node(&head);
|
||||
bt_assert(l.head == &head);
|
||||
bt_assert(head.prev == NODE &l.head);
|
||||
bt_assert(head.next == &nodes[1]);
|
||||
bt_assert(nodes[1].prev == &head);
|
||||
|
||||
replace_node(&nodes[MAX_NUM/2], &inside);
|
||||
inside = nodes[MAX_NUM/2];
|
||||
update_node(&inside);
|
||||
bt_assert(nodes[MAX_NUM/2-1].next == &inside);
|
||||
bt_assert(nodes[MAX_NUM/2+1].prev == &inside);
|
||||
bt_assert(inside.prev == &nodes[MAX_NUM/2-1]);
|
||||
bt_assert(inside.next == &nodes[MAX_NUM/2+1]);
|
||||
|
||||
replace_node(&nodes[MAX_NUM-1], &tail);
|
||||
tail = nodes[MAX_NUM-1];
|
||||
update_node(&tail);
|
||||
bt_assert(l.tail == &tail);
|
||||
bt_assert(tail.prev == &nodes[MAX_NUM-2]);
|
||||
bt_assert(tail.next == NODE &l.null);
|
||||
@ -280,7 +283,7 @@ main(int argc, char *argv[])
|
||||
bt_test_suite(t_add_head, "Adding nodes to head of list");
|
||||
bt_test_suite(t_insert_node, "Inserting nodes to list");
|
||||
bt_test_suite(t_remove_node, "Removing nodes from list");
|
||||
bt_test_suite(t_replace_node, "Replacing nodes in list");
|
||||
bt_test_suite(t_update_node, "Updating nodes in list");
|
||||
bt_test_suite(t_add_tail_list, "At the tail of a list adding the another list");
|
||||
|
||||
return bt_exit_value();
|
||||
|
@ -174,10 +174,10 @@ extern const u16 net_max_text_length[];
|
||||
((net_addr_roa6) { NET_ROA6, pxlen, sizeof(net_addr_roa6), prefix, max_pxlen, asn })
|
||||
|
||||
#define NET_ADDR_FLOW4(prefix,pxlen,dlen) \
|
||||
((net_addr_flow4) { NET_FLOW4, pxlen, sizeof(net_addr_ip4) + dlen, prefix })
|
||||
((net_addr_flow4) { NET_FLOW4, pxlen, sizeof(net_addr_flow4) + dlen, prefix })
|
||||
|
||||
#define NET_ADDR_FLOW6(prefix,pxlen,dlen) \
|
||||
((net_addr_flow6) { NET_FLOW6, pxlen, sizeof(net_addr_ip6) + dlen, prefix })
|
||||
((net_addr_flow6) { NET_FLOW6, pxlen, sizeof(net_addr_flow6) + dlen, prefix })
|
||||
|
||||
#define NET_ADDR_IP6_SADR(dst_prefix,dst_pxlen,src_prefix,src_pxlen) \
|
||||
((net_addr_ip6_sadr) { NET_IP6_SADR, dst_pxlen, sizeof(net_addr_ip6_sadr), dst_prefix, src_pxlen, src_prefix })
|
||||
|
65
lib/printf.c
65
lib/printf.c
@ -34,13 +34,7 @@ static int skip_atoi(const char **s)
|
||||
#define SPECIAL 32 /* 0x */
|
||||
#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
|
||||
|
||||
#define do_div(n,base) ({ \
|
||||
int __res; \
|
||||
__res = ((unsigned long) n) % (unsigned) base; \
|
||||
n = ((unsigned long) n) / (unsigned) base; \
|
||||
__res; })
|
||||
|
||||
static char * number(char * str, long num, int base, int size, int precision,
|
||||
static char * number(char * str, u64 num, uint base, int size, int precision,
|
||||
int type, int remains)
|
||||
{
|
||||
char c,sign,tmp[66];
|
||||
@ -58,7 +52,7 @@ static char * number(char * str, long num, int base, int size, int precision,
|
||||
c = (type & ZEROPAD) ? '0' : ' ';
|
||||
sign = 0;
|
||||
if (type & SIGN) {
|
||||
if (num < 0) {
|
||||
if (num > (u64) INT64_MAX) {
|
||||
sign = '-';
|
||||
num = -num;
|
||||
size--;
|
||||
@ -79,8 +73,11 @@ static char * number(char * str, long num, int base, int size, int precision,
|
||||
i = 0;
|
||||
if (num == 0)
|
||||
tmp[i++]='0';
|
||||
else while (num != 0)
|
||||
tmp[i++] = digits[do_div(num,base)];
|
||||
else while (num != 0) {
|
||||
uint res = num % base;
|
||||
num = num / base;
|
||||
tmp[i++] = digits[res];
|
||||
}
|
||||
if (i > precision)
|
||||
precision = i;
|
||||
size -= precision;
|
||||
@ -128,16 +125,17 @@ static char * number(char * str, long num, int base, int size, int precision,
|
||||
* value printed as eight :-separated octets), |%t| for time values (btime) with
|
||||
* specified subsecond precision, and |%m| resp. |%M| for error messages (uses
|
||||
* strerror() to translate @errno code to message text). On the other hand, it
|
||||
* doesn't support floating point numbers.
|
||||
* doesn't support floating point numbers. The bvsnprintf() supports |%h| and
|
||||
* |%l| qualifiers, but |%l| is used for s64/u64 instead of long/ulong.
|
||||
*
|
||||
* Result: number of characters of the output string or -1 if
|
||||
* the buffer space was insufficient.
|
||||
*/
|
||||
int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
|
||||
{
|
||||
int len;
|
||||
unsigned long num;
|
||||
int i, base;
|
||||
int len, i;
|
||||
u64 num;
|
||||
uint base;
|
||||
u32 x;
|
||||
u64 X;
|
||||
btime t;
|
||||
@ -152,7 +150,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
|
||||
int field_width; /* width of output field */
|
||||
int precision; /* min. # of digits for integers; max
|
||||
number of chars for from string */
|
||||
int qualifier; /* 'h', 'l', or 'L' for integer fields */
|
||||
int qualifier; /* 'h' or 'l' for integer fields */
|
||||
|
||||
for (start=str=buf ; *fmt ; ++fmt, size-=(str-start), start=str) {
|
||||
if (*fmt != '%') {
|
||||
@ -286,16 +284,15 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
|
||||
field_width = 2*sizeof(void *);
|
||||
flags |= ZEROPAD;
|
||||
}
|
||||
str = number(str,
|
||||
(unsigned long) va_arg(args, void *), 16,
|
||||
field_width, precision, flags, size);
|
||||
str = number(str, (uintptr_t) va_arg(args, void *), 16,
|
||||
field_width, precision, flags, size);
|
||||
if (!str)
|
||||
return -1;
|
||||
continue;
|
||||
|
||||
case 'n':
|
||||
if (qualifier == 'l') {
|
||||
long * ip = va_arg(args, long *);
|
||||
s64 * ip = va_arg(args, s64 *);
|
||||
*ip = (str - buf);
|
||||
} else {
|
||||
int * ip = va_arg(args, int *);
|
||||
@ -393,7 +390,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
|
||||
|
||||
/* Print seconds */
|
||||
flags |= SIGN;
|
||||
str = number(str, t1, 10, field_width, 0, flags, size);
|
||||
str = number(str, (u64) t1, 10, field_width, 0, flags, size);
|
||||
if (!str)
|
||||
return -1;
|
||||
|
||||
@ -411,7 +408,7 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
|
||||
|
||||
/* Print sub-seconds */
|
||||
*str++ = '.';
|
||||
str = number(str, t2, 10, precision, 0, ZEROPAD, size - 1);
|
||||
str = number(str, (u64) t2, 10, precision, 0, ZEROPAD, size - 1);
|
||||
if (!str)
|
||||
return -1;
|
||||
}
|
||||
@ -446,16 +443,22 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
|
||||
--fmt;
|
||||
continue;
|
||||
}
|
||||
if (qualifier == 'l')
|
||||
num = va_arg(args, unsigned long);
|
||||
else if (qualifier == 'h') {
|
||||
num = (unsigned short) va_arg(args, int);
|
||||
if (flags & SIGN)
|
||||
num = (short) num;
|
||||
} else if (flags & SIGN)
|
||||
num = va_arg(args, int);
|
||||
else
|
||||
num = va_arg(args, uint);
|
||||
if (flags & SIGN) {
|
||||
/* Conversions valid per ISO C99 6.3.1.3 (2) */
|
||||
if (qualifier == 'l')
|
||||
num = (u64) va_arg(args, s64);
|
||||
else if (qualifier == 'h')
|
||||
num = (u64) (short) va_arg(args, int);
|
||||
else
|
||||
num = (u64) va_arg(args, int);
|
||||
} else {
|
||||
if (qualifier == 'l')
|
||||
num = va_arg(args, u64);
|
||||
else if (qualifier == 'h')
|
||||
num = (unsigned short) va_arg(args, int);
|
||||
else
|
||||
num = va_arg(args, uint);
|
||||
}
|
||||
str = number(str, num, base, field_width, precision, flags, size);
|
||||
if (!str)
|
||||
return -1;
|
||||
|
@ -45,7 +45,7 @@ t_simple(void)
|
||||
else
|
||||
BSPRINTF(16, "00000fee1a15600d", buf, "%p", (void *) 0xfee1a15600d);
|
||||
|
||||
long ln = 0;
|
||||
s64 ln = 0;
|
||||
BSPRINTF(10, "TeStStRiNg", buf, "TeStS%lntRiNg", &ln);
|
||||
bt_assert_msg(ln == 5, "fmt=\"TeStS%%lntRiNg\", &ln makes ln=%ld, want 5", ln);
|
||||
|
||||
@ -54,7 +54,19 @@ t_simple(void)
|
||||
BSPRINTF(2, "+1", buf, "%+d", 1);
|
||||
BSPRINTF(2, " 1", buf, "% d", 1);
|
||||
BSPRINTF(2, "-1", buf, "%d", -1);
|
||||
BSPRINTF(11, "-2147483648", buf, "%d", -2147483648);
|
||||
BSPRINTF(11, "-2147483648", buf, "%d", INT32_MIN);
|
||||
BSPRINTF(10, "2147483647", buf, "%d", INT32_MAX);
|
||||
|
||||
BSPRINTF(1, "0", buf, "%u", 0x0);
|
||||
BSPRINTF(10, "4294967295", buf, "%u", 0xFFFFFFFF);
|
||||
|
||||
BSPRINTF(4, "-100", buf, "%ld", (s64) -100);
|
||||
BSPRINTF(3, "100", buf, "%ld", (s64) 100);
|
||||
BSPRINTF(20, "-9223372036854775808", buf, "%ld", INT64_MIN);
|
||||
BSPRINTF(19, "9223372036854775807", buf, "%ld", INT64_MAX);
|
||||
|
||||
BSPRINTF(3, "0 8", buf, "%lu %lu", U64(0), U64(8));
|
||||
BSPRINTF(20, "18446744073709551615", buf, "%lu", UINT64_MAX);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
@ -340,6 +340,7 @@ mb_alloc(pool *p, unsigned size)
|
||||
struct mblock *b = xmalloc(sizeof(struct mblock) + size);
|
||||
|
||||
b->r.class = &mb_class;
|
||||
b->r.n = (node) {};
|
||||
add_tail(&p->inside, &b->r.n);
|
||||
b->size = size;
|
||||
return b->data;
|
||||
@ -387,7 +388,7 @@ mb_realloc(void *m, unsigned size)
|
||||
struct mblock *b = SKIP_BACK(struct mblock, data, m);
|
||||
|
||||
b = xrealloc(b, sizeof(struct mblock) + size);
|
||||
replace_node(&b->r.n, &b->r.n);
|
||||
update_node(&b->r.n);
|
||||
b->size = size;
|
||||
return b->data;
|
||||
}
|
||||
|
@ -83,6 +83,7 @@ typedef struct slab slab;
|
||||
|
||||
slab *sl_new(pool *, unsigned size);
|
||||
void *sl_alloc(slab *);
|
||||
void *sl_allocz(slab *);
|
||||
void sl_free(slab *, void *);
|
||||
|
||||
/*
|
||||
|
31
lib/slab.c
31
lib/slab.c
@ -88,6 +88,14 @@ sl_alloc(slab *s)
|
||||
return o->data;
|
||||
}
|
||||
|
||||
void *
|
||||
sl_allocz(slab *s)
|
||||
{
|
||||
void *obj = sl_alloc(s);
|
||||
memset(obj, 0, s->size);
|
||||
return obj;
|
||||
}
|
||||
|
||||
void
|
||||
sl_free(slab *s, void *oo)
|
||||
{
|
||||
@ -216,8 +224,11 @@ sl_new_head(slab *s)
|
||||
struct sl_obj *no;
|
||||
uint n = s->objs_per_slab;
|
||||
|
||||
h->first_free = o;
|
||||
h->num_full = 0;
|
||||
*h = (struct sl_head) {
|
||||
.first_free = o,
|
||||
.num_full = 0,
|
||||
};
|
||||
|
||||
while (n--)
|
||||
{
|
||||
o->slab = h;
|
||||
@ -275,6 +286,22 @@ no_partial:
|
||||
goto okay;
|
||||
}
|
||||
|
||||
/**
|
||||
* sl_allocz - allocate an object from Slab and zero it
|
||||
* @s: slab
|
||||
*
|
||||
* sl_allocz() allocates space for a single object from the
|
||||
* Slab and returns a pointer to the object after zeroing out
|
||||
* the object memory.
|
||||
*/
|
||||
void *
|
||||
sl_allocz(slab *s)
|
||||
{
|
||||
void *obj = sl_alloc(s);
|
||||
memset(obj, 0, s->data_size);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* sl_free - return a free object back to a Slab
|
||||
* @s: slab
|
||||
|
@ -76,7 +76,7 @@ typedef struct birdsock {
|
||||
int rcv_ttl; /* TTL of last received datagram */
|
||||
node n;
|
||||
void *rbuf_alloc, *tbuf_alloc;
|
||||
char *password; /* Password for MD5 authentication */
|
||||
const char *password; /* Password for MD5 authentication */
|
||||
const char *err; /* Error message */
|
||||
struct ssh_sock *ssh; /* Used in SK_SSH */
|
||||
} sock;
|
||||
@ -97,7 +97,7 @@ void sk_dump_all(void);
|
||||
int sk_is_ipv4(sock *s); /* True if socket is IPv4 */
|
||||
int sk_is_ipv6(sock *s); /* True if socket is IPv6 */
|
||||
|
||||
static inline int sk_send_buffer_empty(sock *sk)
|
||||
static inline int sk_tx_buffer_empty(sock *sk)
|
||||
{ return sk->tbuf == sk->tpos; }
|
||||
|
||||
int sk_setup_multicast(sock *s); /* Prepare UDP or IP socket for multicasting */
|
||||
@ -106,7 +106,7 @@ int sk_leave_group(sock *s, ip_addr maddr); /* Leave multicast group on sk iface
|
||||
int sk_setup_broadcast(sock *s);
|
||||
int sk_set_ttl(sock *s, int ttl); /* Set transmit TTL for given socket */
|
||||
int sk_set_min_ttl(sock *s, int ttl); /* Set minimal accepted TTL for given socket */
|
||||
int sk_set_md5_auth(sock *s, ip_addr local, ip_addr remote, struct iface *ifa, char *passwd, int setkey);
|
||||
int sk_set_md5_auth(sock *s, ip_addr local, ip_addr remote, int pxlen, struct iface *ifa, const char *passwd, int setkey);
|
||||
int sk_set_ipv6_checksum(sock *s, int offset);
|
||||
int sk_set_icmp6_filter(sock *s, int p1, int p2);
|
||||
void sk_log_error(sock *s, const char *p);
|
||||
|
@ -72,6 +72,15 @@ bstrcmp(const char *s1, const char *s2)
|
||||
return !s2 - !s1;
|
||||
}
|
||||
|
||||
static inline void *
|
||||
bmemcpy(void *dest, const void *src, size_t n)
|
||||
{
|
||||
if (n)
|
||||
return memcpy(dest, src, n);
|
||||
else
|
||||
return dest;
|
||||
}
|
||||
|
||||
#define ROUTER_ID_64_LENGTH 23
|
||||
|
||||
#endif
|
||||
|
@ -253,9 +253,9 @@ timer_init(void)
|
||||
* type &btime.
|
||||
*/
|
||||
btime
|
||||
tm_parse_time(char *x)
|
||||
tm_parse_time(const char *x)
|
||||
{
|
||||
struct tm tm;
|
||||
struct tm tm = {};
|
||||
int usec, n1, n2, n3, r;
|
||||
|
||||
r = sscanf(x, "%d-%d-%d%n %d:%d:%d%n.%d%n",
|
||||
|
@ -106,7 +106,7 @@ void timer_init(void);
|
||||
|
||||
|
||||
struct timeformat {
|
||||
char *fmt1, *fmt2;
|
||||
const char *fmt1, *fmt2;
|
||||
btime limit;
|
||||
};
|
||||
|
||||
@ -120,7 +120,7 @@ struct timeformat {
|
||||
|
||||
#define TM_DATETIME_BUFFER_SIZE 32 /* Buffer size required by tm_format_time() */
|
||||
|
||||
btime tm_parse_time(char *x);
|
||||
btime tm_parse_time(const char *x);
|
||||
void tm_format_time(char *x, struct timeformat *fmt, btime t);
|
||||
int tm_format_real_time(char *x, size_t max, const char *fmt, btime t);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
Summary: BIRD Internet Routing Daemon
|
||||
Name: bird
|
||||
Version: 2.0.5
|
||||
Version: 2.0.8
|
||||
Release: 1
|
||||
Copyright: GPL
|
||||
Group: Networking/Daemons
|
||||
|
11
misc/docker/centos-8-amd64/Dockerfile
Normal file
11
misc/docker/centos-8-amd64/Dockerfile
Normal file
@ -0,0 +1,11 @@
|
||||
FROM centos:8
|
||||
RUN yum -y upgrade
|
||||
RUN yum -y install \
|
||||
autoconf \
|
||||
flex \
|
||||
bison \
|
||||
pkgconfig \
|
||||
'readline-devel' \
|
||||
'pkgconfig(ncurses)' \
|
||||
gcc \
|
||||
make
|
12
misc/docker/debian-10-amd64/Dockerfile
Normal file
12
misc/docker/debian-10-amd64/Dockerfile
Normal file
@ -0,0 +1,12 @@
|
||||
FROM debian:buster-slim
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
|
||||
RUN apt-get -y update
|
||||
RUN apt-get -y upgrade
|
||||
RUN apt-get -y install \
|
||||
autoconf \
|
||||
build-essential \
|
||||
flex \
|
||||
bison \
|
||||
ncurses-dev \
|
||||
libreadline-dev
|
12
misc/docker/debian-10-i386/Dockerfile
Normal file
12
misc/docker/debian-10-i386/Dockerfile
Normal file
@ -0,0 +1,12 @@
|
||||
FROM i386/debian:stretch-slim
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
|
||||
RUN apt-get -y update
|
||||
RUN apt-get -y upgrade
|
||||
RUN apt-get -y install \
|
||||
autoconf \
|
||||
build-essential \
|
||||
flex \
|
||||
bison \
|
||||
ncurses-dev \
|
||||
libreadline-dev
|
@ -1,7 +1,8 @@
|
||||
FROM debian:wheezy-slim
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
|
||||
RUN apt-get -y update
|
||||
RUN echo 'deb http://archive.debian.org/debian/ wheezy main' > /etc/apt/sources.list
|
||||
RUN echo 'deb http://archive.debian.org/debian-security/ wheezy/updates main' >> /etc/apt/sources.list
|
||||
RUN apt-get -y update -o Acquire::Check-Valid-Until=false
|
||||
RUN apt-get -y upgrade
|
||||
RUN apt-get -y install \
|
||||
autoconf \
|
||||
|
@ -1,7 +1,8 @@
|
||||
FROM i386/debian:wheezy-slim
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
|
||||
RUN apt-get -y update
|
||||
RUN echo 'deb http://archive.debian.org/debian/ wheezy main' > /etc/apt/sources.list
|
||||
RUN echo 'deb http://archive.debian.org/debian-security/ wheezy/updates main' >> /etc/apt/sources.list
|
||||
RUN apt-get -y update -o Acquire::Check-Valid-Until=false
|
||||
RUN apt-get -y upgrade
|
||||
RUN apt-get -y install \
|
||||
autoconf \
|
||||
|
@ -1,6 +1,7 @@
|
||||
FROM fedora:25
|
||||
RUN dnf -y upgrade
|
||||
RUN dnf -y install \
|
||||
make \
|
||||
autoconf \
|
||||
flex \
|
||||
bison \
|
||||
|
@ -1,6 +1,7 @@
|
||||
FROM fedora:26
|
||||
RUN dnf -y upgrade
|
||||
RUN dnf -y install \
|
||||
make \
|
||||
autoconf \
|
||||
flex \
|
||||
bison \
|
||||
|
11
misc/docker/fedora-27-amd64/Dockerfile
Normal file
11
misc/docker/fedora-27-amd64/Dockerfile
Normal file
@ -0,0 +1,11 @@
|
||||
FROM fedora:27
|
||||
RUN dnf -y upgrade
|
||||
RUN dnf -y install \
|
||||
make \
|
||||
autoconf \
|
||||
flex \
|
||||
bison \
|
||||
pkgconfig \
|
||||
'readline-devel' \
|
||||
'pkgconfig(ncurses)' \
|
||||
gcc
|
11
misc/docker/fedora-28-amd64/Dockerfile
Normal file
11
misc/docker/fedora-28-amd64/Dockerfile
Normal file
@ -0,0 +1,11 @@
|
||||
FROM fedora:28
|
||||
RUN dnf -y upgrade
|
||||
RUN dnf -y install \
|
||||
make \
|
||||
autoconf \
|
||||
flex \
|
||||
bison \
|
||||
pkgconfig \
|
||||
'readline-devel' \
|
||||
'pkgconfig(ncurses)' \
|
||||
gcc
|
11
misc/docker/fedora-29-amd64/Dockerfile
Normal file
11
misc/docker/fedora-29-amd64/Dockerfile
Normal file
@ -0,0 +1,11 @@
|
||||
FROM fedora:29
|
||||
RUN dnf -y upgrade
|
||||
RUN dnf -y install \
|
||||
make \
|
||||
autoconf \
|
||||
flex \
|
||||
bison \
|
||||
pkgconfig \
|
||||
'readline-devel' \
|
||||
'pkgconfig(ncurses)' \
|
||||
gcc
|
11
misc/docker/fedora-30-amd64/Dockerfile
Normal file
11
misc/docker/fedora-30-amd64/Dockerfile
Normal file
@ -0,0 +1,11 @@
|
||||
FROM fedora:30
|
||||
RUN dnf -y upgrade
|
||||
RUN dnf -y install \
|
||||
make \
|
||||
autoconf \
|
||||
flex \
|
||||
bison \
|
||||
pkgconfig \
|
||||
'readline-devel' \
|
||||
'pkgconfig(ncurses)' \
|
||||
gcc
|
11
misc/docker/fedora-31-amd64/Dockerfile
Normal file
11
misc/docker/fedora-31-amd64/Dockerfile
Normal file
@ -0,0 +1,11 @@
|
||||
FROM fedora:31
|
||||
RUN dnf -y upgrade
|
||||
RUN dnf -y install \
|
||||
make \
|
||||
autoconf \
|
||||
flex \
|
||||
bison \
|
||||
pkgconfig \
|
||||
'readline-devel' \
|
||||
'pkgconfig(ncurses)' \
|
||||
gcc
|
@ -1,4 +1,4 @@
|
||||
FROM opensuse:42.3
|
||||
FROM opensuse/leap:15.0
|
||||
RUN zypper -n up
|
||||
RUN zypper -n install \
|
||||
autoconf \
|
11
misc/docker/opensuse-15.1-amd64/Dockerfile
Normal file
11
misc/docker/opensuse-15.1-amd64/Dockerfile
Normal file
@ -0,0 +1,11 @@
|
||||
FROM opensuse/leap:15.1
|
||||
RUN zypper -n up
|
||||
RUN zypper -n install \
|
||||
autoconf \
|
||||
flex \
|
||||
bison \
|
||||
pkgconfig \
|
||||
readline-devel \
|
||||
ncurses-devel \
|
||||
gcc \
|
||||
gmake
|
12
misc/docker/ubuntu-18.04-amd64/Dockerfile
Normal file
12
misc/docker/ubuntu-18.04-amd64/Dockerfile
Normal file
@ -0,0 +1,12 @@
|
||||
FROM ubuntu:18.04
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
|
||||
RUN apt-get -y update
|
||||
RUN apt-get -y upgrade
|
||||
RUN apt-get -y install \
|
||||
autoconf \
|
||||
build-essential \
|
||||
flex \
|
||||
bison \
|
||||
ncurses-dev \
|
||||
libreadline-dev
|
12
misc/docker/ubuntu-19.10-amd64/Dockerfile
Normal file
12
misc/docker/ubuntu-19.10-amd64/Dockerfile
Normal file
@ -0,0 +1,12 @@
|
||||
FROM ubuntu:19.10
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
|
||||
RUN apt-get -y update
|
||||
RUN apt-get -y upgrade
|
||||
RUN apt-get -y install \
|
||||
autoconf \
|
||||
build-essential \
|
||||
flex \
|
||||
bison \
|
||||
ncurses-dev \
|
||||
libreadline-dev
|
100
nest/a-path.c
100
nest/a-path.c
@ -25,7 +25,7 @@
|
||||
#define BAD(DSC, VAL) ({ err_dsc = DSC; err_val = VAL; goto bad; })
|
||||
|
||||
int
|
||||
as_path_valid(byte *data, uint len, int bs, int confed, char *err, uint elen)
|
||||
as_path_valid(byte *data, uint len, int bs, int sets, int confed, char *err, uint elen)
|
||||
{
|
||||
byte *pos = data;
|
||||
char *err_dsc = NULL;
|
||||
@ -46,13 +46,21 @@ as_path_valid(byte *data, uint len, int bs, int confed, char *err, uint elen)
|
||||
switch (type)
|
||||
{
|
||||
case AS_PATH_SET:
|
||||
if (!sets)
|
||||
BAD("AS_SET segment", type);
|
||||
break;
|
||||
|
||||
case AS_PATH_SEQUENCE:
|
||||
break;
|
||||
|
||||
case AS_PATH_CONFED_SEQUENCE:
|
||||
case AS_PATH_CONFED_SET:
|
||||
if (!confed)
|
||||
BAD("AS_CONFED* segment", type);
|
||||
BAD("AS_CONFED_SEQUENCE segment", type);
|
||||
break;
|
||||
|
||||
case AS_PATH_CONFED_SET:
|
||||
if (!sets || !confed)
|
||||
BAD("AS_CONFED_SET segment", type);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -719,7 +727,7 @@ parse_path(const struct adata *path, struct pm_pos *pp)
|
||||
}
|
||||
|
||||
static int
|
||||
pm_match(struct pm_pos *pos, u32 asn, u32 asn2)
|
||||
pm_match_val(const struct pm_pos *pos, u32 asn, u32 asn2)
|
||||
{
|
||||
u32 gas;
|
||||
if (! pos->set)
|
||||
@ -740,25 +748,59 @@ pm_match(struct pm_pos *pos, u32 asn, u32 asn2)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
pm_mark(struct pm_pos *pos, int i, int plen, int *nl, int *nh)
|
||||
static int
|
||||
pm_match_set(const struct pm_pos *pos, const struct f_tree *set)
|
||||
{
|
||||
int j;
|
||||
struct f_val asn = { .type = T_INT };
|
||||
|
||||
if (pos[i].set)
|
||||
pos[i].mark = 1;
|
||||
if (! pos->set)
|
||||
{
|
||||
asn.val.i = pos->val.asn;
|
||||
return !!find_tree(set, &asn);
|
||||
}
|
||||
|
||||
const u8 *p = pos->val.sp;
|
||||
int len = *p++;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
asn.val.i = get_as(p + i * BS);
|
||||
if (find_tree(set, &asn))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
pm_match(const struct pm_pos *pos, const struct f_path_mask_item *mask, u32 asn, u32 asn2)
|
||||
{
|
||||
return ((mask->kind == PM_QUESTION) ||
|
||||
((mask->kind != PM_ASN_SET) ?
|
||||
pm_match_val(pos, asn, asn2) :
|
||||
pm_match_set(pos, mask->set)));
|
||||
}
|
||||
|
||||
static void
|
||||
pm_mark(struct pm_pos *pos, int *i, int plen, int *nl, int *nh)
|
||||
{
|
||||
int j = *i;
|
||||
|
||||
if (pos[j].set)
|
||||
do { pos[j].mark = 1; j++; }
|
||||
while ((j < plen) && pos[j].set);
|
||||
else
|
||||
j++;
|
||||
|
||||
for (j = i + 1; (j < plen) && pos[j].set && (! pos[j].mark); j++)
|
||||
pos[j].mark = 1;
|
||||
pos[j].mark = 1;
|
||||
|
||||
/* We are going downwards, therefore every mark is
|
||||
new low and just the first mark is new high */
|
||||
/* Update low, high based on first and last marked pos */
|
||||
int l = pos[*i].set ? *i : j;
|
||||
|
||||
*nl = i + (pos[i].set ? 0 : 1);
|
||||
|
||||
if (*nh < 0)
|
||||
*nh = j;
|
||||
*nl = (*nl < 0) ? l : MIN(*nl, l);
|
||||
*nh = MAX(*nh, j);
|
||||
*i = j;
|
||||
}
|
||||
|
||||
/* AS path matching is nontrivial. Because AS path can
|
||||
@ -788,7 +830,7 @@ as_path_match(const struct adata *path, const struct f_path_mask *mask)
|
||||
{
|
||||
struct pm_pos pos[2048 + 1];
|
||||
int plen = parse_path(path, pos);
|
||||
int l, h, i, nh, nl;
|
||||
int l, h, i, nh, nl, last, loop;
|
||||
u32 val = 0;
|
||||
u32 val2 = 0;
|
||||
|
||||
@ -798,7 +840,7 @@ as_path_match(const struct adata *path, const struct f_path_mask *mask)
|
||||
pos[plen].set = 0;
|
||||
pos[plen].mark = 0;
|
||||
|
||||
l = h = 0;
|
||||
l = h = loop = 0;
|
||||
pos[0].mark = 1;
|
||||
|
||||
for (uint m=0; m < mask->len; m++)
|
||||
@ -814,6 +856,10 @@ as_path_match(const struct adata *path, const struct f_path_mask *mask)
|
||||
h = plen;
|
||||
break;
|
||||
|
||||
case PM_LOOP:
|
||||
loop = 1;
|
||||
break;
|
||||
|
||||
case PM_ASN: /* Define single ASN as ASN..ASN - very narrow interval */
|
||||
val2 = val = mask->item[m].asn;
|
||||
goto step;
|
||||
@ -824,14 +870,25 @@ as_path_match(const struct adata *path, const struct f_path_mask *mask)
|
||||
val2 = mask->item[m].to;
|
||||
goto step;
|
||||
case PM_QUESTION:
|
||||
case PM_ASN_SET:
|
||||
step:
|
||||
nh = nl = -1;
|
||||
last = plen;
|
||||
for (i = h; i >= l; i--)
|
||||
if (pos[i].mark)
|
||||
{
|
||||
pos[i].mark = 0;
|
||||
if ((mask->item[m].kind == PM_QUESTION) || pm_match(pos + i, val, val2))
|
||||
pm_mark(pos, i, plen, &nl, &nh);
|
||||
int j = i;
|
||||
|
||||
match:
|
||||
if (pm_match(pos + j, &mask->item[m], val, val2))
|
||||
{
|
||||
pm_mark(pos, &j, plen, &nl, &nh);
|
||||
if (loop && (j < last))
|
||||
goto match;
|
||||
}
|
||||
|
||||
last = i;
|
||||
}
|
||||
|
||||
if (nh < 0)
|
||||
@ -839,6 +896,7 @@ as_path_match(const struct adata *path, const struct f_path_mask *mask)
|
||||
|
||||
h = nh;
|
||||
l = nl;
|
||||
loop = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@
|
||||
|
||||
struct f_tree;
|
||||
|
||||
int as_path_valid(byte *data, uint len, int bs, int confed, char *err, uint elen);
|
||||
int as_path_valid(byte *data, uint len, int bs, int sets, int confed, char *err, uint elen);
|
||||
int as_path_16to32(byte *dst, const byte *src, uint len);
|
||||
int as_path_32to16(byte *dst, const byte *src, uint len);
|
||||
int as_path_contains_as4(const struct adata *path);
|
||||
@ -60,11 +60,14 @@ static inline struct adata *as_path_prepend(struct linpool *pool, const struct a
|
||||
#define PM_ASTERISK 2
|
||||
#define PM_ASN_EXPR 3
|
||||
#define PM_ASN_RANGE 4
|
||||
#define PM_ASN_SET 5
|
||||
#define PM_LOOP 6
|
||||
|
||||
struct f_path_mask_item {
|
||||
union {
|
||||
u32 asn; /* PM_ASN */
|
||||
struct f_line *expr; /* PM_ASN_EXPR */
|
||||
const struct f_line *expr; /* PM_ASN_EXPR */
|
||||
const struct f_tree *set; /* PM_ASN_SET */
|
||||
struct { /* PM_ASN_RANGE */
|
||||
u32 from;
|
||||
u32 to;
|
||||
@ -109,7 +112,7 @@ static inline struct adata *
|
||||
aggregator_to_old(struct linpool *pool, const struct adata *a)
|
||||
{
|
||||
struct adata *d = lp_alloc_adata(pool, 8);
|
||||
put_u32(d->data, 0xFFFF);
|
||||
put_u32(d->data, AS_TRANS);
|
||||
memcpy(d->data + 4, a->data + 4, 4);
|
||||
return d;
|
||||
}
|
||||
|
22
nest/bfd.h
22
nest/bfd.h
@ -9,9 +9,20 @@
|
||||
|
||||
#include "lib/lists.h"
|
||||
#include "lib/resource.h"
|
||||
#include "conf/conf.h"
|
||||
|
||||
struct bfd_session;
|
||||
|
||||
struct bfd_options {
|
||||
u32 min_rx_int;
|
||||
u32 min_tx_int;
|
||||
u32 idle_tx_int;
|
||||
u8 multiplier;
|
||||
u8 passive;
|
||||
u8 passive_set;
|
||||
u8 mode;
|
||||
};
|
||||
|
||||
struct bfd_request {
|
||||
resource r;
|
||||
node n;
|
||||
@ -20,6 +31,7 @@ struct bfd_request {
|
||||
ip_addr local;
|
||||
struct iface *iface;
|
||||
struct iface *vrf;
|
||||
struct bfd_options opts;
|
||||
|
||||
void (*hook)(struct bfd_request *);
|
||||
void *data;
|
||||
@ -32,6 +44,7 @@ struct bfd_request {
|
||||
u8 down;
|
||||
};
|
||||
|
||||
#define BGP_BFD_GRACEFUL 2 /* BFD down triggers graceful restart */
|
||||
|
||||
#define BFD_STATE_ADMIN_DOWN 0
|
||||
#define BFD_STATE_DOWN 1
|
||||
@ -39,15 +52,20 @@ struct bfd_request {
|
||||
#define BFD_STATE_UP 3
|
||||
|
||||
|
||||
static inline struct bfd_options * bfd_new_options(void)
|
||||
{ return cfg_allocz(sizeof(struct bfd_options)); }
|
||||
|
||||
#ifdef CONFIG_BFD
|
||||
|
||||
struct bfd_request * bfd_request_session(pool *p, ip_addr addr, ip_addr local, struct iface *iface, struct iface *vrf, void (*hook)(struct bfd_request *), void *data);
|
||||
struct bfd_request * bfd_request_session(pool *p, ip_addr addr, ip_addr local, struct iface *iface, struct iface *vrf, void (*hook)(struct bfd_request *), void *data, const struct bfd_options *opts);
|
||||
void bfd_update_request(struct bfd_request *req, const struct bfd_options *opts);
|
||||
|
||||
static inline void cf_check_bfd(int use UNUSED) { }
|
||||
|
||||
#else
|
||||
|
||||
static inline struct bfd_request * bfd_request_session(pool *p UNUSED, ip_addr addr UNUSED, ip_addr local UNUSED, struct iface *iface UNUSED, struct iface *vrf UNUSED, void (*hook)(struct bfd_request *) UNUSED, void *data UNUSED) { return NULL; }
|
||||
static inline struct bfd_request * bfd_request_session(pool *p UNUSED, ip_addr addr UNUSED, ip_addr local UNUSED, struct iface *iface UNUSED, struct iface *vrf UNUSED, void (*hook)(struct bfd_request *) UNUSED, void *data UNUSED, const struct bfd_options *opts UNUSED) { return NULL; }
|
||||
static inline void bfd_update_request(struct bfd_request *req UNUSED, const struct bfd_options *opts UNUSED) { };
|
||||
|
||||
static inline void cf_check_bfd(int use) { if (use) cf_error("BFD not available"); }
|
||||
|
||||
|
@ -143,6 +143,7 @@ cli_printf(cli *c, int code, char *msg, ...)
|
||||
{
|
||||
size = bsprintf(buf, "%04d ", cd);
|
||||
errcode = 8000;
|
||||
cd = 0; /* Final message - no more continuation lines */
|
||||
}
|
||||
|
||||
c->last_reply = cd;
|
||||
|
@ -58,6 +58,9 @@ void cli_printf(cli *, int, char *, ...);
|
||||
#define cli_msg(x...) cli_printf(this_cli, x)
|
||||
void cli_set_log_echo(cli *, uint mask, uint size);
|
||||
|
||||
static inline void cli_separator(cli *c)
|
||||
{ if (c->last_reply) cli_printf(c, -c->last_reply, ""); };
|
||||
|
||||
/* Functions provided to sysdep layer */
|
||||
|
||||
cli *cli_new(void *);
|
||||
|
@ -27,6 +27,7 @@ cmd_show_status(void)
|
||||
cli_msg(-1000, "BIRD " BIRD_VERSION);
|
||||
tm_format_time(tim, &config->tf_base, current_time());
|
||||
cli_msg(-1011, "Router ID is %R", config->router_id);
|
||||
cli_msg(-1011, "Hostname is %s", config->hostname);
|
||||
cli_msg(-1011, "Current server time is %s", tim);
|
||||
tm_format_time(tim, &config->tf_base, boot_time);
|
||||
cli_msg(-1011, "Last reboot on %s", tim);
|
||||
|
126
nest/config.Y
126
nest/config.Y
@ -25,6 +25,7 @@ static struct iface_patt_node *this_ipn;
|
||||
static list *this_p_list;
|
||||
static struct password_item *this_p_item;
|
||||
static int password_id;
|
||||
static struct bfd_options *this_bfd_opts;
|
||||
|
||||
static void
|
||||
iface_patt_check(void)
|
||||
@ -51,6 +52,28 @@ get_passwords(void)
|
||||
return rv;
|
||||
}
|
||||
|
||||
static inline void
|
||||
init_bfd_opts(struct bfd_options **opts)
|
||||
{
|
||||
cf_check_bfd(1);
|
||||
|
||||
if (! *opts)
|
||||
*opts = bfd_new_options();
|
||||
}
|
||||
|
||||
static inline void
|
||||
open_bfd_opts(struct bfd_options **opts)
|
||||
{
|
||||
init_bfd_opts(opts);
|
||||
this_bfd_opts = *opts;
|
||||
}
|
||||
|
||||
static inline void
|
||||
close_bfd_opts(void)
|
||||
{
|
||||
this_bfd_opts = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
proto_postconfig(void)
|
||||
{
|
||||
@ -64,17 +87,19 @@ proto_postconfig(void)
|
||||
|
||||
CF_DECLS
|
||||
|
||||
CF_KEYWORDS(ROUTER, ID, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT)
|
||||
CF_KEYWORDS(ROUTER, ID, HOSTNAME, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT)
|
||||
CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, VRF, DEFAULT, TABLE, STATES, ROUTES, FILTERS)
|
||||
CF_KEYWORDS(IPV4, IPV6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, SADR, MPLS)
|
||||
CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED)
|
||||
CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES)
|
||||
CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERED, RPKI)
|
||||
CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, CHANNELS, INTERFACES)
|
||||
CF_KEYWORDS(ALGORITHM, KEYED, HMAC, MD5, SHA1, SHA256, SHA384, SHA512)
|
||||
CF_KEYWORDS(PRIMARY, STATS, COUNT, BY, FOR, COMMANDS, PREEXPORT, NOEXPORT, GENERATE)
|
||||
CF_KEYWORDS(PRIMARY, STATS, COUNT, BY, FOR, COMMANDS, PREEXPORT, NOEXPORT, EXPORTED, GENERATE)
|
||||
CF_KEYWORDS(BGP, PASSWORDS, DESCRIPTION, SORTED)
|
||||
CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP)
|
||||
CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LONG, ROUTE, PROTOCOL, BASE, LOG, S, MS, US)
|
||||
CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS)
|
||||
CF_KEYWORDS(MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE)
|
||||
CF_KEYWORDS(CHECK, LINK)
|
||||
|
||||
/* 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)
|
||||
@ -84,6 +109,7 @@ CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIREC
|
||||
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_ROA, ROA_, UNKNOWN, VALID, INVALID)
|
||||
CF_ENUM_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6)
|
||||
|
||||
%type <i32> idval
|
||||
%type <f> imexport
|
||||
@ -97,7 +123,8 @@ CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID)
|
||||
%type <cl> limit_spec
|
||||
%type <net> r_args_for_val
|
||||
%type <net_ptr> r_args_for
|
||||
%type <t> r_args_channel
|
||||
%type <t> channel_sym
|
||||
%type <c> channel_arg
|
||||
|
||||
CF_GRAMMAR
|
||||
|
||||
@ -124,6 +151,10 @@ idval:
|
||||
}
|
||||
;
|
||||
|
||||
conf: hostname_override ;
|
||||
|
||||
hostname_override: HOSTNAME text ';' { new_config->hostname = $2; } ;
|
||||
|
||||
conf: gr_opts ;
|
||||
|
||||
gr_opts: GRACEFUL RESTART WAIT expr ';' { new_config->gr_wait = $4; } ;
|
||||
@ -221,7 +252,7 @@ channel_start: net_type
|
||||
$$ = this_channel = channel_config_get(NULL, net_label[$1], $1, this_proto);
|
||||
};
|
||||
|
||||
channel_item:
|
||||
channel_item_:
|
||||
TABLE rtable {
|
||||
if (this_channel->net_type && ($2->addr_type != this_channel->net_type))
|
||||
cf_error("Incompatible table type");
|
||||
@ -234,6 +265,13 @@ channel_item:
|
||||
| EXPORT LIMIT limit_spec { this_channel->out_limit = $3; }
|
||||
| PREFERENCE expr { this_channel->preference = $2; check_u16($2); }
|
||||
| IMPORT KEEP FILTERED bool { this_channel->in_keep_filtered = $4; }
|
||||
| RPKI RELOAD bool { this_channel->rpki_reload = $3; }
|
||||
;
|
||||
|
||||
/* To avoid grammar collision in Pipe protocol */
|
||||
channel_item:
|
||||
channel_item_
|
||||
| DEBUG debug_mask { this_channel->debug = $2; }
|
||||
;
|
||||
|
||||
channel_opts:
|
||||
@ -284,6 +322,7 @@ conf: debug_default ;
|
||||
|
||||
debug_default:
|
||||
DEBUG PROTOCOLS debug_mask { new_config->proto_default_debug = $3; }
|
||||
| DEBUG CHANNELS debug_mask { new_config->channel_default_debug = $3; }
|
||||
| DEBUG COMMANDS expr { new_config->cli_debug = $3; }
|
||||
;
|
||||
|
||||
@ -453,11 +492,11 @@ password_item:
|
||||
password_item_begin:
|
||||
PASSWORD text {
|
||||
if (!this_p_list) {
|
||||
this_p_list = cfg_alloc(sizeof(list));
|
||||
this_p_list = cfg_allocz(sizeof(list));
|
||||
init_list(this_p_list);
|
||||
password_id = 1;
|
||||
}
|
||||
this_p_item = cfg_alloc(sizeof (struct password_item));
|
||||
this_p_item = cfg_allocz(sizeof(struct password_item));
|
||||
this_p_item->password = $2;
|
||||
this_p_item->length = strlen($2);
|
||||
this_p_item->genfrom = 0;
|
||||
@ -478,7 +517,7 @@ password_item_params:
|
||||
| ACCEPT TO time ';' password_item_params { this_p_item->accto = $3; }
|
||||
| FROM time ';' password_item_params { this_p_item->genfrom = this_p_item->accfrom = $2; }
|
||||
| TO time ';' password_item_params { this_p_item->gento = this_p_item->accto = $2; }
|
||||
| ID expr ';' password_item_params { this_p_item->id = $2; if ($2 <= 0) cf_error("Password ID has to be greated than zero."); }
|
||||
| ID expr ';' password_item_params { this_p_item->id = $2; if ($2 > 255) cf_error("Password ID must be in range 0-255"); }
|
||||
| ALGORITHM password_algorithm ';' password_item_params { this_p_item->alg = $2; }
|
||||
;
|
||||
|
||||
@ -495,6 +534,28 @@ password_algorithm:
|
||||
| HMAC SHA512 { $$ = ALG_HMAC_SHA512; }
|
||||
;
|
||||
|
||||
|
||||
/* BFD options */
|
||||
|
||||
bfd_item:
|
||||
INTERVAL expr_us { this_bfd_opts->min_rx_int = this_bfd_opts->min_tx_int = $2; }
|
||||
| MIN RX INTERVAL expr_us { this_bfd_opts->min_rx_int = $4; }
|
||||
| MIN TX INTERVAL expr_us { this_bfd_opts->min_tx_int = $4; }
|
||||
| IDLE TX INTERVAL expr_us { this_bfd_opts->idle_tx_int = $4; }
|
||||
| MULTIPLIER expr { this_bfd_opts->multiplier = $2; }
|
||||
| PASSIVE bool { this_bfd_opts->passive = $2; this_bfd_opts->passive_set = 1; }
|
||||
| GRACEFUL { this_bfd_opts->mode = BGP_BFD_GRACEFUL; }
|
||||
;
|
||||
|
||||
bfd_items:
|
||||
/* empty */
|
||||
| bfd_items bfd_item ';'
|
||||
;
|
||||
|
||||
bfd_opts:
|
||||
'{' bfd_items '}'
|
||||
;
|
||||
|
||||
/* Core commands */
|
||||
CF_CLI_HELP(SHOW, ..., [[Show status information]])
|
||||
|
||||
@ -556,15 +617,14 @@ r_args:
|
||||
rt_show_add_table($$, t->table);
|
||||
$$->tables_defined_by = RSD_TDB_ALL;
|
||||
}
|
||||
| r_args IMPORT TABLE CF_SYM_KNOWN '.' r_args_channel {
|
||||
cf_assert_symbol($4, SYM_PROTO);
|
||||
$$ = $1;
|
||||
struct proto_config *cf = $4->proto;
|
||||
if (!cf->proto) cf_error("%s is not a protocol", $4->name);
|
||||
struct channel *c = proto_find_channel_by_name(cf->proto, $6);
|
||||
if (!c) cf_error("Channel %s.%s not found", $4->name, $6);
|
||||
if (!c->in_table) cf_error("No import table in channel %s.%s", $4->name, $6);
|
||||
rt_show_add_table($$, c->in_table);
|
||||
| r_args IMPORT TABLE channel_arg {
|
||||
if (!$4->in_table) cf_error("No import table in channel %s.%s", $4->proto->name, $4->name);
|
||||
rt_show_add_table($$, $4->in_table);
|
||||
$$->tables_defined_by = RSD_TDB_DIRECT;
|
||||
}
|
||||
| r_args EXPORT TABLE channel_arg {
|
||||
if (!$4->out_table) cf_error("No export table in channel %s.%s", $4->proto->name, $4->name);
|
||||
rt_show_add_table($$, $4->out_table);
|
||||
$$->tables_defined_by = RSD_TDB_DIRECT;
|
||||
}
|
||||
| r_args FILTER filter {
|
||||
@ -599,15 +659,11 @@ r_args:
|
||||
$$->export_protocol = c->proto;
|
||||
$$->tables_defined_by = RSD_TDB_INDIRECT;
|
||||
}
|
||||
| r_args export_mode CF_SYM_KNOWN '.' r_args_channel {
|
||||
cf_assert_symbol($3, SYM_PROTO);
|
||||
struct proto_config *c = (struct proto_config *) $3->proto;
|
||||
| r_args export_mode channel_arg {
|
||||
$$ = $1;
|
||||
if ($$->export_mode) cf_error("Export specified twice");
|
||||
if (!c->proto) cf_error("%s is not a protocol", $3->name);
|
||||
$$->export_mode = $2;
|
||||
$$->export_channel = proto_find_channel_by_name(c->proto, $5);
|
||||
if (!$$->export_channel) cf_error("Export channel not found");
|
||||
$$->export_channel = $3;
|
||||
$$->tables_defined_by = RSD_TDB_INDIRECT;
|
||||
}
|
||||
| r_args PROTOCOL CF_SYM_KNOWN {
|
||||
@ -672,10 +728,11 @@ export_mode:
|
||||
PREEXPORT { $$ = RSEM_PREEXPORT; }
|
||||
| EXPORT { $$ = RSEM_EXPORT; }
|
||||
| NOEXPORT { $$ = RSEM_NOEXPORT; }
|
||||
| EXPORTED { $$ = RSEM_EXPORTED; }
|
||||
;
|
||||
|
||||
/* This is ugly hack */
|
||||
r_args_channel:
|
||||
channel_sym:
|
||||
IPV4 { $$ = "ipv4"; }
|
||||
| IPV4_MC { $$ = "ipv4-mc"; }
|
||||
| IPV4_MPLS { $$ = "ipv4-mpls"; }
|
||||
@ -698,6 +755,16 @@ r_args_channel:
|
||||
| SEC { $$ = "sec"; }
|
||||
;
|
||||
|
||||
channel_arg:
|
||||
CF_SYM_KNOWN '.' channel_sym {
|
||||
cf_assert_symbol($1, SYM_PROTO);
|
||||
struct proto *p = $1->proto->proto;
|
||||
if (!p) cf_error("%s is not a protocol", $1->name);
|
||||
$$ = proto_find_channel_by_name(p, $3);
|
||||
if (!$$) cf_error("Channel %s.%s not found", $1->name, $3);
|
||||
}
|
||||
;
|
||||
|
||||
CF_CLI_HELP(SHOW SYMBOLS, ..., [[Show all known symbolic names]])
|
||||
CF_CLI(SHOW SYMBOLS, sym_args, [table|filter|function|protocol|template|<symbol>], [[Show all known symbolic names]])
|
||||
{ cmd_show_symbols($3); } ;
|
||||
@ -772,8 +839,13 @@ CF_CLI(RELOAD OUT, proto_patt, <protocol> | \"<pattern>\" | all, [[Reload protoc
|
||||
{ proto_apply_cmd($3, proto_cmd_reload, 1, CMD_RELOAD_OUT); } ;
|
||||
|
||||
CF_CLI_HELP(DEBUG, ..., [[Control protocol debugging via BIRD logs]])
|
||||
CF_CLI(DEBUG, proto_patt debug_mask, (<protocol> | \"<pattern>\" | all) (all | off | { states|routes|filters|interfaces|events|packets [, ...] }), [[Control protocol debugging via BIRD logs]])
|
||||
{ proto_apply_cmd($2, proto_cmd_debug, 1, $3); } ;
|
||||
CF_CLI(DEBUG, debug_args, (<protocol> | <channel> | \"<pattern>\" | all) (all | off | { states|routes|filters|interfaces|events|packets [, ...] }), [[Control protocol debugging via BIRD logs]])
|
||||
{ /* Done in debug_args */ };
|
||||
|
||||
debug_args:
|
||||
proto_patt debug_mask { proto_apply_cmd($1, proto_cmd_debug, 1, $2); }
|
||||
| channel_arg debug_mask { channel_cmd_debug($1, $2); }
|
||||
;
|
||||
|
||||
CF_CLI_HELP(MRTDUMP, ..., [[Control protocol debugging via MRTdump files]])
|
||||
CF_CLI(MRTDUMP, proto_patt mrtdump_mask, (<protocol> | \"<pattern>\" | all) (all | off | { states|messages [, ...] }), [[Control protocol debugging via MRTdump format]])
|
||||
|
10
nest/iface.c
10
nest/iface.c
@ -172,12 +172,12 @@ static inline void
|
||||
ifa_notify_change(unsigned c, struct ifa *a)
|
||||
{
|
||||
if (c & IF_CHANGE_DOWN)
|
||||
neigh_ifa_update(a);
|
||||
neigh_ifa_down(a);
|
||||
|
||||
ifa_notify_change_(c, a);
|
||||
|
||||
if (c & IF_CHANGE_UP)
|
||||
neigh_ifa_update(a);
|
||||
neigh_ifa_up(a);
|
||||
}
|
||||
|
||||
static inline void
|
||||
@ -444,7 +444,7 @@ if_find_by_index(unsigned idx)
|
||||
* if no such structure exists.
|
||||
*/
|
||||
struct iface *
|
||||
if_find_by_name(char *name)
|
||||
if_find_by_name(const char *name)
|
||||
{
|
||||
struct iface *i;
|
||||
|
||||
@ -455,7 +455,7 @@ if_find_by_name(char *name)
|
||||
}
|
||||
|
||||
struct iface *
|
||||
if_get_by_name(char *name)
|
||||
if_get_by_name(const char *name)
|
||||
{
|
||||
struct iface *i;
|
||||
|
||||
@ -725,7 +725,7 @@ iface_patt_match(struct iface_patt *ifp, struct iface *i, struct ifa *a)
|
||||
|
||||
WALK_LIST(p, ifp->ipn_list)
|
||||
{
|
||||
char *t = p->pattern;
|
||||
const char *t = p->pattern;
|
||||
int pos = p->positive;
|
||||
|
||||
if (t)
|
||||
|
11
nest/iface.h
11
nest/iface.h
@ -115,15 +115,15 @@ void if_end_update(void);
|
||||
void if_flush_ifaces(struct proto *p);
|
||||
void if_feed_baby(struct proto *);
|
||||
struct iface *if_find_by_index(unsigned);
|
||||
struct iface *if_find_by_name(char *);
|
||||
struct iface *if_get_by_name(char *);
|
||||
struct iface *if_find_by_name(const char *);
|
||||
struct iface *if_get_by_name(const char *);
|
||||
void if_recalc_all_preferred_addresses(void);
|
||||
|
||||
|
||||
/* The Neighbor Cache */
|
||||
|
||||
typedef struct neighbor {
|
||||
node n; /* Node in global neighbor list */
|
||||
node n; /* Node in neighbor hash table chain */
|
||||
node if_n; /* Node in per-interface neighbor list */
|
||||
ip_addr addr; /* Address of the neighbor */
|
||||
struct ifa *ifa; /* Ifa on related iface */
|
||||
@ -150,7 +150,8 @@ void neigh_prune(void);
|
||||
void neigh_if_up(struct iface *);
|
||||
void neigh_if_down(struct iface *);
|
||||
void neigh_if_link(struct iface *);
|
||||
void neigh_ifa_update(struct ifa *);
|
||||
void neigh_ifa_up(struct ifa *a);
|
||||
void neigh_ifa_down(struct ifa *a);
|
||||
void neigh_init(struct pool *);
|
||||
|
||||
/*
|
||||
@ -160,7 +161,7 @@ void neigh_init(struct pool *);
|
||||
struct iface_patt_node {
|
||||
node n;
|
||||
int positive;
|
||||
byte *pattern;
|
||||
const byte *pattern;
|
||||
net_addr prefix;
|
||||
};
|
||||
|
||||
|
122
nest/neighbor.c
122
nest/neighbor.c
@ -66,10 +66,32 @@ neigh_hash(struct proto *p, ip_addr a, struct iface *i)
|
||||
return (p->hash_key ^ ipa_hash(a) ^ ptr_hash(i)) >> NEIGH_HASH_OFFSET;
|
||||
}
|
||||
|
||||
static inline int
|
||||
ifa_better(struct ifa *a, struct ifa *b)
|
||||
{
|
||||
return a && (!b || (a->prefix.pxlen > b->prefix.pxlen));
|
||||
}
|
||||
|
||||
static inline int
|
||||
scope_better(int sa, int sb)
|
||||
{
|
||||
/* Order per preference: -1 unknown, 0 for remote, 1 for local */
|
||||
sa = (sa < 0) ? sa : !sa;
|
||||
sb = (sb < 0) ? sb : !sb;
|
||||
|
||||
return sa > sb;
|
||||
}
|
||||
|
||||
static inline int
|
||||
scope_remote(int sa, int sb)
|
||||
{
|
||||
return (sa > SCOPE_HOST) && (sb > SCOPE_HOST);
|
||||
}
|
||||
|
||||
static int
|
||||
if_connected(ip_addr a, struct iface *i, struct ifa **ap, uint flags)
|
||||
{
|
||||
struct ifa *b;
|
||||
struct ifa *b, *addr = NULL;
|
||||
|
||||
/* Handle iface pseudo-neighbors */
|
||||
if (flags & NEF_IFACE)
|
||||
@ -89,12 +111,12 @@ if_connected(ip_addr a, struct iface *i, struct ifa **ap, uint flags)
|
||||
{
|
||||
if (b->flags & IA_PEER)
|
||||
{
|
||||
if (ipa_equal(a, b->opposite))
|
||||
return *ap = b, b->scope;
|
||||
if (ipa_equal(a, b->opposite) && ifa_better(b, addr))
|
||||
addr = b;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ipa_in_netX(a, &b->prefix))
|
||||
if (ipa_in_netX(a, &b->prefix) && ifa_better(b, addr))
|
||||
{
|
||||
/* Do not allow IPv4 network and broadcast addresses */
|
||||
if (ipa_is_ip4(a) &&
|
||||
@ -103,11 +125,15 @@ if_connected(ip_addr a, struct iface *i, struct ifa **ap, uint flags)
|
||||
ipa_equal(a, b->brd))) /* Broadcast */
|
||||
return *ap = NULL, -1;
|
||||
|
||||
return *ap = b, b->scope;
|
||||
addr = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Return found address */
|
||||
if (addr)
|
||||
return *ap = addr, addr->scope;
|
||||
|
||||
/* Handle ONLINK flag */
|
||||
if (flags & NEF_ONLINK)
|
||||
return *ap = NULL, ipa_classify(a) & IADDR_SCOPE_MASK;
|
||||
@ -125,10 +151,10 @@ if_connected_any(ip_addr a, struct iface *vrf, uint vrf_set, struct iface **ifac
|
||||
*iface = NULL;
|
||||
*addr = NULL;
|
||||
|
||||
/* Get first match, but prefer SCOPE_HOST to other matches */
|
||||
/* Prefer SCOPE_HOST or longer prefix */
|
||||
WALK_LIST(i, iface_list)
|
||||
if ((!vrf_set || vrf == i->master) && ((s = if_connected(a, i, &b, flags)) >= 0))
|
||||
if ((scope < 0) || ((scope > SCOPE_HOST) && (s == SCOPE_HOST)))
|
||||
if (scope_better(s, scope) || (scope_remote(s, scope) && ifa_better(b, *addr)))
|
||||
{
|
||||
*iface = i;
|
||||
*addr = b;
|
||||
@ -138,6 +164,33 @@ if_connected_any(ip_addr a, struct iface *vrf, uint vrf_set, struct iface **ifac
|
||||
return scope;
|
||||
}
|
||||
|
||||
/* Is ifa @a subnet of any ifa on iface @ib ? */
|
||||
static inline int
|
||||
ifa_intersect(struct ifa *a, struct iface *ib)
|
||||
{
|
||||
struct ifa *b;
|
||||
|
||||
WALK_LIST(b, ib->addrs)
|
||||
if (net_in_netX(&a->prefix, &b->prefix))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Is any ifa of iface @ia subnet of any ifa on iface @ib ? */
|
||||
static inline int
|
||||
if_intersect(struct iface *ia, struct iface *ib)
|
||||
{
|
||||
struct ifa *a, *b;
|
||||
|
||||
WALK_LIST(a, ia->addrs)
|
||||
WALK_LIST(b, ib->addrs)
|
||||
if (net_in_netX(&a->prefix, &b->prefix))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* neigh_find - find or create a neighbor entry
|
||||
* @p: protocol which asks for the entry
|
||||
@ -200,9 +253,7 @@ neigh_find(struct proto *p, ip_addr a, struct iface *iface, uint flags)
|
||||
if ((scope < 0) && !(flags & NEF_STICKY))
|
||||
return NULL;
|
||||
|
||||
n = sl_alloc(neigh_slab);
|
||||
memset(n, 0, sizeof(neighbor));
|
||||
|
||||
n = sl_allocz(neigh_slab);
|
||||
add_tail(&neigh_hash_table[h], &n->n);
|
||||
add_tail((scope >= 0) ? &iface->neighbors : &sticky_neigh_list, &n->if_n);
|
||||
n->addr = a;
|
||||
@ -323,9 +374,20 @@ neigh_update(neighbor *n, struct iface *iface)
|
||||
|
||||
scope = if_connected(n->addr, iface, &ifa, n->flags);
|
||||
|
||||
/* When neighbor is going down, try to respawn it on other ifaces */
|
||||
if ((scope < 0) && (n->scope >= 0) && !n->ifreq && (n->flags & NEF_STICKY))
|
||||
scope = if_connected_any(n->addr, p->vrf, p->vrf_set, &iface, &ifa, n->flags);
|
||||
/* Update about already assigned iface, or some other iface */
|
||||
if (iface == n->iface)
|
||||
{
|
||||
/* When neighbor is going down, try to respawn it on other ifaces */
|
||||
if ((scope < 0) && (n->scope >= 0) && !n->ifreq && (n->flags & NEF_STICKY))
|
||||
scope = if_connected_any(n->addr, p->vrf, p->vrf_set, &iface, &ifa, n->flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Continue only if the new variant is better than the existing one */
|
||||
if (! (scope_better(scope, n->scope) ||
|
||||
(scope_remote(scope, n->scope) && ifa_better(ifa, n->ifa))))
|
||||
return;
|
||||
}
|
||||
|
||||
/* No change or minor change - ignore or notify */
|
||||
if ((scope == n->scope) && (iface == n->iface))
|
||||
@ -367,9 +429,16 @@ neigh_update(neighbor *n, struct iface *iface)
|
||||
void
|
||||
neigh_if_up(struct iface *i)
|
||||
{
|
||||
struct iface *ii;
|
||||
neighbor *n;
|
||||
node *x, *y;
|
||||
|
||||
/* Update neighbors that might be better off with the new iface */
|
||||
WALK_LIST(ii, iface_list)
|
||||
if (!EMPTY_LIST(ii->neighbors) && (ii != i) && if_intersect(i, ii))
|
||||
WALK_LIST2_DELSAFE(n, x, y, ii->neighbors, if_n)
|
||||
neigh_update(n, i);
|
||||
|
||||
WALK_LIST2_DELSAFE(n, x, y, sticky_neigh_list, if_n)
|
||||
neigh_update(n, i);
|
||||
}
|
||||
@ -420,7 +489,25 @@ neigh_if_link(struct iface *i)
|
||||
* and causes all unreachable neighbors to be flushed.
|
||||
*/
|
||||
void
|
||||
neigh_ifa_update(struct ifa *a)
|
||||
neigh_ifa_up(struct ifa *a)
|
||||
{
|
||||
struct iface *i = a->iface, *ii;
|
||||
neighbor *n;
|
||||
node *x, *y;
|
||||
|
||||
/* Update neighbors that might be better off with the new ifa */
|
||||
WALK_LIST(ii, iface_list)
|
||||
if (!EMPTY_LIST(ii->neighbors) && ifa_intersect(a, ii))
|
||||
WALK_LIST2_DELSAFE(n, x, y, ii->neighbors, if_n)
|
||||
neigh_update(n, i);
|
||||
|
||||
/* Wake up all sticky neighbors that are reachable now */
|
||||
WALK_LIST2_DELSAFE(n, x, y, sticky_neigh_list, if_n)
|
||||
neigh_update(n, i);
|
||||
}
|
||||
|
||||
void
|
||||
neigh_ifa_down(struct ifa *a)
|
||||
{
|
||||
struct iface *i = a->iface;
|
||||
neighbor *n;
|
||||
@ -428,11 +515,8 @@ neigh_ifa_update(struct ifa *a)
|
||||
|
||||
/* Update all neighbors whose scope has changed */
|
||||
WALK_LIST2_DELSAFE(n, x, y, i->neighbors, if_n)
|
||||
neigh_update(n, i);
|
||||
|
||||
/* Wake up all sticky neighbors that are reachable now */
|
||||
WALK_LIST2_DELSAFE(n, x, y, sticky_neigh_list, if_n)
|
||||
neigh_update(n, i);
|
||||
if (n->ifa == a)
|
||||
neigh_update(n, i);
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
struct password_item {
|
||||
node n;
|
||||
char *password; /* Key data, null terminated */
|
||||
const char *password; /* Key data, null terminated */
|
||||
uint length; /* Key length, without null */
|
||||
uint id; /* Key ID */
|
||||
uint alg; /* MAC algorithm */
|
||||
|
384
nest/proto.c
384
nest/proto.c
@ -20,6 +20,7 @@
|
||||
#include "nest/iface.h"
|
||||
#include "nest/cli.h"
|
||||
#include "filter/filter.h"
|
||||
#include "filter/f-inst.h"
|
||||
|
||||
pool *proto_pool;
|
||||
list proto_list;
|
||||
@ -27,7 +28,8 @@ list proto_list;
|
||||
static list protocol_list;
|
||||
struct protocol *class_to_protocol[PROTOCOL__MAX];
|
||||
|
||||
#define PD(pr, msg, args...) do { if (pr->debug & D_STATES) { log(L_TRACE "%s: " msg, pr->name , ## args); } } while(0)
|
||||
#define CD(c, msg, args...) ({ if (c->debug & D_STATES) log(L_TRACE "%s.%s: " msg, c->proto->name, c->name ?: "?", ## args); })
|
||||
#define PD(p, msg, args...) ({ if (p->debug & D_STATES) log(L_TRACE "%s: " msg, p->name, ## args); })
|
||||
|
||||
static timer *proto_shutdown_timer;
|
||||
static timer *gr_wait_timer;
|
||||
@ -42,9 +44,11 @@ static u32 graceful_restart_locks;
|
||||
|
||||
static char *p_states[] = { "DOWN", "START", "UP", "STOP" };
|
||||
static char *c_states[] = { "DOWN", "START", "UP", "FLUSHING" };
|
||||
static char *e_states[] = { "DOWN", "FEEDING", "READY" };
|
||||
|
||||
extern struct protocol proto_unix_iface;
|
||||
|
||||
static void channel_request_reload(struct channel *c);
|
||||
static void proto_shutdown_loop(timer *);
|
||||
static void proto_rethink_goal(struct proto *p);
|
||||
static char *proto_state_name(struct proto *p);
|
||||
@ -58,6 +62,18 @@ static inline int proto_is_done(struct proto *p)
|
||||
static inline int channel_is_active(struct channel *c)
|
||||
{ return (c->channel_state == CS_START) || (c->channel_state == CS_UP); }
|
||||
|
||||
static inline int channel_reloadable(struct channel *c)
|
||||
{ return c->proto->reload_routes && c->reloadable; }
|
||||
|
||||
static inline void
|
||||
channel_log_state_change(struct channel *c)
|
||||
{
|
||||
if (c->export_state)
|
||||
CD(c, "State changed to %s/%s", c_states[c->channel_state], e_states[c->export_state]);
|
||||
else
|
||||
CD(c, "State changed to %s", c_states[c->channel_state]);
|
||||
}
|
||||
|
||||
static void
|
||||
proto_log_state_change(struct proto *p)
|
||||
{
|
||||
@ -159,30 +175,33 @@ proto_add_channel(struct proto *p, struct channel_config *cf)
|
||||
c->net_type = cf->net_type;
|
||||
c->ra_mode = cf->ra_mode;
|
||||
c->preference = cf->preference;
|
||||
c->debug = cf->debug;
|
||||
c->merge_limit = cf->merge_limit;
|
||||
c->in_keep_filtered = cf->in_keep_filtered;
|
||||
c->rpki_reload = cf->rpki_reload;
|
||||
|
||||
c->channel_state = CS_DOWN;
|
||||
c->export_state = ES_DOWN;
|
||||
c->last_state_change = current_time();
|
||||
c->last_tx_filter_change = current_time();
|
||||
c->reloadable = 1;
|
||||
|
||||
init_list(&c->roa_subscriptions);
|
||||
|
||||
CALL(c->channel->init, c, cf);
|
||||
|
||||
add_tail(&p->channels, &c->n);
|
||||
|
||||
PD(p, "Channel %s connected to table %s", c->name, c->table->name);
|
||||
CD(c, "Connected to table %s", c->table->name);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
void
|
||||
proto_remove_channel(struct proto *p, struct channel *c)
|
||||
proto_remove_channel(struct proto *p UNUSED, struct channel *c)
|
||||
{
|
||||
ASSERT(c->channel_state == CS_DOWN);
|
||||
|
||||
PD(p, "Channel %s removed", c->name);
|
||||
CD(c, "Removed", c->name);
|
||||
|
||||
rem_node(&c->n);
|
||||
mb_free(c);
|
||||
@ -233,7 +252,7 @@ channel_schedule_feed(struct channel *c, int initial)
|
||||
c->export_state = ES_FEEDING;
|
||||
c->refeeding = !initial;
|
||||
|
||||
ev_schedule(c->feed_event);
|
||||
ev_schedule_work(c->feed_event);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -244,26 +263,187 @@ channel_feed_loop(void *ptr)
|
||||
if (c->export_state != ES_FEEDING)
|
||||
return;
|
||||
|
||||
/* Start feeding */
|
||||
if (!c->feed_active)
|
||||
{
|
||||
if (c->proto->feed_begin)
|
||||
c->proto->feed_begin(c, !c->refeeding);
|
||||
|
||||
c->refeed_pending = 0;
|
||||
}
|
||||
|
||||
// DBG("Feeding protocol %s continued\n", p->name);
|
||||
if (!rt_feed_channel(c))
|
||||
{
|
||||
ev_schedule(c->feed_event);
|
||||
ev_schedule_work(c->feed_event);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Reset export limit if the feed ended with acceptable number of exported routes */
|
||||
struct channel_limit *l = &c->out_limit;
|
||||
if (c->refeeding &&
|
||||
(l->state == PLS_BLOCKED) &&
|
||||
(c->refeed_count <= l->limit) &&
|
||||
(c->stats.exp_routes <= l->limit))
|
||||
{
|
||||
log(L_INFO "Protocol %s resets route export limit (%u)", c->proto->name, l->limit);
|
||||
channel_reset_limit(&c->out_limit);
|
||||
|
||||
/* Continue in feed - it will process routing table again from beginning */
|
||||
c->refeed_count = 0;
|
||||
ev_schedule_work(c->feed_event);
|
||||
return;
|
||||
}
|
||||
|
||||
// DBG("Feeding protocol %s finished\n", p->name);
|
||||
c->export_state = ES_READY;
|
||||
// proto_log_state_change(p);
|
||||
channel_log_state_change(c);
|
||||
|
||||
if (c->proto->feed_end)
|
||||
c->proto->feed_end(c);
|
||||
|
||||
/* Restart feeding */
|
||||
if (c->refeed_pending)
|
||||
channel_request_feeding(c);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
channel_roa_in_changed(struct rt_subscription *s)
|
||||
{
|
||||
struct channel *c = s->data;
|
||||
int active = c->reload_event && ev_active(c->reload_event);
|
||||
|
||||
CD(c, "Reload triggered by RPKI change%s", active ? " - already active" : "");
|
||||
|
||||
if (!active)
|
||||
channel_request_reload(c);
|
||||
else
|
||||
c->reload_pending = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
channel_roa_out_changed(struct rt_subscription *s)
|
||||
{
|
||||
struct channel *c = s->data;
|
||||
int active = (c->export_state == ES_FEEDING);
|
||||
|
||||
CD(c, "Feeding triggered by RPKI change%s", active ? " - already active" : "");
|
||||
|
||||
if (!active)
|
||||
channel_request_feeding(c);
|
||||
else
|
||||
c->refeed_pending = 1;
|
||||
}
|
||||
|
||||
/* Temporary code, subscriptions should be changed to resources */
|
||||
struct roa_subscription {
|
||||
struct rt_subscription s;
|
||||
node roa_node;
|
||||
};
|
||||
|
||||
static int
|
||||
channel_roa_is_subscribed(struct channel *c, rtable *tab, int dir)
|
||||
{
|
||||
void (*hook)(struct rt_subscription *) =
|
||||
dir ? channel_roa_in_changed : channel_roa_out_changed;
|
||||
|
||||
struct roa_subscription *s;
|
||||
node *n;
|
||||
|
||||
WALK_LIST2(s, n, c->roa_subscriptions, roa_node)
|
||||
if ((s->s.tab == tab) && (s->s.hook == hook))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
channel_roa_subscribe(struct channel *c, rtable *tab, int dir)
|
||||
{
|
||||
if (channel_roa_is_subscribed(c, tab, dir))
|
||||
return;
|
||||
|
||||
struct roa_subscription *s = mb_allocz(c->proto->pool, sizeof(struct roa_subscription));
|
||||
|
||||
s->s.hook = dir ? channel_roa_in_changed : channel_roa_out_changed;
|
||||
s->s.data = c;
|
||||
rt_subscribe(tab, &s->s);
|
||||
|
||||
add_tail(&c->roa_subscriptions, &s->roa_node);
|
||||
}
|
||||
|
||||
static void
|
||||
channel_roa_unsubscribe(struct roa_subscription *s)
|
||||
{
|
||||
rt_unsubscribe(&s->s);
|
||||
rem_node(&s->roa_node);
|
||||
mb_free(s);
|
||||
}
|
||||
|
||||
static void
|
||||
channel_roa_subscribe_filter(struct channel *c, int dir)
|
||||
{
|
||||
const struct filter *f = dir ? c->in_filter : c->out_filter;
|
||||
struct rtable *tab;
|
||||
int valid = 1, found = 0;
|
||||
|
||||
if ((f == FILTER_ACCEPT) || (f == FILTER_REJECT))
|
||||
return;
|
||||
|
||||
/* No automatic reload for non-reloadable channels */
|
||||
if (dir && !channel_reloadable(c))
|
||||
valid = 0;
|
||||
|
||||
#ifdef CONFIG_BGP
|
||||
/* No automatic reload for BGP channels without in_table / out_table */
|
||||
if (c->channel == &channel_bgp)
|
||||
valid = dir ? !!c->in_table : !!c->out_table;
|
||||
#endif
|
||||
|
||||
struct filter_iterator fit;
|
||||
FILTER_ITERATE_INIT(&fit, f, c->proto->pool);
|
||||
|
||||
FILTER_ITERATE(&fit, fi)
|
||||
{
|
||||
switch (fi->fi_code)
|
||||
{
|
||||
case FI_ROA_CHECK_IMPLICIT:
|
||||
tab = fi->i_FI_ROA_CHECK_IMPLICIT.rtc->table;
|
||||
if (valid) channel_roa_subscribe(c, tab, dir);
|
||||
found = 1;
|
||||
break;
|
||||
|
||||
case FI_ROA_CHECK_EXPLICIT:
|
||||
tab = fi->i_FI_ROA_CHECK_EXPLICIT.rtc->table;
|
||||
if (valid) channel_roa_subscribe(c, tab, dir);
|
||||
found = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
FILTER_ITERATE_END;
|
||||
|
||||
FILTER_ITERATE_CLEANUP(&fit);
|
||||
|
||||
if (!valid && found)
|
||||
log(L_WARN "%s.%s: Automatic RPKI reload not active for %s",
|
||||
c->proto->name, c->name ?: "?", dir ? "import" : "export");
|
||||
}
|
||||
|
||||
static void
|
||||
channel_roa_unsubscribe_all(struct channel *c)
|
||||
{
|
||||
struct roa_subscription *s;
|
||||
node *n, *x;
|
||||
|
||||
WALK_LIST2_DELSAFE(s, n, x, c->roa_subscriptions, roa_node)
|
||||
channel_roa_unsubscribe(s);
|
||||
}
|
||||
|
||||
static void
|
||||
channel_start_export(struct channel *c)
|
||||
{
|
||||
@ -282,6 +462,7 @@ channel_stop_export(struct channel *c)
|
||||
|
||||
c->export_state = ES_DOWN;
|
||||
c->stats.exp_routes = 0;
|
||||
bmap_reset(&c->export_map, 1024);
|
||||
}
|
||||
|
||||
|
||||
@ -292,7 +473,7 @@ channel_schedule_reload(struct channel *c)
|
||||
ASSERT(c->channel_state == CS_UP);
|
||||
|
||||
rt_reload_channel_abort(c);
|
||||
ev_schedule(c->reload_event);
|
||||
ev_schedule_work(c->reload_event);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -300,11 +481,19 @@ channel_reload_loop(void *ptr)
|
||||
{
|
||||
struct channel *c = ptr;
|
||||
|
||||
/* Start reload */
|
||||
if (!c->reload_active)
|
||||
c->reload_pending = 0;
|
||||
|
||||
if (!rt_reload_channel(c))
|
||||
{
|
||||
ev_schedule(c->reload_event);
|
||||
ev_schedule_work(c->reload_event);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Restart reload */
|
||||
if (c->reload_pending)
|
||||
channel_request_reload(c);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -317,6 +506,13 @@ channel_reset_import(struct channel *c)
|
||||
rt_prune_sync(c->in_table, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
channel_reset_export(struct channel *c)
|
||||
{
|
||||
/* Just free the routes */
|
||||
rt_prune_sync(c->out_table, 1);
|
||||
}
|
||||
|
||||
/* Called by protocol to activate in_table */
|
||||
void
|
||||
channel_setup_in_table(struct channel *c)
|
||||
@ -331,6 +527,18 @@ channel_setup_in_table(struct channel *c)
|
||||
c->reload_event = ev_new_init(c->proto->pool, channel_reload_loop, c);
|
||||
}
|
||||
|
||||
/* Called by protocol to activate out_table */
|
||||
void
|
||||
channel_setup_out_table(struct channel *c)
|
||||
{
|
||||
struct rtable_config *cf = mb_allocz(c->proto->pool, sizeof(struct rtable_config));
|
||||
cf->name = "export";
|
||||
cf->addr_type = c->net_type;
|
||||
|
||||
c->out_table = mb_allocz(c->proto->pool, sizeof(struct rtable));
|
||||
rt_setup(c->proto->pool, c->out_table, cf);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
channel_do_start(struct channel *c)
|
||||
@ -341,6 +549,9 @@ channel_do_start(struct channel *c)
|
||||
|
||||
c->feed_event = ev_new_init(c->proto->pool, channel_feed_loop, c);
|
||||
|
||||
bmap_init(&c->export_map, c->proto->pool, 1024);
|
||||
memset(&c->stats, 0, sizeof(struct proto_stats));
|
||||
|
||||
channel_reset_limit(&c->rx_limit);
|
||||
channel_reset_limit(&c->in_limit);
|
||||
channel_reset_limit(&c->out_limit);
|
||||
@ -348,6 +559,17 @@ channel_do_start(struct channel *c)
|
||||
CALL(c->channel->start, c);
|
||||
}
|
||||
|
||||
static void
|
||||
channel_do_up(struct channel *c)
|
||||
{
|
||||
/* Register RPKI/ROA subscriptions */
|
||||
if (c->rpki_reload)
|
||||
{
|
||||
channel_roa_subscribe_filter(c, 1);
|
||||
channel_roa_subscribe_filter(c, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
channel_do_flush(struct channel *c)
|
||||
{
|
||||
@ -358,6 +580,14 @@ channel_do_flush(struct channel *c)
|
||||
channel_graceful_restart_unlock(c);
|
||||
|
||||
CALL(c->channel->shutdown, c);
|
||||
|
||||
/* This have to be done in here, as channel pool is freed before channel_do_down() */
|
||||
bmap_free(&c->export_map);
|
||||
c->in_table = NULL;
|
||||
c->reload_event = NULL;
|
||||
c->out_table = NULL;
|
||||
|
||||
channel_roa_unsubscribe_all(c);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -372,10 +602,12 @@ channel_do_down(struct channel *c)
|
||||
if ((c->stats.imp_routes + c->stats.filt_routes) != 0)
|
||||
log(L_ERR "%s: Channel %s is down but still has some routes", c->proto->name, c->name);
|
||||
|
||||
// bmap_free(&c->export_map);
|
||||
memset(&c->stats, 0, sizeof(struct proto_stats));
|
||||
|
||||
c->in_table = NULL;
|
||||
c->reload_event = NULL;
|
||||
c->out_table = NULL;
|
||||
|
||||
CALL(c->channel->cleanup, c);
|
||||
|
||||
@ -411,6 +643,9 @@ channel_set_state(struct channel *c, uint state)
|
||||
if (c->in_table && (cs == CS_UP))
|
||||
channel_reset_import(c);
|
||||
|
||||
if (c->out_table && (cs == CS_UP))
|
||||
channel_reset_export(c);
|
||||
|
||||
break;
|
||||
|
||||
case CS_UP:
|
||||
@ -422,6 +657,7 @@ channel_set_state(struct channel *c, uint state)
|
||||
if (!c->gr_wait && c->proto->rt_notify)
|
||||
channel_start_export(c);
|
||||
|
||||
channel_do_up(c);
|
||||
break;
|
||||
|
||||
case CS_FLUSHING:
|
||||
@ -433,6 +669,9 @@ channel_set_state(struct channel *c, uint state)
|
||||
if (c->in_table && (cs == CS_UP))
|
||||
channel_reset_import(c);
|
||||
|
||||
if (c->out_table && (cs == CS_UP))
|
||||
channel_reset_export(c);
|
||||
|
||||
channel_do_flush(c);
|
||||
break;
|
||||
|
||||
@ -445,7 +684,8 @@ channel_set_state(struct channel *c, uint state)
|
||||
default:
|
||||
ASSERT(0);
|
||||
}
|
||||
// XXXX proto_log_state_change(c);
|
||||
|
||||
channel_log_state_change(c);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -463,6 +703,8 @@ channel_request_feeding(struct channel *c)
|
||||
{
|
||||
ASSERT(c->channel_state == CS_UP);
|
||||
|
||||
CD(c, "Feeding requested");
|
||||
|
||||
/* Do nothing if we are still waiting for feeding */
|
||||
if (c->export_state == ES_DOWN)
|
||||
return;
|
||||
@ -477,19 +719,11 @@ channel_request_feeding(struct channel *c)
|
||||
rt_feed_channel_abort(c);
|
||||
}
|
||||
|
||||
channel_reset_limit(&c->out_limit);
|
||||
|
||||
/* Hack: reset exp_routes during refeed, and do not decrease it later */
|
||||
c->stats.exp_routes = 0;
|
||||
/* Track number of exported routes during refeed */
|
||||
c->refeed_count = 0;
|
||||
|
||||
channel_schedule_feed(c, 0); /* Sets ES_FEEDING */
|
||||
// proto_log_state_change(c);
|
||||
}
|
||||
|
||||
static inline int
|
||||
channel_reloadable(struct channel *c)
|
||||
{
|
||||
return c->proto->reload_routes && c->reloadable;
|
||||
channel_log_state_change(c);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -498,6 +732,8 @@ channel_request_reload(struct channel *c)
|
||||
ASSERT(c->channel_state == CS_UP);
|
||||
ASSERT(channel_reloadable(c));
|
||||
|
||||
CD(c, "Reload requested");
|
||||
|
||||
c->proto->reload_routes(c);
|
||||
|
||||
/*
|
||||
@ -543,6 +779,8 @@ channel_config_new(const struct channel_class *cc, const char *name, uint net_ty
|
||||
cf->net_type = net_type;
|
||||
cf->ra_mode = RA_OPTIMAL;
|
||||
cf->preference = proto->protocol->preference;
|
||||
cf->debug = new_config->channel_default_debug;
|
||||
cf->rpki_reload = 1;
|
||||
|
||||
add_tail(&proto->channels, &cf->n);
|
||||
|
||||
@ -575,6 +813,7 @@ channel_copy_config(struct channel_config *src, struct proto_config *proto)
|
||||
struct channel_config *dst = cfg_alloc(src->channel->config_size);
|
||||
|
||||
memcpy(dst, src, src->channel->config_size);
|
||||
memset(&dst->n, 0, sizeof(node));
|
||||
add_tail(&proto->channels, &dst->n);
|
||||
CALL(src->channel->copy_config, dst, src);
|
||||
|
||||
@ -594,6 +833,7 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
|
||||
/* Note that filter_same() requires arguments in (new, old) order */
|
||||
int import_changed = !filter_same(cf->in_filter, c->in_filter);
|
||||
int export_changed = !filter_same(cf->out_filter, c->out_filter);
|
||||
int rpki_reload_changed = (cf->rpki_reload != c->rpki_reload);
|
||||
|
||||
if (c->preference != cf->preference)
|
||||
import_changed = 1;
|
||||
@ -611,20 +851,31 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
|
||||
// c->ra_mode = cf->ra_mode;
|
||||
c->merge_limit = cf->merge_limit;
|
||||
c->preference = cf->preference;
|
||||
c->debug = cf->debug;
|
||||
c->in_keep_filtered = cf->in_keep_filtered;
|
||||
c->rpki_reload = cf->rpki_reload;
|
||||
|
||||
channel_verify_limits(c);
|
||||
|
||||
if (export_changed)
|
||||
c->last_tx_filter_change = current_time();
|
||||
|
||||
/* Execute channel-specific reconfigure hook */
|
||||
if (c->channel->reconfigure && !c->channel->reconfigure(c, cf))
|
||||
if (c->channel->reconfigure && !c->channel->reconfigure(c, cf, &import_changed, &export_changed))
|
||||
return 0;
|
||||
|
||||
/* If the channel is not open, it has no routes and we cannot reload it anyways */
|
||||
if (c->channel_state != CS_UP)
|
||||
return 1;
|
||||
goto done;
|
||||
|
||||
/* Update RPKI/ROA subscriptions */
|
||||
if (import_changed || export_changed || rpki_reload_changed)
|
||||
{
|
||||
channel_roa_unsubscribe_all(c);
|
||||
|
||||
if (c->rpki_reload)
|
||||
{
|
||||
channel_roa_subscribe_filter(c, 1);
|
||||
channel_roa_subscribe_filter(c, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (reconfigure_type == RECONFIG_SOFT)
|
||||
{
|
||||
@ -634,7 +885,7 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
|
||||
if (export_changed)
|
||||
log(L_INFO "Channel %s.%s changed export", c->proto->name, c->name);
|
||||
|
||||
return 1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Route reload may be not supported */
|
||||
@ -650,6 +901,8 @@ channel_reconfigure(struct channel *c, struct channel_config *cf)
|
||||
if (export_changed)
|
||||
channel_request_feeding(c);
|
||||
|
||||
done:
|
||||
CD(c, "Reconfigured");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -842,7 +1095,7 @@ proto_copy_config(struct proto_config *dest, struct proto_config *src)
|
||||
struct channel_config *cc;
|
||||
node old_node;
|
||||
int old_class;
|
||||
char *old_name;
|
||||
const char *old_name;
|
||||
|
||||
if (dest->protocol != src->protocol)
|
||||
cf_error("Can't copy configuration from a different protocol type");
|
||||
@ -1810,6 +2063,16 @@ channel_show_info(struct channel *c)
|
||||
channel_show_stats(c);
|
||||
}
|
||||
|
||||
void
|
||||
channel_cmd_debug(struct channel *c, uint mask)
|
||||
{
|
||||
if (cli_access_restricted())
|
||||
return;
|
||||
|
||||
c->debug = mask;
|
||||
cli_msg(0, "");
|
||||
}
|
||||
|
||||
void
|
||||
proto_cmd_show(struct proto *p, uintptr_t verbose, int cnt)
|
||||
{
|
||||
@ -1926,7 +2189,7 @@ proto_cmd_reload(struct proto *p, uintptr_t dir, int cnt UNUSED)
|
||||
/* All channels must support reload */
|
||||
if (dir != CMD_RELOAD_OUT)
|
||||
WALK_LIST(c, p->channels)
|
||||
if (!channel_reloadable(c))
|
||||
if ((c->channel_state == CS_UP) && !channel_reloadable(c))
|
||||
{
|
||||
cli_msg(-8006, "%s: reload failed", p->name);
|
||||
return;
|
||||
@ -1937,20 +2200,29 @@ proto_cmd_reload(struct proto *p, uintptr_t dir, int cnt UNUSED)
|
||||
/* re-importing routes */
|
||||
if (dir != CMD_RELOAD_OUT)
|
||||
WALK_LIST(c, p->channels)
|
||||
channel_request_reload(c);
|
||||
if (c->channel_state == CS_UP)
|
||||
channel_request_reload(c);
|
||||
|
||||
/* re-exporting routes */
|
||||
if (dir != CMD_RELOAD_IN)
|
||||
WALK_LIST(c, p->channels)
|
||||
channel_request_feeding(c);
|
||||
if (c->channel_state == CS_UP)
|
||||
channel_request_feeding(c);
|
||||
|
||||
cli_msg(-15, "%s: reloading", p->name);
|
||||
}
|
||||
|
||||
extern void pipe_update_debug(struct proto *P);
|
||||
|
||||
void
|
||||
proto_cmd_debug(struct proto *p, uintptr_t mask, int cnt UNUSED)
|
||||
{
|
||||
p->debug = mask;
|
||||
|
||||
#ifdef CONFIG_PIPE
|
||||
if (p->proto == &proto_pipe)
|
||||
pipe_update_debug(p);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@ -1960,7 +2232,7 @@ proto_cmd_mrtdump(struct proto *p, uintptr_t mask, int cnt UNUSED)
|
||||
}
|
||||
|
||||
static void
|
||||
proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uintptr_t, int), uintptr_t arg)
|
||||
proto_apply_cmd_symbol(const struct symbol *s, void (* cmd)(struct proto *, uintptr_t, int), uintptr_t arg)
|
||||
{
|
||||
if (s->class != SYM_PROTO)
|
||||
{
|
||||
@ -1973,7 +2245,7 @@ proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uintptr_t,
|
||||
}
|
||||
|
||||
static void
|
||||
proto_apply_cmd_patt(char *patt, void (* cmd)(struct proto *, uintptr_t, int), uintptr_t arg)
|
||||
proto_apply_cmd_patt(const char *patt, void (* cmd)(struct proto *, uintptr_t, int), uintptr_t arg)
|
||||
{
|
||||
struct proto *p;
|
||||
int cnt = 0;
|
||||
@ -2031,3 +2303,47 @@ proto_get_named(struct symbol *sym, struct protocol *pr)
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
struct proto *
|
||||
proto_iterate_named(struct symbol *sym, struct protocol *proto, struct proto *old)
|
||||
{
|
||||
if (sym)
|
||||
{
|
||||
/* Just the first pass */
|
||||
if (old)
|
||||
{
|
||||
cli_msg(0, "");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sym->class != SYM_PROTO)
|
||||
cf_error("%s: Not a protocol", sym->name);
|
||||
|
||||
struct proto *p = sym->proto->proto;
|
||||
if (!p || (p->proto != proto))
|
||||
cf_error("%s: Not a %s protocol", sym->name, proto->name);
|
||||
|
||||
return p;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (struct proto *p = !old ? HEAD(proto_list) : NODE_NEXT(old);
|
||||
NODE_VALID(p);
|
||||
p = NODE_NEXT(p))
|
||||
{
|
||||
if ((p->proto == proto) && (p->proto_state != PS_DOWN))
|
||||
{
|
||||
cli_separator(this_cli);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not found anything during first pass */
|
||||
if (!old)
|
||||
cf_error("There is no %s protocol running", proto->name);
|
||||
|
||||
/* No more items */
|
||||
cli_msg(0, "");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ struct protocol {
|
||||
void (*cleanup)(struct proto *); /* Called after shutdown when protocol became hungry/down */
|
||||
void (*get_status)(struct proto *, byte *buf); /* Get instance status (for `show protocols' command) */
|
||||
void (*get_route_info)(struct rte *, byte *buf); /* Get route information (for `show route' command) */
|
||||
int (*get_attr)(struct eattr *, byte *buf, int buflen); /* ASCIIfy dynamic attribute (returns GA_*) */
|
||||
int (*get_attr)(const struct eattr *, byte *buf, int buflen); /* ASCIIfy dynamic attribute (returns GA_*) */
|
||||
void (*show_proto_info)(struct proto *); /* Show protocol info (for `show protocols all' command) */
|
||||
void (*copy_config)(struct proto_config *, struct proto_config *); /* Copy config from given protocol instance */
|
||||
};
|
||||
@ -115,8 +115,8 @@ struct proto_config {
|
||||
struct protocol *protocol; /* Protocol */
|
||||
struct proto *proto; /* Instance we've created */
|
||||
struct proto_config *parent; /* Parent proto_config for dynamic protocols */
|
||||
char *name;
|
||||
char *dsc;
|
||||
const char *name;
|
||||
const char *dsc;
|
||||
int class; /* SYM_PROTO or SYM_TEMPLATE */
|
||||
u8 net_type; /* Protocol network type (NET_*), 0 for undefined */
|
||||
u8 disabled; /* Protocol enabled/disabled by default */
|
||||
@ -171,7 +171,7 @@ struct proto {
|
||||
struct rte_src *main_source; /* Primary route source */
|
||||
struct iface *vrf; /* Related VRF instance, NULL if global */
|
||||
|
||||
char *name; /* Name of this instance (== cf->name) */
|
||||
const char *name; /* Name of this instance (== cf->name) */
|
||||
u32 debug; /* Debugging flags */
|
||||
u32 mrtdump; /* MRTDump flags */
|
||||
uint active_channels; /* Number of active channels */
|
||||
@ -245,7 +245,7 @@ struct proto {
|
||||
};
|
||||
|
||||
struct proto_spec {
|
||||
void *ptr;
|
||||
const void *ptr;
|
||||
int patt;
|
||||
};
|
||||
|
||||
@ -281,6 +281,7 @@ void channel_graceful_restart_unlock(struct channel *c);
|
||||
|
||||
void channel_show_limit(struct channel_limit *l, const char *dsc);
|
||||
void channel_show_info(struct channel *c);
|
||||
void channel_cmd_debug(struct channel *c, uint mask);
|
||||
|
||||
void proto_cmd_show(struct proto *, uintptr_t, int);
|
||||
void proto_cmd_disable(struct proto *, uintptr_t, int);
|
||||
@ -292,6 +293,10 @@ void proto_cmd_mrtdump(struct proto *, uintptr_t, int);
|
||||
|
||||
void proto_apply_cmd(struct proto_spec ps, void (* cmd)(struct proto *, uintptr_t, int), int restricted, uintptr_t arg);
|
||||
struct proto *proto_get_named(struct symbol *, struct protocol *);
|
||||
struct proto *proto_iterate_named(struct symbol *sym, struct protocol *proto, struct proto *old);
|
||||
|
||||
#define PROTO_WALK_CMD(sym,pr,p) for(struct proto *p = NULL; p = proto_iterate_named(sym, pr, p); )
|
||||
|
||||
|
||||
#define CMD_RELOAD 0
|
||||
#define CMD_RELOAD_IN 1
|
||||
@ -451,7 +456,7 @@ struct channel_class {
|
||||
uint config_size; /* Size of channel config data structure */
|
||||
|
||||
void (*init)(struct channel *, struct channel_config *); /* Create new instance */
|
||||
int (*reconfigure)(struct channel *, struct channel_config *); /* Try to reconfigure instance, returns success */
|
||||
int (*reconfigure)(struct channel *, struct channel_config *, int *import_changed, int *export_changed); /* Try to reconfigure instance, returns success */
|
||||
int (*start)(struct channel *); /* Start the instance */
|
||||
void (*shutdown)(struct channel *); /* Stop the instance */
|
||||
void (*cleanup)(struct channel *); /* Channel finished flush */
|
||||
@ -492,8 +497,10 @@ struct channel_config {
|
||||
u8 net_type; /* Routing table network type (NET_*), 0 for undefined */
|
||||
u8 ra_mode; /* Mode of received route advertisements (RA_*) */
|
||||
u16 preference; /* Default route preference */
|
||||
u32 debug; /* Debugging flags (D_*) */
|
||||
u8 merge_limit; /* Maximal number of nexthops for RA_MERGED */
|
||||
u8 in_keep_filtered; /* Routes rejected in import filter are kept */
|
||||
u8 rpki_reload; /* RPKI changes trigger channel reload */
|
||||
};
|
||||
|
||||
struct channel {
|
||||
@ -507,6 +514,7 @@ struct channel {
|
||||
struct rtable *table;
|
||||
const struct filter *in_filter; /* Input filter */
|
||||
const struct filter *out_filter; /* Output filter */
|
||||
struct bmap export_map; /* Keeps track which routes passed export filter */
|
||||
struct channel_limit rx_limit; /* Receive limit (for in_keep_filtered) */
|
||||
struct channel_limit in_limit; /* Input limit */
|
||||
struct channel_limit out_limit; /* Output limit */
|
||||
@ -514,10 +522,12 @@ struct channel {
|
||||
struct event *feed_event; /* Event responsible for feeding */
|
||||
struct fib_iterator feed_fit; /* Routing table iterator used during feeding */
|
||||
struct proto_stats stats; /* Per-channel protocol statistics */
|
||||
u32 refeed_count; /* Number of routes exported during refeed regardless of out_limit */
|
||||
|
||||
u8 net_type; /* Routing table network type (NET_*), 0 for undefined */
|
||||
u8 ra_mode; /* Mode of received route advertisements (RA_*) */
|
||||
u16 preference; /* Default route preference */
|
||||
u32 debug; /* Debugging flags (D_*) */
|
||||
u8 merge_limit; /* Maximal number of nexthops for RA_MERGED */
|
||||
u8 in_keep_filtered; /* Routes rejected in import filter are kept */
|
||||
u8 disabled;
|
||||
@ -533,12 +543,20 @@ struct channel {
|
||||
u8 gr_wait; /* Route export to channel is postponed until graceful restart */
|
||||
|
||||
btime last_state_change; /* Time of last state transition */
|
||||
btime last_tx_filter_change;
|
||||
|
||||
struct rtable *in_table; /* Internal table for received routes */
|
||||
struct event *reload_event; /* Event responsible for reloading from in_table */
|
||||
struct fib_iterator reload_fit; /* Iterator in in_table used during reloading */
|
||||
struct fib_iterator reload_fit; /* FIB iterator in in_table used during reloading */
|
||||
struct rte *reload_next_rte; /* Route iterator in in_table used during reloading */
|
||||
u8 reload_active; /* Iterator reload_fit is linked */
|
||||
|
||||
u8 reload_pending; /* Reloading and another reload is scheduled */
|
||||
u8 refeed_pending; /* Refeeding and another refeed is scheduled */
|
||||
u8 rpki_reload; /* RPKI changes trigger channel reload */
|
||||
|
||||
struct rtable *out_table; /* Internal table for exported routes */
|
||||
|
||||
list roa_subscriptions; /* List of active ROA table subscriptions based on filters roa_check() */
|
||||
};
|
||||
|
||||
|
||||
@ -607,6 +625,7 @@ int proto_configure_channel(struct proto *p, struct channel **c, struct channel_
|
||||
|
||||
void channel_set_state(struct channel *c, uint state);
|
||||
void channel_setup_in_table(struct channel *c);
|
||||
void channel_setup_out_table(struct channel *c);
|
||||
void channel_schedule_reload(struct channel *c);
|
||||
|
||||
static inline void channel_init(struct channel *c) { channel_set_state(c, CS_START); }
|
||||
|
39
nest/route.h
39
nest/route.h
@ -10,6 +10,7 @@
|
||||
#define _BIRD_ROUTE_H_
|
||||
|
||||
#include "lib/lists.h"
|
||||
#include "lib/bitmap.h"
|
||||
#include "lib/resource.h"
|
||||
#include "lib/net.h"
|
||||
|
||||
@ -18,6 +19,7 @@ struct protocol;
|
||||
struct proto;
|
||||
struct rte_src;
|
||||
struct symbol;
|
||||
struct timer;
|
||||
struct filter;
|
||||
struct cli;
|
||||
|
||||
@ -36,7 +38,6 @@ struct cli;
|
||||
struct fib_node {
|
||||
struct fib_node *next; /* Next in hash chain */
|
||||
struct fib_iterator *readers; /* List of readers of this node */
|
||||
byte flags; /* User-defined, will be removed */
|
||||
net_addr addr[0];
|
||||
};
|
||||
|
||||
@ -84,6 +85,8 @@ void fit_init(struct fib_iterator *, struct fib *); /* Internal functions, don't
|
||||
struct fib_node *fit_get(struct fib *, struct fib_iterator *);
|
||||
void fit_put(struct fib_iterator *, struct fib_node *);
|
||||
void fit_put_next(struct fib *f, struct fib_iterator *i, struct fib_node *n, uint hpos);
|
||||
void fit_put_end(struct fib_iterator *i);
|
||||
void fit_copy(struct fib *f, struct fib_iterator *dst, struct fib_iterator *src);
|
||||
|
||||
|
||||
#define FIB_WALK(fib, type, z) do { \
|
||||
@ -118,8 +121,12 @@ void fit_put_next(struct fib *f, struct fib_iterator *i, struct fib_node *n, uin
|
||||
|
||||
#define FIB_ITERATE_PUT_NEXT(it, fib) fit_put_next(fib, it, fn_, hpos_)
|
||||
|
||||
#define FIB_ITERATE_PUT_END(it) fit_put_end(it)
|
||||
|
||||
#define FIB_ITERATE_UNLINK(it, fib) fit_get(fib, it)
|
||||
|
||||
#define FIB_ITERATE_COPY(dst, src, fib) fit_copy(fib, dst, src)
|
||||
|
||||
|
||||
/*
|
||||
* Master Routing Tables. Generally speaking, each of them contains a FIB
|
||||
@ -141,6 +148,8 @@ struct rtable_config {
|
||||
int gc_max_ops; /* Maximum number of operations before GC is run */
|
||||
int gc_min_time; /* Minimum time between two consecutive GC runs */
|
||||
byte sorted; /* Routes of network are sorted according to rte_better() */
|
||||
btime min_settle_time; /* Minimum settle time for notifications */
|
||||
btime max_settle_time; /* Maximum settle time for notifications */
|
||||
};
|
||||
|
||||
typedef struct rtable {
|
||||
@ -152,6 +161,7 @@ typedef struct rtable {
|
||||
int pipe_busy; /* Pipe loop detection */
|
||||
int use_count; /* Number of protocols using this table */
|
||||
u32 rt_count; /* Number of routes in the table */
|
||||
struct hmap id_map;
|
||||
struct hostcache *hostcache;
|
||||
struct rtable_config *config; /* Configuration of this table */
|
||||
struct config *deleted; /* Table doesn't exist in current configuration,
|
||||
@ -159,6 +169,8 @@ typedef struct rtable {
|
||||
* obstacle from this routing table.
|
||||
*/
|
||||
struct event *rt_event; /* Routing table event */
|
||||
btime last_rt_change; /* Last time when route changed */
|
||||
btime base_settle_time; /* Start time of rtable settling interval */
|
||||
btime gc_time; /* Time of last GC */
|
||||
int gc_counter; /* Number of operations since last GC */
|
||||
byte prune_state; /* Table prune state, 1 -> scheduled, 2-> running */
|
||||
@ -166,8 +178,18 @@ typedef struct rtable {
|
||||
byte nhu_state; /* Next Hop Update state */
|
||||
struct fib_iterator prune_fit; /* Rtable prune FIB iterator */
|
||||
struct fib_iterator nhu_fit; /* Next Hop Update FIB iterator */
|
||||
|
||||
list subscribers; /* Subscribers for notifications */
|
||||
struct timer *settle_timer; /* Settle time for notifications */
|
||||
} rtable;
|
||||
|
||||
struct rt_subscription {
|
||||
node n;
|
||||
rtable *tab;
|
||||
void (*hook)(struct rt_subscription *b);
|
||||
void *data;
|
||||
};
|
||||
|
||||
#define NHU_CLEAN 0
|
||||
#define NHU_SCHEDULED 1
|
||||
#define NHU_RUNNING 2
|
||||
@ -210,6 +232,7 @@ typedef struct rte {
|
||||
net *net; /* Network this RTE belongs to */
|
||||
struct channel *sender; /* Channel used to send the route to the routing table */
|
||||
struct rta *attrs; /* Attributes of this route */
|
||||
u32 id; /* Table specific route id */
|
||||
byte flags; /* Flags (REF_...) */
|
||||
byte pflags; /* Protocol-specific flags */
|
||||
word pref; /* Route preference */
|
||||
@ -286,6 +309,8 @@ void rt_preconfig(struct config *);
|
||||
void rt_commit(struct config *new, struct config *old);
|
||||
void rt_lock_table(rtable *);
|
||||
void rt_unlock_table(rtable *);
|
||||
void rt_subscribe(rtable *tab, struct rt_subscription *s);
|
||||
void rt_unsubscribe(struct rt_subscription *s);
|
||||
void rt_setup(pool *, rtable *, struct rtable_config *);
|
||||
static inline net *net_find(rtable *tab, const net_addr *addr) { return (net *) fib_find(&tab->fib, addr); }
|
||||
static inline net *net_find_valid(rtable *tab, const net_addr *addr)
|
||||
@ -320,6 +345,7 @@ int rte_update_in(struct channel *c, const net_addr *n, rte *new, struct rte_src
|
||||
int rt_reload_channel(struct channel *c);
|
||||
void rt_reload_channel_abort(struct channel *c);
|
||||
void rt_prune_sync(rtable *t, int all);
|
||||
int rte_update_out(struct channel *c, const net_addr *n, rte *new, rte *old0, int refeed);
|
||||
struct rtable_config *rt_new_table(struct symbol *s, uint addr_type);
|
||||
|
||||
|
||||
@ -344,6 +370,7 @@ struct rt_show_data {
|
||||
struct proto *export_protocol;
|
||||
struct channel *export_channel;
|
||||
struct config *running_on_config;
|
||||
struct krt_proto *kernel;
|
||||
int export_mode, primary_only, filtered, stats, show_for;
|
||||
|
||||
int table_open; /* Iteration (fit) is open */
|
||||
@ -368,6 +395,7 @@ struct rt_show_data_rtable * rt_show_add_table(struct rt_show_data *d, rtable *t
|
||||
#define RSEM_PREEXPORT 1 /* Routes ready for export, before filtering */
|
||||
#define RSEM_EXPORT 2 /* Routes accepted by export filter */
|
||||
#define RSEM_NOEXPORT 3 /* Routes rejected by export filter */
|
||||
#define RSEM_EXPORTED 4 /* Routes marked in export map */
|
||||
|
||||
/*
|
||||
* Route Attributes
|
||||
@ -447,17 +475,13 @@ typedef struct rta {
|
||||
#define RTD_PROHIBIT 4 /* Administratively prohibited */
|
||||
#define RTD_MAX 5
|
||||
|
||||
/* Flags for net->n.flags, used by kernel syncer */
|
||||
#define KRF_INSTALLED 0x80 /* This route should be installed in the kernel */
|
||||
#define KRF_SYNC_ERROR 0x40 /* Error during kernel table synchronization */
|
||||
|
||||
#define RTAF_CACHED 1 /* This is a cached rta */
|
||||
|
||||
#define IGP_METRIC_UNKNOWN 0x80000000 /* Default igp_metric used when no other
|
||||
protocol-specific metric is availabe */
|
||||
|
||||
|
||||
const char * rta_dest_names[RTD_MAX];
|
||||
extern const char * rta_dest_names[RTD_MAX];
|
||||
|
||||
static inline const char *rta_dest_name(uint n)
|
||||
{ return (n < RTD_MAX) ? rta_dest_names[n] : "???"; }
|
||||
@ -570,7 +594,7 @@ void ea_merge(ea_list *from, ea_list *to); /* Merge sub-lists to allocated buffe
|
||||
int ea_same(ea_list *x, ea_list *y); /* Test whether two ea_lists are identical */
|
||||
uint ea_hash(ea_list *e); /* Calculate 16-bit hash value */
|
||||
ea_list *ea_append(ea_list *to, ea_list *what);
|
||||
void ea_format_bitfield(struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max);
|
||||
void ea_format_bitfield(const struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max);
|
||||
|
||||
#define ea_normalize(ea) do { \
|
||||
if (ea->next) { \
|
||||
@ -651,6 +675,7 @@ void rta_dump(rta *);
|
||||
void rta_dump_all(void);
|
||||
void rta_show(struct cli *, rta *);
|
||||
|
||||
u32 rt_get_igp_metric(rte *rt);
|
||||
struct hostentry * rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep);
|
||||
void rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls);
|
||||
|
||||
|
@ -135,7 +135,7 @@ rt_get_source(struct proto *p, u32 id)
|
||||
if (src)
|
||||
return src;
|
||||
|
||||
src = sl_alloc(rte_src_slab);
|
||||
src = sl_allocz(rte_src_slab);
|
||||
src->proto = p;
|
||||
src->private_id = id;
|
||||
src->global_id = idm_alloc(&src_ids);
|
||||
@ -202,7 +202,7 @@ nexthop__same(struct nexthop *x, struct nexthop *y)
|
||||
}
|
||||
|
||||
static int
|
||||
nexthop_compare_node(const struct nexthop *x, const struct nexthop *y)
|
||||
nexthop_compare_node(const struct nexthop *x, const struct nexthop *y)
|
||||
{
|
||||
int r;
|
||||
|
||||
@ -278,18 +278,22 @@ nexthop_merge(struct nexthop *x, struct nexthop *y, int rx, int ry, int max, lin
|
||||
while ((x || y) && max--)
|
||||
{
|
||||
int cmp = nexthop_compare_node(x, y);
|
||||
|
||||
if (cmp < 0)
|
||||
{
|
||||
ASSUME(x);
|
||||
*n = rx ? x : nexthop_copy_node(x, lp);
|
||||
x = x->next;
|
||||
}
|
||||
else if (cmp > 0)
|
||||
{
|
||||
ASSUME(y);
|
||||
*n = ry ? y : nexthop_copy_node(y, lp);
|
||||
y = y->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSUME(x && y);
|
||||
*n = rx ? x : (ry ? y : nexthop_copy_node(x, lp));
|
||||
x = x->next;
|
||||
y = y->next;
|
||||
@ -362,7 +366,7 @@ nexthop_copy(struct nexthop *o)
|
||||
|
||||
for (; o; o = o->next)
|
||||
{
|
||||
struct nexthop *n = sl_alloc(nexthop_slab(o));
|
||||
struct nexthop *n = sl_allocz(nexthop_slab(o));
|
||||
n->gw = o->gw;
|
||||
n->iface = o->iface;
|
||||
n->next = NULL;
|
||||
@ -786,7 +790,7 @@ ea_free(ea_list *o)
|
||||
}
|
||||
|
||||
static int
|
||||
get_generic_attr(eattr *a, byte **buf, int buflen UNUSED)
|
||||
get_generic_attr(const eattr *a, byte **buf, int buflen UNUSED)
|
||||
{
|
||||
if (a->id == EA_GEN_IGP_METRIC)
|
||||
{
|
||||
@ -798,7 +802,7 @@ get_generic_attr(eattr *a, byte **buf, int buflen UNUSED)
|
||||
}
|
||||
|
||||
void
|
||||
ea_format_bitfield(struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max)
|
||||
ea_format_bitfield(const struct eattr *a, byte *buf, int bufsize, const char **names, int min, int max)
|
||||
{
|
||||
byte *bound = buf + bufsize - 32;
|
||||
u32 data = a->u.data;
|
||||
@ -894,7 +898,7 @@ ea_show_lc_set(struct cli *c, const struct adata *ad, byte *pos, byte *buf, byte
|
||||
* get_attr() hook, it's consulted first.
|
||||
*/
|
||||
void
|
||||
ea_show(struct cli *c, eattr *e)
|
||||
ea_show(struct cli *c, const eattr *e)
|
||||
{
|
||||
struct protocol *p;
|
||||
int status = GA_UNKNOWN;
|
||||
|
@ -327,7 +327,6 @@ fib_get(struct fib *f, const net_addr *a)
|
||||
|
||||
struct fib_node *e = fib_user_to_node(f, b);
|
||||
e->readers = NULL;
|
||||
e->flags = 0;
|
||||
fib_insert(f, a, e);
|
||||
|
||||
memset(b, 0, f->node_offset);
|
||||
@ -585,6 +584,40 @@ found:
|
||||
fit_put(i, n);
|
||||
}
|
||||
|
||||
void
|
||||
fit_put_end(struct fib_iterator *i)
|
||||
{
|
||||
i->prev = i->next = NULL;
|
||||
i->node = NULL;
|
||||
i->hash = ~0 - 1;
|
||||
}
|
||||
|
||||
void
|
||||
fit_copy(struct fib *f, struct fib_iterator *dst, struct fib_iterator *src)
|
||||
{
|
||||
struct fib_iterator *nxt = src->next;
|
||||
|
||||
fit_get(f, dst);
|
||||
|
||||
if (!src->prev)
|
||||
{
|
||||
/* We are at the end */
|
||||
fit_put_end(dst);
|
||||
return;
|
||||
}
|
||||
|
||||
src->next = dst;
|
||||
dst->prev = src;
|
||||
|
||||
dst->next = nxt;
|
||||
if (nxt)
|
||||
nxt->prev = dst;
|
||||
|
||||
dst->node = src->node;
|
||||
dst->hash = src->hash;
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUGGING
|
||||
|
||||
/**
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "nest/cli.h"
|
||||
#include "nest/iface.h"
|
||||
#include "filter/filter.h"
|
||||
#include "sysdep/unix/krt.h"
|
||||
|
||||
static void
|
||||
rt_show_table(struct cli *c, struct rt_show_data *d)
|
||||
@ -28,14 +29,20 @@ rt_show_table(struct cli *c, struct rt_show_data *d)
|
||||
d->last_table = d->tab;
|
||||
}
|
||||
|
||||
static inline struct krt_proto *
|
||||
rt_show_get_kernel(struct rt_show_data *d)
|
||||
{
|
||||
struct proto_config *krt = d->tab->table->config->krt_attached;
|
||||
return krt ? (struct krt_proto *) krt->proto : NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d)
|
||||
rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d, int primary)
|
||||
{
|
||||
byte from[IPA_MAX_TEXT_LENGTH+8];
|
||||
byte tm[TM_DATETIME_BUFFER_SIZE], info[256];
|
||||
rta *a = e->attrs;
|
||||
int primary = (e->net->routes == e);
|
||||
int sync_error = (e->net->n.flags & KRF_SYNC_ERROR);
|
||||
int sync_error = d->kernel ? krt_get_sync_error(d->kernel, e) : 0;
|
||||
void (*get_route_info)(struct rte *, byte *buf);
|
||||
struct nexthop *nh;
|
||||
|
||||
@ -97,6 +104,11 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
|
||||
rte *e, *ee;
|
||||
byte ia[NET_MAX_TEXT_LENGTH+1];
|
||||
struct channel *ec = d->tab->export_channel;
|
||||
|
||||
/* The Clang static analyzer complains that ec may be NULL.
|
||||
* It should be ensured to be not NULL by rt_show_prepare_tables() */
|
||||
ASSUME(!d->export_mode || ec);
|
||||
|
||||
int first = 1;
|
||||
int pass = 0;
|
||||
|
||||
@ -121,9 +133,17 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
|
||||
if (ec && (ec->export_state == ES_DOWN))
|
||||
goto skip;
|
||||
|
||||
/* Special case for merged export */
|
||||
if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_MERGED))
|
||||
if (d->export_mode == RSEM_EXPORTED)
|
||||
{
|
||||
if (!bmap_test(&ec->export_map, ee->id))
|
||||
goto skip;
|
||||
|
||||
// if (ec->ra_mode != RA_ANY)
|
||||
// pass = 1;
|
||||
}
|
||||
else if ((d->export_mode == RSEM_EXPORT) && (ec->ra_mode == RA_MERGED))
|
||||
{
|
||||
/* Special case for merged export */
|
||||
rte *rt_free;
|
||||
e = rt_export_merged(ec, n, &rt_free, c->show_pool, 1);
|
||||
pass = 1;
|
||||
@ -167,7 +187,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
|
||||
goto skip;
|
||||
|
||||
if (d->stats < 2)
|
||||
rt_show_rte(c, ia, e, d);
|
||||
rt_show_rte(c, ia, e, d, (e->net->routes == ee));
|
||||
|
||||
d->show_counter++;
|
||||
ia[0] = 0;
|
||||
@ -223,6 +243,7 @@ rt_show_cont(struct cli *c)
|
||||
FIB_ITERATE_INIT(&d->fit, &d->tab->table->fib);
|
||||
d->table_open = 1;
|
||||
d->table_counter++;
|
||||
d->kernel = rt_show_get_kernel(d);
|
||||
|
||||
d->show_counter_last = d->show_counter;
|
||||
d->rt_counter_last = d->rt_counter;
|
||||
@ -253,6 +274,7 @@ rt_show_cont(struct cli *c)
|
||||
d->net_counter - d->net_counter_last, d->tab->table->name);
|
||||
}
|
||||
|
||||
d->kernel = NULL;
|
||||
d->table_open = 0;
|
||||
d->tab = NODE_NEXT(d->tab);
|
||||
|
||||
@ -396,6 +418,7 @@ rt_show(struct rt_show_data *d)
|
||||
WALK_LIST(tab, d->tables)
|
||||
{
|
||||
d->tab = tab;
|
||||
d->kernel = rt_show_get_kernel(d);
|
||||
|
||||
if (d->show_for)
|
||||
n = net_route(tab->table, d->addr);
|
||||
|
848
nest/rt-table.c
848
nest/rt-table.c
File diff suppressed because it is too large
Load Diff
@ -113,7 +113,7 @@ babel_get_source(struct babel_proto *p, struct babel_entry *e, u64 router_id)
|
||||
if (s)
|
||||
return s;
|
||||
|
||||
s = sl_alloc(p->source_slab);
|
||||
s = sl_allocz(p->source_slab);
|
||||
s->router_id = router_id;
|
||||
s->expires = current_time() + BABEL_GARBAGE_INTERVAL;
|
||||
s->seqno = 0;
|
||||
@ -159,8 +159,7 @@ babel_get_route(struct babel_proto *p, struct babel_entry *e, struct babel_neigh
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = sl_alloc(p->route_slab);
|
||||
memset(r, 0, sizeof(*r));
|
||||
r = sl_allocz(p->route_slab);
|
||||
|
||||
r->e = e;
|
||||
r->neigh = nbr;
|
||||
@ -323,7 +322,7 @@ babel_add_seqno_request(struct babel_proto *p, struct babel_entry *e,
|
||||
}
|
||||
|
||||
/* No entries found */
|
||||
sr = sl_alloc(p->seqno_slab);
|
||||
sr = sl_allocz(p->seqno_slab);
|
||||
|
||||
found:
|
||||
sr->router_id = router_id;
|
||||
@ -640,6 +639,14 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
|
||||
.nh.iface = r->neigh->ifa->iface,
|
||||
};
|
||||
|
||||
/*
|
||||
* If we cannot find a reachable neighbour, set the entry to be onlink. This
|
||||
* makes it possible to, e.g., assign /32 addresses on a mesh interface and
|
||||
* have routing work.
|
||||
*/
|
||||
if (!neigh_find(&p->p, r->next_hop, r->neigh->ifa->iface, 0))
|
||||
a0.nh.flags = RNF_ONLINK;
|
||||
|
||||
rta *a = rta_lookup(&a0);
|
||||
rte *rte = rte_get_temp(a);
|
||||
rte->u.babel.seqno = r->seqno;
|
||||
@ -1852,7 +1859,7 @@ babel_get_route_info(rte *rte, byte *buf)
|
||||
}
|
||||
|
||||
static int
|
||||
babel_get_attr(eattr *a, byte *buf, int buflen UNUSED)
|
||||
babel_get_attr(const eattr *a, byte *buf, int buflen UNUSED)
|
||||
{
|
||||
switch (a->id)
|
||||
{
|
||||
@ -1874,7 +1881,7 @@ babel_get_attr(eattr *a, byte *buf, int buflen UNUSED)
|
||||
}
|
||||
|
||||
void
|
||||
babel_show_interfaces(struct proto *P, char *iff)
|
||||
babel_show_interfaces(struct proto *P, const char *iff)
|
||||
{
|
||||
struct babel_proto *p = (void *) P;
|
||||
struct babel_iface *ifa = NULL;
|
||||
@ -1883,7 +1890,6 @@ babel_show_interfaces(struct proto *P, char *iff)
|
||||
if (p->p.proto_state != PS_UP)
|
||||
{
|
||||
cli_msg(-1023, "%s: is not up", p->p.name);
|
||||
cli_msg(0, "");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1907,12 +1913,10 @@ babel_show_interfaces(struct proto *P, char *iff)
|
||||
ifa->cf->rxcost, nbrs, MAX(timer, 0),
|
||||
ifa->next_hop_ip4, ifa->next_hop_ip6);
|
||||
}
|
||||
|
||||
cli_msg(0, "");
|
||||
}
|
||||
|
||||
void
|
||||
babel_show_neighbors(struct proto *P, char *iff)
|
||||
babel_show_neighbors(struct proto *P, const char *iff)
|
||||
{
|
||||
struct babel_proto *p = (void *) P;
|
||||
struct babel_iface *ifa = NULL;
|
||||
@ -1922,7 +1926,6 @@ babel_show_neighbors(struct proto *P, char *iff)
|
||||
if (p->p.proto_state != PS_UP)
|
||||
{
|
||||
cli_msg(-1024, "%s: is not up", p->p.name);
|
||||
cli_msg(0, "");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1947,8 +1950,6 @@ babel_show_neighbors(struct proto *P, char *iff)
|
||||
n->addr, ifa->iface->name, n->cost, rts, hellos, MAX(timer, 0));
|
||||
}
|
||||
}
|
||||
|
||||
cli_msg(0, "");
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1990,7 +1991,6 @@ babel_show_entries(struct proto *P)
|
||||
if (p->p.proto_state != PS_UP)
|
||||
{
|
||||
cli_msg(-1025, "%s: is not up", p->p.name);
|
||||
cli_msg(0, "");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2000,8 +2000,6 @@ babel_show_entries(struct proto *P)
|
||||
|
||||
babel_show_entries_(p, &p->ip4_rtable);
|
||||
babel_show_entries_(p, &p->ip6_rtable);
|
||||
|
||||
cli_msg(0, "");
|
||||
}
|
||||
|
||||
static void
|
||||
@ -2033,7 +2031,6 @@ babel_show_routes(struct proto *P)
|
||||
if (p->p.proto_state != PS_UP)
|
||||
{
|
||||
cli_msg(-1025, "%s: is not up", p->p.name);
|
||||
cli_msg(0, "");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2043,8 +2040,6 @@ babel_show_routes(struct proto *P)
|
||||
|
||||
babel_show_routes_(p, &p->ip4_rtable);
|
||||
babel_show_routes_(p, &p->ip6_rtable);
|
||||
|
||||
cli_msg(0, "");
|
||||
}
|
||||
|
||||
|
||||
|
@ -368,8 +368,8 @@ void babel_handle_update(union babel_msg *msg, struct babel_iface *ifa);
|
||||
void babel_handle_route_request(union babel_msg *msg, struct babel_iface *ifa);
|
||||
void babel_handle_seqno_request(union babel_msg *msg, struct babel_iface *ifa);
|
||||
|
||||
void babel_show_interfaces(struct proto *P, char *iff);
|
||||
void babel_show_neighbors(struct proto *P, char *iff);
|
||||
void babel_show_interfaces(struct proto *P, const char *iff);
|
||||
void babel_show_neighbors(struct proto *P, const char *iff);
|
||||
void babel_show_entries(struct proto *P);
|
||||
void babel_show_routes(struct proto *P);
|
||||
|
||||
|
@ -130,16 +130,16 @@ dynamic_attr: BABEL_METRIC { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_BAB
|
||||
CF_CLI_HELP(SHOW BABEL, ..., [[Show information about Babel protocol]]);
|
||||
|
||||
CF_CLI(SHOW BABEL INTERFACES, optproto opttext, [<name>] [\"<interface>\"], [[Show information about Babel interfaces]])
|
||||
{ babel_show_interfaces(proto_get_named($4, &proto_babel), $5); };
|
||||
{ PROTO_WALK_CMD($4, &proto_babel, p) babel_show_interfaces(p, $5); };
|
||||
|
||||
CF_CLI(SHOW BABEL NEIGHBORS, optproto opttext, [<name>] [\"<interface>\"], [[Show information about Babel neighbors]])
|
||||
{ babel_show_neighbors(proto_get_named($4, &proto_babel), $5); };
|
||||
{ PROTO_WALK_CMD($4, &proto_babel, p) babel_show_neighbors(p, $5); };
|
||||
|
||||
CF_CLI(SHOW BABEL ENTRIES, optproto opttext, [<name>], [[Show information about Babel prefix entries]])
|
||||
{ babel_show_entries(proto_get_named($4, &proto_babel)); };
|
||||
{ PROTO_WALK_CMD($4, &proto_babel, p) babel_show_entries(p); };
|
||||
|
||||
CF_CLI(SHOW BABEL ROUTES, optproto opttext, [<name>], [[Show information about Babel route entries]])
|
||||
{ babel_show_routes(proto_get_named($4, &proto_babel)); };
|
||||
{ PROTO_WALK_CMD($4, &proto_babel, p) babel_show_routes(p); };
|
||||
|
||||
CF_CODE
|
||||
|
||||
|
@ -1144,7 +1144,6 @@ babel_read_tlv(struct babel_tlv *hdr,
|
||||
return PARSE_ERROR;
|
||||
|
||||
state->current_tlv_endpos = tlv_data[hdr->type].min_length;
|
||||
memset(msg, 0, sizeof(*msg));
|
||||
|
||||
int res = tlv_data[hdr->type].read_tlv(hdr, msg, state);
|
||||
if (res != PARSE_SUCCESS)
|
||||
@ -1278,7 +1277,7 @@ babel_send_unicast(union babel_msg *msg, struct babel_iface *ifa, ip_addr dest)
|
||||
struct babel_msg_node *msgn = sl_alloc(p->msg_slab);
|
||||
list queue;
|
||||
|
||||
msgn->msg = *msg;
|
||||
*msgn = (struct babel_msg_node) { .msg = *msg };
|
||||
init_list(&queue);
|
||||
add_tail(&queue, NODE msgn);
|
||||
babel_write_queue(ifa, &queue);
|
||||
@ -1305,7 +1304,8 @@ babel_enqueue(union babel_msg *msg, struct babel_iface *ifa)
|
||||
{
|
||||
struct babel_proto *p = ifa->proto;
|
||||
struct babel_msg_node *msgn = sl_alloc(p->msg_slab);
|
||||
msgn->msg = *msg;
|
||||
|
||||
*msgn = (struct babel_msg_node) { .msg = *msg };
|
||||
add_tail(&ifa->msg_queue, NODE msgn);
|
||||
babel_kick_queue(ifa);
|
||||
}
|
||||
@ -1386,7 +1386,7 @@ babel_process_packet(struct babel_pkt_header *pkt, int len,
|
||||
break;
|
||||
}
|
||||
|
||||
msg = sl_alloc(p->msg_slab);
|
||||
msg = sl_allocz(p->msg_slab);
|
||||
res = babel_read_tlv(tlv, &msg->msg, &state);
|
||||
if (res == PARSE_SUCCESS)
|
||||
{
|
||||
|
153
proto/bfd/bfd.c
153
proto/bfd/bfd.c
@ -27,13 +27,13 @@
|
||||
* related to the session and two timers (TX timer for periodic packets and hold
|
||||
* timer for session timeout). These sessions are allocated from @session_slab
|
||||
* and are accessible by two hash tables, @session_hash_id (by session ID) and
|
||||
* @session_hash_ip (by IP addresses of neighbors). Slab and both hashes are in
|
||||
* the main protocol structure &bfd_proto. The protocol logic related to BFD
|
||||
* sessions is implemented in internal functions bfd_session_*(), which are
|
||||
* expected to be called from the context of BFD thread, and external functions
|
||||
* bfd_add_session(), bfd_remove_session() and bfd_reconfigure_session(), which
|
||||
* form an interface to the BFD core for the rest and are expected to be called
|
||||
* from the context of main thread.
|
||||
* @session_hash_ip (by IP addresses of neighbors and associated interfaces).
|
||||
* Slab and both hashes are in the main protocol structure &bfd_proto. The
|
||||
* protocol logic related to BFD sessions is implemented in internal functions
|
||||
* bfd_session_*(), which are expected to be called from the context of BFD
|
||||
* thread, and external functions bfd_add_session(), bfd_remove_session() and
|
||||
* bfd_reconfigure_session(), which form an interface to the BFD core for the
|
||||
* rest and are expected to be called from the context of main thread.
|
||||
*
|
||||
* Each BFD session has an associated BFD interface, represented by structure
|
||||
* &bfd_iface. A BFD interface contains a socket used for TX (the one for RX is
|
||||
@ -108,10 +108,10 @@
|
||||
#define HASH_ID_EQ(a,b) a == b
|
||||
#define HASH_ID_FN(k) k
|
||||
|
||||
#define HASH_IP_KEY(n) n->addr
|
||||
#define HASH_IP_KEY(n) n->addr, n->ifindex
|
||||
#define HASH_IP_NEXT(n) n->next_ip
|
||||
#define HASH_IP_EQ(a,b) ipa_equal(a,b)
|
||||
#define HASH_IP_FN(k) ipa_hash(k)
|
||||
#define HASH_IP_EQ(a1,n1,a2,n2) ipa_equal(a1, a2) && n1 == n2
|
||||
#define HASH_IP_FN(a,n) ipa_hash(a) ^ u32_hash(n)
|
||||
|
||||
static list bfd_proto_list;
|
||||
static list bfd_wait_list;
|
||||
@ -128,6 +128,18 @@ static inline void bfd_notify_kick(struct bfd_proto *p);
|
||||
* BFD sessions
|
||||
*/
|
||||
|
||||
static inline struct bfd_session_config
|
||||
bfd_merge_options(const struct bfd_iface_config *cf, const struct bfd_options *opts)
|
||||
{
|
||||
return (struct bfd_session_config) {
|
||||
.min_rx_int = opts->min_rx_int ?: cf->min_rx_int,
|
||||
.min_tx_int = opts->min_tx_int ?: cf->min_tx_int,
|
||||
.idle_tx_int = opts->idle_tx_int ?: cf->idle_tx_int,
|
||||
.multiplier = opts->multiplier ?: cf->multiplier,
|
||||
.passive = opts->passive_set ? opts->passive : cf->passive
|
||||
};
|
||||
}
|
||||
|
||||
static void
|
||||
bfd_session_update_state(struct bfd_session *s, uint state, uint diag)
|
||||
{
|
||||
@ -152,10 +164,10 @@ bfd_session_update_state(struct bfd_session *s, uint state, uint diag)
|
||||
bfd_unlock_sessions(p);
|
||||
|
||||
if (state == BFD_STATE_UP)
|
||||
bfd_session_set_min_tx(s, s->ifa->cf->min_tx_int);
|
||||
bfd_session_set_min_tx(s, s->cf.min_tx_int);
|
||||
|
||||
if (old_state == BFD_STATE_UP)
|
||||
bfd_session_set_min_tx(s, s->ifa->cf->idle_tx_int);
|
||||
bfd_session_set_min_tx(s, s->cf.idle_tx_int);
|
||||
|
||||
if (notify)
|
||||
bfd_notify_kick(p);
|
||||
@ -373,9 +385,9 @@ bfd_find_session_by_id(struct bfd_proto *p, u32 id)
|
||||
}
|
||||
|
||||
struct bfd_session *
|
||||
bfd_find_session_by_addr(struct bfd_proto *p, ip_addr addr)
|
||||
bfd_find_session_by_addr(struct bfd_proto *p, ip_addr addr, uint ifindex)
|
||||
{
|
||||
return HASH_FIND(p->session_hash_ip, HASH_IP, addr);
|
||||
return HASH_FIND(p->session_hash_ip, HASH_IP, addr, ifindex);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -405,31 +417,31 @@ bfd_get_free_id(struct bfd_proto *p)
|
||||
}
|
||||
|
||||
static struct bfd_session *
|
||||
bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface *iface)
|
||||
bfd_add_session(struct bfd_proto *p, ip_addr addr, ip_addr local, struct iface *iface, struct bfd_options *opts)
|
||||
{
|
||||
birdloop_enter(p->loop);
|
||||
|
||||
struct bfd_iface *ifa = bfd_get_iface(p, local, iface);
|
||||
|
||||
struct bfd_session *s = sl_alloc(p->session_slab);
|
||||
bzero(s, sizeof(struct bfd_session));
|
||||
|
||||
struct bfd_session *s = sl_allocz(p->session_slab);
|
||||
s->addr = addr;
|
||||
s->ifa = ifa;
|
||||
s->ifindex = iface ? iface->index : 0;
|
||||
s->loc_id = bfd_get_free_id(p);
|
||||
|
||||
HASH_INSERT(p->session_hash_id, HASH_ID, s);
|
||||
HASH_INSERT(p->session_hash_ip, HASH_IP, s);
|
||||
|
||||
s->cf = bfd_merge_options(ifa->cf, opts);
|
||||
|
||||
/* Initialization of state variables - see RFC 5880 6.8.1 */
|
||||
s->loc_state = BFD_STATE_DOWN;
|
||||
s->rem_state = BFD_STATE_DOWN;
|
||||
s->des_min_tx_int = s->des_min_tx_new = ifa->cf->idle_tx_int;
|
||||
s->req_min_rx_int = s->req_min_rx_new = ifa->cf->min_rx_int;
|
||||
s->des_min_tx_int = s->des_min_tx_new = s->cf.idle_tx_int;
|
||||
s->req_min_rx_int = s->req_min_rx_new = s->cf.min_rx_int;
|
||||
s->rem_min_rx_int = 1;
|
||||
s->detect_mult = ifa->cf->multiplier;
|
||||
s->passive = ifa->cf->passive;
|
||||
s->detect_mult = s->cf.multiplier;
|
||||
s->passive = s->cf.passive;
|
||||
s->tx_csn = random_u32();
|
||||
|
||||
s->tx_timer = tm_new_init(p->tpool, bfd_tx_timer_hook, s, 0, 0);
|
||||
@ -506,15 +518,19 @@ bfd_remove_session(struct bfd_proto *p, struct bfd_session *s)
|
||||
static void
|
||||
bfd_reconfigure_session(struct bfd_proto *p, struct bfd_session *s)
|
||||
{
|
||||
if (EMPTY_LIST(s->request_list))
|
||||
return;
|
||||
|
||||
birdloop_enter(p->loop);
|
||||
|
||||
struct bfd_iface_config *cf = s->ifa->cf;
|
||||
struct bfd_request *req = SKIP_BACK(struct bfd_request, n, HEAD(s->request_list));
|
||||
s->cf = bfd_merge_options(s->ifa->cf, &req->opts);
|
||||
|
||||
u32 tx = (s->loc_state == BFD_STATE_UP) ? cf->min_tx_int : cf->idle_tx_int;
|
||||
u32 tx = (s->loc_state == BFD_STATE_UP) ? s->cf.min_tx_int : s->cf.idle_tx_int;
|
||||
bfd_session_set_min_tx(s, tx);
|
||||
bfd_session_set_min_rx(s, cf->min_rx_int);
|
||||
s->detect_mult = cf->multiplier;
|
||||
s->passive = cf->passive;
|
||||
bfd_session_set_min_rx(s, s->cf.min_rx_int);
|
||||
s->detect_mult = s->cf.multiplier;
|
||||
s->passive = s->cf.passive;
|
||||
|
||||
bfd_session_control_tx_timer(s, 0);
|
||||
|
||||
@ -590,12 +606,20 @@ bfd_free_iface(struct bfd_iface *ifa)
|
||||
static void
|
||||
bfd_reconfigure_iface(struct bfd_proto *p, struct bfd_iface *ifa, struct bfd_config *nc)
|
||||
{
|
||||
struct bfd_iface_config *nic = bfd_find_iface_config(nc, ifa->iface);
|
||||
ifa->changed = !!memcmp(nic, ifa->cf, sizeof(struct bfd_iface_config));
|
||||
struct bfd_iface_config *new = bfd_find_iface_config(nc, ifa->iface);
|
||||
struct bfd_iface_config *old = ifa->cf;
|
||||
|
||||
/* Check options that are handled in bfd_reconfigure_session() */
|
||||
ifa->changed =
|
||||
(new->min_rx_int != old->min_rx_int) ||
|
||||
(new->min_tx_int != old->min_tx_int) ||
|
||||
(new->idle_tx_int != old->idle_tx_int) ||
|
||||
(new->multiplier != old->multiplier) ||
|
||||
(new->passive != old->passive);
|
||||
|
||||
/* This should be probably changed to not access ifa->cf from the BFD thread */
|
||||
birdloop_enter(p->loop);
|
||||
ifa->cf = nic;
|
||||
ifa->cf = new;
|
||||
birdloop_leave(p->loop);
|
||||
}
|
||||
|
||||
@ -624,14 +648,23 @@ bfd_request_notify(struct bfd_request *req, u8 state, u8 diag)
|
||||
static int
|
||||
bfd_add_request(struct bfd_proto *p, struct bfd_request *req)
|
||||
{
|
||||
struct bfd_config *cf = (struct bfd_config *) (p->p.cf);
|
||||
|
||||
if (p->p.vrf_set && (p->p.vrf != req->vrf))
|
||||
return 0;
|
||||
|
||||
struct bfd_session *s = bfd_find_session_by_addr(p, req->addr);
|
||||
if (ipa_is_ip4(req->addr) ? !cf->accept_ipv4 : !cf->accept_ipv6)
|
||||
return 0;
|
||||
|
||||
if (req->iface ? !cf->accept_direct : !cf->accept_multihop)
|
||||
return 0;
|
||||
|
||||
uint ifindex = req->iface ? req->iface->index : 0;
|
||||
struct bfd_session *s = bfd_find_session_by_addr(p, req->addr, ifindex);
|
||||
u8 state, diag;
|
||||
|
||||
if (!s)
|
||||
s = bfd_add_session(p, req->addr, req->local, req->iface);
|
||||
s = bfd_add_session(p, req->addr, req->local, req->iface, &req->opts);
|
||||
|
||||
rem_node(&req->n);
|
||||
add_tail(&s->request_list, &req->n);
|
||||
@ -690,7 +723,8 @@ static struct resclass bfd_request_class;
|
||||
struct bfd_request *
|
||||
bfd_request_session(pool *p, ip_addr addr, ip_addr local,
|
||||
struct iface *iface, struct iface *vrf,
|
||||
void (*hook)(struct bfd_request *), void *data)
|
||||
void (*hook)(struct bfd_request *), void *data,
|
||||
const struct bfd_options *opts)
|
||||
{
|
||||
struct bfd_request *req = ralloc(p, &bfd_request_class);
|
||||
|
||||
@ -702,6 +736,9 @@ bfd_request_session(pool *p, ip_addr addr, ip_addr local,
|
||||
req->iface = iface;
|
||||
req->vrf = vrf;
|
||||
|
||||
if (opts)
|
||||
req->opts = *opts;
|
||||
|
||||
bfd_submit_request(req);
|
||||
|
||||
req->hook = hook;
|
||||
@ -710,6 +747,20 @@ bfd_request_session(pool *p, ip_addr addr, ip_addr local,
|
||||
return req;
|
||||
}
|
||||
|
||||
void
|
||||
bfd_update_request(struct bfd_request *req, const struct bfd_options *opts)
|
||||
{
|
||||
struct bfd_session *s = req->session;
|
||||
|
||||
if (!memcmp(opts, &req->opts, sizeof(const struct bfd_options)))
|
||||
return;
|
||||
|
||||
req->opts = *opts;
|
||||
|
||||
if (s)
|
||||
bfd_reconfigure_session(s->ifa->bfd, s);
|
||||
}
|
||||
|
||||
static void
|
||||
bfd_request_free(resource *r)
|
||||
{
|
||||
@ -759,7 +810,7 @@ bfd_neigh_notify(struct neighbor *nb)
|
||||
if ((nb->scope > 0) && !n->req)
|
||||
{
|
||||
ip_addr local = ipa_nonzero(n->local) ? n->local : nb->ifa->ip;
|
||||
n->req = bfd_request_session(p->p.pool, n->addr, local, nb->iface, p->p.vrf, NULL, NULL);
|
||||
n->req = bfd_request_session(p->p.pool, n->addr, local, nb->iface, p->p.vrf, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
if ((nb->scope <= 0) && n->req)
|
||||
@ -776,7 +827,7 @@ bfd_start_neighbor(struct bfd_proto *p, struct bfd_neighbor *n)
|
||||
|
||||
if (n->multihop)
|
||||
{
|
||||
n->req = bfd_request_session(p->p.pool, n->addr, n->local, NULL, p->p.vrf, NULL, NULL);
|
||||
n->req = bfd_request_session(p->p.pool, n->addr, n->local, NULL, p->p.vrf, NULL, NULL, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -837,10 +888,11 @@ bfd_reconfigure_neighbors(struct bfd_proto *p, struct bfd_config *new)
|
||||
|
||||
nn->req = on->req;
|
||||
nn->active = 1;
|
||||
return;
|
||||
goto next;
|
||||
}
|
||||
|
||||
bfd_stop_neighbor(p, on);
|
||||
next:;
|
||||
}
|
||||
|
||||
WALK_LIST(nn, new->neigh_list)
|
||||
@ -985,10 +1037,19 @@ bfd_start(struct proto *P)
|
||||
add_tail(&bfd_proto_list, &p->bfd_node);
|
||||
|
||||
birdloop_enter(p->loop);
|
||||
p->rx4_1 = bfd_open_rx_sk(p, 0, SK_IPV4);
|
||||
p->rx4_m = bfd_open_rx_sk(p, 1, SK_IPV4);
|
||||
p->rx6_1 = bfd_open_rx_sk(p, 0, SK_IPV6);
|
||||
p->rx6_m = bfd_open_rx_sk(p, 1, SK_IPV6);
|
||||
|
||||
if (cf->accept_ipv4 && cf->accept_direct)
|
||||
p->rx4_1 = bfd_open_rx_sk(p, 0, SK_IPV4);
|
||||
|
||||
if (cf->accept_ipv4 && cf->accept_multihop)
|
||||
p->rx4_m = bfd_open_rx_sk(p, 1, SK_IPV4);
|
||||
|
||||
if (cf->accept_ipv6 && cf->accept_direct)
|
||||
p->rx6_1 = bfd_open_rx_sk(p, 0, SK_IPV6);
|
||||
|
||||
if (cf->accept_ipv6 && cf->accept_multihop)
|
||||
p->rx6_m = bfd_open_rx_sk(p, 1, SK_IPV6);
|
||||
|
||||
birdloop_leave(p->loop);
|
||||
|
||||
bfd_take_requests(p);
|
||||
@ -1033,10 +1094,17 @@ static int
|
||||
bfd_reconfigure(struct proto *P, struct proto_config *c)
|
||||
{
|
||||
struct bfd_proto *p = (struct bfd_proto *) P;
|
||||
// struct bfd_config *old = (struct bfd_config *) (P->cf);
|
||||
struct bfd_config *old = (struct bfd_config *) (P->cf);
|
||||
struct bfd_config *new = (struct bfd_config *) c;
|
||||
struct bfd_iface *ifa;
|
||||
|
||||
/* TODO: Improve accept reconfiguration */
|
||||
if ((new->accept_ipv4 != old->accept_ipv4) ||
|
||||
(new->accept_ipv6 != old->accept_ipv6) ||
|
||||
(new->accept_direct != old->accept_direct) ||
|
||||
(new->accept_multihop != old->accept_multihop))
|
||||
return 0;
|
||||
|
||||
birdloop_mask_wakeups(p->loop);
|
||||
|
||||
WALK_LIST(ifa, p->iface_list)
|
||||
@ -1079,7 +1147,6 @@ bfd_show_sessions(struct proto *P)
|
||||
if (p->p.proto_state != PS_UP)
|
||||
{
|
||||
cli_msg(-1020, "%s: is not up", p->p.name);
|
||||
cli_msg(0, "");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1104,8 +1171,6 @@ bfd_show_sessions(struct proto *P)
|
||||
s->addr, ifname, bfd_state_names[state], tbuf, tx_int, timeout);
|
||||
}
|
||||
HASH_WALK_END;
|
||||
|
||||
cli_msg(0, "");
|
||||
}
|
||||
|
||||
|
||||
|
@ -43,6 +43,10 @@ struct bfd_config
|
||||
list patt_list; /* List of iface configs (struct bfd_iface_config) */
|
||||
list neigh_list; /* List of configured neighbors (struct bfd_neighbor) */
|
||||
struct bfd_iface_config *multihop; /* Multihop pseudoiface config */
|
||||
u8 accept_ipv4;
|
||||
u8 accept_ipv6;
|
||||
u8 accept_direct;
|
||||
u8 accept_multihop;
|
||||
};
|
||||
|
||||
struct bfd_iface_config
|
||||
@ -57,6 +61,15 @@ struct bfd_iface_config
|
||||
list *passwords; /* Passwords for authentication */
|
||||
};
|
||||
|
||||
struct bfd_session_config
|
||||
{
|
||||
u32 min_rx_int;
|
||||
u32 min_tx_int;
|
||||
u32 idle_tx_int;
|
||||
u8 multiplier;
|
||||
u8 passive;
|
||||
};
|
||||
|
||||
struct bfd_neighbor
|
||||
{
|
||||
node n;
|
||||
@ -126,6 +139,9 @@ struct bfd_session
|
||||
u8 rem_diag;
|
||||
u32 loc_id; /* Local session ID (local discriminator) */
|
||||
u32 rem_id; /* Remote session ID (remote discriminator) */
|
||||
|
||||
struct bfd_session_config cf; /* Static configuration parameters */
|
||||
|
||||
u32 des_min_tx_int; /* Desired min rx interval, local option */
|
||||
u32 des_min_tx_new; /* Used for des_min_tx_int change */
|
||||
u32 req_min_rx_int; /* Required min tx interval, local option */
|
||||
@ -137,6 +153,7 @@ struct bfd_session
|
||||
u8 detect_mult; /* Announced detect_mult, local option */
|
||||
u8 rem_detect_mult; /* Last received detect_mult */
|
||||
|
||||
uint ifindex; /* Iface index, for hashing in bfd.session_hash_ip */
|
||||
btime last_tx; /* Time of last sent periodic control packet */
|
||||
btime last_rx; /* Time of last received valid control packet */
|
||||
|
||||
@ -197,7 +214,7 @@ static inline void bfd_unlock_sessions(struct bfd_proto *p) { pthread_spin_unloc
|
||||
|
||||
/* bfd.c */
|
||||
struct bfd_session * bfd_find_session_by_id(struct bfd_proto *p, u32 id);
|
||||
struct bfd_session * bfd_find_session_by_addr(struct bfd_proto *p, ip_addr addr);
|
||||
struct bfd_session * bfd_find_session_by_addr(struct bfd_proto *p, ip_addr addr, uint ifindex);
|
||||
void bfd_session_process_ctl(struct bfd_session *s, u8 flags, u32 old_tx_int, u32 old_rx_int);
|
||||
void bfd_show_sessions(struct proto *P);
|
||||
|
||||
|
@ -23,7 +23,7 @@ CF_DECLS
|
||||
|
||||
CF_KEYWORDS(BFD, MIN, IDLE, RX, TX, INTERVAL, MULTIPLIER, PASSIVE,
|
||||
INTERFACE, MULTIHOP, NEIGHBOR, DEV, LOCAL, AUTHENTICATION,
|
||||
NONE, SIMPLE, METICULOUS, KEYED, MD5, SHA1)
|
||||
NONE, SIMPLE, METICULOUS, KEYED, MD5, SHA1, IPV4, IPV6, DIRECT)
|
||||
|
||||
%type <iface> bfd_neigh_iface
|
||||
%type <a> bfd_neigh_local
|
||||
@ -38,10 +38,13 @@ bfd_proto_start: proto_start BFD
|
||||
this_proto = proto_config_new(&proto_bfd, $1);
|
||||
init_list(&BFD_CFG->patt_list);
|
||||
init_list(&BFD_CFG->neigh_list);
|
||||
BFD_CFG->accept_ipv4 = BFD_CFG->accept_ipv6 = 1;
|
||||
BFD_CFG->accept_direct = BFD_CFG->accept_multihop = 1;
|
||||
};
|
||||
|
||||
bfd_proto_item:
|
||||
proto_item
|
||||
| ACCEPT bfd_accept
|
||||
| INTERFACE bfd_iface
|
||||
| MULTIHOP bfd_multihop
|
||||
| NEIGHBOR bfd_neighbor
|
||||
@ -56,6 +59,21 @@ bfd_proto:
|
||||
bfd_proto_start proto_name '{' bfd_proto_opts '}';
|
||||
|
||||
|
||||
bfd_accept_item:
|
||||
IPV4 { BFD_CFG->accept_ipv4 = 1; BFD_CFG->accept_ipv6 = 0; }
|
||||
| IPV6 { BFD_CFG->accept_ipv4 = 0; BFD_CFG->accept_ipv6 = 1; }
|
||||
| DIRECT { BFD_CFG->accept_direct = 1; BFD_CFG->accept_multihop = 0; }
|
||||
| MULTIHOP { BFD_CFG->accept_direct = 0; BFD_CFG->accept_multihop = 1; }
|
||||
;
|
||||
|
||||
bfd_accept:
|
||||
{
|
||||
BFD_CFG->accept_ipv4 = BFD_CFG->accept_ipv6 = 1;
|
||||
BFD_CFG->accept_direct = BFD_CFG->accept_multihop = 1;
|
||||
}
|
||||
| bfd_accept bfd_accept_item
|
||||
|
||||
|
||||
bfd_iface_start:
|
||||
{
|
||||
this_ipatt = cfg_allocz(sizeof(struct bfd_iface_config));
|
||||
@ -164,7 +182,7 @@ bfd_neighbor: ipa bfd_neigh_iface bfd_neigh_local bfd_neigh_multihop
|
||||
|
||||
CF_CLI_HELP(SHOW BFD, ..., [[Show information about BFD protocol]]);
|
||||
CF_CLI(SHOW BFD SESSIONS, optproto, [<name>], [[Show information about BFD sessions]])
|
||||
{ bfd_show_sessions(proto_get_named($4, &proto_bfd)); };
|
||||
{ PROTO_WALK_CMD($4, &proto_bfd, p) bfd_show_sessions(p); };
|
||||
|
||||
CF_CODE
|
||||
|
||||
|
@ -366,7 +366,8 @@ bfd_rx_hook(sock *sk, uint len)
|
||||
if (ps > BFD_STATE_DOWN)
|
||||
DROP("invalid init state", ps);
|
||||
|
||||
s = bfd_find_session_by_addr(p, sk->faddr);
|
||||
uint ifindex = (sk->sport == BFD_CONTROL_PORT) ? sk->lifindex : 0;
|
||||
s = bfd_find_session_by_addr(p, sk->faddr, ifindex);
|
||||
|
||||
/* FIXME: better session matching and message */
|
||||
if (!s)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user