aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@debian.org>2019-08-05 10:18:06 +0200
committerOndřej Surý <ondrej@debian.org>2019-08-05 10:18:06 +0200
commit73326170f5f75c34a5e42b3699da5861aafe4bd4 (patch)
treeb48614f95e7442cb0ce107b5278a0fe107538071
parentb1535a7be6119c3f7d63082a2aa771bfb0eead19 (diff)
parent35d302e9115c26cc136fcc1b89fa77f73d87978e (diff)
downloadbird-73326170f5f75c34a5e42b3699da5861aafe4bd4.tar.gz
Update upstream source from tag 'upstream/2.0.5'
Update to upstream version '2.0.5' with Debian dir d79b4052eab5ccec98d98a138f2078dc40006086
-rw-r--r--.dir-locals.el7
-rw-r--r--.gitlab-ci.yml16
-rw-r--r--ChangeLog1597
-rw-r--r--Makefile.in47
-rw-r--r--NEWS18
-rw-r--r--aclocal.m443
-rw-r--r--bird-gdb.py153
-rw-r--r--client/Makefile3
-rw-r--r--client/cmds.Y (renamed from client/cmds.m4)0
-rw-r--r--conf/Makefile13
-rw-r--r--conf/cf-lex.l206
-rw-r--r--conf/conf.c30
-rw-r--r--conf/conf.h69
-rw-r--r--conf/confbase.Y72
-rwxr-xr-xconfigure149
-rw-r--r--configure.ac28
-rw-r--r--doc/bird.sgml143
-rw-r--r--doc/reply_codes1
-rw-r--r--filter/Makefile18
-rw-r--r--filter/config.Y754
-rw-r--r--filter/data.c537
-rw-r--r--filter/data.h203
-rw-r--r--filter/decl.m4583
-rw-r--r--filter/f-inst.c1173
-rw-r--r--filter/f-inst.h73
-rw-r--r--filter/f-util.c119
-rw-r--r--filter/filter.c1974
-rw-r--r--filter/filter.h312
-rw-r--r--filter/filter_test.c66
-rw-r--r--filter/test-reconf-begin.conf23
-rw-r--r--filter/test-reconf-end.conf23
-rw-r--r--filter/test.conf37
-rw-r--r--filter/tree.c31
-rw-r--r--filter/tree_test.c11
-rw-r--r--filter/trie.c15
-rw-r--r--filter/trie_test.c1
-rw-r--r--lib/Makefile2
-rw-r--r--lib/birdlib.h13
-rw-r--r--lib/ip.c2
-rw-r--r--lib/ip.h4
-rw-r--r--lib/printf.c16
-rw-r--r--lib/printf_test.c37
-rw-r--r--lib/resource.h10
-rw-r--r--lib/string.h12
-rw-r--r--lib/strtoul.c61
-rw-r--r--nest/a-path.c71
-rw-r--r--nest/a-path_test.c18
-rw-r--r--nest/a-set.c76
-rw-r--r--nest/a-set_test.c37
-rw-r--r--nest/attrs.h117
-rw-r--r--nest/bfd.h5
-rw-r--r--nest/cli.c1
-rw-r--r--nest/cmds.c10
-rw-r--r--nest/cmds.h4
-rw-r--r--nest/config.Y96
-rw-r--r--nest/iface.c4
-rw-r--r--nest/neighbor.c11
-rw-r--r--nest/proto.c75
-rw-r--r--nest/protocol.h32
-rw-r--r--nest/route.h21
-rw-r--r--nest/rt-attr.c46
-rw-r--r--nest/rt-show.c7
-rw-r--r--nest/rt-table.c222
-rw-r--r--proto/babel/babel.c45
-rw-r--r--proto/babel/config.Y8
-rw-r--r--proto/bfd/bfd.c25
-rw-r--r--proto/bfd/config.Y8
-rw-r--r--proto/bfd/packets.c2
-rw-r--r--proto/bgp/attrs.c45
-rw-r--r--proto/bgp/bgp.c234
-rw-r--r--proto/bgp/bgp.h31
-rw-r--r--proto/bgp/config.Y22
-rw-r--r--proto/bgp/packets.c127
-rw-r--r--proto/mrt/mrt.c6
-rw-r--r--proto/mrt/mrt.h6
-rw-r--r--proto/ospf/config.Y22
-rw-r--r--proto/ospf/dbdes.c11
-rw-r--r--proto/ospf/hello.c4
-rw-r--r--proto/ospf/iface.c9
-rw-r--r--proto/ospf/lsalib.c78
-rw-r--r--proto/ospf/lsalib.h15
-rw-r--r--proto/ospf/lsupd.c20
-rw-r--r--proto/ospf/neighbor.c201
-rw-r--r--proto/ospf/ospf.c141
-rw-r--r--proto/ospf/ospf.h43
-rw-r--r--proto/ospf/rt.c201
-rw-r--r--proto/ospf/rt.h1
-rw-r--r--proto/ospf/topology.c115
-rw-r--r--proto/ospf/topology.h30
-rw-r--r--proto/perf/perf.c4
-rw-r--r--proto/rip/config.Y4
-rw-r--r--proto/rip/rip.c41
-rw-r--r--proto/rpki/config.Y2
-rw-r--r--proto/rpki/rpki.c6
-rw-r--r--proto/static/config.Y17
-rw-r--r--proto/static/static.c5
-rw-r--r--proto/static/static.h2
-rw-r--r--sysdep/autoconf.h.in3
-rw-r--r--sysdep/config.h2
-rw-r--r--sysdep/linux/krt-sys.h21
-rw-r--r--sysdep/linux/netlink.Y26
-rw-r--r--sysdep/linux/netlink.c118
-rw-r--r--sysdep/unix/config.Y10
-rw-r--r--sysdep/unix/io.c3
-rw-r--r--sysdep/unix/krt.c39
-rw-r--r--sysdep/unix/log.c28
-rw-r--r--sysdep/unix/main.c46
-rw-r--r--sysdep/unix/unix.h2
-rw-r--r--test/birdtest.c36
-rw-r--r--test/birdtest.h9
110 files changed, 7771 insertions, 3661 deletions
diff --git a/.dir-locals.el b/.dir-locals.el
new file mode 100644
index 00000000..f1eb3b51
--- /dev/null
+++ b/.dir-locals.el
@@ -0,0 +1,7 @@
+; BIRD project coding conventions
+
+((c-mode
+ (c-file-style . "bsd")
+ (c-basic-offset . 2)
+ (fill-column . 80)
+ (show-trailing-whitespace . t)))
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index ff11dda0..4efe0fcf 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -79,11 +79,6 @@ docker_fedora-26-amd64:
IMG_NAME: "fedora-26-amd64"
<<: *docker_build
-docker_centos-6-amd64:
- variables:
- IMG_NAME: "centos-6-amd64"
- <<: *docker_build
-
docker_centos-7-amd64:
variables:
IMG_NAME: "centos-7-amd64"
@@ -174,13 +169,6 @@ docker_ubuntu-16_04-amd64:
- linux
- amd64
-.centos-6-amd64: &centos-6-amd64_env
- image: registry.labs.nic.cz/labs/bird:centos-6-amd64
- tags:
- - docker
- - linux
- - amd64
-
.centos-7-amd64: &centos-7-amd64_env
image: registry.labs.nic.cz/labs/bird:centos-7-amd64
tags:
@@ -264,10 +252,6 @@ build-fedora-26-amd64:
<<: *fedora-26-amd64_env
<<: *build_job
-build-centos-6-amd64:
- <<: *centos-6-amd64_env
- <<: *build_job
-
build-centos-7-amd64:
<<: *centos-7-amd64_env
<<: *build_job
diff --git a/ChangeLog b/ChangeLog
index 969b6bf5..465e84e2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,15 +1,877 @@
+commit cc02da816fb062c93b4f0d61b0cfa02b673a5e00
+Author: Ondrej Zajicek <santiago@crfreenet.org>
+Date: Thu Aug 1 14:49:03 2019 +0200
+
+ Show LDFLAGS in configure
+
+commit 3ffb0c4f25eff926989867b4a7f604f54d68281b
+Author: Ondrej Zajicek <santiago@crfreenet.org>
+Date: Thu Aug 1 14:45:23 2019 +0200
+
+ Enable more threads for flto
+
+commit de41d24a3d67c98d7330551bcf7549c007b89ae8
+Author: Ondrej Zajicek <santiago@crfreenet.org>
+Date: Thu Aug 1 14:27:20 2019 +0200
+
+ Fix output of CFLAGS in configure script
+
+commit 543875e080969876c33d365474589899c1f960e3
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Wed Jul 31 23:35:29 2019 +0200
+
+ NEWS and version update
+
+commit 96e4d0960c2b1bb01dc90ebfe341af8e4284090a
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Wed Jul 31 19:45:29 2019 +0200
+
+ Change 'graceful down' command to 'graceful restart' and update docs
+
+ The command initiating planned graceful restart including bird shutdown
+ should be called 'graceful restart' instead of 'graceful down', as the
+ later should be reserved for graceful shutdown in style of RFC 8326.
+
+commit 8c703ecf73d7ccbd5e767858ba47a5f3ad0bc439
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Tue Jul 30 19:21:06 2019 +0200
+
+ Doc: Update documentation about VRFs and BFD
+
+commit 2de1e2062eba66893a36f5a84f922c880ab6c351
+Author: Maria Matejka <mq@ucw.cz>
+Date: Tue Jul 30 14:28:40 2019 +0200
+
+ Conf: Fixed symbol redefinition
+
+commit 48addc88be27c46d83805ec2854bf1b5e0f0ddb3
+Author: Maria Matejka <mq@ucw.cz>
+Date: Tue Jul 30 12:11:49 2019 +0200
+
+ Log: Fixed race condition in reconfigure while BFD is running
+
+commit 3b62417c356796a35229ce07238abf98fb569ccb
+Author: Vincent Bernat <vincent@bernat.ch>
+Date: Mon Jul 29 15:42:30 2019 +0200
+
+ RPKI: Fix allocation of hostname when using an IPv6 address
+
+commit 00284f0ed67370c7c697adf20b08840482ea18ea
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Thu Jul 25 14:24:16 2019 +0200
+
+ BFD: Fix formatting of 'show bfd sessions'
+
+ The formatting was broken due to longer date in 'since' column.
+
+commit cec40a74679821513e627f93b924067a404f6475
+Merge: 18f70a6 8263690
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Wed Jul 24 15:38:32 2019 +0200
+
+ Merge remote-tracking branch 'origin/mq-filter-stack'
+
+commit 18f70a6229f586d5e4f387075be42d7a1ef5d269
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Wed Jul 24 15:08:03 2019 +0200
+
+ Nest: VRF of protocol can be explicitly specified as 'default'
+
+ Protocol can have specified VRF, in such case it is restricted to a set
+ of ifaces associated with the VRF, otherwise it can use all interfaces.
+
+ The patch allows to specify VRF as 'default', in which case it is
+ restricted to a set of iface not associated with any VRF.
+
+commit 048c2f0e8cc1451b1fa48e915e0bb5e124aa9d26
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Tue Jul 23 17:02:41 2019 +0200
+
+ OSPF: Fix formatting of 'show ospf neighbors'
+
+ The formatting was broken when too short router-id was used.
+
+commit 15b0a9229431dc75425c229b2f94e680db49d594
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Tue Jul 23 01:52:18 2019 +0200
+
+ RPKI: Fix reconfiguration when ssh parameters are undefined
+
+commit d843c274781bf9d30bfba93229b9f02a88f26fe2
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Thu Jul 18 02:39:35 2019 +0200
+
+ Lib: Improve printf() tests
+
+ Includes patch from Maximilian Eschenbacher
+
+commit 39edf4abcafda429f33f98c31ae11bf6a27ab18e
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Thu Jul 18 01:57:26 2019 +0200
+
+ Lib: Fix print of 64-bit router id
+
+ Mismatched types to printf(). The old code coincidentally worked on amd64
+ due to its calling conventions.
+
+ Thanks to Maximilian Eschenbacher for the bugreport.
+
+commit cf7ff99513728e4e405508e5ccd7522289d4ec82
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Wed Jul 17 16:20:35 2019 +0200
+
+ BFD: Support for VRFs
+
+ Allow multiple BFD instances in separate VRFs, dispatch BFD requests
+ according to VRFs.
+
+ Thanks to Alexander Zubkov for notice and patches.
+
+commit 2eaf65ec607b68748744fa8e0d596cf1f3ba117a
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Mon Jul 15 18:16:55 2019 +0200
+
+ Netlink: Fix parsing of multipath routes with MPLS labels
+
+commit 8235c4747dcc92de2ea991f78cdf9c6b8fa7f522
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Mon Jul 15 16:23:18 2019 +0200
+
+ Netlink: Use route replace for IPv4
+
+ Use route replace netlink op instead of delete+add netlink ops for kernel
+ IPv4 route replace. This avoids some packetloss during route replace.
+ Still use the old behavior for IPv6, as some kernel bugs are hidden in
+ IPv6 ECMP handling.
+
+commit 8263690e754a83b8f3c58bd0080a1628d6cba556
+Merge: efd7c87 1aec711
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Jul 15 16:07:16 2019 +0200
+
+ Merge remote-tracking branch 'origin/master' into mq-filter-stack
+
+commit efd7c87b5bcd476ba74ffe9f369e2f6fe978cbb1
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Jul 15 15:43:47 2019 +0200
+
+ Filter: further split of print & die to FI_PRINT, FI_FLUSH and FI_DIE
+
+commit 3782454e8dead1184e698fa84f7491182b54454e
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Jul 15 15:23:35 2019 +0200
+
+ Filter: Simpler filter context allocation
+
+commit f634adc7dcf8cfc2a8ea8a61fe2f85d8aadf5a75
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Jul 15 15:17:04 2019 +0200
+
+ Filter: FID_MEMBER debug string is a C constant string
+
+commit c0999a149c223fa9c622552a314c767e6a640bf6
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Jul 15 15:12:18 2019 +0200
+
+ Filter: Converted FI_PRINT and FI_PATHMASK_CONSTRUCT to VARARG
+
+commit c29d73a06a8052f653e85f6472c663f70f6706cc
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Jul 15 15:06:52 2019 +0200
+
+ Filter: fixed excessive stack allocation in functions with args but no local vars
+
+commit 0da06b7103a5601fb7c224ab82a6d3799cb55308
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Jul 15 13:19:01 2019 +0200
+
+ Filter: lots of documentation
+
+commit 1b9db6d4a7d8ab9f3ada5d21f8f05c3c8bf3c2e2
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Jul 15 12:03:47 2019 +0200
+
+ Filter: Don't write out when re-evaluating filter for internal purposes.
+
+commit 547be53b8cefc6d346cf13dcedb3e527c3472b06
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Jul 15 12:03:13 2019 +0200
+
+ Filter: Don't fail badly when trying to access non-existent route in config time
+
+commit 1aec7112f7314c3e9a4d8b9440dd85a782295310
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Wed Jul 10 18:25:36 2019 +0200
+
+ OSPF: Fix handling of NSSA option flags
+
+ Per RFC 3101, N-bit signalling NSSA support should be used only in Hello
+ packets, not in DBDES packets. BIRD since 2.0.4 verifies N-bit in
+ neighbor structure, which is learned from DBDES packets, therefore
+ NSSA-LSAs are not propagated to proper implementations of RFC 3101.
+
+ This patch fixes that. Both removing the check and removing N-bit from
+ DBDES packet. This will fix compatibility issues with proper
+ implementations, but causes compatibility issues with BIRD 2.0.4.
+
+commit bfa15a642f8e262af5c18ba5898597a20bdf4e2f
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Wed Jul 10 16:46:31 2019 +0200
+
+ Filter: Minor cleanups
+
+commit b2a4feeb4c877ff56d9b2ebd8119225c53ea40db
+Merge: eac9250 422a933
+Author: Maria Matejka <mq@ucw.cz>
+Date: Wed Jul 10 11:27:08 2019 +0200
+
+ Merge branch 'master' into mq-filter-stack
+
+commit 422a9334294dd9a5b13abd8563a3dc7233e64b13
+Author: Maria Matejka <mq@ucw.cz>
+Date: Wed Jul 10 11:12:41 2019 +0200
+
+ Debug: growing message format buffer
+
+ This led in corner cases to undefined buffer content
+ and garbage output.
+
+commit deb84d79896cca3ac10ff9f853604f845c9420a7
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Tue Jul 9 15:57:46 2019 +0200
+
+ OSPF: Minor fix in graceful restart
+
+ Most LSA origination is blocked in ospf_update_topology(), this fix
+ blocks LSA origination from ospf_rt_spf().
+
+commit e840cb9cd54162efca72137f53fddbb0e490d6fe
+Author: Vincent Bernat <bernat@luffy.cx>
+Date: Tue Jul 9 15:25:40 2019 +0200
+
+ Doc: Fix typo in BGP dynamic names feature description
+
+commit 74a38adb6b4b4bf2a67e7a779a7c95b0ef1b9894
+Merge: 1322e20 2872ab9
+Author: Maria Matejka <mq@ucw.cz>
+Date: Tue Jul 9 14:53:15 2019 +0200
+
+ Merge branch 'master' of gitlab.labs.nic.cz:labs/bird
+
+commit 1322e205e2066c0da8526bed505dc699d0f5b92a
+Author: Maria Matejka <mq@ucw.cz>
+Date: Fri Feb 8 11:19:04 2019 +0100
+
+ Test: Fixed annoying warnings (and possible obscure bugs).
+
+commit bb001af0e8022f6445ff50b7f32c9ac102cc244e
+Author: Maria Matejka <mq@ucw.cz>
+Date: Tue Jul 9 14:34:26 2019 +0200
+
+ Test: better random u64 generator
+
+commit 2872ab927ecb94b1555f5e3c8bd33021261d0c54
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Tue Jul 9 03:48:02 2019 +0200
+
+ OSPF: Update DR when local priority changes
+
+ When priority is reconfigured locally, we need to trigger DR election.
+
+ (recommiting, was reset by the previous commit)
+
+commit 85840d4c03552a69927b666774fa39921e7b1047
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Tue Jul 9 03:31:54 2019 +0200
+
+ OSPF: Fix handling of external routes on graceful restart
+
+ We need to flush learned external LSAs a bit later than other LSAs (after
+ first feed after end of the graceful restart) to avoid flap of external
+ routes.
+
+commit 05e3933c06b488e71c9c149c25aec9c733a8bd1f
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Jul 8 13:04:50 2019 +0200
+
+ Nest: Uninitialized variable fix
+
+ Thanks to Vincent Bernat for reporting this.
+
+commit 2ce25ebbefd2eaf517361a446fe40679e78e23e9
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Jul 8 13:00:13 2019 +0200
+
+ Libdmalloc macros fixed
+
+commit fa1e0ba35416561bda3708ec808d24641dd8995f
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Thu Jul 4 13:34:42 2019 +0200
+
+ OSPF: Update DR when local priority changes
+
+ When priority is reconfigured locally, we need to trigger DR election.
+
+commit eac9250fd5b10809830361b94438339b3b31b270
+Merge: 8816b6c 026bfed
+Author: Maria Matejka <mq@ucw.cz>
+Date: Wed Jul 3 11:09:52 2019 +0200
+
+ Merge branch 'master' into mq-filter-stack
+
+commit 8816b6cdd98d24535eece6b5e35730aac57cd9f7
+Merge: c376555 84ac62d
+Author: Maria Matejka <mq@ucw.cz>
+Date: Wed Jul 3 08:44:42 2019 +0200
+
+ Merge branch 'mq-filter-stack' of gitlab.labs.nic.cz:labs/bird into mq-filter-stack
+
+commit 84ac62d3967f5294f4dc208b803a273a101744a8
+Author: Maria Matejka <mq@jmq.cz>
+Date: Wed Jul 3 08:13:07 2019 +0200
+
+ Filter: CLI command to dump all the linearized filters
+
+commit 0206c070ace90c48a806a74fac52ba6e6ff9858b
+Author: Maria Matejka <mq@jmq.cz>
+Date: Wed Jul 3 01:23:49 2019 +0200
+
+ Filter: Split printing and dying
+
+commit 3265c9169dfc185ccdb787c6c83d9d8e2d0429c1
+Author: Maria Matejka <mq@jmq.cz>
+Date: Wed Jul 3 00:04:24 2019 +0200
+
+ Removed obsolete comment at as_path_cut()
+
+commit 78976974e711e52c3b8fa6a80b290cf2fa4f692d
+Author: Maria Matejka <mq@jmq.cz>
+Date: Wed Jul 3 00:00:11 2019 +0200
+
+ Dynamic attributes definition split whether it is bitmask or not.
+
+commit 263fa2c4a6bf226172a6aef9a6b8198a5901c5bf
+Author: Maria Matejka <mq@jmq.cz>
+Date: Tue Jul 2 22:57:00 2019 +0200
+
+ Filter: Dropped some more irrelevant whitespace from generated files
+
+commit 59d3a3611f05c05040cec4bf09f31c26ade0fa0a
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Tue Jul 2 18:23:06 2019 +0200
+
+ Netlink: Handle alien routes with unsorted nexthops
+
+ Nest requires that nexthops are sorted, the kernel protocol have to
+ ensure that for alien routes.
+
+commit 84c58aabd0d7a6a3d1508de004a29fd22423fde6
+Author: Maria Matejka <mq@jmq.cz>
+Date: Tue Jul 2 17:59:21 2019 +0200
+
+ Filter: Nicer whitespaces in generated inst-gen.h
+
+commit 550a6488c9e2241e2979317c04d6d73752618402
+Author: Maria Matejka <mq@jmq.cz>
+Date: Tue Jul 2 17:39:56 2019 +0200
+
+ Filter: documentation of the M4 preprocessor
+
+commit 1187627a1dded6a3673c0d43033f431f15cd1b8f
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Tue Jul 2 16:30:36 2019 +0200
+
+ Netlink: Do unified scan for both IPv4 and IPv6
+
+ Instead of separate scans for IPv4, IPv6 and MPLS, do one AF_UNSPEC scan.
+
+ This also avoids kernel issue when kernel reported IPv4 and IPv6 routes
+ during MPLS scan if MPLS is not active.
+
+commit c376555cecbfea83314e9f76bce5002185d3566a
+Author: Maria Matejka <mq@ucw.cz>
+Date: Tue Jul 2 13:13:29 2019 +0200
+
+ Filter: GCC, don't complain about indentation in generated code.
+
+commit b40c0f028f37086991fefa9197708ba8c7b3d571
+Author: Maria Matejka <mq@ucw.cz>
+Date: Tue Jul 2 10:45:53 2019 +0200
+
+ Filter: Pre-evaluation of constant expressions
+
+commit 30667d50417f926fc948905aaab3e679b416b2e1
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Jul 1 14:12:05 2019 +0200
+
+ Filter: Resolving of defined constants in config time
+
+commit 26bfe59f450c2497dabc536c3e2a604e8aa5839a
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Jul 1 13:13:06 2019 +0200
+
+ Filter: Moved singleton member definitions to f-inst.c
+
+commit 4212c0e7e5647e107e6e06238a417afc44fd7f75
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Jul 1 12:49:02 2019 +0200
+
+ Filter: Moved f_inst allocation to separate function
+
+commit f74d19765ea3fafdff8fd3443f50a7b309babe89
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Jul 1 12:07:06 2019 +0200
+
+ Filter: Getting rid of RESULT_OK. Adding RESULT_VOID.
+
+ This is a preparation for filter pre-evaluation.
+
+commit 236828d06f512b44457970795e44068d9d38ad3e
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Jul 1 11:57:35 2019 +0200
+
+ Filter: The interpreter code now shares its diversion with constructor
+
+ This is a preparation for filter pre-evaluation.
+
+commit 026bfedb332d8c0dde28c693c177fe993b5df26d
+Author: Maria Jan Matejka <mq@jmq.cz>
+Date: Tue Jun 11 13:19:21 2019 +0000
+
+ BGP: Prefix hash is too small, increase its max size.
+
+ This doesn't make any change for you until you have
+ millions of updates waiting to be sent. Increasing
+ the max hash size from 2^20 to 2^24.
+
+commit 9dac814ee89fe41856923a532c87ffd14dbc0f79
+Author: Maria Jan Matejka <mq@jmq.cz>
+Date: Tue Jun 11 09:35:25 2019 +0000
+
+ BGP: split tx explicitly
+
+ If BGP has too many data to send and BIRD is slower than the link, TX is
+ always possible until all data is sent. This patch limits maximum number
+ of generated BGP messages in one iteration of TX hook.
+
+commit bb57d9171f2b4567f54169c8864953c4e5e18025
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Sun Jun 30 22:30:56 2019 +0200
+
+ Add mock-up function for unit tests
+
+ They failed without it.
+
+commit 6c0f85d5de1588b6427f6f47c1be4a220799cd70
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Sun Jun 30 22:11:11 2019 +0200
+
+ Doc: OSPF graceful restart options
+
+commit 1a2ad348f660b150265f6df759a07de8a2b6de2f
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Sun Jun 30 20:12:59 2019 +0200
+
+ OSPF: Support for graceful restart
+
+ Implement OSPFv2 (RFC 3623) and OSPFv3 (RFC 5187) graceful restart,
+ for both restarting and helper sides. Graceful restart is initiated
+ by 'graceful down' command.
+
+commit 8a68316eb96be1fecf91ca395f3321aa99997ad2
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Tue Jun 18 16:27:21 2019 +0200
+
+ Nest: Add command to request graceful restart
+
+ When 'graceful down' command is entered, protocols are shut down
+ with regard to graceful restart. Namely Kernel protocol does
+ not remove routes and BGP protocol does not send notification,
+ just closes the connection.
+
+commit 63f49457dcc4216002742166dfecce751efa78d5
+Author: Maria Matejka <mq@ucw.cz>
+Date: Fri Jun 28 11:08:48 2019 +0200
+
+ Filter: renaming pointers for consistency
+
+ The struct f_inst * is now always "what"
+ and the union member pointer is always "whati".
+
+commit 64bb1346c71cbd2764b1e0a00dc88290bec72a5a
+Author: Maria Matejka <mq@ucw.cz>
+Date: Thu Jun 27 15:55:48 2019 +0200
+
+ Filter: A little cleanup of M4 interpreter generator
+
+commit a8ab54d18d3af8e7dc9811ab8e3ba9a105131a25
+Merge: 63e7620 2e07773
+Author: Maria Matejka <mq@jmq.cz>
+Date: Tue Jun 25 22:40:05 2019 +0200
+
+ Merge remote-tracking branch 'refs/remotes/origin/mq-filter-stack' into mq-filter-stack
+
+commit 63e7620462b80c9c6bbbd4f128b6816e0748d6c6
+Author: Maria Matejka <mq@jmq.cz>
+Date: Tue Jun 25 16:18:06 2019 +0200
+
+ Conf/Filters: Moved argument count to conf scope
+
+commit 2e0777317f373921487d80e88c376d23a842ba2c
+Author: Maria Matejka <mq@ucw.cz>
+Date: Fri Jun 21 11:33:28 2019 +0200
+
+ Filter instructions don't confuse now v1 and res.
+
+commit a84b8b6ebb2b6825b7059e34cfaafe405ab0117e
+Author: Maria Matejka <mq@ucw.cz>
+Date: Wed Jun 19 14:09:57 2019 +0200
+
+ Revert "Filter: Dropped the setter instructions in favor of direct result storage."
+
+ This reverts commit bd91338246c1ba40358243f1bdf5a6dbd3a29f35.
+
+commit 5c864e2cfaf5ff5e8e3db3ccd746162fbc7aac5b
+Author: Maria Matejka <mq@ucw.cz>
+Date: Thu Jun 13 14:27:58 2019 +0200
+
+ String: bstrtoul macro expanded to bstrtoul10 and 16
+
+commit 87c82334a77dfa3fea3057fa6d4bcf8558f203a4
+Author: Maria Matejka <mq@ucw.cz>
+Date: Thu Jun 13 14:24:48 2019 +0200
+
+ Filter: removal of semantically insane consts in filter_commit
+
+commit bdf2e55d98636eacaac8188ee0bd000cc10d217c
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Wed Jun 12 18:02:01 2019 +0200
+
+ Add mock-up function for unit tests
+
+ They failed without it.
+
+commit 9106a750cd76d4a76c7a60294ce3a43eede166c9
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Wed Jun 12 16:13:21 2019 +0200
+
+ Add CLI command to test reconfiguration status
+
+ Based on patch from Kenth Eriksson <kenth.eriksson@infinera.com>.
+
+commit 8a2cbb88d1657b4aee366605cb3d8ffcc5d3f90a
+Author: Kenth Eriksson <kenth.eriksson@infinera.com>
+Date: Wed Jun 12 15:03:37 2019 +0200
+
+ BIRD coding conventions
+
+ Added Emacs config file describing BIRD coding conventions, as suggested
+ by Kenth Eriksson based on existing practice.
+
+commit d35fb9d732b05f20a556e9bda798939358459975
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Wed Jun 12 16:35:42 2019 +0200
+
+ BGP: Fix bug introduced in one of last patches
+
+commit bd91338246c1ba40358243f1bdf5a6dbd3a29f35
+Author: Maria Matejka <mq@ucw.cz>
+Date: Fri May 31 17:33:41 2019 +0200
+
+ Filter: Dropped the setter instructions in favor of direct result storage.
+
+ This should help filter performance a bit.
+
+commit aa6c5f4d92f225452e432991671e7bdad185506a
+Author: Maria Matejka <mq@ucw.cz>
+Date: Thu May 30 14:42:54 2019 +0200
+
+ Filter: Just a little comments in filter structure
+
+commit 1757a6fce5bd23c44cc5b72a042644c4abb744c8
+Author: Maria Matejka <mq@ucw.cz>
+Date: Wed May 29 21:03:52 2019 +0200
+
+ Filter: Stacks moved to thread-local storage if available.
+
+commit 6479e403ef7398f48c0e1c0f1a71aa112938a357
+Author: Jan Maria Matejka <mq@ucw.cz>
+Date: Thu May 23 11:27:24 2019 +0000
+
+ Filters: If somebody doesn't like _Thread_local, don't fail for now, just be a little slower.
+
+ When the parallel execution comes into place, we'll likely enforce this
+ C11 feature. It's much simpler and also faster than pthread_[sg]etspecific().
+
+commit 23e3b1e6652bac4a003a7eb1e074bdaf7ebb4900
+Author: Jan Maria Matejka <mq@ucw.cz>
+Date: Wed May 22 15:16:32 2019 +0000
+
+ Filter: Some people can't pronounce "postfixify" correctly. Let's try "linearize" instead.
+
+ This is just a naming change.
+
+commit 96d757c13fe4b2e21b265275af9ac7d1e2c3f075
+Author: Jan Maria Matejka <mq@ucw.cz>
+Date: Tue May 21 16:33:37 2019 +0000
+
+ Filter: Store variables and function arguments on stack
+
+commit 20c6ea70ccb69f3c1d9ca46cc7941eb686f283c6
+Author: Jan Maria Matejka <mq@ucw.cz>
+Date: Mon May 20 17:53:10 2019 +0000
+
+ Filter: Making the filter state thread local.
+
+ While having the filter code still reentrant if we really need,
+ the compiler can now do constant propagation and address the
+ thread local storage directly to save some computation time.
+
+commit 9eef9c648cee7179e069ea1f978d4e86cef580d3
+Author: Maria Matejka <mq@ucw.cz>
+Date: Fri May 17 22:18:49 2019 +0200
+
+ Lexer now returns known sym / unknown sym / keyword
+
+commit dbbe4a783b3d4e7722bcb466673f8a1d2832fc7b
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Tue Apr 30 16:16:50 2019 +0200
+
+ Doc: Dynamic BGP
+
+commit 7ff34ca2cb86f3947bf049f73e76e6ce5d57e4a8
+Author: Alexander Azimov <a.e.azimov@gmail.com>
+Date: Tue Apr 30 13:55:43 2019 +0200
+
+ BGP: Compliance with RFC8203bis
+
+commit 0b1e1e1a007f860230855d0c9e8adc88969ee077
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Tue Apr 30 13:44:11 2019 +0200
+
+ BGP: Output Local AS number in show protocol
+
+ Useful for implementation of agents implementing the SNMP-BGP MIB, which
+ requires the local AS of a session to be specified.
+
+ Thanks to Jan-Philipp Litza for the patch.
+
+commit e0835db4f137c1686c26165053ec9c0578b94009
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Mon Apr 8 17:05:07 2019 +0200
+
+ BGP: Dynamic BGP
+
+ Support for dynamically spawning BGP protocols for incoming connections.
+ Use 'neighbor range' to specify range of valid neighbor addresses, then
+ incoming connections from these addresses spawn new BGP instances.
+
+commit df092aa1def0419da74f15bd20582fab4ec09207
+Author: Ondrej Filip <feela@network.cz>
+Date: Wed Apr 24 13:49:18 2019 +0200
+
+ Small type in doc.
+
+commit 6ff811976513132c831a352bd0aa0f3600252acb
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Fri Apr 12 14:11:23 2019 +0200
+
+ OSPFv3: Fix some overlooked cases in IPv4 mode
+
+ Prefixes with max length (/32) were not handled properly.
+
+ Thanks to bauen1 for the bugreport.
+
+commit 4a50c8bd0310053a3dcab45c8dde0362348c0503
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Mon Apr 8 16:39:22 2019 +0200
+
+ BGP: Handle corner cases in event ordering
+
+ When BGP connection is opened, it may happen that rx hook (with remote
+ OPEN) is called before tx hook (for local OPEN). Therefore, we need to do
+ internal changes (like setting local_caps) synchronously with OPENSENT
+ transition and we need to ensure that OPEN is sent before KEEPALIVE.
+
+commit 23ee6b1cd6dc597876d91db9a015f7a633764808
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Wed Apr 3 15:54:50 2019 +0200
+
+ BGP: Promiscuous ASN mode
+
+ Allow to specify just 'internal' or 'external' for remote neighbor
+ instead of specific ASN. In the second case that means BGP peers with
+ any non-local ASNs are accepted.
+
+commit a22c3e59683d0ea6c379a37f990e74a6d281ccef
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Tue Apr 2 17:22:31 2019 +0200
+
+ BGP: Separate runtime and config usage of local/remote ip and as fields
+
+commit fe503c7c0632b385222c92b85d04526fdf36a1a3
+Author: Maria Matejka <mq@ucw.cz>
+Date: Sat Mar 23 13:32:14 2019 +0100
+
+ Filter: fixed error-checking bug in !~ operator
+
+commit 7078aa63ae498b55c729df4a075eb28019917e81
+Author: Maria Matejka <mq@ucw.cz>
+Date: Fri Mar 22 21:40:35 2019 +0100
+
+ Fixed one warning and one undefined value.
+
+commit 2ab680c6972306db71d9a8d4ee4fabf265981d30
+Author: Maria Matejka <mq@ucw.cz>
+Date: Wed Mar 20 16:50:58 2019 +0100
+
+ Fixed an undefined symbol bug in CLI introduced by filter refactoring
+
+commit 3a22a6e858cd703d254ab331183ccd56fe195c6b
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Tue Mar 19 19:38:32 2019 +0100
+
+ Doc: Route attribute cleanups
+
+commit 3c3605818fb304f8de6975c56096bfafa43a8b6b
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Tue Mar 19 17:44:50 2019 +0100
+
+ BGP: Mandatory option for channels
+
+ Allow to mark channel to be mandatory, and do not allow BGP sessions if
+ no common AFI/SAFI is established.
+
+commit 7e5f769d91319b4130f7d611dd14252806892ace
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Fri Mar 15 20:54:01 2019 +0100
+
+ BGP: Handle case where capabilites are not used
+
+ If peer does not announce capabilities at all, or when we have
+ capabilities disabled, handle that as implicit IPv4 unicast.
+
+commit 8d65add626b34256879a3e83855af3c0fa8ce4e7
+Merge: 2f02c25 5d51194
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Mar 18 12:54:40 2019 +0100
+
+ Merge branch 'master' into HEAD
+
+commit 5d511948cddee415c2f0f1103bda0468a193f2d6
+Author: Maria Matejka <mq@ucw.cz>
+Date: Fri Mar 15 15:07:00 2019 +0100
+
+ Build: Automatic dependency tracking for generated files
+
+commit 2f02c25e36f3946019c24dafe9b894a9e195350d
+Author: Maria Matejka <mq@ucw.cz>
+Date: Thu Mar 7 18:02:05 2019 +0100
+
+ Perf: fixed stupid allocation bug
+
+commit d638c1794a48dec79fa0a6c118296356754a134d
+Author: Maria Matejka <mq@ucw.cz>
+Date: Wed Mar 6 21:45:28 2019 +0100
+
+ Gitlab CI: Drop CentOS 6 test build as unsupported.
+
+ If someone wants to maintain BIRD for CentOS 6,
+ feel free to send patches.
+
+commit 875cc073b067f295cccc668008e10218f8e98dd3
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Thu Mar 14 17:22:22 2019 +0100
+
+ Nest: Update handling of temporary attributes
+
+ The temporary atttributes are no longer removed by ea_do_prune(), but
+ they are undefined by store_tmp_attrs() protocol hooks. This fixes
+ several bugs where temporary attributes were removed when they should
+ not or not removed when they should be. The flag EAF_TEMP is no longer
+ needed and was removed.
+
+ Update all protocol make_tmp_attrs() / store_tmp_attrs() hooks to use
+ helper functions and to handle unset attributes properly.
+
+ Also fix some related bugs like improper handling of empty eattr list.
+
+commit 9aa77fccebc4d84b5e1496884cd124d09893041b
+Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
+Date: Wed Mar 6 18:14:12 2019 +0100
+
+ OSPF: Improved handling of tmpattrs
+
+ Keep track of whether OSPF tmpattrs are actually defined for given route
+ (using flags in rte->pflags). That makes them behave more like real
+ eattrs so a protocol can define just a subset of them or they can be
+ undefined by filters.
+
+ Do not set ospf_metric2 for other than type 2 external OSPF routes and do
+ not set ospf_tag for non-external OSPF routes. That also fixes a bug
+ where internal/inter-area route propagated from one OSPF instance to
+ another is initiated with infinity ospf_metric2.
+
+ Thanks to Yaroslav Dronskii for the bugreport.
+
+commit e1ac6f1e301416037725b631fd6529a805e65d51
+Author: Maria Matejka <mq@ucw.cz>
+Date: Wed Mar 6 15:01:10 2019 +0100
+
+ Faster filters: documentation on what is happening there
+
+commit a68442e0563f5b756f9a7323cea44a25ce048738
+Author: Maria Matejka <mq@ucw.cz>
+Date: Wed Feb 27 14:40:05 2019 +0100
+
+ Fixed link time optimizer check for FreeBSD
+
commit b9deced219cfda2afe8604b24351ae10ac56f98b
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Tue Feb 26 18:19:35 2019 +0100
NEWS and version update
+commit f249d0b84c840242a084966999a1d228c603b431
+Author: Maria Matejka <mq@ucw.cz>
+Date: Tue Feb 26 16:44:24 2019 +0100
+
+ Filters: comparison of functions and filters caching
+
+commit 0d12aa48363802e751d3b9a4afd6eb090592d7a3
+Author: Maria Matejka <mq@ucw.cz>
+Date: Tue Feb 26 16:11:40 2019 +0100
+
+ Build: No link time optimization when debug is enabled
+
+commit 2915e711f77d68dff756babd19af8da1677c4549
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Feb 25 23:28:36 2019 +0100
+
+ Custom number parser to speed up config parsing
+
+ The glibc's generic parser is slow due to its versatility. Specialized
+ parsers for base-10 and base-16 are much faster and we don't use other
+ bases.
+
+commit 99911873a196975f5221aad89ae5eac42e1330e0
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Feb 25 17:19:47 2019 +0100
+
+ Conf: Lexer parses quoted strings in a more descriptive way
+
commit 7c36eb3e8bd7d06f65dc7319d42b6abe782c5b89
Author: Maria Matejka <mq@ucw.cz>
Date: Fri Feb 22 12:41:51 2019 +0100
Conf: Switch for faster (and slightly bigger) lexer
+commit 412614c700085ac964b07ff9405403eaf02fa5b4
+Author: Maria Matejka <mq@ucw.cz>
+Date: Fri Feb 22 12:41:51 2019 +0100
+
+ Conf: Switch for faster (and slightly bigger) lexer
+
commit 93af78d2d29ce11e20d46f60cfe1d3ef68052e5c
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Fri Feb 22 02:16:39 2019 +0100
@@ -21,6 +883,275 @@ Date: Fri Feb 22 02:16:39 2019 +0100
a part of enhanced route refresh are always re-announced to other peers
due to change in REF_STALE.
+commit ad702bae0ce95ee1913327dd13a877e6bf9b320d
+Author: Maria Matejka <mq@ucw.cz>
+Date: Wed Feb 20 22:14:28 2019 +0100
+
+ Enabled link time optimization.
+
+commit d1039926f5ee5a4e0442919474f16f3c93385cc9
+Author: Maria Matejka <mq@ucw.cz>
+Date: Tue Feb 19 12:34:16 2019 +0100
+
+ Filter: Interpreter merged into the common m4 generator.
+
+ The config-time partial evaluation of constant expressions in filters is nearby.
+
+commit 32793ab685b047b553d6f7afc23b3245e8850e4a
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Feb 18 14:57:15 2019 +0100
+
+ Filter: Fixed bugs in FI_CALL and FI_SWITCH
+
+commit d348a916f57cb0ac390718295624dd9a1cf2d32a
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Feb 18 14:56:49 2019 +0100
+
+ Test: Added -d flag to die directly after first error.
+
+commit d4bf74816faf9955297f93f8bb6973c1f600dbe2
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Feb 18 14:56:10 2019 +0100
+
+ GDB: Added more pretty printers for filters
+
+commit ea4f55e3dcd472bb6d18c030839597ffd9583462
+Author: Maria Matejka <mq@ucw.cz>
+Date: Fri Feb 15 23:59:44 2019 +0100
+
+ Filter: More cleanup -- customized structures also in struct f_line_item
+
+commit 0b39b1cbb70c6f37a30a3130e1c308ddd0ba36de
+Author: Maria Matejka <mq@ucw.cz>
+Date: Fri Feb 15 13:53:17 2019 +0100
+
+ Conf: Symbol implementation converted from void pointers to union
+
+ ... and consted some declarations.
+
+commit 132529ce8908661fd2baa0758c335006fb039ef0
+Author: Maria Matejka <mq@ucw.cz>
+Date: Wed Feb 13 12:25:30 2019 +0100
+
+ Filter: merged filter compare functions into common M4 file
+
+commit dd4d409551ae22d0a9bf4e3a6edc6fb9656911b9
+Author: Maria Matejka <mq@ucw.cz>
+Date: Tue Feb 12 20:37:32 2019 +0100
+
+ Filter: Merged postfixify routine
+
+commit de12cd18fb213ee9cc872fec77b789f34cfd7cc4
+Author: Maria Matejka <mq@ucw.cz>
+Date: Tue Feb 12 14:16:28 2019 +0100
+
+ Filter: Merged filter line item dumpers into common generated source
+
+commit b256f241459c51224a4bf428f4bc5dfa44882920
+Author: Maria Matejka <mq@ucw.cz>
+Date: Tue Feb 12 11:35:41 2019 +0100
+
+ Filter: auto-generating enum-to-string
+
+commit 041608129ab15b5eab6fb607c45ddd2d748295b5
+Author: Maria Matejka <mq@ucw.cz>
+Date: Tue Feb 12 11:31:18 2019 +0100
+
+ Filter generator: workaround for M4 claiming all the put-around code be on one line
+
+commit 5289304519918f62d099463123bf6c69a0dd497e
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Feb 11 17:12:48 2019 +0100
+
+ Filter data manipulation functions separated to their file
+
+commit 87bd7cd7b03f24c9d7c37a2a060ef553f26ead29
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Feb 11 16:44:14 2019 +0100
+
+ Filter: split the constructors to a separate file
+
+commit 75206f266f8534367b88401be463953b7d5dc770
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Feb 11 15:27:47 2019 +0100
+
+ Conf: Fixed makefiles
+
+commit 4f082dfa892e95f86ca8137410992a248110b6ef
+Author: Maria Matejka <mq@ucw.cz>
+Date: Fri Feb 8 13:38:12 2019 +0100
+
+ Filter: merged filter instruction constructors, counting line size on instruction construct
+
+commit 0a793ebc6059f4354c62ccec62ef7c950988ca4a
+Author: Maria Matejka <mq@ucw.cz>
+Date: Fri Feb 8 11:19:04 2019 +0100
+
+ Test: Fixed annoying warnings (and possible obscure bugs).
+
+commit 8bdb05edb2b4e1d2989ed98d67992047ad69443c
+Author: Maria Matejka <mq@ucw.cz>
+Date: Thu Feb 7 21:25:38 2019 +0100
+
+ Filters: split the large filter.h file to smaller files.
+
+ This should be revised, there are still ugly things in the filter API.
+
+commit c1e97169cd96ce39337e75cfdf6882b180341f09
+Author: Maria Matejka <mq@ucw.cz>
+Date: Wed Feb 6 14:41:39 2019 +0100
+
+ Filter: M4 convertors polished a bit.
+
+commit c0e958e022aac79f69e6aca2652fdb6a529e68e2
+Author: Maria Matejka <mq@ucw.cz>
+Date: Wed Jan 30 14:03:47 2019 +0100
+
+ Filter + Config: Fix bugs, tests and split symbols by type
+
+commit 713658798dfafabcd0a74f510c1639f6e3c9c820
+Author: Maria Matejka <mq@ucw.cz>
+Date: Wed Jan 23 17:08:27 2019 +0100
+
+ GDB pretty printers: f_inst and f_val.
+
+commit 9b46748d5b50d1e8c242a571e80fe1f9f33aeb73
+Author: Maria Matejka <mq@ucw.cz>
+Date: Mon Jan 21 09:17:54 2019 +0100
+
+ Filter: refactoring of instruction constructors
+
+commit 4c553c5a5b40c21ba67bd82455e79678b204cd07
+Author: Maria Matejka <mq@ucw.cz>
+Date: Thu Dec 27 14:26:11 2018 +0100
+
+ Filter refactoring: dropped the recursion from the interpreter
+
+ This is a major change of how the filters are interpreted. If everything
+ works how it should, it should not affect you unless you are hacking the
+ filters themselves.
+
+ Anyway, this change should make a huge improvement in the filter performance
+ as previous benchmarks showed that our major problem lies in the
+ recursion itself.
+
+ There are also some changes in nest and protocols, related mostly to
+ spreading const declarations throughout the whole BIRD and also to
+ refactored dynamic attribute definitions. The need of these came up
+ during the whole work and it is too difficult to split out these
+ not-so-related changes.
+
+commit 967b88d9388b3800efed45798542cd0b41f2b903
+Author: Maria Matejka <mq@ucw.cz>
+Date: Thu Dec 20 16:25:54 2018 +0100
+
+ Filter refactoring: The instructions are converted to the switch body by M4
+
+commit 8436040735b84bb185311125bfc91375f740fd86
+Author: Maria Matejka <mq@ucw.cz>
+Date: Thu Dec 20 16:07:59 2018 +0100
+
+ Filter refactoring: Drop the roa check specific f_inst
+
+commit ca2ee91a80aa87b7f18898c28e93ff22cebf73d7
+Author: Maria Matejka <mq@ucw.cz>
+Date: Thu Dec 20 15:25:04 2018 +0100
+
+ Filter refactoring: The constant f_val is simply included inside the instruction
+
+ With 32 bits, size of the args is 12 bytes, the f_val is 20 bytes.
+ With 64 bits, size of the args is 24 bytes, the f_val the same.
+
+ This is not so nice on 32 bits, anyway the f_inst itself is
+ 24 vs. 32 bytes and the overall size of filters must be 32k of
+ instructions to get to one megabyte of RAM eaten by f_inst.
+
+ Therefore it seems to be improbable for common user to get into
+ problems with this change.
+
+commit 7f0ac73724f7383a2bcc229910414d339dbd2434
+Author: Maria Matejka <mq@ucw.cz>
+Date: Thu Dec 20 14:55:40 2018 +0100
+
+ Filter refactoring: Changed arguments from separate unions to an array
+
+commit 224b77d4f786ea09bb2632476a89f0976baafd64
+Author: Maria Matejka <mq@ucw.cz>
+Date: Thu Dec 20 14:34:35 2018 +0100
+
+ Filter refactoring: Converted condition to three-args instruction
+
+commit 8e8b1fe48cfcb92d54e15df5198e8cef9bc3dd8e
+Author: Maria Matejka <mq@ucw.cz>
+Date: Thu Dec 20 14:29:47 2018 +0100
+
+ Filter refactoring: Some instructions eat up excessively much space.
+
+commit c577493908a9cda9008cc4043d0473b69fb2da66
+Author: Maria Matejka <mq@ucw.cz>
+Date: Thu Dec 20 14:05:32 2018 +0100
+
+ Filter refactoring: Expanded the short instructions with common code.
+
+ This will make the further changes more straightforward.
+
+commit a8740d6c09547dd9fe445b3deb5238305c0a5959
+Author: Maria Matejka <mq@ucw.cz>
+Date: Tue Dec 18 17:10:05 2018 +0100
+
+ Filter refactoring: indentation fix
+
+commit fc8df41ec6cd5e6e3d53036a97dc7219dbbade5e
+Author: Jan Maria Matejka <mq@ucw.cz>
+Date: Mon Dec 17 15:00:01 2018 +0100
+
+ Filter refactoring: The values are now saved on a custom stack.
+
+ This shall help with performance.
+
+commit 7afa1438866fa94454ba133608b6877171f71d37
+Author: Jan Maria Matejka <mq@ucw.cz>
+Date: Mon Dec 17 13:51:11 2018 +0100
+
+ Filter refactoring: Passing the resulting struct f_val as a pointer.
+
+ This also drops the multiplexing of errors with the f_val itself
+ together with the T_RETURN f_val type flag.
+
+commit f62a369fb48be066560d6e8f0df82eef9c464ef0
+Author: Jan Maria Matejka <mq@ucw.cz>
+Date: Mon Dec 17 13:08:08 2018 +0100
+
+ Filter refactoring: Moved filter instruction definition to a separate file
+
+commit 25566c68109381178e0305f3e7e1464dea15b90b
+Author: Jan Maria Matejka <mq@ucw.cz>
+Date: Mon Dec 17 12:58:01 2018 +0100
+
+ Filter refactoring: Moved the bitfield bit position formula to route.h
+
+commit aca8263926df6156322e1a1157886f1cad2486b1
+Author: Jan Maria Matejka <mq@ucw.cz>
+Date: Mon Dec 17 12:48:33 2018 +0100
+
+ Filter refactoring: Moved the interpret macros inside the block
+
+commit 02dcbf343d94dd548605c5efbf724f89d81d7258
+Author: Jan Maria Matejka <mq@ucw.cz>
+Date: Mon Dec 17 12:45:21 2018 +0100
+
+ Configure: Don't check for implicit fallthrough unless when debugging.
+
+commit a946317fab9776754192f679f38cb7789050c52d
+Author: Jan Maria Matejka <mq@ucw.cz>
+Date: Tue Feb 27 15:39:39 2018 +0100
+
+ Filter: Converted static global variables to a filter_state struct.
+
+ The static filter state was messy and blocked the planned parallel
+ execution of filters. Anyway, this will be also slower as the state
+ structure must be passed almost everywhere with us.
+
commit ae294cc2d02ec02cbea11c275b64e7637ba0ea68
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Tue Feb 19 18:30:28 2019 +0100
@@ -73,7 +1204,7 @@ Date: Sat Feb 9 16:15:01 2019 +0100
As we do not have much usage for it yet.
commit cd16538fc91778e31f8241f62ee47056f099c051
-Merge: 6e8fb668 f9b97f1c
+Merge: 6e8fb66 f9b97f1
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Sat Feb 9 15:53:16 2019 +0100
@@ -502,7 +1633,7 @@ Date: Mon Dec 10 02:11:42 2018 +0100
Thanks to Cybertinus for a bugreport.
commit 9a5ef043c11ad9fba00557dedcc0d7ae0d2794e9
-Merge: 0e492063 265419a3
+Merge: 0e49206 265419a
Author: Jan Maria Matejka <mq@ucw.cz>
Date: Thu Dec 6 09:55:34 2018 +0100
@@ -600,35 +1731,35 @@ Date: Wed Feb 14 13:42:53 2018 +0100
Author: Martin Mares <mj@ucw.cz>
Date: Mon Apr 5 20:17:59 1999 +0000
- Added some new protocol hooks (look at the comments for better explanation):
+ Added some new protocol hooks (look at the comments for better explanation):
- make_tmp_attrs Convert inline attributes to ea_list
- store_tmp_attrs Convert ea_list to inline attributes
- import_control Pre-import decisions
+ make_tmp_attrs Convert inline attributes to ea_list
+ store_tmp_attrs Convert ea_list to inline attributes
+ import_control Pre-import decisions
commit 5056c559c4eb253a4eee10cf35b694faec5265eb
Author: Martin Mares <mj@ucw.cz>
Date: Mon Apr 5 20:15:31 1999 +0000
- Changed syntax of attaching filters to protocols to hopefully the final
- version:
+ Changed syntax of attaching filters to protocols to hopefully the final
+ version:
- EXPORT <filter-spec> for outbound routes (i.e., those announced
- by BIRD to the rest of the world).
- IMPORT <filter-spec> for inbound routes (i.e., those imported
- by BIRD from the rest of the world).
+ EXPORT <filter-spec> for outbound routes (i.e., those announced
+ by BIRD to the rest of the world).
+ IMPORT <filter-spec> for inbound routes (i.e., those imported
+ by BIRD from the rest of the world).
- where <filter-spec> is one of:
+ where <filter-spec> is one of:
- ALL pass all routes
- NONE drop all routes
- FILTER <name> use named filter
- FILTER { <filter> } use explicitly defined filter
+ ALL pass all routes
+ NONE drop all routes
+ FILTER <name> use named filter
+ FILTER { <filter> } use explicitly defined filter
- For all protocols, the default is IMPORT ALL, EXPORT NONE. This includes
- the kernel protocol, so that you need to add EXPORT ALL to get the previous
- configuration of kernel syncer (as usually, see doc/bird.conf.example for
- a bird.conf example :)).
+ For all protocols, the default is IMPORT ALL, EXPORT NONE. This includes
+ the kernel protocol, so that you need to add EXPORT ALL to get the previous
+ configuration of kernel syncer (as usually, see doc/bird.conf.example for
+ a bird.conf example :)).
Let's say RIP to this almost 19-years-old inconsistency. For now, if you
import a route, it is always from protocol to table. If you export a
@@ -1057,7 +2188,7 @@ Date: Tue Jun 19 16:16:08 2018 +0200
way too slow. The cached pointer is faster.
commit 1ef23f05ee00394e6a2748f658b73c20d3ff7c45
-Merge: 13c0be19 caa9d03d
+Merge: 13c0be1 caa9d03
Author: Jan Maria Matejka <mq@ucw.cz>
Date: Tue Jun 19 14:32:16 2018 +0200
@@ -1300,7 +2431,7 @@ Date: Thu Mar 22 13:30:10 2018 +0100
Date added.
commit 966602602a0f24942bee3ab0492bbb9197e71aa1
-Merge: 44062812 4841804f
+Merge: 4406281 4841804
Author: Ondrej Filip <feela@network.cz>
Date: Thu Mar 22 13:25:58 2018 +0100
@@ -1327,7 +2458,7 @@ Date: Tue Mar 20 19:28:26 2018 +0100
The old one does not work with 2.0.x.
commit 89ac4dd3c433cae865d5339efe5e682d4506044d
-Merge: bcb4af81 8a871e89
+Merge: bcb4af8 8a871e8
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Mon Mar 19 13:29:39 2018 +0100
@@ -1362,7 +2493,7 @@ Date: Sat Mar 17 17:14:02 2018 +0100
Thanks to Joshua McQuistan for the bugreport.
commit 8a871e890a7198f7cbaff5c75033160ae3ad68f3
-Merge: e95705f0 e8bc64e3
+Merge: e95705f e8bc64e
Author: Jan Maria Matejka <mq@ucw.cz>
Date: Wed Mar 14 12:57:16 2018 +0100
@@ -1381,27 +2512,27 @@ Date: Wed Feb 28 16:57:50 2018 +0100
This led to strange behavior like rewriting bgpmasks when they shan't
be rewritten:
- function mask_generator(int as)
- {
- return [= * as * =];
- }
-
- function another()
- bgpmask m1;
- bgpmask m2;
- {
- m1 = mask_generator(10);
- m2 = mask_generator(20);
- if (m1 == m2) {
- print("strange"); # this would happen
- }
- }
+ function mask_generator(int as)
+ {
+ return [= * as * =];
+ }
+
+ function another()
+ bgpmask m1;
+ bgpmask m2;
+ {
+ m1 = mask_generator(10);
+ m2 = mask_generator(20);
+ if (m1 == m2) {
+ print("strange"); # this would happen
+ }
+ }
Moreover, sending this to CLI would cause stack overflow and knock down the
whole BIRD, as soon as there is at least one route to execute the given
filter on.
- show route filter bgpmask mmm; bgppath ppp; { ppp = +empty+; mmm = [= (ppp ~ mmm) =]; print(mmm); accept; }
+ show route filter bgpmask mmm; bgppath ppp; { ppp = +empty+; mmm = [= (ppp ~ mmm) =]; print(mmm); accept; }
The magic match operator (~) inside the bgpmask literal would try to
resolve mmm, which points to the same bgpmask so it would resolve
@@ -1412,7 +2543,7 @@ Date: Wed Feb 28 16:57:50 2018 +0100
convertible to ASN in BIRD.
commit e95705f00c9e297cd6dde1e8fa60bee4a4d539f2
-Merge: d1ba927b 74bfd2f9
+Merge: d1ba927 74bfd2f
Author: Jan Maria Matejka <mq@ucw.cz>
Date: Tue Mar 13 17:02:49 2018 +0100
@@ -1432,7 +2563,7 @@ Date: Tue Mar 13 12:08:37 2018 +0100
Date: Tue Mar 2 19:49:28 1999 +0000
commit d1ba927b369c91ddb2143b686ca4c1be53e46e64
-Merge: f2f5a7d9 7c601e6b
+Merge: f2f5a7d 7c601e6
Author: Jan Maria Matejka <mq@ucw.cz>
Date: Tue Mar 13 16:51:04 2018 +0100
@@ -1477,7 +2608,7 @@ Date: Tue Mar 6 16:03:35 2018 +0100
Config: Dropped the ipv4:netmask4 syntax for IPv4 prefixes.
commit d0f47327f81a278d6adb3d0a6c235ea715798d01
-Merge: 1561ee79 2d6d4b80
+Merge: 1561ee7 2d6d4b8
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Wed Mar 7 17:41:49 2018 +0100
@@ -1494,7 +2625,7 @@ Date: Wed Mar 7 17:35:24 2018 +0100
Thanks to Leo Vandewoestijne for the bugreport.
commit 44062812600bd29f8edf30ebc871ff218069c5a2
-Merge: 6f46465a 1561ee79
+Merge: 6f46465 1561ee7
Author: Ondrej Filip <feela@network.cz>
Date: Tue Feb 27 06:08:03 2018 +0100
@@ -1591,7 +2722,7 @@ Date: Wed Jan 24 13:55:12 2018 +0100
Nest: remove duplicate function
commit 75d98b6013c19598b1d3ba5e05e8f84525e8678a
-Merge: ace3072e d6cf9961
+Merge: ace3072 d6cf996
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Tue Jan 23 18:29:32 2018 +0100
@@ -1752,7 +2883,7 @@ Date: Wed Jan 3 14:12:00 2018 +0100
ROA: Fix reconfiguration
commit 9bd8cb7c3ca23524cb7d69ee503e24658ad2ee42
-Merge: d493d0f1 cce6ba4d
+Merge: d493d0f cce6ba4
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Tue Jan 2 16:59:59 2018 +0100
@@ -1844,7 +2975,7 @@ Date: Wed Dec 13 14:49:55 2017 +0100
Lib: Minor fix
commit cb21c5ffa92494b1a4bf110605509de3326b6c3d
-Merge: 71c51aa4 1e11918c
+Merge: 71c51aa 1e11918
Author: Jan Maria Matejka <mq@ucw.cz>
Date: Wed Dec 13 10:29:10 2017 +0100
@@ -1983,21 +3114,21 @@ Date: Fri Dec 8 02:26:17 2017 +0100
BGP: Autoconfigure BGP next hops from preferred addresses
commit 830ba75e6dd369c3e64d122f0537cc85211e56e6
-Merge: 46434a3c 1e8721e2
+Merge: 46434a3 1e8721e
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Thu Dec 7 21:54:47 2017 +0100
Merge commit '1e8721e2aeccfbc3f533e8b8abc07582cee77e9a' into int-new
commit 46434a3cad99260b5a659e5df874eab4615bcb36
-Merge: 4ff15a75 7b2c5f3d
+Merge: 4ff15a7 7b2c5f3
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Thu Dec 7 18:35:46 2017 +0100
Merge commit '7b2c5f3d2826e3175bf31b1c36056c9efc587a2b' into int-new
commit 4ff15a75c56531fa2d3858d8250dcef1af4e75b6
-Merge: cd80c9b0 98bb80a2
+Merge: cd80c9b 98bb80a
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Thu Dec 7 17:41:09 2017 +0100
@@ -2466,7 +3597,7 @@ Date: Mon Sep 4 22:32:45 2017 +0200
Thanks to Eugene Sevastyanov for the original patch.
commit 96eace1ea70d7c2bc13672fbeba104d34d8ede4c
-Merge: 08b6a617 5c4dfe0c
+Merge: 08b6a61 5c4dfe0
Author: Michal 'vorner' Vaner <michal.vaner@nic.cz>
Date: Mon Sep 4 13:30:13 2017 +0200
@@ -2562,7 +3693,7 @@ Date: Wed Aug 9 13:35:55 2017 +0200
A TODO file last updated 5 years ago is useless.
commit 69f73992477a0e29f939f9e2722f705c4ad72a38
-Merge: 5a41eed2 b3fae3a8
+Merge: 5a41eed b3fae3a
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Wed Aug 9 12:46:27 2017 +0200
@@ -2709,7 +3840,7 @@ Date: Thu Jun 8 12:18:16 2017 +0200
Thanks to Toke Høiland-Jørgensen <toke@toke.dk> for the patch.
commit 801fd81efea5bf51fe459d951e4be95119798b2b
-Merge: 77810030 33f7fbc4
+Merge: 7781003 33f7fbc
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Wed May 31 14:12:03 2017 +0200
@@ -2722,7 +3853,7 @@ Date: Wed May 31 13:31:03 2017 +0200
CLI: Fix bug in symbol handling introduced in previous patches
commit 77810030d2556e3af659d354a2b3d661f58dd735
-Merge: a1dc5267 4fec4306
+Merge: a1dc526 4fec430
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Tue May 30 14:44:37 2017 +0200
@@ -2735,7 +3866,7 @@ Date: Tue May 30 14:43:49 2017 +0200
Workaround for older bisons
commit a1dc5267602062562f9adca7acfbbc2fee3b315e
-Merge: 4b2aef88 b7761af3
+Merge: 4b2aef8 b7761af
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Thu May 25 23:37:50 2017 +0200
@@ -2752,7 +3883,7 @@ Date: Thu May 25 23:30:39 2017 +0200
according to needs.
commit 4b2aef8857a9ac23015e410930d2162d945892f0
-Merge: 6aaaa635 c72b660b
+Merge: 6aaaa63 c72b660
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Tue May 23 18:45:33 2017 +0200
@@ -2811,7 +3942,7 @@ Date: Thu May 18 14:51:36 2017 +0200
When shift count >= width of type the behavior is undefined.
commit 5a9169e152779ac6f99e2eccb79a2a2f6e2c76b2
-Merge: 7d5e61a6 78e4dac9
+Merge: 7d5e61a 78e4dac
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Thu May 18 14:28:03 2017 +0200
@@ -2840,7 +3971,7 @@ Date: Thu May 18 12:05:09 2017 +0200
Space in action branch breaks build on some platforms.
commit 9b701e69cc812260788eced3370c7e65cd0e25fe
-Merge: d19617f0 d6e01ff9
+Merge: d19617f d6e01ff
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Wed May 17 17:37:27 2017 +0200
@@ -2865,14 +3996,14 @@ Date: Wed May 17 16:05:07 2017 +0200
Fix build on systems with dirty headers
commit d19617f06b4526bccc2fc5e5e15c43c754b99a4d
-Merge: 144c10fa 31874783
+Merge: 144c10f 3187478
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Wed May 17 14:50:00 2017 +0200
Merge remote-tracking branch 'origin/int-new' into int-new
commit 144c10fad1ed6a2520abd1c43501ce00ea2699db
-Merge: 1c5b4c5d a01e951d
+Merge: 1c5b4c5 a01e951
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Wed May 17 14:38:04 2017 +0200
@@ -2906,7 +4037,7 @@ Date: Tue May 16 10:35:10 2017 +0200
Bird readline client saves its history.
commit 1c5b4c5d5b937fe6bbc3a599296e40a05f022b33
-Merge: fd1f355b b845ea09
+Merge: fd1f355 b845ea0
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Tue May 16 14:18:25 2017 +0200
@@ -2957,7 +4088,7 @@ Date: Mon May 15 12:10:51 2017 +0200
Nest: split route show into separate file
commit fd1f355b7b24f354f7d57f127911b4fd98354b34
-Merge: 525a88d8 71652572
+Merge: 525a88d 7165257
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Tue May 9 17:37:38 2017 +0200
@@ -2970,7 +4101,7 @@ Date: Tue May 9 16:46:41 2017 +0200
Minor autoconf cleanup and documentation update
commit 525a88d87930d01d4301e2723dda3dca208cd3d4
-Merge: 95639d95 5d6dc930
+Merge: 95639d9 5d6dc93
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Tue May 9 14:07:14 2017 +0200
@@ -3018,7 +4149,7 @@ Date: Sat Apr 29 00:36:35 2017 +0200
Minor fixes
commit e919601aaf29615edb2a231e58a358c2c5c9d286
-Merge: 5ca4bd5d 33b6c292
+Merge: 5ca4bd5 33b6c29
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Fri Apr 28 11:19:12 2017 +0200
@@ -3043,7 +4174,7 @@ Date: Wed Apr 26 14:11:28 2017 +0200
Test: Fix broken test for filters
commit 69fddac0525b1b0c940d778a161ed3a0a742ed6f
-Merge: 93a3661c b2949999
+Merge: 93a3661 b294999
Author: Jan Moskyto Matejka <mq@ucw.cz>
Date: Wed Apr 26 12:30:22 2017 +0200
@@ -3174,7 +4305,7 @@ Date: Tue Mar 28 18:14:32 2017 +0200
Netlink: Fix device route delete
commit 5dbeb87ec96157ca95c84d881e014614dd3164a3
-Merge: 2282030b d1b8fe93
+Merge: 2282030 d1b8fe9
Author: Jan Moskyto Matejka <mq@ucw.cz>
Date: Tue Mar 28 17:35:57 2017 +0200
@@ -3224,7 +4355,7 @@ Date: Wed Mar 22 15:00:07 2017 +0100
Based on patches from Jan Matejka.
commit ead7b8f498ddefc0b7373cbba78f9a7ba1dddaa9
-Merge: da3cf9ea 61e501da
+Merge: da3cf9e 61e501d
Author: Jan Moskyto Matejka <mq@ucw.cz>
Date: Wed Mar 22 14:54:00 2017 +0100
@@ -3361,7 +4492,7 @@ Date: Wed Feb 22 12:02:28 2017 +0100
Babel post-merge fixes
commit c609d039860f97f400d2cf0e9ca2b4e87b3fd1cc
-Merge: 62e64905 2be9218a
+Merge: 62e6490 2be9218
Author: Jan Moskyto Matejka <mq@ucw.cz>
Date: Wed Feb 22 11:58:04 2017 +0100
@@ -3409,7 +4540,7 @@ Date: Fri Feb 17 22:54:06 2017 +0100
Thanks to Lennert Buytenhek for tracking and reporting the bug.
commit c259669fa33ca13b5c6ae60eb8ffa0874ddc01b2
-Merge: 82f42ea0 da65a3d8
+Merge: 82f42ea da65a3d
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Wed Feb 8 14:34:48 2017 +0100
@@ -3645,7 +4776,7 @@ Date: Wed Dec 7 15:35:35 2016 +0100
RPKI: fixed some of the extended warnings
commit ad88b94bca78e010357a6c7806e1d5e01701d4a7
-Merge: d15b0b0a af62c0f9
+Merge: d15b0b0 af62c0f
Author: Jan Moskyto Matejka <mq@ucw.cz>
Date: Wed Dec 7 15:30:46 2016 +0100
@@ -3928,7 +5059,7 @@ Date: Wed Nov 9 16:36:34 2016 +0100
current int-new branch.
commit 8860e991f6650e47cfe6c1af595fe4fe92a4edfd
-Merge: cc5b93f7 c8cafc8e
+Merge: cc5b93f c8cafc8
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Tue Nov 8 19:27:58 2016 +0100
@@ -3941,7 +5072,7 @@ Date: Tue Nov 8 17:46:29 2016 +0100
Minor code cleanups
commit cc5b93f72db80abd1262a0a5e1d8400ceef54385
-Merge: 5de0e848 f51b1f55
+Merge: 5de0e84 f51b1f5
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Tue Nov 8 17:03:31 2016 +0100
@@ -4371,7 +5502,7 @@ Date: Tue Aug 30 12:43:46 2016 +0200
check.
commit 768d013267bd2a52bab70ba6d413f49365fb31e3
-Merge: 678c97f2 d107ef78
+Merge: 678c97f d107ef7
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Tue Aug 23 17:35:26 2016 +0200
@@ -4674,7 +5805,7 @@ Date: Tue May 17 15:21:49 2016 +0200
SKF_V6ONLY are to be removed.
commit 5af7b59660be615fbbd7c20b92b71321c003c43a
-Merge: d39d41fb b66a9e2f
+Merge: d39d41f b66a9e2
Author: Jan Moskyto Matejka <mq@ucw.cz>
Date: Fri May 13 13:48:04 2016 +0200
@@ -4687,7 +5818,7 @@ Date: Fri May 13 13:46:46 2016 +0200
Hash: Fix of previous commit
commit b66a9e2f3376b4cb07ef4cc318f70a9c794f407a
-Merge: 659f80f2 3f2c7600
+Merge: 659f80f 3f2c760
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Thu May 12 21:47:50 2016 +0200
@@ -4706,14 +5837,14 @@ Date: Thu May 12 21:29:04 2016 +0200
We don't need bvsnprintf() in BIRD client
commit 776d6b2c05fe8b14e5ec357eca24fe59c549bfa4
-Merge: af678af0 54ac0bec
+Merge: af678af 54ac0be
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Thu May 12 18:11:12 2016 +0200
Merge remote-tracking branch 'origin/int-new' into int-new
commit af678af0d5c9ef3d8afdc0789b33dd0c40b6d6e5
-Merge: 286e2011 8e433d6a
+Merge: 286e201 8e433d6
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Thu May 12 17:49:12 2016 +0200
@@ -4756,14 +5887,14 @@ Date: Tue May 10 16:45:35 2016 +0300
Signed-off-by: Stijn Tintel <stijn@linux-ipv6.be>
commit 0c6dfe52369a59d7f3da8ee6bc7c505e3da5c064
-Merge: 7a7ac656 92912f06
+Merge: 7a7ac65 92912f0
Author: Jan Moskyto Matejka <mq@ucw.cz>
Date: Tue May 10 14:30:49 2016 +0200
Merge branch 'int-new' into int-new-merged
commit 92912f063a94bd7c743a25628ca2073380e09ef4
-Merge: a8caff32 2003a184
+Merge: a8caff3 2003a18
Author: Jan Moskyto Matejka <mq@ucw.cz>
Date: Tue May 10 14:21:15 2016 +0200
@@ -4882,7 +6013,7 @@ Date: Fri Apr 8 13:08:03 2016 +0200
side of the route-creation routine.
commit 7a7ac656829223713f9e6bcef63d2b5a5efce7d2
-Merge: 4bdf1881 06edbb67
+Merge: 4bdf188 06edbb6
Author: Jan Moskyto Matejka <mq@ucw.cz>
Date: Fri Apr 8 12:09:31 2016 +0200
@@ -4904,7 +6035,7 @@ Date: Mon Apr 4 16:17:11 2016 +0200
Channelize: rt_notify arg conversion table -> channel
commit 0c8c8151fc1fb0dbfcd682153f50192ea1369884
-Merge: a815d62d 1a7daab1
+Merge: a815d62 1a7daab
Author: Jan Moskyto Matejka <mq@ucw.cz>
Date: Thu Apr 7 09:58:31 2016 +0200
@@ -5022,7 +6153,7 @@ Date: Wed Mar 9 17:37:44 2016 +0100
Thanks to Pavel Tvrdik for noticing it.
commit 62a4ad365760afae485ae61c5aab734012545be4
-Merge: 9036bbf2 9c92f692
+Merge: 9036bbf 9c92f69
Author: Jan Moskyto Matejka <mq@ucw.cz>
Date: Tue Mar 22 13:23:55 2016 +0100
@@ -5083,7 +6214,7 @@ Date: Tue Feb 2 18:14:53 2016 +0100
Fix typo
commit ce95af7a5f5159033e569a7ccedce0e20bb5d913
-Merge: b25509e5 e3f506f9
+Merge: b25509e e3f506f
Author: Jan Moskyto Matejka <mq@ucw.cz>
Date: Thu Feb 25 18:25:53 2016 +0100
@@ -5514,7 +6645,7 @@ Date: Wed Nov 25 15:52:58 2015 +0100
BGP: Update capability number from IANA for extended messages
commit 04ae8ddaa15b72c265dc7cf038b733d235198754
-Merge: d44e686e 33b4f40a
+Merge: d44e686 33b4f40
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Wed Nov 25 14:24:35 2015 +0100
@@ -5527,7 +6658,7 @@ Date: Fri Nov 13 16:08:28 2015 +0100
MD5: Mormalize naming style
commit 90f78507f4a13673ccf0ba7c786b43d9e882fca7
-Merge: 8eb8e546 ad276157
+Merge: 8eb8e54 ad27615
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Tue Nov 24 15:21:11 2015 +0100
@@ -5560,7 +6691,7 @@ Date: Tue Nov 24 13:47:28 2015 +0100
Minor changes to SHA hash functions
commit 12d752ef24ab507d249a60098ec98dcf28b70036
-Merge: 1e4891e4 f312a837
+Merge: 1e4891e f312a83
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Mon Nov 23 11:32:18 2015 +0100
@@ -5688,7 +6819,7 @@ Date: Tue Nov 3 11:08:57 2015 +0100
Thanks to Alexander V. Chernikov for the patch.
commit 8eb8e546dc8cc647fcfa4a3a17dfa8ab36b00958
-Merge: 8465dccb acb04cfd
+Merge: 8465dcc acb04cf
Author: Ondrej Zajicek (work) <santiago@crfreenet.org>
Date: Sat Oct 17 14:44:34 2015 +0200
@@ -6204,7 +7335,7 @@ Date: Sat Feb 21 11:46:14 2015 +0100
Configurable fast shutdown of a BGP session when an interface loses link.
commit 7730553b7eeb33d21e5597f110334ca584ad532d
-Merge: 0da562a7 ec2194fa
+Merge: 0da562a ec2194f
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Sat Feb 21 11:39:45 2015 +0100
@@ -6290,7 +7421,7 @@ Date: Thu Oct 16 18:05:54 2014 +0200
__attribute__((packed)) added. Thank to Maksim Tamarsky
commit 78342404ff573e85e396f0611014b90cea9b4c0a
-Merge: 178a197a cfdea7b8
+Merge: 178a197 cfdea7b
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Tue Oct 14 17:23:34 2014 +0200
@@ -6460,7 +7591,7 @@ Date: Mon May 5 11:05:12 2014 +0200
Thanks to Sergey Popovich for the original patch.
commit 5a3905fe905ab1cc1a58fe9e6a4284f7d5057d13
-Merge: 66370eac 7d9ab86b
+Merge: 66370ea 7d9ab86
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Sun May 4 11:59:50 2014 +0200
@@ -6505,7 +7636,7 @@ Date: Wed Apr 23 13:54:28 2014 +0200
Thanks to Peter Christensen for the original patch.
commit 4dd24f05f384ac14546d4bebbfcb0ecf9a976ec6
-Merge: 1cb0f83d 6c6ebd64
+Merge: 1cb0f83 6c6ebd6
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Wed Apr 23 13:52:15 2014 +0200
@@ -6626,7 +7757,7 @@ Date: Mon Mar 24 12:56:56 2014 +0100
Reformat bird.sgml to have consistent line length.
commit c980f8002e0f0578d5e715d48d65d9fb9a0c5a70
-Merge: 2e84b4e8 227af309
+Merge: 2e84b4e 227af30
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Mon Mar 24 12:41:43 2014 +0100
@@ -6698,7 +7829,7 @@ Date: Fri Feb 7 11:46:01 2014 +0100
Fixes autoconf check for ncurses.
commit 5c200e0a4d26d183e04fda43de16340521002c40
-Merge: 4a5eb284 080ed4d8
+Merge: 4a5eb28 080ed4d
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Thu Feb 6 20:15:05 2014 +0100
@@ -6762,7 +7893,7 @@ Date: Thu Feb 6 17:46:01 2014 +0100
- Use ifa->ifname instead of ifa->iface->name
commit f48fa14214301382b2e6b134788a7506b61b664f
-Merge: 6601a148 300e4008
+Merge: 6601a14 300e400
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Thu Feb 6 16:47:43 2014 +0100
@@ -6787,7 +7918,7 @@ Date: Tue Jan 21 23:43:22 2014 +0100
Small doc update.
commit 6601a14831cdd32fc671ebc9dc299d2be427e489
-Merge: 2d0b7e24 283c7dfa
+Merge: 2d0b7e2 283c7df
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Tue Dec 10 22:30:46 2013 +0100
@@ -6820,7 +7951,7 @@ Date: Tue Nov 26 22:37:24 2013 +0100
Fixes some bugs and uses generic hash implementation.
commit 283c7dfada53a6dee6a8a17ecab492ffafd44b66
-Merge: 736e143f 0bb4e37d
+Merge: 736e143 0bb4e37
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Mon Nov 25 18:42:47 2013 +0100
@@ -6931,7 +8062,7 @@ Date: Sat Nov 23 13:26:52 2013 +0100
Added a few sentences about symbol names.
commit 736e143fa50607fcd88132291e96089b899af979
-Merge: 094d2bdb 2b3d52aa
+Merge: 094d2bd 2b3d52a
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Sat Nov 23 11:50:34 2013 +0100
@@ -6939,11 +8070,11 @@ Date: Sat Nov 23 11:50:34 2013 +0100
Conflicts:
- filter/filter.c
- nest/proto.c
- nest/rt-table.c
- proto/bgp/bgp.h
- proto/bgp/config.Y
+ filter/filter.c
+ nest/proto.c
+ nest/rt-table.c
+ proto/bgp/bgp.h
+ proto/bgp/config.Y
commit 2b3d52aa421ae1c31e30107beefd82fddbb42854
Author: Ondrej Zajicek <santiago@crfreenet.org>
@@ -6982,7 +8113,7 @@ Date: Fri Nov 22 18:45:57 2013 +0100
Adds check for buffer size in OSPF LSA flood.
commit 0aeac9cb7f9887374ce0258c8653f9518529bf08
-Merge: 8931425d 7c9930f9
+Merge: 8931425 7c9930f
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Fri Nov 22 02:43:41 2013 +0100
@@ -7215,7 +8346,7 @@ Date: Tue Sep 10 12:58:24 2013 +0200
Thanks to Sergey Popovich for a bugreport.
commit d27e127aa996d500fed21be2bbbe888cafecb830
-Merge: bff9ce51 b0a8c7fc
+Merge: bff9ce5 b0a8c7f
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Tue Sep 10 12:54:57 2013 +0200
@@ -7497,7 +8628,7 @@ Date: Mon Apr 29 22:08:05 2013 +0200
Thanks to Peter Christensen for the bugfix.
commit 32622d0ea366406f3afa14bb9edb4855d6979786
-Merge: efd6d12b a5e9f3d2
+Merge: efd6d12 a5e9f3d
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Tue Apr 23 02:54:13 2013 +0200
@@ -7924,7 +9055,7 @@ Date: Wed Aug 8 14:10:31 2012 +0200
DragonFly support add - thanks to john@marino.st
commit 60c412b9368fd7c3b0a8df2200f02140adcb0cf3
-Merge: 3fe1d9e4 94e2f1c1
+Merge: 3fe1d9e 94e2f1c
Author: Ondrej Filip <feela@network.cz>
Date: Tue Aug 7 11:15:23 2012 +0200
@@ -7989,7 +9120,7 @@ Date: Wed Jul 18 19:29:33 2012 +0200
Thanks Kelly Cochran for suggestion and draft patch.
commit abced4a91495e27fe86b142bc1967cec53bab3dc
-Merge: fc06fb62 76170264
+Merge: fc06fb6 7617026
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Mon Jul 16 14:44:45 2012 +0200
@@ -7997,12 +9128,12 @@ Date: Mon Jul 16 14:44:45 2012 +0200
Conflicts:
- nest/config.Y
- nest/rt-table.c
- proto/bgp/bgp.c
+ nest/config.Y
+ nest/rt-table.c
+ proto/bgp/bgp.c
commit 761702644397886bd3c1be10fd55c01485b7c454
-Merge: 26822d8f 553e4054
+Merge: 26822d8 553e405
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Mon Jul 16 10:41:29 2012 +0200
@@ -8021,7 +9152,7 @@ Date: Sat Jul 7 10:40:00 2012 +0200
Implements RDNSS and DNSSL support for RAdv.
commit 3fe1d9e4a40663b93b59f5b6f9d61af9dc6a8ae6
-Merge: 72b2db8d 95127cbb
+Merge: 72b2db8 95127cb
Author: Ondrej Filip <feela@network.cz>
Date: Tue May 15 23:40:37 2012 +0200
@@ -8052,7 +9183,7 @@ Date: Fri May 11 12:01:27 2012 +0200
Fixes a bug in RA_ACCEPTED handling.
commit 72b2db8db7534c52e928618410ec1f18787752c8
-Merge: 2795700c 95616c82
+Merge: 2795700 95616c8
Author: Ondrej Filip <feela@network.cz>
Date: Fri May 11 00:01:29 2012 +0200
@@ -8086,7 +9217,7 @@ Date: Thu May 3 12:25:15 2012 +0200
Implements build options to specify socket dir and suffix.
commit 2795700c3158fa52b6cf957e9d0b9ad4a27c67a5
-Merge: 1f85226e bf422073
+Merge: 1f85226 bf42207
Author: Ondrej Filip <feela@network.cz>
Date: Wed May 2 11:10:40 2012 +0200
@@ -8163,7 +9294,7 @@ Date: Fri Apr 27 00:04:51 2012 +0200
Some minor fixes.
commit 1f85226ecb76d3803b8fe37eb0891c45a6557dcd
-Merge: 92f8f7e3 d9b77cc2
+Merge: 92f8f7e d9b77cc
Author: Ondrej Filip <feela@network.cz>
Date: Thu Apr 26 17:03:53 2012 +0200
@@ -8178,7 +9309,7 @@ Date: Tue Apr 24 23:39:57 2012 +0200
And also fixes some minor bugs in limits.
commit 3589546af4baa4d349409a318f8c9658dd11b3cc
-Merge: 7d0a31de cca97066
+Merge: 7d0a31d cca9706
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Tue Apr 24 23:37:01 2012 +0200
@@ -8239,14 +9370,14 @@ Date: Sun Apr 15 15:28:29 2012 +0200
Thanks to Alexander V. Chernikov for the original patch.
commit 3e17e380598b9a512bb369f51a4cf55da269f608
-Merge: 00a09f3c ae8b3001
+Merge: 00a09f3 ae8b300
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Sun Apr 15 15:17:03 2012 +0200
Merge branch 'master' into rt-accepted
commit ae8b300164a975597f9b6caea0b205af2e4db30b
-Merge: d360f129 ed7c4b0c
+Merge: d360f12 ed7c4b0
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Sun Apr 15 15:15:05 2012 +0200
@@ -8271,7 +9402,7 @@ Date: Mon Apr 9 14:19:28 2012 +0200
Small bugfix in error message related to reconfiguration.
commit bf2abe2f515d7b7aaed5fb4f37af82169adcd2f2
-Merge: fb829de6 c0adf7e9
+Merge: fb829de c0adf7e
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Fri Mar 30 11:04:12 2012 +0200
@@ -8279,8 +9410,8 @@ Date: Fri Mar 30 11:04:12 2012 +0200
Conflicts:
- nest/proto.c
- nest/rt-table.c
+ nest/proto.c
+ nest/rt-table.c
commit fb829de69052755a31d76d73e17525d050e5ff4d
Author: Ondrej Zajicek <santiago@crfreenet.org>
@@ -8293,7 +9424,7 @@ Date: Wed Mar 28 18:40:04 2012 +0200
maximum number of routes flushed in one step.
commit cb3cf95859d81c711337738f004675f43c8bbb0e
-Merge: c9df01d3 16fc65ac
+Merge: c9df01d 16fc65a
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Sun Mar 25 20:59:13 2012 +0200
@@ -8973,7 +10104,7 @@ Date: Tue May 10 02:42:17 2011 +0200
linux capabilities CAP_NET_* are kept.
commit 46bb7e0d176a4dc0a47bb406988f92fb29cceaf4
-Merge: b8cc390e b7c48981
+Merge: b8cc390 b7c4898
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Fri May 6 22:09:44 2011 +0200
@@ -9010,7 +10141,7 @@ Date: Sun May 1 17:16:05 2011 +0200
There may be more IP address records with the same IP.
commit 5964569c23829ec93fcf671a2582be01c8aebecf
-Merge: acc93efd d600909d
+Merge: acc93ef d600909
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Fri Apr 29 19:03:19 2011 +0200
@@ -9059,7 +10190,7 @@ Date: Tue Apr 5 11:41:18 2011 +0200
Minor fixes.
commit bf27abd6d4a20448f5b4c80e9aa9258dc8670f62
-Merge: 4ef09506 a5b84ab5
+Merge: 4ef0950 a5b84ab
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Fri Apr 1 13:56:42 2011 +0200
@@ -9749,7 +10880,7 @@ Date: Tue Apr 27 18:29:01 2010 +0200
A fix in OSPFv3 next_hop calculation.
commit 96599c957baa9c82bde91d610ce4f519aead05e9
-Merge: ba130172 9d1ee138
+Merge: ba13017 9d1ee13
Author: Ondrej Filip <feela@majklik.network.cz>
Date: Tue Apr 27 11:28:44 2010 +0200
@@ -9793,7 +10924,7 @@ Date: Sat Apr 24 15:18:21 2010 +0200
OSPFv2 and OSPFv3 (previously it displayed stubnets in OSPFv2).
commit 1d44ddf20f3ecef864d4bd20355251839fcd10ee
-Merge: 3b89a232 6bc414d6
+Merge: 3b89a23 6bc414d
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Wed Apr 21 21:52:10 2010 +0200
@@ -9960,7 +11091,7 @@ Date: Mon Mar 29 19:29:03 2010 +0200
- Documentation updates.
commit eb0f129fcedcecbee85403095abad8f59b82683c
-Merge: b1c030b0 48cff379
+Merge: b1c030b 48cff37
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Fri Mar 26 18:53:31 2010 +0100
@@ -10036,7 +11167,7 @@ Date: Wed Mar 17 12:19:22 2010 +0100
- Fixes socket name path check and other error handling in CLI init.
commit 4e3bfd9006ba3033a814a392f9bf4bbca060c8a9
-Merge: e8da1bd0 cda2dfb7
+Merge: e8da1bd cda2dfb
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Mon Mar 15 00:39:45 2010 +0100
@@ -10055,7 +11186,7 @@ Date: Sun Mar 14 16:36:59 2010 +0100
Temporary OSPF commit - sockets.
commit 54305181f6ee3af57dd3d15d53ea2e851b36ed23
-Merge: e7b76b97 afa9f66c
+Merge: e7b76b9 afa9f66
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Thu Mar 11 18:55:59 2010 +0100
@@ -10124,7 +11255,7 @@ Date: Sun Feb 21 14:34:53 2010 +0100
Fix configure to enable warnings and fix most of them.
commit 9e43ccf07b96597ef098955a07383d826938cd2d
-Merge: e285bd23 89534cda
+Merge: e285bd2 89534cd
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Sun Feb 21 10:15:49 2010 +0100
@@ -10246,7 +11377,7 @@ Date: Wed Feb 10 16:18:17 2010 +0100
NEWS and version update.
commit fae9be7782a161bdf93c83884d62941a34cbe518
-Merge: 7d196668 0efd6462
+Merge: 7d19666 0efd646
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Wed Feb 10 14:59:26 2010 +0100
@@ -10324,7 +11455,7 @@ Date: Tue Feb 2 00:03:46 2010 +0100
Because we don't want to have a limit on a function/filter length.
commit 1a7a4e59a22f903a0be791f229e86ab881593851
-Merge: 41677025 1960d203
+Merge: 4167702 1960d20
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Thu Jan 28 16:00:16 2010 +0100
@@ -10366,7 +11497,7 @@ Date: Wed Jan 13 14:53:33 2010 +0100
Comment update.
commit 974363c172e026b00be5f78ec585dda1e402b6f6
-Merge: 99f5fc14 844e0f65
+Merge: 99f5fc1 844e0f6
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Fri Jan 8 22:20:09 2010 +0100
@@ -10379,7 +11510,7 @@ Date: Fri Jan 8 22:19:41 2010 +0100
A partial vlink fix.
commit 844e0f65dbab98f71f2a5631277a720613d4d7a5
-Merge: 3242ab43 fc33143f
+Merge: 3242ab4 fc33143
Author: Ondrej Filip <feela@majklik.network.cz>
Date: Fri Jan 8 21:19:03 2010 +0100
@@ -10482,7 +11613,7 @@ Date: Tue Dec 22 10:49:39 2009 +0100
Show command cleanups.
commit 0ad19261bf551ef49a1565e21e384ec749ec16d4
-Merge: c4ae5385 67b24e7c
+Merge: c4ae538 67b24e7
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Tue Dec 22 01:34:01 2009 +0100
@@ -10509,14 +11640,14 @@ Date: Mon Dec 21 11:50:42 2009 +0100
Change default LOCAL_PREF attribute to 100 (suggested value by RFC 4277).
commit ba9a122142a3d42137c129fabaef097702d44801
-Merge: 0225ea4e c4199e30
+Merge: 0225ea4 c4199e3
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Sun Dec 20 22:57:44 2009 +0100
Merge branch 'ospf3' into new
commit c4199e30313c88c0911060a5b5f3cc181ceedb37
-Merge: f2d7da74 ea7ada38
+Merge: f2d7da7 ea7ada3
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Sun Dec 20 22:56:09 2009 +0100
@@ -10549,7 +11680,7 @@ Date: Sat Dec 19 18:45:46 2009 +0100
NEWS and version update.
commit 0225ea4eddb44bd9dd4f112e512325cbf80134d3
-Merge: 43c1cecc f2d7da74
+Merge: 43c1cec f2d7da7
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Tue Dec 15 00:32:13 2009 +0100
@@ -10580,7 +11711,7 @@ Date: Mon Dec 14 21:17:15 2009 +0100
Implements route reload for OSPF.
commit 63542845dfb6d2277f93f77ad9ca3ad8bbaddd09
-Merge: 34a877cc 8a7fb885
+Merge: 34a877c 8a7fb88
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Mon Dec 14 20:37:32 2009 +0100
@@ -10588,9 +11719,9 @@ Date: Mon Dec 14 20:37:32 2009 +0100
Conflicts:
- proto/ospf/lsreq.c
- proto/ospf/lsupd.c
- proto/ospf/rt.c
+ proto/ospf/lsreq.c
+ proto/ospf/lsupd.c
+ proto/ospf/rt.c
commit 34a877ccac25d38172716d3d2488449c870cad0b
Author: Ondrej Zajicek <santiago@crfreenet.org>
@@ -10710,7 +11841,7 @@ Date: Thu Nov 26 20:47:59 2009 +0100
Implements BGP route refresh.
commit 5e6f568115511e2bcf43c60dfdcbd7a35cb04b93
-Merge: 069bfcb5 1f8be1e4
+Merge: 069bfcb 1f8be1e
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Tue Nov 24 17:19:23 2009 +0100
@@ -10811,14 +11942,14 @@ Date: Mon Nov 9 23:22:53 2009 +0100
Use IPv6 checksums in OSPFv3.
commit 3f22fa9e74c8643d3e4f7e3a7b4f2aa992ad09f5
-Merge: a6bc04d5 b7c0e93e
+Merge: a6bc04d b7c0e93
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Mon Nov 9 22:54:39 2009 +0100
Merge branch 'dev' into ospf3
commit b7c0e93ebd40cdc4f6e89067a3e5f7293263c7f9
-Merge: 7965e17d f1f1e899
+Merge: 7965e17 f1f1e89
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Mon Nov 2 16:27:17 2009 +0100
@@ -11083,7 +12214,7 @@ Date: Sun Aug 23 23:03:31 2009 +0200
Version 1.1.2
commit 2ef58837dcb7b78edc193bc9ef1da316bba86998
-Merge: 86975e58 5516a66d
+Merge: 86975e5 5516a66
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Fri Aug 21 09:46:49 2009 +0200
@@ -11196,7 +12327,7 @@ Date: Mon Aug 10 12:04:25 2009 +0200
Fixes typo in Makefile
commit b92c8e30191dd757c76239076eda82d0065f2348
-Merge: 54d70d3e 71a9574a
+Merge: 54d70d3 71a9574
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Mon Aug 10 10:16:00 2009 +0200
@@ -11259,7 +12390,7 @@ Date: Thu Jul 9 15:42:39 2009 +0200
Fixed bug related to reconfiguration of BGP with MD5 passwords.
commit dd7b4a13848f5a92b403a563e8e27a3cbdfc937f
-Merge: d1abbeac bffd4c0b
+Merge: d1abbea bffd4c0
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Mon Jul 6 23:10:33 2009 +0200
@@ -11328,7 +12459,7 @@ Date: Fri Jun 26 13:33:41 2009 +0200
nobody noticed it already.
commit 5004d02cb9df1f3ee231632a8e89929f4eb4f088
-Merge: 1876e18d 99355da1
+Merge: 1876e18 99355da
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Fri Jun 26 01:06:12 2009 +0200
@@ -11571,7 +12702,7 @@ Date: Fri May 29 13:32:24 2009 +0200
Add 'primary' configuration option.
commit 7c3d06b087946cbea4affa4a814e72b7a3556833
-Merge: f571473e 4c2507da
+Merge: f571473 4c2507d
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Thu May 28 13:58:51 2009 +0200
@@ -11657,7 +12788,7 @@ Date: Fri May 22 00:26:30 2009 +0200
Better checks for M4 in configure.
commit 6c84554b671fce473fe333ab3d8b548a0768882b
-Merge: f434d191 4d176e14
+Merge: f434d19 4d176e1
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Thu May 21 09:26:59 2009 +0200
@@ -12426,7 +13557,7 @@ Date: Tue Oct 28 16:10:42 2008 +0100
Fixes some sloppiness of commit a3b70dc499b64f41aa776b5b4afee5c7bfb8dfa6.
commit a98fbf0f12b5e83e25afa0f585ca6a4d4ac5f6bf
-Merge: a3b70dc4 1567edea
+Merge: a3b70dc 1567ede
Author: Ondrej Zajicek <santiago@crfreenet.org>
Date: Mon Oct 27 00:20:22 2008 +0100
@@ -13095,17 +14226,17 @@ Author: Ondrej Filip <feela@network.cz>
Date: Fri Jun 25 16:39:53 2004 +0000
A lot of changes:
- - metric is 3 byte long now
- - summary lsa originating
- - more OSPF areas possible
- - virtual links
- - better E1/E2 routes handling
- - some bug fixes..
+ - metric is 3 byte long now
+ - summary lsa originating
+ - more OSPF areas possible
+ - virtual links
+ - better E1/E2 routes handling
+ - some bug fixes..
I have to do:
- - md5 auth (last mandatory item from rfc2328)
- - !!!!DEBUG!!!!! (mainly virtual link system has probably a lot of bugs)
- - 2328 appendig E
+ - md5 auth (last mandatory item from rfc2328)
+ - !!!!DEBUG!!!!! (mainly virtual link system has probably a lot of bugs)
+ - 2328 appendig E
commit 5ed68e46d781f8a14d3ef3ffd7fe3afc4a62260e
Author: Ondrej Filip <feela@network.cz>
@@ -14550,7 +15681,7 @@ Author: Ondrej Filip <feela@network.cz>
Date: Fri Aug 25 12:26:03 2000 +0000
Minor bug. It does not work on non RH systems. Thanx to
- Craig Bourne <cbourne@cbourne.com>
+ Craig Bourne <cbourne@cbourne.com>
commit 6cf7e97abe05ee8943f8a2d300e1c46038b95df1
Author: Ondrej Filip <feela@network.cz>
@@ -15548,7 +16679,7 @@ Date: Mon Jun 5 02:23:20 2000 +0000
Many bugfixes (I don't remember all of them):
Added link ID calculation for external routes with same prefix but
- different mask.
+ different mask.
Bugfix in NET lsa origination.
Bugfix in NET hashing.
Bugfix in LSA installing.
@@ -16683,11 +17814,11 @@ Date: Mon May 29 10:32:37 2000 +0000
Added skeleton for subchapters on all the protocols. Each subchapter should
contain:
- Introduction (brief intro to the protocol, where should it be used,
- references to the relevant standards)
- Configuration
- Attributes
- Example
+ Introduction (brief intro to the protocol, where should it be used,
+ references to the relevant standards)
+ Configuration
+ Attributes
+ Example
Added a more detailed description of RIP attributes.
@@ -18235,8 +19366,8 @@ Date: Sat Apr 29 15:45:30 2000 +0000
Mj, could you provide example of static config for ipv6 that is _not_
rejected by checks? I tried this and got rejected.
- route 62:168::/32 via 62:169::;
- route 1:2::/32 via 1:3::;
+ route 62:168::/32 via 62:169::;
+ route 1:2::/32 via 1:3::;
commit 46cdc7e20faaf922431a157bcb0f82a45b7aa2d2
Author: Martin Mares <mj@ucw.cz>
@@ -19180,10 +20311,10 @@ Date: Sat Apr 1 10:19:47 2000 +0000
set to EAP_xxx) and also a callback for naming and formatting of attributes.
The callback can return one of the following results:
- GA_UNKNOWN Attribute not recognized.
- GA_NAME Attribute name recognized and put to the buffer,
- generic code should format the value.
- GA_FULL Both attribute name and value put to the buffer.
+ GA_UNKNOWN Attribute not recognized.
+ GA_NAME Attribute name recognized and put to the buffer,
+ generic code should format the value.
+ GA_FULL Both attribute name and value put to the buffer.
Please update protocols generating dynamic attributes to provide
the attr_class and formatting hook.
@@ -20140,9 +21271,9 @@ Date: Mon Jan 17 11:52:50 2000 +0000
Separated `official protocol names' used in status dumps from name templates
used for automatic generation of instance names.
- protocol->name is the official name
- protocol->template is the name template (usually "name%d"),
- should be all lowercase.
+ protocol->name is the official name
+ protocol->template is the name template (usually "name%d"),
+ should be all lowercase.
Updated all protocols to define the templates, checked that their configuration
grammar includes proto_name which generates the name and interns it in the
@@ -20793,8 +21924,8 @@ Date: Wed Nov 17 12:14:44 1999 +0000
To define a new command, just add a new rule to the gramar:
CF_CLI(COMMAND NAME, arguments, help-args, help-text) {
- what-should-the-command-do
- } ;
+ what-should-the-command-do
+ } ;
where <arguments> are appended to the RHS of the rule, <help-args> is the
argument list as shown in the help and <help-text> is description of the
command for the help.
@@ -20812,8 +21943,8 @@ Date: Wed Nov 17 12:14:44 1999 +0000
Functions to call inside command handlers:
cli_printf(cli, code, printf-args) -- print text to CLI connection,
- <code> is message code as assigned in doc/reply_codes or a negative
- one if it's a continuation line.
+ <code> is message code as assigned in doc/reply_codes or a negative
+ one if it's a continuation line.
cli_msg(code, printf-args) -- the same for this_cli.
Use 'sock -x bird.ctl' for connecting to the CLI until a client is written.
@@ -21043,9 +22174,9 @@ Date: Sun Oct 31 17:47:47 1999 +0000
to exist). This macro automatically creates a skeleton rule for the command,
you only need to append arguments as in:
- CF_CLI(STEAL MONEY, <$>, [[Steal <$> US dollars or equivalent in any other currency]]): NUM {
- cli_msg(0, "%d$ stolen", $3);
- } ;
+ CF_CLI(STEAL MONEY, <$>, [[Steal <$> US dollars or equivalent in any other currency]]): NUM {
+ cli_msg(0, "%d$ stolen", $3);
+ } ;
Also don't forget to reset lexer state between inputs.
@@ -22043,12 +23174,12 @@ Date: Mon Apr 5 20:25:03 1999 +0000
-- instead use a pool pointer passed to all related functions.
o Split rte_update to three functions:
- rte_update The front end: handles all checking, inbound
- filtering and calls rte_recalculate() for the
- final version of the route.
- rte_recalculate Update the table according to already filtered route.
- rte_announce Announce routing table changes to all protocols,
- passing them through export filters and so on.
+ rte_update The front end: handles all checking, inbound
+ filtering and calls rte_recalculate() for the
+ final version of the route.
+ rte_recalculate Update the table according to already filtered route.
+ rte_announce Announce routing table changes to all protocols,
+ passing them through export filters and so on.
The interface has _not_ changed -- still call rte_update() and it will
do the rest for you automagically.
@@ -22060,9 +23191,9 @@ Date: Mon Apr 5 20:17:59 1999 +0000
Added some new protocol hooks (look at the comments for better explanation):
- make_tmp_attrs Convert inline attributes to ea_list
- store_tmp_attrs Convert ea_list to inline attributes
- import_control Pre-import decisions
+ make_tmp_attrs Convert inline attributes to ea_list
+ store_tmp_attrs Convert ea_list to inline attributes
+ import_control Pre-import decisions
commit 5056c559c4eb253a4eee10cf35b694faec5265eb
Author: Martin Mares <mj@ucw.cz>
@@ -22071,17 +23202,17 @@ Date: Mon Apr 5 20:15:31 1999 +0000
Changed syntax of attaching filters to protocols to hopefully the final
version:
- EXPORT <filter-spec> for outbound routes (i.e., those announced
- by BIRD to the rest of the world).
- IMPORT <filter-spec> for inbound routes (i.e., those imported
- by BIRD from the rest of the world).
+ EXPORT <filter-spec> for outbound routes (i.e., those announced
+ by BIRD to the rest of the world).
+ IMPORT <filter-spec> for inbound routes (i.e., those imported
+ by BIRD from the rest of the world).
where <filter-spec> is one of:
- ALL pass all routes
- NONE drop all routes
- FILTER <name> use named filter
- FILTER { <filter> } use explicitly defined filter
+ ALL pass all routes
+ NONE drop all routes
+ FILTER <name> use named filter
+ FILTER { <filter> } use explicitly defined filter
For all protocols, the default is IMPORT ALL, EXPORT NONE. This includes
the kernel protocol, so that you need to add EXPORT ALL to get the previous
@@ -22270,11 +23401,11 @@ Date: Sat Mar 27 22:51:05 1999 +0000
Available configurations:
o linux-20: Old Linux interface via /proc/net/route (selected by default
- on pre-2.1 kernels).
+ on pre-2.1 kernels).
o linux-21: Old Linux interface, but device routes handled by the
- kernel (selected by default for 2.1 and newer kernels).
+ kernel (selected by default for 2.1 and newer kernels).
o linux-22: Linux with Netlink (I play with it a lot yet, so it isn't
- a default).
+ a default).
o linux-ipv6: Prototype config for IPv6 on Linux. Not functional yet.
commit 7dc4827c968053e45bcb7f145e9986eeb20c993b
@@ -22451,7 +23582,7 @@ Date: Thu Mar 4 14:23:32 1999 +0000
To build BIRD with Netlink support, just configure it with
- ./configure --with-sysconfig=linux-21
+ ./configure --with-sysconfig=linux-21
After it will be tested well enough, I'll probably make it a default
for 2.2 kernels (and rename it to linux-22 :)).
@@ -22836,21 +23967,21 @@ Date: Thu Feb 11 21:18:26 1999 +0000
be linked in at most one event list. For most purposes, just use the
global event list handled by the following functions:
- ev_schedule Schedule event to be called at the next event
- scheduling point. If the event was already
- scheduled, it's just re-linked to the end of the list.
- ev_postpone Postpone an already scheduled event, so that it
- won't get called. Postponed events can be scheduled
- again by ev_schedule().
+ ev_schedule Schedule event to be called at the next event
+ scheduling point. If the event was already
+ scheduled, it's just re-linked to the end of the list.
+ ev_postpone Postpone an already scheduled event, so that it
+ won't get called. Postponed events can be scheduled
+ again by ev_schedule().
You can also create custom event lists to build your own synchronization
primitives. Just use:
- ev_init_list to initialize an event list
- ev_enqueue to schedule event on specified event list
- ev_postpone works as well for custom lists
- ev_run_list to run all events on your custom list
- ev_run to run a specific event and dequeue it
+ ev_init_list to initialize an event list
+ ev_enqueue to schedule event on specified event list
+ ev_postpone works as well for custom lists
+ ev_run_list to run all events on your custom list
+ ev_run to run a specific event and dequeue it
commit edf62ba13fa6a74447d7ad44b23acbee964731bc
Author: Pavel Machek <pavel@ucw.cz>
@@ -23039,16 +24170,16 @@ Date: Sat Jan 9 15:02:11 1999 +0000
To compile BIRD, you now need to do:
- autoconf # Create configure from configure.in
- ./configure # Run configure script
- make # Compile everything
+ autoconf # Create configure from configure.in
+ ./configure # Run configure script
+ make # Compile everything
Configuration files:
- sysdep/config.h Master config file
- sysdep/autoconf.h Parameters determined by configure script
- sysdep/cf/*.h Fixed system configuration we're unable
- to guess.
+ sysdep/config.h Master config file
+ sysdep/autoconf.h Parameters determined by configure script
+ sysdep/cf/*.h Fixed system configuration we're unable
+ to guess.
Makefiles are still the original ones, but this will change soon.
@@ -23171,9 +24302,9 @@ Date: Sat Dec 19 11:51:47 1998 +0000
we can use very simple function which is monotonic with respect
to re-hashing:
- n ^= n >> 16;
- n ^= n << 10;
- h = (n >> (16 - o)) & ((1 << o) - 1);
+ n ^= n >> 16;
+ n ^= n << 10;
+ h = (n >> (16 - o)) & ((1 << o) - 1);
where o is table order. Statistical analysis for both backbone routing
table and local OSPF routing tables gives values near theoretical
@@ -23820,7 +24951,7 @@ Date: Tue Oct 13 14:32:18 1998 +0000
I prefer to have broken drivers than completely stupid ones...
- Linus Torvalds
+ Linus Torvalds
Rip now uses main routing table properly: entries are stored directly
into main routing table and we are relying on core to call our's
@@ -23950,10 +25081,10 @@ Date: Wed Jun 17 14:31:36 1998 +0000
Added local version of sprintf (bsprintf and bvsprintf) offering few new
format strings:
- %I IP address
- %#I IP address in hexadecimal
- %1I IP address padded to full length
- %m strerror(errno)
+ %I IP address
+ %#I IP address in hexadecimal
+ %1I IP address padded to full length
+ %m strerror(errno)
commit 97d858c590998786d4d8a16b5c1f657800d74736
Author: Martin Mares <mj@ucw.cz>
diff --git a/Makefile.in b/Makefile.in
index 0ecd6811..e6dbd572 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -65,7 +65,8 @@ endif
docgoals := docs userdocs progdocs
testgoals := check test tests tests_run
cleangoals := clean distclean testsclean
-.PHONY: all daemon cli $(docgoals) $(testgoals) $(cleangoals) tags cscope
+.PHONY: all daemon cli $(docgoals) $(testgoals) $(cleangoals) tags cscope prepare
+
all: daemon cli
daemon: $(daemon)
@@ -76,6 +77,8 @@ $(daemon): LIBS += $(DAEMON_LIBS)
# Include directories
dirs := client conf doc filter lib nest test $(addprefix proto/,$(protocols)) @sysdep_dirs@
+# conf/Makefile declarations needed for all other modules
+conf-lex-targets := $(addprefix $(objdir)/conf/,cf-lex.o)
conf-y-targets := $(addprefix $(objdir)/conf/,cf-parse.y keywords.h commands.h)
cf-local = $(conf-y-targets): $(s)config.Y
@@ -99,39 +102,55 @@ endef
clean = $(eval $(call clean_in,$(1)))
+# Include main Makefiles of the directories
include $(addsuffix /Makefile,$(addprefix $(srcdir)/,$(dirs)))
# Generic rules
# Object file rules
-$(objdir)/%.o: $(srcdir)/%.c $(objdir)/.dir-stamp $(objdir)/sysdep/paths.h
+$(objdir)/%.o: $(srcdir)/%.c | prepare
$(E)echo CC -o $@ -c $<
$(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -c $<
-$(objdir)/%.o: $(objdir)/%.c $(objdir)/.dir-stamp $(objdir)/sysdep/paths.h
+$(objdir)/%.o: $(objdir)/%.c | prepare
$(E)echo CC -o $@ -c $<
$(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -c $<
# Debug: Preprocessed source rules
-$(objdir)/%.E: $(srcdir)/%.c $(objdir)/.dir-stamp $(objdir)/sysdep/paths.h
+$(objdir)/%.E: $(srcdir)/%.c | prepare
$(E)echo CC -o $@ -E $<
$(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -E $<
-$(objdir)/%.E: $(objdir)/%.c $(objdir)/.dir-stamp $(objdir)/sysdep/paths.h
+$(objdir)/%.E: $(objdir)/%.c | prepare
$(E)echo CC -o $@ -E $<
$(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -E $<
# Debug: Assembler object rules
-$(objdir)/%.S: $(srcdir)/%.c $(objdir)/.dir-stamp $(objdir)/sysdep/paths.h
+$(objdir)/%.S: $(srcdir)/%.c | prepare
$(E)echo CC -o $@ -S $<
$(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -S $<
-$(objdir)/%.S: $(objdir)/%.c $(objdir)/.dir-stamp $(objdir)/sysdep/paths.h
+$(objdir)/%.S: $(objdir)/%.c | prepare
$(E)echo CC -o $@ -S $<
$(Q)$(CC) $(CFLAGS) -MMD -MP -o $@ -S $<
+# Finally include the computed dependencies:
+DEPS = $(shell find $(objdir) -name '*.d')
+
+# ## if there is at least one non-clean goal
+ifneq ($(filter-out $(cleangoals),$(MAKECMDGOALS)),)
+-include $(DEPS)
+endif
+
+# ## if the implicit goal is called
+ifeq ($(MAKECMDGOALS),)
+-include $(DEPS)
+endif
+# Rule for pre-generating all generated includables
+# before compiling any C file
+prepare: $(objdir)/sysdep/paths.h | $(objdir)/.dir-stamp
-$(objdir)/.dir-stamp:
+$(objdir)/.dir-stamp: Makefile
$(E)echo MKDIR -p $(addprefix $(objdir)/,$(dirs) doc)
$(Q)mkdir -p $(addprefix $(objdir)/,$(dirs) doc)
$(Q)touch $@
@@ -150,7 +169,7 @@ $(objdir)/sysdep/paths.h: Makefile
tests_targets_ok = $(addsuffix .ok,$(tests_targets))
-$(tests_targets): %: %.o $(tests_objs)
+$(tests_targets): %: %.o $(tests_objs) | prepare
$(E)echo LD $(LDFLAGS) -o $@ $< "..." $(LIBS)
$(Q)$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
@@ -165,16 +184,6 @@ check: tests tests_run
tests: $(tests_targets)
tests_run: $(tests_targets_ok)
-# Finally include the computed dependencies
-
-ifneq ($(filter-out $(cleangoals),$(MAKECMDGOALS)),)
--include $(shell find $(objdir) -name "*.d")
-endif
-
-ifeq ($(MAKECMDGOALS),)
--include $(shell find $(objdir) -name "*.d")
-endif
-
tags:
cd $(srcdir) ; etags -lc `find $(dirs) -name *.[chY]`
diff --git a/NEWS b/NEWS
index dbde0cf4..4cf72365 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,21 @@
+Version 2.0.5 (2019-08-01)
+ o OSPF Graceful restart (RFC 3623, RFC 5187)
+ o BGP: Dynamic BGP
+ o BGP: Promiscuous ASN mode
+ o BGP: Mandatory option for channels
+ o BFD: Support for VRFs
+ o Graceful restart command
+ o Redesigned filtering code
+ o Many bugfixes
+
+ Notes:
+
+ Previous version introduced an error in handling of OSPF NSSA-LSA, causing
+ compatibility issues with proper implementations. The error is fixed in this
+ version, therefore there are compatibility issues in OSPF NSSA areas between
+ this and previous version.
+
+
Version 2.0.4 (2019-02-27)
o OSPF: Opaque LSAs (RFC 5250)
o OSPF: DN-bit handling (RFC 4576)
diff --git a/aclocal.m4 b/aclocal.m4
index c401d447..1613d680 100644
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -1,6 +1,25 @@
dnl ** Additional Autoconf tests for BIRD configure script
dnl ** (c) 1999 Martin Mares <mj@ucw.cz>
+AC_DEFUN([BIRD_CHECK_THREAD_LOCAL],
+[
+ AC_CACHE_CHECK(
+ [whether _Thread_local is known],
+ [bird_cv_thread_local],
+ AC_COMPILE_IFELSE([
+ AC_LANG_PROGRAM(
+ [
+ _Thread_local static int x = 42;
+ ],
+ []
+ )
+ ],
+ [bird_cv_thread_local=yes],
+ [bird_cv_thread_local=no]
+ )
+ )
+])
+
AC_DEFUN([BIRD_CHECK_PTHREADS],
[
bird_tmp_cflags="$CFLAGS"
@@ -131,6 +150,30 @@ AC_DEFUN([BIRD_CHECK_ANDROID_LOG],
)
])
+AC_DEFUN([BIRD_CHECK_LTO],
+[
+ bird_tmp_cflags="$CFLAGS"
+ bird_tmp_ldflags="$LDFLAGS"
+ CFLAGS="-flto"
+ LDFLAGS="-flto=4"
+
+ AC_CACHE_CHECK(
+ [whether link time optimizer is available],
+ [bird_cv_c_lto],
+ [
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM()],
+ [bird_cv_c_lto=yes],
+ [bird_cv_c_lto=no]
+ )
+ ]
+ )
+
+ CFLAGS="$bird_tmp_cflags"
+ LDFLAGS="$bird_tmp_ldflags"
+])
+
+
AC_DEFUN([BIRD_CHECK_GCC_OPTION],
[
bird_tmp_cflags="$CFLAGS"
diff --git a/bird-gdb.py b/bird-gdb.py
new file mode 100644
index 00000000..a85ef8c6
--- /dev/null
+++ b/bird-gdb.py
@@ -0,0 +1,153 @@
+class BIRDPrinter:
+ def __init__(self, val):
+ self.val = val
+
+ @classmethod
+ def lookup(cls, val):
+ if val.type.code != cls.typeCode:
+ return None
+ if val.type.tag != cls.typeTag:
+ return None
+
+ return cls(val)
+
+
+class BIRDFValPrinter(BIRDPrinter):
+ "Print BIRD\s struct f_val"
+ typeCode = gdb.TYPE_CODE_STRUCT
+ typeTag = "f_val"
+
+ codemap = {
+ "T_INT": "i",
+ "T_BOOL": "i",
+ "T_PAIR": "i",
+ "T_QUAD": "i",
+ "T_ENUM_RTS": "i",
+ "T_ENUM_BGP_ORIGIN": "i",
+ "T_ENUM_SCOPE": "i",
+ "T_ENUM_RTC": "i",
+ "T_ENUM_RTD": "i",
+ "T_ENUM_ROA": "i",
+ "T_ENUM_NETTYPE": "i",
+ "T_ENUM_RA_PREFERENCE": "i",
+ "T_IP": "ip",
+ "T_NET": "net",
+ "T_STRING": "s",
+ "T_PATH_MASK": "path_mask",
+ "T_PATH": "ad",
+ "T_CLIST": "ad",
+ "T_EC": "ec",
+ "T_ECLIST": "ad",
+ "T_LC": "lc",
+ "T_LCLIST": "ad",
+ "T_RD": "ec",
+ "T_PATH_MASK_ITEM": "pmi",
+ "T_SET": "t",
+ "T_PREFIX_SET": "ti",
+ }
+
+ def to_string(self):
+ code = self.val['type']
+ if code.type.code != gdb.TYPE_CODE_ENUM or code.type.tag != "f_type":
+ raise Exception("Strange 'type' element in f_val")
+
+ if str(code) == "T_VOID":
+ return "T_VOID"
+ else:
+ return "(%(c)s) %(v)s" % { "c": code, "v": self.val['val'][self.codemap[str(code)]] }
+
+ def display_hint(self):
+ return "map"
+
+class BIRDFValStackPrinter(BIRDPrinter):
+ "Print BIRD's struct f_val_stack"
+ typeCode = gdb.TYPE_CODE_STRUCT
+ typeTag = "f_val_stack"
+
+ def to_string(self):
+ cnt = self.val['cnt']
+ return ("Value stack (%(cnt)d):\n\t" % { "cnt": cnt }) + \
+ "\n\t".join([ (".val[%(n) 3d] = " % { "n": n}) + str(self.val['val'][n]) for n in range(cnt-1, -1, -1) ])
+
+ def display_hint(self):
+ return "map"
+
+class BIRDFInstPrinter(BIRDPrinter):
+ "Print BIRD's struct f_inst"
+ typeCode = gdb.TYPE_CODE_STRUCT
+ typeTag = "f_inst"
+
+ def to_string(self):
+ code = self.val['fi_code']
+ if str(code) == "FI_NOP":
+ return str(code) + ": " + str(self.val.cast(gdb.lookup_type("const char [%(siz)d]" % { "siz": self.val.type.sizeof })))
+ return "%(code)s:\t%(lineno) 6dL\t%(size)6dS\tnext = %(next)s: .i_%(code)s = %(union)s" % {
+ "code": str(code),
+ "lineno": self.val['lineno'],
+ "size": self.val['size'],
+ "next": str(self.val['next']),
+ "union": str(self.val['i_' + str(code)])
+ }
+
+# def children(self): # children iterator
+ def display_hint(self):
+ return "map"
+
+class BIRDFLineItemPrinter(BIRDPrinter):
+ "Print BIRD's struct f_line_item"
+ typeCode = gdb.TYPE_CODE_STRUCT
+ typeTag = "f_line_item"
+
+ def to_string(self):
+ code = self.val['fi_code']
+ if str(code) == "FI_NOP":
+ return str(code) + ": " + str(self.val.cast(gdb.lookup_type("const char [%(siz)d]" % { "siz": self.val.type.sizeof })))
+ return "%(code)s:\t%(lineno) 6dL\t%(flags)2dF: .i_%(code)s = %(union)s" % {
+ "code": str(code),
+ "lineno": self.val['lineno'],
+ "flags": self.val['flags'],
+ "union": str(self.val['i_' + str(code)])
+ }
+
+class BIRDFLinePrinter(BIRDPrinter):
+ "Print BIRD's struct f_line"
+ typeCode = gdb.TYPE_CODE_STRUCT
+ typeTag = "f_line"
+
+ def to_string(self):
+ cnt = self.val['len']
+ return ("FLine (%(cnt)d, args=%(args)d): " % { "cnt": cnt, "args" : self.val['args'] } + \
+ ", ".join([
+ ".items[%(n) 3d] = %(code)s" % {
+ "n": n,
+ "code": str(self.val['items'][n]['fi_code']),
+ } if n % 8 == 0 else str(self.val['items'][n]['fi_code']) for n in range(cnt)]))
+
+
+class BIRDFExecStackPrinter(BIRDPrinter):
+ "Print BIRD's struct f_exec_stack"
+ typeCode = gdb.TYPE_CODE_STRUCT
+ typeTag = "f_exec_stack"
+
+ def to_string(self):
+ cnt = self.val['cnt']
+ return ("Exec stack (%(cnt)d):\n\t" % { "cnt": cnt }) + \
+ "\n\t".join([ ".item[%(n) 3d] = %(retflag)d V%(ventry) 3d P%(pos) 4d %(line)s" % {
+ "retflag": self.val['item'][n]['emask'],
+ "ventry": self.val['item'][n]['ventry'],
+ "pos": self.val['item'][n]['pos'],
+ "line": str(self.val['item'][n]['line'].dereference()),
+ "n": n
+ } for n in range(cnt-1, -1, -1) ])
+
+def register_printers(objfile):
+ objfile.pretty_printers.append(BIRDFInstPrinter.lookup)
+ objfile.pretty_printers.append(BIRDFValPrinter.lookup)
+ objfile.pretty_printers.append(BIRDFValStackPrinter.lookup)
+ objfile.pretty_printers.append(BIRDFLineItemPrinter.lookup)
+ objfile.pretty_printers.append(BIRDFLinePrinter.lookup)
+ objfile.pretty_printers.append(BIRDFExecStackPrinter.lookup)
+
+register_printers(gdb.current_objfile())
+
+print("BIRD pretty printers loaded OK.")
diff --git a/client/Makefile b/client/Makefile
index fccb8346..4f972815 100644
--- a/client/Makefile
+++ b/client/Makefile
@@ -2,8 +2,7 @@ src := commands.c util.c client.c
obj := $(src-o-files)
$(all-client)
-
-$(o)commands.o: $(objdir)/conf/commands.h
+$(conf-y-targets): $(s)cmds.Y
$(exedir)/birdc: $(o)birdc.o
$(exedir)/birdc: LIBS += $(CLIENT_LIBS)
diff --git a/client/cmds.m4 b/client/cmds.Y
index 1cbb4f92..1cbb4f92 100644
--- a/client/cmds.m4
+++ b/client/cmds.Y
diff --git a/conf/Makefile b/conf/Makefile
index 984624b4..19db9c2c 100644
--- a/conf/Makefile
+++ b/conf/Makefile
@@ -10,12 +10,12 @@ BISON_DEBUG=-t
#FLEX_DEBUG=-d
endif
-$(conf-y-targets): $(s)confbase.Y $(s)flowspec.Y
- $(M4) $(M4FLAGS) -P $| $^ >$@
+$(o)cf-parse.y: $(s)gen_parser.m4
+$(o)keywords.h: $(s)gen_keywords.m4
+$(o)commands.h: $(s)gen_commands.m4
-$(o)cf-parse.y: | $(s)gen_parser.m4
-$(o)keywords.h: | $(s)gen_keywords.m4
-$(o)commands.h: | $(s)gen_commands.m4 $(srcdir)/client/cmds.m4
+$(conf-y-targets): $(s)confbase.Y $(s)flowspec.Y
+ $(M4) $(M4FLAGS) -P $(if $(word 2,$(filter %.m4,$^)),$(error "Too many M4 scripts for one target"),$(filter %.m4,$^)) $(filter %.Y,$^) >$@
$(o)cf-parse.tab.h: $(o)cf-parse.tab.c
@@ -25,9 +25,10 @@ $(o)cf-parse.tab.c: $(o)cf-parse.y
$(o)cf-lex.c: $(s)cf-lex.l
$(FLEX) $(FLEX_DEBUG) -f -s -B -8 -Pcf_ -o$@ $<
-$(o)cf-lex.o: $(o)cf-parse.tab.h $(o)keywords.h
$(o)cf-lex.o: CFLAGS+=-Wno-sign-compare -Wno-unused-function
+prepare: $(o)keywords.h $(o)commands.h $(o)cf-parse.tab.h
+
$(addprefix $(o), cf-parse.y keywords.h commands.h cf-parse.tab.h cf-parse.tab.c cf-lex.c): $(objdir)/.dir-stamp
$(call clean,cf-parse.tab.h cf-parse.tab.c cf-parse.y keywords.h commands.h cf-lex.c cf-parse.output)
diff --git a/conf/cf-lex.l b/conf/cf-lex.l
index 9bbb3660..1d6cae2c 100644
--- a/conf/cf-lex.l
+++ b/conf/cf-lex.l
@@ -45,6 +45,7 @@
#include "nest/route.h"
#include "nest/protocol.h"
#include "filter/filter.h"
+#include "filter/f-inst.h"
#include "conf/conf.h"
#include "conf/cf-parse.tab.h"
#include "lib/string.h"
@@ -64,7 +65,7 @@ struct keyword {
#endif
-static uint cf_hash(byte *c);
+static uint cf_hash(const byte *c);
#define KW_KEY(n) n->name
#define KW_NEXT(n) n->next
@@ -87,7 +88,7 @@ HASH_DEFINE_REHASH_FN(SYM, struct symbol)
HASH(struct keyword) kw_hash;
-static struct sym_scope *conf_this_scope;
+struct sym_scope *conf_this_scope;
linpool *cfg_mem;
@@ -95,6 +96,15 @@ int (*cf_read_hook)(byte *buf, unsigned int max, int fd);
struct include_file_stack *ifs;
static struct include_file_stack *ifs_head;
+#define QUOTED_BUFFER_SIZE 4096
+static BUFFER_(char) quoted_buffer;
+static char quoted_buffer_data[QUOTED_BUFFER_SIZE];
+static inline void quoted_buffer_init(void) {
+ quoted_buffer.used = 0;
+ quoted_buffer.size = QUOTED_BUFFER_SIZE;
+ quoted_buffer.data = quoted_buffer_data;
+}
+
#define MAX_INCLUDE_DEPTH 8
#define YY_INPUT(buf,result,max) result = cf_read_hook(buf, max, ifs->fd);
@@ -105,6 +115,8 @@ static struct include_file_stack *ifs_head;
static void cf_include(char *arg, int alen);
static int check_eof(void);
+static enum yytokentype cf_lex_symbol(const char *data);
+
%}
%option noyywrap
@@ -112,24 +124,26 @@ static int check_eof(void);
%option nounput
%option noreject
-%x COMMENT CCOMM CLI
+%x COMMENT CCOMM CLI QUOTED APOSTROPHED INCLUDE
ALPHA [a-zA-Z_]
DIGIT [0-9]
XIGIT [0-9a-fA-F]
ALNUM [a-zA-Z_0-9]
WHITE [ \t]
-include ^{WHITE}*include{WHITE}*\".*\"{WHITE}*;
%%
-{include} {
- char *start, *end;
-
+^{WHITE}*include{WHITE}*\" {
if (!ifs->depth)
cf_error("Include not allowed in CLI");
- start = strchr(yytext, '"');
- start++;
+ BEGIN(INCLUDE);
+}
+
+<INCLUDE>[^"\n]+["]{WHITE}*; {
+ char *start, *end;
+
+ start = yytext;
end = strchr(start, '"');
*end = 0;
@@ -138,15 +152,23 @@ include ^{WHITE}*include{WHITE}*\".*\"{WHITE}*;
cf_error("Include with empty argument");
cf_include(start, end-start);
+
+ BEGIN(INITIAL);
}
+<INCLUDE>["] cf_error("Include with empty argument");
+<INCLUDE>. cf_error("Unterminated include");
+<INCLUDE>\n cf_error("Unterminated include");
+<INCLUDE><<EOF>> cf_error("Unterminated include");
+
+
{DIGIT}+:{DIGIT}+ {
uint len1 UNUSED, len2;
u64 l;
char *e;
errno = 0;
- l = strtoul(yytext, &e, 10);
+ l = bstrtoul10(yytext, &e);
if (e && (*e != ':') || (errno == ERANGE) || (l >> 32))
cf_error("ASN out of range");
@@ -164,7 +186,7 @@ include ^{WHITE}*include{WHITE}*\".*\"{WHITE}*;
}
errno = 0;
- l = strtoul(e+1, &e, 10);
+ l = bstrtoul10(e+1, &e);
if (e && *e || (errno == ERANGE) || (l >> len2))
cf_error("Number out of range");
cf_lval.i64 |= l;
@@ -191,13 +213,13 @@ include ^{WHITE}*include{WHITE}*\".*\"{WHITE}*;
}
errno = 0;
- l = strtoul(yytext+2, &e, 10);
+ l = bstrtoul10(yytext+2, &e);
if (e && (*e != ':') || (errno == ERANGE) || (l >> len1))
cf_error("ASN out of range");
cf_lval.i64 |= ((u64) l) << len2;
errno = 0;
- l = strtoul(e+1, &e, 10);
+ l = bstrtoul10(e+1, &e);
if (e && *e || (errno == ERANGE) || (l >> len2))
cf_error("Number out of range");
cf_lval.i64 |= l;
@@ -219,7 +241,7 @@ include ^{WHITE}*include{WHITE}*\".*\"{WHITE}*;
cf_lval.i64 |= ((u64) ip4_to_u32(ip4)) << 16;
errno = 0;
- l = strtoul(e, &e, 10);
+ l = bstrtoul10(e, &e);
if (e && *e || (errno == ERANGE) || (l >> 16))
cf_error("Number out of range");
cf_lval.i64 |= l;
@@ -243,7 +265,7 @@ include ^{WHITE}*include{WHITE}*\".*\"{WHITE}*;
char *e;
unsigned long int l;
errno = 0;
- l = strtoul(yytext+2, &e, 16);
+ l = bstrtoul16(yytext+2, &e);
if (e && *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l)
cf_error("Number out of range");
cf_lval.i = l;
@@ -254,7 +276,7 @@ include ^{WHITE}*include{WHITE}*\".*\"{WHITE}*;
char *e;
unsigned long int l;
errno = 0;
- l = strtoul(yytext, &e, 10);
+ l = bstrtoul10(yytext, &e);
if (e && *e || errno == ERANGE || (unsigned long int)(unsigned int) l != l)
cf_error("Number out of range");
cf_lval.i = l;
@@ -266,26 +288,23 @@ else: {
return ELSECOL;
}
-({ALPHA}{ALNUM}*|[']({ALNUM}|[-]|[\.]|[:])*[']) {
- if(*yytext == '\'') {
- yytext[yyleng-1] = 0;
- yytext++;
- }
+['] {
+ BEGIN(APOSTROPHED);
+ quoted_buffer_init();
+}
- struct keyword *k = HASH_FIND(kw_hash, KW, yytext);
- if (k)
- {
- if (k->value > 0)
- return k->value;
- else
- {
- cf_lval.i = -k->value;
- return ENUM;
- }
- }
+<APOSTROPHED>{ALNUM}|[-]|[.:] BUFFER_PUSH(quoted_buffer) = yytext[0];
+<APOSTROPHED>\n cf_error("Unterminated symbol");
+<APOSTROPHED><<EOF>> cf_error("Unterminated symbol");
+<APOSTROPHED>['] {
+ BEGIN(INITIAL);
+ BUFFER_PUSH(quoted_buffer) = 0;
+ return cf_lex_symbol(quoted_buffer_data);
+}
+<APOSTROPHED>. cf_error("Invalid character in apostrophed symbol");
- cf_lval.s = cf_get_symbol(yytext);
- return SYM;
+({ALPHA}{ALNUM}*) {
+ return cf_lex_symbol(yytext);
}
<CLI>(.|\n) {
@@ -301,14 +320,21 @@ else: {
return yytext[0];
}
-["][^"\n]*["] {
- yytext[yyleng-1] = 0;
- cf_lval.t = cfg_strdup(yytext+1);
- yytext[yyleng-1] = '"';
+["] {
+ BEGIN(QUOTED);
+ quoted_buffer_init();
+}
+
+<QUOTED>\n cf_error("Unterminated string");
+<QUOTED><<EOF>> cf_error("Unterminated string");
+<QUOTED>["] {
+ BEGIN(INITIAL);
+ BUFFER_PUSH(quoted_buffer) = 0;
+ cf_lval.t = cfg_strdup(quoted_buffer_data);
return TEXT;
}
-["][^"\n]*\n cf_error("Unterminated string");
+<QUOTED>. BUFFER_PUSH(quoted_buffer) = yytext[0];
<INITIAL,COMMENT><<EOF>> { if (check_eof()) return END; }
@@ -349,7 +375,7 @@ else: {
%%
static uint
-cf_hash(byte *c)
+cf_hash(const byte *c)
{
uint h = 13 << 24;
@@ -358,7 +384,6 @@ cf_hash(byte *c)
return h;
}
-
/*
* IFS stack - it contains structures needed for recursive processing
* of include in config files. On the top of the stack is a structure
@@ -519,7 +544,7 @@ check_eof(void)
}
static struct symbol *
-cf_new_symbol(byte *c)
+cf_new_symbol(const byte *c)
{
struct symbol *s;
@@ -527,11 +552,8 @@ cf_new_symbol(byte *c)
if (l > SYM_MAX_LEN)
cf_error("Symbol too long");
- s = cfg_alloc(sizeof(struct symbol) + l);
- s->scope = conf_this_scope;
- s->class = SYM_VOID;
- s->def = NULL;
- s->aux = 0;
+ s = cfg_allocz(sizeof(struct symbol) + l + 1);
+ *s = (struct symbol) { .scope = conf_this_scope, .class = SYM_VOID, };
strcpy(s->name, c);
if (!new_config->sym_hash.data)
@@ -539,6 +561,8 @@ cf_new_symbol(byte *c)
HASH_INSERT2(new_config->sym_hash, SYM, new_config->pool, s);
+ add_tail(&(new_config->symbols), &(s->n));
+
return s;
}
@@ -554,7 +578,7 @@ cf_new_symbol(byte *c)
* signify no match.
*/
struct symbol *
-cf_find_symbol(struct config *cfg, byte *c)
+cf_find_symbol(const struct config *cfg, const byte *c)
{
struct symbol *s;
@@ -562,6 +586,7 @@ cf_find_symbol(struct config *cfg, byte *c)
(s = HASH_FIND(cfg->sym_hash, SYM, c, 1)))
return s;
+ /* In CLI command parsing, fallback points to the current config, otherwise it is NULL. */
if (cfg->fallback &&
cfg->fallback->sym_hash.data &&
(s = HASH_FIND(cfg->fallback->sym_hash, SYM, c, 1)))
@@ -580,11 +605,33 @@ cf_find_symbol(struct config *cfg, byte *c)
* existing symbol is found.
*/
struct symbol *
-cf_get_symbol(byte *c)
+cf_get_symbol(const byte *c)
{
return cf_find_symbol(new_config, c) ?: cf_new_symbol(c);
}
+/**
+ * cf_localize_symbol - get the local instance of given symbol
+ * @sym: the symbol to localize
+ *
+ * This functions finds the symbol that is local to current scope
+ * for purposes of cf_define_symbol().
+ */
+struct symbol *
+cf_localize_symbol(struct symbol *sym)
+{
+ /* If the symbol type is void, it has been recently allocated just in this scope. */
+ if (!sym->class)
+ return sym;
+
+ /* If the scope is the current, it is already defined in this scope. */
+ if (sym->scope == conf_this_scope)
+ cf_error("Symbol already defined");
+
+ /* Not allocated here yet, doing it now. */
+ return cf_new_symbol(sym->name);
+}
+
struct symbol *
cf_default_name(char *template, int *counter)
{
@@ -604,33 +651,32 @@ cf_default_name(char *template, int *counter)
cf_error("Unable to generate default name");
}
-/**
- * cf_define_symbol - define meaning of a symbol
- * @sym: symbol to be defined
- * @type: symbol class to assign
- * @def: class dependent data
- *
- * Defines new meaning of a symbol. If the symbol is an undefined
- * one (%SYM_VOID), it's just re-defined to the new type. If it's defined
- * in different scope, a new symbol in current scope is created and the
- * meaning is assigned to it. If it's already defined in the current scope,
- * an error is reported via cf_error().
- *
- * 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.
- */
-struct symbol *
-cf_define_symbol(struct symbol *sym, int type, void *def)
+static enum yytokentype
+cf_lex_symbol(const char *data)
{
- if (sym->class)
+ /* Have we defined such a symbol? */
+ struct symbol *sym = cf_get_symbol(data);
+ cf_lval.s = sym;
+
+ if (sym->class != SYM_VOID)
+ return CF_SYM_KNOWN;
+
+ /* Is it a keyword? */
+ struct keyword *k = HASH_FIND(kw_hash, KW, data);
+ if (k)
+ {
+ if (k->value > 0)
+ return k->value;
+ else
{
- if (sym->scope == conf_this_scope)
- cf_error("Symbol already defined");
- sym = cf_new_symbol(sym->name);
+ cf_lval.i = -k->value;
+ return ENUM;
}
- sym->class = type;
- sym->def = def;
- return sym;
+ }
+
+ /* OK, undefined symbol */
+ cf_lval.s = sym;
+ return CF_SYM_UNDEFINED;
}
static void
@@ -673,7 +719,8 @@ cf_lex_init(int is_cli, struct config *c)
else
BEGIN(INITIAL);
- conf_this_scope = cfg_allocz(sizeof(struct sym_scope));
+ c->root_scope = cfg_allocz(sizeof(struct sym_scope));
+ conf_this_scope = c->root_scope;
conf_this_scope->active = 1;
}
@@ -696,6 +743,7 @@ cf_push_scope(struct symbol *sym)
conf_this_scope = s;
s->active = 1;
s->name = sym;
+ s->slots = 0;
}
/**
@@ -710,6 +758,7 @@ cf_pop_scope(void)
{
conf_this_scope->active = 0;
conf_this_scope = conf_this_scope->next;
+
ASSERT(conf_this_scope);
}
@@ -723,9 +772,6 @@ cf_pop_scope(void)
char *
cf_symbol_class_name(struct symbol *sym)
{
- if (cf_symbol_is_constant(sym))
- return "constant";
-
switch (sym->class)
{
case SYM_VOID:
@@ -740,6 +786,12 @@ cf_symbol_class_name(struct symbol *sym)
return "filter";
case SYM_TABLE:
return "routing table";
+ case SYM_ATTRIBUTE:
+ return "custom attribute";
+ case SYM_CONSTANT_RANGE:
+ return "constant";
+ case SYM_VARIABLE_RANGE:
+ return "variable";
default:
return "unknown type";
}
diff --git a/conf/conf.c b/conf/conf.c
index 439aa41d..b21d5213 100644
--- a/conf/conf.c
+++ b/conf/conf.c
@@ -98,6 +98,7 @@ config_alloc(const char *name)
memcpy(ndup, name, nlen);
init_list(&c->tests);
+ init_list(&c->symbols);
c->mrtdump_file = -1; /* Hack, this should be sysdep-specific */
c->pool = p;
c->mem = l;
@@ -258,6 +259,8 @@ config_do_commit(struct config *c, int type)
if (old_config)
old_config->obstacle_count++;
+ DBG("filter_commit\n");
+ filter_commit(c, old_config);
DBG("sysdep_commit\n");
int force_restart = sysdep_commit(c, old_config);
DBG("global_commit\n");
@@ -444,6 +447,24 @@ config_undo(void)
return CONF_PROGRESS;
}
+int
+config_status(void)
+{
+ if (shutting_down)
+ return CONF_SHUTDOWN;
+
+ if (configuring)
+ return future_cftype ? CONF_QUEUED : CONF_PROGRESS;
+
+ return CONF_DONE;
+}
+
+btime
+config_timer_status(void)
+{
+ return tm_active(config_timer) ? tm_remains(config_timer) : -1;
+}
+
extern void cmd_reconfig_undo_notify(void);
static void
@@ -474,19 +495,24 @@ config_init(void)
* for switching to an empty configuration.
*/
void
-order_shutdown(void)
+order_shutdown(int gr)
{
struct config *c;
if (shutting_down)
return;
- log(L_INFO "Shutting down");
+ if (!gr)
+ log(L_INFO "Shutting down");
+ else
+ log(L_INFO "Shutting down for graceful restart");
+
c = lp_alloc(config->mem, sizeof(struct config));
memcpy(c, config, sizeof(struct config));
init_list(&c->protos);
init_list(&c->tables);
c->shutdown = 1;
+ c->gr_down = gr;
config_commit(c, RECONFIG_HARD, 0);
shutting_down = 1;
diff --git a/conf/conf.h b/conf/conf.h
index 427569fd..21dc3fa1 100644
--- a/conf/conf.h
+++ b/conf/conf.h
@@ -24,6 +24,7 @@ struct config {
list tables; /* Configured routing tables (struct rtable_config) */
list logfiles; /* Configured log files (sysdep) */
list tests; /* Configured unit tests (f_bt_test_suite) */
+ 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) */
@@ -52,8 +53,10 @@ struct config {
int file_fd; /* File descriptor of main configuration file */
HASH(struct symbol) sym_hash; /* Lexer: symbol hash table */
struct config *fallback; /* Link to regular config for CLI parsing */
+ struct sym_scope *root_scope; /* Scope for root symbols */
int obstacle_count; /* Number of items blocking freeing of this config */
int shutdown; /* This is a pseudo-config for daemon shutdown */
+ int gr_down; /* This is a pseudo-config for graceful restart */
btime load_time; /* When we've got this configuration */
};
@@ -68,11 +71,13 @@ void config_free(struct config *);
int config_commit(struct config *, int type, uint timeout);
int config_confirm(void);
int config_undo(void);
+int config_status(void);
+btime config_timer_status(void);
void config_init(void);
void cf_error(const char *msg, ...) NORET;
void config_add_obstacle(struct config *);
void config_del_obstacle(struct config *);
-void order_shutdown(void);
+void order_shutdown(int gr);
#define RECONFIG_NONE 0
#define RECONFIG_HARD 1
@@ -103,18 +108,29 @@ void cfg_copy_list(list *dest, list *src, unsigned node_size);
extern int (*cf_read_hook)(byte *buf, uint max, int fd);
struct symbol {
+ node n; /* In list of symbols in config */
struct symbol *next;
struct sym_scope *scope;
- int class;
- int aux;
- void *aux2;
- void *def;
- char name[1];
+ int class; /* SYM_* */
+ uint flags; /* SYM_FLAG_* */
+
+ union {
+ struct proto_config *proto; /* For SYM_PROTO and SYM_TEMPLATE */
+ const struct f_line *function; /* For SYM_FUNCTION */
+ const struct filter *filter; /* For SYM_FILTER */
+ struct rtable_config *table; /* For SYM_TABLE */
+ struct f_dynamic_attr *attribute; /* For SYM_ATTRIBUTE */
+ struct f_val *val; /* For SYM_CONSTANT */
+ uint offset; /* For SYM_VARIABLE */
+ };
+
+ char name[0];
};
struct sym_scope {
struct sym_scope *next; /* Next on scope stack */
struct symbol *name; /* Name of this scope */
+ uint slots; /* Variable slots */
int active; /* Currently entered */
};
@@ -134,8 +150,11 @@ struct sym_scope {
#define SYM_CONSTANT 0x200 /* 0x200-0x2ff are variable types */
#define SYM_CONSTANT_RANGE SYM_CONSTANT ... (SYM_CONSTANT | 0xff)
-#define SYM_TYPE(s) (((struct f_val *) (s)->def)->type)
-#define SYM_VAL(s) (((struct f_val *) (s)->def)->val)
+#define SYM_TYPE(s) ((s)->val->type)
+#define SYM_VAL(s) ((s)->val->val)
+
+/* Symbol flags */
+#define SYM_FLAG_SAME 0x1 /* For SYM_FUNCTION and SYM_FILTER */
struct include_file_stack {
void *buffer; /* Internal lexer state */
@@ -152,23 +171,43 @@ struct include_file_stack {
extern struct include_file_stack *ifs;
+extern struct sym_scope *conf_this_scope;
+
int cf_lex(void);
void cf_lex_init(int is_cli, struct config *c);
void cf_lex_unwind(void);
-struct symbol *cf_find_symbol(struct config *cfg, byte *c);
+struct symbol *cf_find_symbol(const struct config *cfg, const byte *c);
-struct symbol *cf_get_symbol(byte *c);
+struct symbol *cf_get_symbol(const byte *c);
struct symbol *cf_default_name(char *template, int *counter);
-struct symbol *cf_define_symbol(struct symbol *symbol, int type, void *def);
+struct symbol *cf_localize_symbol(struct symbol *sym);
+
+/**
+ * cf_define_symbol - define meaning of a symbol
+ * @sym: symbol to be defined
+ * @type: symbol class to assign
+ * @def: class dependent data
+ *
+ * Defines new meaning of a symbol. If the symbol is an undefined
+ * one (%SYM_VOID), it's just re-defined to the new type. If it's defined
+ * in different scope, a new symbol in current scope is created and the
+ * meaning is assigned to it. If it's already defined in the current scope,
+ * an error is reported via cf_error().
+ *
+ * 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; })
+
void cf_push_scope(struct symbol *);
void cf_pop_scope(void);
char *cf_symbol_class_name(struct symbol *sym);
-static inline int cf_symbol_is_constant(struct symbol *sym)
-{ return (sym->class & 0xff00) == SYM_CONSTANT; }
-
-
/* Parser */
extern char *cf_text;
diff --git a/conf/confbase.Y b/conf/confbase.Y
index 3fd034db..75158927 100644
--- a/conf/confbase.Y
+++ b/conf/confbase.Y
@@ -33,6 +33,21 @@ check_u16(uint val)
cf_error("Value %u out of range (0-65535)", val);
}
+#define cf_assert(cond, ...) do { if (!(cond)) cf_error(__VA_ARGS__); } while (0)
+static inline void cf_assert_symbol(const struct symbol *sym, uint class) {
+ switch (class) {
+ case SYM_PROTO: cf_assert(sym->class == SYM_PROTO, "Protocol name required"); break;
+ case SYM_TEMPLATE: cf_assert(sym->class == SYM_TEMPLATE, "Protocol template name required"); break;
+ case SYM_FUNCTION: cf_assert(sym->class == SYM_FUNCTION, "Function name required"); break;
+ case SYM_FILTER: cf_assert(sym->class == SYM_FILTER, "Filter name required"); break;
+ case SYM_TABLE: cf_assert(sym->class == SYM_TABLE, "Table name required"); break;
+ case SYM_ATTRIBUTE: cf_assert(sym->class == SYM_ATTRIBUTE, "Custom attribute name required"); break;
+ case SYM_VARIABLE: cf_assert((sym->class & ~0xff) == SYM_VARIABLE, "Variable name required"); break;
+ case SYM_CONSTANT: cf_assert((sym->class & ~0xff) == SYM_CONSTANT, "Constant name required"); break;
+ default: bug("This shall not happen");
+ }
+}
+
CF_DECLS
%union {
@@ -49,13 +64,19 @@ CF_DECLS
struct rtable_config *r;
struct channel_config *cc;
struct f_inst *x;
+ struct {
+ struct f_inst *begin, *end;
+ } xp;
+ enum filter_return fret;
+ enum ec_subtype ecs;
struct f_dynamic_attr fda;
struct f_static_attr fsa;
- struct filter *f;
+ struct f_lval flv;
+ struct f_line *fl;
+ const struct filter *f;
struct f_tree *e;
struct f_trie *trie;
struct f_val v;
- struct f_path_mask *h;
struct password_item *p;
struct rt_show_data *ra;
struct sym_show_data *sd;
@@ -78,7 +99,7 @@ CF_DECLS
%token <ip4> IP4
%token <ip6> IP6
%token <i64> VPN_RD
-%token <s> SYM
+%token <s> CF_SYM_KNOWN CF_SYM_UNDEFINED
%token <t> TEXT
%type <iface> ipa_scope
@@ -90,6 +111,7 @@ CF_DECLS
%type <mls> label_stack_start label_stack
%type <t> text opttext
+%type <s> symbol
%nonassoc PREFIX_DUMMY
%left AND OR
@@ -99,6 +121,8 @@ CF_DECLS
%left '!'
%nonassoc '.'
+%start config
+
CF_KEYWORDS(DEFINE, ON, OFF, YES, NO, S, MS, US, PORT, VPN, MPLS, FROM)
CF_GRAMMAR
@@ -122,29 +146,29 @@ conf: ';' ;
conf: definition ;
definition:
- DEFINE SYM '=' term ';' {
+ DEFINE symbol '=' term ';' {
struct f_val *val = cfg_alloc(sizeof(struct f_val));
- *val = f_eval($4, cfg_mem);
- if (val->type == T_RETURN) cf_error("Runtime error");
- cf_define_symbol($2, SYM_CONSTANT | val->type, 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);
}
;
expr:
NUM
- | '(' term ')' { $$ = f_eval_int($2); }
- | SYM {
- if ($1->class != (SYM_CONSTANT | T_INT)) cf_error("Number expected");
+ | '(' term ')' { $$ = f_eval_int(f_linearize($2)); }
+ | CF_SYM_KNOWN {
+ if ($1->class != (SYM_CONSTANT | T_INT)) cf_error("Number constant expected");
$$ = SYM_VAL($1).i; }
;
-
expr_us:
expr S { $$ = $1 S_; }
| expr MS { $$ = $1 MS_; }
| expr US { $$ = $1 US_; }
;
+symbol: CF_SYM_UNDEFINED | CF_SYM_KNOWN ;
+
/* Switches */
bool:
@@ -162,15 +186,15 @@ bool:
ipa:
IP4 { $$ = ipa_from_ip4($1); }
| IP6 { $$ = ipa_from_ip6($1); }
- | SYM {
- if ($1->class != (SYM_CONSTANT | T_IP)) cf_error("IP address expected");
+ | CF_SYM_KNOWN {
+ if ($1->class != (SYM_CONSTANT | T_IP)) cf_error("IP address constant expected");
$$ = SYM_VAL($1).ip;
}
;
ipa_scope:
/* empty */ { $$ = NULL; }
- | '%' SYM { $$ = if_get_by_name($2->name); }
+ | '%' symbol { $$ = if_get_by_name($2->name); }
;
@@ -277,27 +301,27 @@ net_:
net_ip6:
net_ip6_
- | SYM {
+ | CF_SYM_KNOWN {
if (($1->class != (SYM_CONSTANT | T_NET)) || (SYM_VAL($1).net->type != NET_IP6))
- cf_error("IPv6 network expected");
+ cf_error("IPv6 network constant expected");
$$ = * SYM_VAL($1).net;
}
;
net_ip:
net_ip_
- | SYM {
+ | CF_SYM_KNOWN {
if (($1->class != (SYM_CONSTANT | T_NET)) || !net_is_ip(SYM_VAL($1).net))
- cf_error("IP network expected");
+ cf_error("IP network constant expected");
$$ = * SYM_VAL($1).net;
}
;
net_any:
net_
- | SYM {
+ | CF_SYM_KNOWN {
if ($1->class != (SYM_CONSTANT | T_NET))
- cf_error("Network expected");
+ cf_error("Network constant expected");
$$ = (net_addr *) SYM_VAL($1).net; /* Avoid const warning */
}
;
@@ -307,13 +331,13 @@ net_or_ipa:
| net_ip6_
| IP4 { net_fill_ip4(&($$), $1, IP4_MAX_PREFIX_LENGTH); }
| IP6 { net_fill_ip6(&($$), $1, IP6_MAX_PREFIX_LENGTH); }
- | SYM {
+ | CF_SYM_KNOWN {
if ($1->class == (SYM_CONSTANT | T_IP))
net_fill_ip_host(&($$), SYM_VAL($1).ip);
else if (($1->class == (SYM_CONSTANT | T_NET)) && net_is_ip(SYM_VAL($1).net))
$$ = * SYM_VAL($1).net;
else
- cf_error("IP address or network expected");
+ cf_error("IP address or network constant expected");
}
;
@@ -344,8 +368,8 @@ time:
text:
TEXT
- | SYM {
- if ($1->class != (SYM_CONSTANT | T_STRING)) cf_error("String expected");
+ | CF_SYM_KNOWN {
+ if ($1->class != (SYM_CONSTANT | T_STRING)) cf_error("String constant expected");
$$ = SYM_VAL($1).s;
}
;
diff --git a/configure b/configure
index 10981f46..017d8092 100755
--- a/configure
+++ b/configure
@@ -704,6 +704,7 @@ ac_user_opts='
enable_option_checking
enable_client
enable_debug
+enable_debug_generated
enable_memcheck
enable_pthreads
enable_libssh
@@ -1338,6 +1339,8 @@ Optional Features:
--enable-FEATURE[=ARG] include FEATURE [ARG=yes]
--enable-client enable building of BIRD client [yes]
--enable-debug enable internal debugging routines [no]
+ --enable-debug-generated
+ enable this to abstain from generating #line [no]
--enable-memcheck check memory allocations when debugging [yes]
--enable-pthreads enable POSIX threads support [try]
--enable-libssh enable LibSSH support together with RPKI [try]
@@ -2185,6 +2188,15 @@ else
fi
+# Check whether --enable-debug-generated was given.
+if test "${enable_debug_generated+set}" = set; then :
+ enableval=$enable_debug_generated;
+else
+ enable_debug_generated=no
+
+fi
+
+
# Check whether --enable-memcheck was given.
if test "${enable_memcheck+set}" = set; then :
enableval=$enable_memcheck;
@@ -3932,6 +3944,46 @@ if test -z "$GCC" ; then
as_fn_error $? "This program requires the GNU C Compiler." "$LINENO" 5
fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether _Thread_local is known" >&5
+$as_echo_n "checking whether _Thread_local is known... " >&6; }
+if ${bird_cv_thread_local+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+
+ _Thread_local static int x = 42;
+
+int
+main ()
+{
+
+
+ ;
+ return 0;
+}
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ bird_cv_thread_local=yes
+else
+ bird_cv_thread_local=no
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $bird_cv_thread_local" >&5
+$as_echo "$bird_cv_thread_local" >&6; }
+
+if test "$bird_cv_thread_local" = yes ; then
+
+$as_echo "#define HAVE_THREAD_LOCAL 1" >>confdefs.h
+
+fi
+
if test "$enable_pthreads" != no ; then
bird_tmp_cflags="$CFLAGS"
@@ -4163,11 +4215,64 @@ $as_echo "$bird_cv_c_option_fno_strict_overflow" >&6; }
fi
fi
+
+if test "$enable_debug" = no; then
+
+ bird_tmp_cflags="$CFLAGS"
+ bird_tmp_ldflags="$LDFLAGS"
+ CFLAGS="-flto"
+ LDFLAGS="-flto=4"
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether link time optimizer is available" >&5
+$as_echo_n "checking whether link time optimizer is available... " >&6; }
+if ${bird_cv_c_lto+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+ bird_cv_c_lto=yes
+else
+ bird_cv_c_lto=no
+
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $bird_cv_c_lto" >&5
+$as_echo "$bird_cv_c_lto" >&6; }
+
+ CFLAGS="$bird_tmp_cflags"
+ LDFLAGS="$bird_tmp_ldflags"
+
+fi
+
+if test "$bird_cv_c_lto" = yes; then
+ CFLAGS="$CFLAGS -flto"
+ LDFLAGS="$LDFLAGS -flto=4"
+fi
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking CFLAGS" >&5
$as_echo_n "checking CFLAGS... " >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CFLAGS" >&5
$as_echo "$CFLAGS" >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking LDFLAGS" >&5
+$as_echo_n "checking LDFLAGS... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $LDFLAGS" >&5
+$as_echo "$LDFLAGS" >&6; }
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
@@ -4635,7 +4740,7 @@ $as_echo_n "checking bison version... " >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $BISON_VERSION" >&5
$as_echo "$BISON_VERSION" >&6; }
-if test "$bird_bison_synclines" = yes; then
+if test "$bird_bison_synclines" = yes && test "$enable_debug_generated" = no; then
M4FLAGS="$M4FLAGS -s"
fi
@@ -5796,6 +5901,48 @@ fi
fi
fi
+else
+
+ bird_tmp_cflags="$CFLAGS"
+ CFLAGS=" -Wno-implicit-fallthrough"
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether CC supports -Wno-implicit-fallthrough" >&5
+$as_echo_n "checking whether CC supports -Wno-implicit-fallthrough... " >&6; }
+if ${bird_cv_c_option_wno_implicit_fallthrough+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ bird_cv_c_option_wno_implicit_fallthrough=yes
+else
+ bird_cv_c_option_wno_implicit_fallthrough=no
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $bird_cv_c_option_wno_implicit_fallthrough" >&5
+$as_echo "$bird_cv_c_option_wno_implicit_fallthrough" >&6; }
+
+ CFLAGS="$bird_tmp_cflags"
+
+
+ if test "$bird_cv_c_option_wno_implicit_fallthrough" = yes ; then
+ CFLAGS="$CFLAGS -Wno-implicit-fallthrough"
+ fi
+
fi
CLIENT=birdcl
diff --git a/configure.ac b/configure.ac
index da1a8f44..14dbcd52 100644
--- a/configure.ac
+++ b/configure.ac
@@ -18,6 +18,12 @@ AC_ARG_ENABLE([debug],
[enable_debug=no]
)
+AC_ARG_ENABLE([debug-generated],
+ [AS_HELP_STRING([--enable-debug-generated], [enable this to abstain from generating #line @<:@no@:>@])],
+ [],
+ [enable_debug_generated=no]
+)
+
AC_ARG_ENABLE([memcheck],
[AS_HELP_STRING([--enable-memcheck], [check memory allocations when debugging @<:@yes@:>@])],
[],
@@ -109,6 +115,11 @@ if test -z "$GCC" ; then
AC_MSG_ERROR([This program requires the GNU C Compiler.])
fi
+BIRD_CHECK_THREAD_LOCAL
+if test "$bird_cv_thread_local" = yes ; then
+ AC_DEFINE([HAVE_THREAD_LOCAL], [1], [Define to 1 if _Thread_local is available])
+fi
+
if test "$enable_pthreads" != no ; then
BIRD_CHECK_PTHREADS
@@ -138,9 +149,21 @@ if test "$bird_cflags_default" = yes ; then
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])
+AC_MSG_CHECKING([LDFLAGS])
+AC_MSG_RESULT([$LDFLAGS])
AC_PROG_CPP
AC_PROG_INSTALL
@@ -156,7 +179,7 @@ test -z "$M4" && AC_MSG_ERROR([M4 is missing.])
AC_MSG_CHECKING([bison version])
BIRD_CHECK_BISON_VERSION(BISON_VERSION)
AC_MSG_RESULT([$BISON_VERSION])
-if test "$bird_bison_synclines" = yes; then
+if test "$bird_bison_synclines" = yes && test "$enable_debug_generated" = no; then
M4FLAGS="$M4FLAGS -s"
fi
@@ -364,6 +387,9 @@ 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])
fi
CLIENT=birdcl
diff --git a/doc/bird.sgml b/doc/bird.sgml
index e531da40..83dec4f8 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -403,6 +403,14 @@ configured for all relevant protocols and requires protocol-specific support
(currently implemented for Kernel and BGP protocols), it is activated for
particular boot by option <cf/-R/.
+<p>Some protocols (e.g. BGP) could be restarted gracefully after both
+intentional outage and crash, while others (e.g. OSPF) after intentional outage
+only. For planned graceful restart, BIRD must be shut down by
+<ref id="cli-graceful-restart" name="graceful restart"> command instead of
+regular <ref id="cli-down" name="down"> command. In this way routing neighbors
+are notified about planned graceful restart and routes are kept in kernel table
+after shutdown.
+
<chapt>Configuration
<label id="config">
@@ -561,7 +569,7 @@ include "tablename.conf";;
can be seen (together with other symbols) using 'show symbols' command.
<tag><label id="opt-attribute">attribute <m/type/ <m/name/</tag>
- Define a custom route attribute. You can set and get it in filters like
+ 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
in import filters for export filtering purposes instead of locally
assigned BGP communities which have to be deleted in export filters.
@@ -671,14 +679,24 @@ agreement").
This is an optional description of the protocol. It is displayed as a
part of the output of 'show protocols all' command.
- <tag><label id="proto-vrf">vrf "<m/text/"</tag>
+ <tag><label id="proto-vrf">vrf "<m/text/"|default</tag>
Associate the protocol with specific VRF. The protocol will be
restricted to interfaces assigned to the VRF and will use sockets bound
- to the VRF. Appropriate VRF interface must exist on OS level. For kernel
- protocol, an appropriate table still must be explicitly selected by
- <cf/table/ option. Note that for proper VRF support it is necessary to
- use Linux kernel version at least 4.14, older versions have limited VRF
- implementation.
+ to the VRF. A corresponding VRF interface must exist on OS level. For
+ kernel protocol, an appropriate table still must be explicitly selected
+ by <cf/table/ option.
+
+ By selecting <cf/default/, the protocol is associated with the default
+ VRF; i.e., it will be restricted to interfaces not assigned to any
+ regular VRF. That is different from not specifying <cf/vrf/ at all, in
+ which case the protocol may use any interface regardless of its VRF
+ status.
+
+ Note that for proper VRF support it is necessary to use Linux kernel
+ version at least 4.14, older versions have limited VRF implementation.
+ Before Linux kernel 5.0, a socket bound to a port in default VRF collide
+ with others in regular VRFs. In BGP, this can be avoided by using
+ <ref id="bgp-strict-bind" name="strict bind"> option.
<tag><label id="proto-channel"><m/channel name/ [{<m/channel config/}]</tag>
Every channel must be explicitly stated. See the protocol-specific
@@ -1106,6 +1124,10 @@ This argument can be omitted if there exists only a single instance.
<tag><label id="cli-down">down</tag>
Shut BIRD down.
+ <tag><label id="cli-graceful-restart">graceful restart</tag>
+ Shut BIRD down for graceful restart. See <ref id="graceful-restart"
+ name="graceful restart"> section for details.
+
<tag><label id="cli-debug">debug <m/protocol/|<m/pattern/|all all|off|{ states|routes|filters|events|packets [, <m/.../] }</tag>
Control protocol debugging.
@@ -1159,7 +1181,7 @@ int var;
<p>As you can see, a filter has a header, a list of local variables, and a body.
The header consists of the <cf/filter/ keyword followed by a (unique) name of
filter. The list of local variables consists of <cf><M>type name</M>;</cf>
-pairs where each pair defines one local variable. The body consists of <cf>
+pairs where each pair declares one local variable. The body consists of <cf>
{ <M>statements</M> }</cf>. Each <m/statement/ is terminated by a <cf/;/. You
can group several statements to a single compound statement by using braces
(<cf>{ <M>statements</M> }</cf>) which is useful if you want to make a bigger
@@ -1188,7 +1210,7 @@ called like in C: <cf>name(); with_parameters(5);</cf>. Function may return
values using the <cf>return <m/[expr]/</cf> command. Returning a value exits
from current function (this is similar to C).
-<p>Filters are declared in a way similar to functions except they can't have
+<p>Filters are defined in a way similar to functions except they can't have
explicit parameters. They get a route table entry as an implicit parameter, it
is also passed automatically to any functions called. The filter must terminate
with either <cf/accept/ or <cf/reject/ statement. If there's a runtime error in
@@ -1571,11 +1593,20 @@ if 1234 = i then printn "."; else {
<label id="route-attributes">
<p>A filter is implicitly passed a route, and it can access its attributes just
-like it accesses variables. Attempts to access undefined attribute result in a
-runtime error; you can check if an attribute is defined by using the
-<cf>defined( <m>attribute</m> )</cf> operator. One notable exception to this
-rule are attributes of bgppath and *clist types, where undefined value is
-regarded as empty bgppath/*clist for most purposes.
+like it accesses variables. There are common route attributes, protocol-specific
+route attributes and custom route attributes. Most common attributes are
+mandatory (always defined), while remaining are optional. Attempts to access
+undefined attribute result in a runtime error; you can check if an attribute is
+defined by using the <cf>defined( <m>attribute</m> )</cf> operator. One notable
+exception to this rule are attributes of bgppath and *clist types, where
+undefined value is regarded as empty bgppath/*clist for most purposes.
+
+Attributes can be defined by just setting them in filters. Custom attributes
+have to be first declared by <ref id="opt-attribute" name="attribute"> global
+option. You can also undefine optional attribute back to non-existence by using
+the <cf>unset( <m/attribute/ )</cf> operator.
+
+Common route attributes are:
<descrip>
<tag><label id="rta-net"><m/prefix/ net</tag>
@@ -1642,8 +1673,8 @@ regarded as empty bgppath/*clist for most purposes.
compare internal distances to boundary routers (see below).
</descrip>
-<p>There also exist protocol-specific attributes which are described in the
-corresponding protocol sections.
+<p>Protocol-specific route attributes are described in the corresponding
+protocol sections.
<sect>Other statements
@@ -1653,7 +1684,7 @@ corresponding protocol sections.
<descrip>
<tag><label id="assignment"><m/variable/ = <m/expr/</tag>
- Set variable to a given value.
+ Set variable (or route attribute) to a given value.
<tag><label id="filter-accept-reject">accept|reject [ <m/expr/ ]</tag>
Accept or reject the route, possibly printing <cf><m>expr</m></cf>.
@@ -1887,12 +1918,11 @@ the BFD session went down).
advanced features like the echo mode or authentication are not implemented), IP
transport for BFD as defined in <rfc id="5881"> and <rfc id="5883"> and
interaction with client protocols as defined in <rfc id="5882">.
-We currently support at most one protocol instance.
<p>BFD packets are sent with a dynamic source port number. Linux systems use by
default a bit different dynamic port range than the IANA approved one
(49152-65535). If you experience problems with compatibility, please adjust
-<cf>/proc/sys/net/ipv4/ip_local_port_range</cf>
+<cf>/proc/sys/net/ipv4/ip_local_port_range</cf>.
<sect1>Configuration
<label id="bfd-config">
@@ -1909,6 +1939,14 @@ 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.
+<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
+single-hop BFD sessions if <cf/net.ipv4.udp_l3mdev_accept/ sysctl is enabled,
+but does not currently work for multihop sessions. Another approach is to
+configure multiple BFD instances, one for each VRF (including the default VRF).
+Each BFD instance associated with a VRF (regular or default) only handles
+session requests from protocols in the same VRF.
+
<p>Some of BFD session options require <m/time/ value, which has to be specified
with the appropriate unit: <m/num/ <cf/s/|<cf/ms/|<cf/us/. Although microseconds
are allowed as units, practical minimum values are usually in order of tens of
@@ -2186,12 +2224,23 @@ using the following configuration parameters:
<cf/local 10.0.0.1; local as 65000;/ are valid). This parameter is
mandatory.
- <tag><label id="bgp-neighbor">neighbor [<m/ip/] [port <m/number/] [as <m/number/]</tag>
+ <tag><label id="bgp-neighbor">neighbor [<m/ip/ | range <m/prefix/] [port <m/number/] [as <m/number/] [internal|external]</tag>
Define neighboring router this instance will be talking to and what AS
it is located in. In case the neighbor is in the same AS as we are, we
- automatically switch to iBGP. Optionally, the remote port may also be
- specified. Like <cf/local/ parameter, this parameter may also be used
- multiple times with different sub-options. This parameter is mandatory.
+ automatically switch to IBGP. Alternatively, it is possible to specify
+ just <cf/internal/ or <cf/external/ instead of AS number, in that case
+ either local AS number, or any external AS number is accepted.
+ Optionally, the remote port may also be specified. Like <cf/local/
+ parameter, this parameter may also be used multiple times with different
+ sub-options. This parameter is mandatory.
+
+ It is possible to specify network prefix (with <cf/range/ keyword)
+ instead of explicit neighbor IP address. This enables dynamic BGP
+ behavior, where the BGP instance listens on BGP port, but new BGP
+ instances are spawned for incoming BGP connections (if source address
+ matches the network prefix). It is possible to mix regular BGP instances
+ with dynamic BGP instances and have multiple dynamic BGP instances with
+ different ranges.
<tag><label id="bgp-iface">interface <m/string/</tag>
Define interface we should use for link-local BGP IPv6 sessions.
@@ -2224,6 +2273,16 @@ using the following configuration parameters:
session. Default: the address of the local end of the interface our
neighbor is connected to.
+ <tag><label id="bgp-dynamic-name">dynamic name "<m/text/"</tag>
+ Define common prefix of names used for new BGP instances spawned when
+ dynamic BGP behavior is active. Actual names also contain numeric
+ index to distinguish individual instances. Default: "dynbgp".
+
+ <tag><label id="bgp-dynamic-name-digits">dynamic name digits <m/number/</tag>
+ Define minimum number of digits for index in names of spawned dynamic
+ BGP instances. E.g., if set to 2, then the first name would be
+ "dynbgp01". Default: 0.
+
<tag><label id="bgp-strict-bind">strict bind <m/switch/</tag>
Specify whether BGP listening socket should be bound to a specific local
address (the same as the <cf/source address/) and associated interface,
@@ -2565,6 +2624,15 @@ be used in explicit configuration.
<p>BGP channels have additional config options (together with the common ones):
<descrip>
+ <tag><label id="bgp-mandatory">mandatory <m/switch/</tag>
+ When local and neighbor sets of configured AFI/SAFI pairs differ,
+ capability negotiation ensures that a common subset is used. For
+ mandatory channels their associated AFI/SAFI must be negotiated
+ (i.e., also announced by the neighbor), otherwise BGP session
+ negotiation fails with <it/'Required capability missing'/ error.
+ Regardless, at least one AFI/SAFI must be negotiated in order to BGP
+ session be successfully established. Default: off.
+
<tag><label id="bgp-next-hop-keep">next hop keep <m/switch/|ibgp|ebgp</tag>
Do not modify the Next Hop attribute and advertise the current one
unchanged even in cases where our own local address should be used
@@ -3230,6 +3298,8 @@ protocol ospf [v2|v3] &lt;name&gt; {
tick &lt;num&gt;;
ecmp &lt;switch&gt; [limit &lt;num&gt;];
merge external &lt;switch&gt;;
+ graceful restart &lt;switch&gt;|aware;
+ graceful restart time &lt;num&gt;;
area &lt;id&gt; {
stub;
nssa;
@@ -3373,6 +3443,31 @@ protocol ospf [v2|v3] &lt;name&gt; {
from different LSAs are treated as separate even if they represents the
same destination. Default value is no.
+ <tag><label id="ospf-graceful-restart">graceful restart <m/switch/|aware</tag>
+ When an OSPF instance is restarted, neighbors break adjacencies and
+ recalculate their routing tables, which disrupts packet forwarding even
+ when the forwarding plane of the restarting router remains intact.
+ <rfc id="3623"> specifies a graceful restart mechanism to alleviate this
+ issue. For OSPF graceful restart, restarting router originates
+ Grace-LSAs, announcing intent to do graceful restart. Neighbors
+ receiving these LSAs enter helper mode, in which they ignore breakdown
+ of adjacencies, behave as if nothing is happening and keep old routes.
+ When adjacencies are reestablished, the restarting router flushes
+ Grace-LSAs and graceful restart is ended.
+
+ This option controls the graceful restart mechanism. It has three
+ states: Disabled, when no support is provided. Aware, when graceful
+ restart helper mode is supported, but no local graceful restart is
+ allowed (i.e. helper-only role). Enabled, when the full graceful restart
+ support is provided (i.e. both restarting and helper role). Note that
+ proper support for local graceful restart requires also configuration of
+ other protocols. Default: aware.
+
+ <tag><label id="ospf-graceful-restart-time">graceful restart time <m/num/</tag>
+ The restart time is announced in the Grace-LSA and specifies how long
+ neighbors should wait for proper end of the graceful restart before
+ exiting helper mode prematurely. Default: 120 seconds.
+
<tag><label id="ospf-area">area <M>id</M></tag>
This defines an OSPF area with given area ID (an integer or an IPv4
address, similarly to a router ID). The most important area is the
@@ -3982,7 +4077,7 @@ definitions, prefix definitions and DNS definitions:
RAdv protocol could be configured to change its behavior based on
availability of routes. When this option is used, the protocol waits in
suppressed state until a <it/trigger route/ (for the specified network)
- is exported to the protocol, the protocol also returnsd to suppressed
+ is exported to the protocol, the protocol also returns to suppressed
state if the <it/trigger route/ disappears. Note that route export
depends on specified export filter, as usual. This option could be used,
e.g., for handling failover in multihoming scenarios.
diff --git a/doc/reply_codes b/doc/reply_codes
index 3a7f2c90..02f4e656 100644
--- a/doc/reply_codes
+++ b/doc/reply_codes
@@ -33,6 +33,7 @@ Reply codes of BIRD command-line interface
0022 Undo scheduled
0023 Evaluation of expression
0024 Graceful restart status report
+0025 Graceful restart ordered
1000 BIRD version
1001 Interface list
diff --git a/filter/Makefile b/filter/Makefile
index 6bada8ca..c2062534 100644
--- a/filter/Makefile
+++ b/filter/Makefile
@@ -1,8 +1,24 @@
-src := filter.c f-util.c tree.c trie.c
+src := filter.c data.c f-util.c tree.c trie.c inst-gen.c
obj := $(src-o-files)
$(all-daemon)
$(cf-local)
+#M4FLAGS_FILTERS=$(filter-out -s,$(M4FLAGS))
+M4FLAGS_FILTERS=$(M4FLAGS)
+
+$(o)inst-gen.h: $(s)decl.m4 $(s)f-inst.c $(objdir)/.dir-stamp
+ $(M4) $(M4FLAGS_FILTERS) -DTARGET=H -P $^ >$@
+
+$(o)inst-gen.c: $(s)decl.m4 $(s)f-inst.c $(objdir)/.dir-stamp
+ $(M4) $(M4FLAGS_FILTERS) -DTARGET=C -P $^ >$@
+
+$(o)inst-interpret.c: $(s)decl.m4 $(s)f-inst.c $(objdir)/.dir-stamp
+ $(M4) $(M4FLAGS_FILTERS) -DTARGET=I -P $^ >$@
+
+prepare: $(o)inst-interpret.c $(o)inst-gen.h
+
tests_src := tree_test.c filter_test.c trie_test.c
tests_targets := $(tests_targets) $(tests-target-files)
tests_objs := $(tests_objs) $(src-o-files)
+
+$(call clean,inst-gen.h inst-gen.c inst-interpret.c)
diff --git a/filter/config.Y b/filter/config.Y
index c1e74531..8171a7c2 100644
--- a/filter/config.Y
+++ b/filter/config.Y
@@ -10,12 +10,17 @@
CF_HDR
+#include "filter/f-inst.h"
+#include "filter/data.h"
+
CF_DEFINES
static inline u32 pair(u32 a, u32 b) { return (a << 16) | b; }
static inline u32 pair_a(u32 p) { return p >> 16; }
static inline u32 pair_b(u32 p) { return p & 0xFFFF; }
+#define f_generate_complex(fi_code, da, arg) \
+ f_new_inst(FI_EA_SET, f_new_inst(fi_code, f_new_inst(FI_EA_GET, da), arg), da)
/*
* Sets and their items are during parsing handled as lists, linked
@@ -158,30 +163,29 @@ f_new_lc_item(u32 f1, u32 t1, u32 f2, u32 t2, u32 f3, u32 t3)
static inline struct f_inst *
f_generate_empty(struct f_dynamic_attr dyn)
{
- struct f_inst *e = f_new_inst(FI_EMPTY);
+ struct f_val empty;
switch (dyn.type & EAF_TYPE_MASK) {
case EAF_TYPE_AS_PATH:
- e->aux = T_PATH;
+ empty = f_const_empty_path;
break;
case EAF_TYPE_INT_SET:
- e->aux = T_CLIST;
+ empty = f_const_empty_clist;
break;
case EAF_TYPE_EC_SET:
- e->aux = T_ECLIST;
+ empty = f_const_empty_eclist;
break;
case EAF_TYPE_LC_SET:
- e->aux = T_LCLIST;
+ empty = f_const_empty_lclist;
break;
default:
cf_error("Can't empty that attribute");
}
- struct f_inst *s = f_new_inst_da(FI_EA_SET, dyn);
- s->a1.p = e;
- return s;
+ 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)
@@ -189,20 +193,22 @@ 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->aux != T_INT) || (t2->aux != T_INT))
+ 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->a2.i);
- check_u16(t2->a2.i);
+ check_u16(t1->a[1].i);
+ check_u16(t2->a[1].i);
rv = f_new_inst(FI_CONSTANT);
- rv->aux = T_PAIR;
- rv->a2.i = pair(t1->a2.i, t2->a2.i);
+ 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->a1.p = t1;
- rv->a2.p = t2;
+ rv->a[0].p = t1;
+ rv->a[1].p = t2;
}
return rv;
@@ -217,26 +223,12 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv)
if (tk->fi_code == FI_CONSTANT) {
c1 = 1;
-
- if (tk->aux == T_INT) {
- ipv4_used = 0; key = tk->a2.i;
- }
- else if (tk->aux == T_QUAD) {
- ipv4_used = 1; key = tk->a2.i;
- }
- else
- cf_error("Can't operate with key of non-integer/IPv4 type in EC constructor");
- }
-
- /* IP->Quad implicit conversion */
- else if (tk->fi_code == FI_CONSTANT_INDIRECT) {
- c1 = 1;
- struct f_val *val = tk->a1.p;
+ struct f_val *val = &(tk->val);
if (val->type == T_INT) {
ipv4_used = 0; key = val->val.i;
}
- else if (val->type == T_QUAD) {
+ 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)) {
@@ -247,10 +239,10 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv)
}
if (tv->fi_code == FI_CONSTANT) {
- if (tv->aux != T_INT)
+ if (tv->val.type != T_INT)
cf_error("Can't operate with value of non-integer type in EC constructor");
c2 = 1;
- val2 = tv->a2.i;
+ val2 = tv->val.val.i;
}
if (c1 && c2) {
@@ -271,17 +263,17 @@ f_generate_ec(u16 kind, struct f_inst *tk, struct f_inst *tv)
ec = ec_as4(kind, key, val2);
}
- NEW_F_VAL;
- rv = f_new_inst(FI_CONSTANT_INDIRECT);
- rv->a1.p = val;
- val->type = T_EC;
- val->val.ec = ec;
+ 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->a1.p = tk;
- rv->a2.p = tv;
+ rv->a[0].p = tk;
+ rv->a[1].p = tv;
}
return rv;
@@ -293,48 +285,59 @@ 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->aux != T_INT) || (t2->aux != T_INT) || (t3->aux != T_INT))
+ 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_INDIRECT);
-
- NEW_F_VAL;
- rv->a1.p = val;
- val->type = T_LC;
- val->val.lc = (lcomm) { t1->a2.i, t2->a2.i, t3->a2.i };
+ 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->a1.p = t1;
- rv->a2.p = t2;
- rv->a3.p = t3;
+ 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_path_mask *t)
+f_generate_path_mask(struct f_inst *t)
{
- for (struct f_path_mask *tt = t; tt; tt = tt->next) {
- if (tt->kind == PM_ASN_EXPR) {
- struct f_inst *mrv = f_new_inst(FI_PATHMASK_CONSTRUCT);
- mrv->a1.p = t;
- return mrv;
- }
+ 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++;
}
- NEW_F_VAL;
- val->type = T_PATH_MASK;
- val->val.path_mask = t;
+ 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_inst *rv = f_new_inst(FI_CONSTANT_INDIRECT);
- rv->a1.p = val;
+ struct f_path_mask *pm = cfg_allocz(sizeof(struct f_path_mask) + len * sizeof(struct f_path_mask_item));
- return rv;
+ 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
@@ -383,21 +386,41 @@ assert_copy_expr(const char *start, size_t len)
static struct f_inst *
assert_done(struct f_inst *expr, const char *start, const char *end)
{
- struct f_inst *i;
- i = f_new_inst(FI_ASSERT);
- i->a1.p = expr;
+ return f_new_inst(FI_ASSERT, expr,
+ (end >= start) ?
+ assert_copy_expr(start, end - start + 1)
+ : "???");
+}
- if (end >= start)
- {
- i->a2.p = assert_copy_expr(start, end - start + 1);
- }
- else
- {
- /* this is a break of lexer buffer */
- i->a2.p = "???";
+static struct f_inst *
+assert_assign(struct f_lval *lval, struct f_inst *expr, const char *start, const char *end)
+{
+ struct f_inst *setter, *getter, *checker;
+ switch (lval->type) {
+ case F_LVAL_VARIABLE:
+ setter = f_new_inst(FI_VAR_SET, expr, lval->sym);
+ getter = f_new_inst(FI_VAR_GET, lval->sym);
+ break;
+ case F_LVAL_PREFERENCE:
+ setter = f_new_inst(FI_PREF_SET, expr);
+ getter = f_new_inst(FI_PREF_GET);
+ break;
+ case F_LVAL_SA:
+ setter = f_new_inst(FI_RTA_SET, expr, lval->sa);
+ getter = f_new_inst(FI_RTA_GET, lval->sa);
+ break;
+ case F_LVAL_EA:
+ setter = f_new_inst(FI_EA_SET, expr, lval->da);
+ getter = f_new_inst(FI_EA_GET, lval->da);
+ break;
+ default:
+ bug("Unknown lval type");
}
- return i;
+ checker = f_new_inst(FI_EQ, expr, getter);
+ setter->next = checker;
+
+ return assert_done(setter, start, end);
}
CF_DECLS
@@ -418,56 +441,58 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
PREPEND, FIRST, LAST, LAST_NONAGGREGATED, MATCH,
EMPTY,
FILTER, WHERE, EVAL, ATTRIBUTE,
- BT_ASSERT, BT_TEST_SUITE, FORMAT)
+ BT_ASSERT, BT_TEST_SUITE, BT_CHECK_ASSIGN, BT_TEST_SAME, FORMAT)
%nonassoc THEN
%nonassoc ELSE
-%type <x> term block cmds cmds_int cmd function_body constant constructor print_one print_list var_list var_listn function_call symbol bgp_path_expr
+%type <xp> cmds_int
+%type <x> term block cmd cmds constant constructor print_list var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail
%type <fda> dynamic_attr
%type <fsa> static_attr
-%type <f> filter filter_body where_filter
-%type <i> type break_command ec_kind
+%type <f> filter where_filter
+%type <fl> filter_body function_body
+%type <flv> lvalue
+%type <i> type function_args function_vars
+%type <ecs> ec_kind
+%type <fret> break_command
%type <i32> cnum
%type <e> pair_item ec_item lc_item set_item switch_item set_items switch_items switch_body
%type <trie> fprefix_set
%type <v> set_atom switch_atom fipa
%type <px> fprefix
-%type <s> decls declsn one_decl function_params
-%type <h> bgp_path bgp_path_tail
%type <t> get_cf_position
CF_GRAMMAR
conf: filter_def ;
filter_def:
- FILTER SYM { $2 = cf_define_symbol($2, SYM_FILTER, NULL); cf_push_scope( $2 ); }
+ FILTER symbol { $2 = cf_define_symbol($2, SYM_FILTER, filter, NULL); cf_push_scope( $2 ); }
filter_body {
- $2->def = $4;
- $4->name = $2->name;
- DBG( "We have new filter defined (%s)\n", $2->name );
+ struct filter *f = cfg_alloc(sizeof(struct filter));
+ *f = (struct filter) { .sym = $2, .root = $4 };
+ $2->filter = f;
+
cf_pop_scope();
}
;
conf: filter_eval ;
filter_eval:
- EVAL term { f_eval_int($2); }
+ EVAL term { f_eval_int(f_linearize($2)); }
;
conf: custom_attr ;
-custom_attr: ATTRIBUTE type SYM ';' {
- cf_define_symbol($3, SYM_ATTRIBUTE, ca_lookup(new_config->pool, $3->name, $2)->fda);
+custom_attr: ATTRIBUTE type symbol ';' {
+ cf_define_symbol($3, SYM_ATTRIBUTE, attribute, ca_lookup(new_config->pool, $3->name, $2)->fda);
};
conf: bt_test_suite ;
bt_test_suite:
- BT_TEST_SUITE '(' SYM ',' text ')' {
- if (!($3->class & SYM_FUNCTION))
- cf_error("Function expected");
-
- struct f_bt_test_suite *t = cfg_alloc(sizeof(struct f_bt_test_suite));
- t->fn = $3->def;
+ BT_TEST_SUITE '(' CF_SYM_KNOWN ',' text ')' {
+ cf_assert_symbol($3, SYM_FUNCTION);
+ struct f_bt_test_suite *t = cfg_allocz(sizeof(struct f_bt_test_suite));
+ t->fn = $3->function;
t->fn_name = $3->name;
t->dsc = $5;
@@ -475,6 +500,21 @@ bt_test_suite:
}
;
+conf: bt_test_same ;
+bt_test_same:
+ BT_TEST_SAME '(' CF_SYM_KNOWN ',' CF_SYM_KNOWN ',' NUM ')' {
+ cf_assert_symbol($3, SYM_FUNCTION);
+ cf_assert_symbol($5, SYM_FUNCTION);
+ struct f_bt_test_suite *t = cfg_allocz(sizeof(struct f_bt_test_suite));
+ t->fn = $3->function;
+ t->cmp = $5->function;
+ t->result = $7;
+ t->fn_name = $3->name;
+ t->dsc = $5->name;
+ add_tail(&new_config->tests, &t->n);
+ }
+ ;
+
type:
INT { $$ = T_INT; }
| BOOL { $$ = T_BOOL; }
@@ -513,112 +553,89 @@ type:
}
;
-one_decl:
- type SYM {
- struct f_val * val = cfg_alloc(sizeof(struct f_val));
- val->type = T_VOID;
- $2 = cf_define_symbol($2, SYM_VARIABLE | $1, val);
- DBG( "New variable %s type %x\n", $2->name, $1 );
- $2->aux2 = NULL;
- $$=$2;
+function_argsn:
+ /* EMPTY */
+ | function_argsn type symbol ';' {
+ if ($3->scope->slots >= 0xfe) cf_error("Too many declarations, at most 255 allowed");
+ cf_define_symbol($3, SYM_VARIABLE | $2, offset, $3->scope->slots++);
}
;
-/* Decls with ';' at the end */
-decls: /* EMPTY */ { $$ = NULL; }
- | one_decl ';' decls {
- $$ = $1;
- $$->aux2 = $3;
+function_args:
+ '(' ')' { $$ = 0; }
+ | '(' function_argsn type symbol ')' {
+ cf_define_symbol($4, SYM_VARIABLE | $3, offset, $4->scope->slots++);
+ $$ = $4->scope->slots;
}
;
-/* Declarations that have no ';' at the end. */
-declsn: one_decl { $$ = $1; }
- | one_decl ';' declsn {
- $$ = $1;
- $$->aux2 = $3;
+function_vars:
+ /* EMPTY */ { $$ = 0; }
+ | function_vars type symbol ';' {
+ cf_define_symbol($3, SYM_VARIABLE | $2, offset, $3->scope->slots++);
+ $$ = $1 + 1;
}
;
-filter_body:
- function_body {
- struct filter *f = cfg_alloc(sizeof(struct filter));
- f->name = NULL;
- f->root = $1;
- $$ = f;
- }
- ;
+filter_body: function_body ;
filter:
- SYM {
- if ($1->class != SYM_FILTER) cf_error("No such filter.");
- $$ = $1->def;
+ CF_SYM_KNOWN {
+ cf_assert_symbol($1, SYM_FILTER);
+ $$ = $1->filter;
}
- | filter_body
- ;
-
-where_filter:
- WHERE term {
- /* Construct 'IF term THEN ACCEPT; REJECT;' */
+ | filter_body {
struct filter *f = cfg_alloc(sizeof(struct filter));
- struct f_inst *i, *acc, *rej;
- acc = f_new_inst(FI_PRINT_AND_DIE); /* ACCEPT */
- acc->a1.p = NULL;
- acc->a2.i = F_ACCEPT;
- rej = f_new_inst(FI_PRINT_AND_DIE); /* REJECT */
- rej->a1.p = NULL;
- rej->a2.i = F_REJECT;
- i = f_new_inst(FI_CONDITION); /* IF */
- i->a1.p = $2;
- i->a2.p = acc;
- i->next = rej;
- f->name = NULL;
- f->root = i;
+ *f = (struct filter) { .root = $1 };
$$ = f;
- }
+ }
;
-function_params:
- '(' declsn ')' { DBG( "Have function parameters\n" ); $$=$2; }
- | '(' ')' { $$=NULL; }
+where_filter:
+ WHERE term {
+ /* Construct 'IF term THEN { ACCEPT; } ELSE { REJECT; }' */
+ $$ = f_new_where($2);
+ }
;
function_body:
- decls '{' cmds '}' {
- if ($1) {
- /* Prepend instruction to clear local variables */
- $$ = f_new_inst(FI_CLEAR_LOCAL_VARS);
- $$->a1.p = $1;
- $$->next = $3;
- } else
- $$ = $3;
+ function_vars '{' cmds '}' {
+ $$ = f_linearize($3);
+ $$->vars = $1;
}
;
conf: function_def ;
function_def:
- FUNCTION SYM { DBG( "Beginning of function %s\n", $2->name );
- $2 = cf_define_symbol($2, SYM_FUNCTION, NULL);
+ FUNCTION symbol { DBG( "Beginning of function %s\n", $2->name );
+ $2 = cf_define_symbol($2, SYM_FUNCTION, function, NULL);
cf_push_scope($2);
- } function_params function_body {
- $2->def = $5;
- $2->aux2 = $4;
- DBG("Hmm, we've got one function here - %s\n", $2->name);
+ } function_args function_body {
+ DBG("Definition of function %s with %u args and %u local vars.\n", $2->name, $4, $5->vars);
+ $5->args = $4;
+ $2->function = $5;
cf_pop_scope();
}
;
/* Programs */
-/* Hack: $$ of cmds_int is the last node.
- $$->next of cmds_int is temporary used for the first node */
-
cmds: /* EMPTY */ { $$ = NULL; }
- | cmds_int { $$ = $1->next; $1->next = NULL; }
+ | cmds_int { $$ = $1.begin; }
;
-cmds_int: cmd { $$ = $1; $1->next = $1; }
- | cmds_int cmd { $$ = $2; $2->next = $1->next ; $1->next = $2; }
+cmds_int: cmd {
+ $$.begin = $$.end = $1;
+ while ($$.end->next)
+ $$.end = $$.end->next;
+ }
+ | cmds_int cmd {
+ $$.begin = $1.begin;
+ $1.end->next = $2;
+ $$.end = $2;
+ while ($$.end->next)
+ $$.end = $$.end->next;
+ }
;
block:
@@ -653,25 +670,25 @@ set_atom:
| VPN_RD { $$.type = T_RD; $$.val.ec = $1; }
| ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
| '(' term ')' {
- $$ = f_eval($2, cfg_mem);
+ if (f_eval(f_linearize($2), cfg_mem, &($$)) > F_RETURN) cf_error("Runtime error");
if (!f_valid_set_type($$.type)) cf_error("Set-incompatible type");
}
- | SYM {
- if (!cf_symbol_is_constant($1)) cf_error("%s: constant expected", $1->name);
+ | CF_SYM_KNOWN {
+ cf_assert_symbol($1, SYM_CONSTANT);
if (!f_valid_set_type(SYM_TYPE($1))) cf_error("%s: set-incompatible type", $1->name);
- $$ = *(struct f_val *)($1->def);
+ $$ = *$1->val;
}
;
switch_atom:
NUM { $$.type = T_INT; $$.val.i = $1; }
- | '(' term ')' { $$.type = T_INT; $$.val.i = f_eval_int($2); }
+ | '(' term ')' { $$.type = T_INT; $$.val.i = f_eval_int(f_linearize($2)); }
| fipa { $$ = $1; }
| ENUM { $$.type = pair_a($1); $$.val.i = pair_b($1); }
;
cnum:
- term { $$ = f_eval_int($1); }
+ term { $$ = f_eval_int(f_linearize($1)); }
pair_item:
'(' cnum ',' cnum ')' { $$ = f_new_pair_item($2, $2, $4, $4); }
@@ -758,23 +775,22 @@ switch_body: /* EMPTY */ { $$ = NULL; }
| switch_body switch_items ':' cmds {
/* Fill data fields */
struct f_tree *t;
+ struct f_line *line = f_linearize($4);
for (t = $2; t; t = t->left)
- t->data = $4;
+ t->data = line;
$$ = f_merge_items($1, $2);
}
| switch_body ELSECOL cmds {
struct f_tree *t = f_new_tree();
t->from.type = t->to.type = T_VOID;
t->right = t;
- t->data = $3;
+ t->data = f_linearize($3);
$$ = f_merge_items($1, t);
}
;
-/* CONST '(' expr ')' { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_INT; $$->a2.i = $3; } */
-
bgp_path_expr:
- symbol { $$ = $1; }
+ symbol_value { $$ = $1; }
| '(' term ')' { $$ = $2; }
;
@@ -783,178 +799,166 @@ bgp_path:
;
bgp_path_tail:
- NUM bgp_path_tail { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN; $$->val = $1; }
- | NUM DDOT NUM bgp_path_tail { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $4; $$->kind = PM_ASN_RANGE; $$->val = $1; $$->val2 = $3; }
- | '*' bgp_path_tail { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASTERISK; }
- | '?' bgp_path_tail { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_QUESTION; }
- | bgp_path_expr bgp_path_tail { $$ = cfg_allocz(sizeof(struct f_path_mask)); $$->next = $2; $$->kind = PM_ASN_EXPR; $$->val = (uintptr_t) $1; }
+ NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .asn = $1, .kind = PM_ASN, }, }); $$->next = $2; }
+ | NUM DDOT NUM bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .from = $1, .to = $3, .kind = PM_ASN_RANGE }, }); $$->next = $4; }
+ | '*' bgp_path_tail { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PATH_MASK_ITEM, .val.pmi = { .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_expr bgp_path_tail { $$ = $1; $$->next = $2; }
| { $$ = NULL; }
;
constant:
- NUM { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_INT; $$->a2.i = $1; }
- | TRUE { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_BOOL; $$->a2.i = 1; }
- | FALSE { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_BOOL; $$->a2.i = 0; }
- | TEXT { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_STRING; $$->a2.p = $1; }
- | fipa { NEW_F_VAL; $$ = f_new_inst(FI_CONSTANT_INDIRECT); $$->a1.p = val; *val = $1; }
- | VPN_RD { NEW_F_VAL; $$ = f_new_inst(FI_CONSTANT_INDIRECT); val->type = T_RD; val->val.ec = $1; $$->a1.p = val; }
- | net_ { NEW_F_VAL; $$ = f_new_inst(FI_CONSTANT_INDIRECT); val->type = T_NET; val->val.net = $1; $$->a1.p = val; }
- | '[' set_items ']' { DBG( "We've got a set here..." ); $$ = f_new_inst(FI_CONSTANT); $$->aux = T_SET; $$->a2.p = build_tree($2); DBG( "ook\n" ); }
- | '[' fprefix_set ']' { $$ = f_new_inst(FI_CONSTANT); $$->aux = T_PREFIX_SET; $$->a2.p = $2; }
- | ENUM { $$ = f_new_inst(FI_CONSTANT); $$->aux = $1 >> 16; $$->a2.i = $1 & 0xffff; }
+ NUM { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_INT, .val.i = $1, }); }
+ | TRUE { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 1, }); }
+ | FALSE { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_BOOL, .val.i = 0, }); }
+ | TEXT { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_STRING, .val.s = $1, }); }
+ | fipa { $$ = f_new_inst(FI_CONSTANT, $1); }
+ | VPN_RD { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_RD, .val.ec = $1, }); }
+ | net_ { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_NET, .val.net = $1, }); }
+ | '[' set_items ']' {
+ DBG( "We've got a set here..." );
+ $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_SET, .val.t = build_tree($2), });
+ DBG( "ook\n" );
+ }
+ | '[' fprefix_set ']' { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_PREFIX_SET, .val.ti = $2, }); }
+ | ENUM { $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = $1 >> 16, .val.i = $1 & 0xffff, }); }
;
constructor:
- '(' term ',' term ')' { $$ = f_generate_dpair($2, $4); }
- | '(' ec_kind ',' term ',' term ')' { $$ = f_generate_ec($2, $4, $6); }
- | '(' term ',' term ',' term ')' { $$ = f_generate_lc($2, $4, $6); }
- | bgp_path { $$ = f_generate_path_mask($1); }
+ '(' term ',' term ')' { $$ = f_new_inst(FI_PAIR_CONSTRUCT, $2, $4); }
+ | '(' ec_kind ',' term ',' term ')' { $$ = f_new_inst(FI_EC_CONSTRUCT, $4, $6, $2); }
+ | '(' term ',' term ',' term ')' { $$ = f_new_inst(FI_LC_CONSTRUCT, $2, $4, $6); }
+ | bgp_path { $$ = f_new_inst(FI_PATHMASK_CONSTRUCT, $1); }
;
-rtadot: /* EMPTY, we are not permitted RTA. prefix */
- ;
+/* This generates the function_call variable list backwards. */
+var_list: /* EMPTY */ { $$ = NULL; }
+ | term { $$ = $1; }
+ | var_list ',' term { $$ = $3; $$->next = $1; }
function_call:
- SYM '(' var_list ')' {
- struct symbol *sym;
- struct f_inst *inst = $3;
+ CF_SYM_KNOWN '(' var_list ')' {
if ($1->class != SYM_FUNCTION)
cf_error("You can't call something which is not a function. Really.");
- DBG("You are calling function %s\n", $1->name);
- $$ = f_new_inst(FI_CALL);
- $$->a1.p = inst;
- $$->a2.p = $1->def;
- sym = $1->aux2;
- while (sym || inst) {
- if (!sym || !inst)
- cf_error("Wrong number of arguments for function %s.", $1->name);
- DBG( "You should pass parameter called %s\n", sym->name);
- inst->a1.p = sym;
- sym = sym->aux2;
- inst = inst->next;
+
+ struct f_inst *fc = f_new_inst(FI_CALL, $1);
+ uint args = 0;
+ while ($3) {
+ args++;
+ struct f_inst *tmp = $3->next;
+ $3->next = fc;
+
+ fc = $3;
+ $3 = tmp;
}
+
+ if (args != $1->function->args)
+ cf_error("Function call '%s' got %u arguments, need %u arguments.",
+ $1->name, args, $1->function->args);
+
+ $$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_VOID });
+ $$->next = fc;
}
;
-symbol:
- SYM {
- switch ($1->class & 0xffff) {
- case SYM_CONSTANT_RANGE:
- $$ = f_new_inst(FI_CONSTANT_INDIRECT);
- goto cv_common;
- case SYM_VARIABLE_RANGE:
- $$ = f_new_inst(FI_VARIABLE);
- cv_common:
- $$->a1.p = $1->def;
- $$->a2.p = $1->name;
- break;
- case SYM_ATTRIBUTE:
- $$ = f_new_inst_da(FI_EA_GET, *((struct f_dynamic_attr *) $1->def));
- break;
- default:
- cf_error("%s: variable expected.", $1->name);
- }
- }
+symbol_value: CF_SYM_KNOWN
+ {
+ switch ($1->class) {
+ case SYM_CONSTANT_RANGE:
+ $$ = f_new_inst(FI_CONSTANT, *($1->val));
+ break;
+ case SYM_VARIABLE_RANGE:
+ $$ = f_new_inst(FI_VAR_GET, $1);
+ break;
+ case SYM_ATTRIBUTE:
+ $$ = f_new_inst(FI_EA_GET, *$1->attribute);
+ break;
+ default:
+ cf_error("Can't get value of symbol %s", $1->name);
+ }
+ }
+ ;
static_attr:
- FROM { $$ = f_new_static_attr(T_IP, SA_FROM, 1); }
- | GW { $$ = f_new_static_attr(T_IP, SA_GW, 1); }
- | NET { $$ = f_new_static_attr(T_NET, SA_NET, 0); }
- | PROTO { $$ = f_new_static_attr(T_STRING, SA_PROTO, 0); }
- | SOURCE { $$ = f_new_static_attr(T_ENUM_RTS, SA_SOURCE, 0); }
- | SCOPE { $$ = f_new_static_attr(T_ENUM_SCOPE, SA_SCOPE, 1); }
- | DEST { $$ = f_new_static_attr(T_ENUM_RTD, SA_DEST, 1); }
- | IFNAME { $$ = f_new_static_attr(T_STRING, SA_IFNAME, 1); }
- | IFINDEX { $$ = f_new_static_attr(T_INT, SA_IFINDEX, 0); }
+ FROM { $$ = f_new_static_attr(T_IP, SA_FROM, 0); }
+ | GW { $$ = f_new_static_attr(T_IP, SA_GW, 0); }
+ | NET { $$ = f_new_static_attr(T_NET, SA_NET, 1); }
+ | PROTO { $$ = f_new_static_attr(T_STRING, SA_PROTO, 1); }
+ | SOURCE { $$ = f_new_static_attr(T_ENUM_RTS, SA_SOURCE, 1); }
+ | SCOPE { $$ = f_new_static_attr(T_ENUM_SCOPE, SA_SCOPE, 0); }
+ | DEST { $$ = f_new_static_attr(T_ENUM_RTD, SA_DEST, 0); }
+ | IFNAME { $$ = f_new_static_attr(T_STRING, SA_IFNAME, 0); }
+ | IFINDEX { $$ = f_new_static_attr(T_INT, SA_IFINDEX, 1); }
;
term:
'(' term ')' { $$ = $2; }
- | term '+' term { $$ = f_new_inst(FI_ADD); $$->a1.p = $1; $$->a2.p = $3; }
- | term '-' term { $$ = f_new_inst(FI_SUBTRACT); $$->a1.p = $1; $$->a2.p = $3; }
- | term '*' term { $$ = f_new_inst(FI_MULTIPLY); $$->a1.p = $1; $$->a2.p = $3; }
- | term '/' term { $$ = f_new_inst(FI_DIVIDE); $$->a1.p = $1; $$->a2.p = $3; }
- | term AND term { $$ = f_new_inst(FI_AND); $$->a1.p = $1; $$->a2.p = $3; }
- | term OR term { $$ = f_new_inst(FI_OR); $$->a1.p = $1; $$->a2.p = $3; }
- | term '=' term { $$ = f_new_inst(FI_EQ); $$->a1.p = $1; $$->a2.p = $3; }
- | term NEQ term { $$ = f_new_inst(FI_NEQ); $$->a1.p = $1; $$->a2.p = $3; }
- | term '<' term { $$ = f_new_inst(FI_LT); $$->a1.p = $1; $$->a2.p = $3; }
- | term LEQ term { $$ = f_new_inst(FI_LTE); $$->a1.p = $1; $$->a2.p = $3; }
- | term '>' term { $$ = f_new_inst(FI_LT); $$->a1.p = $3; $$->a2.p = $1; }
- | term GEQ term { $$ = f_new_inst(FI_LTE); $$->a1.p = $3; $$->a2.p = $1; }
- | term '~' term { $$ = f_new_inst(FI_MATCH); $$->a1.p = $1; $$->a2.p = $3; }
- | term NMA term { $$ = f_new_inst(FI_NOT_MATCH);$$->a1.p = $1; $$->a2.p = $3; }
- | '!' term { $$ = f_new_inst(FI_NOT); $$->a1.p = $2; }
- | DEFINED '(' term ')' { $$ = f_new_inst(FI_DEFINED); $$->a1.p = $3; }
-
- | symbol { $$ = $1; }
+ | term '+' term { $$ = f_new_inst(FI_ADD, $1, $3); }
+ | term '-' term { $$ = f_new_inst(FI_SUBTRACT, $1, $3); }
+ | term '*' term { $$ = f_new_inst(FI_MULTIPLY, $1, $3); }
+ | term '/' term { $$ = f_new_inst(FI_DIVIDE, $1, $3); }
+ | term AND term { $$ = f_new_inst(FI_AND, $1, $3); }
+ | term OR term { $$ = f_new_inst(FI_OR, $1, $3); }
+ | term '=' term { $$ = f_new_inst(FI_EQ, $1, $3); }
+ | term NEQ term { $$ = f_new_inst(FI_NEQ, $1, $3); }
+ | term '<' term { $$ = f_new_inst(FI_LT, $1, $3); }
+ | term LEQ term { $$ = f_new_inst(FI_LTE, $1, $3); }
+ | term '>' term { $$ = f_new_inst(FI_LT, $3, $1); }
+ | term GEQ term { $$ = f_new_inst(FI_LTE, $3, $1); }
+ | term '~' term { $$ = f_new_inst(FI_MATCH, $1, $3); }
+ | term NMA term { $$ = f_new_inst(FI_NOT_MATCH, $1, $3); }
+ | '!' term { $$ = f_new_inst(FI_NOT, $2); }
+ | DEFINED '(' term ')' { $$ = f_new_inst(FI_DEFINED, $3); }
+
+ | symbol_value { $$ = $1; }
| constant { $$ = $1; }
| constructor { $$ = $1; }
| PREFERENCE { $$ = f_new_inst(FI_PREF_GET); }
- | rtadot static_attr { $$ = f_new_inst_sa(FI_RTA_GET, $2); }
+ | static_attr { $$ = f_new_inst(FI_RTA_GET, $1); }
- | rtadot dynamic_attr { $$ = f_new_inst_da(FI_EA_GET, $2); }
+ | dynamic_attr { $$ = f_new_inst(FI_EA_GET, $1); }
- | term '.' IS_V4 { $$ = f_new_inst(FI_IS_V4); $$->a1.p = $1; }
- | term '.' TYPE { $$ = f_new_inst(FI_TYPE); $$->a1.p = $1; }
- | term '.' IP { $$ = f_new_inst(FI_IP); $$->a1.p = $1; $$->aux = T_IP; }
- | term '.' RD { $$ = f_new_inst(FI_ROUTE_DISTINGUISHER); $$->a1.p = $1; $$->aux = T_RD; }
- | term '.' LEN { $$ = f_new_inst(FI_LENGTH); $$->a1.p = $1; }
- | term '.' MAXLEN { $$ = f_new_inst(FI_ROA_MAXLEN); $$->a1.p = $1; }
- | term '.' ASN { $$ = f_new_inst(FI_ROA_ASN); $$->a1.p = $1; }
- | term '.' SRC { $$ = f_new_inst(FI_SADR_SRC); $$->a1.p = $1; }
- | term '.' MASK '(' term ')' { $$ = f_new_inst(FI_IP_MASK); $$->a1.p = $1; $$->a2.p = $5; }
- | term '.' FIRST { $$ = f_new_inst(FI_AS_PATH_FIRST); $$->a1.p = $1; }
- | term '.' LAST { $$ = f_new_inst(FI_AS_PATH_LAST); $$->a1.p = $1; }
- | term '.' LAST_NONAGGREGATED { $$ = f_new_inst(FI_AS_PATH_LAST_NAG); $$->a1.p = $1; }
+ | term '.' IS_V4 { $$ = f_new_inst(FI_IS_V4, $1); }
+ | term '.' TYPE { $$ = f_new_inst(FI_TYPE, $1); }
+ | term '.' IP { $$ = f_new_inst(FI_IP, $1); }
+ | term '.' RD { $$ = f_new_inst(FI_ROUTE_DISTINGUISHER, $1); }
+ | term '.' LEN { $$ = f_new_inst(FI_LENGTH, $1); }
+ | term '.' MAXLEN { $$ = f_new_inst(FI_ROA_MAXLEN, $1); }
+ | term '.' ASN { $$ = f_new_inst(FI_ROA_ASN, $1); }
+ | term '.' SRC { $$ = f_new_inst(FI_SADR_SRC, $1); }
+ | term '.' MASK '(' term ')' { $$ = f_new_inst(FI_IP_MASK, $1, $5); }
+ | term '.' FIRST { $$ = f_new_inst(FI_AS_PATH_FIRST, $1); }
+ | term '.' LAST { $$ = f_new_inst(FI_AS_PATH_LAST, $1); }
+ | term '.' LAST_NONAGGREGATED { $$ = f_new_inst(FI_AS_PATH_LAST_NAG, $1); }
/* Communities */
/* This causes one shift/reduce conflict
- | rtadot dynamic_attr '.' ADD '(' term ')' { }
- | rtadot dynamic_attr '.' DELETE '(' term ')' { }
- | rtadot dynamic_attr '.' CONTAINS '(' term ')' { }
- | rtadot dynamic_attr '.' RESET{ }
+ | dynamic_attr '.' ADD '(' term ')' { }
+ | dynamic_attr '.' DELETE '(' term ')' { }
+ | dynamic_attr '.' CONTAINS '(' term ')' { }
+ | dynamic_attr '.' RESET{ }
*/
- | '+' EMPTY '+' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_PATH; }
- | '-' EMPTY '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_CLIST; }
- | '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_ECLIST; }
- | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_EMPTY); $$->aux = T_LCLIST; }
- | PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND); $$->a1.p = $3; $$->a2.p = $5; }
- | ADD '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD_DEL); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'a'; }
- | DELETE '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD_DEL); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'd'; }
- | FILTER '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD_DEL); $$->a1.p = $3; $$->a2.p = $5; $$->aux = 'f'; }
+ | '+' EMPTY '+' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_path); }
+ | '-' EMPTY '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_clist); }
+ | '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_eclist); }
+ | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_lclist); }
+ | PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND, $3, $5); }
+ | ADD '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD, $3, $5); }
+ | DELETE '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_DEL, $3, $5); }
+ | FILTER '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_FILTER, $3, $5); }
- | ROA_CHECK '(' rtable ')' { $$ = f_generate_roa_check($3, NULL, NULL); }
- | ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_generate_roa_check($3, $5, $7); }
+ | ROA_CHECK '(' rtable ')' { $$ = f_new_inst(FI_ROA_CHECK_IMPLICIT, $3); }
+ | ROA_CHECK '(' rtable ',' term ',' term ')' { $$ = f_new_inst(FI_ROA_CHECK_EXPLICIT, $5, $7, $3); }
- | FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT); $$->a1.p = $3; }
+ | FORMAT '(' term ')' { $$ = f_new_inst(FI_FORMAT, $3); }
/* | term '.' LEN { $$->code = P('P','l'); } */
-/* function_call is inlined here */
- | SYM '(' var_list ')' {
- struct symbol *sym;
- struct f_inst *inst = $3;
- if ($1->class != SYM_FUNCTION)
- cf_error("You can't call something which is not a function. Really.");
- DBG("You are calling function %s\n", $1->name);
- $$ = f_new_inst(FI_CALL);
- $$->a1.p = inst;
- $$->a2.p = $1->def;
- sym = $1->aux2;
- while (sym || inst) {
- if (!sym || !inst)
- cf_error("Wrong number of arguments for function %s.", $1->name);
- DBG( "You should pass parameter called %s\n", sym->name);
- inst->a1.p = sym;
- sym = sym->aux2;
- inst = inst->next;
- }
- }
+ | function_call
;
break_command:
@@ -962,106 +966,85 @@ break_command:
| ACCEPT { $$ = F_ACCEPT; }
| REJECT { $$ = F_REJECT; }
| ERROR { $$ = F_ERROR; }
- | PRINT { $$ = F_NOP; }
- | PRINTN { $$ = F_NONL; }
- ;
-
-print_one:
- term { $$ = f_new_inst(FI_PRINT); $$->a1.p = $1; $$->a2.p = NULL; }
;
print_list: /* EMPTY */ { $$ = NULL; }
- | print_one { $$ = $1; }
- | print_one ',' print_list {
- if ($1) {
- $1->next = $3;
- $$ = $1;
- } else $$ = $3;
- }
- ;
-
-var_listn: term {
- $$ = f_new_inst(FI_SET);
- $$->a1.p = NULL;
- $$->a2.p = $1;
- $$->next = NULL;
- }
- | term ',' var_listn {
- $$ = f_new_inst(FI_SET);
- $$->a1.p = NULL;
- $$->a2.p = $1;
- $$->next = $3;
+ | term { $$ = $1; }
+ | term ',' print_list {
+ ASSERT($1);
+ ASSERT($1->next == NULL);
+ $1->next = $3;
+ $$ = $1;
}
;
-var_list: /* EMPTY */ { $$ = NULL; }
- | var_listn { $$ = $1; }
- ;
-
cmd:
IF term THEN block {
- $$ = f_new_inst(FI_CONDITION);
- $$->a1.p = $2;
- $$->a2.p = $4;
+ $$ = f_new_inst(FI_CONDITION, $2, $4, NULL);
}
| IF term THEN block ELSE block {
- struct f_inst *i = f_new_inst(FI_CONDITION);
- i->a1.p = $2;
- i->a2.p = $4;
- $$ = f_new_inst(FI_CONDITION);
- $$->a1.p = i;
- $$->a2.p = $6;
+ $$ = f_new_inst(FI_CONDITION, $2, $4, $6);
}
- | SYM '=' term ';' {
- DBG( "Ook, we'll set value\n" );
- if ($1->class == SYM_ATTRIBUTE) {
- $$ = f_new_inst_da(FI_EA_SET, *((struct f_dynamic_attr *) $1->def));
- $$->a1.p = $3;
- } else if (($1->class & ~T_MASK) == SYM_VARIABLE) {
- $$ = f_new_inst(FI_SET);
- $$->a1.p = $1;
- $$->a2.p = $3;
- } else
- cf_error( "Symbol `%s' is read-only.", $1->name );
+ | CF_SYM_KNOWN '=' term ';' {
+ switch ($1->class) {
+ case SYM_VARIABLE_RANGE:
+ $$ = f_new_inst(FI_VAR_SET, $3, $1);
+ break;
+ case SYM_ATTRIBUTE:
+ $$ = f_new_inst(FI_EA_SET, $3, *$1->attribute);
+ break;
+ default:
+ cf_error("Can't assign to symbol %s", $1->name);
+ }
}
| RETURN term ';' {
DBG( "Ook, we'll return the value\n" );
- $$ = f_new_inst(FI_RETURN);
- $$->a1.p = $2;
+ $$ = f_new_inst(FI_RETURN, $2);
}
- | rtadot dynamic_attr '=' term ';' {
- $$ = f_new_inst_da(FI_EA_SET, $2);
- $$->a1.p = $4;
+ | dynamic_attr '=' term ';' {
+ $$ = f_new_inst(FI_EA_SET, $3, $1);
}
- | rtadot static_attr '=' term ';' {
- $$ = f_new_inst_sa(FI_RTA_SET, $2);
- if (!$$->a1.i)
+ | static_attr '=' term ';' {
+ if ($1.readonly)
cf_error( "This static attribute is read-only.");
- $$->a1.p = $4;
+ $$ = f_new_inst(FI_RTA_SET, $3, $1);
}
| PREFERENCE '=' term ';' {
- $$ = f_new_inst(FI_PREF_SET);
- $$->a1.p = $3;
+ $$ = f_new_inst(FI_PREF_SET, $3);
+ }
+ | UNSET '(' dynamic_attr ')' ';' {
+ $$ = f_new_inst(FI_EA_UNSET, $3);
+ }
+ | break_command print_list ';' {
+ struct f_inst *breaker = f_new_inst(FI_DIE, $1);
+ if ($2) {
+ struct f_inst *printer = f_new_inst(FI_PRINT, $2);
+ struct f_inst *flusher = f_new_inst(FI_FLUSH);
+ printer->next = flusher;
+ flusher->next = breaker;
+ $$ = printer;
+ } else
+ $$ = breaker;
+ }
+ | PRINT print_list ';' {
+ $$ = f_new_inst(FI_PRINT, $2);
+ $$->next = f_new_inst(FI_FLUSH);
}
- | UNSET '(' rtadot dynamic_attr ')' ';' {
- $$ = f_new_inst_da(FI_EA_SET, $4);
- $$->aux = EAF_TYPE_UNDEF | EAF_TEMP;
- $$->a1.p = NULL;
+ | PRINTN print_list ';' {
+ $$ = f_new_inst(FI_PRINT, $2);
}
- | break_command print_list ';' { $$ = f_new_inst(FI_PRINT_AND_DIE); $$->a1.p = $2; $$->a2.i = $1; }
- | function_call ';' { $$ = $1; }
+ | function_call ';' { $$ = f_new_inst(FI_DROP_RESULT, $1); }
| CASE term '{' switch_body '}' {
- $$ = f_new_inst(FI_SWITCH);
- $$->a1.p = $2;
- $$->a2.p = build_tree( $4 );
+ $$ = f_new_inst(FI_SWITCH, $2, build_tree($4));
}
- | rtadot dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($2); }
- | rtadot dynamic_attr '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex( FI_PATH_PREPEND, 'x', $2, $6 ); }
- | rtadot dynamic_attr '.' ADD '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_ADD_DEL, 'a', $2, $6 ); }
- | rtadot dynamic_attr '.' DELETE '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_ADD_DEL, 'd', $2, $6 ); }
- | rtadot dynamic_attr '.' FILTER '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_ADD_DEL, 'f', $2, $6 ); }
+ | dynamic_attr '.' EMPTY ';' { $$ = f_generate_empty($1); }
+ | dynamic_attr '.' PREPEND '(' term ')' ';' { $$ = f_generate_complex( FI_PATH_PREPEND, $1, $5 ); }
+ | dynamic_attr '.' ADD '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_ADD, $1, $5 ); }
+ | dynamic_attr '.' DELETE '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_DEL, $1, $5 ); }
+ | dynamic_attr '.' FILTER '(' term ')' ';' { $$ = f_generate_complex( FI_CLIST_FILTER, $1, $5 ); }
| BT_ASSERT '(' get_cf_position term get_cf_position ')' ';' { $$ = assert_done($4, $3 + 1, $5 - 1); }
+ | BT_CHECK_ASSIGN '(' get_cf_position lvalue get_cf_position ',' term ')' ';' { $$ = assert_assign(&$4, $7, $3 + 1, $5 - 1); }
;
get_cf_position:
@@ -1069,5 +1052,10 @@ get_cf_position:
$$ = cf_text;
};
+lvalue:
+ CF_SYM_KNOWN { cf_assert_symbol($1, SYM_VARIABLE); $$ = (struct f_lval) { .type = F_LVAL_VARIABLE, .sym = $1 }; }
+ | PREFERENCE { $$ = (struct f_lval) { .type = F_LVAL_PREFERENCE }; }
+ | static_attr { $$ = (struct f_lval) { .type = F_LVAL_SA, .sa = $1 }; }
+ | dynamic_attr { $$ = (struct f_lval) { .type = F_LVAL_EA, .da = $1 }; };
CF_END
diff --git a/filter/data.c b/filter/data.c
new file mode 100644
index 00000000..db55070f
--- /dev/null
+++ b/filter/data.c
@@ -0,0 +1,537 @@
+/*
+ * Filters: utility functions
+ *
+ * (c) 1998 Pavel Machek <pavel@ucw.cz>
+ * (c) 2019 Maria Matejka <mq@jmq.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ *
+ */
+
+#include "nest/bird.h"
+#include "lib/lists.h"
+#include "lib/resource.h"
+#include "lib/socket.h"
+#include "lib/string.h"
+#include "lib/unaligned.h"
+#include "lib/net.h"
+#include "lib/ip.h"
+#include "nest/route.h"
+#include "nest/protocol.h"
+#include "nest/iface.h"
+#include "nest/attrs.h"
+#include "conf/conf.h"
+#include "filter/filter.h"
+#include "filter/f-inst.h"
+#include "filter/data.h"
+
+const struct f_val f_const_empty_path = {
+ .type = T_PATH,
+ .val.ad = &null_adata,
+}, f_const_empty_clist = {
+ .type = T_CLIST,
+ .val.ad = &null_adata,
+}, f_const_empty_eclist = {
+ .type = T_ECLIST,
+ .val.ad = &null_adata,
+}, f_const_empty_lclist = {
+ .type = T_LCLIST,
+ .val.ad = &null_adata,
+};
+
+static struct adata *
+adata_empty(struct linpool *pool, int l)
+{
+ struct adata *res = lp_alloc(pool, sizeof(struct adata) + l);
+ res->length = l;
+ return res;
+}
+
+static void
+pm_format(const struct f_path_mask *p, buffer *buf)
+{
+ buffer_puts(buf, "[= ");
+
+ for (uint i=0; i<p->len; i++)
+ {
+ switch(p->item[i].kind)
+ {
+ case PM_ASN:
+ buffer_print(buf, "%u ", p->item[i].asn);
+ break;
+
+ case PM_QUESTION:
+ buffer_puts(buf, "? ");
+ break;
+
+ case PM_ASTERISK:
+ buffer_puts(buf, "* ");
+ break;
+
+ case PM_ASN_RANGE:
+ buffer_print(buf, "%u..%u ", p->item[i].from, p->item[i].to);
+ break;
+
+ case PM_ASN_EXPR:
+ ASSERT(0);
+ }
+
+ }
+
+ buffer_puts(buf, "=]");
+}
+
+static inline int
+lcomm_cmp(lcomm v1, lcomm v2)
+{
+ if (v1.asn != v2.asn)
+ return (v1.asn > v2.asn) ? 1 : -1;
+ if (v1.ldp1 != v2.ldp1)
+ return (v1.ldp1 > v2.ldp1) ? 1 : -1;
+ if (v1.ldp2 != v2.ldp2)
+ return (v1.ldp2 > v2.ldp2) ? 1 : -1;
+ return 0;
+}
+
+/**
+ * val_compare - compare two values
+ * @v1: first value
+ * @v2: second value
+ *
+ * Compares two values and returns -1, 0, 1 on <, =, > or F_CMP_ERROR on
+ * error. Tree module relies on this giving consistent results so
+ * that it can be used for building balanced trees.
+ */
+int
+val_compare(const struct f_val *v1, const struct f_val *v2)
+{
+ if (v1->type != v2->type) {
+ if (v1->type == T_VOID) /* Hack for else */
+ return -1;
+ if (v2->type == T_VOID)
+ return 1;
+
+ /* IP->Quad implicit conversion */
+ if ((v1->type == T_QUAD) && val_is_ip4(v2))
+ return uint_cmp(v1->val.i, ipa_to_u32(v2->val.ip));
+ if (val_is_ip4(v1) && (v2->type == T_QUAD))
+ return uint_cmp(ipa_to_u32(v1->val.ip), v2->val.i);
+
+ debug( "Types do not match in val_compare\n" );
+ return F_CMP_ERROR;
+ }
+
+ switch (v1->type) {
+ case T_VOID:
+ return 0;
+ case T_ENUM:
+ case T_INT:
+ case T_BOOL:
+ case T_PAIR:
+ case T_QUAD:
+ return uint_cmp(v1->val.i, v2->val.i);
+ case T_EC:
+ case T_RD:
+ return u64_cmp(v1->val.ec, v2->val.ec);
+ case T_LC:
+ return lcomm_cmp(v1->val.lc, v2->val.lc);
+ case T_IP:
+ return ipa_compare(v1->val.ip, v2->val.ip);
+ case T_NET:
+ return net_compare(v1->val.net, v2->val.net);
+ case T_STRING:
+ return strcmp(v1->val.s, v2->val.s);
+ default:
+ return F_CMP_ERROR;
+ }
+}
+
+static inline int
+pmi_same(const struct f_path_mask_item *mi1, const struct f_path_mask_item *mi2)
+{
+ if (mi1->kind != mi2->kind)
+ return 0;
+
+ switch (mi1->kind) {
+ case PM_ASN:
+ if (mi1->asn != mi2->asn)
+ return 0;
+ break;
+ case PM_ASN_EXPR:
+ if (!f_same(mi1->expr, mi2->expr))
+ return 0;
+ break;
+ case PM_ASN_RANGE:
+ if (mi1->from != mi2->from)
+ return 0;
+ if (mi1->to != mi2->to)
+ return 0;
+ break;
+ }
+
+ return 1;
+}
+
+static int
+pm_same(const struct f_path_mask *m1, const struct f_path_mask *m2)
+{
+ if (m1->len != m2->len)
+
+ for (uint i=0; i<m1->len; i++)
+ if (!pmi_same(&(m1->item[i]), &(m2->item[i])))
+ return 0;
+
+ return 1;
+}
+
+/**
+ * val_same - compare two values
+ * @v1: first value
+ * @v2: second value
+ *
+ * Compares two values and returns 1 if they are same and 0 if not.
+ * Comparison of values of different types is valid and returns 0.
+ */
+int
+val_same(const struct f_val *v1, const struct f_val *v2)
+{
+ int rc;
+
+ rc = val_compare(v1, v2);
+ if (rc != F_CMP_ERROR)
+ return !rc;
+
+ if (v1->type != v2->type)
+ return 0;
+
+ switch (v1->type) {
+ case T_PATH_MASK:
+ return pm_same(v1->val.path_mask, v2->val.path_mask);
+ case T_PATH_MASK_ITEM:
+ return pmi_same(&(v1->val.pmi), &(v2->val.pmi));
+ case T_PATH:
+ case T_CLIST:
+ case T_ECLIST:
+ case T_LCLIST:
+ return adata_same(v1->val.ad, v2->val.ad);
+ case T_SET:
+ return same_tree(v1->val.t, v2->val.t);
+ case T_PREFIX_SET:
+ return trie_same(v1->val.ti, v2->val.ti);
+ default:
+ bug("Invalid type in val_same(): %x", v1->type);
+ }
+}
+
+int
+clist_set_type(const struct f_tree *set, struct f_val *v)
+{
+ switch (set->from.type)
+ {
+ case T_PAIR:
+ v->type = T_PAIR;
+ return 1;
+
+ case T_QUAD:
+ v->type = T_QUAD;
+ return 1;
+
+ case T_IP:
+ if (val_is_ip4(&(set->from)) && val_is_ip4(&(set->to)))
+ {
+ v->type = T_QUAD;
+ return 1;
+ }
+ /* Fall through */
+ default:
+ v->type = T_VOID;
+ return 0;
+ }
+}
+
+static int
+clist_match_set(const struct adata *clist, const struct f_tree *set)
+{
+ if (!clist)
+ return 0;
+
+ struct f_val v;
+ if (!clist_set_type(set, &v))
+ return F_CMP_ERROR;
+
+ u32 *l = (u32 *) clist->data;
+ u32 *end = l + clist->length/4;
+
+ while (l < end) {
+ v.val.i = *l++;
+ if (find_tree(set, &v))
+ return 1;
+ }
+ return 0;
+}
+
+static int
+eclist_match_set(const struct adata *list, const struct f_tree *set)
+{
+ if (!list)
+ return 0;
+
+ if (!eclist_set_type(set))
+ return F_CMP_ERROR;
+
+ struct f_val v;
+ u32 *l = int_set_get_data(list);
+ int len = int_set_get_size(list);
+ int i;
+
+ v.type = T_EC;
+ for (i = 0; i < len; i += 2) {
+ v.val.ec = ec_get(l, i);
+ if (find_tree(set, &v))
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+lclist_match_set(const struct adata *list, const struct f_tree *set)
+{
+ if (!list)
+ return 0;
+
+ if (!lclist_set_type(set))
+ return F_CMP_ERROR;
+
+ struct f_val v;
+ u32 *l = int_set_get_data(list);
+ int len = int_set_get_size(list);
+ int i;
+
+ v.type = T_LC;
+ for (i = 0; i < len; i += 3) {
+ v.val.lc = lc_get(l, i);
+ if (find_tree(set, &v))
+ return 1;
+ }
+
+ return 0;
+}
+
+const struct adata *
+clist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos)
+{
+ if (!list)
+ return NULL;
+
+ int tree = (set->type == T_SET); /* 1 -> set is T_SET, 0 -> set is T_CLIST */
+ struct f_val v;
+ if (tree)
+ clist_set_type(set->val.t, &v);
+ else
+ v.type = T_PAIR;
+
+ int len = int_set_get_size(list);
+ u32 *l = int_set_get_data(list);
+ u32 tmp[len];
+ u32 *k = tmp;
+ u32 *end = l + len;
+
+ while (l < end) {
+ v.val.i = *l++;
+ /* pos && member(val, set) || !pos && !member(val, set), member() depends on tree */
+ if ((tree ? !!find_tree(set->val.t, &v) : int_set_contains(set->val.ad, v.val.i)) == pos)
+ *k++ = v.val.i;
+ }
+
+ uint nl = (k - tmp) * sizeof(u32);
+ if (nl == list->length)
+ return list;
+
+ struct adata *res = adata_empty(pool, nl);
+ memcpy(res->data, tmp, nl);
+ return res;
+}
+
+const struct adata *
+eclist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos)
+{
+ if (!list)
+ return NULL;
+
+ int tree = (set->type == T_SET); /* 1 -> set is T_SET, 0 -> set is T_CLIST */
+ struct f_val v;
+
+ int len = int_set_get_size(list);
+ u32 *l = int_set_get_data(list);
+ u32 tmp[len];
+ u32 *k = tmp;
+ int i;
+
+ v.type = T_EC;
+ for (i = 0; i < len; i += 2) {
+ v.val.ec = ec_get(l, i);
+ /* pos && member(val, set) || !pos && !member(val, set), member() depends on tree */
+ if ((tree ? !!find_tree(set->val.t, &v) : ec_set_contains(set->val.ad, v.val.ec)) == pos) {
+ *k++ = l[i];
+ *k++ = l[i+1];
+ }
+ }
+
+ uint nl = (k - tmp) * sizeof(u32);
+ if (nl == list->length)
+ return list;
+
+ struct adata *res = adata_empty(pool, nl);
+ memcpy(res->data, tmp, nl);
+ return res;
+}
+
+const struct adata *
+lclist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos)
+{
+ if (!list)
+ return NULL;
+
+ int tree = (set->type == T_SET); /* 1 -> set is T_SET, 0 -> set is T_CLIST */
+ struct f_val v;
+
+ int len = int_set_get_size(list);
+ u32 *l = int_set_get_data(list);
+ u32 tmp[len];
+ u32 *k = tmp;
+ int i;
+
+ v.type = T_LC;
+ for (i = 0; i < len; i += 3) {
+ v.val.lc = lc_get(l, i);
+ /* pos && member(val, set) || !pos && !member(val, set), member() depends on tree */
+ if ((tree ? !!find_tree(set->val.t, &v) : lc_set_contains(set->val.ad, v.val.lc)) == pos)
+ k = lc_copy(k, l+i);
+ }
+
+ uint nl = (k - tmp) * sizeof(u32);
+ if (nl == list->length)
+ return list;
+
+ struct adata *res = adata_empty(pool, nl);
+ memcpy(res->data, tmp, nl);
+ return res;
+}
+
+/**
+ * val_in_range - implement |~| operator
+ * @v1: element
+ * @v2: set
+ *
+ * Checks if @v1 is element (|~| operator) of @v2.
+ */
+int
+val_in_range(const struct f_val *v1, const struct f_val *v2)
+{
+ if ((v1->type == T_PATH) && (v2->type == T_PATH_MASK))
+ return as_path_match(v1->val.ad, v2->val.path_mask);
+
+ if ((v1->type == T_INT) && (v2->type == T_PATH))
+ return as_path_contains(v2->val.ad, v1->val.i, 1);
+
+ if (((v1->type == T_PAIR) || (v1->type == T_QUAD)) && (v2->type == T_CLIST))
+ return int_set_contains(v2->val.ad, v1->val.i);
+ /* IP->Quad implicit conversion */
+ if (val_is_ip4(v1) && (v2->type == T_CLIST))
+ return int_set_contains(v2->val.ad, ipa_to_u32(v1->val.ip));
+
+ if ((v1->type == T_EC) && (v2->type == T_ECLIST))
+ return ec_set_contains(v2->val.ad, v1->val.ec);
+
+ if ((v1->type == T_LC) && (v2->type == T_LCLIST))
+ return lc_set_contains(v2->val.ad, v1->val.lc);
+
+ if ((v1->type == T_STRING) && (v2->type == T_STRING))
+ return patmatch(v2->val.s, v1->val.s);
+
+ if ((v1->type == T_IP) && (v2->type == T_NET))
+ return ipa_in_netX(v1->val.ip, v2->val.net);
+
+ if ((v1->type == T_NET) && (v2->type == T_NET))
+ return net_in_netX(v1->val.net, v2->val.net);
+
+ if ((v1->type == T_NET) && (v2->type == T_PREFIX_SET))
+ return trie_match_net(v2->val.ti, v1->val.net);
+
+ if (v2->type != T_SET)
+ return F_CMP_ERROR;
+
+ /* With integrated Quad<->IP implicit conversion */
+ if ((v1->type == v2->val.t->from.type) ||
+ ((v1->type == T_QUAD) && val_is_ip4(&(v2->val.t->from)) && val_is_ip4(&(v2->val.t->to))))
+ return !!find_tree(v2->val.t, v1);
+
+ if (v1->type == T_CLIST)
+ return clist_match_set(v1->val.ad, v2->val.t);
+
+ if (v1->type == T_ECLIST)
+ return eclist_match_set(v1->val.ad, v2->val.t);
+
+ if (v1->type == T_LCLIST)
+ return lclist_match_set(v1->val.ad, v2->val.t);
+
+ if (v1->type == T_PATH)
+ return as_path_match_set(v1->val.ad, v2->val.t);
+
+ return F_CMP_ERROR;
+}
+
+/*
+ * val_format - format filter value
+ */
+void
+val_format(const struct f_val *v, buffer *buf)
+{
+ char buf2[1024];
+ switch (v->type)
+ {
+ case T_VOID: buffer_puts(buf, "(void)"); return;
+ case T_BOOL: buffer_puts(buf, v->val.i ? "TRUE" : "FALSE"); return;
+ case T_INT: buffer_print(buf, "%u", v->val.i); return;
+ case T_STRING: buffer_print(buf, "%s", v->val.s); return;
+ case T_IP: buffer_print(buf, "%I", v->val.ip); return;
+ case T_NET: buffer_print(buf, "%N", v->val.net); return;
+ case T_PAIR: buffer_print(buf, "(%u,%u)", v->val.i >> 16, v->val.i & 0xffff); return;
+ case T_QUAD: buffer_print(buf, "%R", v->val.i); return;
+ case T_EC: ec_format(buf2, v->val.ec); buffer_print(buf, "%s", buf2); return;
+ case T_LC: lc_format(buf2, v->val.lc); buffer_print(buf, "%s", buf2); return;
+ case T_RD: rd_format(v->val.ec, buf2, 1024); buffer_print(buf, "%s", buf2); return;
+ case T_PREFIX_SET: trie_format(v->val.ti, buf); return;
+ case T_SET: tree_format(v->val.t, buf); return;
+ case T_ENUM: buffer_print(buf, "(enum %x)%u", v->type, v->val.i); return;
+ case T_PATH: as_path_format(v->val.ad, buf2, 1000); buffer_print(buf, "(path %s)", buf2); return;
+ case T_CLIST: int_set_format(v->val.ad, 1, -1, buf2, 1000); buffer_print(buf, "(clist %s)", buf2); return;
+ case T_ECLIST: ec_set_format(v->val.ad, -1, buf2, 1000); buffer_print(buf, "(eclist %s)", buf2); return;
+ case T_LCLIST: lc_set_format(v->val.ad, -1, buf2, 1000); buffer_print(buf, "(lclist %s)", buf2); return;
+ case T_PATH_MASK: pm_format(v->val.path_mask, buf); return;
+ default: buffer_print(buf, "[unknown type %x]", v->type); return;
+ }
+}
+
+char *
+val_format_str(struct linpool *lp, const struct f_val *v) {
+ buffer b;
+ LOG_BUFFER_INIT(b);
+ val_format(v, &b);
+ return lp_strdup(lp, b.start);
+}
+
+
+static char val_dump_buffer[1024];
+const char *
+val_dump(const struct f_val *v) {
+ static buffer b = {
+ .start = val_dump_buffer,
+ .end = val_dump_buffer + 1024,
+ };
+ b.pos = b.start;
+ val_format(v, &b);
+ return val_dump_buffer;
+}
+
diff --git a/filter/data.h b/filter/data.h
new file mode 100644
index 00000000..083595f4
--- /dev/null
+++ b/filter/data.h
@@ -0,0 +1,203 @@
+/*
+ * BIRD Internet Routing Daemon -- Dynamic data structures
+ *
+ * (c) 1999 Pavel Machek <pavel@ucw.cz>
+ * (c) 2018--2019 Maria Matejka <mq@jmq.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_FILTER_DATA_H_
+#define _BIRD_FILTER_DATA_H_
+
+#include "nest/bird.h"
+
+/* Type numbers must be in 0..0xff range */
+#define T_MASK 0xff
+
+/* Internal types */
+enum f_type {
+/* Nothing. Simply nothing. */
+ T_VOID = 0,
+
+/* User visible types, which fit in int */
+ T_INT = 0x10,
+ T_BOOL = 0x11,
+ T_PAIR = 0x12, /* Notice that pair is stored as integer: first << 16 | second */
+ T_QUAD = 0x13,
+
+/* Put enumerational types in 0x30..0x3f range */
+ T_ENUM_LO = 0x30,
+ T_ENUM_HI = 0x3f,
+
+ T_ENUM_RTS = 0x30,
+ T_ENUM_BGP_ORIGIN = 0x31,
+ T_ENUM_SCOPE = 0x32,
+ T_ENUM_RTC = 0x33,
+ T_ENUM_RTD = 0x34,
+ T_ENUM_ROA = 0x35,
+ T_ENUM_NETTYPE = 0x36,
+ T_ENUM_RA_PREFERENCE = 0x37,
+
+/* new enums go here */
+ T_ENUM_EMPTY = 0x3f, /* Special hack for atomic_aggr */
+
+#define T_ENUM T_ENUM_LO ... T_ENUM_HI
+
+/* Bigger ones */
+ T_IP = 0x20,
+ T_NET = 0x21,
+ T_STRING = 0x22,
+ T_PATH_MASK = 0x23, /* mask for BGP path */
+ T_PATH = 0x24, /* BGP path */
+ T_CLIST = 0x25, /* Community list */
+ T_EC = 0x26, /* Extended community value, u64 */
+ T_ECLIST = 0x27, /* Extended community list */
+ T_LC = 0x28, /* Large community value, lcomm */
+ T_LCLIST = 0x29, /* Large community list */
+ T_RD = 0x2a, /* Route distinguisher for VPN addresses */
+ T_PATH_MASK_ITEM = 0x2b, /* Path mask item for path mask constructors */
+
+ T_SET = 0x80,
+ T_PREFIX_SET = 0x81,
+} PACKED;
+
+/* Filter value; size of this affects filter memory consumption */
+struct f_val {
+ enum f_type type; /* T_* */
+ union {
+ uint i;
+ u64 ec;
+ lcomm lc;
+ ip_addr ip;
+ const net_addr *net;
+ char *s;
+ const struct f_tree *t;
+ const struct f_trie *ti;
+ const struct adata *ad;
+ const struct f_path_mask *path_mask;
+ struct f_path_mask_item pmi;
+ } val;
+};
+
+/* Dynamic attribute definition (eattrs) */
+struct f_dynamic_attr {
+ u8 type; /* EA type (EAF_*) */
+ u8 bit; /* For bitfield accessors */
+ enum f_type f_type; /* Filter type */
+ uint ea_code; /* EA code */
+};
+
+enum f_sa_code {
+ SA_FROM = 1,
+ SA_GW,
+ SA_NET,
+ SA_PROTO,
+ SA_SOURCE,
+ SA_SCOPE,
+ SA_DEST,
+ SA_IFNAME,
+ SA_IFINDEX,
+} PACKED;
+
+/* Static attribute definition (members of struct rta) */
+struct f_static_attr {
+ enum f_type f_type; /* Filter type */
+ enum f_sa_code sa_code; /* Static attribute id */
+ int readonly:1; /* Don't allow writing */
+};
+
+/* Filter l-value type */
+enum f_lval_type {
+ F_LVAL_VARIABLE,
+ F_LVAL_PREFERENCE,
+ F_LVAL_SA,
+ F_LVAL_EA,
+};
+
+/* Filter l-value */
+struct f_lval {
+ enum f_lval_type type;
+ union {
+ struct symbol *sym;
+ struct f_dynamic_attr da;
+ struct f_static_attr sa;
+ };
+};
+
+/* IP prefix range structure */
+struct f_prefix {
+ net_addr net; /* The matching prefix must match this net */
+ u8 lo, hi; /* And its length must fit between lo and hi */
+};
+
+struct f_tree {
+ struct f_tree *left, *right;
+ struct f_val from, to;
+ void *data;
+};
+
+struct f_trie_node
+{
+ ip_addr addr, mask, accept;
+ uint plen;
+ struct f_trie_node *c[2];
+};
+
+struct f_trie
+{
+ linpool *lp;
+ int zero;
+ uint node_size;
+ struct f_trie_node root[0]; /* Root trie node follows */
+};
+
+struct f_tree *f_new_tree(void);
+struct f_tree *build_tree(struct f_tree *);
+const struct f_tree *find_tree(const struct f_tree *t, const struct f_val *val);
+int same_tree(const struct f_tree *t0, const struct f_tree *t2);
+void tree_format(const struct f_tree *t, buffer *buf);
+
+struct f_trie *f_new_trie(linpool *lp, uint node_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);
+void trie_format(const struct f_trie *t, buffer *buf);
+
+#define F_CMP_ERROR 999
+
+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);
+char *val_format_str(struct linpool *lp, const struct f_val *v);
+const char *val_dump(const struct f_val *v);
+
+static inline int val_is_ip4(const struct f_val *v)
+{ return (v->type == T_IP) && ipa_is_ip4(v->val.ip); }
+int val_in_range(const struct f_val *v1, const struct f_val *v2);
+
+int clist_set_type(const struct f_tree *set, struct f_val *v);
+static inline int eclist_set_type(const struct f_tree *set)
+{ return set->from.type == T_EC; }
+static inline int lclist_set_type(const struct f_tree *set)
+{ return set->from.type == T_LC; }
+
+const struct adata *clist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos);
+const struct adata *eclist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos);
+const struct adata *lclist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos);
+
+
+/* Special undef value for paths and clists */
+static inline int
+undef_value(struct f_val v)
+{
+ return ((v.type == T_PATH) || (v.type == T_CLIST) ||
+ (v.type == T_ECLIST) || (v.type == T_LCLIST)) &&
+ (v.val.ad == &null_adata);
+}
+
+extern const struct f_val f_const_empty_path, f_const_empty_clist, f_const_empty_eclist, f_const_empty_lclist;
+
+enum filter_return f_eval(const struct f_line *expr, struct linpool *tmp_pool, struct f_val *pres);
+
+#endif
diff --git a/filter/decl.m4 b/filter/decl.m4
new file mode 100644
index 00000000..4d5b70dc
--- /dev/null
+++ b/filter/decl.m4
@@ -0,0 +1,583 @@
+m4_divert(-1)m4_dnl
+#
+# BIRD -- Construction of per-instruction structures
+#
+# (c) 2018 Maria Matejka <mq@jmq.cz>
+#
+# Can be freely distributed and used under the terms of the GNU GPL.
+#
+# THIS IS A M4 MACRO FILE GENERATING 3 FILES ALTOGETHER.
+# KEEP YOUR HANDS OFF UNLESS YOU KNOW WHAT YOU'RE DOING.
+# EDITING AND DEBUGGING THIS FILE MAY DAMAGE YOUR BRAIN SERIOUSLY.
+#
+# But you're welcome to read and edit and debug if you aren't scared.
+#
+# Uncomment the following line to get exhaustive debug output.
+# m4_debugmode(aceflqtx)
+#
+# How it works:
+# 1) Instruction to code conversion (uses diversions 100..199)
+# 2) Code wrapping (uses diversions 1..99)
+# 3) Final preparation (uses diversions 200..299)
+# 4) Shipout
+#
+# See below for detailed description.
+#
+#
+# 1) Instruction to code conversion
+# The code provided in f-inst.c between consecutive INST() calls
+# is interleaved for many different places. It is here processed
+# and split into separate instances where split-by-instruction
+# happens. These parts are stored in temporary diversions listed:
+#
+# 101 content of per-inst struct
+# 102 constructor arguments
+# 103 constructor body
+# 104 dump line item content
+# (there may be nothing in dump-line content and
+# it must be handled specially in phase 2)
+# 105 linearize body
+# 106 comparator body
+# 107 struct f_line_item content
+# 108 interpreter body
+#
+# Here are macros to allow you to _divert to the right directions.
+m4_define(FID_STRUCT_IN, `m4_divert(101)')
+m4_define(FID_NEW_ARGS, `m4_divert(102)')
+m4_define(FID_NEW_BODY, `m4_divert(103)')
+m4_define(FID_DUMP_BODY, `m4_divert(104)m4_define([[FID_DUMP_BODY_EXISTS]])')
+m4_define(FID_LINEARIZE_BODY, `m4_divert(105)')
+m4_define(FID_SAME_BODY, `m4_divert(106)')
+m4_define(FID_LINE_IN, `m4_divert(107)')
+m4_define(FID_INTERPRET_BODY, `m4_divert(108)')
+
+# Sometimes you want slightly different code versions in different
+# outputs.
+# Use FID_HIC(code for inst-gen.h, code for inst-gen.c, code for inst-interpret.c)
+# and put it into [[ ]] quotes if it shall contain commas.
+m4_define(FID_HIC, `m4_ifelse(TARGET, [[H]], [[$1]], TARGET, [[I]], [[$2]], TARGET, [[C]], [[$3]])')
+
+# In interpreter code, this is quite common.
+m4_define(FID_INTERPRET_EXEC, `FID_HIC(,[[FID_INTERPRET_BODY()]],[[m4_divert(-1)]])')
+m4_define(FID_INTERPRET_NEW, `FID_HIC(,[[m4_divert(-1)]],[[FID_INTERPRET_BODY()]])')
+
+# If the instruction is never converted to constant, the interpret
+# code is not produced at all for constructor
+m4_define(NEVER_CONSTANT, `m4_define([[INST_NEVER_CONSTANT]])')
+m4_define(FID_IFCONST, `m4_ifdef([[INST_NEVER_CONSTANT]],[[$2]],[[$1]])')
+
+# If the instruction has some attributes (here called members),
+# these are typically carried with the instruction from constructor
+# to interpreter. This yields a line of code everywhere on the path.
+# FID_MEMBER is a macro to help with this task.
+m4_define(FID_MEMBER, `m4_dnl
+FID_LINE_IN()m4_dnl
+ $1 $2;
+FID_STRUCT_IN()m4_dnl
+ $1 $2;
+FID_NEW_ARGS()m4_dnl
+ , $1 $2
+FID_NEW_BODY()m4_dnl
+whati->$2 = $2;
+FID_LINEARIZE_BODY()m4_dnl
+item->$2 = whati->$2;
+m4_ifelse($3,,,[[
+FID_SAME_BODY()m4_dnl
+if ($3) return 0;
+]])
+m4_ifelse($4,,,[[
+FID_DUMP_BODY()m4_dnl
+debug("%s" $4 "\n", INDENT, $5);
+]])
+FID_INTERPRET_EXEC()m4_dnl
+const $1 $2 = whati->$2
+FID_INTERPRET_BODY')
+
+# Instruction arguments are needed only until linearization is done.
+# This puts the arguments into the filter line to be executed before
+# the instruction itself.
+#
+# To achieve this, ARG_ANY must be called before anything writes into
+# the instruction line as it moves the instruction pointer forward.
+m4_define(ARG_ANY, `
+FID_STRUCT_IN()m4_dnl
+ struct f_inst * f$1;
+FID_NEW_ARGS()m4_dnl
+ , struct f_inst * f$1
+FID_NEW_BODY
+whati->f$1 = f$1;
+for (const struct f_inst *child = f$1; child; child = child->next) {
+ what->size += child->size;
+FID_IFCONST([[
+ if (child->fi_code != FI_CONSTANT)
+ constargs = 0;
+]])
+}
+FID_LINEARIZE_BODY
+pos = linearize(dest, whati->f$1, pos);
+FID_INTERPRET_BODY()')
+
+# Some instructions accept variable number of arguments.
+m4_define(VARARG, `
+FID_NEW_ARGS()m4_dnl
+ , struct f_inst * fvar
+FID_STRUCT_IN()m4_dnl
+ struct f_inst * fvar;
+ uint varcount;
+FID_LINE_IN()m4_dnl
+ uint varcount;
+FID_NEW_BODY()m4_dnl
+whati->varcount = 0;
+whati->fvar = fvar;
+for (const struct f_inst *child = fvar; child; child = child->next, whati->varcount++) {
+ what->size += child->size;
+FID_IFCONST([[
+ if (child->fi_code != FI_CONSTANT)
+ constargs = 0;
+]])
+}
+FID_IFCONST([[
+ const struct f_inst **items = NULL;
+ if (constargs) {
+ items = alloca(whati->varcount * sizeof(struct f_inst *));
+ const struct f_inst *child = fvar;
+ for (uint i=0; child; i++)
+ child = (items[i] = child)->next;
+ }
+]])
+FID_LINEARIZE_BODY()m4_dnl
+ pos = linearize(dest, whati->fvar, pos);
+ item->varcount = whati->varcount;
+FID_DUMP_BODY()m4_dnl
+ debug("%snumber of varargs %u\n", INDENT, item->varcount);
+FID_SAME_BODY()m4_dnl
+ if (f1->varcount != f2->varcount) return 0;
+FID_INTERPRET_BODY()
+FID_HIC(,[[
+ if (fstk->vcnt < whati->varcount) runtime("Stack underflow");
+ fstk->vcnt -= whati->varcount;
+]],)
+')
+
+# Some arguments need to check their type. After that, ARG_ANY is called.
+m4_define(ARG, `ARG_ANY($1)
+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
+FID_INTERPRET_BODY()')
+
+# Executing another filter line. This replaces the recursion
+# that was needed in the former implementation.
+m4_define(LINEX, `FID_INTERPRET_EXEC()LINEX_($1)FID_INTERPRET_NEW()return $1 FID_INTERPRET_BODY()')
+m4_define(LINEX_, `do {
+ fstk->estk[fstk->ecnt].pos = 0;
+ fstk->estk[fstk->ecnt].line = $1;
+ fstk->estk[fstk->ecnt].ventry = fstk->vcnt;
+ fstk->estk[fstk->ecnt].vbase = fstk->estk[fstk->ecnt-1].vbase;
+ fstk->estk[fstk->ecnt].emask = 0;
+ fstk->ecnt++;
+} while (0)')
+
+m4_define(LINE, `
+FID_LINE_IN()m4_dnl
+ const struct f_line * fl$1;
+FID_STRUCT_IN()m4_dnl
+ struct f_inst * f$1;
+FID_NEW_ARGS()m4_dnl
+ , struct f_inst * f$1
+FID_NEW_BODY()m4_dnl
+whati->f$1 = f$1;
+FID_DUMP_BODY()m4_dnl
+f_dump_line(item->fl$1, indent + 1);
+FID_LINEARIZE_BODY()m4_dnl
+item->fl$1 = f_linearize(whati->f$1);
+FID_SAME_BODY()m4_dnl
+if (!f_same(f1->fl$1, f2->fl$1)) return 0;
+FID_INTERPRET_EXEC()m4_dnl
+do { if (whati->fl$1) {
+ LINEX_(whati->fl$1);
+} } while(0)
+FID_INTERPRET_NEW()m4_dnl
+return whati->f$1
+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_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 } ]])')
+
+# 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)')
+m4_define(STATIC_ATTR, `FID_MEMBER(struct f_static_attr, sa, f1->sa.sa_code != f2->sa.sa_code,,)')
+m4_define(DYNAMIC_ATTR, `FID_MEMBER(struct f_dynamic_attr, da, f1->da.ea_code != f2->da.ea_code,,)')
+m4_define(ACCESS_RTE, `FID_HIC(,[[do { if (!fs->rte) runtime("No route to access"); } while (0)]],NEVER_CONSTANT())')
+
+# 2) Code wrapping
+# The code produced in 1xx temporary diversions is a raw code without
+# any auxiliary commands and syntactical structures around. When the
+# instruction is done, INST_FLUSH is called. More precisely, it is called
+# at the beginning of INST() call and at the end of file.
+#
+# INST_FLUSH picks all the temporary diversions, wraps their content
+# into appropriate headers and structures and saves them into global
+# diversions listed:
+#
+# 4 enum fi_code
+# 5 enum fi_code to string
+# 6 dump line item
+# 7 dump line item callers
+# 8 linearize
+# 9 same (filter comparator)
+# 1 union in struct f_inst
+# 3 constructors + interpreter
+#
+# These global diversions contain blocks of code that can be directly
+# put into the final file, yet it still can't be written out now as
+# every instruction writes to all of these diversions.
+
+# Code wrapping diversion names. Here we want an explicit newline
+# after the C comment.
+m4_define(FID_ZONE, `m4_divert($1) /* $2 for INST_NAME() */
+')
+m4_define(FID_INST, `FID_ZONE(1, Instruction structure for config)')
+m4_define(FID_LINE, `FID_ZONE(2, Instruction structure for interpreter)')
+m4_define(FID_NEW, `FID_ZONE(3, Constructor)')
+m4_define(FID_ENUM, `FID_ZONE(4, Code enum)')
+m4_define(FID_ENUM_STR, `FID_ZONE(5, Code enum to string)')
+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)')
+
+# This macro does all the code wrapping. See inline comments.
+m4_define(INST_FLUSH, `m4_ifdef([[INST_NAME]], [[
+FID_ENUM()m4_dnl Contents of enum fi_code { ... }
+ INST_NAME(),
+FID_ENUM_STR()m4_dnl Contents of const char * indexed by enum fi_code
+ [INST_NAME()] = "INST_NAME()",
+FID_INST()m4_dnl Anonymous structure inside struct f_inst
+ struct {
+m4_undivert(101)m4_dnl
+ } i_[[]]INST_NAME();
+FID_LINE()m4_dnl Anonymous structure inside struct f_line_item
+ struct {
+m4_undivert(107)m4_dnl
+ } i_[[]]INST_NAME();
+FID_NEW()m4_dnl Constructor and interpreter code together
+FID_HIC(
+[[m4_dnl Public declaration of constructor in H file
+struct f_inst *f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code
+m4_undivert(102)m4_dnl
+);]],
+[[m4_dnl The one case in The Big Switch inside interpreter
+ case INST_NAME():
+ #define whati (&(what->i_]]INST_NAME()[[))
+ m4_ifelse(m4_eval(INST_INVAL() > 0), 1, [[if (fstk->vcnt < INST_INVAL()) runtime("Stack underflow"); fstk->vcnt -= INST_INVAL(); ]])
+ m4_undivert(108)m4_dnl
+ #undef whati
+ break;
+]],
+[[m4_dnl Constructor itself
+struct f_inst *f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code
+m4_undivert(102)m4_dnl
+)
+ {
+ /* Allocate the structure */
+ struct f_inst *what = fi_new(fi_code);
+ FID_IFCONST([[uint constargs = 1;]])
+
+ /* Initialize all the members */
+ #define whati (&(what->i_]]INST_NAME()[[))
+ m4_undivert(103)m4_dnl
+
+ /* If not constant, return the instruction itself */
+ FID_IFCONST([[if (!constargs)]])
+ return what;
+
+ /* Try to pre-calculate the result */
+ FID_IFCONST([[m4_undivert(108)]])m4_dnl
+ #undef whati
+ }
+]])
+
+FID_DUMP_CALLER()m4_dnl Case in another big switch used in instruction dumping (debug)
+case INST_NAME(): f_dump_line_item_]]INST_NAME()[[(item, indent + 1); break;
+
+FID_DUMP()m4_dnl The dumper itself
+m4_ifdef([[FID_DUMP_BODY_EXISTS]],
+[[static inline void f_dump_line_item_]]INST_NAME()[[(const struct f_line_item *item_, const int indent)]],
+[[static inline void f_dump_line_item_]]INST_NAME()[[(const struct f_line_item *item UNUSED, const int indent UNUSED)]])
+m4_undefine([[FID_DUMP_BODY_EXISTS]])
+{
+#define item (&(item_->i_]]INST_NAME()[[))
+m4_undivert(104)m4_dnl
+#undef item
+}
+
+FID_LINEARIZE()m4_dnl The linearizer
+case INST_NAME(): {
+#define whati (&(what->i_]]INST_NAME()[[))
+#define item (&(dest->items[pos].i_]]INST_NAME()[[))
+ m4_undivert(105)m4_dnl
+#undef whati
+#undef item
+ dest->items[pos].fi_code = what->fi_code;
+ dest->items[pos].lineno = what->lineno;
+ break;
+}
+
+FID_SAME()m4_dnl This code compares two f_line"s while reconfiguring
+case INST_NAME():
+#define f1 (&(f1_->i_]]INST_NAME()[[))
+#define f2 (&(f2_->i_]]INST_NAME()[[))
+m4_undivert(106)m4_dnl
+#undef f1
+#undef f2
+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.
+FID_INTERPRET_BODY()m4_dnl By default, every code is interpreter code.
+')
+
+# 3) Final preparation
+#
+# Now we prepare all the code around the global diversions.
+# It must be here, not in m4wrap, as we want M4 to mark the code
+# by #line directives correctly, not to claim that every single line
+# is at the beginning of the m4wrap directive.
+#
+# This part is split by the final file.
+# H for inst-gen.h
+# I for inst-interpret.c
+# C for inst-gen.c
+#
+# So we in cycle:
+# A. open a diversion
+# B. send there some code
+# C. close that diversion
+# D. flush a global diversion
+# E. open another diversion and goto B.
+#
+# Final diversions
+# 200+ completed text before it is flushed to output
+
+# This is a list of output diversions
+m4_define(FID_WR_PUT_LIST)
+
+# This macro does the steps C to E, see before.
+m4_define(FID_WR_PUT_ALSO, `m4_define([[FID_WR_PUT_LIST]],FID_WR_PUT_LIST()[[FID_WR_DPUT(]]FID_WR_DIDX[[)FID_WR_DPUT(]]$1[[)]])m4_define([[FID_WR_DIDX]],m4_eval(FID_WR_DIDX+1))m4_divert(FID_WR_DIDX)')
+
+# These macros do the splitting between H/I/C
+m4_define(FID_WR_DIRECT, `m4_ifelse(TARGET,[[$1]],[[FID_WR_INIT()]],[[FID_WR_STOP()]])')
+m4_define(FID_WR_INIT, `m4_define([[FID_WR_DIDX]],200)m4_define([[FID_WR_PUT]],[[FID_WR_PUT_ALSO($]][[@)]])m4_divert(200)')
+m4_define(FID_WR_STOP, `m4_define([[FID_WR_PUT]])m4_divert(-1)')
+
+# Here is the direct code to be put into the output files
+# together with the undiversions, being hidden under FID_WR_PUT()
+
+m4_changequote([[,]])
+FID_WR_DIRECT(I)
+FID_WR_PUT(3)
+FID_WR_DIRECT(C)
+
+#if defined(__GNUC__) && __GNUC__ >= 6
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmisleading-indentation"
+#endif
+
+#include "nest/bird.h"
+#include "filter/filter.h"
+#include "filter/f-inst.h"
+
+/* Instruction codes to string */
+static const char * const f_instruction_name_str[] = {
+FID_WR_PUT(5)
+};
+
+const char *
+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];
+ else
+ bug("Got unknown instruction code: %d", fi);
+}
+
+static inline struct f_inst *
+fi_new(enum f_instruction_code fi_code)
+{
+ struct f_inst *what = cfg_allocz(sizeof(struct f_inst));
+ what->lineno = ifs->lino;
+ what->size = 1;
+ what->fi_code = fi_code;
+ return what;
+}
+
+static inline struct f_inst *
+fi_constant(struct f_inst *what, struct f_val val)
+{
+ what->fi_code = FI_CONSTANT;
+ what->i_FI_CONSTANT.val = val;
+ return what;
+}
+
+#define v1 whati->f1->i_FI_CONSTANT.val
+#define v2 whati->f2->i_FI_CONSTANT.val
+#define v3 whati->f3->i_FI_CONSTANT.val
+#define vv(i) items[i]->i_FI_CONSTANT.val
+#define runtime(fmt, ...) cf_error("filter preevaluation, line %d: " fmt, ifs->lino, ##__VA_ARGS__)
+#define fpool cfg_mem
+#define falloc(size) cfg_alloc(size)
+/* Instruction constructors */
+FID_WR_PUT(3)
+#undef v1
+#undef v2
+#undef v3
+#undef vv
+
+/* Line dumpers */
+#define INDENT (((const char *) f_dump_line_indent_str) + sizeof(f_dump_line_indent_str) - (indent) - 1)
+static const char f_dump_line_indent_str[] = " ";
+
+FID_WR_PUT(6)
+
+void f_dump_line(const struct f_line *dest, uint indent)
+{
+ if (!dest) {
+ debug("%sNo filter line (NULL)\n", INDENT);
+ return;
+ }
+ 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);
+ switch (item->fi_code) {
+FID_WR_PUT(7)
+ default: bug("Unknown instruction %x in f_dump_line", item->fi_code);
+ }
+ }
+ debug("%sFilter line %p dump done\n", INDENT, dest);
+}
+
+/* Linearize */
+static uint
+linearize(struct f_line *dest, const struct f_inst *what, uint pos)
+{
+ for ( ; what; what = what->next) {
+ switch (what->fi_code) {
+FID_WR_PUT(8)
+ }
+ pos++;
+ }
+ return pos;
+}
+
+struct f_line *
+f_linearize_concat(const struct f_inst * const inst[], uint count)
+{
+ uint len = 0;
+ for (uint i=0; i<count; i++)
+ for (const struct f_inst *what = inst[i]; what; what = what->next)
+ len += what->size;
+
+ struct f_line *out = cfg_allocz(sizeof(struct f_line) + sizeof(struct f_line_item)*len);
+
+ for (uint i=0; i<count; i++)
+ out->len = linearize(out, inst[i], out->len);
+
+#if DEBUGGING
+ f_dump_line(out, 0);
+#endif
+ return out;
+}
+
+/* Filter line comparison */
+int
+f_same(const struct f_line *fl1, const struct f_line *fl2)
+{
+ if ((!fl1) && (!fl2))
+ return 1;
+ if ((!fl1) || (!fl2))
+ return 0;
+ if (fl1->len != fl2->len)
+ return 0;
+ for (uint i=0; i<fl1->len; i++) {
+#define f1_ (&(fl1->items[i]))
+#define f2_ (&(fl2->items[i]))
+ if (f1_->fi_code != f2_->fi_code)
+ return 0;
+ if (f1_->flags != f2_->flags)
+ return 0;
+
+ switch(f1_->fi_code) {
+FID_WR_PUT(9)
+ }
+ }
+#undef f1_
+#undef f2_
+ return 1;
+}
+
+#if defined(__GNUC__) && __GNUC__ >= 6
+#pragma GCC diagnostic pop
+#endif
+
+FID_WR_DIRECT(H)
+/* Filter instruction codes */
+enum f_instruction_code {
+FID_WR_PUT(4)m4_dnl
+} PACKED;
+
+/* Filter instruction structure for config */
+struct f_inst {
+ struct f_inst *next; /* Next instruction */
+ enum f_instruction_code fi_code; /* Instruction code */
+ int size; /* How many instructions are underneath */
+ int lineno; /* Line number */
+ union {
+FID_WR_PUT(1)m4_dnl
+ };
+};
+
+/* Filter line item */
+struct f_line_item {
+ enum f_instruction_code fi_code; /* What to do */
+ enum f_instruction_flags flags; /* Flags, instruction-specific */
+ uint lineno; /* Where */
+ union {
+FID_WR_PUT(2)m4_dnl
+ };
+};
+
+/* Instruction constructors */
+FID_WR_PUT(3)
+m4_divert(-1)
+
+# 4) Shipout
+#
+# Everything is prepared in FID_WR_PUT_LIST now. Let's go!
+
+m4_changequote(`,')
+
+# Flusher auxiliary macro
+m4_define(FID_FLUSH, `m4_ifelse($1,$2,,[[m4_undivert($1)FID_FLUSH(m4_eval($1+1),$2)]])')
+
+# Defining the macro used in FID_WR_PUT_LIST
+m4_define(FID_WR_DPUT, `m4_undivert($1)')
+
+# After the code is read and parsed, we:
+m4_m4wrap(`INST_FLUSH()m4_divert(0)FID_WR_PUT_LIST()m4_divert(-1)FID_FLUSH(1,200)')
+
+m4_changequote([[,]])
+# And now M4 is going to parse f-inst.c, fill the diversions
+# and after the file is done, the content of m4_m4wrap (see before)
+# is executed.
diff --git a/filter/f-inst.c b/filter/f-inst.c
new file mode 100644
index 00000000..0867ac4a
--- /dev/null
+++ b/filter/f-inst.c
@@ -0,0 +1,1173 @@
+/*
+ * Filters: Instructions themselves
+ *
+ * Copyright 1998 Pavel Machek <pavel@ucw.cz>
+ * Copyright 2018 Maria Matejka <mq@jmq.cz>
+ * Copyright 2018 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ *
+ * The filter code goes through several phases:
+ *
+ * 1 Parsing
+ * Flex- and Bison-generated parser decodes the human-readable data into
+ * a struct f_inst tree. This is an infix tree that was interpreted by
+ * depth-first search execution in previous versions of the interpreter.
+ * All instructions have their constructor: f_new_inst(FI_EXAMPLE, ...)
+ * translates into f_new_inst_FI_EXAMPLE(...) and the types are checked in
+ * compile time. If the result of the instruction is always the same,
+ * it's reduced to FI_CONSTANT directly in constructor. This phase also
+ * counts how many instructions are underlying in means of f_line_item
+ * fields to know how much we have to allocate in the next phase.
+ *
+ * 2 Linearize before interpreting
+ * The infix tree is always interpreted in the same order. Therefore we
+ * sort the instructions one after another into struct f_line. Results
+ * and arguments of these instructions are implicitly put on a value
+ * stack; e.g. the + operation just takes two arguments from the value
+ * stack and puts the result on there.
+ *
+ * 3 Interpret
+ * The given line is put on a custom execution stack. If needed (FI_CALL,
+ * FI_SWITCH, FI_AND, FI_OR, FI_CONDITION, ...), another line is put on top
+ * of the stack; when that line finishes, the execution continues on the
+ * older lines on the stack where it stopped before.
+ *
+ * 4 Same
+ * On config reload, the filters have to be compared whether channel
+ * reload is needed or not. The comparison is done by comparing the
+ * struct f_line's recursively.
+ *
+ * The main purpose of this rework was to improve filter performance
+ * by making the interpreter non-recursive.
+ *
+ * The other outcome is concentration of instruction definitions to
+ * one place -- right here. You shall define your instruction only here
+ * and nowhere else.
+ *
+ * Beware. This file is interpreted by M4 macros. These macros
+ * may be more stupid than you could imagine. If something strange
+ * happens after changing this file, compare the results before and
+ * after your change (see the Makefile to find out where the results are)
+ * and see what really happened.
+ *
+ * This file is not directly a C source code -> it is a generator input
+ * for several C sources; every instruction block gets expanded into many
+ * different places.
+ *
+ * All the arguments are processed literally; if you need an argument including comma,
+ * you have to quote it by [[ ... ]]
+ *
+ * What is the syntax here?
+ * m4_dnl INST(FI_NOP, in, out) { enum value, input args, output args
+ * m4_dnl ARG(num, type); argument, its id (in data fields) and type accessible by v1, v2, v3
+ * m4_dnl ARG_ANY(num); argument with no type check accessible by v1, v2, v3
+ * m4_dnl VARARG; variable-length argument list; accessible by vv(i) and whati->varcount
+ * m4_dnl LINE(num, unused); this argument has to be converted to its own f_line
+ * m4_dnl SYMBOL; symbol handed from config
+ * m4_dnl STATIC_ATTR; static attribute definition
+ * m4_dnl DYNAMIC_ATTR; dynamic attribute definition
+ * m4_dnl RTC; route table config
+ * m4_dnl ACCESS_RTE; this instruction needs route
+ * m4_dnl ACCESS_EATTRS; this instruction needs extended attributes
+ *
+ * m4_dnl FID_MEMBER( custom instruction member
+ * m4_dnl C type, for storage in structs
+ * m4_dnl name, how the member is named
+ * m4_dnl comparator for same(), if different, this should be TRUE (CAVEAT)
+ * m4_dnl dump format string debug -> format string for bvsnprintf
+ * m4_dnl dump format args appropriate args
+ * m4_dnl )
+ *
+ * m4_dnl RESULT(type, union-field, value); putting this on value stack
+ * m4_dnl RESULT_VAL(value-struct); pass the struct f_val directly
+ * m4_dnl RESULT_VOID; return undef
+ * m4_dnl }
+ *
+ * Also note that the { ... } blocks are not respected by M4 at all.
+ * If you get weird unmatched-brace-pair errors, check what it generated and why.
+ * What is really considered as one instruction is not the { ... } block
+ * after m4_dnl INST() but all the code between them.
+ *
+ * Other code is just copied into the interpreter part.
+ *
+ * If you are satisfied with this, you don't need to read the following
+ * detailed description of what is really done with the instruction definitions.
+ *
+ * m4_dnl Now let's look under the cover. The code between each INST()
+ * m4_dnl is copied to several places, namely these (numbered by the M4 diversions
+ * m4_dnl used in filter/decl.m4):
+ *
+ * m4_dnl (102) struct f_inst *f_new_inst(FI_EXAMPLE [[ put it here ]])
+ * m4_dnl {
+ * m4_dnl ... (common code)
+ * m4_dnl (103) [[ put it here ]]
+ * m4_dnl ...
+ * m4_dnl if (all arguments are constant)
+ * m4_dnl (108) [[ put it here ]]
+ * m4_dnl }
+ * m4_dnl For writing directly to constructor argument list, use FID_NEW_ARGS.
+ * m4_dnl For computing something in constructor (103), use FID_NEW_BODY.
+ * m4_dnl For constant pre-interpretation (108), see below at FID_INTERPRET_BODY.
+ *
+ * m4_dnl struct f_inst {
+ * m4_dnl ... (common fields)
+ * m4_dnl union {
+ * m4_dnl struct {
+ * m4_dnl (101) [[ put it here ]]
+ * m4_dnl } i_FI_EXAMPLE;
+ * m4_dnl ...
+ * m4_dnl };
+ * m4_dnl };
+ * m4_dnl This structure is returned from constructor.
+ * m4_dnl For writing directly to this structure, use FID_STRUCT_IN.
+ *
+ * m4_dnl linearize(struct f_line *dest, const struct f_inst *what, uint pos) {
+ * m4_dnl ...
+ * m4_dnl switch (what->fi_code) {
+ * m4_dnl case FI_EXAMPLE:
+ * m4_dnl (105) [[ put it here ]]
+ * m4_dnl break;
+ * m4_dnl }
+ * m4_dnl }
+ * m4_dnl This is called when translating from struct f_inst to struct f_line_item.
+ * m4_dnl For accessing your custom instruction data, use following macros:
+ * m4_dnl whati -> for accessing (struct f_inst).i_FI_EXAMPLE
+ * m4_dnl item -> for accessing (struct f_line)[pos].i_FI_EXAMPLE
+ * m4_dnl For writing directly here, use FID_LINEARIZE_BODY.
+ *
+ * m4_dnl (107) struct f_line_item {
+ * m4_dnl ... (common fields)
+ * m4_dnl union {
+ * m4_dnl struct {
+ * m4_dnl (101) [[ put it here ]]
+ * m4_dnl } i_FI_EXAMPLE;
+ * m4_dnl ...
+ * m4_dnl };
+ * m4_dnl };
+ * m4_dnl The same as FID_STRUCT_IN (101) but for the other structure.
+ * m4_dnl This structure is returned from the linearizer (105).
+ * m4_dnl For writing directly to this structure, use FID_LINE_IN.
+ *
+ * m4_dnl f_dump_line_item_FI_EXAMPLE(const struct f_line_item *item, const int indent)
+ * m4_dnl {
+ * m4_dnl (104) [[ put it here ]]
+ * m4_dnl }
+ * m4_dnl This code dumps the instruction on debug. Note that the argument
+ * m4_dnl is the linearized instruction; if the instruction has arguments,
+ * m4_dnl their code has already been linearized and their value is taken
+ * m4_dnl from the value stack.
+ * m4_dnl For writing directly here, use FID_DUMP_BODY.
+ *
+ * m4_dnl f_same(...)
+ * m4_dnl {
+ * m4_dnl switch (f1_->fi_code) {
+ * m4_dnl case FI_EXAMPLE:
+ * m4_dnl (106) [[ put it here ]]
+ * m4_dnl break;
+ * m4_dnl }
+ * m4_dnl }
+ * m4_dnl This code compares the two given instrucions (f1_ and f2_)
+ * m4_dnl on reconfigure. For accessing your custom instruction data,
+ * m4_dnl use macros f1 and f2.
+ * m4_dnl For writing directly here, use FID_SAME_BODY.
+ *
+ * m4_dnl interpret(...)
+ * m4_dnl {
+ * m4_dnl switch (what->fi_code) {
+ * m4_dnl case FI_EXAMPLE:
+ * m4_dnl (108) [[ put it here ]]
+ * m4_dnl break;
+ * m4_dnl }
+ * m4_dnl }
+ * m4_dnl This code executes the instruction. Every pre-defined macro
+ * m4_dnl resets the output here. For setting it explicitly,
+ * m4_dnl use FID_INTERPRET_BODY.
+ * m4_dnl This code is put on two places; one is the interpreter, the other
+ * m4_dnl is instruction constructor. If you need to distinguish between
+ * m4_dnl these two, use FID_INTERPRET_EXEC or FID_INTERPRET_NEW respectively.
+ * m4_dnl To address the difference between interpreter and constructor
+ * m4_dnl environments, there are several convenience macros defined:
+ * m4_dnl runtime() -> for spitting out runtime error like division by zero
+ * m4_dnl RESULT(...) -> declare result; may overwrite arguments
+ * m4_dnl v1, v2, v3 -> positional arguments, may be overwritten by RESULT()
+ * m4_dnl falloc(size) -> allocate memory from the appropriate linpool
+ * m4_dnl fpool -> the current linpool
+ * m4_dnl NEVER_CONSTANT-> don't generate pre-interpretation code at all
+ * m4_dnl ACCESS_RTE -> check that route is available, also NEVER_CONSTANT
+ * m4_dnl ACCESS_EATTRS -> pre-cache the eattrs; use only with ACCESS_RTE
+ * m4_dnl f_rta_cow(fs) -> function to call before any change to route should be done
+ *
+ * m4_dnl If you are stymied, see FI_CALL or FI_CONSTANT or just search for
+ * m4_dnl the mentioned macros in this file to see what is happening there in wild.
+ */
+
+/* Binary operators */
+ INST(FI_ADD, 2, 1) {
+ ARG(1,T_INT);
+ ARG(2,T_INT);
+ RESULT(T_INT, i, v1.val.i + v2.val.i);
+ }
+ INST(FI_SUBTRACT, 2, 1) {
+ ARG(1,T_INT);
+ ARG(2,T_INT);
+ RESULT(T_INT, i, v1.val.i - v2.val.i);
+ }
+ INST(FI_MULTIPLY, 2, 1) {
+ ARG(1,T_INT);
+ ARG(2,T_INT);
+ RESULT(T_INT, i, v1.val.i * v2.val.i);
+ }
+ INST(FI_DIVIDE, 2, 1) {
+ ARG(1,T_INT);
+ ARG(2,T_INT);
+ if (v2.val.i == 0) runtime( "Mother told me not to divide by 0" );
+ RESULT(T_INT, i, v1.val.i / v2.val.i);
+ }
+ INST(FI_AND, 1, 1) {
+ ARG(1,T_BOOL);
+ if (v1.val.i)
+ LINE(2,0);
+ else
+ RESULT_VAL(v1);
+ }
+ INST(FI_OR, 1, 1) {
+ ARG(1,T_BOOL);
+ if (!v1.val.i)
+ LINE(2,0);
+ else
+ RESULT_VAL(v1);
+ }
+
+ INST(FI_PAIR_CONSTRUCT, 2, 1) {
+ ARG(1,T_INT);
+ ARG(2,T_INT);
+ uint u1 = v1.val.i;
+ uint u2 = v2.val.i;
+ if ((u1 > 0xFFFF) || (u2 > 0xFFFF))
+ runtime( "Can't operate with value out of bounds in pair constructor" );
+ RESULT(T_PAIR, i, (u1 << 16) | u2);
+ }
+
+ INST(FI_EC_CONSTRUCT, 2, 1) {
+ ARG_ANY(1);
+ ARG(2, T_INT);
+
+ FID_MEMBER(enum ec_subtype, ecs, f1->ecs != f2->ecs, "ec subtype %s", ec_subtype_str(item->ecs));
+
+ int check, ipv4_used;
+ u32 key, val;
+
+ if (v1.type == T_INT) {
+ ipv4_used = 0; key = v1.val.i;
+ }
+ else if (v1.type == T_QUAD) {
+ ipv4_used = 1; key = v1.val.i;
+ }
+ /* IP->Quad implicit conversion */
+ else if (val_is_ip4(&v1)) {
+ ipv4_used = 1; key = ipa_to_u32(v1.val.ip);
+ }
+ else
+ runtime("Argument 1 of EC constructor must be integer or IPv4 address, got 0x%02x", v1.type);
+
+ 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);
+ }
+
+ INST(FI_LC_CONSTRUCT, 3, 1) {
+ ARG(1, T_INT);
+ ARG(2, T_INT);
+ ARG(3, T_INT);
+ RESULT(T_LC, lc, [[(lcomm) { v1.val.i, v2.val.i, v3.val.i }]]);
+ }
+
+ INST(FI_PATHMASK_CONSTRUCT, 0, 1) {
+ VARARG;
+
+ struct f_path_mask *pm = falloc(sizeof(struct f_path_mask) + whati->varcount * sizeof(struct f_path_mask_item));
+ pm->len = whati->varcount;
+
+ for (uint i=0; i<whati->varcount; i++) {
+ switch (vv(i).type) {
+ case T_PATH_MASK_ITEM:
+ 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;
+ default:
+ runtime( "Error resolving path mask template: value not an integer" );
+ }
+ }
+
+ RESULT(T_PATH_MASK, path_mask, pm);
+ }
+
+/* Relational operators */
+
+ INST(FI_NEQ, 2, 1) {
+ ARG_ANY(1);
+ ARG_ANY(2);
+ RESULT(T_BOOL, i, !val_same(&v1, &v2));
+ }
+
+ INST(FI_EQ, 2, 1) {
+ ARG_ANY(1);
+ ARG_ANY(2);
+ RESULT(T_BOOL, i, val_same(&v1, &v2));
+ }
+
+ INST(FI_LT, 2, 1) {
+ ARG_ANY(1);
+ ARG_ANY(2);
+ int i = val_compare(&v1, &v2);
+ if (i == F_CMP_ERROR)
+ runtime( "Can't compare values of incompatible types" );
+ RESULT(T_BOOL, i, (i == -1));
+ }
+
+ INST(FI_LTE, 2, 1) {
+ ARG_ANY(1);
+ ARG_ANY(2);
+ int i = val_compare(&v1, &v2);
+ if (i == F_CMP_ERROR)
+ runtime( "Can't compare values of incompatible types" );
+ RESULT(T_BOOL, i, (i != 1));
+ }
+
+ INST(FI_NOT, 1, 1) {
+ ARG(1,T_BOOL);
+ RESULT(T_BOOL, i, !v1.val.i);
+ }
+
+ INST(FI_MATCH, 2, 1) {
+ ARG_ANY(1);
+ ARG_ANY(2);
+ int i = val_in_range(&v1, &v2);
+ if (i == F_CMP_ERROR)
+ runtime( "~ applied on unknown type pair" );
+ RESULT(T_BOOL, i, !!i);
+ }
+
+ INST(FI_NOT_MATCH, 2, 1) {
+ ARG_ANY(1);
+ ARG_ANY(2);
+ int i = val_in_range(&v1, &v2);
+ if (i == F_CMP_ERROR)
+ runtime( "!~ applied on unknown type pair" );
+ RESULT(T_BOOL, i, !i);
+ }
+
+ INST(FI_DEFINED, 1, 1) {
+ ARG_ANY(1);
+ RESULT(T_BOOL, i, (v1.type != T_VOID) && !undef_value(v1));
+ }
+
+ INST(FI_TYPE, 1, 1) {
+ ARG_ANY(1); /* There may be more types supporting this operation */
+ switch (v1.type)
+ {
+ case T_NET:
+ RESULT(T_ENUM_NETTYPE, i, v1.val.net->type);
+ break;
+ default:
+ runtime( "Can't determine type of this item" );
+ }
+ }
+
+ INST(FI_IS_V4, 1, 1) {
+ ARG(1, T_IP);
+ RESULT(T_BOOL, i, ipa_is_ip4(v1.val.ip));
+ }
+
+ /* Set to indirect value prepared in v1 */
+ INST(FI_VAR_SET, 1, 0) {
+ 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" );
+ }
+
+ fstk->vstk[curline.vbase + sym->offset] = v1;
+ }
+
+ INST(FI_VAR_GET, 0, 1) {
+ SYMBOL;
+ NEVER_CONSTANT;
+ RESULT_VAL(fstk->vstk[curline.vbase + sym->offset]);
+ }
+
+ INST(FI_CONSTANT, 0, 1) {
+ FID_MEMBER(
+ struct f_val,
+ val,
+ [[ !val_same(&(f1->val), &(f2->val)) ]],
+ "value %s",
+ val_dump(&(item->val))
+ );
+
+ RESULT_VAL(val);
+ }
+
+ INST(FI_CONDITION, 1, 0) {
+ ARG(1, T_BOOL);
+ if (v1.val.i)
+ LINE(2,0);
+ else
+ LINE(3,1);
+ }
+
+ INST(FI_PRINT, 0, 0) {
+ NEVER_CONSTANT;
+ VARARG;
+
+ if (whati->varcount && !(fs->flags & FF_SILENT))
+ for (uint i=0; i<whati->varcount; i++)
+ val_format(&(vv(i)), &fs->buf);
+ }
+
+ INST(FI_FLUSH, 0, 0) {
+ NEVER_CONSTANT;
+ if (!(fs->flags & FF_SILENT))
+ /* After log_commit, the buffer is reset */
+ log_commit(*L_INFO, &fs->buf);
+ }
+
+ INST(FI_DIE, 0, 0) {
+ NEVER_CONSTANT;
+ 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? */
+ return fret; /* We have to return now, no more processing. */
+ default:
+ bug( "unknown return type: Can't happen");
+ }
+ }
+
+ INST(FI_RTA_GET, 0, 1) {
+ {
+ STATIC_ATTR;
+ ACCESS_RTE;
+ struct rta *rta = (*fs->rte)->attrs;
+
+ switch (sa.sa_code)
+ {
+ case SA_FROM: RESULT(sa.f_type, ip, rta->from); break;
+ case SA_GW: RESULT(sa.f_type, ip, rta->nh.gw); break;
+ case SA_NET: RESULT(sa.f_type, net, (*fs->rte)->net->n.addr); break;
+ case SA_PROTO: RESULT(sa.f_type, s, rta->src->proto->name); break;
+ case SA_SOURCE: RESULT(sa.f_type, i, rta->source); break;
+ case SA_SCOPE: RESULT(sa.f_type, i, rta->scope); break;
+ case SA_DEST: RESULT(sa.f_type, i, rta->dest); break;
+ case SA_IFNAME: RESULT(sa.f_type, s, rta->nh.iface ? rta->nh.iface->name : ""); break;
+ case SA_IFINDEX: RESULT(sa.f_type, i, rta->nh.iface ? rta->nh.iface->index : 0); break;
+
+ default:
+ bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code);
+ }
+ }
+ }
+
+ INST(FI_RTA_SET, 1, 0) {
+ ACCESS_RTE;
+ ARG_ANY(1);
+ STATIC_ATTR;
+ if (sa.f_type != v1.type)
+ runtime( "Attempt to set static attribute to incompatible type" );
+
+ f_rta_cow(fs);
+ {
+ struct rta *rta = (*fs->rte)->attrs;
+
+ switch (sa.sa_code)
+ {
+ case SA_FROM:
+ rta->from = v1.val.ip;
+ break;
+
+ case SA_GW:
+ {
+ ip_addr ip = v1.val.ip;
+ neighbor *n = neigh_find(rta->src->proto, ip, NULL, 0);
+ if (!n || (n->scope == SCOPE_HOST))
+ runtime( "Invalid gw address" );
+
+ rta->dest = RTD_UNICAST;
+ rta->nh.gw = ip;
+ rta->nh.iface = n->iface;
+ rta->nh.next = NULL;
+ rta->hostentry = NULL;
+ }
+ break;
+
+ case SA_SCOPE:
+ rta->scope = v1.val.i;
+ break;
+
+ case SA_DEST:
+ {
+ int i = v1.val.i;
+ if ((i != RTD_BLACKHOLE) && (i != RTD_UNREACHABLE) && (i != RTD_PROHIBIT))
+ runtime( "Destination can be changed only to blackhole, unreachable or prohibit" );
+
+ rta->dest = i;
+ rta->nh.gw = IPA_NONE;
+ rta->nh.iface = NULL;
+ rta->nh.next = NULL;
+ rta->hostentry = NULL;
+ }
+ break;
+
+ case SA_IFNAME:
+ {
+ struct iface *ifa = if_find_by_name(v1.val.s);
+ if (!ifa)
+ runtime( "Invalid iface name" );
+
+ rta->dest = RTD_UNICAST;
+ rta->nh.gw = IPA_NONE;
+ rta->nh.iface = ifa;
+ rta->nh.next = NULL;
+ rta->hostentry = NULL;
+ }
+ break;
+
+ default:
+ bug("Invalid static attribute access (%u/%u)", sa.f_type, sa.sa_code);
+ }
+ }
+ }
+
+ INST(FI_EA_GET, 0, 1) { /* Access to extended attributes */
+ DYNAMIC_ATTR;
+ ACCESS_RTE;
+ ACCESS_EATTRS;
+ {
+ 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);
+ break;
+ }
+
+ /* The same special case for int_set */
+ if (da.type == EAF_TYPE_INT_SET) {
+ RESULT(T_CLIST, ad, &null_adata);
+ break;
+ }
+
+ /* The same special case for ec_set */
+ if (da.type == EAF_TYPE_EC_SET) {
+ RESULT(T_ECLIST, ad, &null_adata);
+ break;
+ }
+
+ /* The same special case for lc_set */
+ if (da.type == EAF_TYPE_LC_SET) {
+ RESULT(T_LCLIST, ad, &null_adata);
+ break;
+ }
+
+ /* Undefined value */
+ RESULT_VOID;
+ break;
+ }
+
+ switch (e->type & EAF_TYPE_MASK) {
+ case EAF_TYPE_INT:
+ RESULT(da.f_type, i, e->u.data);
+ break;
+ case EAF_TYPE_ROUTER_ID:
+ RESULT(T_QUAD, i, e->u.data);
+ break;
+ case EAF_TYPE_OPAQUE:
+ RESULT(T_ENUM_EMPTY, i, 0);
+ break;
+ case EAF_TYPE_IP_ADDRESS:
+ RESULT(T_IP, ip, *((ip_addr *) e->u.ptr->data));
+ break;
+ case EAF_TYPE_AS_PATH:
+ RESULT(T_PATH, ad, e->u.ptr);
+ break;
+ case EAF_TYPE_BITFIELD:
+ RESULT(T_BOOL, i, !!(e->u.data & (1u << da.bit)));
+ break;
+ case EAF_TYPE_INT_SET:
+ RESULT(T_CLIST, ad, e->u.ptr);
+ break;
+ case EAF_TYPE_EC_SET:
+ RESULT(T_ECLIST, ad, e->u.ptr);
+ break;
+ case EAF_TYPE_LC_SET:
+ RESULT(T_LCLIST, ad, e->u.ptr);
+ break;
+ case EAF_TYPE_UNDEF:
+ RESULT_VOID;
+ break;
+ default:
+ bug("Unknown dynamic attribute type");
+ }
+ }
+ }
+
+ INST(FI_EA_SET, 1, 0) {
+ ACCESS_RTE;
+ ACCESS_EATTRS;
+ ARG_ANY(1);
+ DYNAMIC_ATTR;
+ {
+ struct ea_list *l = lp_alloc(fs->pool, sizeof(struct ea_list) + sizeof(eattr));
+
+ l->next = NULL;
+ l->flags = EALF_SORTED;
+ l->count = 1;
+ l->attrs[0].id = da.ea_code;
+ l->attrs[0].flags = 0;
+ l->attrs[0].type = da.type | EAF_ORIGINATED | EAF_FRESH;
+
+ 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;
+
+ case EAF_TYPE_OPAQUE:
+ 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" );
+ int len = sizeof(ip_addr);
+ struct adata *ad = lp_alloc(fs->pool, sizeof(struct adata) + len);
+ ad->length = len;
+ (* (ip_addr *) ad->data) = v1.val.ip;
+ l->attrs[0].u.ptr = ad;
+ break;
+
+ case EAF_TYPE_AS_PATH:
+ if (v1.type != T_PATH)
+ runtime( "Setting path attribute to non-path value" );
+ 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);
+ u32 data = e ? e->u.data : 0;
+
+ if (v1.val.i)
+ l->attrs[0].u.data = data | (1u << da.bit);
+ else
+ l->attrs[0].u.data = data & ~(1u << da.bit);
+ }
+ 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");
+ }
+
+ f_rta_cow(fs);
+ l->next = *fs->eattrs;
+ *fs->eattrs = l;
+ }
+ }
+
+ INST(FI_EA_UNSET, 0, 0) {
+ DYNAMIC_ATTR;
+ ACCESS_RTE;
+ ACCESS_EATTRS;
+
+ {
+ struct ea_list *l = lp_alloc(fs->pool, sizeof(struct ea_list) + sizeof(eattr));
+
+ l->next = NULL;
+ l->flags = EALF_SORTED;
+ l->count = 1;
+ l->attrs[0].id = da.ea_code;
+ l->attrs[0].flags = 0;
+ l->attrs[0].type = EAF_TYPE_UNDEF | EAF_ORIGINATED | EAF_FRESH;
+ l->attrs[0].u.data = 0;
+
+ f_rta_cow(fs);
+ l->next = *fs->eattrs;
+ *fs->eattrs = l;
+ }
+ }
+
+ INST(FI_PREF_GET, 0, 1) {
+ ACCESS_RTE;
+ RESULT(T_INT, i, (*fs->rte)->pref);
+ }
+
+ INST(FI_PREF_SET, 1, 0) {
+ ACCESS_RTE;
+ ARG(1,T_INT);
+ if (v1.val.i > 0xFFFF)
+ runtime( "Setting preference value out of bounds" );
+ f_rte_cow(fs);
+ (*fs->rte)->pref = v1.val.i;
+ }
+
+ INST(FI_LENGTH, 1, 1) { /* Get length of */
+ ARG_ANY(1);
+ switch(v1.type) {
+ case T_NET: RESULT(T_INT, i, net_pxlen(v1.val.net)); break;
+ case T_PATH: RESULT(T_INT, i, as_path_getlen(v1.val.ad)); break;
+ case T_CLIST: RESULT(T_INT, i, int_set_get_size(v1.val.ad)); break;
+ case T_ECLIST: RESULT(T_INT, i, ec_set_get_size(v1.val.ad)); break;
+ case T_LCLIST: RESULT(T_INT, i, lc_set_get_size(v1.val.ad)); break;
+ default: runtime( "Prefix, path, clist or eclist expected" );
+ }
+ }
+
+ INST(FI_SADR_SRC, 1, 1) { /* Get SADR 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 *src = falloc(sizeof(net_addr_ip6));
+ net_fill_ip6(src, net->src_prefix, net->src_pxlen);
+
+ RESULT(T_NET, net, src);
+ }
+
+ INST(FI_ROA_MAXLEN, 1, 1) { /* Get ROA max prefix length */
+ ARG(1, T_NET);
+ if (!net_is_roa(v1.val.net))
+ runtime( "ROA expected" );
+
+ RESULT(T_INT, i, (v1.val.net->type == NET_ROA4) ?
+ ((net_addr_roa4 *) v1.val.net)->max_pxlen :
+ ((net_addr_roa6 *) v1.val.net)->max_pxlen);
+ }
+
+ INST(FI_ROA_ASN, 1, 1) { /* Get ROA ASN */
+ ARG(1, T_NET);
+ if (!net_is_roa(v1.val.net))
+ runtime( "ROA expected" );
+
+ RESULT(T_INT, i, (v1.val.net->type == NET_ROA4) ?
+ ((net_addr_roa4 *) v1.val.net)->asn :
+ ((net_addr_roa6 *) v1.val.net)->asn);
+ }
+
+ INST(FI_IP, 1, 1) { /* Convert prefix to ... */
+ ARG(1, T_NET);
+ RESULT(T_IP, ip, net_prefix(v1.val.net));
+ }
+
+ INST(FI_ROUTE_DISTINGUISHER, 1, 1) {
+ ARG(1, T_NET);
+ if (!net_is_vpn(v1.val.net))
+ runtime( "VPN address expected" );
+ RESULT(T_RD, ec, net_rd(v1.val.net));
+ }
+
+ INST(FI_AS_PATH_FIRST, 1, 1) { /* Get first ASN from AS PATH */
+ ARG(1, T_PATH);
+ int 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;
+ as_path_get_last(v1.val.ad, &as);
+ RESULT(T_INT, i, as);
+ }
+
+ INST(FI_AS_PATH_LAST_NAG, 1, 1) { /* Get last ASN from non-aggregated part of AS PATH */
+ ARG(1, T_PATH);
+ RESULT(T_INT, i, as_path_get_last_nonaggregated(v1.val.ad));
+ }
+
+ INST(FI_RETURN, 1, 1) {
+ NEVER_CONSTANT;
+ /* Acquire the return value */
+ ARG_ANY(1);
+ uint retpos = fstk->vcnt;
+
+ /* Drop every sub-block including ourselves */
+ 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;
+ 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;
+
+ /* Copy the return value */
+ RESULT_VAL(fstk->vstk[retpos]);
+ }
+
+ INST(FI_CALL, 0, 1) {
+ NEVER_CONSTANT;
+ SYMBOL;
+
+ /* Push the body on stack */
+ LINEX(sym->function);
+ curline.emask |= FE_RETURN;
+
+ /* Before this instruction was called, there was the T_VOID
+ * automatic return value pushed on value stack and also
+ * sym->function->args function arguments. Setting the
+ * vbase to point to first argument. */
+ ASSERT(curline.ventry >= sym->function->args);
+ curline.ventry -= sym->function->args;
+ curline.vbase = curline.ventry;
+
+ /* Storage for local variables */
+ memset(&(fstk->vstk[fstk->vcnt]), 0, sizeof(struct f_val) * sym->function->vars);
+ fstk->vcnt += sym->function->vars;
+ }
+
+ INST(FI_DROP_RESULT, 1, 0) {
+ NEVER_CONSTANT;
+ ARG_ANY(1);
+ }
+
+ INST(FI_SWITCH, 1, 0) {
+ ARG_ANY(1);
+
+ FID_MEMBER(struct f_tree *, tree, [[!same_tree(f1->tree, f2->tree)]], "tree %p", item->tree);
+
+ const struct f_tree *t = find_tree(tree, &v1);
+ if (!t) {
+ v1.type = T_VOID;
+ t = find_tree(tree, &v1);
+ if (!t) {
+ debug( "No else statement?\n");
+ FID_HIC(,break,return NULL);
+ }
+ }
+ /* It is actually possible to have t->data NULL */
+
+ LINEX(t->data);
+ }
+
+ INST(FI_IP_MASK, 2, 1) { /* IP.MASK(val) */
+ ARG(1, T_IP);
+ ARG(2, T_INT);
+ RESULT(T_IP, ip, [[ ipa_is_ip4(v1.val.ip) ?
+ ipa_from_ip4(ip4_and(ipa_to_ip4(v1.val.ip), ip4_mkmask(v2.val.i))) :
+ ipa_from_ip6(ip6_and(ipa_to_ip6(v1.val.ip), ip6_mkmask(v2.val.i))) ]]);
+ }
+
+ INST(FI_PATH_PREPEND, 2, 1) { /* Path prepend */
+ ARG(1, T_PATH);
+ ARG(2, T_INT);
+ RESULT(T_PATH, ad, [[ as_path_prepend(fpool, v1.val.ad, v2.val.i) ]]);
+ }
+
+ INST(FI_CLIST_ADD, 2, 1) { /* (Extended) Community list add */
+ ARG_ANY(1);
+ ARG_ANY(2);
+ if (v1.type == T_PATH)
+ runtime("Can't add to path");
+
+ else if (v1.type == T_CLIST)
+ {
+ /* Community (or cluster) list */
+ struct f_val dummy;
+
+ if ((v2.type == T_PAIR) || (v2.type == T_QUAD))
+ RESULT(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, v2.val.i) ]]);
+ /* IP->Quad implicit conversion */
+ else if (val_is_ip4(&v2))
+ RESULT(T_CLIST, ad, [[ int_set_add(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]);
+ else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy))
+ runtime("Can't add set");
+ else if (v2.type == T_CLIST)
+ RESULT(T_CLIST, ad, [[ int_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
+ else
+ runtime("Can't add non-pair");
+ }
+
+ else if (v1.type == T_ECLIST)
+ {
+ /* v2.val is either EC or EC-set */
+ if ((v2.type == T_SET) && eclist_set_type(v2.val.t))
+ runtime("Can't add set");
+ else if (v2.type == T_ECLIST)
+ RESULT(T_ECLIST, ad, [[ ec_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
+ else if (v2.type != T_EC)
+ runtime("Can't add non-ec");
+ else
+ RESULT(T_ECLIST, ad, [[ ec_set_add(fpool, v1.val.ad, v2.val.ec) ]]);
+ }
+
+ else if (v1.type == T_LCLIST)
+ {
+ /* v2.val is either LC or LC-set */
+ if ((v2.type == T_SET) && lclist_set_type(v2.val.t))
+ runtime("Can't add set");
+ else if (v2.type == T_LCLIST)
+ RESULT(T_LCLIST, ad, [[ lc_set_union(fpool, v1.val.ad, v2.val.ad) ]]);
+ else if (v2.type != T_LC)
+ runtime("Can't add non-lc");
+ else
+ RESULT(T_LCLIST, ad, [[ lc_set_add(fpool, v1.val.ad, v2.val.lc) ]]);
+
+ }
+
+ else
+ runtime("Can't add to non-[e|l]clist");
+ }
+
+ INST(FI_CLIST_DEL, 2, 1) { /* (Extended) Community list add or delete */
+ ARG_ANY(1);
+ ARG_ANY(2);
+ if (v1.type == T_PATH)
+ {
+ const struct f_tree *set = NULL;
+ u32 key = 0;
+
+ if (v2.type == T_INT)
+ key = v2.val.i;
+ else if ((v2.type == T_SET) && (v2.val.t->from.type == T_INT))
+ set = v2.val.t;
+ else
+ runtime("Can't delete non-integer (set)");
+
+ RESULT(T_PATH, ad, [[ as_path_filter(fpool, v1.val.ad, set, key, 0) ]]);
+ }
+
+ else if (v1.type == T_CLIST)
+ {
+ /* Community (or cluster) list */
+ struct f_val dummy;
+
+ if ((v2.type == T_PAIR) || (v2.type == T_QUAD))
+ RESULT(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, v2.val.i) ]]);
+ /* IP->Quad implicit conversion */
+ else if (val_is_ip4(&v2))
+ RESULT(T_CLIST, ad, [[ int_set_del(fpool, v1.val.ad, ipa_to_u32(v2.val.ip)) ]]);
+ else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy) || (v2.type == T_CLIST))
+ RESULT(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 0) ]]);
+ else
+ runtime("Can't delete non-pair");
+ }
+
+ else if (v1.type == T_ECLIST)
+ {
+ /* v2.val is either EC or EC-set */
+ if ((v2.type == T_SET) && eclist_set_type(v2.val.t) || (v2.type == T_ECLIST))
+ RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
+ else if (v2.type != T_EC)
+ runtime("Can't delete non-ec");
+ else
+ RESULT(T_ECLIST, ad, [[ ec_set_del(fpool, v1.val.ad, v2.val.ec) ]]);
+ }
+
+ else if (v1.type == T_LCLIST)
+ {
+ /* v2.val is either LC or LC-set */
+ if ((v2.type == T_SET) && lclist_set_type(v2.val.t) || (v2.type == T_LCLIST))
+ RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 0) ]]);
+ else if (v2.type != T_LC)
+ runtime("Can't delete non-lc");
+ else
+ RESULT(T_LCLIST, ad, [[ lc_set_del(fpool, v1.val.ad, v2.val.lc) ]]);
+ }
+
+ else
+ runtime("Can't delete in non-[e|l]clist");
+ }
+
+ INST(FI_CLIST_FILTER, 2, 1) { /* (Extended) Community list add or delete */
+ ARG_ANY(1);
+ ARG_ANY(2);
+ 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) ]]);
+ else
+ runtime("Can't filter integer");
+ }
+
+ else if (v1.type == T_CLIST)
+ {
+ /* Community (or cluster) list */
+ struct f_val dummy;
+
+ if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy) || (v2.type == T_CLIST))
+ RESULT(T_CLIST, ad, [[ clist_filter(fpool, v1.val.ad, &v2, 1) ]]);
+ else
+ runtime("Can't filter pair");
+ }
+
+ else if (v1.type == T_ECLIST)
+ {
+ /* v2.val is either EC or EC-set */
+ if ((v2.type == T_SET) && eclist_set_type(v2.val.t) || (v2.type == T_ECLIST))
+ RESULT(T_ECLIST, ad, [[ eclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
+ else
+ runtime("Can't filter ec");
+ }
+
+ else if (v1.type == T_LCLIST)
+ {
+ /* v2.val is either LC or LC-set */
+ if ((v2.type == T_SET) && lclist_set_type(v2.val.t) || (v2.type == T_LCLIST))
+ RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 1) ]]);
+ else
+ runtime("Can't filter lc");
+ }
+
+ else
+ runtime("Can't filter non-[e|l]clist");
+ }
+
+ INST(FI_ROA_CHECK_IMPLICIT, 0, 1) { /* ROA Check */
+ NEVER_CONSTANT;
+ RTC(1);
+ struct rtable *table = rtc->table;
+ ACCESS_RTE;
+ ACCESS_EATTRS;
+ const net_addr *net = (*fs->rte)->net->n.addr;
+
+ /* We ignore temporary attributes, probably not a problem here */
+ /* 0x02 is a value of BA_AS_PATH, we don't want to include BGP headers */
+ eattr *e = ea_find(*fs->eattrs, EA_CODE(PROTOCOL_BGP, 0x02));
+
+ if (!e || ((e->type & EAF_TYPE_MASK) != EAF_TYPE_AS_PATH))
+ runtime("Missing AS_PATH attribute");
+
+ u32 as = 0;
+ as_path_get_last(e->u.ptr, &as);
+
+ if (!table)
+ runtime("Missing ROA table");
+
+ if (table->addr_type != NET_ROA4 && table->addr_type != NET_ROA6)
+ runtime("Table type must be either ROA4 or ROA6");
+
+ if (table->addr_type != (net->type == NET_IP4 ? NET_ROA4 : NET_ROA6))
+ RESULT(T_ENUM_ROA, i, ROA_UNKNOWN); /* Prefix and table type mismatch */
+ else
+ RESULT(T_ENUM_ROA, i, [[ net_roa_check(table, net, as) ]]);
+ }
+
+ INST(FI_ROA_CHECK_EXPLICIT, 2, 1) { /* ROA Check */
+ NEVER_CONSTANT;
+ ARG(1, T_NET);
+ ARG(2, T_INT);
+ RTC(3);
+ struct rtable *table = rtc->table;
+
+ u32 as = v2.val.i;
+
+ if (!table)
+ runtime("Missing ROA table");
+
+ if (table->addr_type != NET_ROA4 && table->addr_type != NET_ROA6)
+ runtime("Table type must be either ROA4 or ROA6");
+
+ if (table->addr_type != (v1.val.net->type == NET_IP4 ? NET_ROA4 : NET_ROA6))
+ RESULT(T_ENUM_ROA, i, ROA_UNKNOWN); /* Prefix and table type mismatch */
+ else
+ RESULT(T_ENUM_ROA, i, [[ net_roa_check(table, v1.val.net, as) ]]);
+
+ }
+
+ INST(FI_FORMAT, 1, 0) { /* Format */
+ ARG_ANY(1);
+ RESULT(T_STRING, s, val_format_str(fpool, &v1));
+ }
+
+ INST(FI_ASSERT, 1, 0) { /* Birdtest Assert */
+ NEVER_CONSTANT;
+ ARG(1, T_BOOL);
+
+ FID_MEMBER(char *, s, [[strcmp(f1->s, f2->s)]], "string %s", item->s);
+
+ ASSERT(s);
+
+ if (!bt_assert_hook)
+ runtime("No bt_assert hook registered, can't assert");
+
+ bt_assert_hook(v1.val.i, what);
+ }
diff --git a/filter/f-inst.h b/filter/f-inst.h
new file mode 100644
index 00000000..33fcf4a9
--- /dev/null
+++ b/filter/f-inst.h
@@ -0,0 +1,73 @@
+/*
+ * BIRD Internet Routing Daemon -- Filter instructions
+ *
+ * (c) 1999 Pavel Machek <pavel@ucw.cz>
+ * (c) 2018--2019 Maria Matejka <mq@jmq.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ *
+ * Filter interpreter data structures and internal API.
+ * See filter/f-inst.c for documentation.
+ */
+
+#ifndef _BIRD_F_INST_H_
+#define _BIRD_F_INST_H_
+
+#include "nest/bird.h"
+#include "conf/conf.h"
+#include "filter/filter.h"
+#include "filter/data.h"
+
+/* Flags for instructions */
+enum f_instruction_flags {
+ FIF_PRINTED = 1, /* FI_PRINT_AND_DIE: message put in buffer */
+} PACKED;
+
+/* Include generated filter instruction declarations */
+#include "filter/inst-gen.h"
+
+#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);
+
+/* Filter structures for execution */
+/* Line of instructions to be unconditionally executed one after another */
+struct f_line {
+ uint len; /* Line length */
+ u8 args; /* Function: Args required */
+ u8 vars;
+ struct f_line_item items[0]; /* The items themselves */
+};
+
+/* Convert the f_inst infix tree to the f_line structures */
+struct f_line *f_linearize_concat(const struct f_inst * const inst[], uint count);
+static inline struct f_line *f_linearize(const struct f_inst *root)
+{ return f_linearize_concat(&root, 1); }
+
+void f_dump_line(const struct f_line *, uint indent);
+
+struct filter *f_new_where(struct f_inst *);
+static inline struct f_dynamic_attr f_new_dynamic_attr(u8 type, enum f_type f_type, uint code) /* Type as core knows it, type as filters know it, and code of dynamic attribute */
+{ return (struct f_dynamic_attr) { .type = type, .f_type = f_type, .ea_code = code }; } /* f_type currently unused; will be handy for static type checking */
+static inline struct f_dynamic_attr f_new_dynamic_attr_bit(u8 bit, enum f_type f_type, uint code) /* Type as core knows it, type as filters know it, and code of dynamic attribute */
+{ return (struct f_dynamic_attr) { .type = EAF_TYPE_BITFIELD, .bit = bit, .f_type = f_type, .ea_code = code }; } /* f_type currently unused; will be handy for static type checking */
+static inline struct f_static_attr f_new_static_attr(int f_type, int code, int readonly)
+{ return (struct f_static_attr) { .f_type = f_type, .sa_code = code, .readonly = readonly }; }
+struct f_inst *f_generate_complex(enum f_instruction_code fi_code, struct f_dynamic_attr da, struct f_inst *argument);
+struct f_inst *f_generate_roa_check(struct rtable_config *table, struct f_inst *prefix, struct f_inst *asn);
+
+/* Hook for call bt_assert() function in configuration */
+extern void (*bt_assert_hook)(int result, const struct f_line_item *assert);
+
+/* Bird Tests */
+struct f_bt_test_suite {
+ node n; /* Node in config->tests */
+ const struct f_line *fn; /* Root of function */
+ const struct f_line *cmp; /* Compare to this function */
+ const char *fn_name; /* Name of test */
+ const char *dsc; /* Description */
+ int result; /* Desired result */
+};
+
+#endif
diff --git a/filter/f-util.c b/filter/f-util.c
index ee9490b4..e61949f2 100644
--- a/filter/f-util.c
+++ b/filter/f-util.c
@@ -10,103 +10,56 @@
#include "nest/bird.h"
#include "conf/conf.h"
#include "filter/filter.h"
+#include "filter/f-inst.h"
#include "lib/idm.h"
#include "nest/protocol.h"
#include "nest/route.h"
#define P(a,b) ((a<<8) | b)
-struct f_inst *
-f_new_inst(enum f_instruction_code fi_code)
-{
- struct f_inst * ret;
- ret = cfg_allocz(sizeof(struct f_inst));
- ret->fi_code = fi_code;
- ret->lineno = ifs->lino;
- return ret;
-}
-
-struct f_inst *
-f_new_inst_da(enum f_instruction_code fi_code, struct f_dynamic_attr da)
-{
- struct f_inst *ret = f_new_inst(fi_code);
- ret->aux = (da.f_type << 8) | da.type;
- ret->a2.i = da.ea_code;
- return ret;
-}
-
-struct f_inst *
-f_new_inst_sa(enum f_instruction_code fi_code, struct f_static_attr sa)
-{
- struct f_inst *ret = f_new_inst(fi_code);
- ret->aux = sa.f_type;
- ret->a2.i = sa.sa_code;
- ret->a1.i = sa.readonly;
- return ret;
-}
-
-/*
- * Generate set_dynamic( operation( get_dynamic(), argument ) )
- */
-struct f_inst *
-f_generate_complex(int operation, int operation_aux, struct f_dynamic_attr da, struct f_inst *argument)
-{
- struct f_inst *set_dyn = f_new_inst_da(FI_EA_SET, da),
- *oper = f_new_inst(operation),
- *get_dyn = f_new_inst_da(FI_EA_GET, da);
-
- oper->aux = operation_aux;
- oper->a1.p = get_dyn;
- oper->a2.p = argument;
-
- set_dyn->a1.p = oper;
- return set_dyn;
-}
-
-struct f_inst *
-f_generate_roa_check(struct rtable_config *table, struct f_inst *prefix, struct f_inst *asn)
-{
- struct f_inst_roa_check *ret = cfg_allocz(sizeof(struct f_inst_roa_check));
- ret->i.fi_code = FI_ROA_CHECK;
- ret->i.lineno = ifs->lino;
- ret->i.arg1 = prefix;
- ret->i.arg2 = asn;
- /* prefix == NULL <-> asn == NULL */
-
- if (table->addr_type != NET_ROA4 && table->addr_type != NET_ROA6)
- cf_error("%s is not a ROA table", table->name);
- ret->rtc = table;
-
- return &ret->i;
-}
-
-static const char * const f_instruction_name_str[] = {
-#define F(c,a,b) \
- [c] = #c,
-FI__LIST
-#undef F
-};
-
const char *
-f_instruction_name(enum f_instruction_code fi)
-{
- if (fi < FI__MAX)
- return f_instruction_name_str[fi];
- else
- bug("Got unknown instruction code: %d", fi);
-}
-
-char *
-filter_name(struct filter *filter)
+filter_name(const struct filter *filter)
{
if (!filter)
return "ACCEPT";
else if (filter == FILTER_REJECT)
return "REJECT";
- else if (!filter->name)
+ else if (!filter->sym)
return "(unnamed)";
else
- return filter->name;
+ return filter->sym->name;
+}
+
+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 filter *f = cfg_allocz(sizeof(struct filter));
+ f->root = f_linearize(&i);
+ return f;
}
#define CA_KEY(n) n->name, n->fda.type
diff --git a/filter/filter.c b/filter/filter.c
index 37cf16a3..35bd75e6 100644
--- a/filter/filter.c
+++ b/filter/filter.c
@@ -15,20 +15,13 @@
* the source from user into a tree of &f_inst structures. These trees are
* later interpreted using code in |filter/filter.c|.
*
- * A filter is represented by a tree of &f_inst structures, one structure per
- * "instruction". Each &f_inst contains @code, @aux value which is
- * usually the data type this instruction operates on and two generic
- * arguments (@a1, @a2). Some instructions contain pointer(s) to other
- * instructions in their (@a1, @a2) fields.
+ * A filter is represented by a tree of &f_inst structures, later translated
+ * into lists called &f_line. All the instructions are defined and documented
+ * in |filter/f-inst.c| definition file.
*
* Filters use a &f_val structure for their data. Each &f_val
- * contains type and value (types are constants prefixed with %T_). Few
- * of the types are special; %T_RETURN can be or-ed with a type to indicate
- * that return from a function or from the whole filter should be
- * forced. Important thing about &f_val's is that they may be copied
- * with a simple |=|. That's fine for all currently defined types: strings
- * are read-only (and therefore okay), paths are copied for each
- * operation (okay too).
+ * contains type and value (types are constants prefixed with %T_).
+ * Look into |filter/data.h| for more information and appropriate calls.
*/
#undef LOCAL_DEBUG
@@ -47,583 +40,109 @@
#include "nest/attrs.h"
#include "conf/conf.h"
#include "filter/filter.h"
+#include "filter/f-inst.h"
+#include "filter/data.h"
-#define CMP_ERROR 999
-void (*bt_assert_hook)(int result, struct f_inst *assert);
+/* Exception bits */
+enum f_exception {
+ FE_RETURN = 0x1,
+};
-static struct adata undef_adata; /* adata of length 0 used for undefined */
-/* Special undef value for paths and clists */
-static inline int
-undef_value(struct f_val v)
-{
- return ((v.type == T_PATH) || (v.type == T_CLIST) ||
- (v.type == T_ECLIST) || (v.type == T_LCLIST)) &&
- (v.val.ad == &undef_adata);
-}
-
-static struct adata *
-adata_empty(struct linpool *pool, int l)
-{
- struct adata *res = lp_alloc(pool, sizeof(struct adata) + l);
- res->length = l;
- return res;
-}
-
-static void
-pm_format(struct f_path_mask *p, buffer *buf)
-{
- buffer_puts(buf, "[= ");
-
- while (p)
- {
- switch(p->kind)
- {
- case PM_ASN:
- buffer_print(buf, "%u ", p->val);
- break;
-
- case PM_QUESTION:
- buffer_puts(buf, "? ");
- break;
-
- case PM_ASTERISK:
- buffer_puts(buf, "* ");
- break;
-
- case PM_ASN_RANGE:
- buffer_print(buf, "%u..%u ", p->val, p->val2);
- break;
-
- case PM_ASN_EXPR:
- ASSERT(0);
- }
-
- p = p->next;
- }
-
- buffer_puts(buf, "=]");
-}
-
-static inline int val_is_ip4(const struct f_val v)
-{ return (v.type == T_IP) && ipa_is_ip4(v.val.ip); }
-
-static inline int
-lcomm_cmp(lcomm v1, lcomm v2)
-{
- if (v1.asn != v2.asn)
- return (v1.asn > v2.asn) ? 1 : -1;
- if (v1.ldp1 != v2.ldp1)
- return (v1.ldp1 > v2.ldp1) ? 1 : -1;
- if (v1.ldp2 != v2.ldp2)
- return (v1.ldp2 > v2.ldp2) ? 1 : -1;
- return 0;
-}
-
-/**
- * val_compare - compare two values
- * @v1: first value
- * @v2: second value
- *
- * Compares two values and returns -1, 0, 1 on <, =, > or CMP_ERROR on
- * error. Tree module relies on this giving consistent results so
- * that it can be used for building balanced trees.
- */
-int
-val_compare(struct f_val v1, struct f_val v2)
-{
- if (v1.type != v2.type) {
- if (v1.type == T_VOID) /* Hack for else */
- return -1;
- if (v2.type == T_VOID)
- return 1;
-
- /* IP->Quad implicit conversion */
- if ((v1.type == T_QUAD) && val_is_ip4(v2))
- return uint_cmp(v1.val.i, ipa_to_u32(v2.val.ip));
- if (val_is_ip4(v1) && (v2.type == T_QUAD))
- return uint_cmp(ipa_to_u32(v1.val.ip), v2.val.i);
-
- debug( "Types do not match in val_compare\n" );
- return CMP_ERROR;
- }
-
- switch (v1.type) {
- case T_VOID:
- return 0;
- case T_ENUM:
- case T_INT:
- case T_BOOL:
- case T_PAIR:
- case T_QUAD:
- return uint_cmp(v1.val.i, v2.val.i);
- case T_EC:
- case T_RD:
- return u64_cmp(v1.val.ec, v2.val.ec);
- case T_LC:
- return lcomm_cmp(v1.val.lc, v2.val.lc);
- case T_IP:
- return ipa_compare(v1.val.ip, v2.val.ip);
- case T_NET:
- return net_compare(v1.val.net, v2.val.net);
- case T_STRING:
- return strcmp(v1.val.s, v2.val.s);
- default:
- return CMP_ERROR;
- }
-}
-
-static int
-pm_same(struct f_path_mask *m1, struct f_path_mask *m2)
-{
- while (m1 && m2)
- {
- if (m1->kind != m2->kind)
- return 0;
-
- if (m1->kind == PM_ASN_EXPR)
- {
- if (!i_same((struct f_inst *) m1->val, (struct f_inst *) m2->val))
- return 0;
- }
- else
- {
- if ((m1->val != m2->val) || (m1->val2 != m2->val2))
- return 0;
- }
-
- m1 = m1->next;
- m2 = m2->next;
- }
-
- return !m1 && !m2;
-}
-
-/**
- * val_same - compare two values
- * @v1: first value
- * @v2: second value
- *
- * Compares two values and returns 1 if they are same and 0 if not.
- * Comparison of values of different types is valid and returns 0.
- */
-int
-val_same(struct f_val v1, struct f_val v2)
-{
- int rc;
-
- rc = val_compare(v1, v2);
- if (rc != CMP_ERROR)
- return !rc;
-
- if (v1.type != v2.type)
- return 0;
-
- switch (v1.type) {
- case T_PATH_MASK:
- return pm_same(v1.val.path_mask, v2.val.path_mask);
- case T_PATH:
- case T_CLIST:
- case T_ECLIST:
- case T_LCLIST:
- return adata_same(v1.val.ad, v2.val.ad);
- case T_SET:
- return same_tree(v1.val.t, v2.val.t);
- case T_PREFIX_SET:
- return trie_same(v1.val.ti, v2.val.ti);
- default:
- bug("Invalid type in val_same(): %x", v1.type);
- }
-}
-
-static int
-clist_set_type(struct f_tree *set, struct f_val *v)
-{
- switch (set->from.type)
- {
- case T_PAIR:
- v->type = T_PAIR;
- return 1;
-
- case T_QUAD:
- v->type = T_QUAD;
- return 1;
-
- case T_IP:
- if (val_is_ip4(set->from) && val_is_ip4(set->to))
- {
- v->type = T_QUAD;
- return 1;
- }
- /* Fall through */
- default:
- v->type = T_VOID;
- return 0;
- }
-}
-
-static inline int
-eclist_set_type(struct f_tree *set)
-{ return set->from.type == T_EC; }
-
-static inline int
-lclist_set_type(struct f_tree *set)
-{ return set->from.type == T_LC; }
-
-static int
-clist_match_set(struct adata *clist, struct f_tree *set)
-{
- if (!clist)
- return 0;
-
- struct f_val v;
- if (!clist_set_type(set, &v))
- return CMP_ERROR;
-
- u32 *l = (u32 *) clist->data;
- u32 *end = l + clist->length/4;
-
- while (l < end) {
- v.val.i = *l++;
- if (find_tree(set, v))
- return 1;
- }
- return 0;
-}
-
-static int
-eclist_match_set(struct adata *list, struct f_tree *set)
-{
- if (!list)
- return 0;
-
- if (!eclist_set_type(set))
- return CMP_ERROR;
-
- struct f_val v;
- u32 *l = int_set_get_data(list);
- int len = int_set_get_size(list);
- int i;
-
- v.type = T_EC;
- for (i = 0; i < len; i += 2) {
- v.val.ec = ec_get(l, i);
- if (find_tree(set, v))
- return 1;
- }
-
- return 0;
-}
-
-static int
-lclist_match_set(struct adata *list, struct f_tree *set)
-{
- if (!list)
- return 0;
-
- if (!lclist_set_type(set))
- return CMP_ERROR;
-
- struct f_val v;
- u32 *l = int_set_get_data(list);
- int len = int_set_get_size(list);
- int i;
-
- v.type = T_LC;
- for (i = 0; i < len; i += 3) {
- v.val.lc = lc_get(l, i);
- if (find_tree(set, v))
- return 1;
- }
-
- return 0;
-}
-
-static struct adata *
-clist_filter(struct linpool *pool, struct adata *list, struct f_val set, int pos)
-{
- if (!list)
- return NULL;
-
- int tree = (set.type == T_SET); /* 1 -> set is T_SET, 0 -> set is T_CLIST */
- struct f_val v;
- if (tree)
- clist_set_type(set.val.t, &v);
- else
- v.type = T_PAIR;
-
- int len = int_set_get_size(list);
- u32 *l = int_set_get_data(list);
- u32 tmp[len];
- u32 *k = tmp;
- u32 *end = l + len;
-
- while (l < end) {
- v.val.i = *l++;
- /* pos && member(val, set) || !pos && !member(val, set), member() depends on tree */
- if ((tree ? !!find_tree(set.val.t, v) : int_set_contains(set.val.ad, v.val.i)) == pos)
- *k++ = v.val.i;
- }
-
- uint nl = (k - tmp) * sizeof(u32);
- if (nl == list->length)
- return list;
-
- struct adata *res = adata_empty(pool, nl);
- memcpy(res->data, tmp, nl);
- return res;
-}
-
-static struct adata *
-eclist_filter(struct linpool *pool, struct adata *list, struct f_val set, int pos)
-{
- if (!list)
- return NULL;
-
- int tree = (set.type == T_SET); /* 1 -> set is T_SET, 0 -> set is T_CLIST */
- struct f_val v;
-
- int len = int_set_get_size(list);
- u32 *l = int_set_get_data(list);
- u32 tmp[len];
- u32 *k = tmp;
- int i;
-
- v.type = T_EC;
- for (i = 0; i < len; i += 2) {
- v.val.ec = ec_get(l, i);
- /* pos && member(val, set) || !pos && !member(val, set), member() depends on tree */
- if ((tree ? !!find_tree(set.val.t, v) : ec_set_contains(set.val.ad, v.val.ec)) == pos) {
- *k++ = l[i];
- *k++ = l[i+1];
- }
- }
-
- uint nl = (k - tmp) * sizeof(u32);
- if (nl == list->length)
- return list;
-
- struct adata *res = adata_empty(pool, nl);
- memcpy(res->data, tmp, nl);
- return res;
-}
-
-static struct adata *
-lclist_filter(struct linpool *pool, struct adata *list, struct f_val set, int pos)
-{
- if (!list)
- return NULL;
-
- int tree = (set.type == T_SET); /* 1 -> set is T_SET, 0 -> set is T_CLIST */
- struct f_val v;
-
- int len = int_set_get_size(list);
- u32 *l = int_set_get_data(list);
- u32 tmp[len];
- u32 *k = tmp;
- int i;
-
- v.type = T_LC;
- for (i = 0; i < len; i += 3) {
- v.val.lc = lc_get(l, i);
- /* pos && member(val, set) || !pos && !member(val, set), member() depends on tree */
- if ((tree ? !!find_tree(set.val.t, v) : lc_set_contains(set.val.ad, v.val.lc)) == pos)
- k = lc_copy(k, l+i);
- }
-
- uint nl = (k - tmp) * sizeof(u32);
- if (nl == list->length)
- return list;
-
- struct adata *res = adata_empty(pool, nl);
- memcpy(res->data, tmp, nl);
- return res;
-}
-
-/**
- * val_in_range - implement |~| operator
- * @v1: element
- * @v2: set
- *
- * Checks if @v1 is element (|~| operator) of @v2.
- */
-static int
-val_in_range(struct f_val v1, struct f_val v2)
-{
- if ((v1.type == T_PATH) && (v2.type == T_PATH_MASK))
- return as_path_match(v1.val.ad, v2.val.path_mask);
+struct filter_stack {
+ /* Value stack for execution */
+#define F_VAL_STACK_MAX 4096
+ uint vcnt; /* Current value stack size; 0 for empty */
+ uint ecnt; /* Current execute stack size; 0 for empty */
- if ((v1.type == T_INT) && (v2.type == T_PATH))
- return as_path_contains(v2.val.ad, v1.val.i, 1);
+ struct f_val vstk[F_VAL_STACK_MAX]; /* The stack itself */
- if (((v1.type == T_PAIR) || (v1.type == T_QUAD)) && (v2.type == T_CLIST))
- return int_set_contains(v2.val.ad, v1.val.i);
- /* IP->Quad implicit conversion */
- if (val_is_ip4(v1) && (v2.type == T_CLIST))
- return int_set_contains(v2.val.ad, ipa_to_u32(v1.val.ip));
+ /* Instruction stack for execution */
+#define F_EXEC_STACK_MAX 4096
+ struct {
+ const struct f_line *line; /* The line that is being executed */
+ uint pos; /* Instruction index in the line */
+ uint ventry; /* Value stack depth on entry */
+ uint vbase; /* Where to index variable positions from */
+ enum f_exception emask; /* Exception mask */
+ } estk[F_EXEC_STACK_MAX];
+};
- if ((v1.type == T_EC) && (v2.type == T_ECLIST))
- return ec_set_contains(v2.val.ad, v1.val.ec);
+/* Internal filter state, to be allocated on stack when executing filters */
+struct filter_state {
+ /* Stacks needed for execution */
+ struct filter_stack *stack;
- if ((v1.type == T_LC) && (v2.type == T_LCLIST))
- return lc_set_contains(v2.val.ad, v1.val.lc);
+ /* The route we are processing. This may be NULL to indicate no route available. */
+ struct rte **rte;
- if ((v1.type == T_STRING) && (v2.type == T_STRING))
- return patmatch(v2.val.s, v1.val.s);
+ /* The old rta to be freed after filters are done. */
+ struct rta *old_rta;
- if ((v1.type == T_IP) && (v2.type == T_NET))
- return ipa_in_netX(v1.val.ip, v2.val.net);
+ /* Cached pointer to ea_list */
+ struct ea_list **eattrs;
- if ((v1.type == T_NET) && (v2.type == T_NET))
- return net_in_netX(v1.val.net, v2.val.net);
+ /* Linpool for adata allocation */
+ struct linpool *pool;
- if ((v1.type == T_NET) && (v2.type == T_PREFIX_SET))
- return trie_match_net(v2.val.ti, v1.val.net);
+ /* Buffer for log output */
+ struct buffer buf;
- if (v2.type != T_SET)
- return CMP_ERROR;
+ /* Filter execution flags */
+ int flags;
+};
- /* With integrated Quad<->IP implicit conversion */
- if ((v1.type == v2.val.t->from.type) ||
- ((v1.type == T_QUAD) && val_is_ip4(v2.val.t->from) && val_is_ip4(v2.val.t->to)))
- return !!find_tree(v2.val.t, v1);
+_Thread_local static struct filter_state filter_state;
+_Thread_local static struct filter_stack filter_stack;
- if (v1.type == T_CLIST)
- return clist_match_set(v1.val.ad, v2.val.t);
+void (*bt_assert_hook)(int result, const struct f_line_item *assert);
- if (v1.type == T_ECLIST)
- return eclist_match_set(v1.val.ad, v2.val.t);
-
- if (v1.type == T_LCLIST)
- return lclist_match_set(v1.val.ad, v2.val.t);
-
- if (v1.type == T_PATH)
- return as_path_match_set(v1.val.ad, v2.val.t);
-
- return CMP_ERROR;
-}
-
-/*
- * val_format - format filter value
- */
-void
-val_format(struct f_val v, buffer *buf)
-{
- char buf2[1024];
- switch (v.type)
- {
- case T_VOID: buffer_puts(buf, "(void)"); return;
- case T_BOOL: buffer_puts(buf, v.val.i ? "TRUE" : "FALSE"); return;
- case T_INT: buffer_print(buf, "%u", v.val.i); return;
- case T_STRING: buffer_print(buf, "%s", v.val.s); return;
- case T_IP: buffer_print(buf, "%I", v.val.ip); return;
- case T_NET: buffer_print(buf, "%N", v.val.net); return;
- case T_PAIR: buffer_print(buf, "(%u,%u)", v.val.i >> 16, v.val.i & 0xffff); return;
- case T_QUAD: buffer_print(buf, "%R", v.val.i); return;
- case T_EC: ec_format(buf2, v.val.ec); buffer_print(buf, "%s", buf2); return;
- case T_LC: lc_format(buf2, v.val.lc); buffer_print(buf, "%s", buf2); return;
- case T_RD: rd_format(v.val.ec, buf2, 1024); buffer_print(buf, "%s", buf2); return;
- case T_PREFIX_SET: trie_format(v.val.ti, buf); return;
- case T_SET: tree_format(v.val.t, buf); return;
- case T_ENUM: buffer_print(buf, "(enum %x)%u", v.type, v.val.i); return;
- case T_PATH: as_path_format(v.val.ad, buf2, 1000); buffer_print(buf, "(path %s)", buf2); return;
- case T_CLIST: int_set_format(v.val.ad, 1, -1, buf2, 1000); buffer_print(buf, "(clist %s)", buf2); return;
- case T_ECLIST: ec_set_format(v.val.ad, -1, buf2, 1000); buffer_print(buf, "(eclist %s)", buf2); return;
- case T_LCLIST: lc_set_format(v.val.ad, -1, buf2, 1000); buffer_print(buf, "(lclist %s)", buf2); return;
- case T_PATH_MASK: pm_format(v.val.path_mask, buf); return;
- default: buffer_print(buf, "[unknown type %x]", v.type); return;
- }
-}
-
-static struct rte **f_rte;
-static struct rta *f_old_rta;
-static struct ea_list **f_eattrs;
-static struct linpool *f_pool;
-static struct buffer f_buf;
-static int f_flags;
-
-static inline void f_cache_eattrs(void)
+static inline void f_cache_eattrs(struct filter_state *fs)
{
- f_eattrs = &((*f_rte)->attrs->eattrs);
+ fs->eattrs = &((*fs->rte)->attrs->eattrs);
}
-static inline void f_rte_cow(void)
+static inline void f_rte_cow(struct filter_state *fs)
{
- if (!((*f_rte)->flags & REF_COW))
+ if (!((*fs->rte)->flags & REF_COW))
return;
- *f_rte = rte_do_cow(*f_rte);
+ *fs->rte = rte_cow(*fs->rte);
}
/*
* rta_cow - prepare rta for modification by filter
*/
static void
-f_rta_cow(void)
+f_rta_cow(struct filter_state *fs)
{
- if (!rta_is_cached((*f_rte)->attrs))
+ if (!rta_is_cached((*fs->rte)->attrs))
return;
/* Prepare to modify rte */
- f_rte_cow();
+ f_rte_cow(fs);
/* Store old rta to free it later, it stores reference from rte_cow() */
- f_old_rta = (*f_rte)->attrs;
+ fs->old_rta = (*fs->rte)->attrs;
/*
* Get shallow copy of rta. Fields eattrs and nexthops of rta are shared
- * with f_old_rta (they will be copied when the cached rta will be obtained
+ * with fs->old_rta (they will be copied when the cached rta will be obtained
* at the end of f_run()), also the lock of hostentry is inherited (we
* suppose hostentry is not changed by filters).
*/
- (*f_rte)->attrs = rta_do_cow((*f_rte)->attrs, f_pool);
+ (*fs->rte)->attrs = rta_do_cow((*fs->rte)->attrs, fs->pool);
/* Re-cache the ea_list */
- f_cache_eattrs();
-}
-
-static char *
-val_format_str(struct f_val v) {
- buffer b;
- LOG_BUFFER_INIT(b);
- val_format(v, &b);
- return lp_strdup(f_pool, b.start);
+ f_cache_eattrs(fs);
}
static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS;
-#define runtime(fmt, ...) do { \
- if (!(f_flags & FF_SILENT)) \
- log_rl(&rl_runtime_err, L_ERR "filters, line %d: " fmt, what->lineno, ##__VA_ARGS__); \
- res.type = T_RETURN; \
- res.val.i = F_ERROR; \
- return res; \
- } while(0)
-
-#define ARG_ANY(n) INTERPRET(v##n, what->a##n.p)
-
-#define ARG(n,t) ARG_ANY(n) \
- if (v##n.type != t) \
- runtime("Argument %d of instruction %s must be of type %02x, got %02x", \
- n, f_instruction_name(what->fi_code), t, v##n.type);
-
-#define INTERPRET(val, what_) \
- val = interpret(what_); \
- if (val.type & T_RETURN) \
- return val;
-
-#define ACCESS_RTE \
- do { if (!f_rte) runtime("No route to access"); } while (0)
-
-#define ACCESS_EATTRS \
- do { if (!f_eattrs) f_cache_eattrs(); } while (0)
-
-#define BITFIELD_MASK(what) \
- (1u << (what->a2.i >> 24))
-
/**
* interpret
+ * @fs: filter state
* @what: filter to interpret
*
* Interpret given tree of filter instructions. This is core function
@@ -635,1099 +154,88 @@ static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS;
* are either integers, or pointers to instruction trees. Common
* instructions like +, that have two expressions as arguments use
* TWOARGS macro to get both of them evaluated.
- *
- * &f_val structures are copied around, so there are no problems with
- * memory managment.
*/
-static struct f_val
-interpret(struct f_inst *what)
+static enum filter_return
+interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val)
{
- struct symbol *sym;
- struct f_val v1, v2, v3, res = { .type = T_VOID }, *vp;
- unsigned u1, u2;
- int i;
- u32 as;
-
- for ( ; what; what = what->next) {
- res.type = T_VOID;
- switch(what->fi_code) {
-/* Binary operators */
- case FI_ADD:
- ARG(1,T_INT);
- ARG(2,T_INT);
- res.type = T_INT;
- res.val.i = v1.val.i + v2.val.i;
- break;
- case FI_SUBTRACT:
- ARG(1,T_INT);
- ARG(2,T_INT);
- res.type = T_INT;
- res.val.i = v1.val.i - v2.val.i;
- break;
- case FI_MULTIPLY:
- ARG(1,T_INT);
- ARG(2,T_INT);
- res.type = T_INT;
- res.val.i = v1.val.i * v2.val.i;
- break;
- case FI_DIVIDE:
- ARG(1,T_INT);
- ARG(2,T_INT);
- res.type = T_INT;
- if (v2.val.i == 0) runtime( "Mother told me not to divide by 0" );
- res.val.i = v1.val.i / v2.val.i;
- break;
- case FI_AND:
- case FI_OR:
- ARG(1,T_BOOL);
- if (v1.val.i == (what->fi_code == FI_OR)) {
- res.type = T_BOOL;
- res.val.i = v1.val.i;
- } else {
- ARG(2,T_BOOL);
- res = v2;
- }
- break;
- case FI_PAIR_CONSTRUCT:
- ARG(1,T_INT);
- ARG(2,T_INT);
- u1 = v1.val.i;
- u2 = v2.val.i;
- if ((u1 > 0xFFFF) || (u2 > 0xFFFF))
- runtime( "Can't operate with value out of bounds in pair constructor" );
- res.val.i = (u1 << 16) | u2;
- res.type = T_PAIR;
- break;
-
- case FI_EC_CONSTRUCT:
- {
- ARG_ANY(1);
- ARG(2, T_INT);
-
- int check, ipv4_used;
- u32 key, val;
-
- if (v1.type == T_INT) {
- ipv4_used = 0; key = v1.val.i;
- }
- else if (v1.type == T_QUAD) {
- ipv4_used = 1; key = v1.val.i;
- }
- /* IP->Quad implicit conversion */
- else if (val_is_ip4(v1)) {
- ipv4_used = 1; key = ipa_to_u32(v1.val.ip);
- }
- else
- runtime("Can't operate with key of non-integer/IPv4 type in EC constructor");
-
- val = v2.val.i;
-
- /* XXXX */
- res.type = T_EC;
-
- if (what->aux == EC_GENERIC) {
- check = 0; res.val.ec = ec_generic(key, val);
- }
- else if (ipv4_used) {
- check = 1; res.val.ec = ec_ip4(what->aux, key, val);
- }
- else if (key < 0x10000) {
- check = 0; res.val.ec = ec_as2(what->aux, key, val);
- }
- else {
- check = 1; res.val.ec = ec_as4(what->aux, key, val);
- }
-
- if (check && (val > 0xFFFF))
- runtime("Can't operate with value out of bounds in EC constructor");
-
- break;
- }
-
- case FI_LC_CONSTRUCT:
- {
- ARG(1, T_INT);
- ARG(2, T_INT);
- ARG(3, T_INT);
-
- res.type = T_LC;
- res.val.lc = (lcomm) { v1.val.i, v2.val.i, v3.val.i };
-
- break;
- }
-
- case FI_PATHMASK_CONSTRUCT:
- {
- struct f_path_mask *tt = what->a1.p, *vbegin, **vv = &vbegin;
-
- while (tt) {
- *vv = lp_alloc(f_pool, sizeof(struct f_path_mask));
- if (tt->kind == PM_ASN_EXPR) {
- struct f_val res;
- INTERPRET(res, (struct f_inst *) tt->val);
- (*vv)->kind = PM_ASN;
- if (res.type != T_INT) {
- runtime( "Error resolving path mask template: value not an integer" );
- return (struct f_val) { .type = T_VOID };
- }
-
- (*vv)->val = res.val.i;
- } else {
- **vv = *tt;
- }
- tt = tt->next;
- vv = &((*vv)->next);
- }
-
- res = (struct f_val) { .type = T_PATH_MASK, .val.path_mask = vbegin };
- break;
- }
-
-/* Relational operators */
-
-#define COMPARE(x) \
- ARG_ANY(1); \
- ARG_ANY(2); \
- i = val_compare(v1, v2); \
- if (i==CMP_ERROR) \
- runtime( "Can't compare values of incompatible types" ); \
- res.type = T_BOOL; \
- res.val.i = (x); \
- break;
-
-#define SAME(x) \
- ARG_ANY(1); \
- ARG_ANY(2); \
- i = val_same(v1, v2); \
- res.type = T_BOOL; \
- res.val.i = (x); \
- break;
-
- case FI_NEQ: SAME(!i);
- case FI_EQ: SAME(i);
- case FI_LT: COMPARE(i==-1);
- case FI_LTE: COMPARE(i!=1);
-
- case FI_NOT:
- ARG(1,T_BOOL);
- res = v1;
- res.val.i = !res.val.i;
- break;
-
- case FI_MATCH:
- ARG_ANY(1);
- ARG_ANY(2);
- res.type = T_BOOL;
- res.val.i = val_in_range(v1, v2);
- if (res.val.i == CMP_ERROR)
- runtime( "~ applied on unknown type pair" );
- res.val.i = !!res.val.i;
- break;
-
- case FI_NOT_MATCH:
- ARG_ANY(1);
- ARG_ANY(2);
- res.type = T_BOOL;
- res.val.i = val_in_range(v1, v2);
- if (res.val.i == CMP_ERROR)
- runtime( "!~ applied on unknown type pair" );
- res.val.i = !res.val.i;
- break;
-
- case FI_DEFINED:
- ARG_ANY(1);
- res.type = T_BOOL;
- res.val.i = (v1.type != T_VOID) && !undef_value(v1);
- break;
- case FI_TYPE:
- ARG_ANY(1); /* There may be more types supporting this operation */
- switch (v1.type)
- {
- case T_NET:
- res.type = T_ENUM_NETTYPE;
- res.val.i = v1.val.net->type;
- break;
- default:
- runtime( "Can't determine type of this item" );
- }
- break;
- case FI_IS_V4:
- ARG(1, T_IP);
- res.type = T_BOOL;
- res.val.i = ipa_is_ip4(v1.val.ip);
- break;
-
- /* Set to indirect value, a1 = variable, a2 = value */
- case FI_SET:
- ARG_ANY(2);
- sym = what->a1.p;
- vp = sym->def;
- if ((sym->class != (SYM_VARIABLE | v2.type)) && (v2.type != T_VOID))
- {
- /* IP->Quad implicit conversion */
- if ((sym->class == (SYM_VARIABLE | T_QUAD)) && val_is_ip4(v2))
- {
- vp->type = T_QUAD;
- vp->val.i = ipa_to_u32(v2.val.ip);
- break;
- }
- runtime( "Assigning to variable of incompatible type" );
- }
- *vp = v2;
- break;
-
- /* some constants have value in a2, some in *a1.p, strange. */
- case FI_CONSTANT: /* integer (or simple type) constant, string, set, or prefix_set */
- res.type = what->aux;
-
- if (res.type == T_PREFIX_SET)
- res.val.ti = what->a2.p;
- else if (res.type == T_SET)
- res.val.t = what->a2.p;
- else if (res.type == T_STRING)
- res.val.s = what->a2.p;
- else
- res.val.i = what->a2.i;
- break;
- case FI_VARIABLE:
- case FI_CONSTANT_INDIRECT:
- res = * ((struct f_val *) what->a1.p);
- break;
- case FI_PRINT:
- ARG_ANY(1);
- val_format(v1, &f_buf);
- break;
- case FI_CONDITION: /* ? has really strange error value, so we can implement if ... else nicely :-) */
- ARG(1, T_BOOL);
- if (v1.val.i) {
- ARG_ANY(2);
- res.val.i = 0;
- } else
- res.val.i = 1;
- res.type = T_BOOL;
- break;
- case FI_NOP:
- debug( "No operation\n" );
- break;
- case FI_PRINT_AND_DIE:
- ARG_ANY(1);
- if ((what->a2.i == F_NOP || (what->a2.i != F_NONL && what->a1.p)) &&
- !(f_flags & FF_SILENT))
- log_commit(*L_INFO, &f_buf);
-
- switch (what->a2.i) {
- 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: /* FIXME (noncritical) Should print complete route along with reason to reject route */
- res.type = T_RETURN;
- res.val.i = what->a2.i;
- return res; /* We have to return now, no more processing. */
- case F_NONL:
- case F_NOP:
- break;
- default:
- bug( "unknown return type: Can't happen");
- }
- break;
- case FI_RTA_GET: /* rta access */
- {
- ACCESS_RTE;
- struct rta *rta = (*f_rte)->attrs;
- res.type = what->aux;
-
- switch (what->a2.i)
- {
- case SA_FROM: res.val.ip = rta->from; break;
- case SA_GW: res.val.ip = rta->nh.gw; break;
- case SA_NET: res.val.net = (*f_rte)->net->n.addr; break;
- case SA_PROTO: res.val.s = rta->src->proto->name; break;
- case SA_SOURCE: res.val.i = rta->source; break;
- case SA_SCOPE: res.val.i = rta->scope; break;
- case SA_DEST: res.val.i = rta->dest; break;
- case SA_IFNAME: res.val.s = rta->nh.iface ? rta->nh.iface->name : ""; break;
- case SA_IFINDEX: res.val.i = rta->nh.iface ? rta->nh.iface->index : 0; break;
-
- default:
- bug("Invalid static attribute access (%x)", res.type);
- }
- }
- break;
- case FI_RTA_SET:
- ACCESS_RTE;
- ARG_ANY(1);
- if (what->aux != v1.type)
- runtime( "Attempt to set static attribute to incompatible type" );
-
- f_rta_cow();
- {
- struct rta *rta = (*f_rte)->attrs;
-
- switch (what->a2.i)
- {
- case SA_FROM:
- rta->from = v1.val.ip;
- break;
-
- case SA_GW:
- {
- ip_addr ip = v1.val.ip;
- neighbor *n = neigh_find(rta->src->proto, ip, NULL, 0);
- if (!n || (n->scope == SCOPE_HOST))
- runtime( "Invalid gw address" );
-
- rta->dest = RTD_UNICAST;
- rta->nh.gw = ip;
- rta->nh.iface = n->iface;
- rta->nh.next = NULL;
- rta->hostentry = NULL;
- }
- break;
-
- case SA_SCOPE:
- rta->scope = v1.val.i;
- break;
+ /* No arguments allowed */
+ ASSERT(line->args == 0);
- case SA_DEST:
- i = v1.val.i;
- if ((i != RTD_BLACKHOLE) && (i != RTD_UNREACHABLE) && (i != RTD_PROHIBIT))
- runtime( "Destination can be changed only to blackhole, unreachable or prohibit" );
+ /* Initialize the filter stack */
+ struct filter_stack *fstk = fs->stack;
- rta->dest = i;
- rta->nh.gw = IPA_NONE;
- rta->nh.iface = NULL;
- rta->nh.next = NULL;
- rta->hostentry = NULL;
- break;
-
- case SA_IFNAME:
- {
- struct iface *ifa = if_find_by_name(v1.val.s);
- if (!ifa)
- runtime( "Invalid iface name" );
-
- rta->dest = RTD_UNICAST;
- rta->nh.gw = IPA_NONE;
- rta->nh.iface = ifa;
- rta->nh.next = NULL;
- rta->hostentry = NULL;
- }
- break;
-
- default:
- bug("Invalid static attribute access (%x)", res.type);
- }
- }
- break;
- case FI_EA_GET: /* Access to extended attributes */
- ACCESS_RTE;
- ACCESS_EATTRS;
- {
- u16 code = what->a2.i;
- int f_type = what->aux >> 8;
- eattr *e = ea_find(*f_eattrs, code);
-
- if (!e) {
- /* A special case: undefined as_path looks like empty as_path */
- if ((what->aux & EAF_TYPE_MASK) == EAF_TYPE_AS_PATH) {
- res.type = T_PATH;
- res.val.ad = &undef_adata;
- break;
- }
-
- /* The same special case for int_set */
- if ((what->aux & EAF_TYPE_MASK) == EAF_TYPE_INT_SET) {
- res.type = T_CLIST;
- res.val.ad = &undef_adata;
- break;
- }
-
- /* The same special case for ec_set */
- if ((what->aux & EAF_TYPE_MASK) == EAF_TYPE_EC_SET) {
- res.type = T_ECLIST;
- res.val.ad = &undef_adata;
- break;
- }
-
- /* The same special case for lc_set */
- if ((what->aux & EAF_TYPE_MASK) == EAF_TYPE_LC_SET) {
- res.type = T_LCLIST;
- res.val.ad = &undef_adata;
- break;
- }
-
- /* Undefined value */
- res.type = T_VOID;
- break;
- }
-
- switch (e->type & EAF_TYPE_MASK) {
- case EAF_TYPE_INT:
- res.type = f_type;
- res.val.i = e->u.data;
- break;
- case EAF_TYPE_ROUTER_ID:
- res.type = T_QUAD;
- res.val.i = e->u.data;
- break;
- case EAF_TYPE_OPAQUE:
- res.type = T_ENUM_EMPTY;
- res.val.i = 0;
- break;
- case EAF_TYPE_IP_ADDRESS:
- res.type = T_IP;
- struct adata * ad = e->u.ptr;
- res.val.ip = * (ip_addr *) ad->data;
- break;
- case EAF_TYPE_AS_PATH:
- res.type = T_PATH;
- res.val.ad = e->u.ptr;
- break;
- case EAF_TYPE_BITFIELD:
- res.type = T_BOOL;
- res.val.i = !!(e->u.data & BITFIELD_MASK(what));
- break;
- case EAF_TYPE_INT_SET:
- res.type = T_CLIST;
- res.val.ad = e->u.ptr;
- break;
- case EAF_TYPE_EC_SET:
- res.type = T_ECLIST;
- res.val.ad = e->u.ptr;
- break;
- case EAF_TYPE_LC_SET:
- res.type = T_LCLIST;
- res.val.ad = e->u.ptr;
- break;
- case EAF_TYPE_UNDEF:
- res.type = T_VOID;
- break;
- default:
- bug("Unknown type in e,a");
- }
- }
- break;
- case FI_EA_SET:
- ACCESS_RTE;
- ACCESS_EATTRS;
- ARG_ANY(1);
- {
- struct ea_list *l = lp_alloc(f_pool, sizeof(struct ea_list) + sizeof(eattr));
- u16 code = what->a2.i;
- int f_type = what->aux >> 8;
-
- l->next = NULL;
- l->flags = EALF_SORTED;
- l->count = 1;
- l->attrs[0].id = code;
- l->attrs[0].flags = 0;
- l->attrs[0].type = (what->aux & 0xff) | EAF_ORIGINATED | EAF_FRESH;
-
- switch (what->aux & EAF_TYPE_MASK) {
- case EAF_TYPE_INT:
- if (v1.type != 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;
-
- case EAF_TYPE_OPAQUE:
- 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" );
- int len = sizeof(ip_addr);
- struct adata *ad = lp_alloc(f_pool, sizeof(struct adata) + len);
- ad->length = len;
- (* (ip_addr *) ad->data) = v1.val.ip;
- l->attrs[0].u.ptr = ad;
- break;
- case EAF_TYPE_AS_PATH:
- if (v1.type != T_PATH)
- runtime( "Setting path attribute to non-path value" );
- 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(*f_eattrs, code);
- u32 data = e ? e->u.data : 0;
-
- if (v1.val.i)
- l->attrs[0].u.data = data | BITFIELD_MASK(what);
- else
- l->attrs[0].u.data = data & ~BITFIELD_MASK(what);;
- }
- 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;
- case EAF_TYPE_UNDEF:
- if (v1.type != T_VOID)
- runtime( "Setting void attribute to non-void value" );
- l->attrs[0].u.data = 0;
- break;
- default: bug("Unknown type in e,S");
- }
-
- f_rta_cow();
- l->next = *f_eattrs;
- *f_eattrs = l;
- }
- break;
- case FI_PREF_GET:
- ACCESS_RTE;
- res.type = T_INT;
- res.val.i = (*f_rte)->pref;
- break;
- case FI_PREF_SET:
- ACCESS_RTE;
- ARG(1,T_INT);
- if (v1.val.i > 0xFFFF)
- runtime( "Setting preference value out of bounds" );
- f_rte_cow();
- (*f_rte)->pref = v1.val.i;
- break;
- case FI_LENGTH: /* Get length of */
- ARG_ANY(1);
- res.type = T_INT;
- switch(v1.type) {
- case T_NET: res.val.i = net_pxlen(v1.val.net); break;
- case T_PATH: res.val.i = as_path_getlen(v1.val.ad); break;
- case T_CLIST: res.val.i = int_set_get_size(v1.val.ad); break;
- case T_ECLIST: res.val.i = ec_set_get_size(v1.val.ad); break;
- case T_LCLIST: res.val.i = lc_set_get_size(v1.val.ad); break;
- default: runtime( "Prefix, path, clist or eclist expected" );
- }
- break;
- case FI_SADR_SRC: /* Get SADR 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 *src = lp_alloc(f_pool, sizeof(net_addr_ip6));
- net_fill_ip6(src, net->src_prefix, net->src_pxlen);
-
- res.type = T_NET;
- res.val.net = src;
- }
- break;
- case FI_ROA_MAXLEN: /* Get ROA max prefix length */
- ARG(1, T_NET);
- if (!net_is_roa(v1.val.net))
- runtime( "ROA expected" );
-
- res.type = T_INT;
- res.val.i = (v1.val.net->type == NET_ROA4) ?
- ((net_addr_roa4 *) v1.val.net)->max_pxlen :
- ((net_addr_roa6 *) v1.val.net)->max_pxlen;
- break;
- case FI_ROA_ASN: /* Get ROA ASN */
- ARG(1, T_NET);
- if (!net_is_roa(v1.val.net))
- runtime( "ROA expected" );
-
- res.type = T_INT;
- res.val.i = (v1.val.net->type == NET_ROA4) ?
- ((net_addr_roa4 *) v1.val.net)->asn :
- ((net_addr_roa6 *) v1.val.net)->asn;
- break;
- case FI_IP: /* Convert prefix to ... */
- ARG(1, T_NET);
- res.type = T_IP;
- res.val.ip = net_prefix(v1.val.net);
- break;
- case FI_ROUTE_DISTINGUISHER:
- ARG(1, T_NET);
- res.type = T_IP;
- if (!net_is_vpn(v1.val.net))
- runtime( "VPN address expected" );
- res.type = T_RD;
- res.val.ec = net_rd(v1.val.net);
- break;
- case FI_AS_PATH_FIRST: /* Get first ASN from AS PATH */
- ARG(1, T_PATH);
-
- as = 0;
- as_path_get_first(v1.val.ad, &as);
- res.type = T_INT;
- res.val.i = as;
- break;
- case FI_AS_PATH_LAST: /* Get last ASN from AS PATH */
- ARG(1, T_PATH);
-
- as = 0;
- as_path_get_last(v1.val.ad, &as);
- res.type = T_INT;
- res.val.i = as;
- break;
- case FI_AS_PATH_LAST_NAG: /* Get last ASN from non-aggregated part of AS PATH */
- ARG(1, T_PATH);
-
- res.type = T_INT;
- res.val.i = as_path_get_last_nonaggregated(v1.val.ad);
- break;
- case FI_RETURN:
- ARG_ANY(1);
- res = v1;
- res.type |= T_RETURN;
- return res;
- case FI_CALL: /* CALL: this is special: if T_RETURN and returning some value, mask it out */
- ARG_ANY(1);
- res = interpret(what->a2.p);
- if (res.type == T_RETURN)
- return res;
- res.type &= ~T_RETURN;
- break;
- case FI_CLEAR_LOCAL_VARS: /* Clear local variables */
- for (sym = what->a1.p; sym != NULL; sym = sym->aux2)
- ((struct f_val *) sym->def)->type = T_VOID;
- break;
- case FI_SWITCH:
- ARG_ANY(1);
- {
- struct f_tree *t = find_tree(what->a2.p, v1);
- if (!t) {
- v1.type = T_VOID;
- t = find_tree(what->a2.p, v1);
- if (!t) {
- debug( "No else statement?\n");
- break;
- }
- }
- /* It is actually possible to have t->data NULL */
-
- INTERPRET(res, t->data);
- }
- break;
- case FI_IP_MASK: /* IP.MASK(val) */
- ARG(1, T_IP);
- ARG(2, T_INT);
-
- res.type = T_IP;
- res.val.ip = ipa_is_ip4(v1.val.ip) ?
- ipa_from_ip4(ip4_and(ipa_to_ip4(v1.val.ip), ip4_mkmask(v2.val.i))) :
- ipa_from_ip6(ip6_and(ipa_to_ip6(v1.val.ip), ip6_mkmask(v2.val.i)));
- break;
-
- case FI_EMPTY: /* Create empty attribute */
- res.type = what->aux;
- res.val.ad = adata_empty(f_pool, 0);
- break;
- case FI_PATH_PREPEND: /* Path prepend */
- ARG(1, T_PATH);
- ARG(2, T_INT);
-
- res.type = T_PATH;
- res.val.ad = as_path_prepend(f_pool, v1.val.ad, v2.val.i);
- break;
-
- case FI_CLIST_ADD_DEL: /* (Extended) Community list add or delete */
- ARG_ANY(1);
- ARG_ANY(2);
- if (v1.type == T_PATH)
- {
- struct f_tree *set = NULL;
- u32 key = 0;
- int pos;
-
- if (v2.type == T_INT)
- key = v2.val.i;
- else if ((v2.type == T_SET) && (v2.val.t->from.type == T_INT))
- set = v2.val.t;
- else
- runtime("Can't delete non-integer (set)");
-
- switch (what->aux)
- {
- case 'a': runtime("Can't add to path");
- case 'd': pos = 0; break;
- case 'f': pos = 1; break;
- default: bug("unknown Ca operation");
- }
-
- if (pos && !set)
- runtime("Can't filter integer");
-
- res.type = T_PATH;
- res.val.ad = as_path_filter(f_pool, v1.val.ad, set, key, pos);
- }
- else if (v1.type == T_CLIST)
- {
- /* Community (or cluster) list */
- struct f_val dummy;
- int arg_set = 0;
- uint n = 0;
-
- if ((v2.type == T_PAIR) || (v2.type == T_QUAD))
- n = v2.val.i;
- /* IP->Quad implicit conversion */
- else if (val_is_ip4(v2))
- n = ipa_to_u32(v2.val.ip);
- else if ((v2.type == T_SET) && clist_set_type(v2.val.t, &dummy))
- arg_set = 1;
- else if (v2.type == T_CLIST)
- arg_set = 2;
- else
- runtime("Can't add/delete non-pair");
-
- res.type = T_CLIST;
- switch (what->aux)
- {
- case 'a':
- if (arg_set == 1)
- runtime("Can't add set");
- else if (!arg_set)
- res.val.ad = int_set_add(f_pool, v1.val.ad, n);
- else
- res.val.ad = int_set_union(f_pool, v1.val.ad, v2.val.ad);
- break;
-
- case 'd':
- if (!arg_set)
- res.val.ad = int_set_del(f_pool, v1.val.ad, n);
- else
- res.val.ad = clist_filter(f_pool, v1.val.ad, v2, 0);
- break;
-
- case 'f':
- if (!arg_set)
- runtime("Can't filter pair");
- res.val.ad = clist_filter(f_pool, v1.val.ad, v2, 1);
- break;
+ fstk->vcnt = line->vars;
+ memset(fstk->vstk, 0, sizeof(struct f_val) * line->vars);
- default:
- bug("unknown Ca operation");
- }
- }
- else if (v1.type == T_ECLIST)
- {
- /* Extended community list */
- int arg_set = 0;
-
- /* v2.val is either EC or EC-set */
- if ((v2.type == T_SET) && eclist_set_type(v2.val.t))
- arg_set = 1;
- else if (v2.type == T_ECLIST)
- arg_set = 2;
- else if (v2.type != T_EC)
- runtime("Can't add/delete non-ec");
-
- res.type = T_ECLIST;
- switch (what->aux)
- {
- case 'a':
- if (arg_set == 1)
- runtime("Can't add set");
- else if (!arg_set)
- res.val.ad = ec_set_add(f_pool, v1.val.ad, v2.val.ec);
- else
- res.val.ad = ec_set_union(f_pool, v1.val.ad, v2.val.ad);
- break;
+ /* The same as with the value stack. Not resetting the stack for performance reasons. */
+ fstk->ecnt = 1;
+ fstk->estk[0].line = line;
+ fstk->estk[0].pos = 0;
- case 'd':
- if (!arg_set)
- res.val.ad = ec_set_del(f_pool, v1.val.ad, v2.val.ec);
- else
- res.val.ad = eclist_filter(f_pool, v1.val.ad, v2, 0);
- break;
+#define curline fstk->estk[fstk->ecnt-1]
- case 'f':
- if (!arg_set)
- runtime("Can't filter ec");
- res.val.ad = eclist_filter(f_pool, v1.val.ad, v2, 1);
- break;
-
- default:
- bug("unknown Ca operation");
- }
- }
- else if (v1.type == T_LCLIST)
- {
- /* Large community list */
- int arg_set = 0;
-
- /* v2.val is either LC or LC-set */
- if ((v2.type == T_SET) && lclist_set_type(v2.val.t))
- arg_set = 1;
- else if (v2.type == T_LCLIST)
- arg_set = 2;
- else if (v2.type != T_LC)
- runtime("Can't add/delete non-lc");
-
- res.type = T_LCLIST;
- switch (what->aux)
- {
- case 'a':
- if (arg_set == 1)
- runtime("Can't add set");
- else if (!arg_set)
- res.val.ad = lc_set_add(f_pool, v1.val.ad, v2.val.lc);
- else
- res.val.ad = lc_set_union(f_pool, v1.val.ad, v2.val.ad);
- break;
+#if DEBUGGING
+ debug("Interpreting line.");
+ f_dump_line(line, 1);
+#endif
- case 'd':
- if (!arg_set)
- res.val.ad = lc_set_del(f_pool, v1.val.ad, v2.val.lc);
- else
- res.val.ad = lclist_filter(f_pool, v1.val.ad, v2, 0);
- break;
+ while (fstk->ecnt > 0) {
+ while (curline.pos < curline.line->len) {
+ const struct f_line_item *what = &(curline.line->items[curline.pos++]);
- case 'f':
- if (!arg_set)
- runtime("Can't filter lc");
- res.val.ad = lclist_filter(f_pool, v1.val.ad, v2, 1);
- break;
+ switch (what->fi_code) {
+#define res fstk->vstk[fstk->vcnt]
+#define vv(i) fstk->vstk[fstk->vcnt + (i)]
+#define v1 vv(0)
+#define v2 vv(1)
+#define v3 vv(2)
- default:
- bug("unknown Ca operation");
+#define runtime(fmt, ...) do { \
+ if (!(fs->flags & FF_SILENT)) \
+ log_rl(&rl_runtime_err, L_ERR "filters, line %d: " fmt, what->lineno, ##__VA_ARGS__); \
+ return F_ERROR; \
+} while(0)
+
+#define falloc(size) lp_alloc(fs->pool, size)
+#define fpool fs->pool
+
+#define ACCESS_EATTRS do { if (!fs->eattrs) f_cache_eattrs(fs); } while (0)
+
+#include "filter/inst-interpret.c"
+#undef res
+#undef v1
+#undef v2
+#undef v3
+#undef runtime
+#undef falloc
+#undef fpool
+#undef ACCESS_EATTRS
}
}
- else
- runtime("Can't add/delete to non-[e|l]clist");
-
- break;
- case FI_ROA_CHECK: /* ROA Check */
- if (what->arg1)
- {
- ARG(1, T_NET);
- ARG(2, T_INT);
-
- as = v2.val.i;
- }
- else
- {
- ACCESS_RTE;
- ACCESS_EATTRS;
- v1.val.net = (*f_rte)->net->n.addr;
-
- /* We ignore temporary attributes, probably not a problem here */
- /* 0x02 is a value of BA_AS_PATH, we don't want to include BGP headers */
- eattr *e = ea_find(*f_eattrs, EA_CODE(PROTOCOL_BGP, 0x02));
-
- if (!e || ((e->type & EAF_TYPE_MASK) != EAF_TYPE_AS_PATH))
- runtime("Missing AS_PATH attribute");
+ /* End of current line. Drop local variables before exiting. */
+ fstk->vcnt -= curline.line->vars;
+ fstk->vcnt -= curline.line->args;
+ fstk->ecnt--;
+ }
- as_path_get_last(e->u.ptr, &as);
+ if (fstk->vcnt == 0) {
+ if (val) {
+ log_rl(&rl_runtime_err, L_ERR "filters: No value left on stack");
+ return F_ERROR;
}
+ return F_NOP;
+ }
- struct rtable *table = ((struct f_inst_roa_check *) what)->rtc->table;
- if (!table)
- runtime("Missing ROA table");
-
- if (table->addr_type != NET_ROA4 && table->addr_type != NET_ROA6)
- runtime("Table type must be either ROA4 or ROA6");
-
- res.type = T_ENUM_ROA;
-
- if (table->addr_type != (v1.val.net->type == NET_IP4 ? NET_ROA4 : NET_ROA6))
- res.val.i = ROA_UNKNOWN; /* Prefix and table type mismatch */
- else
- res.val.i = net_roa_check(table, v1.val.net, as);
-
- break;
-
- case FI_FORMAT: /* Format */
- ARG_ANY(1);
-
- res.type = T_STRING;
- res.val.s = val_format_str(v1);
- break;
-
- case FI_ASSERT: /* Birdtest Assert */
- ARG(1, T_BOOL);
-
- res.type = v1.type;
- res.val = v1.val;
-
- CALL(bt_assert_hook, res.val.i, what);
- break;
+ if (val && (fstk->vcnt == 1)) {
+ *val = fstk->vstk[0];
+ return F_NOP;
+ }
- default:
- bug( "Unknown instruction %d (%c)", what->fi_code, what->fi_code & 0xff);
- }}
- return res;
+ log_rl(&rl_runtime_err, L_ERR "Too many items left on stack: %u", fstk->vcnt);
+ return F_ERROR;
}
-#undef ARG
-#undef ARG_ANY
-
-#define ARG(n) \
- if (!i_same(f1->a##n.p, f2->a##n.p)) \
- return 0;
-
-#define ONEARG ARG(1);
-#define TWOARGS ONEARG; ARG(2);
-#define THREEARGS TWOARGS; ARG(3);
-
-#define A2_SAME if (f1->a2.i != f2->a2.i) return 0;
-
-/*
- * i_same - function that does real comparing of instruction trees, you should call filter_same from outside
- */
-int
-i_same(struct f_inst *f1, struct f_inst *f2)
-{
- if ((!!f1) != (!!f2))
- return 0;
- if (!f1)
- return 1;
- if (f1->aux != f2->aux)
- return 0;
- if (f1->fi_code != f2->fi_code)
- return 0;
- if (f1 == f2) /* It looks strange, but it is possible with call rewriting trickery */
- return 1;
-
- switch(f1->fi_code) {
- case FI_ADD: /* fall through */
- case FI_SUBTRACT:
- case FI_MULTIPLY:
- case FI_DIVIDE:
- case FI_OR:
- case FI_AND:
- case FI_PAIR_CONSTRUCT:
- case FI_EC_CONSTRUCT:
- case FI_NEQ:
- case FI_EQ:
- case FI_LT:
- case FI_LTE: TWOARGS; break;
-
- case FI_PATHMASK_CONSTRUCT: if (!pm_same(f1->a1.p, f2->a1.p)) return 0; break;
-
- case FI_NOT: ONEARG; break;
- case FI_NOT_MATCH:
- case FI_MATCH: TWOARGS; break;
- case FI_DEFINED: ONEARG; break;
- case FI_TYPE: ONEARG; break;
-
- case FI_LC_CONSTRUCT:
- THREEARGS;
- break;
-
- case FI_SET:
- ARG(2);
- {
- struct symbol *s1, *s2;
- s1 = f1->a1.p;
- s2 = f2->a1.p;
- if (strcmp(s1->name, s2->name))
- return 0;
- if (s1->class != s2->class)
- return 0;
- }
- break;
-
- case FI_CONSTANT:
- switch (f1->aux) {
-
- case T_PREFIX_SET:
- if (!trie_same(f1->a2.p, f2->a2.p))
- return 0;
- break;
-
- case T_SET:
- if (!same_tree(f1->a2.p, f2->a2.p))
- return 0;
- break;
-
- case T_STRING:
- if (strcmp(f1->a2.p, f2->a2.p))
- return 0;
- break;
-
- default:
- A2_SAME;
- }
- break;
-
- case FI_CONSTANT_INDIRECT:
- if (!val_same(* (struct f_val *) f1->a1.p, * (struct f_val *) f2->a1.p))
- return 0;
- break;
-
- case FI_VARIABLE:
- if (strcmp((char *) f1->a2.p, (char *) f2->a2.p))
- return 0;
- break;
- case FI_PRINT: case FI_LENGTH: ONEARG; break;
- case FI_CONDITION: TWOARGS; break;
- case FI_NOP: case FI_EMPTY: break;
- case FI_PRINT_AND_DIE: ONEARG; A2_SAME; break;
- case FI_PREF_GET:
- case FI_RTA_GET: A2_SAME; break;
- case FI_EA_GET: A2_SAME; break;
- case FI_PREF_SET:
- case FI_RTA_SET:
- case FI_EA_SET: ONEARG; A2_SAME; break;
-
- case FI_RETURN: ONEARG; break;
- case FI_ROA_MAXLEN: ONEARG; break;
- case FI_ROA_ASN: ONEARG; break;
- case FI_SADR_SRC: ONEARG; break;
- case FI_IP: ONEARG; break;
- case FI_IS_V4: ONEARG; break;
- case FI_ROUTE_DISTINGUISHER: ONEARG; break;
- case FI_CALL: /* Call rewriting trickery to avoid exponential behaviour */
- ONEARG;
- if (!i_same(f1->a2.p, f2->a2.p))
- return 0;
- f2->a2.p = f1->a2.p;
- break;
- case FI_CLEAR_LOCAL_VARS: break; /* internal instruction */
- case FI_SWITCH: ONEARG; if (!same_tree(f1->a2.p, f2->a2.p)) return 0; break;
- case FI_IP_MASK: TWOARGS; break;
- case FI_PATH_PREPEND: TWOARGS; break;
- case FI_CLIST_ADD_DEL: TWOARGS; break;
- case FI_AS_PATH_FIRST:
- case FI_AS_PATH_LAST:
- case FI_AS_PATH_LAST_NAG: ONEARG; break;
- case FI_ROA_CHECK:
- TWOARGS;
- /* Does not really make sense - ROA check results may change anyway */
- if (strcmp(((struct f_inst_roa_check *) f1)->rtc->name,
- ((struct f_inst_roa_check *) f2)->rtc->name))
- return 0;
- break;
- case FI_FORMAT: ONEARG; break;
- case FI_ASSERT: ONEARG; break;
- default:
- bug( "Unknown instruction %d in same (%c)", f1->fi_code, f1->fi_code & 0xff);
- }
- return i_same(f1->next, f2->next);
-}
/**
* f_run - run a filter for a route
@@ -1743,7 +251,7 @@ i_same(struct f_inst *f1, struct f_inst *f2)
* copied).
*
* The returned rte may reuse the (possibly cached, cloned) rta, or
- * (if rta was modificied) contains a modified uncached rta, which
+ * (if rta was modified) contains a modified uncached rta, which
* uses parts allocated from @tmp_pool and parts shared from original
* rta. There is one exception - if @rte is rw but contains a cached
* rta and that is modified, rta in returned rte is also cached.
@@ -1753,8 +261,8 @@ i_same(struct f_inst *f1, struct f_inst *f2)
* (and cached rta of read-only source rte is intact), if rte is
* modified in place, old cached rta is possibly freed.
*/
-int
-f_run(struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int flags)
+enum filter_return
+f_run(const struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int flags)
{
if (filter == FILTER_ACCEPT)
return F_ACCEPT;
@@ -1765,107 +273,241 @@ f_run(struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int fla
int rte_cow = ((*rte)->flags & REF_COW);
DBG( "Running filter `%s'...", filter->name );
- f_rte = rte;
- f_eattrs = NULL;
- f_old_rta = NULL;
- f_pool = tmp_pool;
- f_flags = flags;
+ /* Initialize the filter state */
+ filter_state = (struct filter_state) {
+ .stack = &filter_stack,
+ .rte = rte,
+ .pool = tmp_pool,
+ .flags = flags,
+ };
- LOG_BUFFER_INIT(f_buf);
+ LOG_BUFFER_INIT(filter_state.buf);
- struct f_val res = interpret(filter->root);
+ /* Run the interpreter itself */
+ enum filter_return fret = interpret(&filter_state, filter->root, NULL);
- if (f_old_rta) {
+ if (filter_state.old_rta) {
/*
- * Cached rta was modified and f_rte contains now an uncached one,
+ * Cached rta was modified and filter_state->rte contains now an uncached one,
* sharing some part with the cached one. The cached rta should
- * be freed (if rte was originally COW, f_old_rta is a clone
+ * be freed (if rte was originally COW, filter_state->old_rta is a clone
* obtained during rte_cow()).
*
* This also implements the exception mentioned in f_run()
* description. The reason for this is that rta reuses parts of
- * f_old_rta, and these may be freed during rta_free(f_old_rta).
+ * filter_state->old_rta, and these may be freed during rta_free(filter_state->old_rta).
* This is not the problem if rte was COW, because original rte
* also holds the same rta.
*/
- if (!rte_cow)
- (*f_rte)->attrs = rta_lookup((*f_rte)->attrs);
+ if (!rte_cow) {
+ /* Cache the new attrs */
+ (*filter_state.rte)->attrs = rta_lookup((*filter_state.rte)->attrs);
- rta_free(f_old_rta);
- }
+ /* Drop cached ea_list pointer */
+ filter_state.eattrs = NULL;
+ }
+ /* Uncache the old attrs and drop the pointer as it is invalid now. */
+ rta_free(filter_state.old_rta);
+ filter_state.old_rta = NULL;
+ }
- if (res.type != T_RETURN) {
- if (!(f_flags & FF_SILENT))
- log_rl(&rl_runtime_err, L_ERR "Filter %s did not return accept nor reject. Make up your mind", filter->name);
+ /* Process the filter output, log it and return */
+ if (fret < F_ACCEPT) {
+ if (!(filter_state.flags & FF_SILENT))
+ log_rl(&rl_runtime_err, L_ERR "Filter %s did not return accept nor reject. Make up your mind", filter_name(filter));
return F_ERROR;
}
DBG( "done (%u)\n", res.val.i );
- return res.val.i;
+ return fret;
}
-/* TODO: perhaps we could integrate f_eval(), f_eval_rte() and f_run() */
+/**
+ * f_eval_rte - run a filter line for an uncached route
+ * @expr: filter line to run
+ * @rte: route being filtered, may be modified
+ * @tmp_pool: all filter allocations go from this pool
+ *
+ * This specific filter entry point runs the given filter line
+ * (which must not have any arguments) on the given route.
+ *
+ * The route MUST NOT have REF_COW set and its attributes MUST NOT
+ * be cached by rta_lookup().
+ */
-struct f_val
-f_eval_rte(struct f_inst *expr, struct rte **rte, struct linpool *tmp_pool)
+enum filter_return
+f_eval_rte(const struct f_line *expr, struct rte **rte, struct linpool *tmp_pool)
{
+ filter_state = (struct filter_state) {
+ .stack = &filter_stack,
+ .rte = rte,
+ .pool = tmp_pool,
+ };
- f_rte = rte;
- f_eattrs = NULL;
- f_old_rta = NULL;
- f_pool = tmp_pool;
- f_flags = 0;
+ LOG_BUFFER_INIT(filter_state.buf);
- LOG_BUFFER_INIT(f_buf);
+ ASSERT(!((*rte)->flags & REF_COW));
+ ASSERT(!rta_is_cached((*rte)->attrs));
- /* Note that in this function we assume that rte->attrs is private / uncached */
- struct f_val res = interpret(expr);
-
- return res;
+ return interpret(&filter_state, expr, NULL);
}
-struct f_val
-f_eval(struct f_inst *expr, struct linpool *tmp_pool)
+/*
+ * f_eval - get a value of a term
+ * @expr: filter line containing the term
+ * @tmp_pool: long data may get allocated from this pool
+ * @pres: here the output will be stored
+ */
+enum filter_return
+f_eval(const struct f_line *expr, struct linpool *tmp_pool, struct f_val *pres)
{
- f_flags = 0;
- f_eattrs = NULL;
- f_rte = NULL;
- f_pool = tmp_pool;
+ filter_state = (struct filter_state) {
+ .stack = &filter_stack,
+ .pool = tmp_pool,
+ };
- LOG_BUFFER_INIT(f_buf);
+ LOG_BUFFER_INIT(filter_state.buf);
- return interpret(expr);
+ enum filter_return fret = interpret(&filter_state, expr, pres);
+ return fret;
}
+/*
+ * f_eval_int - get an integer value of a term
+ * Called internally from the config parser, uses its internal memory pool
+ * for allocations. Do not call in other cases.
+ */
uint
-f_eval_int(struct f_inst *expr)
+f_eval_int(const struct f_line *expr)
{
/* Called independently in parse-time to eval expressions */
- struct f_val res = f_eval(expr, cfg_mem);
+ filter_state = (struct filter_state) {
+ .stack = &filter_stack,
+ .pool = cfg_mem,
+ };
+
+ struct f_val val;
- if (res.type != T_INT)
+ LOG_BUFFER_INIT(filter_state.buf);
+
+ if (interpret(&filter_state, expr, &val) > F_RETURN)
+ cf_error("Runtime error while evaluating expression; see log for details");
+
+ if (val.type != T_INT)
cf_error("Integer expression expected");
- return res.val.i;
+ return val.val.i;
+}
+
+/*
+ * f_eval_buf - get a value of a term and print it to the supplied buffer
+ */
+enum filter_return
+f_eval_buf(const struct f_line *expr, struct linpool *tmp_pool, buffer *buf)
+{
+ struct f_val val;
+ enum filter_return fret = f_eval(expr, tmp_pool, &val);
+ if (fret > F_RETURN)
+ val_format(&val, buf);
+ return fret;
}
/**
* filter_same - compare two filters
* @new: first filter to be compared
- * @old: second filter to be compared, notice that this filter is
- * damaged while comparing.
+ * @old: second filter to be compared
*
* Returns 1 in case filters are same, otherwise 0. If there are
* underlying bugs, it will rather say 0 on same filters than say
* 1 on different.
*/
int
-filter_same(struct filter *new, struct filter *old)
+filter_same(const struct filter *new, const struct filter *old)
{
if (old == new) /* Handle FILTER_ACCEPT and FILTER_REJECT */
return 1;
if (old == FILTER_ACCEPT || old == FILTER_REJECT ||
new == FILTER_ACCEPT || new == FILTER_REJECT)
return 0;
- return i_same(new->root, old->root);
+
+ if ((!old->sym) && (!new->sym))
+ return f_same(new->root, old->root);
+
+ if ((!old->sym) || (!new->sym))
+ return 0;
+
+ if (strcmp(old->sym->name, new->sym->name))
+ return 0;
+
+ return new->sym->flags & SYM_FLAG_SAME;
+}
+
+/**
+ * filter_commit - do filter comparisons on all the named functions and filters
+ */
+void
+filter_commit(struct config *new, struct config *old)
+{
+ if (!old)
+ return;
+
+ struct symbol *sym, *osym;
+ WALK_LIST(sym, new->symbols)
+ switch (sym->class) {
+ case SYM_FUNCTION:
+ if ((osym = cf_find_symbol(old, sym->name)) &&
+ (osym->class == SYM_FUNCTION) &&
+ f_same(sym->function, osym->function))
+ sym->flags |= SYM_FLAG_SAME;
+ else
+ sym->flags &= ~SYM_FLAG_SAME;
+ break;
+
+ case SYM_FILTER:
+ if ((osym = cf_find_symbol(old, sym->name)) &&
+ (osym->class == SYM_FILTER) &&
+ f_same(sym->filter->root, osym->filter->root))
+ sym->flags |= SYM_FLAG_SAME;
+ else
+ sym->flags &= ~SYM_FLAG_SAME;
+ break;
+ }
+}
+
+void filters_dump_all(void)
+{
+ struct symbol *sym;
+ WALK_LIST(sym, config->symbols) {
+ switch (sym->class) {
+ case SYM_FILTER:
+ debug("Named filter %s:\n", sym->name);
+ f_dump_line(sym->filter->root, 1);
+ break;
+ case SYM_FUNCTION:
+ debug("Function %s:\n", sym->name);
+ f_dump_line(sym->function, 1);
+ break;
+ case SYM_PROTO:
+ {
+ debug("Protocol %s:\n", sym->name);
+ struct channel *c;
+ WALK_LIST(c, sym->proto->proto->channels) {
+ debug(" Channel %s (%s) IMPORT", c->name, net_label[c->net_type]);
+ if (c->in_filter == FILTER_ACCEPT)
+ debug(" ALL\n");
+ else if (c->in_filter == FILTER_REJECT)
+ debug(" NONE\n");
+ else if (c->in_filter == FILTER_UNDEF)
+ debug(" UNDEF\n");
+ else if (c->in_filter->sym) {
+ ASSERT(c->in_filter->sym->filter == c->in_filter);
+ debug(" named filter %s\n", c->in_filter->sym->name);
+ } else {
+ debug("\n");
+ f_dump_line(c->in_filter->root, 2);
+ }
+ }
+ }
+ }
+ }
}
diff --git a/filter/filter.h b/filter/filter.h
index a8c33287..9d997efb 100644
--- a/filter/filter.h
+++ b/filter/filter.h
@@ -2,6 +2,7 @@
* BIRD Internet Routing Daemon -- Filters
*
* (c) 1999 Pavel Machek <pavel@ucw.cz>
+ * (c) 2018--2019 Maria Matejka <mq@jmq.cz>
*
* Can be freely distributed and used under the terms of the GNU GPL.
*/
@@ -11,279 +12,63 @@
#include "lib/resource.h"
#include "lib/ip.h"
+#include "lib/macro.h"
#include "nest/route.h"
#include "nest/attrs.h"
-/* Filter instruction types */
-
-#define FI__TWOCHAR(a,b) ((a<<8) | b)
-#define FI__LIST \
- F(FI_ADD, 0, '+') \
- F(FI_SUBTRACT, 0, '-') \
- F(FI_MULTIPLY, 0, '*') \
- F(FI_DIVIDE, 0, '/') \
- F(FI_AND, 0, '&') \
- F(FI_OR, 0, '|') \
- F(FI_PAIR_CONSTRUCT, 'm', 'p') \
- F(FI_EC_CONSTRUCT, 'm', 'c') \
- F(FI_LC_CONSTRUCT, 'm', 'l') \
- F(FI_PATHMASK_CONSTRUCT, 'm', 'P') \
- F(FI_NEQ, '!', '=') \
- F(FI_EQ, '=', '=') \
- F(FI_LT, 0, '<') \
- F(FI_LTE, '<', '=') \
- F(FI_NOT, 0, '!') \
- F(FI_MATCH, 0, '~') \
- F(FI_NOT_MATCH, '!', '~') \
- F(FI_DEFINED, 'd', 'e') \
- F(FI_TYPE, 0, 'T') \
- F(FI_IS_V4, 'I', 'i') \
- F(FI_SET, 0, 's') \
- F(FI_CONSTANT, 0, 'c') \
- F(FI_VARIABLE, 0, 'V') \
- F(FI_CONSTANT_INDIRECT, 0, 'C') \
- F(FI_PRINT, 0, 'p') \
- F(FI_CONDITION, 0, '?') \
- F(FI_NOP, 0, '0') \
- F(FI_PRINT_AND_DIE, 'p', ',') \
- F(FI_RTA_GET, 0, 'a') \
- F(FI_RTA_SET, 'a', 'S') \
- F(FI_EA_GET, 'e', 'a') \
- F(FI_EA_SET, 'e', 'S') \
- F(FI_PREF_GET, 0, 'P') \
- F(FI_PREF_SET, 'P', 'S') \
- F(FI_LENGTH, 0, 'L') \
- F(FI_ROA_MAXLEN, 'R', 'M') \
- F(FI_ROA_ASN, 'R', 'A') \
- F(FI_SADR_SRC, 'n', 's') \
- F(FI_IP, 'c', 'p') \
- F(FI_ROUTE_DISTINGUISHER, 'R', 'D') \
- F(FI_AS_PATH_FIRST, 'a', 'f') \
- F(FI_AS_PATH_LAST, 'a', 'l') \
- F(FI_AS_PATH_LAST_NAG, 'a', 'L') \
- F(FI_RETURN, 0, 'r') \
- F(FI_CALL, 'c', 'a') \
- F(FI_CLEAR_LOCAL_VARS, 'c', 'V') \
- F(FI_SWITCH, 'S', 'W') \
- F(FI_IP_MASK, 'i', 'M') \
- F(FI_EMPTY, 0, 'E') \
- F(FI_PATH_PREPEND, 'A', 'p') \
- F(FI_CLIST_ADD_DEL, 'C', 'a') \
- F(FI_ROA_CHECK, 'R', 'C') \
- F(FI_FORMAT, 0, 'F') \
- F(FI_ASSERT, 'a', 's')
-
-enum f_instruction_code {
-#define F(c,a,b) \
- c,
-FI__LIST
-#undef F
- FI__MAX,
-} PACKED;
-
-const char *f_instruction_name(enum f_instruction_code fi);
-
-struct f_inst { /* Instruction */
- struct f_inst *next; /* Structure is 16 bytes, anyway */
- enum f_instruction_code fi_code;
- u16 aux; /* Extension to instruction code, T_*, EA_*, EAF_* */
- union {
- uint i;
- void *p;
- } a1; /* The first argument */
- union {
- uint i;
- void *p;
- } a2; /* The second argument */
- union {
- int i;
- void *p;
- } a3; /* The third argument */
- int lineno;
-};
-
-#define arg1 a1.p
-#define arg2 a2.p
-
-/* Not enough fields in f_inst for three args used by roa_check() */
-struct f_inst_roa_check {
- struct f_inst i;
- struct rtable_config *rtc;
-};
-
-struct f_prefix {
- net_addr net;
- u8 lo, hi;
-};
-
-struct f_val {
- int type; /* T_* */
- union {
- uint i;
- u64 ec;
- lcomm lc;
- ip_addr ip;
- const net_addr *net;
- char *s;
- struct f_tree *t;
- struct f_trie *ti;
- struct adata *ad;
- struct f_path_mask *path_mask;
- } val;
-};
-
-struct f_dynamic_attr {
- int type;
- int f_type;
- int ea_code;
-};
-
-struct f_static_attr {
- int f_type;
- int sa_code;
- int readonly;
+/* Possible return values of filter execution */
+enum filter_return {
+ F_NOP = 0,
+ F_NONL,
+ F_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) {
+ switch (fret) {
+#define FRS(x) case x: return #x
+ FRS(F_NOP);
+ FRS(F_NONL);
+ FRS(F_RETURN);
+ FRS(F_ACCEPT);
+ FRS(F_REJECT);
+ FRS(F_ERROR);
+ FRS(F_QUITBIRD);
+#undef FRS
+ default: bug("This shall not happen");
+ }
+}
+
+struct f_val;
+
+/* The filter encapsulating structure to be pointed-to from outside */
+struct f_line;
struct filter {
- char *name;
- struct f_inst *root;
+ struct symbol *sym;
+ const struct f_line *root;
};
-struct f_inst *f_new_inst(enum f_instruction_code fi_code);
-struct f_inst *f_new_inst_da(enum f_instruction_code fi_code, struct f_dynamic_attr da);
-struct f_inst *f_new_inst_sa(enum f_instruction_code fi_code, struct f_static_attr sa);
-static inline struct f_dynamic_attr f_new_dynamic_attr(int type, int f_type, int code) /* Type as core knows it, type as filters know it, and code of dynamic attribute */
-{ return (struct f_dynamic_attr) { .type = type, .f_type = f_type, .ea_code = code }; } /* f_type currently unused; will be handy for static type checking */
-static inline struct f_static_attr f_new_static_attr(int f_type, int code, int readonly)
-{ return (struct f_static_attr) { .f_type = f_type, .sa_code = code, .readonly = readonly }; }
-struct f_tree *f_new_tree(void);
-struct f_inst *f_generate_complex(int operation, int operation_aux, struct f_dynamic_attr da, struct f_inst *argument);
-struct f_inst *f_generate_roa_check(struct rtable_config *table, struct f_inst *prefix, struct f_inst *asn);
-
-
-struct f_tree *build_tree(struct f_tree *);
-struct f_tree *find_tree(struct f_tree *t, struct f_val val);
-int same_tree(struct f_tree *t1, struct f_tree *t2);
-void tree_format(struct f_tree *t, buffer *buf);
-
-struct f_trie *f_new_trie(linpool *lp, uint node_size);
-void *trie_add_prefix(struct f_trie *t, const net_addr *n, uint l, uint h);
-int trie_match_net(struct f_trie *t, const net_addr *n);
-int trie_same(struct f_trie *t1, struct f_trie *t2);
-void trie_format(struct f_trie *t, buffer *buf);
-
-struct ea_list;
struct rte;
-int f_run(struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int flags);
-struct f_val f_eval_rte(struct f_inst *expr, struct rte **rte, struct linpool *tmp_pool);
-struct f_val f_eval(struct f_inst *expr, struct linpool *tmp_pool);
-uint f_eval_int(struct f_inst *expr);
-
-char *filter_name(struct filter *filter);
-int filter_same(struct filter *new, struct filter *old);
-
-int i_same(struct f_inst *f1, struct f_inst *f2);
-
-int val_compare(struct f_val v1, struct f_val v2);
-int val_same(struct f_val v1, struct f_val v2);
+enum filter_return f_run(const struct filter *filter, struct rte **rte, struct linpool *tmp_pool, int flags);
+enum filter_return f_eval_rte(const struct f_line *expr, struct rte **rte, struct linpool *tmp_pool);
+uint f_eval_int(const struct f_line *expr);
+enum filter_return f_eval_buf(const struct f_line *expr, struct linpool *tmp_pool, buffer *buf);
-void val_format(struct f_val v, buffer *buf);
+const char *filter_name(const struct filter *filter);
+int filter_same(const struct filter *new, const struct filter *old);
+int f_same(const struct f_line *f1, const struct f_line *f2);
+void filter_commit(struct config *new, struct config *old);
-#define F_NOP 0
-#define F_NONL 1
-#define F_ACCEPT 2 /* Need to preserve ordering: accepts < rejects! */
-#define F_REJECT 3
-#define F_ERROR 4
-#define F_QUITBIRD 5
+void filters_dump_all(void);
#define FILTER_ACCEPT NULL
-#define FILTER_REJECT ((void *) 1)
-#define FILTER_UNDEF ((void *) 2) /* Used in BGP */
-
-/* Type numbers must be in 0..0xff range */
-#define T_MASK 0xff
-
-/* Internal types */
-/* Do not use type of zero, that way we'll see errors easier. */
-#define T_VOID 1
-
-/* User visible types, which fit in int */
-#define T_INT 0x10
-#define T_BOOL 0x11
-#define T_PAIR 0x12 /* Notice that pair is stored as integer: first << 16 | second */
-#define T_QUAD 0x13
-
-/* Put enumerational types in 0x30..0x3f range */
-#define T_ENUM_LO 0x30
-#define T_ENUM_HI 0x3f
-
-#define T_ENUM_RTS 0x30
-#define T_ENUM_BGP_ORIGIN 0x31
-#define T_ENUM_SCOPE 0x32
-#define T_ENUM_RTC 0x33
-#define T_ENUM_RTD 0x34
-#define T_ENUM_ROA 0x35
-#define T_ENUM_NETTYPE 0x36
-#define T_ENUM_RA_PREFERENCE 0x37
-
-/* new enums go here */
-#define T_ENUM_EMPTY 0x3f /* Special hack for atomic_aggr */
-
-#define T_ENUM T_ENUM_LO ... T_ENUM_HI
-
-/* Bigger ones */
-#define T_IP 0x20
-#define T_NET 0x21
-#define T_STRING 0x22
-#define T_PATH_MASK 0x23 /* mask for BGP path */
-#define T_PATH 0x24 /* BGP path */
-#define T_CLIST 0x25 /* Community list */
-#define T_EC 0x26 /* Extended community value, u64 */
-#define T_ECLIST 0x27 /* Extended community list */
-#define T_LC 0x28 /* Large community value, lcomm */
-#define T_LCLIST 0x29 /* Large community list */
-#define T_RD 0x2a /* Route distinguisher for VPN addresses */
-
-#define T_RETURN 0x40
-#define T_SET 0x80
-#define T_PREFIX_SET 0x81
-
-
-#define SA_FROM 1
-#define SA_GW 2
-#define SA_NET 3
-#define SA_PROTO 4
-#define SA_SOURCE 5
-#define SA_SCOPE 6
-#define SA_DEST 7
-#define SA_IFNAME 8
-#define SA_IFINDEX 9
-
-
-struct f_tree {
- struct f_tree *left, *right;
- struct f_val from, to;
- void *data;
-};
-
-struct f_trie_node
-{
- ip_addr addr, mask, accept;
- uint plen;
- struct f_trie_node *c[2];
-};
-
-struct f_trie
-{
- linpool *lp;
- int zero;
- uint node_size;
- struct f_trie_node root[0]; /* Root trie node follows */
-};
-
-#define NEW_F_VAL struct f_val * val; val = cfg_alloc(sizeof(struct f_val));
+#define FILTER_REJECT ((struct filter *) 1)
+#define FILTER_UNDEF ((struct filter *) 2) /* Used in BGP */
#define FF_SILENT 2 /* Silent filter execution */
@@ -296,15 +81,4 @@ struct custom_attribute {
struct custom_attribute *ca_lookup(pool *p, const char *name, int ea_type);
-/* Bird Tests */
-struct f_bt_test_suite {
- node n; /* Node in config->tests */
- struct f_inst *fn; /* Root of function */
- const char *fn_name; /* Name of test */
- const char *dsc; /* Description */
-};
-
-/* Hook for call bt_assert() function in configuration */
-extern void (*bt_assert_hook)(int result, struct f_inst *assert);
-
#endif
diff --git a/filter/filter_test.c b/filter/filter_test.c
index be7fd521..3abe095b 100644
--- a/filter/filter_test.c
+++ b/filter/filter_test.c
@@ -17,44 +17,53 @@
#include "test/bt-utils.h"
#include "filter/filter.h"
+#include "filter/data.h"
+#include "filter/f-inst.h"
#include "conf/conf.h"
#define BT_CONFIG_FILE "filter/test.conf"
-static struct config *
-parse_config_file(const void *filename_void)
-{
- bt_bird_init();
+struct parse_config_file_arg {
+ struct config **cp;
+ const char *filename;
+};
- size_t fn_size = strlen((const char *) filename_void) + 1;
+static int
+parse_config_file(const void *argv)
+{
+ const struct parse_config_file_arg *arg = argv;
+ size_t fn_size = strlen(arg->filename) + 1;
char *filename = alloca(fn_size);
- strncpy(filename, filename_void, fn_size);
-
- struct config *c = bt_config_file_parse(filename);
- bt_bird_cleanup();
-
- return c;
+ memcpy(filename, arg->filename, fn_size);
+
+ *(arg->cp) = bt_config_file_parse(filename);
+ return !!*(arg->cp);
}
static int
-run_function(const void *parsed_fn_def)
+run_function(const void *arg)
{
- /* XXX: const -> non-const */
- struct f_inst *f = (struct f_inst *) parsed_fn_def;
+ const struct f_bt_test_suite *t = arg;
- linpool *tmp = lp_new_default(&root_pool);
- struct f_val res = f_eval(f, tmp);
- rfree(tmp);
+ if (t->cmp)
+ return t->result == f_same(t->fn, t->cmp);
- if (res.type == T_RETURN && res.val.i >= F_REJECT)
+ 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);
- return 1;
+ return (fret < F_REJECT);
}
static void
-bt_assert_filter(int result, struct f_inst *assert)
+bt_assert_filter(int result, const struct f_line_item *assert)
{
int bt_suit_case_result = 1;
if (!result)
@@ -64,23 +73,30 @@ bt_assert_filter(int result, struct f_inst *assert)
bt_suit_case_result = 0;
}
- bt_log_suite_case_result(bt_suit_case_result, "Assertion at line %d (%s)", assert->lineno, (char *) assert->a2.p);
+ bt_log_suite_case_result(bt_suit_case_result, "Assertion at line %d (%s)",
+ assert->lineno, assert->i_FI_ASSERT.s);
}
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
+ bt_bird_init();
+
+ 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");
- struct config *c = parse_config_file(BT_CONFIG_FILE);
+ bt_bird_cleanup();
if (c)
{
- bt_assert_hook = bt_assert_filter;
-
struct f_bt_test_suite *t;
WALK_LIST(t, c->tests)
- bt_test_suite_base(run_function, t->fn_name, t->fn, BT_FORKING, BT_TIMEOUT, "%s", t->dsc);
+ bt_test_suite_base(run_function, t->fn_name, t, BT_FORKING, BT_TIMEOUT, "%s", t->dsc);
}
return bt_exit_value();
diff --git a/filter/test-reconf-begin.conf b/filter/test-reconf-begin.conf
new file mode 100644
index 00000000..aeed007f
--- /dev/null
+++ b/filter/test-reconf-begin.conf
@@ -0,0 +1,23 @@
+router id 1.1.1.1;
+protocol device {}
+
+function a() {
+ return true;
+}
+
+function b() {
+ return a();
+}
+
+function c() {
+ return b();
+}
+
+filter d {
+ if c() then accept; else reject;
+}
+
+protocol static {
+ ipv4 { import filter d; };
+ route 10.0.0.0/24 unreachable;
+}
diff --git a/filter/test-reconf-end.conf b/filter/test-reconf-end.conf
new file mode 100644
index 00000000..19164825
--- /dev/null
+++ b/filter/test-reconf-end.conf
@@ -0,0 +1,23 @@
+router id 1.1.1.1;
+protocol device {}
+
+function a() {
+ return false;
+}
+
+function b() {
+ return a();
+}
+
+function c() {
+ return b();
+}
+
+filter d {
+ if c() then accept; else reject;
+}
+
+protocol static {
+ ipv4 { import filter d; };
+ route 10.0.0.0/24 unreachable;
+}
diff --git a/filter/test.conf b/filter/test.conf
index 39b349cc..9abd76f3 100644
--- a/filter/test.conf
+++ b/filter/test.conf
@@ -7,8 +7,7 @@
router id 62.168.0.1;
/* We have to setup any protocol */
-protocol static { ipv4; }
-
+protocol device { }
@@ -25,8 +24,19 @@ function onef(int a)
return 1;
}
+function twof(int a)
+{
+ return 2;
+}
+function oneg(int a)
+{
+ return 1;
+}
+bt_test_same(onef, onef, 1);
+bt_test_same(onef, oneg, 1);
+bt_test_same(onef, twof, 0);
/*
* Testing boolean expressions
@@ -62,7 +72,6 @@ bt_test_suite(t_bool, "Testing boolean expressions");
-
/*
* Testing integers
* ----------------
@@ -126,6 +135,7 @@ int set is;
bt_assert(2 ~ [ 1, 2, 3 ]);
bt_assert(5 ~ [ 4 .. 7 ]);
bt_assert(1 !~ [ 2, 3, 4 ]);
+ bt_assert(999 !~ [ 666, 333 ]);
is = [ 2, 3, 4, 7..11 ];
bt_assert(10 ~ is);
@@ -1107,6 +1117,11 @@ int i;
return 0;
}
+function callmeagain(int a; int b; int c)
+{
+ return a + b + c;
+}
+
function fifteen()
{
return 15;
@@ -1123,6 +1138,7 @@ function t_call_function()
bt_assert(callme(3, 2) = 6);
bt_assert(callme(4, 4) = 16);
bt_assert(callme(7, 2) = 14);
+ bt_assert(callmeagain(1, 2, 3) = 6);
}
bt_test_suite(t_call_function, "Testing calling functions");
@@ -1336,7 +1352,7 @@ bt_test_suite(t_mixed_prefix, "Testing mixed net types");
filter vpn_filter
{
- bt_assert(format(net) = "0:1:2 10.1.10.0/24");
+ bt_assert(format(net) = "1:2 10.1.10.0/24");
bt_assert(net.type = NET_VPN4);
bt_assert(net.type != NET_IP4);
bt_assert(net.type != NET_IP6);
@@ -1347,6 +1363,13 @@ filter vpn_filter
NET_IP6: print "IPV6";
}
+ bt_check_assign(from, 10.20.30.40);
+ bt_check_assign(gw, 55.55.55.44);
+
+ bgp_community.add((3,5));
+ bgp_ext_community.add((ro, 135, 999));
+ bgp_large_community.add((6464156, 89646354, 8675643));
+
accept;
}
@@ -1358,3 +1381,9 @@ protocol static
vpn4 { table v4; import filter vpn_filter; };
route 0:1:2 10.1.10.0/24 unreachable;
}
+
+protocol static
+{
+ ipv6 { import where false; };
+ route fd01::/48 unreachable;
+}
diff --git a/filter/tree.c b/filter/tree.c
index f8379fa8..46d6e529 100644
--- a/filter/tree.c
+++ b/filter/tree.c
@@ -10,6 +10,7 @@
#include "nest/bird.h"
#include "conf/conf.h"
#include "filter/filter.h"
+#include "filter/data.h"
/**
* find_tree
@@ -23,15 +24,15 @@
* Both set matching and |switch() { }| construction is implemented using this function,
* thus both are as fast as they can be.
*/
-struct f_tree *
-find_tree(struct f_tree *t, struct f_val val)
+const struct f_tree *
+find_tree(const struct f_tree *t, const struct f_val *val)
{
if (!t)
return NULL;
- if ((val_compare(t->from, val) != 1) &&
- (val_compare(t->to, val) != -1))
+ if ((val_compare(&(t->from), val) != 1) &&
+ (val_compare(&(t->to), val) != -1))
return t;
- if (val_compare(t->from, val) == -1)
+ if (val_compare(&(t->from), val) == -1)
return find_tree(t->right, val);
else
return find_tree(t->left, val);
@@ -56,7 +57,7 @@ build_tree_rec(struct f_tree **buf, int l, int h)
static int
tree_compare(const void *p1, const void *p2)
{
- return val_compare((* (struct f_tree **) p1)->from, (* (struct f_tree **) p2)->from);
+ return val_compare(&((* (struct f_tree **) p1)->from), &((* (struct f_tree **) p2)->from));
}
/**
@@ -119,39 +120,39 @@ f_new_tree(void)
* Compares two trees and returns 1 if they are same
*/
int
-same_tree(struct f_tree *t1, struct f_tree *t2)
+same_tree(const struct f_tree *t1, const struct f_tree *t2)
{
if ((!!t1) != (!!t2))
return 0;
if (!t1)
return 1;
- if (val_compare(t1->from, t2->from))
+ if (val_compare(&(t1->from), &(t2->from)))
return 0;
- if (val_compare(t1->to, t2->to))
+ if (val_compare(&(t1->to), &(t2->to)))
return 0;
if (!same_tree(t1->left, t2->left))
return 0;
if (!same_tree(t1->right, t2->right))
return 0;
- if (!i_same(t1->data, t2->data))
+ if (!f_same(t1->data, t2->data))
return 0;
return 1;
}
static void
-tree_node_format(struct f_tree *t, buffer *buf)
+tree_node_format(const struct f_tree *t, buffer *buf)
{
if (t == NULL)
return;
tree_node_format(t->left, buf);
- val_format(t->from, buf);
- if (val_compare(t->from, t->to) != 0)
+ val_format(&(t->from), buf);
+ if (val_compare(&(t->from), &(t->to)) != 0)
{
buffer_puts(buf, "..");
- val_format(t->to, buf);
+ val_format(&(t->to), buf);
}
buffer_puts(buf, ", ");
@@ -159,7 +160,7 @@ tree_node_format(struct f_tree *t, buffer *buf)
}
void
-tree_format(struct f_tree *t, buffer *buf)
+tree_format(const struct f_tree *t, buffer *buf)
{
buffer_puts(buf, "[");
diff --git a/filter/tree_test.c b/filter/tree_test.c
index 5b22a9fe..6472d17e 100644
--- a/filter/tree_test.c
+++ b/filter/tree_test.c
@@ -10,6 +10,7 @@
#include "test/bt-utils.h"
#include "filter/filter.h"
+#include "filter/data.h"
#include "conf/conf.h"
#define MAX_TREE_HEIGHT 13
@@ -226,8 +227,8 @@ t_find(void)
};
for(looking_up_value.val.i = 0; looking_up_value.val.i < nodes_count; looking_up_value.val.i++)
{
- struct f_tree *found_tree = find_tree(tree, looking_up_value);
- bt_assert((val_compare(looking_up_value, found_tree->from) == 0) && (val_compare(looking_up_value, found_tree->to) == 0));
+ const struct f_tree *found_tree = find_tree(tree, &looking_up_value);
+ bt_assert((val_compare(&looking_up_value, &(found_tree->from)) == 0) && (val_compare(&looking_up_value, &(found_tree->to)) == 0));
}
}
@@ -278,11 +279,11 @@ t_find_ranges(void)
for(*i = 0; *i <= max_value; *i += (uint)bt_random()/nodes_count)
{
- struct f_tree *found_tree = find_tree(tree, needle);
+ const struct f_tree *found_tree = find_tree(tree, &needle);
bt_debug("searching: %u \n", *i);
bt_assert(
- (val_compare(needle, found_tree->from) == 0) || (val_compare(needle, found_tree->to) == 0) ||
- ((val_compare(needle, found_tree->from) == 1) && (val_compare(needle, found_tree->to) == -1))
+ (val_compare(&needle, &(found_tree->from)) == 0) || (val_compare(&needle, &(found_tree->to)) == 0) ||
+ ((val_compare(&needle, &(found_tree->from)) == 1) && (val_compare(&needle, &(found_tree->to)) == -1))
);
}
}
diff --git a/filter/trie.c b/filter/trie.c
index adcfcdf3..3038f5ec 100644
--- a/filter/trie.c
+++ b/filter/trie.c
@@ -73,6 +73,7 @@
#include "lib/string.h"
#include "conf/conf.h"
#include "filter/filter.h"
+#include "filter/data.h"
/*
@@ -220,7 +221,7 @@ trie_add_prefix(struct f_trie *t, const net_addr *net, uint l, uint h)
}
static int
-trie_match_prefix(struct f_trie *t, ip_addr px, uint plen)
+trie_match_prefix(const struct f_trie *t, ip_addr px, uint plen)
{
ip_addr pmask = ipa_mkmask(plen);
ip_addr paddr = ipa_and(px, pmask);
@@ -229,7 +230,7 @@ trie_match_prefix(struct f_trie *t, ip_addr px, uint plen)
return t->zero;
int plentest = plen - 1;
- struct f_trie_node *n = t->root;
+ const struct f_trie_node *n = t->root;
while(n)
{
@@ -264,7 +265,7 @@ trie_match_prefix(struct f_trie *t, ip_addr px, uint plen)
* is such prefix pattern in the trie.
*/
int
-trie_match_net(struct f_trie *t, const net_addr *n)
+trie_match_net(const struct f_trie *t, const net_addr *n)
{
uint add = 0;
@@ -279,7 +280,7 @@ trie_match_net(struct f_trie *t, const net_addr *n)
}
static int
-trie_node_same(struct f_trie_node *t1, struct f_trie_node *t2)
+trie_node_same(const struct f_trie_node *t1, const struct f_trie_node *t2)
{
if ((t1 == NULL) && (t2 == NULL))
return 1;
@@ -303,13 +304,13 @@ trie_node_same(struct f_trie_node *t1, struct f_trie_node *t2)
* Compares two tries and returns 1 if they are same
*/
int
-trie_same(struct f_trie *t1, struct f_trie *t2)
+trie_same(const struct f_trie *t1, const struct f_trie *t2)
{
return (t1->zero == t2->zero) && trie_node_same(t1->root, t2->root);
}
static void
-trie_node_format(struct f_trie_node *t, buffer *buf)
+trie_node_format(const struct f_trie_node *t, buffer *buf)
{
if (t == NULL)
return;
@@ -329,7 +330,7 @@ trie_node_format(struct f_trie_node *t, buffer *buf)
* Prints the trie to the supplied buffer.
*/
void
-trie_format(struct f_trie *t, buffer *buf)
+trie_format(const struct f_trie *t, buffer *buf)
{
buffer_puts(buf, "[");
diff --git a/filter/trie_test.c b/filter/trie_test.c
index 7529a5c5..38c387b0 100644
--- a/filter/trie_test.c
+++ b/filter/trie_test.c
@@ -10,6 +10,7 @@
#include "test/bt-utils.h"
#include "filter/filter.h"
+#include "filter/data.h"
#include "conf/conf.h"
#define TESTS_NUM 10
diff --git a/lib/Makefile b/lib/Makefile
index 01f3114d..18816bb3 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -1,4 +1,4 @@
-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 tbf.c timer.c xmalloc.c
+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
obj := $(src-o-files)
$(all-daemon)
diff --git a/lib/birdlib.h b/lib/birdlib.h
index 7cd78032..30ea433c 100644
--- a/lib/birdlib.h
+++ b/lib/birdlib.h
@@ -73,6 +73,10 @@ static inline int u64_cmp(u64 i1, u64 i2)
#define UNUSED __attribute__((unused))
#define PACKED __attribute__((packed))
+#ifndef HAVE_THREAD_LOCAL
+#define _Thread_local
+#endif
+
/* Microsecond time */
typedef s64 btime;
@@ -164,6 +168,15 @@ void debug(const char *msg, ...); /* Printf to debug output */
#define ASSERT(x) do { if (!(x)) log(L_BUG "Assertion '%s' failed at %s:%d", #x, __FILE__, __LINE__); } while(0)
#endif
+#ifdef DEBUGGING
+asm(
+ ".pushsection \".debug_gdb_scripts\", \"MS\",@progbits,1\n"
+ ".byte 1\n" /* Python */
+ ".asciz \"bird-gdb.py\"\n"
+ ".popsection\n"
+ );
+#endif
+
/* Pseudorandom numbers */
u32 random_u32(void);
diff --git a/lib/ip.c b/lib/ip.c
index 9497248c..2d195160 100644
--- a/lib/ip.c
+++ b/lib/ip.c
@@ -245,7 +245,7 @@ ip4_pton(const char *a, ip4_addr *o)
char *d, *c = strchr(a, '.');
if (!c != !i)
return 0;
- l = strtoul(a, &d, 10);
+ l = bstrtoul10(a, &d);
if (((d != c) && *d) || (l > 255))
return 0;
ia = (ia << 8) | l;
diff --git a/lib/ip.h b/lib/ip.h
index b78a5e22..945f2893 100644
--- a/lib/ip.h
+++ b/lib/ip.h
@@ -354,12 +354,12 @@ mpls_put(char *buf, int len, u32 *stack)
* Unaligned data access (in network order)
*/
-static inline ip4_addr get_ip4(void *buf)
+static inline ip4_addr get_ip4(const void *buf)
{
return _MI4(get_u32(buf));
}
-static inline ip6_addr get_ip6(void *buf)
+static inline ip6_addr get_ip6(const void *buf)
{
ip6_addr a;
memcpy(&a, buf, 16);
diff --git a/lib/printf.c b/lib/printf.c
index c2065d9a..8f2cccb3 100644
--- a/lib/printf.c
+++ b/lib/printf.c
@@ -356,14 +356,14 @@ int bvsnprintf(char *buf, int size, const char *fmt, va_list args)
if (qualifier == 'l') {
X = va_arg(args, u64);
bsprintf(ipbuf, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
- ((X >> 56) & 0xff),
- ((X >> 48) & 0xff),
- ((X >> 40) & 0xff),
- ((X >> 32) & 0xff),
- ((X >> 24) & 0xff),
- ((X >> 16) & 0xff),
- ((X >> 8) & 0xff),
- (X & 0xff));
+ (uint) ((X >> 56) & 0xff),
+ (uint) ((X >> 48) & 0xff),
+ (uint) ((X >> 40) & 0xff),
+ (uint) ((X >> 32) & 0xff),
+ (uint) ((X >> 24) & 0xff),
+ (uint) ((X >> 16) & 0xff),
+ (uint) ((X >> 8) & 0xff),
+ (uint) (X & 0xff));
}
else
{
diff --git a/lib/printf_test.c b/lib/printf_test.c
index a2683d93..341fde9c 100644
--- a/lib/printf_test.c
+++ b/lib/printf_test.c
@@ -56,6 +56,27 @@ t_simple(void)
BSPRINTF(2, "-1", buf, "%d", -1);
BSPRINTF(11, "-2147483648", buf, "%d", -2147483648);
+ return 1;
+}
+
+static int
+t_router_id(void)
+{
+ char buf[256];
+
+ BSPRINTF(7, "1.2.3.4", buf, "%R", (u32) 0x01020304);
+ BSPRINTF(15, "240.224.208.192", buf, "%R", (u32) 0xF0E0D0C0);
+ BSPRINTF(23, "01:02:03:04:05:06:07:08", buf, "%lR", (u64) 0x0102030405060708);
+ BSPRINTF(23, "f0:e0:d0:c0:b0:a0:90:80", buf, "%lR", (u64) 0xF0E0D0C0B0A09080);
+
+ return 1;
+}
+
+static int
+t_time(void)
+{
+ char buf[256];
+
BSPRINTF(7, "123.456", buf, "%t", (btime) 123456789);
BSPRINTF(7, "123.456", buf, "%2t", (btime) 123456789);
BSPRINTF(8, " 123.456", buf, "%8t", (btime) 123456789);
@@ -68,12 +89,28 @@ t_simple(void)
return 1;
}
+static int
+t_bstrcmp(void)
+{
+ bt_assert(bstrcmp("aa", "aa") == 0);
+ bt_assert(bstrcmp("aa", "bb") == -1);
+ bt_assert(bstrcmp("bb", "aa") == 1);
+ bt_assert(bstrcmp(NULL, NULL) == 0);
+ bt_assert(bstrcmp(NULL, "bb") == -1);
+ bt_assert(bstrcmp("bb", NULL) == 1);
+
+ return 1;
+}
+
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
bt_test_suite(t_simple, "printf without varargs");
+ bt_test_suite(t_router_id, "print router id");
+ bt_test_suite(t_time, "print time");
+ bt_test_suite(t_bstrcmp, "bstrcmp");
return bt_exit_value();
}
diff --git a/lib/resource.h b/lib/resource.h
index d9d4bb8f..ad17d9ed 100644
--- a/lib/resource.h
+++ b/lib/resource.h
@@ -101,9 +101,13 @@ void buffer_realloc(void **buf, unsigned *size, unsigned need, unsigned item_siz
*/
#define DMALLOC_DISABLE
#include <dmalloc.h>
-#define xmalloc(size) _xmalloc_leap(__FILE__, __LINE__, size)
-#define xrealloc(size) _xrealloc_leap(__FILE__, __LINE__, size)
-#define xfree(ptr) _xfree_leap(__FILE__, __LINE__, ptr)
+#define xmalloc(size) \
+ dmalloc_malloc(__FILE__, __LINE__, (size), DMALLOC_FUNC_MALLOC, 0, 1)
+#define xrealloc(ptr, size) \
+ dmalloc_realloc(__FILE__, __LINE__, (ptr), (size), DMALLOC_FUNC_REALLOC, 1)
+#define xfree(ptr) \
+ dmalloc_free(__FILE__, __LINE__, (ptr), DMALLOC_FUNC_FREE)
+
#else
/*
* Unfortunately, several libraries we might want to link to define
diff --git a/lib/string.h b/lib/string.h
index 0d34f9c5..d6ae5ef7 100644
--- a/lib/string.h
+++ b/lib/string.h
@@ -24,6 +24,9 @@ int buffer_vprint(buffer *buf, const char *fmt, va_list args);
int buffer_print(buffer *buf, const char *fmt, ...);
void buffer_puts(buffer *buf, const char *str);
+u64 bstrtoul10(const char *str, char **end);
+u64 bstrtoul16(const char *str, char **end);
+
int patmatch(const byte *pat, const byte *str);
static inline char *xbasename(const char *str)
@@ -60,6 +63,15 @@ memset32(void *D, u32 val, uint n)
dst[i] = val;
}
+static inline int
+bstrcmp(const char *s1, const char *s2)
+{
+ if (s1 && s2)
+ return strcmp(s1, s2);
+ else
+ return !s2 - !s1;
+}
+
#define ROUTER_ID_64_LENGTH 23
#endif
diff --git a/lib/strtoul.c b/lib/strtoul.c
new file mode 100644
index 00000000..44a1bb1d
--- /dev/null
+++ b/lib/strtoul.c
@@ -0,0 +1,61 @@
+/*
+ * BIRD Library -- Parse numbers
+ *
+ * (c) 2019 Maria Matejka <mq@jmq.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include "nest/bird.h"
+#include "lib/string.h"
+
+#include <errno.h>
+
+#define ULI_MAX_DIV10 (UINT64_MAX / 10)
+#define ULI_MAX_MOD10 (UINT64_MAX % 10)
+
+u64
+bstrtoul10(const char *str, char **end)
+{
+ u64 out = 0;
+ for (*end = (char *) str; (**end >= '0') && (**end <= '9'); (*end)++) {
+ u64 digit = **end - '0';
+ if ((out > ULI_MAX_DIV10) ||
+ (out == ULI_MAX_DIV10) && (digit > ULI_MAX_MOD10)) {
+ errno = ERANGE;
+ return UINT64_MAX;
+ }
+
+ out *= 10;
+ out += (**end) - '0';
+ }
+ return out;
+}
+
+u64
+bstrtoul16(const char *str, char **end)
+{
+ u64 out = 0;
+ for (int i=0; i<=(64/4); i++) {
+ switch (str[i]) {
+ case '0' ... '9':
+ out *= 16;
+ out += str[i] - '0';
+ break;
+ case 'a' ... 'f':
+ out *= 16;
+ out += str[i] + 10 - 'a';
+ break;
+ case 'A' ... 'F':
+ out *= 16;
+ out += str[i] + 10 - 'A';
+ break;
+ default:
+ *end = (char *) &(str[i]);
+ return out;
+ }
+ }
+
+ errno = ERANGE;
+ return UINT64_MAX;
+}
diff --git a/nest/a-path.c b/nest/a-path.c
index 6f1c40bf..a1b7c42f 100644
--- a/nest/a-path.c
+++ b/nest/a-path.c
@@ -13,7 +13,7 @@
#include "lib/resource.h"
#include "lib/unaligned.h"
#include "lib/string.h"
-#include "filter/filter.h"
+#include "filter/data.h"
// static inline void put_as(byte *data, u32 as) { put_u32(data, as); }
// static inline u32 get_as(byte *data) { return get_u32(data); }
@@ -77,10 +77,10 @@ bad:
}
int
-as_path_16to32(byte *dst, byte *src, uint len)
+as_path_16to32(byte *dst, const byte *src, uint len)
{
byte *dst0 = dst;
- byte *end = src + len;
+ const byte *end = src + len;
uint i, n;
while (src < end)
@@ -101,10 +101,10 @@ as_path_16to32(byte *dst, byte *src, uint len)
}
int
-as_path_32to16(byte *dst, byte *src, uint len)
+as_path_32to16(byte *dst, const byte *src, uint len)
{
byte *dst0 = dst;
- byte *end = src + len;
+ const byte *end = src + len;
uint i, n;
while (src < end)
@@ -271,13 +271,12 @@ as_path_to_old(struct linpool *pool, const struct adata *path)
/*
* Cut the path to the length @num, measured to the usual path metric. Note that
* AS_CONFED_* segments have zero length and must be added if they are on edge.
- * In contrast to other as_path_* functions, @path is modified in place.
*/
-void
-as_path_cut(struct adata *path, uint num)
+struct adata *
+as_path_cut(struct linpool *pool, const struct adata *path, uint num)
{
- byte *pos = path->data;
- byte *end = pos + path->length;
+ const byte *pos = path->data;
+ const byte *end = pos + path->length;
while (pos < end)
{
@@ -297,28 +296,39 @@ as_path_cut(struct adata *path, uint num)
/* Cannot add whole segment, so try partial one and finish */
if (num < n)
{
+ const byte *nend = pos;
+ if (num)
+ nend += 2 + BS * num;
+
+ struct adata *res = lp_alloc_adata(pool, path->length);
+ res->length = nend - (const byte *) path->data;
+ memcpy(res->data, path->data, res->length);
+
if (num)
{
- pos[1] = num;
- pos += 2 + BS * num;
+ byte *dpos = ((byte *) res->data) + (pos - (const byte *) path->data);
+ dpos[1] = num;
}
- break;
+ return res;
}
num -= n;
pos += 2 + BS * l;
}
- path->length = pos - path->data;
+ struct adata *res = lp_alloc_adata(pool, path->length);
+ res->length = path->length;
+ memcpy(res->data, path->data, res->length);
+ return res;
}
/*
* Merge (concatenate) paths @p1 and @p2 and return the result.
* In contrast to other as_path_* functions, @p1 and @p2 may be reused.
*/
-struct adata *
-as_path_merge(struct linpool *pool, struct adata *p1, struct adata *p2)
+const struct adata *
+as_path_merge(struct linpool *pool, const struct adata *p1, const struct adata *p2)
{
if (p1->length == 0)
return p2;
@@ -561,7 +571,7 @@ as_path_contains(const struct adata *path, u32 as, int min)
}
int
-as_path_match_set(const struct adata *path, struct f_tree *set)
+as_path_match_set(const struct adata *path, const struct f_tree *set)
{
const u8 *p = path->data;
const u8 *q = p+path->length;
@@ -574,7 +584,7 @@ as_path_match_set(const struct adata *path, struct f_tree *set)
for (i=0; i<n; i++)
{
struct f_val v = {T_INT, .val.i = get_as(p)};
- if (find_tree(set, v))
+ if (find_tree(set, &v))
return 1;
p += BS;
}
@@ -583,8 +593,8 @@ as_path_match_set(const struct adata *path, struct f_tree *set)
return 0;
}
-struct adata *
-as_path_filter(struct linpool *pool, struct adata *path, struct f_tree *set, u32 key, int pos)
+const struct adata *
+as_path_filter(struct linpool *pool, const struct adata *path, const struct f_tree *set, u32 key, int pos)
{
if (!path)
return NULL;
@@ -612,7 +622,10 @@ as_path_filter(struct linpool *pool, struct adata *path, struct f_tree *set, u32
int match;
if (set)
- match = !!find_tree(set, (struct f_val){T_INT, .val.i = as});
+ {
+ struct f_val v = {T_INT, .val.i = as};
+ match = !!find_tree(set, &v);
+ }
else
match = (as == key);
@@ -771,7 +784,7 @@ pm_mark(struct pm_pos *pos, int i, int plen, int *nl, int *nh)
* is marked.
*/
int
-as_path_match(const struct adata *path, struct f_path_mask *mask)
+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);
@@ -788,12 +801,12 @@ as_path_match(const struct adata *path, struct f_path_mask *mask)
l = h = 0;
pos[0].mark = 1;
- while (mask)
+ for (uint m=0; m < mask->len; m++)
{
/* We remove this mark to not step after pos[plen] */
pos[plen].mark = 0;
- switch (mask->kind)
+ switch (mask->item[m].kind)
{
case PM_ASTERISK:
for (i = l; i <= plen; i++)
@@ -802,13 +815,13 @@ as_path_match(const struct adata *path, struct f_path_mask *mask)
break;
case PM_ASN: /* Define single ASN as ASN..ASN - very narrow interval */
- val2 = val = mask->val;
+ val2 = val = mask->item[m].asn;
goto step;
case PM_ASN_EXPR:
bug("Expressions should be evaluated on AS path mask construction.");
case PM_ASN_RANGE:
- val = mask->val;
- val2 = mask->val2;
+ val = mask->item[m].from;
+ val2 = mask->item[m].to;
goto step;
case PM_QUESTION:
step:
@@ -817,7 +830,7 @@ as_path_match(const struct adata *path, struct f_path_mask *mask)
if (pos[i].mark)
{
pos[i].mark = 0;
- if ((mask->kind == PM_QUESTION) || pm_match(pos + i, val, val2))
+ if ((mask->item[m].kind == PM_QUESTION) || pm_match(pos + i, val, val2))
pm_mark(pos, i, plen, &nl, &nh);
}
@@ -828,8 +841,6 @@ as_path_match(const struct adata *path, struct f_path_mask *mask)
l = nl;
break;
}
-
- mask = mask->next;
}
return pos[plen].mark;
diff --git a/nest/a-path_test.c b/nest/a-path_test.c
index a71b48ba..9ed0a786 100644
--- a/nest/a-path_test.c
+++ b/nest/a-path_test.c
@@ -34,26 +34,24 @@ t_as_path_match(void)
first_prepended = last_prepended = 0;
struct linpool *lp = lp_new_default(&root_pool);
- struct f_path_mask mask[AS_PATH_LENGTH] = {};
- int i;
- for (i = 0; i < AS_PATH_LENGTH; i++)
+ struct f_path_mask *mask = alloca(sizeof(struct f_path_mask) + AS_PATH_LENGTH * sizeof(struct f_path_mask_item));
+ mask->len = AS_PATH_LENGTH;
+ for (int i = AS_PATH_LENGTH - 1; i >= 0; i--)
{
u32 val = bt_random();
as_path = as_path_prepend(lp, as_path, val);
bt_debug("Prepending ASN: %10u \n", val);
if (i == 0)
- first_prepended = val;
- if (i == AS_PATH_LENGTH-1)
last_prepended = val;
+ if (i == AS_PATH_LENGTH-1)
+ first_prepended = val;
- mask[i].kind = PM_ASN;
- mask[i].val = val;
- if (i)
- mask[i].next = &mask[i-1];
+ mask->item[i].kind = PM_ASN;
+ mask->item[i].asn = val;
}
- bt_assert_msg(as_path_match(as_path, &mask[AS_PATH_LENGTH-1]), "Mask should match with AS path");
+ bt_assert_msg(as_path_match(as_path, mask), "Mask should match with AS path");
u32 asn;
diff --git a/nest/a-set.c b/nest/a-set.c
index 048e522d..1186eb56 100644
--- a/nest/a-set.c
+++ b/nest/a-set.c
@@ -34,7 +34,7 @@
* the buffer to indicate truncation.
*/
int
-int_set_format(struct adata *set, int way, int from, byte *buf, uint size)
+int_set_format(const struct adata *set, int way, int from, byte *buf, uint size)
{
u32 *z = (u32 *) set->data;
byte *end = buf + size - 24;
@@ -69,18 +69,16 @@ int
ec_format(byte *buf, u64 ec)
{
u32 type, key, val;
- char tbuf[16], *kind;
+ char tbuf[16];
+ const char *kind;
type = ec >> 48;
- switch (type & 0xf0ff)
- {
- case EC_RT: kind = "rt"; break;
- case EC_RO: kind = "ro"; break;
+ kind = ec_subtype_str(type & 0xf0ff);
- default:
- kind = tbuf;
- bsprintf(kind, "unknown 0x%x", type);
- }
+ if (!kind) {
+ bsprintf(tbuf, "unknown 0x%x", type);
+ kind = tbuf;
+ }
switch (ec >> 56)
{
@@ -115,7 +113,7 @@ ec_format(byte *buf, u64 ec)
}
int
-ec_set_format(struct adata *set, int from, byte *buf, uint size)
+ec_set_format(const struct adata *set, int from, byte *buf, uint size)
{
u32 *z = int_set_get_data(set);
byte *end = buf + size - 64;
@@ -150,7 +148,7 @@ lc_format(byte *buf, lcomm lc)
}
int
-lc_set_format(struct adata *set, int from, byte *buf, uint bufsize)
+lc_set_format(const struct adata *set, int from, byte *buf, uint bufsize)
{
u32 *d = (u32 *) set->data;
byte *end = buf + bufsize - 64;
@@ -181,7 +179,7 @@ lc_set_format(struct adata *set, int from, byte *buf, uint bufsize)
}
int
-int_set_contains(struct adata *list, u32 val)
+int_set_contains(const struct adata *list, u32 val)
{
if (!list)
return 0;
@@ -198,7 +196,7 @@ int_set_contains(struct adata *list, u32 val)
}
int
-ec_set_contains(struct adata *list, u64 val)
+ec_set_contains(const struct adata *list, u64 val)
{
if (!list)
return 0;
@@ -217,7 +215,7 @@ ec_set_contains(struct adata *list, u64 val)
}
int
-lc_set_contains(struct adata *list, lcomm val)
+lc_set_contains(const struct adata *list, lcomm val)
{
if (!list)
return 0;
@@ -233,8 +231,8 @@ lc_set_contains(struct adata *list, lcomm val)
return 0;
}
-struct adata *
-int_set_prepend(struct linpool *pool, struct adata *list, u32 val)
+const struct adata *
+int_set_prepend(struct linpool *pool, const struct adata *list, u32 val)
{
struct adata *res;
int len;
@@ -254,8 +252,8 @@ int_set_prepend(struct linpool *pool, struct adata *list, u32 val)
return res;
}
-struct adata *
-int_set_add(struct linpool *pool, struct adata *list, u32 val)
+const struct adata *
+int_set_add(struct linpool *pool, const struct adata *list, u32 val)
{
struct adata *res;
int len;
@@ -275,8 +273,8 @@ int_set_add(struct linpool *pool, struct adata *list, u32 val)
return res;
}
-struct adata *
-ec_set_add(struct linpool *pool, struct adata *list, u64 val)
+const struct adata *
+ec_set_add(struct linpool *pool, const struct adata *list, u64 val)
{
if (ec_set_contains(list, val))
return list;
@@ -295,8 +293,8 @@ ec_set_add(struct linpool *pool, struct adata *list, u64 val)
return res;
}
-struct adata *
-lc_set_add(struct linpool *pool, struct adata *list, lcomm val)
+const struct adata *
+lc_set_add(struct linpool *pool, const struct adata *list, lcomm val)
{
if (lc_set_contains(list, val))
return list;
@@ -313,8 +311,8 @@ lc_set_add(struct linpool *pool, struct adata *list, lcomm val)
return res;
}
-struct adata *
-int_set_del(struct linpool *pool, struct adata *list, u32 val)
+const struct adata *
+int_set_del(struct linpool *pool, const struct adata *list, u32 val)
{
if (!int_set_contains(list, val))
return list;
@@ -335,8 +333,8 @@ int_set_del(struct linpool *pool, struct adata *list, u32 val)
return res;
}
-struct adata *
-ec_set_del(struct linpool *pool, struct adata *list, u64 val)
+const struct adata *
+ec_set_del(struct linpool *pool, const struct adata *list, u64 val)
{
if (!ec_set_contains(list, val))
return list;
@@ -362,8 +360,8 @@ ec_set_del(struct linpool *pool, struct adata *list, u64 val)
return res;
}
-struct adata *
-lc_set_del(struct linpool *pool, struct adata *list, lcomm val)
+const struct adata *
+lc_set_del(struct linpool *pool, const struct adata *list, lcomm val)
{
if (!lc_set_contains(list, val))
return list;
@@ -384,8 +382,8 @@ lc_set_del(struct linpool *pool, struct adata *list, lcomm val)
return res;
}
-struct adata *
-int_set_union(struct linpool *pool, struct adata *l1, struct adata *l2)
+const struct adata *
+int_set_union(struct linpool *pool, const struct adata *l1, const struct adata *l2)
{
if (!l1)
return l2;
@@ -414,8 +412,8 @@ int_set_union(struct linpool *pool, struct adata *l1, struct adata *l2)
return res;
}
-struct adata *
-ec_set_union(struct linpool *pool, struct adata *l1, struct adata *l2)
+const struct adata *
+ec_set_union(struct linpool *pool, const struct adata *l1, const struct adata *l2)
{
if (!l1)
return l2;
@@ -447,8 +445,8 @@ ec_set_union(struct linpool *pool, struct adata *l1, struct adata *l2)
return res;
}
-struct adata *
-lc_set_union(struct linpool *pool, struct adata *l1, struct adata *l2)
+const struct adata *
+lc_set_union(struct linpool *pool, const struct adata *l1, const struct adata *l2)
{
if (!l1)
return l2;
@@ -479,7 +477,7 @@ lc_set_union(struct linpool *pool, struct adata *l1, struct adata *l2)
struct adata *
-ec_set_del_nontrans(struct linpool *pool, struct adata *set)
+ec_set_del_nontrans(struct linpool *pool, const struct adata *set)
{
adata *res = lp_alloc_adata(pool, set->length);
u32 *src = int_set_get_data(set);
@@ -510,7 +508,7 @@ int_set_cmp(const void *X, const void *Y)
}
struct adata *
-int_set_sort(struct linpool *pool, struct adata *src)
+int_set_sort(struct linpool *pool, const struct adata *src)
{
struct adata *dst = lp_alloc_adata(pool, src->length);
memcpy(dst->data, src->data, src->length);
@@ -528,7 +526,7 @@ ec_set_cmp(const void *X, const void *Y)
}
struct adata *
-ec_set_sort(struct linpool *pool, struct adata *src)
+ec_set_sort(struct linpool *pool, const struct adata *src)
{
struct adata *dst = lp_alloc_adata(pool, src->length);
memcpy(dst->data, src->data, src->length);
@@ -558,7 +556,7 @@ lc_set_cmp(const void *X, const void *Y)
}
struct adata *
-lc_set_sort(struct linpool *pool, struct adata *src)
+lc_set_sort(struct linpool *pool, const struct adata *src)
{
struct adata *dst = lp_alloc_adata(pool, src->length);
memcpy(dst->data, src->data, src->length);
diff --git a/nest/a-set_test.c b/nest/a-set_test.c
index a5081f9f..96b6a727 100644
--- a/nest/a-set_test.c
+++ b/nest/a-set_test.c
@@ -15,10 +15,10 @@
#include "lib/resource.h"
#define SET_SIZE 10
-static struct adata *set_sequence; /* <0; SET_SIZE) */
-static struct adata *set_sequence_same; /* <0; SET_SIZE) */
-static struct adata *set_sequence_higher; /* <SET_SIZE; 2*SET_SIZE) */
-static struct adata *set_random;
+static const struct adata *set_sequence; /* <0; SET_SIZE) */
+static const struct adata *set_sequence_same; /* <0; SET_SIZE) */
+static const struct adata *set_sequence_higher; /* <SET_SIZE; 2*SET_SIZE) */
+static const struct adata *set_random;
#define BUFFER_SIZE 1000
static byte buf[BUFFER_SIZE] = {};
@@ -34,14 +34,14 @@ enum set_type
};
static void
-generate_set_sequence(enum set_type type)
+generate_set_sequence(enum set_type type, int len)
{
struct adata empty_as_path = {};
set_sequence = set_sequence_same = set_sequence_higher = set_random = &empty_as_path;
lp = lp_new_default(&root_pool);
int i;
- for (i = 0; i < SET_SIZE; i++)
+ for (i = 0; i < len; i++)
{
if (type == SET_TYPE_INT)
{
@@ -72,7 +72,7 @@ t_set_int_contains(void)
int i;
resource_init();
- generate_set_sequence(SET_TYPE_INT);
+ generate_set_sequence(SET_TYPE_INT, SET_SIZE);
bt_assert(int_set_get_size(set_sequence) == SET_SIZE);
@@ -93,9 +93,9 @@ static int
t_set_int_union(void)
{
resource_init();
- generate_set_sequence(SET_TYPE_INT);
+ generate_set_sequence(SET_TYPE_INT, SET_SIZE);
- struct adata *set_union;
+ const struct adata *set_union;
set_union = int_set_union(lp, set_sequence, set_sequence_same);
bt_assert(int_set_get_size(set_union) == SET_SIZE);
bt_assert(int_set_format(set_union, 0, 2, buf, BUFFER_SIZE) == 0);
@@ -112,9 +112,8 @@ static int
t_set_int_format(void)
{
resource_init();
- generate_set_sequence(SET_TYPE_INT);
+ generate_set_sequence(SET_TYPE_INT, SET_SIZE_FOR_FORMAT_OUTPUT);
- set_sequence->length = 4 * SET_SIZE_FOR_FORMAT_OUTPUT; /* dirty */
bt_assert(int_set_format(set_sequence, 0, 0, buf, BUFFER_SIZE) == 0);
bt_assert(strcmp(buf, "0.0.0.0 0.0.0.1 0.0.0.2 0.0.0.3 0.0.0.4 0.0.0.5 0.0.0.6 0.0.0.7 0.0.0.8 0.0.0.9") == 0);
@@ -134,9 +133,9 @@ static int
t_set_int_delete(void)
{
resource_init();
- generate_set_sequence(SET_TYPE_INT);
+ generate_set_sequence(SET_TYPE_INT, SET_SIZE);
- struct adata *deleting_sequence = set_sequence;
+ const struct adata *deleting_sequence = set_sequence;
u32 i;
for (i = 0; i < SET_SIZE; i++)
{
@@ -162,7 +161,7 @@ t_set_ec_contains(void)
u32 i;
resource_init();
- generate_set_sequence(SET_TYPE_EC);
+ generate_set_sequence(SET_TYPE_EC, SET_SIZE);
bt_assert(ec_set_get_size(set_sequence) == SET_SIZE);
@@ -183,9 +182,9 @@ static int
t_set_ec_union(void)
{
resource_init();
- generate_set_sequence(SET_TYPE_EC);
+ generate_set_sequence(SET_TYPE_EC, SET_SIZE);
- struct adata *set_union;
+ const struct adata *set_union;
set_union = ec_set_union(lp, set_sequence, set_sequence_same);
bt_assert(ec_set_get_size(set_union) == SET_SIZE);
bt_assert(ec_set_format(set_union, 0, buf, BUFFER_SIZE) == 0);
@@ -203,7 +202,7 @@ t_set_ec_format(void)
{
resource_init();
- struct adata empty_as_path = {};
+ const struct adata empty_as_path = {};
set_sequence = set_sequence_same = set_sequence_higher = set_random = &empty_as_path;
lp = lp_new_default(&root_pool);
@@ -224,9 +223,9 @@ static int
t_set_ec_delete(void)
{
resource_init();
- generate_set_sequence(SET_TYPE_EC);
+ generate_set_sequence(SET_TYPE_EC, SET_SIZE);
- struct adata *deleting_sequence = set_sequence;
+ const struct adata *deleting_sequence = set_sequence;
u32 i;
for (i = 0; i < SET_SIZE; i++)
{
diff --git a/nest/attrs.h b/nest/attrs.h
index 102f378a..4efcff79 100644
--- a/nest/attrs.h
+++ b/nest/attrs.h
@@ -31,15 +31,15 @@
struct f_tree;
int as_path_valid(byte *data, uint len, int bs, int confed, char *err, uint elen);
-int as_path_16to32(byte *dst, byte *src, uint len);
-int as_path_32to16(byte *dst, byte *src, uint len);
+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);
int as_path_contains_confed(const struct adata *path);
struct adata *as_path_strip_confed(struct linpool *pool, const struct adata *op);
struct adata *as_path_prepend2(struct linpool *pool, const struct adata *op, int seq, u32 as);
struct adata *as_path_to_old(struct linpool *pool, const struct adata *path);
-void as_path_cut(struct adata *path, uint num);
-struct adata *as_path_merge(struct linpool *pool, struct adata *p1, struct adata *p2);
+struct adata *as_path_cut(struct linpool *pool, const struct adata *path, uint num);
+const struct adata *as_path_merge(struct linpool *pool, const struct adata *p1, const struct adata *p2);
void as_path_format(const struct adata *path, byte *buf, uint size);
int as_path_getlen(const struct adata *path);
int as_path_getlen_int(const struct adata *path, int bs);
@@ -48,8 +48,8 @@ int as_path_get_first_regular(const struct adata *path, u32 *last_as);
int as_path_get_last(const struct adata *path, u32 *last_as);
u32 as_path_get_last_nonaggregated(const struct adata *path);
int as_path_contains(const struct adata *path, u32 as, int min);
-int as_path_match_set(const struct adata *path, struct f_tree *set);
-struct adata *as_path_filter(struct linpool *pool, struct adata *path, struct f_tree *set, u32 key, int pos);
+int as_path_match_set(const struct adata *path, const struct f_tree *set);
+const struct adata *as_path_filter(struct linpool *pool, const struct adata *path, const struct f_tree *set, u32 key, int pos);
static inline struct adata *as_path_prepend(struct linpool *pool, const struct adata *path, u32 as)
{ return as_path_prepend2(pool, path, AS_PATH_SEQUENCE, as); }
@@ -61,20 +61,30 @@ static inline struct adata *as_path_prepend(struct linpool *pool, const struct a
#define PM_ASN_EXPR 3
#define PM_ASN_RANGE 4
-struct f_path_mask {
- struct f_path_mask *next;
+struct f_path_mask_item {
+ union {
+ u32 asn; /* PM_ASN */
+ struct f_line *expr; /* PM_ASN_EXPR */
+ struct { /* PM_ASN_RANGE */
+ u32 from;
+ u32 to;
+ };
+ };
int kind;
- uintptr_t val;
- uintptr_t val2;
};
-int as_path_match(const struct adata *path, struct f_path_mask *mask);
+struct f_path_mask {
+ uint len;
+ struct f_path_mask_item item[0];
+};
+
+int as_path_match(const struct adata *path, const struct f_path_mask *mask);
/* Counterparts to appropriate as_path_* functions */
static inline int
-aggregator_16to32(byte *dst, byte *src)
+aggregator_16to32(byte *dst, const byte *src)
{
put_u32(dst, get_u16(src));
memcpy(dst+4, src+2, 4);
@@ -82,7 +92,7 @@ aggregator_16to32(byte *dst, byte *src)
}
static inline int
-aggregator_32to16(byte *dst, byte *src)
+aggregator_32to16(byte *dst, const byte *src)
{
put_u16(dst, get_u32(src));
memcpy(dst+2, src+4, 4);
@@ -90,13 +100,13 @@ aggregator_32to16(byte *dst, byte *src)
}
static inline int
-aggregator_contains_as4(struct adata *a)
+aggregator_contains_as4(const struct adata *a)
{
return get_u32(a->data) > 0xFFFF;
}
static inline struct adata *
-aggregator_to_old(struct linpool *pool, struct adata *a)
+aggregator_to_old(struct linpool *pool, const struct adata *a)
{
struct adata *d = lp_alloc_adata(pool, 8);
put_u32(d->data, 0xFFFF);
@@ -109,26 +119,35 @@ aggregator_to_old(struct linpool *pool, struct adata *a)
/* Extended Community subtypes (kinds) */
-#define EC_RT 0x0002
-#define EC_RO 0x0003
+enum ec_subtype {
+ EC_RT = 0x0002,
+ EC_RO = 0x0003,
+ EC_GENERIC = 0xFFFF,
+};
-#define EC_GENERIC 0xFFFF
+static inline const char *ec_subtype_str(const enum ec_subtype ecs) {
+ switch (ecs) {
+ case EC_RT: return "rt";
+ case EC_RO: return "ro";
+ default: return NULL;
+ }
+}
/* Transitive bit (for first u32 half of EC) */
#define EC_TBIT 0x40000000
#define ECOMM_LENGTH 8
-static inline int int_set_get_size(struct adata *list)
+static inline int int_set_get_size(const struct adata *list)
{ return list->length / 4; }
-static inline int ec_set_get_size(struct adata *list)
+static inline int ec_set_get_size(const struct adata *list)
{ return list->length / 8; }
-static inline int lc_set_get_size(struct adata *list)
+static inline int lc_set_get_size(const struct adata *list)
{ return list->length / 12; }
-static inline u32 *int_set_get_data(struct adata *list)
+static inline u32 *int_set_get_data(const struct adata *list)
{ return (u32 *) list->data; }
static inline u32 ec_hi(u64 ec) { return ec >> 32; }
@@ -137,16 +156,16 @@ static inline u64 ec_get(const u32 *l, int i)
{ return (((u64) l[i]) << 32) | l[i+1]; }
/* RFC 4360 3.1. Two-Octet AS Specific Extended Community */
-static inline u64 ec_as2(u64 kind, u64 key, u64 val)
-{ return ((kind | 0x0000) << 48) | (key << 32) | val; }
+static inline u64 ec_as2(enum ec_subtype kind, u64 key, u64 val)
+{ return (((u64) kind | 0x0000) << 48) | (key << 32) | val; }
/* RFC 5668 4-Octet AS Specific BGP Extended Community */
-static inline u64 ec_as4(u64 kind, u64 key, u64 val)
-{ return ((kind | 0x0200) << 48) | (key << 16) | val; }
+static inline u64 ec_as4(enum ec_subtype kind, u64 key, u64 val)
+{ return (((u64) kind | 0x0200) << 48) | (key << 16) | val; }
/* RFC 4360 3.2. IPv4 Address Specific Extended Community */
-static inline u64 ec_ip4(u64 kind, u64 key, u64 val)
-{ return ((kind | 0x0100) << 48) | (key << 16) | val; }
+static inline u64 ec_ip4(enum ec_subtype kind, u64 key, u64 val)
+{ return (((u64) kind | 0x0100) << 48) | (key << 16) | val; }
static inline u64 ec_generic(u64 key, u64 val)
{ return (key << 32) | val; }
@@ -173,29 +192,29 @@ static inline u32 *lc_copy(u32 *dst, const u32 *src)
{ memcpy(dst, src, LCOMM_LENGTH); return dst + 3; }
-int int_set_format(struct adata *set, int way, int from, byte *buf, uint size);
+int int_set_format(const struct adata *set, int way, int from, byte *buf, uint size);
int ec_format(byte *buf, u64 ec);
-int ec_set_format(struct adata *set, int from, byte *buf, uint size);
+int ec_set_format(const struct adata *set, int from, byte *buf, uint size);
int lc_format(byte *buf, lcomm lc);
-int lc_set_format(struct adata *set, int from, byte *buf, uint size);
-int int_set_contains(struct adata *list, u32 val);
-int ec_set_contains(struct adata *list, u64 val);
-int lc_set_contains(struct adata *list, lcomm val);
-struct adata *int_set_prepend(struct linpool *pool, struct adata *list, u32 val);
-struct adata *int_set_add(struct linpool *pool, struct adata *list, u32 val);
-struct adata *ec_set_add(struct linpool *pool, struct adata *list, u64 val);
-struct adata *lc_set_add(struct linpool *pool, struct adata *list, lcomm val);
-struct adata *int_set_del(struct linpool *pool, struct adata *list, u32 val);
-struct adata *ec_set_del(struct linpool *pool, struct adata *list, u64 val);
-struct adata *lc_set_del(struct linpool *pool, struct adata *list, lcomm val);
-struct adata *int_set_union(struct linpool *pool, struct adata *l1, struct adata *l2);
-struct adata *ec_set_union(struct linpool *pool, struct adata *l1, struct adata *l2);
-struct adata *lc_set_union(struct linpool *pool, struct adata *l1, struct adata *l2);
-
-struct adata *ec_set_del_nontrans(struct linpool *pool, struct adata *set);
-struct adata *int_set_sort(struct linpool *pool, struct adata *src);
-struct adata *ec_set_sort(struct linpool *pool, struct adata *src);
-struct adata *lc_set_sort(struct linpool *pool, struct adata *src);
+int lc_set_format(const struct adata *set, int from, byte *buf, uint size);
+int int_set_contains(const struct adata *list, u32 val);
+int ec_set_contains(const struct adata *list, u64 val);
+int lc_set_contains(const struct adata *list, lcomm val);
+const struct adata *int_set_prepend(struct linpool *pool, const struct adata *list, u32 val);
+const struct adata *int_set_add(struct linpool *pool, const struct adata *list, u32 val);
+const struct adata *ec_set_add(struct linpool *pool, const struct adata *list, u64 val);
+const struct adata *lc_set_add(struct linpool *pool, const struct adata *list, lcomm val);
+const struct adata *int_set_del(struct linpool *pool, const struct adata *list, u32 val);
+const struct adata *ec_set_del(struct linpool *pool, const struct adata *list, u64 val);
+const struct adata *lc_set_del(struct linpool *pool, const struct adata *list, lcomm val);
+const struct adata *int_set_union(struct linpool *pool, const struct adata *l1, const struct adata *l2);
+const struct adata *ec_set_union(struct linpool *pool, const struct adata *l1, const struct adata *l2);
+const struct adata *lc_set_union(struct linpool *pool, const struct adata *l1, const struct adata *l2);
+
+struct adata *ec_set_del_nontrans(struct linpool *pool, const struct adata *set);
+struct adata *int_set_sort(struct linpool *pool, const struct adata *src);
+struct adata *ec_set_sort(struct linpool *pool, const struct adata *src);
+struct adata *lc_set_sort(struct linpool *pool, const struct adata *src);
void ec_set_sort_x(struct adata *set); /* Sort in place */
diff --git a/nest/bfd.h b/nest/bfd.h
index 04d6c001..36add991 100644
--- a/nest/bfd.h
+++ b/nest/bfd.h
@@ -19,6 +19,7 @@ struct bfd_request {
ip_addr addr;
ip_addr local;
struct iface *iface;
+ struct iface *vrf;
void (*hook)(struct bfd_request *);
void *data;
@@ -40,13 +41,13 @@ struct bfd_request {
#ifdef CONFIG_BFD
-struct bfd_request * bfd_request_session(pool *p, ip_addr addr, ip_addr local, struct iface *iface, 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);
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, 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) { return NULL; }
static inline void cf_check_bfd(int use) { if (use) cf_error("BFD not available"); }
diff --git a/nest/cli.c b/nest/cli.c
index c421cc7e..24962f10 100644
--- a/nest/cli.c
+++ b/nest/cli.c
@@ -262,6 +262,7 @@ cli_command(struct cli *c)
bzero(&f, sizeof(f));
f.mem = c->parser_pool;
f.pool = rp_new(c->pool, "Config");
+ init_list(&f.symbols);
cf_read_hook = cli_cmd_read_hook;
cli_rh_pos = c->rx_buf;
cli_rh_len = strlen(c->rx_buf);
diff --git a/nest/cmds.c b/nest/cmds.c
index ca601ef2..da4015cf 100644
--- a/nest/cmds.c
+++ b/nest/cmds.c
@@ -95,18 +95,16 @@ cmd_show_memory(void)
}
void
-cmd_eval(struct f_inst *expr)
+cmd_eval(const struct f_line *expr)
{
- struct f_val v = f_eval(expr, this_cli->parser_pool);
+ buffer buf;
+ LOG_BUFFER_INIT(buf);
- if (v.type == T_RETURN)
+ if (f_eval_buf(expr, this_cli->parser_pool, &buf) > F_RETURN)
{
cli_msg(8008, "runtime error");
return;
}
- buffer buf;
- LOG_BUFFER_INIT(buf);
- val_format(v, &buf);
cli_msg(23, "%s", buf.start);
}
diff --git a/nest/cmds.h b/nest/cmds.h
index 4cf8fb1b..194a9d7f 100644
--- a/nest/cmds.h
+++ b/nest/cmds.h
@@ -16,4 +16,6 @@ struct f_inst;
void cmd_show_status(void);
void cmd_show_symbols(struct sym_show_data *sym);
void cmd_show_memory(void);
-void cmd_eval(struct f_inst *expr);
+
+struct f_line;
+void cmd_eval(const struct f_line *expr);
diff --git a/nest/config.Y b/nest/config.Y
index aef5ed46..e1a932d4 100644
--- a/nest/config.Y
+++ b/nest/config.Y
@@ -65,7 +65,7 @@ proto_postconfig(void)
CF_DECLS
CF_KEYWORDS(ROUTER, ID, PROTOCOL, TEMPLATE, PREFERENCE, DISABLED, DEBUG, ALL, OFF, DIRECT)
-CF_KEYWORDS(INTERFACE, IMPORT, EXPORT, FILTER, NONE, VRF, TABLE, STATES, ROUTES, FILTERS)
+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)
@@ -88,7 +88,7 @@ CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID)
%type <i32> idval
%type <f> imexport
%type <r> rtable
-%type <s> optsym
+%type <s> optproto
%type <ra> r_args
%type <sd> sym_args
%type <i> proto_start echo_mask echo_size debug_mask debug_list debug_flag mrtdump_mask mrtdump_list mrtdump_flag export_mode limit_action net_type table_sorted tos password_algorithm
@@ -112,9 +112,9 @@ rtrid:
idval:
NUM { $$ = $1; }
- | '(' term ')' { $$ = f_eval_int($2); }
+ | '(' term ')' { $$ = f_eval_int(f_linearize($2)); }
| IP4 { $$ = ip4_to_u32($1); }
- | SYM {
+ | CF_SYM_KNOWN {
if ($1->class == (SYM_CONSTANT | T_INT) || $1->class == (SYM_CONSTANT | T_QUAD))
$$ = SYM_VAL($1).i;
else if (($1->class == (SYM_CONSTANT | T_IP)) && ipa_is_ip4(SYM_VAL($1).ip))
@@ -156,7 +156,7 @@ table_sorted:
| SORTED { $$ = 1; }
;
-table: net_type TABLE SYM table_sorted {
+table: net_type TABLE symbol table_sorted {
struct rtable_config *cf;
cf = rt_new_table($3, $1);
cf->sorted = $4;
@@ -177,28 +177,30 @@ proto_name:
/* EMPTY */ {
struct symbol *s = cf_default_name(this_proto->protocol->template, &this_proto->protocol->name_counter);
s->class = this_proto->class;
- s->def = this_proto;
+ s->proto = this_proto;
this_proto->name = s->name;
}
- | SYM {
- cf_define_symbol($1, this_proto->class, this_proto);
+ | symbol {
+ cf_define_symbol($1, this_proto->class, proto, this_proto);
this_proto->name = $1->name;
}
- | FROM SYM {
+ | FROM CF_SYM_KNOWN {
+ if (($2->class != SYM_TEMPLATE) && ($2->class != SYM_PROTO)) cf_error("Template or protocol name expected");
+
struct symbol *s = cf_default_name(this_proto->protocol->template, &this_proto->protocol->name_counter);
s->class = this_proto->class;
- s->def = this_proto;
+ s->proto = this_proto;
this_proto->name = s->name;
- if (($2->class != SYM_TEMPLATE) && ($2->class != SYM_PROTO)) cf_error("Template or protocol name expected");
- proto_copy_config(this_proto, $2->def);
+ proto_copy_config(this_proto, $2->proto);
}
- | SYM FROM SYM {
- cf_define_symbol($1, this_proto->class, this_proto);
+ | symbol FROM CF_SYM_KNOWN {
+ if (($3->class != SYM_TEMPLATE) && ($3->class != SYM_PROTO)) cf_error("Template or protocol name expected");
+
+ cf_define_symbol($1, this_proto->class, proto, this_proto);
this_proto->name = $1->name;
- if (($3->class != SYM_TEMPLATE) && ($3->class != SYM_PROTO)) cf_error("Template or protocol name expected");
- proto_copy_config(this_proto, $3->def);
+ proto_copy_config(this_proto, $3->proto);
}
;
@@ -209,7 +211,8 @@ proto_item:
| MRTDUMP mrtdump_mask { this_proto->mrtdump = $2; }
| ROUTER ID idval { this_proto->router_id = $3; }
| DESCRIPTION text { this_proto->dsc = $2; }
- | VRF text { this_proto->vrf = if_get_by_name($2); }
+ | VRF text { this_proto->vrf = if_get_by_name($2); this_proto->vrf_set = 1; }
+ | VRF DEFAULT { this_proto->vrf = NULL; this_proto->vrf_set = 1; }
;
@@ -254,12 +257,7 @@ channel_end:
proto_channel: channel_start channel_opt_list channel_end;
-rtable:
- SYM {
- if ($1->class != SYM_TABLE) cf_error("Table expected");
- $$ = $1->def;
- }
- ;
+rtable: CF_SYM_KNOWN { cf_assert_symbol($1, SYM_TABLE); $$ = $1->table; } ;
imexport:
FILTER filter { $$ = $2; }
@@ -512,8 +510,8 @@ CF_CLI(SHOW PROTOCOLS, proto_patt2, [<protocol> | \"<pattern>\"], [[Show routing
CF_CLI(SHOW PROTOCOLS ALL, proto_patt2, [<protocol> | \"<pattern>\"], [[Show routing protocol details]])
{ proto_apply_cmd($4, proto_cmd_show, 0, 1); } ;
-optsym:
- SYM
+optproto:
+ CF_SYM_KNOWN { cf_assert_symbol($1, SYM_PROTO); $$ = $1; }
| /* empty */ { $$ = NULL; }
;
@@ -545,10 +543,10 @@ r_args:
$$->show_for = 1;
$$->addr = $3;
}
- | r_args TABLE SYM {
+ | r_args TABLE CF_SYM_KNOWN {
+ cf_assert_symbol($3, SYM_TABLE);
$$ = $1;
- if ($3->class != SYM_TABLE) cf_error("%s is not a table", $3->name);
- rt_show_add_table($$, ((struct rtable_config *)$3->def)->table);
+ rt_show_add_table($$, $3->table->table);
$$->tables_defined_by = RSD_TDB_DIRECT;
}
| r_args TABLE ALL {
@@ -558,10 +556,11 @@ r_args:
rt_show_add_table($$, t->table);
$$->tables_defined_by = RSD_TDB_ALL;
}
- | r_args IMPORT TABLE SYM '.' r_args_channel {
+ | r_args IMPORT TABLE CF_SYM_KNOWN '.' r_args_channel {
+ cf_assert_symbol($4, SYM_PROTO);
$$ = $1;
- struct proto_config *cf = (void *) $4->def;
- if ($4->class != SYM_PROTO || !cf->proto) cf_error("%s is not a protocol", $4->name);
+ 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);
@@ -590,30 +589,33 @@ r_args:
$$ = $1;
$$->filtered = 1;
}
- | r_args export_mode SYM {
- struct proto_config *c = (struct proto_config *) $3->def;
+ | r_args export_mode CF_SYM_KNOWN {
+ cf_assert_symbol($3, SYM_PROTO);
+ struct proto_config *c = (struct proto_config *) $3->proto;
$$ = $1;
if ($$->export_mode) cf_error("Export specified twice");
- if ($3->class != SYM_PROTO || !c->proto) cf_error("%s is not a protocol", $3->name);
+ if (!c->proto) cf_error("%s is not a protocol", $3->name);
$$->export_mode = $2;
$$->export_protocol = c->proto;
$$->tables_defined_by = RSD_TDB_INDIRECT;
}
- | r_args export_mode SYM '.' r_args_channel {
- struct proto_config *c = (struct proto_config *) $3->def;
+ | 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;
$$ = $1;
if ($$->export_mode) cf_error("Export specified twice");
- if ($3->class != SYM_PROTO || !c->proto) cf_error("%s is not a protocol", $3->name);
+ 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");
$$->tables_defined_by = RSD_TDB_INDIRECT;
}
- | r_args PROTOCOL SYM {
- struct proto_config *c = (struct proto_config *) $3->def;
+ | r_args PROTOCOL CF_SYM_KNOWN {
+ cf_assert_symbol($3, SYM_PROTO);
+ struct proto_config *c = (struct proto_config *) $3->proto;
$$ = $1;
if ($$->show_protocol) cf_error("Protocol specified twice");
- if ($3->class != SYM_PROTO || !c->proto) cf_error("%s is not a protocol", $3->name);
+ if (!c->proto) cf_error("%s is not a protocol", $3->name);
$$->show_protocol = c->proto;
$$->tables_defined_by = RSD_TDB_INDIRECT;
}
@@ -647,7 +649,7 @@ r_args_for:
$$ = cfg_alloc(sizeof(net_addr_ip6_sadr));
net_fill_ip6_sadr($$, $1, IP6_MAX_PREFIX_LENGTH, $3, IP6_MAX_PREFIX_LENGTH);
}
- | SYM {
+ | CF_SYM_KNOWN {
if ($1->class == (SYM_CONSTANT | T_IP))
{
$$ = cfg_alloc(ipa_is_ip4(SYM_VAL($1).ip) ? sizeof(net_addr_ip4) : sizeof(net_addr_ip6));
@@ -656,7 +658,7 @@ r_args_for:
else if (($1->class == (SYM_CONSTANT | T_NET)) && net_type_match(SYM_VAL($1).net, NB_IP | NB_VPN))
$$ = (net_addr *) SYM_VAL($1).net; /* Avoid const warning */
else
- cf_error("IP address or network expected");
+ cf_error("IP address or network constant expected");
}
;
@@ -709,7 +711,7 @@ sym_args:
| sym_args FILTER { $$ = $1; $$->type = SYM_FILTER; }
| sym_args PROTOCOL { $$ = $1; $$->type = SYM_PROTO; }
| sym_args TEMPLATE { $$ = $1; $$->type = SYM_TEMPLATE; }
- | sym_args SYM { $$ = $1; $$->sym = $2; }
+ | sym_args symbol { $$ = $1; $$->sym = $2; }
;
@@ -730,9 +732,11 @@ CF_CLI(DUMP ROUTES,,, [[Dump routing table]])
{ rt_dump_all(); cli_msg(0, ""); } ;
CF_CLI(DUMP PROTOCOLS,,, [[Dump protocol information]])
{ protos_dump_all(); cli_msg(0, ""); } ;
+CF_CLI(DUMP FILTER ALL,,, [[Dump all filters in linearized form]])
+{ filters_dump_all(); cli_msg(0, ""); } ;
CF_CLI(EVAL, term, <expr>, [[Evaluate an expression]])
-{ cmd_eval($2); } ;
+{ cmd_eval(f_linearize($2)); } ;
CF_CLI_HELP(ECHO, ..., [[Control echoing of log messages]])
CF_CLI(ECHO, echo_mask echo_size, (all | off | { debug|trace|info|remote|warning|error|auth [, ...] }) [<buffer-size>], [[Control echoing of log messages]]) {
@@ -779,13 +783,13 @@ CF_CLI(RESTRICT,,,[[Restrict current CLI session to safe commands]])
{ this_cli->restricted = 1; cli_msg(16, "Access restricted"); } ;
proto_patt:
- SYM { $$.ptr = $1; $$.patt = 0; }
+ CF_SYM_KNOWN { cf_assert_symbol($1, SYM_PROTO); $$.ptr = $1; $$.patt = 0; }
| ALL { $$.ptr = NULL; $$.patt = 1; }
| TEXT { $$.ptr = $1; $$.patt = 1; }
;
proto_patt2:
- SYM { $$.ptr = $1; $$.patt = 0; }
+ CF_SYM_KNOWN { cf_assert_symbol($1, SYM_PROTO); $$.ptr = $1; $$.patt = 0; }
| { $$.ptr = NULL; $$.patt = 1; }
| TEXT { $$.ptr = $1; $$.patt = 1; }
;
diff --git a/nest/iface.c b/nest/iface.c
index c1966ac6..00dfc2ca 100644
--- a/nest/iface.c
+++ b/nest/iface.c
@@ -147,7 +147,7 @@ ifa_send_notify(struct proto *p, unsigned c, struct ifa *a)
{
if (p->ifa_notify &&
(p->proto_state != PS_DOWN) &&
- (!p->vrf || p->vrf == a->iface->master))
+ (!p->vrf_set || p->vrf == a->iface->master))
{
if (p->debug & D_IFACES)
log(L_TRACE "%s < address %N on interface %s %s",
@@ -185,7 +185,7 @@ if_send_notify(struct proto *p, unsigned c, struct iface *i)
{
if (p->if_notify &&
(p->proto_state != PS_DOWN) &&
- (!p->vrf || p->vrf == i->master))
+ (!p->vrf_set || p->vrf == i->master))
{
if (p->debug & D_IFACES)
log(L_TRACE "%s < interface %s %s", p->name, i->name,
diff --git a/nest/neighbor.c b/nest/neighbor.c
index 87253857..00a8e8a5 100644
--- a/nest/neighbor.c
+++ b/nest/neighbor.c
@@ -116,7 +116,7 @@ if_connected(ip_addr a, struct iface *i, struct ifa **ap, uint flags)
}
static inline int
-if_connected_any(ip_addr a, struct iface *vrf, struct iface **iface, struct ifa **addr, uint flags)
+if_connected_any(ip_addr a, struct iface *vrf, uint vrf_set, struct iface **iface, struct ifa **addr, uint flags)
{
struct iface *i;
struct ifa *b;
@@ -127,7 +127,7 @@ if_connected_any(ip_addr a, struct iface *vrf, struct iface **iface, struct ifa
/* Get first match, but prefer SCOPE_HOST to other matches */
WALK_LIST(i, iface_list)
- if ((!vrf || vrf == i->master) && ((s = if_connected(a, i, &b, flags)) >= 0))
+ if ((!vrf_set || vrf == i->master) && ((s = if_connected(a, i, &b, flags)) >= 0))
if ((scope < 0) || ((scope > SCOPE_HOST) && (s == SCOPE_HOST)))
{
*iface = i;
@@ -192,7 +192,7 @@ neigh_find(struct proto *p, ip_addr a, struct iface *iface, uint flags)
iface = (scope < 0) ? NULL : iface;
}
else
- scope = if_connected_any(a, p->vrf, &iface, &addr, flags);
+ scope = if_connected_any(a, p->vrf, p->vrf_set, &iface, &addr, flags);
/* scope < 0 means i don't know neighbor */
/* scope >= 0 <=> iface != NULL */
@@ -309,6 +309,7 @@ neigh_free(neighbor *n)
void
neigh_update(neighbor *n, struct iface *iface)
{
+ struct proto *p = n->proto;
struct ifa *ifa = NULL;
int scope = -1;
@@ -317,14 +318,14 @@ neigh_update(neighbor *n, struct iface *iface)
return;
/* VRF-bound neighbors ignore changes in other VRFs */
- if (n->proto->vrf && (n->proto->vrf != iface->master))
+ if (p->vrf_set && (p->vrf != iface->master))
return;
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, n->proto->vrf, &iface, &ifa, n->flags);
+ scope = if_connected_any(n->addr, p->vrf, p->vrf_set, &iface, &ifa, n->flags);
/* No change or minor change - ignore or notify */
if ((scope == n->scope) && (iface == n->iface))
diff --git a/nest/proto.c b/nest/proto.c
index d4a333d0..2fb4e96f 100644
--- a/nest/proto.c
+++ b/nest/proto.c
@@ -765,6 +765,7 @@ proto_init(struct proto_config *c, node *n)
p->proto_state = PS_DOWN;
p->last_state_change = current_time();
p->vrf = c->vrf;
+ p->vrf_set = c->vrf_set;
insert_node(&p->n, n);
p->event = ev_new_init(proto_pool, proto_event, p);
@@ -874,6 +875,28 @@ proto_copy_config(struct proto_config *dest, struct proto_config *src)
dest->protocol->copy_config(dest, src);
}
+void
+proto_clone_config(struct symbol *sym, struct proto_config *parent)
+{
+ struct proto_config *cf = proto_config_new(parent->protocol, SYM_PROTO);
+ proto_copy_config(cf, parent);
+ cf->name = sym->name;
+ cf->proto = NULL;
+ cf->parent = parent;
+
+ sym->class = cf->class;
+ sym->proto = cf;
+}
+
+static void
+proto_undef_clone(struct symbol *sym, struct proto_config *cf)
+{
+ rem_node(&cf->n);
+
+ sym->class = SYM_VOID;
+ sym->proto = NULL;
+}
+
/**
* protos_preconfig - pre-configuration processing
* @c: new configuration
@@ -910,7 +933,8 @@ proto_reconfigure(struct proto *p, struct proto_config *oc, struct proto_config
if ((nc->protocol != oc->protocol) ||
(nc->net_type != oc->net_type) ||
(nc->disabled != p->disabled) ||
- (nc->vrf != oc->vrf))
+ (nc->vrf != oc->vrf) ||
+ (nc->vrf_set != oc->vrf_set))
return 0;
p->name = nc->name;
@@ -973,17 +997,41 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
{
p = oc->proto;
sym = cf_find_symbol(new, oc->name);
+
+ /* Handle dynamic protocols */
+ if (!sym && oc->parent && !new->shutdown)
+ {
+ struct symbol *parsym = cf_find_symbol(new, oc->parent->name);
+ if (parsym && parsym->class == SYM_PROTO)
+ {
+ /* This is hack, we would like to share config, but we need to copy it now */
+ new_config = new;
+ cfg_mem = new->mem;
+ conf_this_scope = new->root_scope;
+ sym = cf_get_symbol(oc->name);
+ proto_clone_config(sym, parsym->proto);
+ new_config = NULL;
+ cfg_mem = NULL;
+ }
+ }
+
if (sym && sym->class == SYM_PROTO && !new->shutdown)
{
/* Found match, let's check if we can smoothly switch to new configuration */
/* No need to check description */
- nc = sym->def;
+ nc = sym->proto;
nc->proto = p;
/* We will try to reconfigure protocol p */
if (! force_reconfig && proto_reconfigure(p, oc, nc, type))
continue;
+ if (nc->parent)
+ {
+ proto_undef_clone(sym, nc);
+ goto remove;
+ }
+
/* Unsuccessful, we will restart it */
if (!p->disabled && !nc->disabled)
log(L_INFO "Restarting protocol %s", p->name);
@@ -997,10 +1045,16 @@ protos_commit(struct config *new, struct config *old, int force_reconfig, int ty
}
else if (!new->shutdown)
{
+ remove:
log(L_INFO "Removing protocol %s", p->name);
p->down_code = PDC_CF_REMOVE;
p->cf_new = NULL;
}
+ else if (new->gr_down)
+ {
+ p->down_code = PDC_CMD_GR_DOWN;
+ p->cf_new = NULL;
+ }
else /* global shutdown */
{
p->down_code = PDC_CMD_SHUTDOWN;
@@ -1105,6 +1159,15 @@ proto_rethink_goal(struct proto *p)
}
}
+struct proto *
+proto_spawn(struct proto_config *cf, uint disabled)
+{
+ struct proto *p = proto_init(cf, TAIL(proto_list));
+ p->disabled = disabled;
+ proto_rethink_goal(p);
+ return p;
+}
+
/**
* DOC: Graceful restart recovery
@@ -1777,8 +1840,8 @@ proto_cmd_show(struct proto *p, uintptr_t verbose, int cnt)
cli_msg(-1006, " Message: %s", p->message);
if (p->cf->router_id)
cli_msg(-1006, " Router ID: %R", p->cf->router_id);
- if (p->vrf)
- cli_msg(-1006, " VRF: %s", p->vrf->name);
+ if (p->vrf_set)
+ cli_msg(-1006, " VRF: %s", p->vrf ? p->vrf->name : "default");
if (p->proto->show_proto_info)
p->proto->show_proto_info(p);
@@ -1905,7 +1968,7 @@ proto_apply_cmd_symbol(struct symbol *s, void (* cmd)(struct proto *, uintptr_t,
return;
}
- cmd(((struct proto_config *)s->def)->proto, arg, 0);
+ cmd(s->proto->proto, arg, 0);
cli_msg(0, "");
}
@@ -1948,7 +2011,7 @@ proto_get_named(struct symbol *sym, struct protocol *pr)
if (sym->class != SYM_PROTO)
cf_error("%s: Not a protocol", sym->name);
- p = ((struct proto_config *) sym->def)->proto;
+ p = sym->proto->proto;
if (!p || p->proto != pr)
cf_error("%s: Not a %s protocol", sym->name, pr->name);
}
diff --git a/nest/protocol.h b/nest/protocol.h
index 6c04071b..da12c771 100644
--- a/nest/protocol.h
+++ b/nest/protocol.h
@@ -89,6 +89,7 @@ void protos_build(void);
void proto_build(struct protocol *);
void protos_preconfig(struct config *);
void protos_commit(struct config *new, struct config *old, int force_restart, int type);
+struct proto * proto_spawn(struct proto_config *cf, uint disabled);
void protos_dump_all(void);
#define GA_UNKNOWN 0 /* Attribute not recognized */
@@ -113,11 +114,13 @@ struct proto_config {
struct config *global; /* Global configuration data */
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;
int class; /* SYM_PROTO or SYM_TEMPLATE */
u8 net_type; /* Protocol network type (NET_*), 0 for undefined */
u8 disabled; /* Protocol enabled/disabled by default */
+ u8 vrf_set; /* Related VRF instance (below) is defined */
u32 debug, mrtdump; /* Debugging bitfields, both use D_* constants */
u32 router_id; /* Protocol specific router ID */
@@ -174,6 +177,7 @@ struct proto {
uint active_channels; /* Number of active channels */
byte net_type; /* Protocol network type (NET_*), 0 for undefined */
byte disabled; /* Manually disabled */
+ byte vrf_set; /* Related VRF instance (above) is defined */
byte proto_state; /* Protocol state machine (PS_*, see below) */
byte active; /* From PS_START to cleanup after PS_STOP */
byte do_start; /* Start actions are scheduled */
@@ -194,8 +198,8 @@ struct proto {
* ifa_notify Notify protocol about interface address changes.
* rt_notify Notify protocol about routing table updates.
* neigh_notify Notify protocol about neighbor cache events.
- * make_tmp_attrs Construct ea_list from private attrs stored in rta.
- * store_tmp_attrs Store private attrs back to rta. The route MUST NOT be cached.
+ * make_tmp_attrs Add attributes to rta from from private attrs stored in rte. The route and rta MUST NOT be cached.
+ * store_tmp_attrs Store private attrs back to rte and undef added attributes. The route and rta MUST NOT be cached.
* preexport Called as the first step of the route exporting process.
* It can construct a new rte, add private attributes and
* decide whether the route shall be exported: 1=yes, -1=no,
@@ -211,8 +215,8 @@ struct proto {
void (*ifa_notify)(struct proto *, unsigned flags, struct ifa *a);
void (*rt_notify)(struct proto *, struct channel *, struct network *net, struct rte *new, struct rte *old);
void (*neigh_notify)(struct neighbor *neigh);
- struct ea_list *(*make_tmp_attrs)(struct rte *rt, struct linpool *pool);
- void (*store_tmp_attrs)(struct rte *rt);
+ void (*make_tmp_attrs)(struct rte *rt, struct linpool *pool);
+ void (*store_tmp_attrs)(struct rte *rt, struct linpool *pool);
int (*preexport)(struct proto *, struct rte **rt, struct linpool *pool);
void (*reload_routes)(struct channel *);
void (*feed_begin)(struct channel *, int initial);
@@ -255,6 +259,7 @@ struct proto_spec {
#define PDC_CMD_DISABLE 0x11 /* Result of disable command */
#define PDC_CMD_RESTART 0x12 /* Result of restart command */
#define PDC_CMD_SHUTDOWN 0x13 /* Result of global shutdown */
+#define PDC_CMD_GR_DOWN 0x14 /* Result of global graceful restart */
#define PDC_RX_LIMIT_HIT 0x21 /* Route receive limit reached */
#define PDC_IN_LIMIT_HIT 0x22 /* Route import limit reached */
#define PDC_OUT_LIMIT_HIT 0x23 /* Route export limit reached */
@@ -263,6 +268,7 @@ struct proto_spec {
void *proto_new(struct proto_config *);
void *proto_config_new(struct protocol *, int class);
void proto_copy_config(struct proto_config *dest, struct proto_config *src);
+void proto_clone_config(struct symbol *sym, struct proto_config *parent);
void proto_set_message(struct proto *p, char *msg, int len);
void graceful_restart_recovery(void);
@@ -297,18 +303,6 @@ proto_get_router_id(struct proto_config *pc)
return pc->router_id ? pc->router_id : pc->global->router_id;
}
-static inline void
-rte_make_tmp_attrs(struct rte **rt, struct linpool *pool)
-{
- struct ea_list *(*mta)(struct rte *rt, struct linpool *pool);
- mta = (*rt)->attrs->src->proto->make_tmp_attrs;
- if (!mta) return;
- *rt = rte_cow_rta(*rt, pool);
- struct ea_list *ea = mta(*rt, pool);
- ea->next = (*rt)->attrs->eattrs;
- (*rt)->attrs->eattrs = ea;
-}
-
extern pool *proto_pool;
extern list proto_list;
@@ -489,7 +483,7 @@ struct channel_config {
struct proto_config *parent; /* Where channel is defined (proto or template) */
struct rtable_config *table; /* Table we're attached to */
- struct filter *in_filter, *out_filter; /* Attached filters */
+ const struct filter *in_filter, *out_filter; /* Attached filters */
struct channel_limit rx_limit; /* Limit for receiving routes from protocol
(relevant when in_keep_filtered is active) */
struct channel_limit in_limit; /* Limit for importing routes from protocol */
@@ -511,8 +505,8 @@ struct channel {
struct proto *proto;
struct rtable *table;
- struct filter *in_filter; /* Input filter */
- struct filter *out_filter; /* Output filter */
+ const struct filter *in_filter; /* Input filter */
+ const struct filter *out_filter; /* Output 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 */
diff --git a/nest/route.h b/nest/route.h
index 8dfbb376..8d799454 100644
--- a/nest/route.h
+++ b/nest/route.h
@@ -297,7 +297,7 @@ rte *rte_find(net *net, struct rte_src *src);
rte *rte_get_temp(struct rta *);
void rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src);
/* rte_update() moved to protocol.h to avoid dependency conflicts */
-int rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter);
+int rt_examine(rtable *t, net_addr *a, struct proto *p, const struct filter *filter);
rte *rt_export_merged(struct channel *c, net *net, rte **rt_free, linpool *pool, int silent);
void rt_refresh_begin(rtable *t, struct channel *c);
void rt_refresh_end(rtable *t, struct channel *c);
@@ -308,6 +308,10 @@ void rte_free(rte *);
rte *rte_do_cow(rte *);
static inline rte * rte_cow(rte *r) { return (r->flags & REF_COW) ? rte_do_cow(r) : r; }
rte *rte_cow_rta(rte *r, linpool *lp);
+void rte_init_tmp_attrs(struct rte *r, linpool *lp, uint max);
+void rte_make_tmp_attr(struct rte *r, uint id, uint type, uintptr_t val);
+void rte_make_tmp_attrs(struct rte **r, struct linpool *pool, struct rta **old_attrs);
+uintptr_t rte_store_tmp_attr(struct rte *r, uint id);
void rt_dump(rtable *);
void rt_dump_all(void);
int rt_feed_channel(struct channel *c);
@@ -335,7 +339,7 @@ struct rt_show_data {
struct rt_show_data_rtable *last_table; /* Last table in output */
struct fib_iterator fit; /* Iterator over networks in table */
int verbose, tables_defined_by;
- struct filter *filter;
+ const struct filter *filter;
struct proto *show_protocol;
struct proto *export_protocol;
struct channel *export_channel;
@@ -473,7 +477,7 @@ typedef struct eattr {
byte type; /* Attribute type and several flags (EAF_...) */
union {
u32 data;
- struct adata *ptr; /* Attribute data elsewhere */
+ const struct adata *ptr; /* Attribute data elsewhere */
} u;
} eattr;
@@ -481,6 +485,7 @@ typedef struct eattr {
#define EA_CODE(proto,id) (((proto) << 8) | (id))
#define EA_ID(ea) ((ea) & 0xff)
#define EA_PROTO(ea) ((ea) >> 8)
+#define EA_ID_FLAG(ea) (1 << EA_ID(ea))
#define EA_CUSTOM(id) ((id) | EA_CUSTOM_BIT)
#define EA_IS_CUSTOM(ea) ((ea) & EA_CUSTOM_BIT)
#define EA_CUSTOM_ID(ea) ((ea) & ~EA_CUSTOM_BIT)
@@ -493,6 +498,7 @@ const char *ea_custom_name(uint ea);
#define EA_CUSTOM_BIT 0x8000
#define EA_ALLOW_UNDEF 0x10000 /* ea_find: allow EAF_TYPE_UNDEF */
#define EA_BIT(n) ((n) << 24) /* Used in bitfield accessors */
+#define EA_BIT_GET(ea) ((ea) >> 24)
#define EAF_TYPE_MASK 0x1f /* Mask with this to get type */
#define EAF_TYPE_INT 0x01 /* 32-bit unsigned integer number */
@@ -509,13 +515,14 @@ const char *ea_custom_name(uint ea);
#define EAF_VAR_LENGTH 0x02 /* Attribute length is variable (part of type spec) */
#define EAF_ORIGINATED 0x20 /* The attribute has originated locally */
#define EAF_FRESH 0x40 /* An uncached attribute (e.g. modified in export filter) */
-#define EAF_TEMP 0x80 /* A temporary attribute (the one stored in the tmp attr list) */
typedef struct adata {
uint length; /* Length of data */
byte data[0];
} adata;
+extern const adata null_adata; /* adata of length 0 */
+
static inline struct adata *
lp_alloc_adata(struct linpool *pool, uint len)
{
@@ -524,7 +531,7 @@ lp_alloc_adata(struct linpool *pool, uint len)
return ad;
}
-static inline int adata_same(struct adata *a, struct adata *b)
+static inline int adata_same(const struct adata *a, const struct adata *b)
{ return (a->length == b->length && !memcmp(a->data, b->data, a->length)); }
@@ -539,6 +546,7 @@ typedef struct ea_list {
#define EALF_SORTED 1 /* Attributes are sorted by code */
#define EALF_BISECT 2 /* Use interval bisection for searching */
#define EALF_CACHED 4 /* Attributes belonging to cached rta */
+#define EALF_TEMP 8 /* Temporary ea_list added by make_tmp_attrs hooks */
struct rte_src *rt_find_source(struct proto *p, u32 id);
struct rte_src *rt_get_source(struct proto *p, u32 id);
@@ -571,6 +579,8 @@ void ea_format_bitfield(struct eattr *a, byte *buf, int bufsize, const char **na
ea = t; \
} \
ea_sort(ea); \
+ if (ea->count == 0) \
+ ea = NULL; \
} while(0) \
static inline eattr *
@@ -621,6 +631,7 @@ int nexthop__same(struct nexthop *x, struct nexthop *y); /* Compare multipath ne
static inline int nexthop_same(struct nexthop *x, struct nexthop *y)
{ return (x == y) || nexthop__same(x, y); }
struct nexthop *nexthop_merge(struct nexthop *x, struct nexthop *y, int rx, int ry, int max, linpool *lp);
+struct nexthop *nexthop_sort(struct nexthop *x);
static inline void nexthop_link(struct rta *a, struct nexthop *from)
{ memcpy(&a->nh, from, nexthop_size(from)); }
void nexthop_insert(struct nexthop **n, struct nexthop *y);
diff --git a/nest/rt-attr.c b/nest/rt-attr.c
index 3e4578b0..8620d321 100644
--- a/nest/rt-attr.c
+++ b/nest/rt-attr.c
@@ -58,6 +58,8 @@
#include <stddef.h>
+const adata null_adata; /* adata of length 0 */
+
const char * const rta_src_names[RTS_MAX] = {
[RTS_DUMMY] = "",
[RTS_STATIC] = "static",
@@ -200,7 +202,7 @@ nexthop__same(struct nexthop *x, struct nexthop *y)
}
static int
-nexthop_compare_node(struct nexthop *x, struct nexthop *y)
+nexthop_compare_node(const struct nexthop *x, const struct nexthop *y)
{
int r;
@@ -318,6 +320,24 @@ nexthop_insert(struct nexthop **n, struct nexthop *x)
*n = x;
}
+struct nexthop *
+nexthop_sort(struct nexthop *x)
+{
+ struct nexthop *s = NULL;
+
+ /* Simple insert-sort */
+ while (x)
+ {
+ struct nexthop *n = x;
+ x = n->next;
+ n->next = NULL;
+
+ nexthop_insert(&s, n);
+ }
+
+ return s;
+}
+
int
nexthop_is_sorted(struct nexthop *x)
{
@@ -571,8 +591,8 @@ ea_do_sort(ea_list *e)
}
/**
- * In place discard duplicates, undefs and temporary attributes in sorted
- * ea_list. We use stable sort for this reason.
+ * In place discard duplicates and undefs in sorted ea_list. We use stable sort
+ * for this reason.
**/
static inline void
ea_do_prune(ea_list *e)
@@ -596,10 +616,6 @@ ea_do_prune(ea_list *e)
if ((s0->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF)
continue;
- /* Drop temporary attributes */
- if (s0->type & EAF_TEMP)
- continue;
-
/* Copy the newest version to destination */
*d = *s0;
@@ -763,7 +779,7 @@ ea_free(ea_list *o)
{
eattr *a = &o->attrs[i];
if (!(a->type & EAF_EMBEDDED))
- mb_free(a->u.ptr);
+ mb_free((void *) a->u.ptr);
}
mb_free(o);
}
@@ -808,7 +824,7 @@ ea_format_bitfield(struct eattr *a, byte *buf, int bufsize, const char **names,
}
static inline void
-opaque_format(struct adata *ad, byte *buf, uint size)
+opaque_format(const struct adata *ad, byte *buf, uint size)
{
byte *bound = buf + size - 10;
uint i;
@@ -831,7 +847,7 @@ opaque_format(struct adata *ad, byte *buf, uint size)
}
static inline void
-ea_show_int_set(struct cli *c, struct adata *ad, int way, byte *pos, byte *buf, byte *end)
+ea_show_int_set(struct cli *c, const struct adata *ad, int way, byte *pos, byte *buf, byte *end)
{
int i = int_set_format(ad, way, 0, pos, end - pos);
cli_printf(c, -1012, "\t%s", buf);
@@ -843,7 +859,7 @@ ea_show_int_set(struct cli *c, struct adata *ad, int way, byte *pos, byte *buf,
}
static inline void
-ea_show_ec_set(struct cli *c, struct adata *ad, byte *pos, byte *buf, byte *end)
+ea_show_ec_set(struct cli *c, const struct adata *ad, byte *pos, byte *buf, byte *end)
{
int i = ec_set_format(ad, 0, pos, end - pos);
cli_printf(c, -1012, "\t%s", buf);
@@ -855,7 +871,7 @@ ea_show_ec_set(struct cli *c, struct adata *ad, byte *pos, byte *buf, byte *end)
}
static inline void
-ea_show_lc_set(struct cli *c, struct adata *ad, byte *pos, byte *buf, byte *end)
+ea_show_lc_set(struct cli *c, const struct adata *ad, byte *pos, byte *buf, byte *end)
{
int i = lc_set_format(ad, 0, pos, end - pos);
cli_printf(c, -1012, "\t%s", buf);
@@ -882,7 +898,7 @@ ea_show(struct cli *c, eattr *e)
{
struct protocol *p;
int status = GA_UNKNOWN;
- struct adata *ad = (e->type & EAF_EMBEDDED) ? NULL : e->u.ptr;
+ const struct adata *ad = (e->type & EAF_EMBEDDED) ? NULL : e->u.ptr;
byte buf[CLI_MSG_SIZE];
byte *pos = buf, *end = buf + sizeof(buf);
@@ -979,8 +995,6 @@ ea_dump(ea_list *e)
{
eattr *a = &e->attrs[i];
debug(" %02x:%02x.%02x", EA_PROTO(a->id), EA_ID(a->id), a->flags);
- if (a->type & EAF_TEMP)
- debug("T");
debug("=%c", "?iO?I?P???S?????" [a->type & EAF_TYPE_MASK]);
if (a->type & EAF_ORIGINATED)
debug("o");
@@ -1023,7 +1037,7 @@ ea_hash(ea_list *e)
h ^= a->u.data;
else
{
- struct adata *d = a->u.ptr;
+ const struct adata *d = a->u.ptr;
h ^= mem_hash(d->data, d->length);
}
h *= mul;
diff --git a/nest/rt-show.c b/nest/rt-show.c
index c7bcdf2f..002a6039 100644
--- a/nest/rt-show.c
+++ b/nest/rt-show.c
@@ -45,10 +45,11 @@ rt_show_rte(struct cli *c, byte *ia, rte *e, struct rt_show_data *d)
else
from[0] = 0;
- get_route_info = a->src->proto->proto->get_route_info;
/* Need to normalize the extended attributes */
- if ((get_route_info || d->verbose) && !rta_is_cached(a))
+ if (d->verbose && !rta_is_cached(a) && a->eattrs)
ea_normalize(a->eattrs);
+
+ get_route_info = a->src->proto->proto->get_route_info;
if (get_route_info)
get_route_info(e, info);
else
@@ -114,7 +115,7 @@ rt_show_net(struct cli *c, net *n, struct rt_show_data *d)
continue;
ee = e;
- rte_make_tmp_attrs(&e, c->show_pool);
+ rte_make_tmp_attrs(&e, c->show_pool, NULL);
/* Export channel is down, do not try to export routes to it */
if (ec && (ec->export_state == ES_DOWN))
diff --git a/nest/rt-table.c b/nest/rt-table.c
index 6bf07a09..53f5d979 100644
--- a/nest/rt-table.c
+++ b/nest/rt-table.c
@@ -39,6 +39,7 @@
#include "lib/string.h"
#include "conf/conf.h"
#include "filter/filter.h"
+#include "filter/data.h"
#include "lib/hash.h"
#include "lib/string.h"
#include "lib/alloca.h"
@@ -325,6 +326,176 @@ rte_cow_rta(rte *r, linpool *lp)
return r;
}
+
+/**
+ * rte_init_tmp_attrs - initialize temporary ea_list for route
+ * @r: route entry to be modified
+ * @lp: linpool from which to allocate attributes
+ * @max: maximum number of added temporary attribus
+ *
+ * This function is supposed to be called from make_tmp_attrs() and
+ * store_tmp_attrs() hooks before rte_make_tmp_attr() / rte_store_tmp_attr()
+ * functions. It allocates &ea_list with length for @max items for temporary
+ * attributes and puts it on top of eattrs stack.
+ */
+void
+rte_init_tmp_attrs(rte *r, linpool *lp, uint max)
+{
+ struct ea_list *e = lp_alloc(lp, sizeof(struct ea_list) + max * sizeof(eattr));
+
+ e->next = r->attrs->eattrs;
+ e->flags = EALF_SORTED | EALF_TEMP;
+ e->count = 0;
+
+ r->attrs->eattrs = e;
+}
+
+/**
+ * rte_make_tmp_attr - make temporary eattr from private route fields
+ * @r: route entry to be modified
+ * @id: attribute ID
+ * @type: attribute type
+ * @val: attribute value (u32 or adata ptr)
+ *
+ * This function is supposed to be called from make_tmp_attrs() hook for
+ * each temporary attribute, after temporary &ea_list was initialized by
+ * rte_init_tmp_attrs(). It checks whether temporary attribute is supposed to
+ * be defined (based on route pflags) and if so then it fills &eattr field in
+ * preallocated temporary &ea_list on top of route @r eattrs stack.
+ *
+ * Note that it may require free &eattr in temporary &ea_list, so it must not be
+ * called more times than @max argument of rte_init_tmp_attrs().
+ */
+void
+rte_make_tmp_attr(rte *r, uint id, uint type, uintptr_t val)
+{
+ if (r->pflags & EA_ID_FLAG(id))
+ {
+ ea_list *e = r->attrs->eattrs;
+ eattr *a = &e->attrs[e->count++];
+ a->id = id;
+ a->type = type;
+ a->flags = 0;
+
+ if (type & EAF_EMBEDDED)
+ a->u.data = (u32) val;
+ else
+ a->u.ptr = (struct adata *) val;
+ }
+}
+
+/**
+ * rte_store_tmp_attr - store temporary eattr to private route fields
+ * @r: route entry to be modified
+ * @id: attribute ID
+ *
+ * This function is supposed to be called from store_tmp_attrs() hook for
+ * each temporary attribute, after temporary &ea_list was initialized by
+ * rte_init_tmp_attrs(). It checks whether temporary attribute is defined in
+ * route @r eattrs stack, updates route pflags accordingly, undefines it by
+ * filling &eattr field in preallocated temporary &ea_list on top of the eattrs
+ * stack, and returns the value. Caller is supposed to store it in the
+ * appropriate private field.
+ *
+ * Note that it may require free &eattr in temporary &ea_list, so it must not be
+ * called more times than @max argument of rte_init_tmp_attrs()
+ */
+uintptr_t
+rte_store_tmp_attr(rte *r, uint id)
+{
+ ea_list *e = r->attrs->eattrs;
+ eattr *a = ea_find(e->next, id);
+
+ if (a)
+ {
+ e->attrs[e->count++] = (struct eattr) { .id = id, .type = EAF_TYPE_UNDEF };
+ r->pflags |= EA_ID_FLAG(id);
+ return (a->type & EAF_EMBEDDED) ? a->u.data : (uintptr_t) a->u.ptr;
+ }
+ else
+ {
+ r->pflags &= ~EA_ID_FLAG(id);
+ return 0;
+ }
+}
+
+/**
+ * rte_make_tmp_attrs - prepare route by adding all relevant temporary route attributes
+ * @r: route entry to be modified (may be replaced if COW)
+ * @lp: linpool from which to allocate attributes
+ * @old_attrs: temporary ref to old &rta (may be NULL)
+ *
+ * This function expands privately stored protocol-dependent route attributes
+ * to a uniform &eattr / &ea_list representation. It is essentially a wrapper
+ * around protocol make_tmp_attrs() hook, which does some additional work like
+ * ensuring that route @r is writable.
+ *
+ * The route @r may be read-only (with %REF_COW flag), in that case rw copy is
+ * obtained by rte_cow() and @r is replaced. If @rte is originally rw, it may be
+ * directly modified (and it is never copied).
+ *
+ * If the @old_attrs ptr is supplied, the function obtains another reference of
+ * old cached &rta, that is necessary in some cases (see rte_cow_rta() for
+ * details). It is freed by rte_store_tmp_attrs(), or manually by rta_free().
+ *
+ * Generally, if caller ensures that @r is read-only (e.g. in route export) then
+ * it may ignore @old_attrs (and set it to NULL), but must handle replacement of
+ * @r. If caller ensures that @r is writable (e.g. in route import) then it may
+ * ignore replacement of @r, but it must handle @old_attrs.
+ */
+void
+rte_make_tmp_attrs(rte **r, linpool *lp, rta **old_attrs)
+{
+ void (*make_tmp_attrs)(rte *r, linpool *lp);
+ make_tmp_attrs = (*r)->attrs->src->proto->make_tmp_attrs;
+
+ if (!make_tmp_attrs)
+ return;
+
+ /* We may need to keep ref to old attributes, will be freed in rte_store_tmp_attrs() */
+ if (old_attrs)
+ *old_attrs = rta_is_cached((*r)->attrs) ? rta_clone((*r)->attrs) : NULL;
+
+ *r = rte_cow_rta(*r, lp);
+ make_tmp_attrs(*r, lp);
+}
+
+/**
+ * rte_store_tmp_attrs - store temporary route attributes back to private route fields
+ * @r: route entry to be modified
+ * @lp: linpool from which to allocate attributes
+ * @old_attrs: temporary ref to old &rta
+ *
+ * This function stores temporary route attributes that were expanded by
+ * rte_make_tmp_attrs() back to private route fields and also undefines them.
+ * It is essentially a wrapper around protocol store_tmp_attrs() hook, which
+ * does some additional work like shortcut if there is no change and cleanup
+ * of @old_attrs reference obtained by rte_make_tmp_attrs().
+ */
+static void
+rte_store_tmp_attrs(rte *r, linpool *lp, rta *old_attrs)
+{
+ void (*store_tmp_attrs)(rte *rt, linpool *lp);
+ store_tmp_attrs = r->attrs->src->proto->store_tmp_attrs;
+
+ if (!store_tmp_attrs)
+ return;
+
+ ASSERT(!rta_is_cached(r->attrs));
+
+ /* If there is no new ea_list, we just skip the temporary ea_list */
+ ea_list *ea = r->attrs->eattrs;
+ if (ea && (ea->flags & EALF_TEMP))
+ r->attrs->eattrs = ea->next;
+ else
+ store_tmp_attrs(r, lp);
+
+ /* Free ref we got in rte_make_tmp_attrs(), have to do rta_lookup() first */
+ r->attrs = rta_lookup(r->attrs);
+ rta_free(old_attrs);
+}
+
+
static int /* Actually better or at least as good as */
rte_better(rte *new, rte *old)
{
@@ -397,7 +568,7 @@ static rte *
export_filter_(struct channel *c, rte *rt0, rte **rt_free, linpool *pool, int silent)
{
struct proto *p = c->proto;
- struct filter *filter = c->out_filter;
+ const struct filter *filter = c->out_filter;
struct proto_stats *stats = &c->stats;
rte *rt;
int v;
@@ -423,7 +594,7 @@ export_filter_(struct channel *c, rte *rt0, rte **rt_free, linpool *pool, int si
goto accept;
}
- rte_make_tmp_attrs(&rt, pool);
+ rte_make_tmp_attrs(&rt, pool, NULL);
v = filter && ((filter == FILTER_REJECT) ||
(f_run(filter, &rt, pool,
@@ -1364,7 +1535,7 @@ rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
{
struct proto *p = c->proto;
struct proto_stats *stats = &c->stats;
- struct filter *filter = c->in_filter;
+ const struct filter *filter = c->in_filter;
rte *dummy = NULL;
net *nn;
@@ -1403,26 +1574,27 @@ rte_update2(struct channel *c, const net_addr *n, rte *new, struct rte_src *src)
/* new is a private copy, i could modify it */
new->flags |= REF_FILTERED;
}
- else
+ else if (filter)
{
- rte_make_tmp_attrs(&new, rte_update_pool);
- if (filter && (filter != FILTER_REJECT))
- {
- ea_list *oldea = new->attrs->eattrs;
- int fr = f_run(filter, &new, rte_update_pool, 0);
- if (fr > F_ACCEPT)
- {
- stats->imp_updates_filtered++;
- rte_trace_in(D_FILTERS, p, new, "filtered out");
+ rta *old_attrs = NULL;
+ rte_make_tmp_attrs(&new, rte_update_pool, &old_attrs);
- if (! c->in_keep_filtered)
- goto drop;
+ int fr = f_run(filter, &new, rte_update_pool, 0);
+ if (fr > F_ACCEPT)
+ {
+ stats->imp_updates_filtered++;
+ rte_trace_in(D_FILTERS, p, new, "filtered out");
- new->flags |= REF_FILTERED;
- }
- if (new->attrs->eattrs != oldea && src->proto->store_tmp_attrs)
- src->proto->store_tmp_attrs(new);
+ if (! c->in_keep_filtered)
+ {
+ rta_free(old_attrs);
+ goto drop;
}
+
+ new->flags |= REF_FILTERED;
+ }
+
+ rte_store_tmp_attrs(new, rte_update_pool, old_attrs);
}
if (!rta_is_cached(new->attrs)) /* Need to copy attributes */
new->attrs = rta_lookup(new->attrs);
@@ -1505,7 +1677,7 @@ rte_modify(rte *old)
/* Check rtable for best route to given net whether it would be exported do p */
int
-rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter)
+rt_examine(rtable *t, net_addr *a, struct proto *p, const struct filter *filter)
{
net *n = net_find(t, a);
rte *rt = n ? n->routes : NULL;
@@ -1519,7 +1691,7 @@ rt_examine(rtable *t, net_addr *a, struct proto *p, struct filter *filter)
int v = p->preexport ? p->preexport(p, &rt, rte_update_pool) : 0;
if (v == RIC_PROCESS)
{
- rte_make_tmp_attrs(&rt, rte_update_pool);
+ rte_make_tmp_attrs(&rt, rte_update_pool, NULL);
v = (f_run(filter, &rt, rte_update_pool, FF_SILENT) <= F_ACCEPT);
}
@@ -2108,13 +2280,13 @@ rt_new_table(struct symbol *s, uint addr_type)
{
/* Hack that allows to 'redefine' the master table */
if ((s->class == SYM_TABLE) &&
- (s->def == new_config->def_tables[addr_type]) &&
+ (s->table == new_config->def_tables[addr_type]) &&
((addr_type == NET_IP4) || (addr_type == NET_IP6)))
- return s->def;
+ return s->table;
struct rtable_config *c = cfg_allocz(sizeof(struct rtable_config));
- cf_define_symbol(s, SYM_TABLE, c);
+ cf_define_symbol(s, SYM_TABLE, table, c);
c->name = s->name;
c->addr_type = addr_type;
c->gc_max_ops = 1000;
@@ -2173,7 +2345,7 @@ static struct rtable_config *
rt_find_table_config(struct config *cf, char *name)
{
struct symbol *sym = cf_find_symbol(cf, name);
- return (sym && (sym->class == SYM_TABLE)) ? sym->def : NULL;
+ return (sym && (sym->class == SYM_TABLE)) ? sym->table : NULL;
}
/**
diff --git a/proto/babel/babel.c b/proto/babel/babel.c
index d321f1d8..177ff3a3 100644
--- a/proto/babel/babel.c
+++ b/proto/babel/babel.c
@@ -645,7 +645,7 @@ babel_announce_rte(struct babel_proto *p, struct babel_entry *e)
rte->u.babel.seqno = r->seqno;
rte->u.babel.metric = r->metric;
rte->u.babel.router_id = r->router_id;
- rte->pflags = 0;
+ rte->pflags = EA_ID_FLAG(EA_BABEL_METRIC) | EA_ID_FLAG(EA_BABEL_ROUTER_ID);
e->unreachable = 0;
rte_update2(c, e->n.addr, rte, p->p.main_source);
@@ -2077,32 +2077,6 @@ babel_kick_timer(struct babel_proto *p)
}
-static struct ea_list *
-babel_prepare_attrs(struct linpool *pool, ea_list *next, uint metric, u64 router_id)
-{
- struct ea_list *l = lp_alloc(pool, sizeof(struct ea_list) + 2*sizeof(eattr));
- struct adata *rid = lp_alloc(pool, sizeof(struct adata) + sizeof(u64));
- rid->length = sizeof(u64);
- memcpy(&rid->data, &router_id, sizeof(u64));
-
- l->next = next;
- l->flags = EALF_SORTED;
- l->count = 2;
-
- l->attrs[0].id = EA_BABEL_METRIC;
- l->attrs[0].flags = 0;
- l->attrs[0].type = EAF_TYPE_INT | EAF_TEMP;
- l->attrs[0].u.data = metric;
-
- l->attrs[1].id = EA_BABEL_ROUTER_ID;
- l->attrs[1].flags = 0;
- l->attrs[1].type = EAF_TYPE_OPAQUE | EAF_TEMP;
- l->attrs[1].u.ptr = rid;
-
- return l;
-}
-
-
static int
babel_preexport(struct proto *P, struct rte **new, struct linpool *pool UNUSED)
{
@@ -2115,16 +2089,25 @@ babel_preexport(struct proto *P, struct rte **new, struct linpool *pool UNUSED)
return 0;
}
-static struct ea_list *
+static void
babel_make_tmp_attrs(struct rte *rt, struct linpool *pool)
{
- return babel_prepare_attrs(pool, NULL, rt->u.babel.metric, rt->u.babel.router_id);
+ struct adata *id = lp_alloc_adata(pool, sizeof(u64));
+ memcpy(id->data, &rt->u.babel.router_id, sizeof(u64));
+
+ rte_init_tmp_attrs(rt, pool, 2);
+ rte_make_tmp_attr(rt, EA_BABEL_METRIC, EAF_TYPE_INT, rt->u.babel.metric);
+ rte_make_tmp_attr(rt, EA_BABEL_ROUTER_ID, EAF_TYPE_OPAQUE, (uintptr_t) id);
}
static void
-babel_store_tmp_attrs(struct rte *rt)
+babel_store_tmp_attrs(struct rte *rt, struct linpool *pool)
{
- rt->u.babel.metric = ea_get_int(rt->attrs->eattrs, EA_BABEL_METRIC, 0);
+ rte_init_tmp_attrs(rt, pool, 2);
+ rt->u.babel.metric = rte_store_tmp_attr(rt, EA_BABEL_METRIC);
+
+ /* EA_BABEL_ROUTER_ID is read-only, we do not really save the value */
+ rte_store_tmp_attr(rt, EA_BABEL_ROUTER_ID);
}
/*
diff --git a/proto/babel/config.Y b/proto/babel/config.Y
index 3af79fd6..b6bc70fa 100644
--- a/proto/babel/config.Y
+++ b/proto/babel/config.Y
@@ -129,16 +129,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, optsym opttext, [<name>] [\"<interface>\"], [[Show information about Babel interfaces]])
+CF_CLI(SHOW BABEL INTERFACES, optproto opttext, [<name>] [\"<interface>\"], [[Show information about Babel interfaces]])
{ babel_show_interfaces(proto_get_named($4, &proto_babel), $5); };
-CF_CLI(SHOW BABEL NEIGHBORS, optsym opttext, [<name>] [\"<interface>\"], [[Show information about Babel neighbors]])
+CF_CLI(SHOW BABEL NEIGHBORS, optproto opttext, [<name>] [\"<interface>\"], [[Show information about Babel neighbors]])
{ babel_show_neighbors(proto_get_named($4, &proto_babel), $5); };
-CF_CLI(SHOW BABEL ENTRIES, optsym opttext, [<name>], [[Show information about Babel prefix entries]])
+CF_CLI(SHOW BABEL ENTRIES, optproto opttext, [<name>], [[Show information about Babel prefix entries]])
{ babel_show_entries(proto_get_named($4, &proto_babel)); };
-CF_CLI(SHOW BABEL ROUTES, optsym opttext, [<name>], [[Show information about Babel route entries]])
+CF_CLI(SHOW BABEL ROUTES, optproto opttext, [<name>], [[Show information about Babel route entries]])
{ babel_show_routes(proto_get_named($4, &proto_babel)); };
CF_CODE
diff --git a/proto/bfd/bfd.c b/proto/bfd/bfd.c
index fdcd7225..f774e67b 100644
--- a/proto/bfd/bfd.c
+++ b/proto/bfd/bfd.c
@@ -624,6 +624,9 @@ bfd_request_notify(struct bfd_request *req, u8 state, u8 diag)
static int
bfd_add_request(struct bfd_proto *p, struct bfd_request *req)
{
+ if (p->p.vrf_set && (p->p.vrf != req->vrf))
+ return 0;
+
struct bfd_session *s = bfd_find_session_by_addr(p, req->addr);
u8 state, diag;
@@ -685,7 +688,8 @@ bfd_drop_requests(struct bfd_proto *p)
static struct resclass bfd_request_class;
struct bfd_request *
-bfd_request_session(pool *p, ip_addr addr, ip_addr local, struct iface *iface,
+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 *req = ralloc(p, &bfd_request_class);
@@ -696,6 +700,7 @@ bfd_request_session(pool *p, ip_addr addr, ip_addr local, struct iface *iface,
req->addr = addr;
req->local = local;
req->iface = iface;
+ req->vrf = vrf;
bfd_submit_request(req);
@@ -754,7 +759,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, NULL, NULL);
+ n->req = bfd_request_session(p->p.pool, n->addr, local, nb->iface, p->p.vrf, NULL, NULL);
}
if ((nb->scope <= 0) && n->req)
@@ -771,7 +776,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, NULL, NULL);
+ n->req = bfd_request_session(p->p.pool, n->addr, n->local, NULL, p->p.vrf, NULL, NULL);
return;
}
@@ -1051,15 +1056,6 @@ bfd_reconfigure(struct proto *P, struct proto_config *c)
return 1;
}
-/* Ensure one instance */
-struct bfd_config *bfd_cf;
-
-static void
-bfd_preconfig(struct protocol *P UNUSED, struct config *c UNUSED)
-{
- bfd_cf = NULL;
-}
-
static void
bfd_copy_config(struct proto_config *dest, struct proto_config *src UNUSED)
{
@@ -1088,7 +1084,7 @@ bfd_show_sessions(struct proto *P)
}
cli_msg(-1020, "%s:", p->p.name);
- cli_msg(-1020, "%-25s %-10s %-10s %-10s %8s %8s",
+ cli_msg(-1020, "%-25s %-10s %-10s %-12s %8s %8s",
"IP address", "Interface", "State", "Since", "Interval", "Timeout");
@@ -1104,7 +1100,7 @@ bfd_show_sessions(struct proto *P)
state = (state < 4) ? state : 0;
tm_format_time(tbuf, &config->tf_proto, s->last_state_change);
- cli_msg(-1020, "%-25I %-10s %-10s %-10s %7t %7t",
+ cli_msg(-1020, "%-25I %-10s %-10s %-12s %7t %7t",
s->addr, ifname, bfd_state_names[state], tbuf, tx_int, timeout);
}
HASH_WALK_END;
@@ -1123,6 +1119,5 @@ struct protocol proto_bfd = {
.start = bfd_start,
.shutdown = bfd_shutdown,
.reconfigure = bfd_reconfigure,
- .preconfig = bfd_preconfig,
.copy_config = bfd_copy_config,
};
diff --git a/proto/bfd/config.Y b/proto/bfd/config.Y
index 3f5714fd..ed416f25 100644
--- a/proto/bfd/config.Y
+++ b/proto/bfd/config.Y
@@ -38,10 +38,6 @@ 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);
-
- if (bfd_cf)
- cf_error("Only one BFD instance allowed");
- bfd_cf = BFD_CFG;
};
bfd_proto_item:
@@ -134,7 +130,7 @@ bfd_multihop:
bfd_neigh_iface:
/* empty */ { $$ = NULL; }
- | '%' SYM { $$ = if_get_by_name($2->name); }
+ | '%' symbol { $$ = if_get_by_name($2->name); }
| DEV text { $$ = if_get_by_name($2); }
;
@@ -167,7 +163,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, optsym, [<name>], [[Show information about BFD sessions]])
+CF_CLI(SHOW BFD SESSIONS, optproto, [<name>], [[Show information about BFD sessions]])
{ bfd_show_sessions(proto_get_named($4, &proto_bfd)); };
CF_CODE
diff --git a/proto/bfd/packets.c b/proto/bfd/packets.c
index 6d5151ea..703c6e28 100644
--- a/proto/bfd/packets.c
+++ b/proto/bfd/packets.c
@@ -413,6 +413,7 @@ bfd_open_rx_sk(struct bfd_proto *p, int multihop, int af)
sk->type = SK_UDP;
sk->subtype = af;
sk->sport = !multihop ? BFD_CONTROL_PORT : BFD_MULTI_CTL_PORT;
+ sk->vrf = p->p.vrf;
sk->data = p;
sk->rbsize = BFD_MAX_LEN;
@@ -444,6 +445,7 @@ bfd_open_tx_sk(struct bfd_proto *p, ip_addr local, struct iface *ifa)
sk->saddr = local;
sk->dport = ifa ? BFD_CONTROL_PORT : BFD_MULTI_CTL_PORT;
sk->iface = ifa;
+ sk->vrf = p->p.vrf;
sk->data = p;
sk->tbsize = BFD_MAX_LEN;
diff --git a/proto/bgp/attrs.c b/proto/bgp/attrs.c
index cbb22038..69c4b172 100644
--- a/proto/bgp/attrs.c
+++ b/proto/bgp/attrs.c
@@ -181,7 +181,7 @@ bgp_encode_u32s(struct bgp_write_state *s UNUSED, eattr *a, byte *buf, uint size
}
static int
-bgp_put_attr(byte *buf, uint size, uint code, uint flags, byte *data, uint len)
+bgp_put_attr(byte *buf, uint size, uint code, uint flags, const byte *data, uint len)
{
if (size < (4+len))
return -1;
@@ -234,15 +234,15 @@ bgp_format_origin(eattr *a, byte *buf, uint size UNUSED)
static int
bgp_encode_as_path(struct bgp_write_state *s, eattr *a, byte *buf, uint size)
{
- byte *data = a->u.ptr->data;
+ const byte *data = a->u.ptr->data;
uint len = a->u.ptr->length;
if (!s->as4_session)
{
/* Prepare 16-bit AS_PATH (from 32-bit one) in a temporary buffer */
- byte *src = data;
- data = alloca(len);
- len = as_path_32to16(data, src, len);
+ byte *dst = alloca(len);
+ len = as_path_32to16(dst, data, len);
+ data = dst;
}
return bgp_put_attr(buf, size, BA_AS_PATH, a->flags, data, len);
@@ -381,15 +381,14 @@ bgp_decode_atomic_aggr(struct bgp_parse_state *s, uint code UNUSED, uint flags,
static int
bgp_encode_aggregator(struct bgp_write_state *s, eattr *a, byte *buf, uint size)
{
- byte *data = a->u.ptr->data;
+ const byte *data = a->u.ptr->data;
uint len = a->u.ptr->length;
if (!s->as4_session)
{
/* Prepare 16-bit AGGREGATOR (from 32-bit one) in a temporary buffer */
- byte *src = data;
- data = alloca(6);
- len = aggregator_32to16(data, src);
+ byte *dst = alloca(6);
+ len = aggregator_32to16(dst, data);
}
return bgp_put_attr(buf, size, BA_AGGREGATOR, a->flags, data, len);
@@ -415,7 +414,7 @@ bgp_decode_aggregator(struct bgp_parse_state *s, uint code UNUSED, uint flags, b
static void
bgp_format_aggregator(eattr *a, byte *buf, uint size UNUSED)
{
- byte *data = a->u.ptr->data;
+ const byte *data = a->u.ptr->data;
bsprintf(buf, "%I4 AS%u", get_ip4(data+4), get_u32(data+0));
}
@@ -545,12 +544,13 @@ bgp_decode_mp_unreach_nlri(struct bgp_parse_state *s, uint code UNUSED, uint fla
static void
bgp_export_ext_community(struct bgp_export_state *s, eattr *a)
{
- a->u.ptr = ec_set_del_nontrans(s->pool, a->u.ptr);
+ struct adata *ad = ec_set_del_nontrans(s->pool, a->u.ptr);
- if (a->u.ptr->length == 0)
+ if (ad->length == 0)
UNSET(a);
- ec_set_sort_x(a->u.ptr);
+ ec_set_sort_x(ad);
+ a->u.ptr = ad;
}
static void
@@ -1232,7 +1232,7 @@ bgp_get_bucket(struct bgp_channel *c, ea_list *new)
if (!(a->type & EAF_EMBEDDED))
{
- struct adata *oa = a->u.ptr;
+ const struct adata *oa = a->u.ptr;
struct adata *na = (struct adata *) dest;
memcpy(na, oa, sizeof(struct adata) + oa->length);
a->u.ptr = na;
@@ -1302,7 +1302,7 @@ bgp_withdraw_bucket(struct bgp_channel *c, struct bgp_bucket *b)
#define PXH_FN(n,i,h) h
#define PXH_REHASH bgp_pxh_rehash
-#define PXH_PARAMS /8, *2, 2, 2, 8, 20
+#define PXH_PARAMS /8, *2, 2, 2, 8, 24
HASH_DEFINE_REHASH_FN(PXH, struct bgp_prefix)
@@ -1404,7 +1404,7 @@ bgp_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
if (p->cf->interpret_communities &&
(c = ea_find(e->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY))))
{
- struct adata *d = c->u.ptr;
+ const struct adata *d = c->u.ptr;
/* Do not export anywhere */
if (int_set_contains(d, BGP_COMM_NO_ADVERTISE))
@@ -1426,9 +1426,6 @@ bgp_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
return 0;
}
-
-static adata null_adata; /* adata of length 0 */
-
static ea_list *
bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *attrs0, struct linpool *pool)
{
@@ -1437,7 +1434,7 @@ bgp_update_attrs(struct bgp_proto *p, struct bgp_channel *c, rte *e, ea_list *at
struct bgp_export_state s = { .proto = p, .channel = c, .pool = pool, .src = src, .route = e, .mpls = c->desc->mpls };
ea_list *attrs = attrs0;
eattr *a;
- adata *ad;
+ const adata *ad;
/* ORIGIN attribute - mandatory, attach if missing */
if (! bgp_find_attr(attrs0, BA_ORIGIN))
@@ -1733,7 +1730,7 @@ bgp_rte_better(rte *new, rte *old)
return 0;
/* RFC 4271 9.1.2.2. g) Compare peer IP adresses */
- return (ipa_compare(new_bgp->cf->remote_ip, old_bgp->cf->remote_ip) < 0);
+ return ipa_compare(new_bgp->remote_ip, old_bgp->remote_ip) < 0;
}
@@ -1962,7 +1959,7 @@ struct rte *
bgp_rte_modify_stale(struct rte *r, struct linpool *pool)
{
eattr *a = ea_find(r->attrs->eattrs, EA_CODE(PROTOCOL_BGP, BA_COMMUNITY));
- struct adata *ad = a ? a->u.ptr : NULL;
+ const struct adata *ad = a ? a->u.ptr : NULL;
uint flags = a ? a->flags : BAF_PARTIAL;
if (ad && int_set_contains(ad, BGP_COMM_NO_LLGR))
@@ -2021,8 +2018,8 @@ bgp_process_as4_attrs(ea_list **attrs, struct linpool *pool)
return;
/* Merge AS_PATH and AS4_PATH */
- as_path_cut(p2->u.ptr, p2_len - p4_len);
- p2->u.ptr = as_path_merge(pool, p2->u.ptr, p4->u.ptr);
+ struct adata *apc = as_path_cut(pool, p2->u.ptr, p2_len - p4_len);
+ p2->u.ptr = as_path_merge(pool, apc, p4->u.ptr);
}
}
diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c
index 8dedde9f..5a403b40 100644
--- a/proto/bgp/bgp.c
+++ b/proto/bgp/bgp.c
@@ -129,6 +129,9 @@ static list bgp_sockets; /* Global list of listening sockets */
static void bgp_connect(struct bgp_proto *p);
static void bgp_active(struct bgp_proto *p);
+static void bgp_setup_conn(struct bgp_proto *p, struct bgp_conn *conn);
+static void bgp_setup_sk(struct bgp_conn *conn, sock *s);
+static void bgp_send_open(struct bgp_conn *conn);
static void bgp_update_bfd(struct bgp_proto *p, int use_bfd);
static int bgp_incoming_connection(sock *sk, uint dummy UNUSED);
@@ -149,7 +152,7 @@ bgp_open(struct bgp_proto *p)
struct bgp_socket *bs = NULL;
struct iface *ifa = p->cf->strict_bind ? p->cf->iface : NULL;
ip_addr addr = p->cf->strict_bind ? p->cf->local_ip :
- (ipa_is_ip4(p->cf->remote_ip) ? IPA_NONE4 : IPA_NONE6);
+ (p->ipv4 ? IPA_NONE4 : IPA_NONE6);
uint port = p->cf->local_port;
/* FIXME: Add some global init? */
@@ -272,8 +275,17 @@ bgp_startup(struct bgp_proto *p)
BGP_TRACE(D_EVENTS, "Started");
p->start_state = BSS_CONNECT;
- if (!p->cf->passive)
+ if (!p->passive)
bgp_active(p);
+
+ if (p->postponed_sk)
+ {
+ /* Apply postponed incoming connection */
+ bgp_setup_conn(p, &p->incoming_conn);
+ bgp_setup_sk(&p->incoming_conn, p->postponed_sk);
+ bgp_send_open(&p->incoming_conn);
+ p->postponed_sk = NULL;
+ }
}
static void
@@ -387,7 +399,7 @@ bgp_close_conn(struct bgp_conn *conn)
void
bgp_update_startup_delay(struct bgp_proto *p)
{
- struct bgp_config *cf = p->cf;
+ const struct bgp_config *cf = p->cf;
DBG("BGP: Updating startup delay\n");
@@ -410,7 +422,7 @@ bgp_update_startup_delay(struct bgp_proto *p)
}
static void
-bgp_graceful_close_conn(struct bgp_conn *conn, uint subcode, byte *data, uint len)
+bgp_graceful_close_conn(struct bgp_conn *conn, int subcode, byte *data, uint len)
{
switch (conn->state)
{
@@ -426,7 +438,13 @@ bgp_graceful_close_conn(struct bgp_conn *conn, uint subcode, byte *data, uint le
case BS_OPENSENT:
case BS_OPENCONFIRM:
case BS_ESTABLISHED:
- bgp_error(conn, 6, subcode, data, len);
+ if (subcode < 0)
+ {
+ bgp_conn_enter_close_state(conn);
+ bgp_schedule_packet(conn, NULL, PKT_SCHEDULE_CLOSE);
+ }
+ else
+ bgp_error(conn, 6, subcode, data, len);
return;
default:
@@ -456,7 +474,7 @@ bgp_decision(void *vp)
if ((p->p.proto_state == PS_START) &&
(p->outgoing_conn.state == BS_IDLE) &&
(p->incoming_conn.state != BS_OPENCONFIRM) &&
- !p->cf->passive)
+ !p->passive)
bgp_active(p);
if ((p->p.proto_state == PS_STOP) &&
@@ -465,8 +483,31 @@ bgp_decision(void *vp)
bgp_down(p);
}
+static struct bgp_proto *
+bgp_spawn(struct bgp_proto *pp, ip_addr remote_ip)
+{
+ struct symbol *sym;
+ char fmt[SYM_MAX_LEN];
+
+ bsprintf(fmt, "%s%%0%dd", pp->cf->dynamic_name, pp->cf->dynamic_name_digits);
+
+ /* This is hack, we would like to share config, but we need to copy it now */
+ new_config = config;
+ cfg_mem = config->mem;
+ conf_this_scope = config->root_scope;
+ sym = cf_default_name(fmt, &(pp->dynamic_name_counter));
+ proto_clone_config(sym, pp->p.cf);
+ new_config = NULL;
+ cfg_mem = NULL;
+
+ /* Just pass remote_ip to bgp_init() */
+ ((struct bgp_config *) sym->proto)->remote_ip = remote_ip;
+
+ return (void *) proto_spawn(sym->proto, 0);
+}
+
void
-bgp_stop(struct bgp_proto *p, uint subcode, byte *data, uint len)
+bgp_stop(struct bgp_proto *p, int subcode, byte *data, uint len)
{
proto_notify_state(&p->p, PS_STOP);
bgp_graceful_close_conn(&p->outgoing_conn, subcode, data, len);
@@ -491,6 +532,7 @@ bgp_conn_enter_openconfirm_state(struct bgp_conn *conn)
}
static const struct bgp_af_caps dummy_af_caps = { };
+static const struct bgp_af_caps basic_af_caps = { .ready = 1 };
void
bgp_conn_enter_established_state(struct bgp_conn *conn)
@@ -503,8 +545,12 @@ bgp_conn_enter_established_state(struct bgp_conn *conn)
BGP_TRACE(D_EVENTS, "BGP session established");
/* For multi-hop BGP sessions */
- if (ipa_zero(p->source_addr))
- p->source_addr = conn->sk->saddr;
+ if (ipa_zero(p->local_ip))
+ p->local_ip = conn->sk->saddr;
+
+ /* For promiscuous sessions */
+ if (!p->remote_as)
+ p->remote_as = conn->received_as;
/* In case of LLv6 is not valid during BGP start */
if (ipa_zero(p->link_addr) && p->neigh && p->neigh->iface && p->neigh->iface->llv6)
@@ -541,6 +587,13 @@ bgp_conn_enter_established_state(struct bgp_conn *conn)
const struct bgp_af_caps *loc = bgp_find_af_caps(local, c->afi);
const struct bgp_af_caps *rem = bgp_find_af_caps(peer, c->afi);
+ /* Use default if capabilities were not announced */
+ if (!local->length && (c->afi == BGP_AF_IPV4))
+ loc = &basic_af_caps;
+
+ if (!peer->length && (c->afi == BGP_AF_IPV4))
+ rem = &basic_af_caps;
+
/* Ignore AFIs that were not announced in multiprotocol capability */
if (!loc || !loc->ready)
loc = &dummy_af_caps;
@@ -880,6 +933,7 @@ bgp_send_open(struct bgp_conn *conn)
conn->sk->rx_hook = bgp_rx;
conn->sk->tx_hook = bgp_tx;
tm_stop(conn->connect_timer);
+ bgp_prepare_capabilities(conn);
bgp_schedule_packet(conn, NULL, PKT_OPEN);
bgp_conn_set_state(conn, BS_OPENSENT);
bgp_start_timer(conn->hold_timer, conn->bgp->cf->initial_hold_time);
@@ -1039,8 +1093,8 @@ bgp_connect(struct bgp_proto *p) /* Enter Connect state and start establishing c
DBG("BGP: Connecting\n");
sock *s = sk_new(p->p.pool);
s->type = SK_TCP_ACTIVE;
- s->saddr = p->source_addr;
- s->daddr = p->cf->remote_ip;
+ s->saddr = p->local_ip;
+ s->daddr = p->remote_ip;
s->dport = p->cf->remote_port;
s->iface = p->neigh ? p->neigh->iface : NULL;
s->vrf = p->p.vrf;
@@ -1075,6 +1129,9 @@ err:
return;
}
+static inline int bgp_is_dynamic(struct bgp_proto *p)
+{ return ipa_zero(p->remote_ip); }
+
/**
* bgp_find_proto - find existing proto for incoming connection
* @sk: TCP socket
@@ -1083,6 +1140,7 @@ err:
static struct bgp_proto *
bgp_find_proto(sock *sk)
{
+ struct bgp_proto *best = NULL;
struct bgp_proto *p;
/* sk->iface is valid only if src or dst address is link-local */
@@ -1090,13 +1148,20 @@ bgp_find_proto(sock *sk)
WALK_LIST(p, proto_list)
if ((p->p.proto == &proto_bgp) &&
- (p->sock == sk->data) &&
- ipa_equal(p->cf->remote_ip, sk->daddr) &&
+ (ipa_equal(p->remote_ip, sk->daddr) || bgp_is_dynamic(p)) &&
+ (!p->cf->remote_range || ipa_in_netX(sk->daddr, p->cf->remote_range)) &&
+ (p->p.vrf == sk->vrf) &&
+ (p->cf->local_port == sk->sport) &&
(!link || (p->cf->iface == sk->iface)) &&
(ipa_zero(p->cf->local_ip) || ipa_equal(p->cf->local_ip, sk->saddr)))
- return p;
+ {
+ best = p;
- return NULL;
+ if (!bgp_is_dynamic(p))
+ break;
+ }
+
+ return best;
}
/**
@@ -1175,6 +1240,16 @@ bgp_incoming_connection(sock *sk, uint dummy UNUSED)
sk_reallocate(sk);
}
+ /* For dynamic BGP, spawn new instance and postpone the socket */
+ if (bgp_is_dynamic(p))
+ {
+ p = bgp_spawn(p, sk->daddr);
+ p->postponed_sk = sk;
+ rmove(sk, p->p.pool);
+ return 0;
+ }
+
+ rmove(sk, p->p.pool);
bgp_setup_conn(p, &p->incoming_conn);
bgp_setup_sk(&p->incoming_conn, sk);
bgp_send_open(&p->incoming_conn);
@@ -1201,11 +1276,11 @@ bgp_start_neighbor(struct bgp_proto *p)
{
/* Called only for single-hop BGP sessions */
- if (ipa_zero(p->source_addr))
- p->source_addr = p->neigh->ifa->ip;
+ if (ipa_zero(p->local_ip))
+ p->local_ip = p->neigh->ifa->ip;
- if (ipa_is_link_local(p->source_addr))
- p->link_addr = p->source_addr;
+ if (ipa_is_link_local(p->local_ip))
+ p->link_addr = p->local_ip;
else if (p->neigh->iface->llv6)
p->link_addr = p->neigh->iface->llv6->ip;
@@ -1293,10 +1368,10 @@ bgp_bfd_notify(struct bfd_request *req)
static void
bgp_update_bfd(struct bgp_proto *p, int use_bfd)
{
- if (use_bfd && !p->bfd_req)
- p->bfd_req = bfd_request_session(p->p.pool, p->cf->remote_ip, p->source_addr,
+ if (use_bfd && !p->bfd_req && !bgp_is_dynamic(p))
+ p->bfd_req = bfd_request_session(p->p.pool, p->remote_ip, p->local_ip,
p->cf->multihop ? NULL : p->neigh->iface,
- bgp_bfd_notify, p);
+ p->p.vrf, bgp_bfd_notify, p);
if (!use_bfd && p->bfd_req)
{
@@ -1375,7 +1450,7 @@ static void
bgp_start_locked(struct object_lock *lock)
{
struct bgp_proto *p = lock->data;
- struct bgp_config *cf = p->cf;
+ const struct bgp_config *cf = p->cf;
if (p->p.proto_state != PS_START)
{
@@ -1385,17 +1460,17 @@ bgp_start_locked(struct object_lock *lock)
DBG("BGP: Got lock\n");
- if (cf->multihop)
+ if (cf->multihop || bgp_is_dynamic(p))
{
/* Multi-hop sessions do not use neighbor entries */
bgp_initiate(p);
return;
}
- neighbor *n = neigh_find(&p->p, cf->remote_ip, cf->iface, NEF_STICKY);
+ neighbor *n = neigh_find(&p->p, p->remote_ip, cf->iface, NEF_STICKY);
if (!n)
{
- log(L_ERR "%s: Invalid remote address %I%J", p->p.name, cf->remote_ip, cf->iface);
+ log(L_ERR "%s: Invalid remote address %I%J", p->p.name, p->remote_ip, cf->iface);
/* As we do not start yet, we can just disable protocol */
p->p.disabled = 1;
bgp_store_error(p, NULL, BE_MISC, BEM_INVALID_NEXT_HOP);
@@ -1406,7 +1481,7 @@ bgp_start_locked(struct object_lock *lock)
p->neigh = n;
if (n->scope <= 0)
- BGP_TRACE(D_EVENTS, "Waiting for %I%J to become my neighbor", cf->remote_ip, cf->iface);
+ BGP_TRACE(D_EVENTS, "Waiting for %I%J to become my neighbor", p->remote_ip, cf->iface);
else if (p->cf->check_link && !(n->iface->flags & IF_LINK_UP))
BGP_TRACE(D_EVENTS, "Waiting for link on %s", n->iface->name);
else
@@ -1417,14 +1492,29 @@ static int
bgp_start(struct proto *P)
{
struct bgp_proto *p = (struct bgp_proto *) P;
- struct object_lock *lock;
+ const struct bgp_config *cf = p->cf;
+
+ p->local_ip = cf->local_ip;
+ p->local_as = cf->local_as;
+ p->remote_as = cf->remote_as;
+ p->public_as = cf->local_as;
+
+ /* For dynamic BGP childs, remote_ip is already set */
+ if (ipa_nonzero(cf->remote_ip))
+ p->remote_ip = cf->remote_ip;
+
+ /* Confederation ID is used for truly external peers */
+ if (p->cf->confederation && !p->is_interior)
+ p->public_as = cf->confederation;
+
+ p->passive = cf->passive || bgp_is_dynamic(p);
- DBG("BGP: Startup.\n");
p->start_state = BSS_PREPARE;
p->outgoing_conn.state = BS_IDLE;
p->incoming_conn.state = BS_IDLE;
p->neigh = NULL;
p->bfd_req = NULL;
+ p->postponed_sk = NULL;
p->gr_ready = 0;
p->gr_active_num = 0;
@@ -1437,7 +1527,6 @@ bgp_start(struct proto *P)
p->rr_cluster_id = p->cf->rr_cluster_id ? p->cf->rr_cluster_id : p->local_id;
p->remote_id = 0;
- p->source_addr = p->cf->local_ip;
p->link_addr = IPA_NONE;
/* Lock all channels when in GR recovery mode */
@@ -1452,9 +1541,9 @@ bgp_start(struct proto *P)
* Before attempting to create the connection, we need to lock the port,
* so that we are the only instance attempting to talk with that neighbor.
*/
-
+ struct object_lock *lock;
lock = p->lock = olock_new(P->pool);
- lock->addr = p->cf->remote_ip;
+ lock->addr = p->remote_ip;
lock->port = p->cf->remote_port;
lock->iface = p->cf->iface;
lock->vrf = p->cf->iface ? NULL : p->p.vrf;
@@ -1472,7 +1561,7 @@ static int
bgp_shutdown(struct proto *P)
{
struct bgp_proto *p = (struct bgp_proto *) P;
- uint subcode = 0;
+ int subcode = 0;
char *message = NULL;
byte *data = NULL;
@@ -1493,6 +1582,7 @@ bgp_shutdown(struct proto *P)
case PDC_CMD_DISABLE:
case PDC_CMD_SHUTDOWN:
+ shutdown:
subcode = 2; // Errcode 6, 2 - administrative shutdown
message = P->message;
break;
@@ -1502,6 +1592,14 @@ bgp_shutdown(struct proto *P)
message = P->message;
break;
+ case PDC_CMD_GR_DOWN:
+ if ((p->cf->gr_mode != BGP_GR_ABLE) &&
+ (p->cf->llgr_mode != BGP_LLGR_ABLE))
+ goto shutdown;
+
+ subcode = -1; // Do not send NOTIFICATION, just close the connection
+ break;
+
case PDC_RX_LIMIT_HIT:
case PDC_IN_LIMIT_HIT:
subcode = 1; // Errcode 6, 1 - max number of prefixes reached
@@ -1528,7 +1626,7 @@ bgp_shutdown(struct proto *P)
if (message)
{
uint msg_len = strlen(message);
- msg_len = MIN(msg_len, 128);
+ msg_len = MIN(msg_len, 255);
/* Buffer will be freed automatically by protocol shutdown */
data = mb_alloc(p->p.pool, msg_len + 1);
@@ -1562,17 +1660,21 @@ bgp_init(struct proto_config *CF)
P->rte_modify = bgp_rte_modify_stale;
p->cf = cf;
- p->local_as = cf->local_as;
- p->remote_as = cf->remote_as;
- p->public_as = cf->local_as;
p->is_internal = (cf->local_as == cf->remote_as);
p->is_interior = p->is_internal || cf->confederation_member;
p->rs_client = cf->rs_client;
p->rr_client = cf->rr_client;
- /* Confederation ID is used for truly external peers */
- if (cf->confederation && !p->is_interior)
- p->public_as = cf->confederation;
+ p->ipv4 = ipa_nonzero(cf->remote_ip) ?
+ ipa_is_ip4(cf->remote_ip) :
+ (cf->remote_range && (cf->remote_range->type == NET_IP4));
+
+ p->remote_ip = cf->remote_ip;
+ p->remote_as = cf->remote_as;
+
+ /* Hack: We use cf->remote_ip just to pass remote_ip from bgp_spawn() */
+ if (cf->c.parent)
+ cf->remote_ip = IPA_NONE;
/* Add all channels */
struct bgp_channel_config *cc;
@@ -1604,7 +1706,7 @@ bgp_channel_start(struct channel *C)
{
struct bgp_proto *p = (void *) C->proto;
struct bgp_channel *c = (void *) C;
- ip_addr src = p->source_addr;
+ ip_addr src = p->local_ip;
if (c->igp_table_ip4)
rt_lock_table(c->igp_table_ip4);
@@ -1745,14 +1847,19 @@ void
bgp_postconfig(struct proto_config *CF)
{
struct bgp_config *cf = (void *) CF;
- int internal = (cf->local_as == cf->remote_as);
- int interior = internal || cf->confederation_member;
/* Do not check templates at all */
if (cf->c.class == SYM_TEMPLATE)
return;
+ /* Handle undefined remote_as, zero should mean unspecified external */
+ if (!cf->remote_as && (cf->peer_type == BGP_PT_INTERNAL))
+ cf->remote_as = cf->local_as;
+
+ int internal = (cf->local_as == cf->remote_as);
+ int interior = internal || cf->confederation_member;
+
/* EBGP direct by default, IBGP multihop by default */
if (cf->multihop < 0)
cf->multihop = internal ? 64 : 0;
@@ -1769,11 +1876,20 @@ bgp_postconfig(struct proto_config *CF)
if (!cf->local_as)
cf_error("Local AS number must be set");
- if (ipa_zero(cf->remote_ip))
+ if (ipa_zero(cf->remote_ip) && !cf->remote_range)
cf_error("Neighbor must be configured");
- if (!cf->remote_as)
- cf_error("Remote AS number must be set");
+ if (ipa_zero(cf->local_ip) && cf->strict_bind)
+ cf_error("Local address must be configured for strict bind");
+
+ if (!cf->remote_as && !cf->peer_type)
+ cf_error("Remote AS number (or peer type) must be set");
+
+ if ((cf->peer_type == BGP_PT_INTERNAL) && !internal)
+ cf_error("IBGP cannot have different ASNs");
+
+ if ((cf->peer_type == BGP_PT_EXTERNAL) && internal)
+ cf_error("EBGP cannot have the same ASNs");
if (!cf->iface && (ipa_is_link_local(cf->local_ip) ||
ipa_is_link_local(cf->remote_ip)))
@@ -1885,8 +2001,8 @@ static int
bgp_reconfigure(struct proto *P, struct proto_config *CF)
{
struct bgp_proto *p = (void *) P;
- struct bgp_config *new = (void *) CF;
- struct bgp_config *old = p->cf;
+ const struct bgp_config *new = (void *) CF;
+ const struct bgp_config *old = p->cf;
if (proto_get_router_id(CF) != p->local_id)
return 0;
@@ -1895,8 +2011,11 @@ bgp_reconfigure(struct proto *P, struct proto_config *CF)
((byte *) new) + sizeof(struct proto_config),
// password item is last and must be checked separately
OFFSETOF(struct bgp_config, password) - sizeof(struct proto_config))
- && ((!old->password && !new->password)
- || (old->password && new->password && !strcmp(old->password, new->password)));
+ && !bstrcmp(old->password, new->password)
+ && ((!old->remote_range && !new->remote_range)
+ || (old->remote_range && new->remote_range && net_equal(old->remote_range, new->remote_range)))
+ && !bstrcmp(old->dynamic_name, new->dynamic_name)
+ && (old->dynamic_name_digits == new->dynamic_name_digits);
/* FIXME: Move channel reconfiguration to generic protocol code ? */
struct channel *C, *C2;
@@ -1926,6 +2045,9 @@ bgp_reconfigure(struct proto *P, struct proto_config *CF)
if (same)
p->cf = new;
+ /* Reset name counter */
+ p->dynamic_name_counter = 0;
+
return same;
}
@@ -2056,7 +2178,7 @@ bgp_state_dsc(struct bgp_proto *p)
return "Down";
int state = MAX(p->incoming_conn.state, p->outgoing_conn.state);
- if ((state == BS_IDLE) && (p->start_state >= BSS_CONNECT) && p->cf->passive)
+ if ((state == BS_IDLE) && (p->start_state >= BSS_CONNECT) && p->passive)
return "Passive";
return bgp_state_names[state];
@@ -2232,8 +2354,14 @@ bgp_show_proto_info(struct proto *P)
struct bgp_proto *p = (struct bgp_proto *) P;
cli_msg(-1006, " BGP state: %s", bgp_state_dsc(p));
- cli_msg(-1006, " Neighbor address: %I%J", p->cf->remote_ip, p->cf->iface);
+
+ if (bgp_is_dynamic(p) && p->cf->remote_range)
+ cli_msg(-1006, " Neighbor range: %N", p->cf->remote_range);
+ else
+ cli_msg(-1006, " Neighbor address: %I%J", p->remote_ip, p->cf->iface);
+
cli_msg(-1006, " Neighbor AS: %u", p->remote_as);
+ cli_msg(-1006, " Local AS: %u", p->cf->local_as);
if (p->gr_active_num)
cli_msg(-1006, " Neighbor graceful restart active");
@@ -2269,7 +2397,7 @@ bgp_show_proto_info(struct proto *P)
p->rr_client ? " route-reflector" : "",
p->rs_client ? " route-server" : "",
p->as4_session ? " AS4" : "");
- cli_msg(-1006, " Source address: %I", p->source_addr);
+ cli_msg(-1006, " Source address: %I", p->local_ip);
cli_msg(-1006, " Hold timer: %t/%u",
tm_remains(p->conn->hold_timer), p->conn->hold_time);
cli_msg(-1006, " Keepalive timer: %t/%u",
diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h
index cfc88d8e..075e1bb9 100644
--- a/proto/bgp/bgp.h
+++ b/proto/bgp/bgp.h
@@ -83,6 +83,7 @@ struct bgp_config {
struct iface *iface; /* Interface for link-local addresses */
u16 local_port; /* Local listening port */
u16 remote_port; /* Neighbor destination port */
+ int peer_type; /* Internal or external BGP (BGP_PT_*, optional) */
int multihop; /* Number of hops if multihop */
int strict_bind; /* Bind listening socket to local address */
int ttl_security; /* Enable TTL security [RFC 5082] */
@@ -123,6 +124,9 @@ struct bgp_config {
u32 disable_after_cease; /* Disable it when cease is received, bitfield */
char *password; /* Password used for MD5 authentication */
+ net_addr *remote_range; /* Allowed neighbor range for dynamic BGP */
+ char *dynamic_name; /* Name pattern for dynamic BGP */
+ int dynamic_name_digits; /* Minimum number of digits for dynamic names */
int check_link; /* Use iface link state for liveness detection */
int bfd; /* Use BFD for liveness detection */
};
@@ -136,6 +140,7 @@ struct bgp_channel_config {
ip_addr next_hop_addr; /* Local address for NEXT_HOP attribute */
u8 next_hop_self; /* Always set next hop to local IP address (NH_*) */
u8 next_hop_keep; /* Do not modify next hop attribute (NH_*) */
+ u8 mandatory; /* Channel is mandatory in capability negotiation */
u8 missing_lladdr; /* What we will do when we don' know link-local addr, see MLL_* */
u8 gw_mode; /* How we compute route gateway from next_hop attr, see GW_* */
u8 secondary; /* Accept also non-best routes (i.e. RA_ACCEPTED) */
@@ -151,6 +156,9 @@ struct bgp_channel_config {
struct rtable_config *igp_table_ip6; /* Table for recursive IPv6 next hop lookups */
};
+#define BGP_PT_INTERNAL 1
+#define BGP_PT_EXTERNAL 2
+
#define NH_NO 0
#define NH_ALL 1
#define NH_IBGP 2
@@ -213,8 +221,11 @@ struct bgp_caps {
u16 gr_time; /* Graceful restart time in seconds */
u8 llgr_aware; /* Long-lived GR capability, RFC draft */
+ u8 any_ext_next_hop; /* Bitwise OR of per-AF ext_next_hop */
+ u8 any_add_path; /* Bitwise OR of per-AF add_path */
u16 af_count; /* Number of af_data items */
+ u16 length; /* Length of capabilities in OPEN msg */
struct bgp_af_caps af_data[0]; /* Per-AF capability data */
};
@@ -235,6 +246,7 @@ struct bgp_conn {
u8 state; /* State of connection state machine */
u8 as4_session; /* Session uses 4B AS numbers in AS_PATH (both sides support it) */
u8 ext_messages; /* Session uses extended message length */
+ u32 received_as; /* ASN received in OPEN message */
struct bgp_caps *local_caps;
struct bgp_caps *remote_caps;
@@ -254,18 +266,21 @@ struct bgp_conn {
struct bgp_proto {
struct proto p;
- struct bgp_config *cf; /* Shortcut to BGP configuration */
+ const struct bgp_config *cf; /* Shortcut to BGP configuration */
+ ip_addr local_ip, remote_ip;
u32 local_as, remote_as;
u32 public_as; /* Externally visible ASN (local_as or confederation id) */
u32 local_id; /* BGP identifier of this router */
u32 remote_id; /* BGP identifier of the neighbor */
u32 rr_cluster_id; /* Route reflector cluster ID */
- int start_state; /* Substates that partitions BS_START */
+ u8 start_state; /* Substates that partitions BS_START */
u8 is_internal; /* Internal BGP session (local_as == remote_as) */
u8 is_interior; /* Internal or intra-confederation BGP session */
u8 as4_session; /* Session uses 4B AS numbers in AS_PATH (both sides support it) */
u8 rr_client; /* Whether neighbor is RR client of me */
u8 rs_client; /* Whether neighbor is RS client of me */
+ u8 ipv4; /* Use IPv4 connection, i.e. remote_ip is IPv4 */
+ u8 passive; /* Do not initiate outgoing connection */
u8 route_refresh; /* Route refresh allowed to send [RFC 2918] */
u8 enhanced_refresh; /* Enhanced refresh is negotiated [RFC 7313] */
u8 gr_ready; /* Neighbor could do graceful restart */
@@ -282,11 +297,12 @@ struct bgp_proto {
struct neighbor *neigh; /* Neighbor entry corresponding to remote ip, NULL if multihop */
struct bgp_socket *sock; /* Shared listening socket */
struct bfd_request *bfd_req; /* BFD request, if BFD is used */
- ip_addr source_addr; /* Local address used as an advertised next hop */
- ip_addr link_addr; /* Link-local version of source_addr */
+ struct birdsock *postponed_sk; /* Postponed incoming socket for dynamic BGP */
+ ip_addr link_addr; /* Link-local version of local_ip */
event *event; /* Event for respawning and shutting process */
timer *startup_timer; /* Timer used to delay protocol startup due to previous errors (startup_delay) */
timer *gr_timer; /* Timer waiting for reestablishment after graceful restart */
+ int dynamic_name_counter; /* Counter for dynamic BGP names */
uint startup_delay; /* Delay (in seconds) of protocol startup due to previous errors */
btime last_proto_error; /* Time of last error that leads to protocol stop */
u8 last_error_class; /* Error class of last error */
@@ -376,7 +392,7 @@ struct bgp_write_state {
int mpls;
eattr *mp_next_hop;
- adata *mpls_labels;
+ const adata *mpls_labels;
};
struct bgp_parse_state {
@@ -472,7 +488,7 @@ void bgp_graceful_restart_done(struct bgp_channel *c);
void bgp_refresh_begin(struct bgp_channel *c);
void bgp_refresh_end(struct bgp_channel *c);
void bgp_store_error(struct bgp_proto *p, struct bgp_conn *c, u8 class, u32 code);
-void bgp_stop(struct bgp_proto *p, uint subcode, byte *data, uint len);
+void bgp_stop(struct bgp_proto *p, int subcode, byte *data, uint len);
struct rte_source *bgp_find_source(struct bgp_proto *p, u32 path_id);
struct rte_source *bgp_get_source(struct bgp_proto *p, u32 path_id);
@@ -507,7 +523,7 @@ bgp_set_attr_u32(ea_list **to, struct linpool *pool, uint code, uint flags, u32
{ bgp_set_attr(to, pool, code, flags, (uintptr_t) val); }
static inline void
-bgp_set_attr_ptr(ea_list **to, struct linpool *pool, uint code, uint flags, struct adata *val)
+bgp_set_attr_ptr(ea_list **to, struct linpool *pool, uint code, uint flags, const struct adata *val)
{ bgp_set_attr(to, pool, code, flags, (uintptr_t) val); }
static inline void
@@ -549,6 +565,7 @@ void bgp_get_route_info(struct rte *, byte *buf);
/* packets.c */
void bgp_dump_state_change(struct bgp_conn *conn, uint old, uint new);
+void bgp_prepare_capabilities(struct bgp_conn *conn);
const struct bgp_af_desc *bgp_get_af_desc(u32 afi);
const struct bgp_af_caps *bgp_find_af_caps(struct bgp_caps *caps, u32 afi);
void bgp_schedule_packet(struct bgp_conn *conn, struct bgp_channel *c, int type);
diff --git a/proto/bgp/config.Y b/proto/bgp/config.Y
index ac8d024a..bbc7d9a4 100644
--- a/proto/bgp/config.Y
+++ b/proto/bgp/config.Y
@@ -29,7 +29,8 @@ CF_KEYWORDS(BGP, LOCAL, NEIGHBOR, AS, HOLD, TIME, CONNECT, RETRY, KEEPALIVE,
SECURITY, DETERMINISTIC, SECONDARY, ALLOW, BFD, ADD, PATHS, RX, TX,
GRACEFUL, RESTART, AWARE, CHECK, LINK, PORT, EXTENDED, MESSAGES, SETKEY,
STRICT, BIND, CONFEDERATION, MEMBER, MULTICAST, FLOW4, FLOW6, LONG,
- LIVED, STALE, IMPORT, IBGP, EBGP)
+ LIVED, STALE, IMPORT, IBGP, EBGP, MANDATORY, INTERNAL, EXTERNAL,
+ DYNAMIC, RANGE, NAME, DIGITS)
%type <i> bgp_nh
%type <i32> bgp_afi
@@ -68,6 +69,7 @@ bgp_proto_start: proto_start BGP {
BGP_CFG->llgr_mode = -1;
BGP_CFG->llgr_time = 3600;
BGP_CFG->setkey = 1;
+ BGP_CFG->dynamic_name = "dynbgp";
BGP_CFG->check_link = -1;
}
;
@@ -82,6 +84,8 @@ bgp_nbr_opts:
/* empty */
| bgp_nbr_opts PORT expr { BGP_CFG->remote_port = $3; if (($3<1) || ($3>65535)) cf_error("Invalid port number"); }
| bgp_nbr_opts AS expr { BGP_CFG->remote_as = $3; }
+ | bgp_nbr_opts INTERNAL { BGP_CFG->peer_type = BGP_PT_INTERNAL; }
+ | bgp_nbr_opts EXTERNAL { BGP_CFG->peer_type = BGP_PT_EXTERNAL; }
;
bgp_cease_mask:
@@ -118,11 +122,18 @@ bgp_proto:
}
| bgp_proto NEIGHBOR bgp_nbr_opts ';'
| bgp_proto NEIGHBOR ipa ipa_scope bgp_nbr_opts ';' {
- if (ipa_nonzero(BGP_CFG->remote_ip))
+ if (ipa_nonzero(BGP_CFG->remote_ip) || BGP_CFG->remote_range)
cf_error("Only one neighbor per BGP instance is allowed");
BGP_CFG->remote_ip = $3;
if ($4) BGP_CFG->iface = $4;
}
+ | bgp_proto NEIGHBOR RANGE net_ip bgp_nbr_opts ';' {
+ if (ipa_nonzero(BGP_CFG->remote_ip) || BGP_CFG->remote_range)
+ cf_error("Only one neighbor per BGP instance is allowed");
+ net_addr *n = cfg_alloc($4.length);
+ net_copy(n, &($4));
+ BGP_CFG->remote_range = n;
+ }
| bgp_proto INTERFACE TEXT ';' { BGP_CFG->iface = if_get_by_name($3); }
| bgp_proto RR CLUSTER ID idval ';' { BGP_CFG->rr_cluster_id = $5; }
| bgp_proto RR CLIENT bool ';' { BGP_CFG->rr_client = $4; }
@@ -134,6 +145,12 @@ bgp_proto:
| bgp_proto DIRECT ';' { BGP_CFG->multihop = 0; }
| bgp_proto MULTIHOP ';' { BGP_CFG->multihop = 64; }
| bgp_proto MULTIHOP expr ';' { BGP_CFG->multihop = $3; if (($3<1) || ($3>255)) cf_error("Multihop must be in range 1-255"); }
+ | bgp_proto DYNAMIC NAME text ';' {
+ if (strchr($4, '%')) cf_error("Forbidden character '%%' in dynamic name");
+ if (strlen($4) > (SYM_MAX_LEN - 16)) cf_error("Dynamic name too long");
+ BGP_CFG->dynamic_name = $4;
+ }
+ | bgp_proto DYNAMIC NAME DIGITS expr ';' { BGP_CFG->dynamic_name_digits = $5; if ($5>10) cf_error("Dynamic name digits must be at most 10"); }
| bgp_proto STRICT BIND bool ';' { BGP_CFG->strict_bind = $4; }
| bgp_proto PATH METRIC bool ';' { BGP_CFG->compare_path_lengths = $4; }
| bgp_proto MED METRIC bool ';' { BGP_CFG->med_metric = $4; }
@@ -223,6 +240,7 @@ bgp_channel_item:
| NEXT HOP ADDRESS ipa { BGP_CC->next_hop_addr = $4; }
| NEXT HOP SELF bgp_nh { BGP_CC->next_hop_self = $4; }
| NEXT HOP KEEP bgp_nh { BGP_CC->next_hop_keep = $4; }
+ | MANDATORY bool { BGP_CC->mandatory = $2; }
| MISSING LLADDR SELF { BGP_CC->missing_lladdr = MLL_SELF; }
| MISSING LLADDR DROP { BGP_CC->missing_lladdr = MLL_DROP; }
| MISSING LLADDR IGNORE { BGP_CC->missing_lladdr = MLL_IGNORE; }
diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c
index 26716573..daa88630 100644
--- a/proto/bgp/packets.c
+++ b/proto/bgp/packets.c
@@ -100,7 +100,7 @@ init_mrt_bgp_data(struct bgp_conn *conn, struct mrt_bgp_data *d)
d->peer_as = p->remote_as;
d->local_as = p->local_as;
d->index = (p->neigh && p->neigh->iface) ? p->neigh->iface->index : 0;
- d->af = ipa_is_ip4(p->cf->remote_ip) ? BGP_AFI_IPV4 : BGP_AFI_IPV6;
+ d->af = ipa_is_ip4(p->remote_ip) ? BGP_AFI_IPV4 : BGP_AFI_IPV6;
d->peer_ip = conn->sk ? conn->sk->daddr : IPA_NONE;
d->local_ip = conn->sk ? conn->sk->saddr : IPA_NONE;
d->as4 = p_ok ? p->as4_session : 0;
@@ -208,19 +208,22 @@ bgp_af_caps_cmp(const void *X, const void *Y)
}
-static byte *
-bgp_write_capabilities(struct bgp_conn *conn, byte *buf)
+void
+bgp_prepare_capabilities(struct bgp_conn *conn)
{
struct bgp_proto *p = conn->bgp;
struct bgp_channel *c;
struct bgp_caps *caps;
struct bgp_af_caps *ac;
- uint any_ext_next_hop = 0;
- uint any_add_path = 0;
- byte *data;
- /* Prepare bgp_caps structure */
+ if (!p->cf->capabilities)
+ {
+ /* Just prepare empty local_caps */
+ conn->local_caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps));
+ return;
+ }
+ /* Prepare bgp_caps structure */
int n = list_length(&p->p.channels);
caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps) + n * sizeof(struct bgp_af_caps));
conn->local_caps = caps;
@@ -251,10 +254,10 @@ bgp_write_capabilities(struct bgp_conn *conn, byte *buf)
ac->ready = 1;
ac->ext_next_hop = bgp_channel_is_ipv4(c) && c->cf->ext_next_hop;
- any_ext_next_hop |= ac->ext_next_hop;
+ caps->any_ext_next_hop |= ac->ext_next_hop;
ac->add_path = c->cf->add_path;
- any_add_path |= ac->add_path;
+ caps->any_add_path |= ac->add_path;
if (c->cf->gr_able)
{
@@ -276,7 +279,16 @@ bgp_write_capabilities(struct bgp_conn *conn, byte *buf)
/* Sort capability fields by AFI/SAFI */
qsort(caps->af_data, caps->af_count, sizeof(struct bgp_af_caps), bgp_af_caps_cmp);
+}
+static byte *
+bgp_write_capabilities(struct bgp_conn *conn, byte *buf)
+{
+ struct bgp_proto *p = conn->bgp;
+ struct bgp_caps *caps = conn->local_caps;
+ struct bgp_af_caps *ac;
+ byte *buf_head = buf;
+ byte *data;
/* Create capability list in buffer */
@@ -301,7 +313,7 @@ bgp_write_capabilities(struct bgp_conn *conn, byte *buf)
*buf++ = 0; /* Capability data length */
}
- if (any_ext_next_hop)
+ if (caps->any_ext_next_hop)
{
*buf++ = 5; /* Capability 5: Support for extended next hop */
*buf++ = 0; /* Capability data length, will be fixed later */
@@ -353,7 +365,7 @@ bgp_write_capabilities(struct bgp_conn *conn, byte *buf)
buf += 4;
}
- if (any_add_path)
+ if (caps->any_add_path)
{
*buf++ = 69; /* Capability 69: Support for ADD-PATH */
*buf++ = 0; /* Capability data length, will be fixed later */
@@ -394,6 +406,8 @@ bgp_write_capabilities(struct bgp_conn *conn, byte *buf)
data[-1] = buf - data;
}
+ caps->length = buf - buf_head;
+
return buf;
}
@@ -405,6 +419,8 @@ bgp_read_capabilities(struct bgp_conn *conn, struct bgp_caps *caps, byte *pos, i
int i, cl;
u32 af;
+ caps->length += len;
+
while (len > 0)
{
if (len < 2 || len < (2 + pos[1]))
@@ -569,6 +585,42 @@ err:
}
static int
+bgp_check_capabilities(struct bgp_conn *conn)
+{
+ struct bgp_proto *p = conn->bgp;
+ struct bgp_caps *local = conn->local_caps;
+ struct bgp_caps *remote = conn->remote_caps;
+ struct bgp_channel *c;
+ int count = 0;
+
+ /* This is partially overlapping with bgp_conn_enter_established_state(),
+ but we need to run this just after we receive OPEN message */
+
+ WALK_LIST(c, p->p.channels)
+ {
+ const struct bgp_af_caps *loc = bgp_find_af_caps(local, c->afi);
+ const struct bgp_af_caps *rem = bgp_find_af_caps(remote, c->afi);
+
+ /* Find out whether this channel will be active */
+ int active = loc && loc->ready &&
+ ((rem && rem->ready) || (!remote->length && (c->afi == BGP_AF_IPV4)));
+
+ /* Mandatory must be active */
+ if (c->cf->mandatory && !active)
+ return 0;
+
+ if (active)
+ count++;
+ }
+
+ /* We need at least one channel active */
+ if (!count)
+ return 0;
+
+ return 1;
+}
+
+static int
bgp_read_options(struct bgp_conn *conn, byte *pos, int len)
{
struct bgp_proto *p = conn->bgp;
@@ -635,9 +687,6 @@ bgp_create_open(struct bgp_conn *conn, byte *buf)
}
else
{
- /* Prepare empty local_caps */
- conn->local_caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps));
-
buf[9] = 0; /* No optional parameters */
return buf + 10;
}
@@ -678,6 +727,10 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
if (!id || (p->is_internal && id == p->local_id))
{ bgp_error(conn, 2, 3, pkt+24, -4); return; }
+ /* RFC 5492 4 - check for required capabilities */
+ if (p->cf->capabilities && !bgp_check_capabilities(conn))
+ { bgp_error(conn, 2, 7, NULL, 0); return; }
+
struct bgp_caps *caps = conn->remote_caps;
if (caps->as4_support)
@@ -687,13 +740,18 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
if ((as4 != asn) && (asn != AS_TRANS))
log(L_WARN "%s: Peer advertised inconsistent AS numbers", p->p.name);
- if (as4 != p->remote_as)
+ /* When remote ASN is unspecified, it must be external one */
+ if (p->remote_as ? (as4 != p->remote_as) : (as4 == p->local_as))
{ as4 = htonl(as4); bgp_error(conn, 2, 2, (byte *) &as4, 4); return; }
+
+ conn->received_as = as4;
}
else
{
- if (asn != p->remote_as)
+ if (p->remote_as ? (asn != p->remote_as) : (asn == p->local_as))
{ bgp_error(conn, 2, 2, pkt+20, 2); return; }
+
+ conn->received_as = asn;
}
/* Check the other connection */
@@ -962,7 +1020,7 @@ bgp_update_next_hop_ip(struct bgp_export_state *s, eattr *a, ea_list **to)
WITHDRAW(NO_NEXT_HOP);
ip_addr *nh = (void *) a->u.ptr->data;
- ip_addr peer = s->proto->cf->remote_ip;
+ ip_addr peer = s->proto->remote_ip;
uint len = a->u.ptr->length;
/* Forbid zero next hop */
@@ -1210,10 +1268,10 @@ bgp_rte_update(struct bgp_parse_state *s, net_addr *n, u32 path_id, rta *a0)
}
static void
-bgp_encode_mpls_labels(struct bgp_write_state *s UNUSED, adata *mpls, byte **pos, uint *size, byte *pxlen)
+bgp_encode_mpls_labels(struct bgp_write_state *s UNUSED, const adata *mpls, byte **pos, uint *size, byte *pxlen)
{
- u32 dummy = 0;
- u32 *labels = mpls ? (u32 *) mpls->data : &dummy;
+ const u32 dummy = 0;
+ const u32 *labels = mpls ? (const u32 *) mpls->data : &dummy;
uint lnum = mpls ? (mpls->length / 4) : 1;
for (uint i = 0; i < lnum; i++)
@@ -2280,7 +2338,7 @@ bgp_decode_nlri(struct bgp_parse_state *s, u32 afi, byte *nlri, uint len, ea_lis
a->source = RTS_BGP;
a->scope = SCOPE_UNIVERSE;
- a->from = s->proto->cf->remote_ip;
+ a->from = s->proto->remote_ip;
a->eattrs = ea;
c->desc->decode_next_hop(s, nh, nh_len, a);
@@ -2634,6 +2692,12 @@ bgp_fire_tx(struct bgp_conn *conn)
end = bgp_create_notification(conn, pkt);
return bgp_send(conn, PKT_NOTIFICATION, end - buf);
}
+ else if (s & (1 << PKT_OPEN))
+ {
+ conn->packets_to_send &= ~(1 << PKT_OPEN);
+ end = bgp_create_open(conn, pkt);
+ return bgp_send(conn, PKT_OPEN, end - buf);
+ }
else if (s & (1 << PKT_KEEPALIVE))
{
conn->packets_to_send &= ~(1 << PKT_KEEPALIVE);
@@ -2641,12 +2705,6 @@ bgp_fire_tx(struct bgp_conn *conn)
bgp_start_timer(conn->keepalive_timer, conn->keepalive_time);
return bgp_send(conn, PKT_KEEPALIVE, BGP_HEADER_LENGTH);
}
- else if (s & (1 << PKT_OPEN))
- {
- conn->packets_to_send &= ~(1 << PKT_OPEN);
- end = bgp_create_open(conn, pkt);
- return bgp_send(conn, PKT_OPEN, end - buf);
- }
else while (conn->channels_to_send)
{
c = bgp_get_channel_to_send(p, conn);
@@ -2731,15 +2789,18 @@ bgp_schedule_packet(struct bgp_conn *conn, struct bgp_channel *c, int type)
if ((conn->sk->tpos == conn->sk->tbuf) && !ev_active(conn->tx_ev))
ev_schedule(conn->tx_ev);
}
-
void
bgp_kick_tx(void *vconn)
{
struct bgp_conn *conn = vconn;
DBG("BGP: kicking TX\n");
- while (bgp_fire_tx(conn) > 0)
+ uint max = 1024;
+ while (--max && (bgp_fire_tx(conn) > 0))
;
+
+ if (!max && !ev_active(conn->tx_ev))
+ ev_schedule(conn->tx_ev);
}
void
@@ -2748,8 +2809,12 @@ bgp_tx(sock *sk)
struct bgp_conn *conn = sk->data;
DBG("BGP: TX hook\n");
- while (bgp_fire_tx(conn) > 0)
+ uint max = 1024;
+ while (--max && (bgp_fire_tx(conn) > 0))
;
+
+ if (!max && !ev_active(conn->tx_ev))
+ ev_schedule(conn->tx_ev);
}
@@ -2835,7 +2900,7 @@ bgp_handle_message(struct bgp_proto *p, byte *data, uint len, byte **bp)
return 1;
/* Handle proper message */
- if ((msg_len > 128) && (msg_len + 1 > len))
+ if ((msg_len > 255) && (msg_len + 1 > len))
return 0;
/* Some elementary cleanup */
diff --git a/proto/mrt/mrt.c b/proto/mrt/mrt.c
index 95014958..7a396a84 100644
--- a/proto/mrt/mrt.c
+++ b/proto/mrt/mrt.c
@@ -361,7 +361,7 @@ mrt_peer_table_dump(struct mrt_table_dump_state *s)
if ((P->proto == &proto_bgp) && (P->proto_state != PS_DOWN))
{
struct bgp_proto *p = (void *) P;
- mrt_peer_table_entry(s, p->remote_id, p->remote_as, p->cf->remote_ip);
+ mrt_peer_table_entry(s, p->remote_id, p->remote_as, p->remote_ip);
}
#endif
@@ -429,7 +429,7 @@ mrt_rib_table_entry(struct mrt_table_dump_state *s, rte *r)
{
struct bgp_proto *p = (void *) r->attrs->src->proto;
struct mrt_peer_entry *n =
- HASH_FIND(s->peer_hash, PEER, p->remote_id, p->remote_as, p->cf->remote_ip);
+ HASH_FIND(s->peer_hash, PEER, p->remote_id, p->remote_as, p->remote_ip);
peer = n ? n->index : 0;
}
@@ -496,7 +496,7 @@ mrt_rib_table_dump(struct mrt_table_dump_state *s, net *n, int add_path)
continue;
}
- rte_make_tmp_attrs(&rt, s->linpool);
+ rte_make_tmp_attrs(&rt, s->linpool, NULL);
if (f_run(s->filter, &rt, s->linpool, 0) <= F_ACCEPT)
mrt_rib_table_entry(s, rt);
diff --git a/proto/mrt/mrt.h b/proto/mrt/mrt.h
index b2cec09d..4dfb1b19 100644
--- a/proto/mrt/mrt.h
+++ b/proto/mrt/mrt.h
@@ -23,7 +23,7 @@ struct mrt_config {
struct rtable_config *table_cf;
const char *table_expr;
- struct filter *filter;
+ const struct filter *filter;
const char *filename;
uint period;
int always_add_path;
@@ -41,7 +41,7 @@ struct mrt_proto {
struct mrt_dump_data {
const char *table_expr;
struct rtable *table_ptr;
- struct filter *filter;
+ const struct filter *filter;
char *filename;
};
@@ -61,7 +61,7 @@ struct mrt_table_dump_state {
/* Configuration information */
const char *table_expr; /* Wildcard for table name (or NULL) */
struct rtable *table_ptr; /* Explicit table (or NULL) */
- struct filter *filter; /* Optional filter */
+ const struct filter *filter; /* Optional filter */
const char *filename; /* Filename pattern */
int always_add_path; /* Always use *_ADDPATH message subtypes */
diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y
index 36fbd5f1..2ec8c0b6 100644
--- a/proto/ospf/config.Y
+++ b/proto/ospf/config.Y
@@ -200,6 +200,7 @@ CF_KEYWORDS(RX, BUFFER, LARGE, NORMAL, STUBNET, HIDDEN, SUMMARY, TAG, EXTERNAL)
CF_KEYWORDS(WAIT, DELAY, LSADB, ECMP, LIMIT, WEIGHT, NSSA, TRANSLATOR, STABILITY)
CF_KEYWORDS(GLOBAL, LSID, ROUTER, SELF, INSTANCE, REAL, NETMASK, TX, PRIORITY, LENGTH)
CF_KEYWORDS(MERGE, LSA, SUPPRESSION, MULTICAST, RFC5838, VPN, PE)
+CF_KEYWORDS(GRACEFUL, RESTART, AWARE, TIME)
%type <ld> lsadb_args
%type <i> ospf_variant ospf_af_mc nbma_eligible
@@ -226,6 +227,8 @@ ospf_proto_start: proto_start ospf_variant
OSPF_CFG->tick = OSPF_DEFAULT_TICK;
OSPF_CFG->ospf2 = $2;
OSPF_CFG->af_ext = !$2;
+ OSPF_CFG->gr_mode = OSPF_GR_AWARE;
+ OSPF_CFG->gr_time = OSPF_DEFAULT_GR_TIME;
};
ospf_proto:
@@ -258,6 +261,9 @@ ospf_proto_item:
| RFC5838 bool { OSPF_CFG->af_ext = $2; if (!ospf_cfg_is_v3()) cf_error("RFC5838 option requires OSPFv3"); }
| VPN PE bool { OSPF_CFG->vpn_pe = $3; }
| STUB ROUTER bool { OSPF_CFG->stub_router = $3; }
+ | GRACEFUL RESTART bool { OSPF_CFG->gr_mode = $3; }
+ | GRACEFUL RESTART AWARE { OSPF_CFG->gr_mode = OSPF_GR_AWARE; }
+ | GRACEFUL RESTART TIME expr { OSPF_CFG->gr_time = $4; if (($4 < 1) || ($4 > 1800)) cf_error("Graceful restart time must be in range 1-1800"); }
| ECMP bool { OSPF_CFG->ecmp = $2 ? OSPF_DEFAULT_ECMP_LIMIT : 0; }
| ECMP bool LIMIT expr { OSPF_CFG->ecmp = $2 ? $4 : 0; }
| MERGE EXTERNAL bool { OSPF_CFG->merge_external = $3; }
@@ -504,29 +510,29 @@ dynamic_attr: OSPF_TAG { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_OSPF_TA
dynamic_attr: OSPF_ROUTER_ID { $$ = f_new_dynamic_attr(EAF_TYPE_ROUTER_ID, T_QUAD, EA_OSPF_ROUTER_ID); } ;
CF_CLI_HELP(SHOW OSPF, ..., [[Show information about OSPF protocol]]);
-CF_CLI(SHOW OSPF, optsym, [<name>], [[Show information about OSPF protocol]])
+CF_CLI(SHOW OSPF, optproto, [<name>], [[Show information about OSPF protocol]])
{ ospf_sh(proto_get_named($3, &proto_ospf)); };
-CF_CLI(SHOW OSPF NEIGHBORS, optsym opttext, [<name>] [\"<interface>\"], [[Show information about OSPF neighbors]])
+CF_CLI(SHOW OSPF NEIGHBORS, optproto opttext, [<name>] [\"<interface>\"], [[Show information about OSPF neighbors]])
{ ospf_sh_neigh(proto_get_named($4, &proto_ospf), $5); };
-CF_CLI(SHOW OSPF INTERFACE, optsym opttext, [<name>] [\"<interface>\"], [[Show information about interface]])
+CF_CLI(SHOW OSPF INTERFACE, optproto opttext, [<name>] [\"<interface>\"], [[Show information about interface]])
{ ospf_sh_iface(proto_get_named($4, &proto_ospf), $5); };
CF_CLI_HELP(SHOW OSPF TOPOLOGY, [all] [<name>], [[Show information about OSPF network topology]])
-CF_CLI(SHOW OSPF TOPOLOGY, optsym opttext, [<name>], [[Show information about reachable OSPF network topology]])
+CF_CLI(SHOW OSPF TOPOLOGY, optproto opttext, [<name>], [[Show information about reachable OSPF network topology]])
{ ospf_sh_state(proto_get_named($4, &proto_ospf), 0, 1); };
-CF_CLI(SHOW OSPF TOPOLOGY ALL, optsym opttext, [<name>], [[Show information about all OSPF network topology]])
+CF_CLI(SHOW OSPF TOPOLOGY ALL, optproto opttext, [<name>], [[Show information about all OSPF network topology]])
{ ospf_sh_state(proto_get_named($5, &proto_ospf), 0, 0); };
CF_CLI_HELP(SHOW OSPF STATE, [all] [<name>], [[Show information about OSPF network state]])
-CF_CLI(SHOW OSPF STATE, optsym opttext, [<name>], [[Show information about reachable OSPF network state]])
+CF_CLI(SHOW OSPF STATE, optproto opttext, [<name>], [[Show information about reachable OSPF network state]])
{ ospf_sh_state(proto_get_named($4, &proto_ospf), 1, 1); };
-CF_CLI(SHOW OSPF STATE ALL, optsym opttext, [<name>], [[Show information about all OSPF network state]])
+CF_CLI(SHOW OSPF STATE ALL, optproto opttext, [<name>], [[Show information about all OSPF network state]])
{ ospf_sh_state(proto_get_named($5, &proto_ospf), 1, 0); };
CF_CLI_HELP(SHOW OSPF LSADB, ..., [[Show content of OSPF LSA database]]);
@@ -544,7 +550,7 @@ lsadb_args:
| lsadb_args LSID idval { $$ = $1; $$->lsid = $3; }
| lsadb_args SELF { $$ = $1; $$->router = SH_ROUTER_SELF; }
| lsadb_args ROUTER idval { $$ = $1; $$->router = $3; }
- | lsadb_args SYM { $$ = $1; $$->name = $2; }
+ | lsadb_args CF_SYM_KNOWN { cf_assert_symbol($2, SYM_PROTO); $$ = $1; $$->proto = (struct ospf_proto *) proto_get_named($2, &proto_ospf); }
;
CF_CODE
diff --git a/proto/ospf/dbdes.c b/proto/ospf/dbdes.c
index a1559782..5a5f76f8 100644
--- a/proto/ospf/dbdes.c
+++ b/proto/ospf/dbdes.c
@@ -127,7 +127,7 @@ ospf_prepare_dbdes(struct ospf_proto *p, struct ospf_neighbor *n)
{
struct ospf_dbdes2_packet *ps = (void *) pkt;
ps->iface_mtu = htons(iface_mtu);
- ps->options = ifa->oa->options | OPT_O;
+ ps->options = ifa->oa->options & DBDES2_OPT_MASK;
ps->imms = 0; /* Will be set later */
ps->ddseq = htonl(n->dds);
length = sizeof(struct ospf_dbdes2_packet);
@@ -135,7 +135,8 @@ ospf_prepare_dbdes(struct ospf_proto *p, struct ospf_neighbor *n)
else /* OSPFv3 */
{
struct ospf_dbdes3_packet *ps = (void *) pkt;
- ps->options = htonl(ifa->oa->options | (ifa->autype == OSPF_AUTH_CRYPT ? OPT_AT : 0));
+ u32 options = ifa->oa->options | (ifa->autype == OSPF_AUTH_CRYPT ? OPT_AT : 0);
+ ps->options = htonl(options & DBDES3_OPT_MASK);
ps->iface_mtu = htons(iface_mtu);
ps->padding = 0;
ps->imms = 0; /* Will be set later */
@@ -215,7 +216,7 @@ ospf_send_dbdes(struct ospf_proto *p, struct ospf_neighbor *n)
ASSERT((n->state == NEIGHBOR_EXSTART) || (n->state == NEIGHBOR_EXCHANGE));
- if (n->ifa->oa->rt == NULL)
+ if (!n->ifa->oa->rt && !p->gr_recovery)
return;
ospf_prepare_dbdes(p, n);
@@ -279,6 +280,10 @@ ospf_process_dbdes(struct ospf_proto *p, struct ospf_packet *pkt, struct ospf_ne
if (LSA_SCOPE(lsa_type) == LSA_SCOPE_RES)
DROP1("LSA with invalid scope");
+ /* RFC 3623 2.2 (2) special case - check for my router-LSA (GR recovery) */
+ if ((lsa_type == LSA_T_RT) && (lsa.rt == p->router_id))
+ n->got_my_rt_lsa = 1;
+
en = ospf_hash_find(p->gr, lsa_domain, lsa.id, lsa.rt, lsa_type);
if (!en || (lsa_comp(&lsa, &(en->lsa)) == CMP_NEWER))
{
diff --git a/proto/ospf/hello.c b/proto/ospf/hello.c
index 9e427c4f..d094f934 100644
--- a/proto/ospf/hello.c
+++ b/proto/ospf/hello.c
@@ -81,7 +81,7 @@ ospf_send_hello(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn)
ps->netmask = htonl(u32_mkmask(ifa->addr->prefix.pxlen));
ps->helloint = ntohs(ifa->helloint);
- ps->options = ifa->oa->options;
+ ps->options = ifa->oa->options & HELLO2_OPT_MASK;
ps->priority = ifa->priority;
ps->deadint = htonl(ifa->deadint);
ps->dr = htonl(ipa_to_u32(ifa->drip));
@@ -96,7 +96,7 @@ ospf_send_hello(struct ospf_iface *ifa, int kind, struct ospf_neighbor *dirn)
u32 options = ifa->oa->options | (ifa->autype == OSPF_AUTH_CRYPT ? OPT_AT : 0);
ps->iface_id = htonl(ifa->iface_id);
- ps->options = ntohl(options | (ifa->priority << 24));
+ ps->options = ntohl((options & HELLO3_OPT_MASK) | (ifa->priority << 24));
ps->helloint = ntohs(ifa->helloint);
ps->deadint = htons(ifa->deadint);
ps->dr = htonl(ifa->drid);
diff --git a/proto/ospf/iface.c b/proto/ospf/iface.c
index 388c91c8..6e7e498f 100644
--- a/proto/ospf/iface.c
+++ b/proto/ospf/iface.c
@@ -772,6 +772,14 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new)
ifa->cf = new;
ifa->marked = 0;
+ /* Cancel GR peers if GR is disabled */
+ if (!p->gr_mode && p->gr_count)
+ {
+ struct ospf_neighbor *n, *nx;
+ WALK_LIST_DELSAFE(n, nx, ifa->neigh_list)
+ if (n->gr_active)
+ ospf_neigh_cancel_graceful_restart(n);
+ }
/* HELLO TIMER */
if (ifa->helloint != new->helloint)
@@ -872,6 +880,7 @@ ospf_iface_reconfigure(struct ospf_iface *ifa, struct ospf_iface_patt *new)
ifname, ifa->priority, new->priority);
ifa->priority = new->priority;
+ ospf_iface_sm(ifa, ISM_NEICH);
ospf_notify_link_lsa(ifa);
}
diff --git a/proto/ospf/lsalib.c b/proto/ospf/lsalib.c
index 7ddf64e3..7aae96ba 100644
--- a/proto/ospf/lsalib.c
+++ b/proto/ospf/lsalib.c
@@ -12,6 +12,9 @@
#include "lib/fletcher16.h"
+#define HDRLEN sizeof(struct ospf_lsa_header)
+
+
#ifndef CPU_BIG_ENDIAN
void
lsa_hton_hdr(struct ospf_lsa_header *h, struct ospf_lsa_header *n)
@@ -61,7 +64,6 @@ lsa_ntoh_body(void *n, void *h, u16 len)
#endif /* little endian */
-
int
lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa)
{
@@ -96,8 +98,7 @@ lsa_is_acceptable(u32 type, struct ospf_neighbor *n, struct ospf_proto *p)
{
if (ospf_is_v2(p))
{
- if (type == LSA_T_NSSA)
- return !!(n->options & OPT_N);
+ /* Do not check NSSA-LSA here, as OPT_N is only in HELLO packets */
if (lsa_is_opaque(type))
return !!(n->options & OPT_O);
@@ -147,11 +148,13 @@ static const u16 lsa_v2_types[] = {
/* Maps OSPFv2 opaque types to OSPFv3 function codes */
static const u16 opaque_lsa_types[] = {
+ [LSA_OT_GR] = LSA_T_GR,
[LSA_OT_RI] = LSA_T_RI_,
};
/* Maps (subset of) OSPFv3 function codes to OSPFv2 opaque types */
static const u8 opaque_lsa_types_inv[] = {
+ [LSA_T_GR] = LSA_OT_GR,
[LSA_T_RI_] = LSA_OT_RI,
};
@@ -168,7 +171,13 @@ lsa_get_type_domain_(u32 type, u32 id, struct ospf_iface *ifa, u32 *otype, u32 *
uint code;
if (LSA_FUNCTION(type) == LSA_T_OPAQUE_)
if (code = LOOKUP(opaque_lsa_types, id >> 24))
+ {
type = code | LSA_UBIT | LSA_SCOPE(type);
+
+ /* Hack for Grace-LSA: It does not use U-bit for link-scoped LSAs */
+ if (type == (LSA_T_GR | LSA_UBIT))
+ type = LSA_T_GR;
+ }
}
else
{
@@ -196,6 +205,13 @@ lsa_get_type_domain_(u32 type, u32 id, struct ospf_iface *ifa, u32 *otype, u32 *
}
}
+int
+lsa_is_opaque(u32 type)
+{
+ u32 fn = LSA_FUNCTION(type);
+ return LOOKUP(opaque_lsa_types_inv, fn) || (fn == LSA_T_OPAQUE_);
+}
+
u32
lsa_get_opaque_type(u32 type)
{
@@ -267,6 +283,51 @@ lsa_comp(struct ospf_lsa_header *l1, struct ospf_lsa_header *l2)
}
+#define LSA_TLV_LENGTH(tlv) \
+ (sizeof(struct ospf_tlv) + BIRD_ALIGN((tlv)->length, 4))
+
+#define LSA_NEXT_TLV(tlv) \
+ ((struct ospf_tlv *) ((byte *) (tlv) + LSA_TLV_LENGTH(tlv)))
+
+#define LSA_WALK_TLVS(tlv,buf,len) \
+ for(struct ospf_tlv *tlv = (void *) (buf); \
+ (byte *) tlv < (byte *) (buf) + (len); \
+ tlv = LSA_NEXT_TLV(tlv))
+
+struct ospf_tlv *
+lsa_get_tlv(struct top_hash_entry *en, uint type)
+{
+ LSA_WALK_TLVS(tlv, en->lsa_body, en->lsa.length - HDRLEN)
+ if (tlv->type == type)
+ return tlv;
+
+ return NULL;
+}
+
+int
+lsa_validate_tlvs(byte *buf, uint len)
+{
+ byte *pos = buf;
+ byte *end = buf + len;
+
+ while (pos < end)
+ {
+ if ((pos + sizeof(struct ospf_tlv)) > end)
+ return 0;
+
+ struct ospf_tlv *tlv = (void *) pos;
+ uint len = LSA_TLV_LENGTH(tlv);
+
+ if ((pos + len) > end)
+ return 0;
+
+ pos += len;
+ }
+
+ return 1;
+}
+
+
static inline int
lsa_walk_rt2(struct ospf_lsa_rt_walk *rt)
{
@@ -408,7 +469,6 @@ lsa_parse_ext(struct top_hash_entry *en, int ospf2, int af, struct ospf_lsa_ext_
}
}
-#define HDRLEN sizeof(struct ospf_lsa_header)
static int
lsa_validate_rt2(struct ospf_lsa_header *lsa, struct ospf_lsa_rt *body)
@@ -604,6 +664,12 @@ lsa_validate_prefix(struct ospf_lsa_header *lsa, struct ospf_lsa_prefix *body)
}
static int
+lsa_validate_gr(struct ospf_lsa_header *lsa, void *body)
+{
+ return lsa_validate_tlvs(body, lsa->length - HDRLEN);
+}
+
+static int
lsa_validate_ri(struct ospf_lsa_header *lsa UNUSED, struct ospf_lsa_net *body UNUSED)
{
/*
@@ -643,6 +709,8 @@ lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, int ospf2, void *body)
case LSA_T_EXT:
case LSA_T_NSSA:
return lsa_validate_ext2(lsa, body);
+ case LSA_T_GR:
+ return lsa_validate_gr(lsa, body);
case LSA_T_RI_LINK:
case LSA_T_RI_AREA:
case LSA_T_RI_AS:
@@ -674,6 +742,8 @@ lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, int ospf2, void *body)
return lsa_validate_link(lsa, body);
case LSA_T_PREFIX:
return lsa_validate_prefix(lsa, body);
+ case LSA_T_GR:
+ return lsa_validate_gr(lsa, body);
case LSA_T_RI_LINK:
case LSA_T_RI_AREA:
case LSA_T_RI_AS:
diff --git a/proto/ospf/lsalib.h b/proto/ospf/lsalib.h
index af8901ce..eca138d7 100644
--- a/proto/ospf/lsalib.h
+++ b/proto/ospf/lsalib.h
@@ -44,10 +44,7 @@ static inline void lsa_get_type_domain(struct ospf_lsa_header *lsa, struct ospf_
static inline u32 lsa_get_etype(struct ospf_lsa_header *h, struct ospf_proto *p)
{ return ospf_is_v2(p) ? (h->type_raw & LSA_T_V2_MASK) : h->type_raw; }
-/* Assuming OSPFv2 - All U-bit LSAs are mapped to Opaque LSAs */
-static inline int lsa_is_opaque(u32 type)
-{ return !!(type & LSA_UBIT); }
-
+int lsa_is_opaque(u32 type);
u32 lsa_get_opaque_type(u32 type);
int lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa);
int lsa_is_acceptable(u32 type, struct ospf_neighbor *n, struct ospf_proto *p);
@@ -58,6 +55,16 @@ u16 lsa_verify_checksum(const void *lsa_n, int lsa_len);
#define CMP_SAME 0
#define CMP_OLDER -1
int lsa_comp(struct ospf_lsa_header *l1, struct ospf_lsa_header *l2);
+
+struct ospf_tlv * lsa_get_tlv(struct top_hash_entry *en, uint type);
+
+static inline u32
+lsa_get_tlv_u32(struct top_hash_entry *en, uint type)
+{
+ struct ospf_tlv *tlv = lsa_get_tlv(en, type);
+ return (tlv && (tlv->length == 4)) ? tlv->data[0] : 0;
+}
+
void lsa_walk_rt_init(struct ospf_proto *po, struct top_hash_entry *act, struct ospf_lsa_rt_walk *rt);
int lsa_walk_rt(struct ospf_lsa_rt_walk *rt);
void lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, int af, net_addr *net, u8 *pxopts, u32 *metric);
diff --git a/proto/ospf/lsupd.c b/proto/ospf/lsupd.c
index 7318b751..fafe4872 100644
--- a/proto/ospf/lsupd.c
+++ b/proto/ospf/lsupd.c
@@ -185,6 +185,13 @@ static int ospf_flood_lsupd(struct ospf_proto *p, struct top_hash_entry **lsa_li
static void
ospf_enqueue_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_iface *ifa)
{
+ /* Exception for local Grace-LSA, they are flooded synchronously */
+ if ((en->lsa_type == LSA_T_GR) && (en->lsa.rt == p->router_id))
+ {
+ ospf_flood_lsupd(p, &en, 1, 1, ifa);
+ return;
+ }
+
if (ifa->flood_queue_used == ifa->flood_queue_size)
{
/* If we already have full queue, we send some packets */
@@ -591,8 +598,9 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa,
}
/* 13. (5f) - handle self-originated LSAs, see also 13.4. */
- if ((lsa.rt == p->router_id) ||
- (ospf_is_v2(p) && (lsa_type == LSA_T_NET) && ospf_addr_is_local(p, ifa->oa, ipa_from_u32(lsa.id))))
+ if (!p->gr_recovery &&
+ ((lsa.rt == p->router_id) ||
+ (ospf_is_v2(p) && (lsa_type == LSA_T_NET) && ospf_addr_is_local(p, ifa->oa, ipa_from_u32(lsa.id)))))
{
OSPF_TRACE(D_EVENTS, "Received unexpected self-originated LSA");
ospf_advance_lsa(p, en, &lsa, lsa_type, lsa_domain, body);
@@ -629,6 +637,14 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa,
if (lsa_type == LSA_T_LINK)
ospf_notify_net_lsa(ifa);
+ /* RFC 3623 3.1 - entering graceful restart helper mode */
+ if (lsa_type == LSA_T_GR)
+ ospf_neigh_notify_grace_lsa(n, en);
+
+ /* Link received pre-restart router LSA */
+ if (p->gr_recovery && (lsa_type == LSA_T_RT) && (lsa.rt == p->router_id))
+ ifa->oa->rt = en;
+
/* 13. (5b) - flood new LSA */
int flood_back = ospf_flood_lsa(p, en, n);
diff --git a/proto/ospf/neighbor.c b/proto/ospf/neighbor.c
index c143b130..30e80640 100644
--- a/proto/ospf/neighbor.c
+++ b/proto/ospf/neighbor.c
@@ -28,6 +28,8 @@ static void dbdes_timer_hook(timer *t);
static void lsrq_timer_hook(timer *t);
static void lsrt_timer_hook(timer *t);
static void ackd_timer_hook(timer *t);
+static void ospf_neigh_stop_graceful_restart_(struct ospf_neighbor *n);
+static void graceful_restart_timeout(timer *t);
static void
@@ -163,7 +165,7 @@ ospf_neigh_chstate(struct ospf_neighbor *n, u8 state)
if (old_state == NEIGHBOR_FULL)
ifa->fadj--;
- if (ifa->fadj != old_fadj)
+ if ((ifa->fadj != old_fadj) && !n->gr_active)
{
/* RFC 2328 12.4 Event 4 - neighbor enters/leaves Full state */
ospf_notify_rt_lsa(ifa->oa);
@@ -182,6 +184,7 @@ ospf_neigh_chstate(struct ospf_neighbor *n, u8 state)
n->dds++;
n->myimms = DBDES_IMMS;
+ n->got_my_rt_lsa = 0;
tm_start(n->dbdes_timer, 0);
tm_start(n->ackd_timer, ifa->rxmtint S / 2);
@@ -191,9 +194,9 @@ ospf_neigh_chstate(struct ospf_neighbor *n, u8 state)
n->myimms &= ~DBDES_I;
/* Generate NeighborChange event if needed, see RFC 2328 9.2 */
- if ((state == NEIGHBOR_2WAY) && (old_state < NEIGHBOR_2WAY))
+ if ((state == NEIGHBOR_2WAY) && (old_state < NEIGHBOR_2WAY) && !n->gr_active)
ospf_iface_sm(ifa, ISM_NEICH);
- if ((state < NEIGHBOR_2WAY) && (old_state >= NEIGHBOR_2WAY))
+ if ((state < NEIGHBOR_2WAY) && (old_state >= NEIGHBOR_2WAY) && !n->gr_active)
ospf_iface_sm(ifa, ISM_NEICH);
}
@@ -291,6 +294,17 @@ ospf_neigh_sm(struct ospf_neighbor *n, int event)
case INM_KILLNBR:
case INM_LLDOWN:
case INM_INACTTIM:
+ if (n->gr_active && (event == INM_INACTTIM))
+ {
+ /* Just down the neighbor, but do not remove it */
+ reset_lists(p, n);
+ ospf_neigh_chstate(n, NEIGHBOR_DOWN);
+ break;
+ }
+
+ if (n->gr_active)
+ ospf_neigh_stop_graceful_restart_(n);
+
/* No need for reset_lists() */
ospf_neigh_chstate(n, NEIGHBOR_DOWN);
ospf_neigh_down(n);
@@ -356,6 +370,180 @@ can_do_adj(struct ospf_neighbor *n)
return i;
}
+static void
+ospf_neigh_start_graceful_restart(struct ospf_neighbor *n, uint gr_time)
+{
+ struct ospf_proto *p = n->ifa->oa->po;
+
+ OSPF_TRACE(D_EVENTS, "Neighbor %R on %s started graceful restart",
+ n->rid, n->ifa->ifname);
+
+ n->gr_active = 1;
+ p->gr_count++;
+
+ n->gr_timer = tm_new_init(n->pool, graceful_restart_timeout, n, 0, 0);
+ tm_start(n->gr_timer, gr_time S);
+}
+
+static void
+ospf_neigh_stop_graceful_restart_(struct ospf_neighbor *n)
+{
+ struct ospf_proto *p = n->ifa->oa->po;
+ struct ospf_iface *ifa = n->ifa;
+
+ n->gr_active = 0;
+ p->gr_count--;
+
+ rfree(n->gr_timer);
+ n->gr_timer = NULL;
+
+ ospf_notify_rt_lsa(ifa->oa);
+ ospf_notify_net_lsa(ifa);
+
+ if (ifa->type == OSPF_IT_VLINK)
+ ospf_notify_rt_lsa(ifa->voa);
+
+ ospf_iface_sm(ifa, ISM_NEICH);
+}
+
+static void
+ospf_neigh_stop_graceful_restart(struct ospf_neighbor *n)
+{
+ struct ospf_proto *p = n->ifa->oa->po;
+
+ OSPF_TRACE(D_EVENTS, "Neighbor %R on %s finished graceful restart",
+ n->rid, n->ifa->ifname);
+
+ ospf_neigh_stop_graceful_restart_(n);
+}
+
+void
+ospf_neigh_cancel_graceful_restart(struct ospf_neighbor *n)
+{
+ struct ospf_proto *p = n->ifa->oa->po;
+
+ OSPF_TRACE(D_EVENTS, "Graceful restart canceled for nbr %R on %s",
+ n->rid, n->ifa->ifname);
+
+ ospf_neigh_stop_graceful_restart_(n);
+
+ if (n->state == NEIGHBOR_DOWN)
+ ospf_neigh_down(n);
+}
+
+static void
+graceful_restart_timeout(timer *t)
+{
+ struct ospf_neighbor *n = t->data;
+ struct ospf_proto *p = n->ifa->oa->po;
+
+ OSPF_TRACE(D_EVENTS, "Graceful restart timer expired for nbr %R on %s",
+ n->rid, n->ifa->ifname);
+
+ ospf_neigh_stop_graceful_restart_(n);
+
+ if (n->state == NEIGHBOR_DOWN)
+ ospf_neigh_down(n);
+}
+
+static inline int
+changes_in_lsrtl(struct ospf_neighbor *n)
+{
+ /* This could be improved, see RFC 3623 3.1 (2) */
+
+ struct top_hash_entry *en;
+ WALK_SLIST(en, n->lsrtl)
+ if (LSA_FUNCTION(en->lsa_type) <= LSA_FUNCTION(LSA_T_NSSA))
+ return 1;
+
+ return 0;
+}
+
+void
+ospf_neigh_notify_grace_lsa(struct ospf_neighbor *n, struct top_hash_entry *en)
+{
+ struct ospf_iface *ifa = n->ifa;
+ struct ospf_proto *p = ifa->oa->po;
+
+ /* In OSPFv2, neighbors are identified by either IP or Router ID, based on network type */
+ uint t = ifa->type;
+ if (ospf_is_v2(p) && ((t == OSPF_IT_BCAST) || (t == OSPF_IT_NBMA) || (t == OSPF_IT_PTMP)))
+ {
+ struct ospf_tlv *tlv = lsa_get_tlv(en, LSA_GR_ADDRESS);
+ if (!tlv || tlv->length != 4)
+ return;
+
+ ip_addr addr = ipa_from_u32(tlv->data[0]);
+ if (!ipa_equal(n->ip, addr))
+ n = find_neigh_by_ip(ifa, addr);
+ }
+ else
+ {
+ if (n->rid != en->lsa.rt)
+ n = find_neigh(ifa, en->lsa.rt);
+ }
+
+ if (!n)
+ return;
+
+ if (en->lsa.age < LSA_MAXAGE)
+ {
+ u32 period = lsa_get_tlv_u32(en, LSA_GR_PERIOD);
+
+ /* Exception for updating grace period */
+ if (n->gr_active)
+ {
+ tm_start(n->gr_timer, (period S) - (en->lsa.age S));
+ return;
+ }
+
+ /* RFC 3623 3.1 (1) - full adjacency */
+ if (n->state != NEIGHBOR_FULL)
+ return;
+
+ /* RFC 3623 3.1 (2) - no changes in LSADB */
+ if (changes_in_lsrtl(n))
+ return;
+
+ /* RFC 3623 3.1 (3) - grace period not expired */
+ if (en->lsa.age >= period)
+ return;
+
+ /* RFC 3623 3.1 (4) - helper mode allowed */
+ if (!p->gr_mode)
+ return;
+
+ /* RFC 3623 3.1 (5) - no local graceful restart */
+ if (p->p.gr_recovery)
+ return;
+
+ ospf_neigh_start_graceful_restart(n, period - en->lsa.age);
+ }
+ else /* Grace-LSA is flushed */
+ {
+ if (n->gr_active)
+ ospf_neigh_stop_graceful_restart(n);
+ }
+}
+
+void
+ospf_neigh_lsadb_changed_(struct ospf_proto *p, struct top_hash_entry *en)
+{
+ struct ospf_iface *ifa;
+ struct ospf_neighbor *n, *nx;
+
+ if (LSA_FUNCTION(en->lsa_type) > LSA_FUNCTION(LSA_T_NSSA))
+ return;
+
+ /* RFC 3623 3.2 (3) - cancel graceful restart when LSdb changed */
+ WALK_LIST(ifa, p->iface_list)
+ if (lsa_flooding_allowed(en->lsa_type, en->domain, ifa))
+ WALK_LIST_DELSAFE(n, nx, ifa->neigh_list)
+ if (n->gr_active)
+ ospf_neigh_cancel_graceful_restart(n);
+}
+
+
static inline u32 neigh_get_id(struct ospf_proto *p, struct ospf_neighbor *n)
{ return ospf_is_v2(p) ? ipa_to_u32(n->ip) : n->rid; }
@@ -583,8 +771,11 @@ ospf_neigh_bfd_hook(struct bfd_request *req)
void
ospf_neigh_update_bfd(struct ospf_neighbor *n, int use_bfd)
{
+ struct ospf_proto *p = n->ifa->oa->po;
+
if (use_bfd && !n->bfd_req)
- n->bfd_req = bfd_request_session(n->pool, n->ip, n->ifa->addr->ip, n->ifa->iface,
+ n->bfd_req = bfd_request_session(n->pool, n->ip, n->ifa->addr->ip,
+ n->ifa->iface, p->p.vrf,
ospf_neigh_bfd_hook, n);
if (!use_bfd && n->bfd_req)
@@ -666,7 +857,7 @@ ospf_sh_neigh_info(struct ospf_neighbor *n)
pos = "Other";
}
- cli_msg(-1013, "%-1R\t%3u\t%s/%s\t%7t\t%-10s %-1I",
+ cli_msg(-1013, "%-12R\t%3u\t%s/%s\t%6t\t%-10s %I",
n->rid, n->priority, ospf_ns_names[n->state], pos,
tm_remains(n->inactim), ifa->ifname, n->ip);
}
diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c
index ef2a0df4..63ff9e56 100644
--- a/proto/ospf/ospf.c
+++ b/proto/ospf/ospf.c
@@ -92,7 +92,9 @@
* - RFC 2328 - main OSPFv2 standard
* - RFC 5340 - main OSPFv3 standard
* - RFC 3101 - OSPFv2 NSSA areas
+ * - RFC 3623 - OSPFv2 Graceful Restart
* - RFC 4576 - OSPFv2 VPN loop prevention
+ * - RFC 5187 - OSPFv3 Graceful Restart
* - RFC 5250 - OSPFv2 Opaque LSAs
* - RFC 5709 - OSPFv2 HMAC-SHA Cryptographic Authentication
* - RFC 5838 - OSPFv3 Support of Address Families
@@ -106,8 +108,8 @@
#include "ospf.h"
static int ospf_preexport(struct proto *P, rte **new, struct linpool *pool);
-static struct ea_list *ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool);
-static void ospf_store_tmp_attrs(struct rte *rt);
+static void ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool);
+static void ospf_store_tmp_attrs(struct rte *rt, struct linpool *pool);
static void ospf_reload_routes(struct channel *C);
static int ospf_rte_better(struct rte *new, struct rte *old);
static int ospf_rte_same(struct rte *new, struct rte *old);
@@ -144,7 +146,7 @@ static inline uint
ospf_opts(struct ospf_proto *p)
{
if (ospf_is_v2(p))
- return 0;
+ return OPT_O;
return ((ospf_is_ip6(p) && !p->af_mc) ? OPT_V6 : 0) |
(!p->stub_router ? OPT_R : 0) | (p->af_ext ? OPT_AF : 0);
@@ -207,7 +209,6 @@ ospf_area_remove(struct ospf_area *oa)
mb_free(oa);
}
-
struct ospf_area *
ospf_find_area(struct ospf_proto *p, u32 aid)
{
@@ -228,6 +229,52 @@ ospf_find_vlink(struct ospf_proto *p, u32 voa, u32 vid)
return NULL;
}
+static void
+ospf_start_gr_recovery(struct ospf_proto *p)
+{
+ OSPF_TRACE(D_EVENTS, "Graceful restart started");
+
+ p->gr_recovery = 1;
+ p->gr_timeout = current_time() + (p->gr_time S);
+ channel_graceful_restart_lock(p->p.main_channel);
+ p->p.main_channel->gr_wait = 1;
+
+ /* NOTE: We should get end of grace period from non-volatile storage */
+}
+
+void
+ospf_stop_gr_recovery(struct ospf_proto *p)
+{
+ p->gr_recovery = 0;
+ p->gr_cleanup = 1;
+ p->gr_timeout = 0;
+
+ /* Reorigination of router/network LSAs is already scheduled */
+
+ /* Rest is done in ospf_cleanup_gr_recovery() */
+}
+
+static void
+ospf_cleanup_gr_recovery(struct ospf_proto *p)
+{
+ struct top_hash_entry *en;
+
+ /* Flush dirty LSAa except external ones, these will be handled by feed */
+ WALK_SLIST(en, p->lsal)
+ if (en->gr_dirty)
+ {
+ if ((en->lsa_type == LSA_T_EXT) || (en->lsa_type == LSA_T_NSSA))
+ en->mode = LSA_M_EXPORT;
+ else
+ ospf_flush_lsa(p, en);
+ }
+
+ /* End graceful restart on channel, will also schedule feed */
+ channel_graceful_restart_unlock(p->p.main_channel);
+
+ p->gr_cleanup = 0;
+}
+
static int
ospf_start(struct proto *P)
{
@@ -246,6 +293,8 @@ ospf_start(struct proto *P)
p->asbr = c->asbr;
p->vpn_pe = c->vpn_pe;
p->ecmp = c->ecmp;
+ p->gr_mode = c->gr_mode;
+ p->gr_time = c->gr_time;
p->tick = c->tick;
p->disp_timer = tm_new_init(P->pool, ospf_disp, p, p->tick S, 0);
tm_start(p->disp_timer, 100 MS);
@@ -267,6 +316,10 @@ ospf_start(struct proto *P)
p->log_pkt_tbf = (struct tbf){ .rate = 1, .burst = 5 };
p->log_lsa_tbf = (struct tbf){ .rate = 4, .burst = 20 };
+ /* Lock the channel when in GR recovery mode */
+ if (p->p.gr_recovery && (p->gr_mode == OSPF_GR_ABLE))
+ ospf_start_gr_recovery(p);
+
WALK_LIST(ac, c->area_list)
ospf_area_add(p, ac);
@@ -323,6 +376,8 @@ ospf_init(struct proto_config *CF)
P->ifa_notify = cf->ospf2 ? ospf_ifa_notify2 : ospf_ifa_notify3;
P->preexport = ospf_preexport;
P->reload_routes = ospf_reload_routes;
+ P->feed_begin = ospf_feed_begin;
+ P->feed_end = ospf_feed_end;
P->make_tmp_attrs = ospf_make_tmp_attrs;
P->store_tmp_attrs = ospf_store_tmp_attrs;
P->rte_better = ospf_rte_better;
@@ -364,35 +419,6 @@ ospf_rte_same(struct rte *new, struct rte *old)
new->u.ospf.router_id == old->u.ospf.router_id;
}
-static ea_list *
-ospf_build_attrs(ea_list * next, struct linpool *pool, u32 m1, u32 m2,
- u32 tag, u32 rid)
-{
- struct ea_list *l =
- lp_alloc(pool, sizeof(struct ea_list) + 4 * sizeof(eattr));
-
- l->next = next;
- l->flags = EALF_SORTED;
- l->count = 4;
- l->attrs[0].id = EA_OSPF_METRIC1;
- l->attrs[0].flags = 0;
- l->attrs[0].type = EAF_TYPE_INT | EAF_TEMP;
- l->attrs[0].u.data = m1;
- l->attrs[1].id = EA_OSPF_METRIC2;
- l->attrs[1].flags = 0;
- l->attrs[1].type = EAF_TYPE_INT | EAF_TEMP;
- l->attrs[1].u.data = m2;
- l->attrs[2].id = EA_OSPF_TAG;
- l->attrs[2].flags = 0;
- l->attrs[2].type = EAF_TYPE_INT | EAF_TEMP;
- l->attrs[2].u.data = tag;
- l->attrs[3].id = EA_OSPF_ROUTER_ID;
- l->attrs[3].flags = 0;
- l->attrs[3].type = EAF_TYPE_ROUTER_ID | EAF_TEMP;
- l->attrs[3].u.data = rid;
- return l;
-}
-
void
ospf_schedule_rtcalc(struct ospf_proto *p)
@@ -427,6 +453,10 @@ ospf_disp(timer * timer)
{
struct ospf_proto *p = timer->data;
+ /* Check for end of graceful restart */
+ if (p->gr_recovery)
+ ospf_update_gr_recovery(p);
+
/* Originate or flush local topology LSAs */
ospf_update_topology(p);
@@ -436,6 +466,10 @@ ospf_disp(timer * timer)
/* Calculate routing table */
if (p->calcrt)
ospf_rt_spf(p);
+
+ /* Cleanup after graceful restart */
+ if (p->gr_cleanup)
+ ospf_cleanup_gr_recovery(p);
}
@@ -467,20 +501,24 @@ ospf_preexport(struct proto *P, rte **new, struct linpool *pool UNUSED)
return 0;
}
-static struct ea_list *
+static void
ospf_make_tmp_attrs(struct rte *rt, struct linpool *pool)
{
- return ospf_build_attrs(NULL, pool, rt->u.ospf.metric1, rt->u.ospf.metric2,
- rt->u.ospf.tag, rt->u.ospf.router_id);
+ rte_init_tmp_attrs(rt, pool, 4);
+ rte_make_tmp_attr(rt, EA_OSPF_METRIC1, EAF_TYPE_INT, rt->u.ospf.metric1);
+ rte_make_tmp_attr(rt, EA_OSPF_METRIC2, EAF_TYPE_INT, rt->u.ospf.metric2);
+ rte_make_tmp_attr(rt, EA_OSPF_TAG, EAF_TYPE_INT, rt->u.ospf.tag);
+ rte_make_tmp_attr(rt, EA_OSPF_ROUTER_ID, EAF_TYPE_ROUTER_ID, rt->u.ospf.router_id);
}
static void
-ospf_store_tmp_attrs(struct rte *rt)
+ospf_store_tmp_attrs(struct rte *rt, struct linpool *pool)
{
- rt->u.ospf.metric1 = ea_get_int(rt->attrs->eattrs, EA_OSPF_METRIC1, LSINFINITY);
- rt->u.ospf.metric2 = ea_get_int(rt->attrs->eattrs, EA_OSPF_METRIC2, 10000);
- rt->u.ospf.tag = ea_get_int(rt->attrs->eattrs, EA_OSPF_TAG, 0);
- rt->u.ospf.router_id = ea_get_int(rt->attrs->eattrs, EA_OSPF_ROUTER_ID, 0);
+ rte_init_tmp_attrs(rt, pool, 4);
+ rt->u.ospf.metric1 = rte_store_tmp_attr(rt, EA_OSPF_METRIC1);
+ rt->u.ospf.metric2 = rte_store_tmp_attr(rt, EA_OSPF_METRIC2);
+ rt->u.ospf.tag = rte_store_tmp_attr(rt, EA_OSPF_TAG);
+ rt->u.ospf.router_id = rte_store_tmp_attr(rt, EA_OSPF_ROUTER_ID);
}
/**
@@ -500,9 +538,18 @@ ospf_shutdown(struct proto *P)
OSPF_TRACE(D_EVENTS, "Shutdown requested");
- /* And send to all my neighbors 1WAY */
- WALK_LIST(ifa, p->iface_list)
- ospf_iface_shutdown(ifa);
+ if ((P->down_code == PDC_CMD_GR_DOWN) && (p->gr_mode == OSPF_GR_ABLE))
+ {
+ /* Originate Grace LSAs */
+ WALK_LIST(ifa, p->iface_list)
+ ospf_originate_gr_lsa(p, ifa);
+ }
+ else
+ {
+ /* Send to all my neighbors 1WAY */
+ WALK_LIST(ifa, p->iface_list)
+ ospf_iface_shutdown(ifa);
+ }
/* Cleanup locked rta entries */
FIB_WALK(&p->rtf, ort, nf)
@@ -689,6 +736,8 @@ ospf_reconfigure(struct proto *P, struct proto_config *CF)
p->merge_external = new->merge_external;
p->asbr = new->asbr;
p->ecmp = new->ecmp;
+ p->gr_mode = new->gr_mode;
+ p->gr_time = new->gr_time;
p->tick = new->tick;
p->disp_timer->recurrent = p->tick S;
tm_start(p->disp_timer, 10 MS);
@@ -756,7 +805,7 @@ ospf_sh_neigh(struct proto *P, char *iff)
}
cli_msg(-1013, "%s:", p->p.name);
- cli_msg(-1013, "%-12s\t%3s\t%-15s\t%-5s\t%-10s %-12s", "Router ID", "Pri",
+ cli_msg(-1013, "%-12s\t%3s\t%-15s\t%-5s\t%-10s %s", "Router ID", "Pri",
" State", "DTime", "Interface", "Router IP");
WALK_LIST(ifa, p->iface_list)
if ((iff == NULL) || patmatch(iff, ifa->ifname))
@@ -1402,7 +1451,7 @@ lsa_compare_for_lsadb(const void *p1, const void *p2)
void
ospf_sh_lsadb(struct lsadb_show_data *ld)
{
- struct ospf_proto *p = (struct ospf_proto *) proto_get_named(ld->name, &proto_ospf);
+ struct ospf_proto *p = ld->proto;
uint num = p->gr->hash_entries;
uint i, j;
int last_dscope = -1;
diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h
index 7fac47c8..8318ee95 100644
--- a/proto/ospf/ospf.h
+++ b/proto/ospf/ospf.h
@@ -75,6 +75,7 @@
#define OSPF_DEFAULT_TICK 1
#define OSPF_DEFAULT_STUB_COST 1000
#define OSPF_DEFAULT_ECMP_LIMIT 16
+#define OSPF_DEFAULT_GR_TIME 120
#define OSPF_DEFAULT_TRANSINT 40
#define OSPF_MIN_PKT_SIZE 256
@@ -82,6 +83,9 @@
#define OSPF_VLINK_ID_OFFSET 0x80000000
+#define OSPF_GR_ABLE 1
+#define OSPF_GR_AWARE 2
+
struct ospf_config
{
struct proto_config c;
@@ -97,7 +101,9 @@ struct ospf_config
u8 abr;
u8 asbr;
u8 vpn_pe;
- int ecmp;
+ u8 gr_mode; /* Graceful restart mode (OSPF_GR_*) */
+ uint gr_time; /* Graceful restart interval */
+ uint ecmp;
list area_list; /* list of area configs (struct ospf_area_config) */
list vlink_list; /* list of configured vlinks (struct ospf_iface_patt) */
};
@@ -216,6 +222,10 @@ struct ospf_proto
list area_list; /* List of OSPF areas (struct ospf_area) */
int areano; /* Number of area I belong to */
int padj; /* Number of neighbors in Exchange or Loading state */
+ int gr_count; /* Number of neighbors in graceful restart state */
+ u8 gr_recovery; /* Graceful restart recovery is active */
+ u8 gr_cleanup; /* GR cleanup scheduled */
+ btime gr_timeout; /* The end time of grace restart recovery */
struct fib rtf; /* Routing table */
struct idm idm; /* OSPFv3 LSA ID map */
u8 ospf2; /* OSPF v2 or v3 */
@@ -228,6 +238,8 @@ struct ospf_proto
u8 asbr; /* May i originate any ext/NSSA lsa? */
u8 vpn_pe; /* Should we do VPN PE specific behavior (RFC 4577)? */
u8 ecmp; /* Maximal number of nexthops in ECMP route, or 0 */
+ u8 gr_mode; /* Graceful restart mode (OSPF_GR_*) */
+ uint gr_time; /* Graceful restart interval */
u64 csn64; /* Last used cryptographic sequence number */
struct ospf_area *backbone; /* If exists */
event *flood_event; /* Event for flooding LS updates */
@@ -346,6 +358,8 @@ struct ospf_neighbor
pool *pool;
struct ospf_iface *ifa;
u8 state;
+ u8 gr_active; /* We act as GR helper for the neighbor */
+ u8 got_my_rt_lsa; /* Received my Rt-LSA in DBDES exchanged */
timer *inactim; /* Inactivity timer */
u8 imms; /* I, M, Master/slave received */
u8 myimms; /* I, M Master/slave */
@@ -388,6 +402,7 @@ struct ospf_neighbor
#define ACKL_DIRECT 0
#define ACKL_DELAY 1
timer *ackd_timer; /* Delayed ack timer */
+ timer *gr_timer; /* Graceful restart timer, non-NULL only if gr_active */
struct bfd_request *bfd_req; /* BFD request, if BFD is used */
void *ldd_buffer; /* Last database description packet */
u32 ldd_bsize; /* Buffer size for ldd_buffer */
@@ -469,11 +484,17 @@ struct ospf_neighbor
#define OPT_R 0x0010 /* OSPFv3, originator is active router */
#define OPT_DC 0x0020 /* Related to demand circuits, not used */
#define OPT_O 0x0040 /* OSPFv2 Opaque LSA (RFC 5250) */
-#define OPT_DN 0x0080 /* OSPFv2 VPN loop prevention (RFC 4576)*/
+#define OPT_DN 0x0080 /* OSPFv2 VPN loop prevention (RFC 4576) */
#define OPT_AF 0x0100 /* OSPFv3 Address Families (RFC 5838) */
#define OPT_L_V3 0x0200 /* OSPFv3, link-local signaling */
#define OPT_AT 0x0400 /* OSPFv3, authentication trailer */
+#define HELLO2_OPT_MASK (OPT_E | OPT_N | OPT_L_V2)
+#define DBDES2_OPT_MASK (OPT_E | OPT_L_V2 | OPT_O)
+
+#define HELLO3_OPT_MASK (OPT_V6 | OPT_E | OPT_N | OPT_R | OPT_AF | OPT_L_V3 | OPT_AT )
+#define DBDES3_OPT_MASK (OPT_V6 | OPT_E | OPT_R | OPT_AF | OPT_L_V3 | OPT_AT )
+
/* Router-LSA VEB flags are are stored together with links (OSPFv2) or options (OSPFv3) */
#define OPT_RT_B (0x01 << 24)
#define OPT_RT_E (0x02 << 24)
@@ -555,6 +576,7 @@ struct ospf_auth3
#define LSA_T_NSSA 0x2007
#define LSA_T_LINK 0x0008
#define LSA_T_PREFIX 0x2009
+#define LSA_T_GR 0x000B
#define LSA_T_RI_ 0x000C
#define LSA_T_RI_LINK 0x800C
#define LSA_T_RI_AREA 0xA00C
@@ -569,6 +591,7 @@ struct ospf_auth3
/* OSPFv2 Opaque LSA Types */
/* https://www.iana.org/assignments/ospf-opaque-types/ospf-opaque-types.xhtml#ospf-opaque-types-2 */
+#define LSA_OT_GR 0x03
#define LSA_OT_RI 0x04
#define LSA_FUNCTION_MASK 0x1FFF
@@ -613,6 +636,12 @@ struct ospf_auth3
#define LSA_EXT3_FBIT 0x02000000
#define LSA_EXT3_TBIT 0x01000000
+/* OSPF Grace LSA (GR) TLVs */
+/* https://www.iana.org/assignments/ospfv2-parameters/ospfv2-parameters.xhtml#ospfv2-parameters-13 */
+#define LSA_GR_PERIOD 1
+#define LSA_GR_REASON 2
+#define LSA_GR_ADDRESS 3
+
/* OSPF Router Information (RI) TLVs */
/* https://www.iana.org/assignments/ospf-parameters/ospf-parameters.xhtml#ri-tlv */
#define LSA_RI_RIC 1
@@ -900,7 +929,7 @@ struct ospf_lsreq_header
#define SH_ROUTER_SELF 0xffffffff
struct lsadb_show_data {
- struct symbol *name; /* Protocol to request data from */
+ struct ospf_proto *proto; /* Protocol to request data from */
u16 type; /* LSA Type, 0 -> all */
u16 scope; /* Scope, 0 -> all, hack to handle link scope as 1 */
u32 area; /* Specified for area scope */
@@ -959,6 +988,8 @@ static inline int oa_is_ext(struct ospf_area *oa)
static inline int oa_is_nssa(struct ospf_area *oa)
{ return oa->options & OPT_N; }
+void ospf_stop_gr_recovery(struct ospf_proto *p);
+
void ospf_sh_neigh(struct proto *P, char *iff);
void ospf_sh(struct proto *P);
void ospf_sh_iface(struct proto *P, char *iff);
@@ -990,12 +1021,18 @@ static inline struct nbma_node * find_nbma_node(struct ospf_iface *ifa, ip_addr
/* neighbor.c */
struct ospf_neighbor *ospf_neighbor_new(struct ospf_iface *ifa);
void ospf_neigh_sm(struct ospf_neighbor *n, int event);
+void ospf_neigh_cancel_graceful_restart(struct ospf_neighbor *n);
+void ospf_neigh_notify_grace_lsa(struct ospf_neighbor *n, struct top_hash_entry *en);
+void ospf_neigh_lsadb_changed_(struct ospf_proto *p, struct top_hash_entry *en);
void ospf_dr_election(struct ospf_iface *ifa);
struct ospf_neighbor *find_neigh(struct ospf_iface *ifa, u32 rid);
struct ospf_neighbor *find_neigh_by_ip(struct ospf_iface *ifa, ip_addr ip);
void ospf_neigh_update_bfd(struct ospf_neighbor *n, int use_bfd);
void ospf_sh_neigh_info(struct ospf_neighbor *n);
+static inline void ospf_neigh_lsadb_changed(struct ospf_proto *p, struct top_hash_entry *en)
+{ if (p->gr_count) ospf_neigh_lsadb_changed_(p, en); }
+
/* packet.c */
void ospf_pkt_fill_hdr(struct ospf_iface *ifa, void *buf, u8 h_type);
int ospf_rx_hook(sock * sk, uint size);
diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c
index c580d06e..b5787b54 100644
--- a/proto/ospf/rt.c
+++ b/proto/ospf/rt.c
@@ -10,7 +10,7 @@
#include "ospf.h"
-static void add_cand(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry *par, u32 dist, int i, uint lif, uint nif);
+static void add_cand(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry *par, u32 dist, int i, uint data, uint lif, uint nif);
static void rt_sync(struct ospf_proto *p);
@@ -392,6 +392,40 @@ px_pos_to_ifa(struct ospf_area *oa, int pos)
return NULL;
}
+static inline struct ospf_iface *
+rt_find_iface2(struct ospf_area *oa, uint data)
+{
+ ip_addr addr = ipa_from_u32(data);
+
+ /* We should handle it differently for unnumbered PTP links */
+ struct ospf_iface *ifa;
+ WALK_LIST(ifa, oa->po->iface_list)
+ if ((ifa->oa == oa) && ifa->addr && (ipa_equal(ifa->addr->ip, addr)))
+ return ifa;
+
+ return NULL;
+}
+
+static inline struct ospf_iface *
+rt_find_iface3(struct ospf_area *oa, uint lif)
+{
+ struct ospf_iface *ifa;
+ WALK_LIST(ifa, oa->po->iface_list)
+ if ((ifa->oa == oa) && (ifa->iface_id == lif))
+ return ifa;
+
+ return NULL;
+}
+
+static struct ospf_iface *
+rt_find_iface(struct ospf_area *oa, int pos, uint data, uint lif)
+{
+ if (0)
+ return rt_pos_to_ifa(oa, pos);
+ else
+ return ospf_is_v2(oa->po) ? rt_find_iface2(oa, data) : rt_find_iface3(oa, lif);
+}
+
static void
add_network(struct ospf_area *oa, net_addr *net, int metric, struct top_hash_entry *en, int pos)
@@ -402,8 +436,6 @@ add_network(struct ospf_area *oa, net_addr *net, int metric, struct top_hash_ent
.type = RTS_OSPF,
.options = 0,
.metric1 = metric,
- .metric2 = LSINFINITY,
- .tag = 0,
.rid = en->lsa.rt,
.oa = oa,
.nhs = en->nhs
@@ -459,8 +491,6 @@ spfa_process_rt(struct ospf_proto *p, struct ospf_area *oa, struct top_hash_entr
.type = RTS_OSPF,
.options = rt->options,
.metric1 = act->dist,
- .metric2 = LSINFINITY,
- .tag = 0,
.rid = act->lsa.rt,
.oa = oa,
.nhs = act->nhs
@@ -507,7 +537,7 @@ spfa_process_rt(struct ospf_proto *p, struct ospf_area *oa, struct top_hash_entr
break;
}
- add_cand(oa, tmp, act, act->dist + rtl.metric, i, rtl.lif, rtl.nif);
+ add_cand(oa, tmp, act, act->dist + rtl.metric, i, rtl.data, rtl.lif, rtl.nif);
}
}
@@ -530,7 +560,7 @@ spfa_process_net(struct ospf_proto *p, struct ospf_area *oa, struct top_hash_ent
for (i = 0; i < cnt; i++)
{
tmp = ospf_hash_find_rt(p->gr, oa->areaid, ln->routers[i]);
- add_cand(oa, tmp, act, act->dist, -1, 0, 0);
+ add_cand(oa, tmp, act, act->dist, -1, 0, 0, 0);
}
}
@@ -823,8 +853,6 @@ ospf_rt_sum(struct ospf_area *oa)
.type = RTS_OSPF_IA,
.options = options,
.metric1 = abr->n.metric1 + metric,
- .metric2 = LSINFINITY,
- .tag = 0,
.rid = en->lsa.rt, /* ABR ID */
.oa = oa,
.nhs = abr->n.nhs
@@ -1563,7 +1591,7 @@ ospf_ext_spf(struct ospf_proto *p)
{
nfa.type = RTS_OSPF_EXT1;
nfa.metric1 = br_metric + rt.metric;
- nfa.metric2 = LSINFINITY;
+ nfa.metric2 = 0;
}
/* Mark the LSA as reachable */
@@ -1612,7 +1640,7 @@ ospf_rt_reset(struct ospf_proto *p)
en->lb = IPA_NONE;
if (en->mode == LSA_M_RTCALC)
- en->mode = LSA_M_STALE;
+ en->mode = LSA_M_RTCALC_STALE;
}
WALK_LIST(oa, p->area_list)
@@ -1714,7 +1742,7 @@ link_lsa_lladdr(struct ospf_proto *p, struct top_hash_entry *en)
static struct nexthop *
calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en,
- struct top_hash_entry *par, int pos, uint lif, uint nif)
+ struct top_hash_entry *par, int pos, uint data, uint lif, uint nif)
{
struct ospf_proto *p = oa->po;
struct nexthop *pn = par->nhs;
@@ -1741,7 +1769,7 @@ calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en,
/* The first case - local network */
if ((en->lsa_type == LSA_T_NET) && (par == oa->rt))
{
- ifa = rt_pos_to_ifa(oa, pos);
+ ifa = rt_find_iface(oa, pos, data, lif);
if (!ifa)
return NULL;
@@ -1754,7 +1782,7 @@ calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en,
/* The second case - ptp or ptmp neighbor */
if ((en->lsa_type == LSA_T_RT) && (par == oa->rt))
{
- ifa = rt_pos_to_ifa(oa, pos);
+ ifa = rt_find_iface(oa, pos, data, lif);
if (!ifa)
return NULL;
@@ -1844,7 +1872,7 @@ calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en,
/* Add LSA into list of candidates in Dijkstra's algorithm */
static void
add_cand(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry *par,
- u32 dist, int pos, uint lif, uint nif)
+ u32 dist, int pos, uint data, uint lif, uint nif)
{
struct ospf_proto *p = oa->po;
node *prev, *n;
@@ -1877,7 +1905,7 @@ add_cand(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry
if (!link_back(oa, en, par, lif, nif))
return;
- struct nexthop *nhs = calc_next_hop(oa, en, par, pos, lif, nif);
+ struct nexthop *nhs = calc_next_hop(oa, en, par, pos, data, lif, nif);
if (!nhs)
{
log(L_WARN "%s: Cannot find next hop for LSA (Type: %04x, Id: %R, Rt: %R)",
@@ -2033,7 +2061,14 @@ again1:
e->u.ospf.metric2 = nf->old_metric2 = nf->n.metric2;
e->u.ospf.tag = nf->old_tag = nf->n.tag;
e->u.ospf.router_id = nf->old_rid = nf->n.rid;
- e->pflags = 0;
+ e->pflags = EA_ID_FLAG(EA_OSPF_METRIC1) | EA_ID_FLAG(EA_OSPF_ROUTER_ID);
+
+ if (nf->n.type == RTS_OSPF_EXT2)
+ e->pflags |= EA_ID_FLAG(EA_OSPF_METRIC2);
+
+ /* Perhaps onfly if tag is non-zero? */
+ if ((nf->n.type == RTS_OSPF_EXT1) || (nf->n.type == RTS_OSPF_EXT2))
+ e->pflags |= EA_ID_FLAG(EA_OSPF_TAG);
DBG("Mod rte type %d - %N via %I on iface %s, met %d\n",
a0.source, nf->fn.addr, a0.gw, a0.iface ? a0.iface->name : "(none)", nf->n.metric1);
@@ -2082,6 +2117,136 @@ again2:
/* Cleanup stale LSAs */
WALK_SLIST(en, p->lsal)
- if (en->mode == LSA_M_STALE)
+ if (en->mode == LSA_M_RTCALC_STALE)
ospf_flush_lsa(p, en);
}
+
+
+/* RFC 3623 2.2 - checking for graceful restart termination conditions */
+void
+ospf_update_gr_recovery(struct ospf_proto *p)
+{
+ struct top_hash_entry *rt, *net, *nbr;
+ struct ospf_lsa_rt_walk rtl;
+ struct ospf_neighbor *n;
+ struct ospf_iface *ifa;
+ struct ospf_area *oa;
+ const char *err_dsc = NULL;
+ uint i, j, missing = 0, err_val = 0;
+
+ /*
+ * We check here for three cases:
+ * RFC 3623 2.2 (1) - success when all adjacencies are established
+ * RFC 3623 2.2 (2) - failure when inconsistent LSA was received
+ * RFC 3623 2.2 (3) - grace period timeout
+ *
+ * It is handled by processing pre-restart local router-LSA and adjacent
+ * network-LSAs, checking neighbor association for referenced routers (1)
+ * and checking back links from their router-LSAs (2).
+ *
+ * TODO: Use timer for grace period timeout. We avoided that as function
+ * ospf_stop_gr_recovery() called from ospf_disp() makes ending of graceful
+ * restart uninterrupted by other events.
+ */
+
+ #define CONTINUE { missing++; continue; }
+
+ if (current_time() > p->gr_timeout)
+ goto timeout;
+
+ WALK_LIST(oa, p->area_list)
+ {
+ /* Get the router-LSA */
+ rt = oa->rt;
+ if (!rt || (rt->lsa.age == LSA_MAXAGE))
+ CONTINUE;
+
+ for (lsa_walk_rt_init(p, rt, &rtl), i = 0; lsa_walk_rt(&rtl); i++)
+ {
+ if (rtl.type == LSART_STUB)
+ continue;
+
+ ifa = rt_find_iface(oa, i, rtl.data, rtl.lif);
+ if (!ifa)
+ DROP("inconsistent interface", ospf_is_v2(p) ? rtl.data : rtl.lif);
+
+ switch (rtl.type)
+ {
+ case LSART_NET:
+ /* Find the network-LSA */
+ net = ospf_hash_find_net(p->gr, oa->areaid, rtl.id, rtl.nif);
+ if (!net)
+ CONTINUE;
+
+ if (!link_back(oa, net, rt, rtl.lif, rtl.nif))
+ DROP("Inconsistent network-LSA", net->lsa.id);
+
+ if (ifa->state == OSPF_IS_DR)
+ {
+ /* Find all neighbors from the network-LSA */
+ struct ospf_lsa_net *net_body = net->lsa_body;
+ uint cnt = lsa_net_count(&net->lsa);
+ for (j = 0; j < cnt; i++)
+ {
+ n = find_neigh(ifa, net_body->routers[j]);
+ if (!n || (n->state != NEIGHBOR_FULL))
+ CONTINUE;
+
+ if (!n->got_my_rt_lsa)
+ DROP("not received my router-LSA", n->rid);
+
+ nbr = ospf_hash_find_rt(p->gr, oa->areaid, n->rid);
+ if (!link_back(oa, nbr, net, 0, 0))
+ DROP("inconsistent router-LSA", n->rid);
+ }
+ }
+ else
+ {
+ /* Find the DR (by IP for OSPFv2) */
+ n = ospf_is_v2(p) ?
+ find_neigh_by_ip(ifa, ipa_from_u32(rtl.id)) :
+ find_neigh(ifa, rtl.id);
+ if (!n || (n->state != NEIGHBOR_FULL))
+ CONTINUE;
+
+ if (!n->got_my_rt_lsa)
+ DROP("not received my router-LSA", n->rid);
+ }
+ break;
+
+ case LSART_VLNK:
+ case LSART_PTP:
+ /* Find the PtP peer */
+ n = find_neigh(ifa, rtl.id);
+ if (!n || (n->state != NEIGHBOR_FULL))
+ CONTINUE;
+
+ if (!n->got_my_rt_lsa)
+ DROP("not received my router-LSA", n->rid);
+
+ nbr = ospf_hash_find_rt(p->gr, oa->areaid, rtl.id);
+ if (!link_back(oa, nbr, rt, rtl.lif, rtl.nif))
+ DROP("inconsistent router-LSA", rtl.id);
+ }
+ }
+ }
+
+ #undef CONTINUE
+
+ if (missing)
+ return;
+
+ OSPF_TRACE(D_EVENTS, "Graceful restart finished");
+ ospf_stop_gr_recovery(p);
+ return;
+
+drop:
+ log(L_INFO "%s: Graceful restart ended - %s (%R)", p->p.name, err_dsc, err_val);
+ ospf_stop_gr_recovery(p);
+ return;
+
+timeout:
+ log(L_INFO "%s: Graceful restart ended - grace period expired", p->p.name);
+ ospf_stop_gr_recovery(p);
+ return;
+}
diff --git a/proto/ospf/rt.h b/proto/ospf/rt.h
index 589d2bc5..094e125b 100644
--- a/proto/ospf/rt.h
+++ b/proto/ospf/rt.h
@@ -130,6 +130,7 @@ static inline int rt_is_nssa(ort *nf)
void ospf_rt_spf(struct ospf_proto *p);
void ospf_rt_initort(struct fib_node *fn);
+void ospf_update_gr_recovery(struct ospf_proto *p);
#endif /* _BIRD_OSPF_RT_H_ */
diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c
index 7d5deca0..2e9c3965 100644
--- a/proto/ospf/topology.c
+++ b/proto/ospf/topology.c
@@ -71,6 +71,7 @@ ospf_install_lsa(struct ospf_proto *p, struct ospf_lsa_header *lsa, u32 type, u3
en->lsa = *lsa;
en->init_age = en->lsa.age;
en->inst_time = current_time();
+ en->gr_dirty = p->gr_recovery && (lsa->rt == p->router_id);
/*
* We do not set en->mode. It is either default LSA_M_BASIC, or in a special
@@ -83,7 +84,10 @@ ospf_install_lsa(struct ospf_proto *p, struct ospf_lsa_header *lsa, u32 type, u3
en->lsa_type, en->lsa.id, en->lsa.rt, en->lsa.sn, en->lsa.age);
if (change)
+ {
+ ospf_neigh_lsadb_changed(p, en);
ospf_schedule_rtcalc(p);
+ }
return en;
}
@@ -243,6 +247,7 @@ ospf_do_originate_lsa(struct ospf_proto *p, struct top_hash_entry *en, void *lsa
en->lsa.age = 0;
en->init_age = 0;
en->inst_time = current_time();
+ en->gr_dirty = 0;
lsa_generate_checksum(&en->lsa, en->lsa_body);
OSPF_TRACE(D_EVENTS, "Originating LSA: Type: %04x, Id: %R, Rt: %R, Seq: %08x",
@@ -251,7 +256,10 @@ ospf_do_originate_lsa(struct ospf_proto *p, struct top_hash_entry *en, void *lsa
ospf_flood_lsa(p, en, NULL);
if (en->mode == LSA_M_BASIC)
+ {
+ ospf_neigh_lsadb_changed(p, en);
ospf_schedule_rtcalc(p);
+ }
return 1;
}
@@ -273,11 +281,15 @@ ospf_do_originate_lsa(struct ospf_proto *p, struct top_hash_entry *en, void *lsa
struct top_hash_entry *
ospf_originate_lsa(struct ospf_proto *p, struct ospf_new_lsa *lsa)
{
- struct top_hash_entry *en;
+ struct top_hash_entry *en = NULL;
void *lsa_body = p->lsab;
u16 lsa_blen = p->lsab_used;
u16 lsa_length = sizeof(struct ospf_lsa_header) + lsa_blen;
+ /* RFC 3623 2 (1) - do not originate topology LSAs during graceful restart */
+ if (p->gr_recovery && (LSA_FUNCTION(lsa->type) <= LSA_FUNCTION(LSA_T_NSSA)))
+ goto drop;
+
/* For OSPFv2 Opaque LSAs, LS ID consists of Opaque Type and Opaque ID */
if (ospf_is_v2(p) && lsa_is_opaque(lsa->type))
lsa->id |= (u32) lsa_get_opaque_type(lsa->type) << 24;
@@ -321,7 +333,8 @@ ospf_originate_lsa(struct ospf_proto *p, struct ospf_new_lsa *lsa)
if ((en->lsa.age < LSA_MAXAGE) &&
(lsa_length == en->lsa.length) &&
!memcmp(lsa_body, en->lsa_body, lsa_blen) &&
- (!ospf_is_v2(p) || (lsa->opts == lsa_get_options(&en->lsa))))
+ (!ospf_is_v2(p) || (lsa->opts == lsa_get_options(&en->lsa))) &&
+ !en->gr_dirty)
goto drop;
lsa_body = lsab_flush(p);
@@ -414,6 +427,7 @@ void
ospf_flush_lsa(struct ospf_proto *p, struct top_hash_entry *en)
{
en->nf = NULL;
+ en->gr_dirty = 0;
if (en->next_lsa_body)
{
@@ -433,7 +447,10 @@ ospf_flush_lsa(struct ospf_proto *p, struct top_hash_entry *en)
ospf_flood_lsa(p, en, NULL);
if (en->mode == LSA_M_BASIC)
+ {
+ ospf_neigh_lsadb_changed(p, en);
ospf_schedule_rtcalc(p);
+ }
en->mode = LSA_M_BASIC;
}
@@ -525,6 +542,29 @@ ospf_update_lsadb(struct ospf_proto *p)
}
}
+void
+ospf_feed_begin(struct channel *C, int initial UNUSED)
+{
+ struct ospf_proto *p = (struct ospf_proto *) C->proto;
+ struct top_hash_entry *en;
+
+ /* Mark all external LSAs as stale */
+ WALK_SLIST(en, p->lsal)
+ if (en->mode == LSA_M_EXPORT)
+ en->mode = LSA_M_EXPORT_STALE;
+}
+
+void
+ospf_feed_end(struct channel *C)
+{
+ struct ospf_proto *p = (struct ospf_proto *) C->proto;
+ struct top_hash_entry *en;
+
+ /* Flush stale LSAs */
+ WALK_SLIST(en, p->lsal)
+ if (en->mode == LSA_M_EXPORT_STALE)
+ ospf_flush_lsa(p, en);
+}
static u32
ort_to_lsaid(struct ospf_proto *p, ort *nf)
@@ -1424,6 +1464,7 @@ prepare_prefix_rt_lsa_body(struct ospf_proto *p, struct ospf_area *oa)
struct ospf_config *cf = (struct ospf_config *) (p->p.cf);
struct ospf_iface *ifa;
struct ospf_lsa_prefix *lp;
+ uint max = ospf_is_ip4(p) ? IP4_MAX_PREFIX_LENGTH : IP6_MAX_PREFIX_LENGTH;
int host_addr = 0;
int net_lsa;
int i = 0;
@@ -1457,7 +1498,7 @@ prepare_prefix_rt_lsa_body(struct ospf_proto *p, struct ospf_area *oa)
(a->scope <= SCOPE_LINK))
continue;
- if (((a->prefix.pxlen < IP6_MAX_PREFIX_LENGTH) && net_lsa) ||
+ if (((a->prefix.pxlen < max) && net_lsa) ||
configured_stubnet(oa, a))
continue;
@@ -1465,8 +1506,13 @@ prepare_prefix_rt_lsa_body(struct ospf_proto *p, struct ospf_area *oa)
(ifa->state == OSPF_IS_LOOP) ||
(ifa->type == OSPF_IT_PTMP))
{
- net_addr_ip6 net = NET_ADDR_IP6(a->ip, IP6_MAX_PREFIX_LENGTH);
- lsab_put_prefix(p, (net_addr *) &net, 0);
+ net_addr net;
+ if (a->prefix.type == NET_IP4)
+ net_fill_ip4(&net, ipa_to_ip4(a->ip), IP4_MAX_PREFIX_LENGTH);
+ else
+ net_fill_ip6(&net, ipa_to_ip6(a->ip), IP6_MAX_PREFIX_LENGTH);
+
+ lsab_put_prefix(p, &net, 0);
host_addr = 1;
}
else
@@ -1482,7 +1528,7 @@ prepare_prefix_rt_lsa_body(struct ospf_proto *p, struct ospf_area *oa)
if (!sn->hidden)
{
lsab_put_prefix(p, &sn->prefix, sn->cost);
- if (sn->prefix.pxlen == IP6_MAX_PREFIX_LENGTH)
+ if (sn->prefix.pxlen == max)
host_addr = 1;
i++;
}
@@ -1670,6 +1716,59 @@ ospf_originate_prefix_net_lsa(struct ospf_proto *p, struct ospf_iface *ifa)
/*
+ * Grace LSA handling
+ * Type = LSA_T_GR, opaque type = LSA_OT_GR
+ */
+
+static inline void
+ospf_add_gr_period_tlv(struct ospf_proto *p, uint period)
+{
+ struct ospf_tlv *tlv = lsab_allocz(p, sizeof(struct ospf_tlv) + sizeof(u32));
+ tlv->type = LSA_GR_PERIOD;
+ tlv->length = 4;
+ tlv->data[0] = period;
+}
+
+static inline void
+ospf_add_gr_reason_tlv(struct ospf_proto *p, uint reason)
+{
+ struct ospf_tlv *tlv = lsab_allocz(p, sizeof(struct ospf_tlv) + sizeof(u32));
+ tlv->type = LSA_GR_REASON;
+ tlv->length = 1;
+ tlv->data[0] = reason << 24;
+}
+
+static inline void
+ospf_add_gr_address_tlv(struct ospf_proto *p, ip4_addr addr)
+{
+ struct ospf_tlv *tlv = lsab_allocz(p, sizeof(struct ospf_tlv) + sizeof(u32));
+ tlv->type = LSA_GR_ADDRESS;
+ tlv->length = 4;
+ tlv->data[0] = ip4_to_u32(addr);
+}
+
+void
+ospf_originate_gr_lsa(struct ospf_proto *p, struct ospf_iface *ifa)
+{
+ struct ospf_new_lsa lsa = {
+ .type = LSA_T_GR,
+ .dom = ifa->iface_id,
+ .id = ospf_is_v2(p) ? 0 : ifa->iface_id,
+ .ifa = ifa
+ };
+
+ ospf_add_gr_period_tlv(p, p->gr_time);
+ ospf_add_gr_reason_tlv(p, 0);
+
+ uint t = ifa->type;
+ if (ospf_is_v2(p) && ((t == OSPF_IT_BCAST) || (t == OSPF_IT_NBMA) || (t == OSPF_IT_PTMP)))
+ ospf_add_gr_address_tlv(p, ipa_to_ip4(ifa->addr->ip));
+
+ ospf_originate_lsa(p, &lsa);
+}
+
+
+/*
* Router Information LSA handling
* Type = LSA_T_RI_AREA, opaque type = LSA_OT_RI
*/
@@ -1712,6 +1811,10 @@ ospf_update_topology(struct ospf_proto *p)
struct ospf_area *oa;
struct ospf_iface *ifa;
+ /* No LSA reorigination during GR recovery */
+ if (p->gr_recovery)
+ return;
+
WALK_LIST(oa, p->area_list)
{
if (oa->update_rt_lsa)
diff --git a/proto/ospf/topology.h b/proto/ospf/topology.h
index fd70239d..535d1f1b 100644
--- a/proto/ospf/topology.h
+++ b/proto/ospf/topology.h
@@ -33,6 +33,7 @@ struct top_hash_entry
u32 lb_id; /* Interface ID of link back iface (for bcast or NBMA networks) */
u32 dist; /* Distance from the root */
int ret_count; /* Number of retransmission lists referencing the entry */
+ u8 gr_dirty; /* Local LSA received during GR, will be removed unless reoriginated */
u8 color;
#define OUTSPF 0
#define CANDIDATE 1
@@ -114,10 +115,11 @@ struct top_hash_entry
*/
-#define LSA_M_BASIC 0
-#define LSA_M_EXPORT 1
-#define LSA_M_RTCALC 2
-#define LSA_M_STALE 3
+#define LSA_M_BASIC 0
+#define LSA_M_EXPORT 1
+#define LSA_M_RTCALC 2
+#define LSA_M_EXPORT_STALE 3
+#define LSA_M_RTCALC_STALE 4
/*
* LSA entry modes:
@@ -127,9 +129,13 @@ struct top_hash_entry
* routing table calculation is scheduled. This is also the mode used for LSAs
* received from neighbors. Example: Router-LSAs, Network-LSAs.
*
- * LSA_M_EXPORT - like LSA_M_BASIC, but the routing table calculation does not
- * depend on the LSA. Therefore, the calculation is not scheduled when the LSA
- * is changed. Example: AS-external-LSAs for exported routes.
+ * LSA_M_EXPORT - The LSA is originated using ospf_originate_lsa() as a
+ * consequence of route export to the OSPF instance. It has to be reoriginated
+ * during each channel feed, otherwise it is flushed automatically at the end of
+ * the feed. May be originated and flushed asynchronously. Also, routing table
+ * calculation does not depend on the LSA. Therefore, the routing table
+ * calculation is not scheduled when the LSA is changed. Example:
+ * AS-external-LSAs for exported routes.
*
* LSA_M_RTCALC - The LSA has to be requested using ospf_originate_lsa() during
* each routing table calculation, otherwise it is flushed automatically at the
@@ -137,8 +143,11 @@ struct top_hash_entry
* source for it. Therefore, the calculation is not scheduled when the LSA is
* changed. Example: Summary-LSAs.
*
- * LSA_M_STALE - Temporary state for LSA_M_RTCALC that is not requested during
- * the current routing table calculation.
+ * LSA_M_EXPORT_STALE - Temporary state for LSA_M_EXPORT that is not requested
+ * during current external route feed.
+ *
+ * LSA_M_RTCALC_STALE - Temporary state for LSA_M_RTCALC that is not requested
+ * during current routing table calculation.
*
*
* Note that we do not schedule the routing table calculation when the age of
@@ -180,6 +189,8 @@ struct top_hash_entry * ospf_originate_lsa(struct ospf_proto *p, struct ospf_new
void ospf_advance_lsa(struct ospf_proto *p, struct top_hash_entry *en, struct ospf_lsa_header *lsa, u32 type, u32 domain, void *body);
void ospf_flush_lsa(struct ospf_proto *p, struct top_hash_entry *en);
void ospf_update_lsadb(struct ospf_proto *p);
+void ospf_feed_begin(struct channel *C, int initial);
+void ospf_feed_end(struct channel *C);
static inline void ospf_flush2_lsa(struct ospf_proto *p, struct top_hash_entry **en)
{ if (*en) { ospf_flush_lsa(p, *en); *en = NULL; } }
@@ -187,6 +198,7 @@ static inline void ospf_flush2_lsa(struct ospf_proto *p, struct top_hash_entry *
void ospf_originate_sum_net_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, int metric);
void ospf_originate_sum_rt_lsa(struct ospf_proto *p, struct ospf_area *oa, u32 drid, int metric, u32 options);
void ospf_originate_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, u8 mode, u32 metric, u32 ebit, ip_addr fwaddr, u32 tag, int pbit, int dn);
+void ospf_originate_gr_lsa(struct ospf_proto *p, struct ospf_iface *ifa);
void ospf_rt_notify(struct proto *P, struct channel *ch, net *n, rte *new, rte *old);
void ospf_update_topology(struct ospf_proto *p);
diff --git a/proto/perf/perf.c b/proto/perf/perf.c
index 8412254a..bfc0f09e 100644
--- a/proto/perf/perf.c
+++ b/proto/perf/perf.c
@@ -90,6 +90,8 @@ struct perf_random_routes {
struct rta a;
};
+static const uint perf_random_routes_size = sizeof(net_addr) + sizeof(rte *) + RTA_MAX_SIZE;
+
static inline s64 timediff(struct timespec *begin, struct timespec *end)
{ return (end->tv_sec - begin->tv_sec) * (s64) 1000000000 + end->tv_nsec - begin->tv_nsec; }
@@ -124,7 +126,7 @@ perf_loop(void *data)
struct perf_proto *p = data;
const uint N = 1U << p->exp;
- const uint offset = sizeof(net_addr) + RTA_MAX_SIZE;
+ const uint offset = perf_random_routes_size;
if (!p->run) {
ASSERT(p->data == NULL);
diff --git a/proto/rip/config.Y b/proto/rip/config.Y
index 172299d0..5b5f94a0 100644
--- a/proto/rip/config.Y
+++ b/proto/rip/config.Y
@@ -191,10 +191,10 @@ dynamic_attr: RIP_TAG { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_RIP_TAG)
CF_CLI_HELP(SHOW RIP, ..., [[Show information about RIP protocol]]);
-CF_CLI(SHOW RIP INTERFACES, optsym opttext, [<name>] [\"<interface>\"], [[Show information about RIP interfaces]])
+CF_CLI(SHOW RIP INTERFACES, optproto opttext, [<name>] [\"<interface>\"], [[Show information about RIP interfaces]])
{ rip_show_interfaces(proto_get_named($4, &proto_rip), $5); };
-CF_CLI(SHOW RIP NEIGHBORS, optsym opttext, [<name>] [\"<interface>\"], [[Show information about RIP neighbors]])
+CF_CLI(SHOW RIP NEIGHBORS, optproto opttext, [<name>] [\"<interface>\"], [[Show information about RIP neighbors]])
{ rip_show_neighbors(proto_get_named($4, &proto_rip), $5); };
diff --git a/proto/rip/rip.c b/proto/rip/rip.c
index 612b6f92..4559310e 100644
--- a/proto/rip/rip.c
+++ b/proto/rip/rip.c
@@ -193,8 +193,7 @@ rip_announce_rte(struct rip_proto *p, struct rip_entry *en)
e->u.rip.from = a0.nh.iface;
e->u.rip.metric = rt_metric;
e->u.rip.tag = rt_tag;
-
- e->pflags = 0;
+ e->pflags = EA_ID_FLAG(EA_RIP_METRIC) | EA_ID_FLAG(EA_RIP_TAG);
rte_update(&p->p, en->n.addr, e);
}
@@ -484,7 +483,8 @@ rip_update_bfd(struct rip_proto *p, struct rip_neighbor *n)
*/
ip_addr saddr = rip_is_v2(p) ? n->ifa->sk->saddr : n->nbr->ifa->ip;
n->bfd_req = bfd_request_session(p->p.pool, n->nbr->addr, saddr,
- n->nbr->iface, rip_bfd_notify, n);
+ n->nbr->iface, p->p.vrf,
+ rip_bfd_notify, n);
}
if (!use_bfd && n->bfd_req)
@@ -997,28 +997,6 @@ rip_trigger_update(struct rip_proto *p)
* RIP protocol glue
*/
-static struct ea_list *
-rip_prepare_attrs(struct linpool *pool, ea_list *next, u8 metric, u16 tag)
-{
- struct ea_list *l = lp_alloc(pool, sizeof(struct ea_list) + 2 * sizeof(eattr));
-
- l->next = next;
- l->flags = EALF_SORTED;
- l->count = 2;
-
- l->attrs[0].id = EA_RIP_METRIC;
- l->attrs[0].flags = 0;
- l->attrs[0].type = EAF_TYPE_INT | EAF_TEMP;
- l->attrs[0].u.data = metric;
-
- l->attrs[1].id = EA_RIP_TAG;
- l->attrs[1].flags = 0;
- l->attrs[1].type = EAF_TYPE_INT | EAF_TEMP;
- l->attrs[1].u.data = tag;
-
- return l;
-}
-
static void
rip_reload_routes(struct channel *C)
{
@@ -1032,17 +1010,20 @@ rip_reload_routes(struct channel *C)
rip_kick_timer(p);
}
-static struct ea_list *
+static void
rip_make_tmp_attrs(struct rte *rt, struct linpool *pool)
{
- return rip_prepare_attrs(pool, NULL, rt->u.rip.metric, rt->u.rip.tag);
+ rte_init_tmp_attrs(rt, pool, 2);
+ rte_make_tmp_attr(rt, EA_RIP_METRIC, EAF_TYPE_INT, rt->u.rip.metric);
+ rte_make_tmp_attr(rt, EA_RIP_TAG, EAF_TYPE_INT, rt->u.rip.tag);
}
static void
-rip_store_tmp_attrs(struct rte *rt)
+rip_store_tmp_attrs(struct rte *rt, struct linpool *pool)
{
- rt->u.rip.metric = ea_get_int(rt->attrs->eattrs, EA_RIP_METRIC, 1);
- rt->u.rip.tag = ea_get_int(rt->attrs->eattrs, EA_RIP_TAG, 0);
+ rte_init_tmp_attrs(rt, pool, 2);
+ rt->u.rip.metric = rte_store_tmp_attr(rt, EA_RIP_METRIC);
+ rt->u.rip.tag = rte_store_tmp_attr(rt, EA_RIP_TAG);
}
static int
diff --git a/proto/rpki/config.Y b/proto/rpki/config.Y
index a88a29a1..63c7105c 100644
--- a/proto/rpki/config.Y
+++ b/proto/rpki/config.Y
@@ -97,7 +97,7 @@ rpki_cache_addr:
rpki_check_unused_hostname();
RPKI_CFG->ip = $1;
/* Ensure hostname is filled */
- char *hostname = cfg_allocz(sizeof(INET6_ADDRSTRLEN + 1));
+ char *hostname = cfg_allocz(INET6_ADDRSTRLEN + 1);
bsnprintf(hostname, INET6_ADDRSTRLEN+1, "%I", RPKI_CFG->ip);
RPKI_CFG->hostname = hostname;
}
diff --git a/proto/rpki/rpki.c b/proto/rpki/rpki.c
index 36097dc5..70cd0cdd 100644
--- a/proto/rpki/rpki.c
+++ b/proto/rpki/rpki.c
@@ -687,9 +687,9 @@ rpki_reconfigure_cache(struct rpki_proto *p UNUSED, struct rpki_cache *cache, st
{
struct rpki_tr_ssh_config *ssh_old = (void *) old->tr_config.spec;
struct rpki_tr_ssh_config *ssh_new = (void *) new->tr_config.spec;
- if ((strcmp(ssh_old->bird_private_key, ssh_new->bird_private_key) != 0) ||
- (strcmp(ssh_old->cache_public_key, ssh_new->cache_public_key) != 0) ||
- (strcmp(ssh_old->user, ssh_new->user) != 0))
+ if (bstrcmp(ssh_old->bird_private_key, ssh_new->bird_private_key) ||
+ bstrcmp(ssh_old->cache_public_key, ssh_new->cache_public_key) ||
+ bstrcmp(ssh_old->user, ssh_new->user))
{
CACHE_TRACE(D_EVENTS, cache, "Settings of SSH transport configuration changed");
try_fast_reconnect = 1;
diff --git a/proto/static/config.Y b/proto/static/config.Y
index d7a02961..6e410879 100644
--- a/proto/static/config.Y
+++ b/proto/static/config.Y
@@ -14,7 +14,7 @@ CF_DEFINES
#define STATIC_CFG ((struct static_config *) this_proto)
static struct static_route *this_srt, *this_snh;
-static struct f_inst **this_srt_last_cmd;
+static struct f_inst *this_srt_cmds, *this_srt_last_cmd;
static struct static_route *
static_nexthop_new(void)
@@ -39,6 +39,8 @@ static_route_finish(void)
{
if (net_type_match(this_srt->net, NB_DEST) == !this_srt->dest)
cf_error("Unexpected or missing nexthop/type");
+
+ this_srt->cmds = f_linearize(this_srt_cmds);
}
CF_DECLS
@@ -108,7 +110,8 @@ stat_route0: ROUTE net_any {
this_srt = cfg_allocz(sizeof(struct static_route));
add_tail(&STATIC_CFG->routes, &this_srt->n);
this_srt->net = $2;
- this_srt_last_cmd = &(this_srt->cmds);
+ this_srt_cmds = NULL;
+ this_srt_last_cmd = NULL;
this_srt->mp_next = NULL;
this_snh = NULL;
}
@@ -134,7 +137,13 @@ stat_route:
;
stat_route_item:
- cmd { *this_srt_last_cmd = $1; this_srt_last_cmd = &($1->next); }
+ cmd {
+ if (this_srt_last_cmd)
+ this_srt_last_cmd->next = $1;
+ else
+ this_srt_cmds = $1;
+ this_srt_last_cmd = $1;
+ }
;
stat_route_opts:
@@ -148,7 +157,7 @@ stat_route_opt_list:
;
-CF_CLI(SHOW STATIC, optsym, [<name>], [[Show details of static protocol]])
+CF_CLI(SHOW STATIC, optproto, [<name>], [[Show details of static protocol]])
{ static_show(proto_get_named($3, &proto_static)); } ;
CF_CODE
diff --git a/proto/static/static.c b/proto/static/static.c
index 75a74ad0..c899cc87 100644
--- a/proto/static/static.c
+++ b/proto/static/static.c
@@ -159,7 +159,8 @@ static_update_bfd(struct static_proto *p, struct static_route *r)
if (bfd_up && !r->bfd_req)
{
// ip_addr local = ipa_nonzero(r->local) ? r->local : nb->ifa->ip;
- r->bfd_req = bfd_request_session(p->p.pool, r->via, nb->ifa->ip, nb->iface,
+ r->bfd_req = bfd_request_session(p->p.pool, r->via, nb->ifa->ip,
+ nb->iface, p->p.vrf,
static_bfd_notify, r);
}
@@ -308,7 +309,7 @@ static inline int
static_same_rte(struct static_route *or, struct static_route *nr)
{
/* Note that i_same() requires arguments in (new, old) order */
- return static_same_dest(or, nr) && i_same(nr->cmds, or->cmds);
+ return static_same_dest(or, nr) && f_same(nr->cmds, or->cmds);
}
static void
diff --git a/proto/static/static.h b/proto/static/static.h
index a3c30b87..f736996c 100644
--- a/proto/static/static.h
+++ b/proto/static/static.h
@@ -39,7 +39,7 @@ struct static_route {
struct static_route *chain; /* Next for the same neighbor */
struct static_route *mp_head; /* First nexthop of this route */
struct static_route *mp_next; /* Nexthops for multipath routes */
- struct f_inst *cmds; /* List of commands for setting attributes */
+ struct f_line *cmds; /* List of commands for setting attributes */
byte dest; /* Destination type (RTD_*) */
byte state; /* State of route announcement (SRS_*) */
byte active; /* Next hop is active (nbr/iface/BFD available) */
diff --git a/sysdep/autoconf.h.in b/sysdep/autoconf.h.in
index ddbb70f9..a424e020 100644
--- a/sysdep/autoconf.h.in
+++ b/sysdep/autoconf.h.in
@@ -105,6 +105,9 @@
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
+/* Define to 1 if _Thread_local is available */
+#undef HAVE_THREAD_LOCAL
+
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
diff --git a/sysdep/config.h b/sysdep/config.h
index ab5f12a6..7165fb43 100644
--- a/sysdep/config.h
+++ b/sysdep/config.h
@@ -13,7 +13,7 @@
#ifdef GIT_LABEL
#define BIRD_VERSION XSTR1(GIT_LABEL)
#else
-#define BIRD_VERSION "2.0.4"
+#define BIRD_VERSION "2.0.5"
#endif
/* Include parameters determined by configure script */
diff --git a/sysdep/linux/krt-sys.h b/sysdep/linux/krt-sys.h
index 2b8cdaa7..a8af4c95 100644
--- a/sysdep/linux/krt-sys.h
+++ b/sysdep/linux/krt-sys.h
@@ -65,27 +65,6 @@ static inline struct ifa * kif_get_primary_ip(struct iface *i UNUSED) { return N
#define EA_KRT_INITRWND EA_CODE(PROTOCOL_KERNEL, 0x2e)
#define EA_KRT_QUICKACK EA_CODE(PROTOCOL_KERNEL, 0x2f)
-/* Bits of EA_KRT_LOCK, also based on RTAX_* constants */
-#define EA_KRT_LOCK_MTU EA_KRT_LOCK | EA_BIT(0x2)
-#define EA_KRT_LOCK_WINDOW EA_KRT_LOCK | EA_BIT(0x3)
-#define EA_KRT_LOCK_RTT EA_KRT_LOCK | EA_BIT(0x4)
-#define EA_KRT_LOCK_RTTVAR EA_KRT_LOCK | EA_BIT(0x5)
-#define EA_KRT_LOCK_SSTHRESH EA_KRT_LOCK | EA_BIT(0x6)
-#define EA_KRT_LOCK_CWND EA_KRT_LOCK | EA_BIT(0x7)
-#define EA_KRT_LOCK_ADVMSS EA_KRT_LOCK | EA_BIT(0x8)
-#define EA_KRT_LOCK_REORDERING EA_KRT_LOCK | EA_BIT(0x9)
-#define EA_KRT_LOCK_HOPLIMIT EA_KRT_LOCK | EA_BIT(0xa)
-// define EA_KRT_LOCK_INITCWND EA_KRT_LOCK | EA_BIT(0xb)
-// define EA_KRT_LOCK_FEATURES EA_KRT_LOCK | EA_BIT(0xc)
-#define EA_KRT_LOCK_RTO_MIN EA_KRT_LOCK | EA_BIT(0xd)
-// define EA_KRT_LOCK_INITRWND EA_KRT_LOCK | EA_BIT(0xe)
-
-/* Bits of EA_KRT_FEATURES, based on RTAX_FEATURE_* constants */
-#define EA_KRT_FEATURE_ECN EA_KRT_FEATURES | EA_BIT(0x0)
-// define EA_KRT_FEATURE_SACK EA_KRT_FEATURES | EA_BIT(0x1)
-// define EA_KRT_FEATURE_TSTAMP EA_KRT_FEATURES | EA_BIT(0x2)
-#define EA_KRT_FEATURE_ALLFRAG EA_KRT_FEATURES | EA_BIT(0x3)
-
struct krt_params {
u32 table_id; /* Kernel table ID we sync with */
diff --git a/sysdep/linux/netlink.Y b/sysdep/linux/netlink.Y
index 1030e5d4..7097f577 100644
--- a/sysdep/linux/netlink.Y
+++ b/sysdep/linux/netlink.Y
@@ -44,19 +44,21 @@ dynamic_attr: KRT_RTO_MIN { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_
dynamic_attr: KRT_INITRWND { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_INITRWND); } ;
dynamic_attr: KRT_QUICKACK { $$ = f_new_dynamic_attr(EAF_TYPE_INT, T_INT, EA_KRT_QUICKACK); } ;
-dynamic_attr: KRT_LOCK_MTU { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_MTU); } ;
-dynamic_attr: KRT_LOCK_WINDOW { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_WINDOW); } ;
-dynamic_attr: KRT_LOCK_RTT { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_RTT); } ;
-dynamic_attr: KRT_LOCK_RTTVAR { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_RTTVAR); } ;
-dynamic_attr: KRT_LOCK_SSTRESH { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_SSTHRESH); } ;
-dynamic_attr: KRT_LOCK_CWND { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_CWND); } ;
-dynamic_attr: KRT_LOCK_ADVMSS { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_ADVMSS); } ;
-dynamic_attr: KRT_LOCK_REORDERING { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_REORDERING); } ;
-dynamic_attr: KRT_LOCK_HOPLIMIT { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_HOPLIMIT); } ;
-dynamic_attr: KRT_LOCK_RTO_MIN { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_LOCK_RTO_MIN); } ;
+/* Bits of EA_KRT_LOCK, based on RTAX_* constants */
-dynamic_attr: KRT_FEATURE_ECN { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_FEATURE_ECN); } ;
-dynamic_attr: KRT_FEATURE_ALLFRAG { $$ = f_new_dynamic_attr(EAF_TYPE_BITFIELD, T_BOOL, EA_KRT_FEATURE_ALLFRAG); } ;
+dynamic_attr: KRT_LOCK_MTU { $$ = f_new_dynamic_attr_bit(2, T_BOOL, EA_KRT_LOCK); } ;
+dynamic_attr: KRT_LOCK_WINDOW { $$ = f_new_dynamic_attr_bit(3, T_BOOL, EA_KRT_LOCK); } ;
+dynamic_attr: KRT_LOCK_RTT { $$ = f_new_dynamic_attr_bit(4, T_BOOL, EA_KRT_LOCK); } ;
+dynamic_attr: KRT_LOCK_RTTVAR { $$ = f_new_dynamic_attr_bit(5, T_BOOL, EA_KRT_LOCK); } ;
+dynamic_attr: KRT_LOCK_SSTRESH { $$ = f_new_dynamic_attr_bit(6, T_BOOL, EA_KRT_LOCK); } ;
+dynamic_attr: KRT_LOCK_CWND { $$ = f_new_dynamic_attr_bit(7, T_BOOL, EA_KRT_LOCK); } ;
+dynamic_attr: KRT_LOCK_ADVMSS { $$ = f_new_dynamic_attr_bit(8, T_BOOL, EA_KRT_LOCK); } ;
+dynamic_attr: KRT_LOCK_REORDERING { $$ = f_new_dynamic_attr_bit(9, T_BOOL, EA_KRT_LOCK); } ;
+dynamic_attr: KRT_LOCK_HOPLIMIT { $$ = f_new_dynamic_attr_bit(10, T_BOOL, EA_KRT_LOCK); } ;
+dynamic_attr: KRT_LOCK_RTO_MIN { $$ = f_new_dynamic_attr_bit(13, T_BOOL, EA_KRT_LOCK); } ;
+
+dynamic_attr: KRT_FEATURE_ECN { $$ = f_new_dynamic_attr_bit(0, T_BOOL, EA_KRT_FEATURES); } ;
+dynamic_attr: KRT_FEATURE_ALLFRAG { $$ = f_new_dynamic_attr(3, T_BOOL, EA_KRT_FEATURES); } ;
CF_CODE
diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c
index d773743d..10e9a18b 100644
--- a/sysdep/linux/netlink.c
+++ b/sysdep/linux/netlink.c
@@ -69,6 +69,7 @@
#define RTA_ENCAP 22
#endif
+#define krt_ipv4(p) ((p)->af == AF_INET)
#define krt_ecmp6(p) ((p)->af == AF_INET6)
const int rt_default_ecmp = 16;
@@ -466,10 +467,21 @@ static inline ip_addr rta_get_via(struct rtattr *a)
static u32 rta_mpls_stack[MPLS_MAX_LABEL_STACK];
static inline int rta_get_mpls(struct rtattr *a, u32 *stack)
{
+ if (!a)
+ return 0;
+
if (RTA_PAYLOAD(a) % 4)
log(L_WARN "KRT: Strange length of received MPLS stack: %u", RTA_PAYLOAD(a));
- return mpls_get(RTA_DATA(a), RTA_PAYLOAD(a) & ~0x3, stack);
+ int labels = mpls_get(RTA_DATA(a), RTA_PAYLOAD(a) & ~0x3, stack);
+
+ if (labels < 0)
+ {
+ log(L_WARN "KRT: Too long MPLS stack received, ignoring");
+ labels = 0;
+ }
+
+ return labels;
}
#endif
@@ -705,7 +717,7 @@ nl_parse_multipath(struct nl_parse_state *s, struct krt_proto *p, struct rtattr
rv->gw = IPA_NONE;
#ifdef HAVE_MPLS_KERNEL
- if (a[RTA_ENCAP_TYPE])
+ if (a[RTA_ENCAP] && a[RTA_ENCAP_TYPE])
{
if (rta_get_u16(a[RTA_ENCAP_TYPE]) != LWTUNNEL_ENCAP_MPLS) {
log(L_WARN "KRT: Unknown encapsulation method %d in multipath", rta_get_u16(a[RTA_ENCAP_TYPE]));
@@ -716,7 +728,6 @@ nl_parse_multipath(struct nl_parse_state *s, struct krt_proto *p, struct rtattr
nl_attr_len = RTA_PAYLOAD(a[RTA_ENCAP]);
nl_parse_attrs(RTA_DATA(a[RTA_ENCAP]), encap_mpls_want, enca, sizeof(enca));
rv->labels = rta_get_mpls(enca[RTA_DST], rv->label);
- break;
}
#endif
@@ -725,6 +736,10 @@ nl_parse_multipath(struct nl_parse_state *s, struct krt_proto *p, struct rtattr
nh = RTNH_NEXT(nh);
}
+ /* Ensure nexthops are sorted to satisfy nest invariant */
+ if (!nexthop_is_sorted(first))
+ first = nexthop_sort(first);
+
return first;
}
@@ -1364,27 +1379,43 @@ nl_delete_rte(struct krt_proto *p, rte *e)
return err;
}
+static inline int
+nl_replace_rte(struct krt_proto *p, rte *e)
+{
+ rta *a = e->attrs;
+ return nl_send_route(p, e, NL_OP_REPLACE, a->dest, &(a->nh));
+}
+
+
void
krt_replace_rte(struct krt_proto *p, net *n, rte *new, rte *old)
{
int err = 0;
/*
- * We could use NL_OP_REPLACE, but route replace on Linux has some problems:
+ * We use NL_OP_REPLACE for IPv4, it has an issue with not checking for
+ * matching rtm_protocol, but that is OK when dedicated priority is used.
*
- * 1) Does not check for matching rtm_protocol
- * 2) Has broken semantics for IPv6 ECMP
- * 3) Crashes some kernel version when used for IPv6 ECMP
+ * We do not use NL_OP_REPLACE for IPv6, as it has broken semantics for ECMP
+ * and with some kernel versions ECMP replace crashes kernel. Would need more
+ * testing and checks for kernel versions.
*
- * So we use NL_OP_DELETE and then NL_OP_ADD. We also do not trust the old
- * route value, so we do not try to optimize IPv6 ECMP reconfigurations.
+ * For IPv6, we use NL_OP_DELETE and then NL_OP_ADD. We also do not trust the
+ * old route value, so we do not try to optimize IPv6 ECMP reconfigurations.
*/
- if (old)
- nl_delete_rte(p, old);
+ if (krt_ipv4(p) && old && new)
+ {
+ err = nl_replace_rte(p, new);
+ }
+ else
+ {
+ if (old)
+ nl_delete_rte(p, old);
- if (new)
- err = nl_add_rte(p, new);
+ if (new)
+ err = nl_add_rte(p, new);
+ }
if (err < 0)
n->n.flags |= KRF_SYNC_ERROR;
@@ -1393,10 +1424,10 @@ krt_replace_rte(struct krt_proto *p, net *n, rte *new, rte *old)
}
static int
-nl_mergable_route(struct nl_parse_state *s, net *net, struct krt_proto *p, uint priority, uint krt_type)
+nl_mergable_route(struct nl_parse_state *s, net *net, struct krt_proto *p, uint priority, uint krt_type, uint rtm_family)
{
- /* Route merging must be active */
- if (!s->merge)
+ /* Route merging is used for IPv6 scans */
+ if (!s->scan || (rtm_family != AF_INET6))
return 0;
/* Saved and new route must have same network, proto/table, and priority */
@@ -1433,12 +1464,11 @@ nl_announce_route(struct nl_parse_state *s)
}
static inline void
-nl_parse_begin(struct nl_parse_state *s, int scan, int merge)
+nl_parse_begin(struct nl_parse_state *s, int scan)
{
memset(s, 0, sizeof (struct nl_parse_state));
s->pool = nl_linpool;
s->scan = scan;
- s->merge = merge;
}
static inline void
@@ -1581,7 +1611,7 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
net *net = net_get(p->p.main_channel->table, n);
- if (s->net && !nl_mergable_route(s, net, p, priority, i->rtm_type))
+ if (s->net && !nl_mergable_route(s, net, p, priority, i->rtm_type, i->rtm_family))
nl_announce_route(s);
rta *ra = lp_allocz(s->pool, RTA_MAX_SIZE);
@@ -1603,7 +1633,7 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
return;
}
- ra->nh = *nh;
+ nexthop_link(ra, nh);
break;
}
@@ -1663,9 +1693,8 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
}
#ifdef HAVE_MPLS_KERNEL
- int labels = 0;
if ((i->rtm_family == AF_MPLS) && a[RTA_NEWDST] && !ra->nh.next)
- labels = rta_get_mpls(a[RTA_NEWDST], ra->nh.label);
+ ra->nh.labels = rta_get_mpls(a[RTA_NEWDST], ra->nh.label);
if (a[RTA_ENCAP] && a[RTA_ENCAP_TYPE] && !ra->nh.next)
{
@@ -1676,7 +1705,7 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
struct rtattr *enca[BIRD_RTA_MAX];
nl_attr_len = RTA_PAYLOAD(a[RTA_ENCAP]);
nl_parse_attrs(RTA_DATA(a[RTA_ENCAP]), encap_mpls_want, enca, sizeof(enca));
- labels = rta_get_mpls(enca[RTA_DST], ra->nh.label);
+ ra->nh.labels = rta_get_mpls(enca[RTA_DST], ra->nh.label);
break;
}
default:
@@ -1684,14 +1713,6 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
break;
}
}
-
- if (labels < 0)
- {
- log(L_WARN "KRT: Too long MPLS stack received, ignoring.");
- ra->nh.labels = 0;
- }
- else
- ra->nh.labels = labels;
#endif
if (i->rtm_scope != def_scope)
@@ -1719,9 +1740,12 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
ea->attrs[0].id = EA_KRT_PREFSRC;
ea->attrs[0].flags = 0;
ea->attrs[0].type = EAF_TYPE_IP_ADDRESS;
- ea->attrs[0].u.ptr = lp_alloc(s->pool, sizeof(struct adata) + sizeof(ps));
- ea->attrs[0].u.ptr->length = sizeof(ps);
- memcpy(ea->attrs[0].u.ptr->data, &ps, sizeof(ps));
+
+ struct adata *ad = lp_alloc(s->pool, sizeof(struct adata) + sizeof(ps));
+ ad->length = sizeof(ps);
+ memcpy(ad->data, &ps, sizeof(ps));
+
+ ea->attrs[0].u.ptr = ad;
}
if (a[RTA_FLOW])
@@ -1814,34 +1838,14 @@ krt_do_scan(struct krt_proto *p UNUSED) /* CONFIG_ALL_TABLES_AT_ONCE => p is NUL
struct nlmsghdr *h;
struct nl_parse_state s;
- nl_parse_begin(&s, 1, 0);
- nl_request_dump(AF_INET, RTM_GETROUTE);
- while (h = nl_get_scan())
- if (h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE)
- nl_parse_route(&s, h);
- else
- log(L_DEBUG "nl_scan_fire: Unknown packet received (type=%d)", h->nlmsg_type);
- nl_parse_end(&s);
-
- nl_parse_begin(&s, 1, 1);
- nl_request_dump(AF_INET6, RTM_GETROUTE);
+ nl_parse_begin(&s, 1);
+ nl_request_dump(AF_UNSPEC, RTM_GETROUTE);
while (h = nl_get_scan())
if (h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE)
nl_parse_route(&s, h);
else
log(L_DEBUG "nl_scan_fire: Unknown packet received (type=%d)", h->nlmsg_type);
nl_parse_end(&s);
-
-#ifdef HAVE_MPLS_KERNEL
- nl_parse_begin(&s, 1, 1);
- nl_request_dump(AF_MPLS, RTM_GETROUTE);
- while (h = nl_get_scan())
- if (h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE)
- nl_parse_route(&s, h);
- else
- log(L_DEBUG "nl_scan_fire: Unknown packet received (type=%d)", h->nlmsg_type);
- nl_parse_end(&s);
-#endif
}
/*
@@ -1861,7 +1865,7 @@ nl_async_msg(struct nlmsghdr *h)
case RTM_NEWROUTE:
case RTM_DELROUTE:
DBG("KRT: Received async route notification (%d)\n", h->nlmsg_type);
- nl_parse_begin(&s, 0, 0);
+ nl_parse_begin(&s, 0);
nl_parse_route(&s, h);
nl_parse_end(&s);
break;
diff --git a/sysdep/unix/config.Y b/sysdep/unix/config.Y
index e7ecd735..c76eb73b 100644
--- a/sysdep/unix/config.Y
+++ b/sysdep/unix/config.Y
@@ -18,7 +18,8 @@ static struct log_config *this_log;
CF_DECLS
CF_KEYWORDS(LOG, SYSLOG, ALL, DEBUG, TRACE, INFO, REMOTE, WARNING, ERROR, AUTH, FATAL, BUG, STDERR, SOFT)
-CF_KEYWORDS(NAME, CONFIRM, UNDO, CHECK, TIMEOUT, DEBUG, LATENCY, LIMIT, WATCHDOG, WARNING)
+CF_KEYWORDS(NAME, CONFIRM, UNDO, CHECK, TIMEOUT, DEBUG, LATENCY, LIMIT, WATCHDOG, WARNING, STATUS)
+CF_KEYWORDS(GRACEFUL, RESTART)
%type <i> log_mask log_mask_list log_cat cfg_timeout
%type <t> cfg_name
@@ -124,12 +125,19 @@ CF_CLI(CONFIGURE CONFIRM,,, [[Confirm last configuration change - deactivate und
CF_CLI(CONFIGURE UNDO,,, [[Undo last configuration change]])
{ cmd_reconfig_undo(); } ;
+CF_CLI(CONFIGURE STATUS,,, [[Show configuration status]])
+{ cmd_reconfig_status(); } ;
+
CF_CLI(CONFIGURE CHECK, cfg_name, [\"<file>\"], [[Parse configuration and check its validity]])
{ cmd_check_config($3); } ;
CF_CLI(DOWN,,, [[Shut the daemon down]])
{ cmd_shutdown(); } ;
+CF_CLI(GRACEFUL RESTART,,, [[Shut the daemon down for graceful restart]])
+{ cmd_graceful_restart(); } ;
+
+
cfg_name:
/* empty */ { $$ = NULL; }
| TEXT
diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c
index d1d86e3b..c9fee3ab 100644
--- a/sysdep/unix/io.c
+++ b/sysdep/unix/io.c
@@ -132,7 +132,7 @@ times_init(struct timeloop *loop)
if (rv < 0)
die("Monotonic clock is missing");
- if ((ts.tv_sec < 0) || (((s64) ts.tv_sec) > ((s64) 1 << 40)))
+ if ((ts.tv_sec < 0) || (((u64) ts.tv_sec) > ((u64) 1 << 40)))
log(L_WARN "Monotonic clock is crazy");
loop->last_time = ts.tv_sec S + ts.tv_nsec NS;
@@ -1082,6 +1082,7 @@ sk_passive_connected(sock *s, int type)
t->fd = fd;
t->ttl = s->ttl;
t->tos = s->tos;
+ t->vrf = s->vrf;
t->rbsize = s->rbsize;
t->tbsize = s->tbsize;
diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c
index ded5dfe4..27868fab 100644
--- a/sysdep/unix/krt.c
+++ b/sysdep/unix/krt.c
@@ -301,7 +301,7 @@ krt_learn_announce_update(struct krt_proto *p, rte *e)
net *n = e->net;
rta *aa = rta_clone(e->attrs);
rte *ee = rte_get_temp(aa);
- ee->pflags = 0;
+ ee->pflags = EA_ID_FLAG(EA_KRT_SOURCE) | EA_ID_FLAG(EA_KRT_METRIC);
ee->u.krt = e->u.krt;
rte_update(&p->p, n->n.addr, ee);
}
@@ -562,7 +562,7 @@ static struct rte *
krt_export_net(struct krt_proto *p, net *net, rte **rt_free)
{
struct channel *c = p->p.main_channel;
- struct filter *filter = c->out_filter;
+ const struct filter *filter = c->out_filter;
rte *rt;
if (c->ra_mode == RA_MERGED)
@@ -577,7 +577,7 @@ krt_export_net(struct krt_proto *p, net *net, rte **rt_free)
if (filter == FILTER_REJECT)
return NULL;
- rte_make_tmp_attrs(&rt, krt_filter_lp);
+ rte_make_tmp_attrs(&rt, krt_filter_lp, NULL);
/* We could run krt_preexport() here, but it is already handled by KRF_INSTALLED */
@@ -910,33 +910,20 @@ krt_scan_timer_kick(struct krt_proto *p)
* Updates
*/
-static struct ea_list *
-krt_make_tmp_attrs(rte *rt, struct linpool *pool)
+static void
+krt_make_tmp_attrs(struct rte *rt, struct linpool *pool)
{
- struct ea_list *l = lp_alloc(pool, sizeof(struct ea_list) + 2 * sizeof(eattr));
-
- l->next = NULL;
- l->flags = EALF_SORTED;
- l->count = 2;
-
- l->attrs[0].id = EA_KRT_SOURCE;
- l->attrs[0].flags = 0;
- l->attrs[0].type = EAF_TYPE_INT | EAF_TEMP;
- l->attrs[0].u.data = rt->u.krt.proto;
-
- l->attrs[1].id = EA_KRT_METRIC;
- l->attrs[1].flags = 0;
- l->attrs[1].type = EAF_TYPE_INT | EAF_TEMP;
- l->attrs[1].u.data = rt->u.krt.metric;
-
- return l;
+ rte_init_tmp_attrs(rt, pool, 2);
+ rte_make_tmp_attr(rt, EA_KRT_SOURCE, EAF_TYPE_INT, rt->u.krt.proto);
+ rte_make_tmp_attr(rt, EA_KRT_METRIC, EAF_TYPE_INT, rt->u.krt.metric);
}
static void
-krt_store_tmp_attrs(rte *rt)
+krt_store_tmp_attrs(struct rte *rt, struct linpool *pool)
{
- /* EA_KRT_SOURCE is read-only */
- rt->u.krt.metric = ea_get_int(rt->attrs->eattrs, EA_KRT_METRIC, 0);
+ rte_init_tmp_attrs(rt, pool, 2);
+ rt->u.krt.proto = rte_store_tmp_attr(rt, EA_KRT_SOURCE);
+ rt->u.krt.metric = rte_store_tmp_attr(rt, EA_KRT_METRIC);
}
static int
@@ -1142,7 +1129,7 @@ krt_shutdown(struct proto *P)
krt_scan_timer_stop(p);
/* FIXME we should flush routes even when persist during reconfiguration */
- if (p->initialized && !KRT_CF->persist)
+ if (p->initialized && !KRT_CF->persist && (P->down_code != PDC_CMD_GR_DOWN))
krt_flush_routes(p);
p->ready = 0;
diff --git a/sysdep/unix/log.c b/sysdep/unix/log.c
index c9064834..18894f98 100644
--- a/sysdep/unix/log.c
+++ b/sysdep/unix/log.c
@@ -309,14 +309,23 @@ die(const char *msg, ...)
void
debug(const char *msg, ...)
{
+#define MAX_DEBUG_BUFSIZE 65536
va_list args;
- char buf[1024];
+ static uint bufsize = 4096;
+ static char *buf = NULL;
+
+ if (!buf)
+ buf = mb_alloc(&root_pool, bufsize);
va_start(args, msg);
if (dbgf)
{
- if (bvsnprintf(buf, sizeof(buf), msg, args) < 0)
- bsprintf(buf + sizeof(buf) - 100, " ... <too long>\n");
+ while (bvsnprintf(buf, bufsize, msg, args) < 0)
+ if (bufsize >= MAX_DEBUG_BUFSIZE)
+ bug("Extremely long debug output, split it.");
+ else
+ buf = mb_realloc(buf, (bufsize *= 2));
+
fputs(buf, dbgf);
}
va_end(args);
@@ -363,6 +372,9 @@ log_switch(int initial, list *logs, char *new_syslog_name)
if (!logs || EMPTY_LIST(*logs))
logs = default_log_list(initial, &new_syslog_name);
+ /* We shouldn't close the logs when other threads may use them */
+ log_lock();
+
/* Close the logs to avoid pinning them on disk when deleted */
if (current_log_list)
WALK_LIST(l, *current_log_list)
@@ -378,9 +390,8 @@ log_switch(int initial, list *logs, char *new_syslog_name)
current_log_list = logs;
#ifdef HAVE_SYSLOG_H
- if (current_syslog_name && new_syslog_name &&
- !strcmp(current_syslog_name, new_syslog_name))
- return;
+ if (!bstrcmp(current_syslog_name, new_syslog_name))
+ goto done;
if (current_syslog_name)
{
@@ -394,7 +405,12 @@ log_switch(int initial, list *logs, char *new_syslog_name)
current_syslog_name = xstrdup(new_syslog_name);
openlog(current_syslog_name, LOG_CONS | LOG_NDELAY, LOG_DAEMON);
}
+
#endif
+
+done:
+ /* Logs exchange done, let the threads log as before */
+ log_unlock();
}
void
diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c
index 921115b1..05becbe7 100644
--- a/sysdep/unix/main.c
+++ b/sysdep/unix/main.c
@@ -36,6 +36,7 @@
#include "nest/locks.h"
#include "conf/conf.h"
#include "filter/filter.h"
+#include "filter/data.h"
#include "unix.h"
#include "krt.h"
@@ -93,11 +94,9 @@ drop_gid(gid_t gid)
static inline void
add_num_const(char *name, int val)
{
- struct symbol *s = cf_get_symbol(name);
- s->class = SYM_CONSTANT | T_INT;
- s->def = cfg_allocz(sizeof(struct f_val));
- SYM_TYPE(s) = T_INT;
- SYM_VAL(s).i = val;
+ struct f_val *v = cfg_alloc(sizeof(struct f_val));
+ *v = (struct f_val) { .type = T_INT, .val.i = val };
+ cf_define_symbol(cf_get_symbol(name), SYM_CONSTANT | T_INT, val, v);
}
/* the code of read_iproute_table() is based on
@@ -339,6 +338,28 @@ cmd_reconfig_undo(void)
cmd_reconfig_msg(r);
}
+void
+cmd_reconfig_status(void)
+{
+ int s = config_status();
+ btime t = config_timer_status();
+
+ switch (s)
+ {
+ case CONF_DONE: cli_msg(-3, "Daemon is up and running"); break;
+ case CONF_PROGRESS: cli_msg(-4, "Reconfiguration in progress"); break;
+ case CONF_QUEUED: cli_msg(-5, "Reconfiguration in progress, next one enqueued"); break;
+ case CONF_SHUTDOWN: cli_msg(-6, "Shutdown in progress"); break;
+ default: break;
+ }
+
+ if (t >= 0)
+ cli_msg(-22, "Configuration unconfirmed, undo in %t s", t);
+
+ cli_msg(0, "");
+}
+
+
/*
* Command-Line Interface
*/
@@ -543,14 +564,14 @@ cmd_shutdown(void)
return;
cli_msg(7, "Shutdown requested");
- order_shutdown();
+ order_shutdown(0);
}
void
async_shutdown(void)
{
DBG("Shutting down...\n");
- order_shutdown();
+ order_shutdown(0);
}
void
@@ -562,6 +583,17 @@ sysdep_shutdown_done(void)
exit(0);
}
+void
+cmd_graceful_restart(void)
+{
+ if (cli_access_restricted())
+ return;
+
+ cli_msg(25, "Graceful restart requested");
+ order_shutdown(1);
+}
+
+
/*
* Signals
*/
diff --git a/sysdep/unix/unix.h b/sysdep/unix/unix.h
index 0e1e98c0..bf0aedeb 100644
--- a/sysdep/unix/unix.h
+++ b/sysdep/unix/unix.h
@@ -26,7 +26,9 @@ void cmd_check_config(char *name);
void cmd_reconfig(char *name, int type, uint timeout);
void cmd_reconfig_confirm(void);
void cmd_reconfig_undo(void);
+void cmd_reconfig_status(void);
void cmd_shutdown(void);
+void cmd_graceful_restart(void);
#define UNIX_DEFAULT_CONFIGURE_TIMEOUT 300
diff --git a/test/birdtest.c b/test/birdtest.c
index a4312e9b..c0d4bd05 100644
--- a/test/birdtest.c
+++ b/test/birdtest.c
@@ -31,6 +31,7 @@
static const char *request;
static int list_tests;
static int do_core;
+static int do_die;
static int no_fork;
static int no_timeout;
static int is_terminal; /* Whether stdout is a live terminal or pipe redirect */
@@ -43,23 +44,17 @@ int bt_result; /* Overall program run result */
int bt_suite_result; /* One suit result */
char bt_out_fmt_buf[1024]; /* Temporary memory buffer for output of testing function */
-long int
-bt_random(void)
-{
- /* Seeded in bt_init() */
- long int rand_low, rand_high;
-
- rand_low = random();
- rand_high = random();
- return (rand_low & 0xffff) | ((rand_high & 0xffff) << 16);
-}
+u64 bt_random_state[] = {
+ 0x80241f302bd4d95d, 0xd10ba2e910f772b, 0xea188c9046f507c5, 0x4c4c581f04e6da05,
+ 0x53d9772877c1b647, 0xab8ce3eb466de6c5, 0xad02844c8a8e865f, 0xe8cc78080295065d
+};
void
bt_init(int argc, char *argv[])
{
int c;
- srandom(BT_RANDOM_SEED);
+ initstate(BT_RANDOM_SEED, (char *) bt_random_state, sizeof(bt_random_state));
bt_verbose = 0;
bt_filename = argv[0];
@@ -67,7 +62,7 @@ bt_init(int argc, char *argv[])
bt_test_id = NULL;
is_terminal = isatty(fileno(stdout));
- while ((c = getopt(argc, argv, "lcftv")) >= 0)
+ while ((c = getopt(argc, argv, "lcdftv")) >= 0)
switch (c)
{
case 'l':
@@ -78,6 +73,10 @@ bt_init(int argc, char *argv[])
do_core = 1;
break;
+ case 'd':
+ do_die = 1;
+ break;
+
case 'f':
no_fork = 1;
break;
@@ -111,10 +110,11 @@ bt_init(int argc, char *argv[])
return;
usage:
- printf("Usage: %s [-l] [-c] [-f] [-t] [-vvv] [<test_suit_name>]\n", argv[0]);
+ printf("Usage: %s [-l] [-c] [-d] [-f] [-t] [-vvv] [<test_suit_name>]\n", argv[0]);
printf("Options: \n");
printf(" -l List all test suite names and descriptions \n");
printf(" -c Force unlimit core dumps (needs root privileges) \n");
+ printf(" -d Die on first failed test case \n");
printf(" -f No forking \n");
printf(" -t No timeout limit \n");
printf(" -v More verbosity, maximum is 3 -vvv \n");
@@ -155,10 +155,7 @@ int bt_run_test_fn(int (*fn)(const void *), const void *fn_arg, int timeout)
int result;
alarm(timeout);
- if (fn_arg)
- result = fn(fn_arg);
- else
- result = ((int (*)(void))fn)();
+ result = fn(fn_arg);
if (!bt_suite_result)
result = 0;
@@ -226,6 +223,9 @@ bt_log_result(int result, const char *fmt, va_list argptr)
result_str = is_terminal ? BT_PROMPT_FAIL : BT_PROMPT_FAIL_NO_COLOR;
printf("%s\n", result_str);
+
+ if (do_die && !result)
+ abort();
}
/**
@@ -486,6 +486,8 @@ void cmd_check_config(char *name UNUSED) {}
void cmd_reconfig(char *name UNUSED, int type UNUSED, int timeout UNUSED) {}
void cmd_reconfig_confirm(void) {}
void cmd_reconfig_undo(void) {}
+void cmd_reconfig_status(void) {}
+void cmd_graceful_restart(void) {}
void cmd_shutdown(void) {}
void cmd_reconfig_undo_notify(void) {}
diff --git a/test/birdtest.h b/test/birdtest.h
index 4443bfc1..dacfb095 100644
--- a/test/birdtest.h
+++ b/test/birdtest.h
@@ -33,7 +33,8 @@ extern const char *bt_test_id;
void bt_init(int argc, char *argv[]);
int bt_exit_value(void);
int bt_test_suite_base(int (*test_fn)(const void *), const char *test_id, const void *test_fn_argument, int forked, int timeout, const char *dsc, ...);
-long int bt_random(void);
+static inline u64 bt_random(void)
+{ return ((u64) random() & 0xffffffff) | ((u64) random() << 32); }
void bt_log_suite_result(int result, const char *fmt, ...);
void bt_log_suite_case_result(int result, const char *fmt, ...);
@@ -41,7 +42,7 @@ void bt_log_suite_case_result(int result, const char *fmt, ...);
#define BT_TIMEOUT 5 /* Default timeout in seconds */
#define BT_FORKING 1 /* Forking is enabled in default */
-#define BT_RANDOM_SEED 982451653
+#define BT_RANDOM_SEED 0x5097d2bb
#define BT_BUFFER_SIZE 10000
@@ -54,11 +55,13 @@ void bt_log_suite_case_result(int result, const char *fmt, ...);
#define BT_PROMPT_FAIL_NO_COLOR " [" "FAIL" "] "
#define BT_PROMPT_OK_FAIL_STRLEN 8 /* strlen ' [FAIL] ' */
+static inline int bt_test_fn_noarg(const void *cp) { return ((int (*)(void)) cp)(); }
+
#define bt_test_suite(fn, dsc, ...) \
bt_test_suite_extra(fn, BT_FORKING, BT_TIMEOUT, dsc, ##__VA_ARGS__)
#define bt_test_suite_extra(fn, f, t, dsc, ...) \
- bt_test_suite_base((int (*)(const void *))fn, #fn, NULL, f, t, dsc, ##__VA_ARGS__)
+ bt_test_suite_base(bt_test_fn_noarg, #fn, fn, f, t, dsc, ##__VA_ARGS__)
#define bt_test_suite_arg(fn, arg, dsc, ...) \
bt_test_suite_arg_extra(fn, arg, BT_FORKING, BT_TIMEOUT, dsc, ##__VA_ARGS__)