diff options
45 files changed, 1095 insertions, 172 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d1a824ff..c913d0f8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -60,14 +60,6 @@ stages: - linux - amd64 -build-debian-8-amd64: - <<: *build-linux - image: registry.nic.cz/labs/bird:debian-8-amd64 - -build-debian-8-i386: - <<: *build-linux - image: registry.nic.cz/labs/bird:debian-8-i386 - build-debian-9-amd64: <<: *build-linux image: registry.nic.cz/labs/bird:debian-9-amd64 @@ -140,18 +132,14 @@ build-fedora-34-amd64: <<: *build-linux image: registry.nic.cz/labs/bird:fedora-33-amd64 -build-centos-7-amd64: - <<: *build-linux - image: registry.nic.cz/labs/bird:centos-7-amd64 +#build-centos-7-amd64: +# <<: *build-linux +# image: registry.nic.cz/labs/bird:centos-7-amd64 build-centos-8-amd64: <<: *build-linux image: registry.nic.cz/labs/bird:centos-8-amd64 -build-ubuntu-14_04-amd64: - <<: *build-linux - image: registry.nic.cz/labs/bird:ubuntu-14.04-amd64 - build-ubuntu-16_04-amd64: <<: *build-linux image: registry.nic.cz/labs/bird:ubuntu-16.04-amd64 @@ -232,28 +220,6 @@ build-opensuse-15.3-amd64: paths: - pkg/pkgs/* -# Dpkg error: PATH is not set -#pkg-debian-8-amd64: -# <<: *pkg-deb -# needs: [build-debian-8-amd64] -# image: registry.nic.cz/labs/bird:debian-8-amd64 - -# Dpkg error: PATH is not set -#pkg-debian-8-i386: -# <<: *pkg-deb -# needs: [build-debian-8-i386] -# image: registry.nic.cz/labs/bird:debian-8-i386 - -pkg-debian-9-amd64: - <<: *pkg-deb - needs: [build-debian-9-amd64] - image: registry.nic.cz/labs/bird:debian-9-amd64 - -pkg-debian-9-i386: - <<: *pkg-deb - needs: [build-debian-9-i386] - image: registry.nic.cz/labs/bird:debian-9-i386 - pkg-debian-10-amd64: <<: *pkg-deb needs: [build-debian-10-amd64] @@ -294,12 +260,12 @@ pkg-fedora-34-amd64: needs: [build-fedora-34-amd64] image: registry.nic.cz/labs/bird:fedora-34-amd64 -pkg-centos-7-amd64: - <<: *pkg-rpm-wa - variables: - LC_ALL: en_US.UTF-8 - needs: [build-centos-7-amd64] - image: registry.nic.cz/labs/bird:centos-7-amd64 +#pkg-centos-7-amd64: +# <<: *pkg-rpm-wa +# variables: +# LC_ALL: en_US.UTF-8 +# needs: [build-centos-7-amd64] +# image: registry.nic.cz/labs/bird:centos-7-amd64 pkg-centos-8-amd64: <<: *pkg-rpm-wa @@ -1,3 +1,9 @@ +Version 2.13.1 (2023-06-23) + o BGP: Fix role check when no capability option is present + o Filter: Fixed segfault when a case option had an empty block + + This is a bugfix version. + Version 2.13 (2023-04-21) o Babel: IPv4 via IPv6 extension (RFC 9229) o Babel: Improve authentication on lossy networks diff --git a/conf/cf-lex.l b/conf/cf-lex.l index 9555949d..9025a84d 100644 --- a/conf/cf-lex.l +++ b/conf/cf-lex.l @@ -255,12 +255,17 @@ WHITE [ \t] return IP4; } -{XIGIT}{2}((:{XIGIT}{2}){15,}|({XIGIT}{2}){15,}) { - char *s = yytext; +({XIGIT}{2}){16,}|{XIGIT}{2}(:{XIGIT}{2}){15,}|hex:({XIGIT}{2}(:?{XIGIT}{2})*)? { + char *s, *sb = yytext; size_t len = 0, i; struct bytestring *bytes; byte *b; + /* skip 'hex:' prefix */ + if (sb[0] == 'h' && sb[1] == 'e' && sb[2] == 'x' && sb[3] == ':') + sb += 4; + + s = sb; while (*s) { len++; s += 2; @@ -271,7 +276,7 @@ WHITE [ \t] bytes->length = len; b = &bytes->data[0]; - s = yytext; + s = sb; errno = 0; for (i = 0; i < len; i++) { *b = bstrtobyte16(s); diff --git a/conf/confbase.Y b/conf/confbase.Y index 3e8f5807..3dd5fed7 100644 --- a/conf/confbase.Y +++ b/conf/confbase.Y @@ -94,7 +94,7 @@ CF_DECLS struct channel_limit cl; struct timeformat *tf; mpls_label_stack *mls; - struct bytestring *bs; + const struct bytestring *bs; } %token END CLI_MARKER INVALID_TOKEN ELSECOL DDOT diff --git a/distro/config/apkg.toml b/distro/config/apkg.toml index c70306d6..73a79a3b 100644 --- a/distro/config/apkg.toml +++ b/distro/config/apkg.toml @@ -6,3 +6,7 @@ make_archive_script = "tools/make-dev-archive" [upstream] # needed for get-archive archive_url = "https://bird.network.cz/download/bird-{{ version }}.tar.gz" + +[apkg] +# apkg compat level +compat = 3 diff --git a/distro/pkg/deb/bird.xml b/distro/pkg/deb/bird.xml index 4ba3868f..2cc69575 100644 --- a/distro/pkg/deb/bird.xml +++ b/distro/pkg/deb/bird.xml @@ -65,11 +65,23 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ <email>&dhemail;</email> </address> </author> + <author> + <firstname>Jakub</firstname> + <surname>Ružička</surname> + <contrib>Updated this manpage for birdcl.</contrib> + <address> + <email>jakub.ruzicka@nic.cz</email> + </address> + </author> </authorgroup> <copyright> <year>2010</year> <holder>&dhusername;</holder> </copyright> + <copyright> + <year>2022</year> + <holder>Jakub Ružička</holder> + </copyright> <legalnotice> <para>This manual page was written for the Debian system (and may be used by others).</para> @@ -95,6 +107,10 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ <refname>birdc</refname> <refpurpose>BIRD Internet Routing Daemon remote control</refpurpose> </refnamediv> + <refnamediv> + <refname>birdcl</refname> + <refpurpose>BIRD Internet Routing Daemon remote control light</refpurpose> + </refnamediv> <refsynopsisdiv> <cmdsynopsis> @@ -118,6 +134,13 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ <arg choice="opt"><option>-s <replaceable>control-socket</replaceable></option></arg> <arg choice="opt"><option>-v</option></arg> </cmdsynopsis> + <cmdsynopsis> + <command>birdcl</command> + <arg choice="opt"><option>-l</option></arg> + <arg choice="opt"><option>-r</option></arg> + <arg choice="opt"><option>-s <replaceable>control-socket</replaceable></option></arg> + <arg choice="opt"><option>-v</option></arg> + </cmdsynopsis> </refsynopsisdiv> <refsect1 id="description"> @@ -136,7 +159,10 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ communicate. Once started, <command>bird</command> will give access to an interactive shell: commands can be completed with TAB and help can be requested by pressing the key `?'. More documentation on - the available commands can be foung on the website, see below.</para> + the available commands can be found on the website, see below.</para> + <para><command>birdcl</command> is a light version of <command>birdc</command> + remote control for <command>bird</command> without readline/ncurses support. + TAB completion isn't available.</para> </refsect1> <refsect1 id="options"> @@ -230,7 +256,7 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ </varlistentry> </variablelist> - <para>The <command>birdc</command> accepts these options:</para> + <para><command>birdc</command> and <command>birdcl</command> accept these options:</para> <variablelist> <varlistentry> <term><option>-l</option></term> @@ -279,8 +305,7 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ <refsect1 id="see_also"> <title>SEE ALSO</title> - <para>More documentation con be found on the website: + <para>More documentation can be found on the website: https://bird.network.cz/.</para> </refsect1> </refentry> - diff --git a/distro/pkg/deb/bird2.lintian-overrides b/distro/pkg/deb/bird2.lintian-overrides deleted file mode 100644 index e64c0ef6..00000000 --- a/distro/pkg/deb/bird2.lintian-overrides +++ /dev/null @@ -1 +0,0 @@ -bird2: binary-without-manpage usr/sbin/birdcl diff --git a/distro/pkg/deb/bird2.manpages b/distro/pkg/deb/bird2.manpages index 94898ea1..cd80062c 100644 --- a/distro/pkg/deb/bird2.manpages +++ b/distro/pkg/deb/bird2.manpages @@ -1,2 +1,3 @@ bird.8 birdc.8 +birdcl.8 diff --git a/distro/pkg/deb/changelog b/distro/pkg/deb/changelog index d371bc06..cdfe3d95 100644 --- a/distro/pkg/deb/changelog +++ b/distro/pkg/deb/changelog @@ -2,4 +2,4 @@ bird2 ({{ version }}-cznic.{{ release }}) unstable; urgency=medium * upstream package - -- Jakub Ružička <jakub.ruzicka@nic.cz> Mon, 29 Mar 2021 14:15:50 +0000 + -- Jakub Ružička <jakub.ruzicka@nic.cz> {{ now }} diff --git a/distro/pkg/deb/compat b/distro/pkg/deb/compat index ec635144..b4de3947 100644 --- a/distro/pkg/deb/compat +++ b/distro/pkg/deb/compat @@ -1 +1 @@ -9 +11 diff --git a/distro/pkg/deb/control b/distro/pkg/deb/control index d431022b..638795e2 100644 --- a/distro/pkg/deb/control +++ b/distro/pkg/deb/control @@ -5,18 +5,18 @@ Build-Depends: bison, debhelper, docbook-xsl, flex, - libncurses5-dev, + libncurses-dev, libreadline-dev | libreadline6-dev | libreadline5-dev, libssh-gcrypt-dev, - linuxdoc-tools-latex, m4, - opensp, quilt, - texlive-latex-extra, xsltproc +Build-Depends-Indep: linuxdoc-tools-latex, + opensp, + texlive-latex-extra Maintainer: Jakub Ružička <jakub.ruzicka@nic.cz> Uploaders: Ondřej Surý <ondrej@debian.org> -Standards-Version: 4.3.0 +Standards-Version: 4.6.2 Vcs-Browser: https://salsa.debian.org/debian/bird2 Vcs-Git: https://salsa.debian.org/debian/bird2.git Homepage: https://bird.network.cz/ @@ -26,7 +26,6 @@ Architecture: kfreebsd-any linux-any Pre-Depends: init-system-helpers, ${misc:Pre-Depends} Depends: adduser, - lsb-base, ucf, ${misc:Depends}, ${shlibs:Depends} diff --git a/distro/pkg/deb/rules b/distro/pkg/deb/rules index 5630ed1c..1740fb95 100755 --- a/distro/pkg/deb/rules +++ b/distro/pkg/deb/rules @@ -23,8 +23,7 @@ LDFLAGS += -g -O2 -fno-strict-aliasing -fno-strict-overflow -fPIC -Wl,-z,defs -W override_dh_auto_configure: CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" dh_auto_configure -- $(COMMON_FLAGS) --with-protocols=all -override_dh_auto_build: - dh_auto_build +override_dh_auto_build-indep: dh_auto_build -- docs override_dh_auto_install: @@ -47,10 +46,10 @@ override_dh_installman: bird.8 override_dh_clean: dh_clean - -rm -f bird.8 birdc.8 + -rm -f bird.8 birdc.8 birdcl.8 override_dh_missing: - dh_missing --fail-missing + dh_missing override_dh_compress: dh_compress -X.conf diff --git a/distro/pkg/deb/watch b/distro/pkg/deb/watch index 1874bfbb..cec2d664 100644 --- a/distro/pkg/deb/watch +++ b/distro/pkg/deb/watch @@ -1,2 +1,2 @@ -version=3 +version=4 https://bird.network.cz/download/bird-(2\.[\d.]+).tar.gz diff --git a/distro/pkg/rpm/bird.spec b/distro/pkg/rpm/bird.spec index cd51ef51..eb6b8aa9 100644 --- a/distro/pkg/rpm/bird.spec +++ b/distro/pkg/rpm/bird.spec @@ -1,17 +1,21 @@ %global _hardened_build 1 %global _without_doc 1 +%{!?_rundir:%global _rundir %%{_localstatedir}/run} Name: bird Version: {{ version }} Release: cznic.{{ release }}%{?dist} Summary: BIRD Internet Routing Daemon +Group: System Environment/Daemons License: GPL-2.0-or-later URL: https://bird.network.cz/ Source0: https://bird.network.cz/download/bird-%{version}.tar.gz Source1: bird.service Source2: bird.tmpfilesd +Source3: bird.sysusersd +BuildRequires: autoconf BuildRequires: flex BuildRequires: bison BuildRequires: ncurses-devel @@ -20,14 +24,13 @@ BuildRequires: sed BuildRequires: gcc BuildRequires: make BuildRequires: libssh-devel -%if 0%{?fedora} || (0%{?rhel} && 0%{?rhel} > 7) -BuildRequires: systemd-rpm-macros -%else -BuildRequires: systemd +%if 0%{?rhel} && 0%{?rhel} < 8 +# http://trubka.network.cz/pipermail/bird-users/2019-August/013631.html +BuildRequires: devtoolset-8-toolchain %endif - -Obsoletes: bird6 < 2.0.2-1 -Provides: bird6 = %{version}-%{release} +BuildRequires: systemd-rpm-macros +%{?systemd_requires} +%{?sysusers_requires_compat} %description BIRD is a dynamic IP routing daemon supporting both, IPv4 and IPv6, Border @@ -41,6 +44,7 @@ powerful language for route filtering. %if 0%{!?_without_doc:1} %package doc Summary: Documentation for BIRD Internet Routing Daemon +Group: Documentation BuildRequires: linuxdoc-tools sgml-common perl(FindBin) BuildArch: noarch @@ -57,9 +61,13 @@ powerful language for route filtering. %endif %prep -%setup -q +%setup -q -n bird-%{version} %build +%if 0%{?rhel} && 0%{?rhel} < 8 +. /opt/rh/devtoolset-8/enable +%endif + %configure --runstatedir=%{_rundir}/bird %make_build all %{!?_without_doc:docs} @@ -70,17 +78,18 @@ powerful language for route filtering. install -d %{buildroot}{%{_localstatedir}/lib/bird,%{_rundir}/bird} install -D -p -m 0644 %{SOURCE1} %{buildroot}%{_unitdir}/bird.service install -D -p -m 0644 %{SOURCE2} %{buildroot}%{_tmpfilesdir}/bird.conf +install -D -p -m 0644 %{SOURCE3} %{buildroot}%{_sysusersdir}/bird.conf {% endraw %} %check +%if 0%{?rhel} && 0%{?rhel} < 8 +. /opt/rh/devtoolset-8/enable +%endif + make test %pre -getent group bird >/dev/null || groupadd -r bird -getent passwd bird >/dev/null || \ - useradd -r -g bird -d %{_localstatedir}/lib/bird -s /sbin/nologin \ - -c "BIRD daemon user" bird -exit 0 +%sysusers_create_compat %{SOURCE3} %post %systemd_post bird.service @@ -95,12 +104,13 @@ exit 0 %doc NEWS README %attr(0640,root,bird) %config(noreplace) %{_sysconfdir}/bird.conf %{_unitdir}/bird.service +%{_sysusersdir}/bird.conf %{_tmpfilesdir}/bird.conf %{_sbindir}/bird %{_sbindir}/birdc %{_sbindir}/birdcl %dir %attr(0750,bird,bird) %{_localstatedir}/lib/bird -%dir %attr(0750,bird,bird) %ghost %{_rundir}/bird +%dir %attr(0750,bird,bird) %{_rundir}/bird %if 0%{!?_without_doc:1} %files doc diff --git a/distro/pkg/rpm/bird.sysusersd b/distro/pkg/rpm/bird.sysusersd new file mode 100644 index 00000000..1c808666 --- /dev/null +++ b/distro/pkg/rpm/bird.sysusersd @@ -0,0 +1,2 @@ +#Type Name ID GECOS Home directory Shell +u bird - "BIRD daemon user" /var/lib/bird - diff --git a/distro/tests/control b/distro/tests/control new file mode 100644 index 00000000..f588af8f --- /dev/null +++ b/distro/tests/control @@ -0,0 +1,3 @@ +Tests: test-bird.sh +Restrictions: needs-root +Depends: bird2, iproute2 diff --git a/distro/tests/test-bird.sh b/distro/tests/test-bird.sh new file mode 100755 index 00000000..464be32f --- /dev/null +++ b/distro/tests/test-bird.sh @@ -0,0 +1,105 @@ +#!/bin/bash + +set -e + +LOCAL=2001:db8:dead:: + +EXTERNAL=2001:db8:beef:: +EXTERNAL_NET=${EXTERNAL}/48 +EXTERNAL_NH=${LOCAL}beef + +LEARN=2001:db8:feed:: +LEARN_NET=${LEARN}/48 +LEARN_NH=${LOCAL}feed + +IFACE=bird-test-dummy +IFACE_EXISTS=false + +BIRD_RUNNING=false + +D=$(mktemp -d) +pushd ${D} >/dev/null + +stop_bird() { + birdc -l down >/dev/null + sleep 1 + grep -q "<FATAL> Shutdown completed" bird.log + [ ! -e bird.pid ] + [ ! -e bird.ctl ] +} + +cleanup() { + if ${BIRD_RUNNING}; then + stop_bird + if [ -e bird.pid ]; then + kill -9 $(<bird.pid) + fi + fi + + if ${IFACE_EXISTS}; then + ip link del ${IFACE} + fi + + + popd > /dev/null + rm -rf ${D} +} + +failed() { + cleanup + exit 1 +} + +trap failed ERR +trap failed INT +trap failed HUP + +ip link add ${IFACE} type dummy +IFACE_EXISTS=true + +ip link set ${IFACE} up +ip -6 addr add ${LOCAL}/64 dev bird-test-dummy + +ip -6 route add ${LEARN_NET} via ${LEARN_NH} + +cat >bird.conf <<EOF +log "bird.log" all; + +protocol device {} + +protocol kernel { + ipv6 { import all; export all; }; + learn; +} + +protocol static { + ipv6; + route ${EXTERNAL_NET} via ${EXTERNAL_NH}; +} +EOF + +bird -l -P bird.pid + +if [ ! -S bird.ctl ] || [ ! -f bird.pid ] || [ ! -f bird.log ]; then + failed +fi + +BIRD_RUNNING=true + +ROUTE_INSERTED=false +for _ in $(seq 10); do + if ip -6 route show ${EXTERNAL_NET} | egrep -q "${EXTERNAL_NET} via ${EXTERNAL_NH} dev ${IFACE} proto bird metric [0-9]+ pref medium"; then + ROUTE_INSERTED=true + break + fi + sleep 1 +done + +$ROUTE_INSERTED || failed + +if birdc -l show route "${LEARN_NET}" | egrep -q "Network not found"; then + failed +fi + +cleanup +exit 0 diff --git a/doc/bird.sgml b/doc/bird.sgml index 39eae4cd..29e12b7a 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -1918,7 +1918,7 @@ protocol babel [<name>] { ipv6 [sadr] { <channel config> }; randomize router id <switch>; interface <interface pattern> { - type <wired|wireless>; + type <wired|wireless|tunnel>; rxcost <number>; limit <number>; hello interval <time>; @@ -1932,6 +1932,11 @@ protocol babel [<name>] { next hop ipv4 <address>; next hop ipv6 <address>; extended next hop <switch>; + rtt cost <number>; + rtt min <time>; + rtt max <time>; + rtt decay <number>; + send timestamps <switch>; authentication none|mac [permissive]; password "<text>"; password "<text>" { @@ -1962,15 +1967,16 @@ protocol babel [<name>] { router ID every time it starts up, which avoids this problem at the cost of not having stable router IDs in the network. Default: no. - <tag><label id="babel-type">type wired|wireless </tag> - This option specifies the interface type: Wired or wireless. On wired - interfaces a neighbor is considered unreachable after a small number of - Hello packets are lost, as described by <cf/limit/ option. On wireless + <tag><label id="babel-type">type wired|wireless|tunnel </tag> + This option specifies the interface type: Wired, wireless or tunnel. On + wired interfaces a neighbor is considered unreachable after a small number + of Hello packets are lost, as described by <cf/limit/ option. On wireless interfaces the ETX link quality estimation technique is used to compute the metrics of routes discovered over this interface. This technique will gradually degrade the metric of routes when packets are lost rather than - the more binary up/down mechanism of wired type links. Default: - <cf/wired/. + the more binary up/down mechanism of wired type links. A tunnel is like a + wired interface, but turns on RTT-based metrics with a default cost of 96. + Default: <cf/wired/. <tag><label id="babel-rxcost">rxcost <m/num/</tag> This option specifies the nominal RX cost of the interface. The effective @@ -2041,6 +2047,37 @@ protocol babel [<name>] { hop when IPv4 addresses are absent from the interface as described in <rfc id="9229">. Default: yes. + <tag><label id="babel-rtt-cost">rtt cost <m/number/</tag> + The RTT-based cost that will be applied to all routes from each neighbour + based on the measured RTT to that neighbour. If this value is set, + timestamps will be included in generated Babel Hello and IHU messages, and + (if the neighbours also have timestamps enabled), the RTT to each + neighbour will be computed. An additional cost is added to a neighbour if + its RTT is above the <ref id="babel-rtt-min" name="rtt min"> value + configured on the interface. The added cost scales linearly from 0 up to + the RTT cost configured in this option; the full cost is applied if the + neighbour RTT reaches the RTT configured in the <ref id="babel-rtt-max" + name="rtt max"> option (and for all RTTs above this value). Default: 0 + (disabled), except for tunnel interfaces, where it is 96. + + <tag><label id="babel-rtt-min">rtt min <m/time/ s|ms</tag> + The minimum RTT above which the RTT cost will start to be applied (scaling + linearly from zero up to the full cost). Default: 10 ms + + <tag><label id="babel-rtt-max">rtt max <m/time/ s|ms</tag> + The maximum RTT above which the full RTT cost will start be applied. + Default: 120 ms + + <tag><label id="babel-rtt-decay">rtt decay <m/number/</tag> + The decay factor used for the exponentional moving average of the RTT + samples from each neighbour, in units of 1/256. Higher values discards old + RTT samples faster. Must be between 1 and 256. Default: 42 + + <tag><label id="babel-send-timestamps">send timestamps <m/switch/</tag> + Whether to send the timestamps used for RTT calculation on this interface. + Sending the timestamps enables peers to calculate an RTT to this node, + even if no RTT cost is applied to the route metrics. Default: yes. + <tag><label id="babel-authentication">authentication none|mac [permissive]</tag> Selects authentication method to be used. <cf/none/ means that packets are not authenticated at all, <cf/mac/ means MAC authentication is @@ -2388,7 +2425,6 @@ avoid routing loops. <item> <rfc id="5065"> - AS confederations for BGP <item> <rfc id="5082"> - Generalized TTL Security Mechanism <item> <rfc id="5492"> - Capabilities Advertisement with BGP -<item> <rfc id="5549"> - Advertising IPv4 NLRI with an IPv6 Next Hop <item> <rfc id="5575"> - Dissemination of Flow Specification Rules <item> <rfc id="5668"> - 4-Octet AS Specific BGP Extended Community <item> <rfc id="6286"> - AS-Wide Unique BGP Identifier @@ -2403,6 +2439,7 @@ avoid routing loops. <item> <rfc id="8203"> - BGP Administrative Shutdown Communication <item> <rfc id="8212"> - Default EBGP Route Propagation Behavior without Policies <item> <rfc id="8654"> - Extended Message Support for BGP +<item> <rfc id="8950"> - Advertising IPv4 NLRI with an IPv6 Next Hop <item> <rfc id="9072"> - Extended Optional Parameters Length for BGP OPEN Message <item> <rfc id="9117"> - Revised Validation Procedure for BGP Flow Specifications <item> <rfc id="9234"> - Route Leak Prevention and Detection Using Roles @@ -3063,7 +3100,7 @@ be used in explicit configuration. associated network prefixes. This option provides an extension to use IPv4 next hops with IPv6 prefixes and vice versa. For IPv4 / VPNv4 channels, the behavior is controlled by the Extended Next Hop Encoding - capability, as described in <rfc id="5549">. For IPv6 / VPNv6 channels, + capability, as described in <rfc id="8950">. For IPv6 / VPNv6 channels, just IPv4-mapped IPv6 addresses are used, as described in <rfc id="4798"> and <rfc id="4659">. Default: off. @@ -5333,8 +5370,8 @@ as the destination becomes adjacent again. (i.e., they can be used multiple times for a route, one time for each nexthop). Syntactically, they are not separate options but just parts of <cf/route/ statement after each <cf/via/ statement, not separated by semicolons. E.g., -statement <cf/route 10.0.0.0/8 via 192.0.2.1 bfd weight 1 via 192.0.2.2 weight -2;/ describes a route with two nexthops, the first nexthop has two per-nexthop +statement <cf>route 10.0.0.0/8 via 192.0.2.1 bfd weight 1 via 192.0.2.2 weight +2;</cf> describes a route with two nexthops, the first nexthop has two per-nexthop options (<cf/bfd/ and <cf/weight 1/), the second nexthop has just <cf/weight 2/. <descrip> diff --git a/filter/decl.m4 b/filter/decl.m4 index 5b35b672..7c863bdc 100644 --- a/filter/decl.m4 +++ b/filter/decl.m4 @@ -200,7 +200,7 @@ 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 { +m4_define(LINEX_, `do if ($1) { fstk->estk[fstk->ecnt].pos = 0; fstk->estk[fstk->ecnt].line = $1; fstk->estk[fstk->ecnt].ventry = fstk->vcnt; @@ -227,9 +227,7 @@ if (!f_same(f1->fl$1, f2->fl$1)) return 0; FID_ITERATE_BODY()m4_dnl if (whati->fl$1) BUFFER_PUSH(fit->lines) = whati->fl$1; FID_INTERPRET_EXEC()m4_dnl -do { if (whati->fl$1) { - LINEX_(whati->fl$1); -} } while(0) +LINEX_(whati->fl$1) FID_INTERPRET_NEW()m4_dnl return whati->f$1 FID_INTERPRET_BODY()') diff --git a/filter/f-inst.c b/filter/f-inst.c index e4b47ff4..33436853 100644 --- a/filter/f-inst.c +++ b/filter/f-inst.c @@ -1327,7 +1327,6 @@ FID_HIC(,break,return NULL); } } - /* It is actually possible to have t->data NULL */ LINEX(t->data); } diff --git a/filter/test.conf b/filter/test.conf index 600c551e..e9e3af89 100644 --- a/filter/test.conf +++ b/filter/test.conf @@ -39,6 +39,18 @@ bt_test_same(onef, oneg, 1); bt_test_same(onef, twof, 0); /* + * Testing filter corner cases + * --------------------------- + */ + +function t_nothing() {} +bt_test_suite(t_nothing, "Testing nothing"); + +function t_metanothing() { t_nothing(); } +bt_test_suite(t_metanothing, "Testing meta nothing"); + + +/* * Testing boolean expressions * --------------------------- */ @@ -76,6 +88,14 @@ bt_test_suite(t_bool, "Testing boolean expressions"); * ---------------- */ +function aux_t_int(int t; int u) +{ + case t { + 1: {} + else: {} + } +} + define four = 4; define xyzzy = (120+10); define '1a-a1' = (xyzzy-100); @@ -120,7 +140,10 @@ function t_int() else: bt_assert(false); } - + aux_t_int(1, 2); + aux_t_int(1, 3); + aux_t_int(2, 3); + aux_t_int(2, 2); } bt_test_suite(t_int, "Testing integers"); diff --git a/filter/trie_test.c b/filter/trie_test.c index dc791280..5724e49f 100644 --- a/filter/trie_test.c +++ b/filter/trie_test.c @@ -28,12 +28,6 @@ struct f_prefix_node { struct f_prefix prefix; }; -static u32 -xrandom(u32 max) -{ - return (bt_random() % max); -} - static inline uint get_exp_random(void) { @@ -96,26 +90,9 @@ is_prefix_included(list *prefixes, const net_addr *needle) } static void -get_random_net(net_addr *net, int v6) -{ - if (!v6) - { - uint pxlen = xrandom(24)+8; - ip4_addr ip4 = ip4_from_u32((u32) bt_random()); - net_fill_ip4(net, ip4_and(ip4, ip4_mkmask(pxlen)), pxlen); - } - else - { - uint pxlen = xrandom(120)+8; - ip6_addr ip6 = ip6_build(bt_random(), bt_random(), bt_random(), bt_random()); - net_fill_ip6(net, ip6_and(ip6, ip6_mkmask(pxlen)), pxlen); - } -} - -static void get_random_prefix(struct f_prefix *px, int v6, int tight) { - get_random_net(&px->net, v6); + bt_random_net(&px->net, !v6 ? NET_IP4 : NET_IP6); if (tight) { @@ -379,7 +356,7 @@ select_random_prefix_subset(list *src[], net_addr dst[], int sn, int dn) struct f_prefix_node *px; WALK_LIST(px, *src[i]) { - if (xrandom(rnd) != 0) + if (bt_random_n(rnd) != 0) continue; net_copy(&dst[n], &px->prefix.net); @@ -395,7 +372,7 @@ done: /* Shuffle networks */ for (int i = 0; i < dn; i++) { - int j = xrandom(dn); + int j = bt_random_n(dn); if (i == j) continue; @@ -444,7 +421,7 @@ t_match_random_net(void) for (int i = 0; i < PREFIX_TESTS_NUM; i++) { net_addr net; - get_random_net(&net, v6); + bt_random_net(&net, !v6 ? NET_IP4 : NET_IP6); test_match_net(prefixes, trie, &net); } @@ -828,7 +805,7 @@ t_trie_walk_to_root(void) for (i = 0; i < (PREFIX_TESTS_NUM / 10); i++) { net_addr from; - get_random_net(&from, v6); + bt_random_net(&from, !v6 ? NET_IP4 : NET_IP6); net_addr found[129]; int found_num = find_covering_nets(pxset, num, &from, found); diff --git a/lib/birdlib.h b/lib/birdlib.h index e03bd0b2..b7226411 100644 --- a/lib/birdlib.h +++ b/lib/birdlib.h @@ -162,6 +162,7 @@ void bug(const char *msg, ...) NORET; void debug(const char *msg, ...); /* Printf to debug output */ void debug_safe(const char *msg); /* Printf to debug output, async-safe */ + /* Debugging */ #if defined(LOCAL_DEBUG) || defined(GLOBAL_DEBUG) @@ -196,10 +197,36 @@ asm( ); #endif + /* Pseudorandom numbers */ u32 random_u32(void); void random_init(void); void random_bytes(void *buf, size_t size); + +/* Hashing */ + +/* Constant parameter for non-parametrized hashes */ +#define HASH_PARAM 2902958171u + +/* Precomputed powers of HASH_PARAM */ +#define HASH_PARAM1 ((u64) HASH_PARAM) +#define HASH_PARAM2 (HASH_PARAM1 * HASH_PARAM) +#define HASH_PARAM3 (HASH_PARAM2 * HASH_PARAM) +#define HASH_PARAM4 (HASH_PARAM3 * HASH_PARAM) + +/* Reduce intermediate 64-bit value to final 32-bit value */ +static inline u32 hash_value(u64 a) +{ return ((u32) a) ^ ((u32) (a >> 32)); } + +static inline u64 u32_hash0(u32 v, u32 p, u64 acc) +{ return (acc + v) * p; } + +static inline u64 u64_hash0(u64 v, u32 p, u64 acc) +{ return u32_hash0(v >> 32, p, u32_hash0(v, p, acc)); } + +static inline u32 u64_hash(u64 v) +{ return hash_value(u64_hash0(v, HASH_PARAM, 0)); } + #endif @@ -194,14 +194,37 @@ static inline int ipa_nonzero2(ip_addr a) * Hash and compare functions */ +static inline u64 ip4_hash0(ip4_addr a, u32 p, u64 acc) +{ return (acc + _I(a)) * p; } + static inline u32 ip4_hash(ip4_addr a) -{ return u32_hash(_I(a)); } +{ + // return hash_value(ip4_hash0(a, HASH_PARAM, 0)); + + /* For some reason, the old hash works slightly better */ + return u32_hash(_I(a)); +} + +static inline u64 ip6_hash0(ip6_addr a, u32 p, u64 acc) +{ + acc += _I0(a); acc *= p; + acc += _I1(a); acc *= p; + acc += _I2(a); acc *= p; + acc += _I3(a); acc *= p; + return acc; +} static inline u32 ip6_hash(ip6_addr a) { - /* Returns a 32-bit hash key, although low-order bits are not mixed */ - u32 x = _I0(a) ^ _I1(a) ^ _I2(a) ^ _I3(a); - return x ^ (x << 16) ^ (x << 24); + // return hash_value(ip6_hash0(a, HASH_PARAM, 0)); + + /* Just use the expanded form */ + u64 acc = + _I0(a) * HASH_PARAM4 + + _I1(a) * HASH_PARAM3 + + _I2(a) * HASH_PARAM2 + + _I3(a) * HASH_PARAM1; + return hash_value(acc); } static inline int ip4_compare(ip4_addr a, ip4_addr b) @@ -479,39 +479,47 @@ static inline void net_copy_mpls(net_addr_mpls *dst, const net_addr_mpls *src) { memcpy(dst, src, sizeof(net_addr_mpls)); } -/* XXXX */ -static inline u32 u64_hash(u64 a) -{ return u32_hash(a); } +static inline u32 px4_hash(ip4_addr prefix, u32 pxlen) +{ return ip4_hash(prefix) ^ (pxlen << 26); } + +static inline u32 px6_hash(ip6_addr prefix, u32 pxlen) +{ return ip6_hash(prefix) ^ (pxlen << 26); } static inline u32 net_hash_ip4(const net_addr_ip4 *n) -{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26); } +{ return px4_hash(n->prefix, n->pxlen); } static inline u32 net_hash_ip6(const net_addr_ip6 *n) -{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26); } +{ return px6_hash(n->prefix, n->pxlen); } static inline u32 net_hash_vpn4(const net_addr_vpn4 *n) -{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26) ^ u64_hash(n->rd); } +{ + u64 acc = ip4_hash0(n->prefix, HASH_PARAM, 0) ^ (n->pxlen << 26); + return hash_value(u64_hash0(n->rd, HASH_PARAM, acc)); +} static inline u32 net_hash_vpn6(const net_addr_vpn6 *n) -{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26) ^ u64_hash(n->rd); } +{ + u64 acc = ip6_hash0(n->prefix, HASH_PARAM, 0) ^ (n->pxlen << 26); + return hash_value(u64_hash0(n->rd, HASH_PARAM, acc)); +} static inline u32 net_hash_roa4(const net_addr_roa4 *n) -{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26); } +{ return px4_hash(n->prefix, n->pxlen); } static inline u32 net_hash_roa6(const net_addr_roa6 *n) -{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26); } +{ return px6_hash(n->prefix, n->pxlen); } static inline u32 net_hash_flow4(const net_addr_flow4 *n) -{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26); } +{ return px4_hash(n->prefix, n->pxlen); } static inline u32 net_hash_flow6(const net_addr_flow6 *n) -{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26); } +{ return px6_hash(n->prefix, n->pxlen); } static inline u32 net_hash_ip6_sadr(const net_addr_ip6_sadr *n) -{ return net_hash_ip6((net_addr_ip6 *) n); } +{ return px6_hash(n->dst_prefix, n->dst_pxlen); } static inline u32 net_hash_mpls(const net_addr_mpls *n) -{ return n->label; } +{ return u32_hash(n->label); } u32 net_hash(const net_addr *a); diff --git a/lib/timer.h b/lib/timer.h index c5ea430c..0f87852b 100644 --- a/lib/timer.h +++ b/lib/timer.h @@ -46,6 +46,9 @@ extern struct timeloop main_timeloop; btime current_time(void); btime current_real_time(void); +/* In sysdep code */ +btime current_time_now(void); + //#define now (current_time() TO_S) //#define now_real (current_real_time() TO_S) extern btime boot_time; diff --git a/misc/bird.spec b/misc/bird.spec index af534c64..2fa6c2bf 100644 --- a/misc/bird.spec +++ b/misc/bird.spec @@ -1,6 +1,6 @@ Summary: BIRD Internet Routing Daemon Name: bird -Version: 2.13 +Version: 2.13.1 Release: 1 Copyright: GPL Group: Networking/Daemons diff --git a/nest/Makefile b/nest/Makefile index 163a1199..5a244c75 100644 --- a/nest/Makefile +++ b/nest/Makefile @@ -9,6 +9,6 @@ $(o)proto-build.c: Makefile $(lastword $(MAKEFILE_LIST)) $(objdir)/.dir-stamp prepare: $(o)proto-build.c -tests_src := a-set_test.c a-path_test.c +tests_src := a-set_test.c a-path_test.c rt-fib_test.c tests_targets := $(tests_targets) $(tests-target-files) tests_objs := $(tests_objs) $(src-o-files) diff --git a/nest/rt-fib_test.c b/nest/rt-fib_test.c new file mode 100644 index 00000000..2dd7ce8a --- /dev/null +++ b/nest/rt-fib_test.c @@ -0,0 +1,246 @@ +/* + * BIRD -- Forwarding Information Base -- Tests + * + * (c) 2023 CZ.NIC z.s.p.o. + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include "test/birdtest.h" +#include "test/bt-utils.h" + +#include "nest/route.h" + + +#define TESTS_NUM 10 +#define PREFIXES_NUM 400000 +#define PREFIX_TESTS_NUM 200000 +#define PREFIX_BENCH_MAX 1000000 +#define PREFIX_BENCH_NUM 10000000 + +struct test_node +{ + int pos; + struct fib_node n; +}; + +static inline int net_match(struct test_node *tn, net_addr *query, net_addr *data) +{ return (tn->pos < PREFIXES_NUM) && net_equal(query, &data[tn->pos]); } + +static int +t_match_random_net(void) +{ + bt_bird_init(); + bt_config_parse(BT_CONFIG_SIMPLE); + + for (int round = 0; round < TESTS_NUM; round++) + { + int type = !(round & 1) ? NET_IP4 : NET_IP6; + + pool *p = rp_new(&root_pool, "FIB pool"); + net_addr *nets = bt_random_nets(type, PREFIXES_NUM); + + /* Make FIB structure */ + struct fib f; + fib_init(&f, &root_pool, type, sizeof(struct test_node), OFFSETOF(struct test_node, n), 4, NULL); + + for (int i = 0; i < PREFIXES_NUM; i++) + { + struct test_node *tn = fib_get(&f, &nets[i]); + bt_assert(!tn->pos || net_match(tn, &nets[i], nets)); + tn->pos = i; + } + + /* Test (mostly) negative matches */ + for (int i = 0; i < PREFIX_TESTS_NUM; i++) + { + net_addr net; + bt_random_net(&net, type); + + struct test_node *tn = fib_find(&f, &net); + bt_assert(!tn || net_match(tn, &net, nets)); + } + + /* Test positive matches */ + for (int i = 0; i < PREFIX_TESTS_NUM; i++) + { + int j = bt_random_n(PREFIXES_NUM); + + struct test_node *tn = fib_find(&f, &nets[j]); + bt_assert(tn && net_match(tn, &nets[j], nets)); + } + + rfree(p); + tmp_flush(); + } + + bt_bird_cleanup(); + return 1; +} + +static int +t_fib_walk(void) +{ + bt_bird_init(); + bt_config_parse(BT_CONFIG_SIMPLE); + + for (int round = 0; round < TESTS_NUM; round++) + { + int type = !(round & 1) ? NET_IP4 : NET_IP6; + + pool *p = rp_new(&root_pool, "FIB pool"); + net_addr *nets = bt_random_nets(type, PREFIXES_NUM); + byte *marks = tmp_allocz(PREFIXES_NUM); + + /* Make FIB structure */ + struct fib f; + fib_init(&f, p, type, sizeof(struct test_node), OFFSETOF(struct test_node, n), 4, NULL); + + for (int i = 1; i < PREFIXES_NUM; i++) + { + struct test_node *tn = fib_get(&f, &nets[i]); + bt_assert(!tn->pos || net_match(tn, &nets[i], nets)); + if (tn->pos) + { + /* Mark dupicate nets */ + bt_assert(!marks[tn->pos]); + marks[tn->pos] = 1; + } + tn->pos = i; + } + + /* Walk FIB and mark nets */ + FIB_WALK(&f, struct test_node, tn) + { + bt_assert(!marks[tn->pos]); + marks[tn->pos] = 1; + } + FIB_WALK_END; + + /* Check in all nets are marked */ + for (int i = 1; i < PREFIXES_NUM; i++) + bt_assert(marks[i]); + + rfree(p); + tmp_flush(); + } + + bt_bird_cleanup(); + return 1; +} + +static int +benchmark_fib_dataset(const char *filename, int type) +{ + net_addr *nets, *test_r, *test_s; + uint n = PREFIX_BENCH_MAX; + int tn = PREFIX_BENCH_NUM; + int match; + + bt_reset_suite_case_timer(); + bt_log_suite_case_result(1, "Reading %s", filename, n); + nets = bt_read_net_file(filename, type, &n); + bt_log_suite_case_result(1, "Read net data, %u nets", n); + bt_reset_suite_case_timer(); + + pool *p = rp_new(&root_pool, "FIB pool"); + + /* Make FIB structure */ + struct fib f; + fib_init(&f, p, type, sizeof(struct test_node), OFFSETOF(struct test_node, n), 0, NULL); + + for (int i = 0; i < (int) n; i++) + { + struct test_node *tn = fib_get(&f, &nets[i]); + tn->pos = i; + } + + bt_log_suite_case_result(1, "Fill FIB structure, %u nets, order %u", n, f.hash_order); + bt_reset_suite_case_timer(); + + /* Compute FIB size */ + size_t fib_size = rmemsize(p).effective * 1000 / (1024*1024); + bt_log_suite_case_result(1, "FIB size: %u.%03u MB", (uint) (fib_size / 1000), (uint) (fib_size % 1000)); + + /* Compute FIB histogram */ + uint hist[16] = {}; + uint sum = 0; + for (uint i = 0; i < f.hash_size; i++) + { + int len = 0; + for (struct fib_node *fn = f.hash_table[i]; fn; fn = fn->next) + len++; + + sum += len; + len = MIN(len, 15); + hist[len]++; + } + bt_log_suite_case_result(1, "FIB histogram:"); + for (uint i = 0; i < 16; i++) + if (hist[i]) + bt_log_suite_case_result(1, "%02u: %8u", i, hist[i]); + + uint avg = (sum * 1000) / (f.hash_size - hist[0]); + bt_log_suite_case_result(1, "FIB chain length: %u.%03u", (uint) (avg / 1000), (uint) (avg % 1000)); + bt_reset_suite_case_timer(); + + /* Make test data */ + test_r = bt_random_nets(type, tn); + test_s = bt_random_net_subset(nets, n, tn); + + bt_log_suite_case_result(1, "Make test data, 2x %u nets", tn); + bt_reset_suite_case_timer(); + + /* Test (mostly negative) random matches */ + match = 0; + for (int i = 0; i < tn; i++) + if (fib_find(&f, &test_r[i])) + match++; + + bt_log_suite_case_result(1, "Random match, %d / %d matches", match, tn); + bt_reset_suite_case_timer(); + + /* Test (positive) subset matches */ + match = 0; + for (int i = 0; i < tn; i++) + if (fib_find(&f, &test_s[i])) + match++; + + bt_log_suite_case_result(1, "Subset match, %d / %d matches", match, tn); + bt_log_suite_case_result(1, ""); + bt_reset_suite_case_timer(); + + rfree(p); + tmp_flush(); + return 1; +} + +static int UNUSED +t_bench_fib_datasets(void) +{ + bt_bird_init(); + bt_config_parse(BT_CONFIG_SIMPLE); + + /* Specific datasets, not included */ + benchmark_fib_dataset("fib-data-bgp-v4-1", NET_IP4); + benchmark_fib_dataset("fib-data-bgp-v4-10", NET_IP4); + benchmark_fib_dataset("fib-data-bgp-v6-1", NET_IP6); + benchmark_fib_dataset("fib-data-bgp-v6-10", NET_IP6); + + bt_bird_cleanup(); + + return 1; +} + +int +main(int argc, char *argv[]) +{ + bt_init(argc, argv); + + bt_test_suite(t_match_random_net, "Testing random prefix matching"); + bt_test_suite(t_fib_walk, "Testing FIB_WALK() on random FIB"); + + // bt_test_suite(t_bench_fib_datasets, "Benchmark FIB from datasets by random subset of nets"); + + return bt_exit_value(); +} diff --git a/proto/babel/babel.c b/proto/babel/babel.c index 9f33dd34..7f0cca73 100644 --- a/proto/babel/babel.c +++ b/proto/babel/babel.c @@ -596,6 +596,7 @@ babel_update_cost(struct babel_neighbor *nbr) switch (cf->type) { case BABEL_IFACE_TYPE_WIRED: + case BABEL_IFACE_TYPE_TUNNEL: /* k-out-of-j selection - Appendix 2.1 in the RFC. */ /* Link is bad if less than cf->limit/16 of expected hellos were received */ @@ -624,6 +625,24 @@ babel_update_cost(struct babel_neighbor *nbr) break; } + if (cf->rtt_cost && nbr->srtt > cf->rtt_min) + { + uint rtt_cost = cf->rtt_cost; + + if (nbr->srtt < cf->rtt_max) + { + uint rtt_interval = cf->rtt_max TO_US - cf->rtt_min TO_US; + uint rtt_diff = (nbr->srtt TO_US - cf->rtt_min TO_US); + + rtt_cost = (rtt_cost * rtt_diff) / rtt_interval; + } + + txcost = MIN(txcost + rtt_cost, BABEL_INFINITY); + + TRACE(D_EVENTS, "Added RTT cost %u to nbr %I on %s with srtt %t ms", + rtt_cost, nbr->addr, nbr->ifa->iface->name, nbr->srtt * 1000); + } + done: /* If RX cost changed, send IHU with next Hello */ if (rxcost != nbr->rxcost) @@ -854,6 +873,12 @@ babel_build_ihu(union babel_msg *msg, struct babel_iface *ifa, struct babel_neig msg->ihu.rxcost = n->rxcost; msg->ihu.interval = ifa->cf->ihu_interval; + if (n->last_tstamp_rcvd && ifa->cf->rtt_send) + { + msg->ihu.tstamp = n->last_tstamp; + msg->ihu.tstamp_rcvd = n->last_tstamp_rcvd TO_US; + } + TRACE(D_PACKETS, "Sending IHU for %I with rxcost %d interval %t", msg->ihu.addr, msg->ihu.rxcost, (btime) msg->ihu.interval); } @@ -893,6 +918,9 @@ babel_send_hello(struct babel_iface *ifa, uint interval) msg.hello.seqno = ifa->hello_seqno++; msg.hello.interval = interval ?: ifa->cf->hello_interval; + if (ifa->cf->rtt_send) + msg.hello.tstamp = 1; /* real timestamp will be set on TLV write */ + TRACE(D_PACKETS, "Sending hello on %s with seqno %d interval %t", ifa->ifname, msg.hello.seqno, (btime) msg.hello.interval); @@ -1199,14 +1227,26 @@ babel_handle_hello(union babel_msg *m, struct babel_iface *ifa) msg->seqno, (btime) msg->interval); struct babel_neighbor *n = babel_get_neighbor(ifa, msg->sender); + struct babel_iface_config *cf = n->ifa->cf; int first_hello = !n->hello_cnt; + if (msg->tstamp) + { + n->last_tstamp = msg->tstamp; + n->last_tstamp_rcvd = msg->pkt_received; + } babel_update_hello_history(n, msg->seqno, msg->interval); babel_update_cost(n); /* Speed up session establishment by sending IHU immediately */ if (first_hello) - babel_send_ihu(ifa, n); + { + /* if using RTT, all IHUs must be paired with hellos */ + if(cf->rtt_send) + babel_send_hello(ifa, 0); + else + babel_send_ihu(ifa, n); + } } void @@ -1225,6 +1265,39 @@ babel_handle_ihu(union babel_msg *m, struct babel_iface *ifa) struct babel_neighbor *n = babel_get_neighbor(ifa, msg->sender); n->txcost = msg->rxcost; n->ihu_expiry = current_time() + BABEL_IHU_EXPIRY_FACTOR(msg->interval); + + if (msg->tstamp) + { + u32 rtt_sample = 0, pkt_received = msg->pkt_received TO_US; + int remote_time, full_time; + + /* processing time reported by peer */ + remote_time = (n->last_tstamp - msg->tstamp_rcvd); + /* time since we sent the last timestamp - RTT including remote time */ + full_time = (pkt_received - msg->tstamp); + + /* sanity checks */ + if (remote_time < 0 || full_time < 0 || + remote_time US_ > BABEL_RTT_MAX_VALUE || full_time US_ > BABEL_RTT_MAX_VALUE) + goto out; + + if (remote_time < full_time) + rtt_sample = full_time - remote_time; + + if (n->srtt) + { + uint decay = n->ifa->cf->rtt_decay; + + n->srtt = (decay * rtt_sample + (256 - decay) * n->srtt) / 256; + } + else + n->srtt = rtt_sample; + + TRACE(D_EVENTS, "RTT sample for neighbour %I on %s: %u us (srtt %t ms)", + n->addr, ifa->ifname, rtt_sample, n->srtt * 1000); + } + +out: babel_update_cost(n); } @@ -2199,8 +2272,8 @@ babel_show_neighbors(struct proto *P, const char *iff) } cli_msg(-1024, "%s:", p->p.name); - cli_msg(-1024, "%-25s %-10s %6s %6s %6s %7s %4s", - "IP address", "Interface", "Metric", "Routes", "Hellos", "Expires", "Auth"); + cli_msg(-1024, "%-25s %-10s %6s %6s %6s %7s %4s %9s", + "IP address", "Interface", "Metric", "Routes", "Hellos", "Expires", "Auth", "RTT (ms)"); WALK_LIST(ifa, p->interfaces) { @@ -2215,9 +2288,10 @@ babel_show_neighbors(struct proto *P, const char *iff) uint hellos = u32_popcount(n->hello_map); btime timer = (n->hello_expiry ?: n->init_expiry) - current_time(); - cli_msg(-1024, "%-25I %-10s %6u %6u %6u %7t %-4s", + cli_msg(-1024, "%-25I %-10s %6u %6u %6u %7t %-4s %9t", n->addr, ifa->iface->name, n->cost, rts, hellos, MAX(timer, 0), - n->auth_passed ? "Yes" : "No"); + n->auth_passed ? "Yes" : "No", + n->srtt * 1000); } } } diff --git a/proto/babel/babel.h b/proto/babel/babel.h index dcd303e1..edde4cab 100644 --- a/proto/babel/babel.h +++ b/proto/babel/babel.h @@ -53,10 +53,16 @@ #define BABEL_GARBAGE_INTERVAL (300 S_) #define BABEL_RXCOST_WIRED 96 #define BABEL_RXCOST_WIRELESS 256 +#define BABEL_RXCOST_RTT 96 #define BABEL_INITIAL_HOP_COUNT 255 #define BABEL_MAX_SEND_INTERVAL 5 /* Unused ? */ #define BABEL_INITIAL_NEIGHBOR_TIMEOUT (60 S_) +#define BABEL_RTT_MAX_VALUE (600 S_) +#define BABEL_RTT_MIN (10 MS_) +#define BABEL_RTT_MAX (120 MS_) +#define BABEL_RTT_DECAY 42 + /* Max interval that will not overflow when carried as 16-bit centiseconds */ #define BABEL_TIME_UNITS 10000 /* On-wire times are counted in centiseconds */ #define BABEL_MIN_INTERVAL (0x0001 * BABEL_TIME_UNITS) @@ -96,6 +102,8 @@ enum babel_tlv_type { enum babel_subtlv_type { BABEL_SUBTLV_PAD1 = 0, BABEL_SUBTLV_PADN = 1, + BABEL_SUBTLV_DIVERSITY = 2, /* we don't support this */ + BABEL_SUBTLV_TIMESTAMP = 3, /* Mandatory subtlvs */ BABEL_SUBTLV_SOURCE_PREFIX = 128, @@ -106,6 +114,7 @@ enum babel_iface_type { BABEL_IFACE_TYPE_UNDEF = 0, BABEL_IFACE_TYPE_WIRED = 1, BABEL_IFACE_TYPE_WIRELESS = 2, + BABEL_IFACE_TYPE_TUNNEL = 3, BABEL_IFACE_TYPE_MAX }; @@ -141,6 +150,12 @@ struct babel_iface_config { uint ihu_interval; /* IHU interval, in us */ uint update_interval; /* Update interval, in us */ + btime rtt_min; /* rtt above which to start penalising metric */ + btime rtt_max; /* max rtt metric penalty applied above this */ + u16 rtt_cost; /* metric penalty to apply at rtt_max */ + u16 rtt_decay; /* decay of neighbour RTT (units of 1/256) */ + u8 rtt_send; /* whether to send timestamps on this interface */ + u16 rx_buffer; /* RX buffer size, 0 for MTU */ u16 tx_length; /* TX packet length limit (including headers), 0 for MTU */ int tx_tos; @@ -229,6 +244,10 @@ struct babel_neighbor { u16 next_hello_seqno; uint last_hello_int; + u32 last_tstamp; + btime last_tstamp_rcvd; + btime srtt; + u32 auth_pc_unicast; u32 auth_pc_multicast; u8 auth_passed; @@ -326,6 +345,8 @@ struct babel_msg_hello { u16 seqno; uint interval; ip_addr sender; + u32 tstamp; + btime pkt_received; }; struct babel_msg_ihu { @@ -335,6 +356,9 @@ struct babel_msg_ihu { uint interval; ip_addr addr; ip_addr sender; + u32 tstamp; + u32 tstamp_rcvd; + btime pkt_received; }; struct babel_msg_update { diff --git a/proto/babel/config.Y b/proto/babel/config.Y index 1b4dc6f5..b8af0267 100644 --- a/proto/babel/config.Y +++ b/proto/babel/config.Y @@ -26,7 +26,7 @@ CF_KEYWORDS(BABEL, INTERFACE, METRIC, RXCOST, HELLO, UPDATE, INTERVAL, PORT, TYPE, WIRED, WIRELESS, RX, TX, BUFFER, PRIORITY, LENGTH, CHECK, LINK, NEXT, HOP, IPV4, IPV6, BABEL_METRIC, SHOW, INTERFACES, NEIGHBORS, ENTRIES, RANDOMIZE, ROUTER, ID, AUTHENTICATION, NONE, MAC, PERMISSIVE, - EXTENDED) + EXTENDED, TUNNEL, RTT, MIN, MAX, DECAY, SEND, TIMESTAMPS) CF_GRAMMAR @@ -67,6 +67,10 @@ babel_iface_start: BABEL_IFACE->limit = BABEL_HELLO_LIMIT; BABEL_IFACE->tx_tos = IP_PREC_INTERNET_CONTROL; BABEL_IFACE->tx_priority = sk_priority_control; + BABEL_IFACE->rtt_min = BABEL_RTT_MIN; + BABEL_IFACE->rtt_max = BABEL_RTT_MAX; + BABEL_IFACE->rtt_decay = BABEL_RTT_DECAY; + BABEL_IFACE->rtt_send = 1; BABEL_IFACE->check_link = 1; BABEL_IFACE->ext_next_hop = 1; }; @@ -87,8 +91,16 @@ babel_iface_finish: BABEL_IFACE->hello_interval = BABEL_HELLO_INTERVAL_WIRED; if (!BABEL_IFACE->rxcost) BABEL_IFACE->rxcost = BABEL_RXCOST_WIRED; + if (BABEL_IFACE->type == BABEL_IFACE_TYPE_TUNNEL && !BABEL_IFACE->rtt_cost) + BABEL_IFACE->rtt_cost = BABEL_RXCOST_RTT; } + if (BABEL_IFACE->rtt_cost && !BABEL_IFACE->rtt_send) + cf_error("Can't set RTT cost when sending timestamps is disabled"); + + if (BABEL_IFACE->rtt_min >= BABEL_IFACE->rtt_max) + cf_error("Min RTT must be smaller than max RTT"); + /* Make sure we do not overflow the 16-bit centisec fields */ if (!BABEL_IFACE->update_interval) BABEL_IFACE->update_interval = MIN_(BABEL_IFACE->hello_interval*BABEL_UPDATE_INTERVAL_FACTOR, BABEL_MAX_INTERVAL); @@ -136,6 +148,7 @@ babel_iface_item: | LIMIT expr { BABEL_IFACE->limit = $2; if (($2<1) || ($2>16)) cf_error("Limit must be in range 1-16"); } | TYPE WIRED { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRED; } | TYPE WIRELESS { BABEL_IFACE->type = BABEL_IFACE_TYPE_WIRELESS; } + | TYPE TUNNEL { BABEL_IFACE->type = BABEL_IFACE_TYPE_TUNNEL; } | HELLO INTERVAL expr_us { BABEL_IFACE->hello_interval = $3; if (($3<BABEL_MIN_INTERVAL) || ($3>BABEL_MAX_INTERVAL)) cf_error("Hello interval must be in range 10 ms - 655 s"); } | UPDATE INTERVAL expr_us { BABEL_IFACE->update_interval = $3; if (($3<BABEL_MIN_INTERVAL) || ($3>BABEL_MAX_INTERVAL)) cf_error("Update interval must be in range 10 ms - 655 s"); } | RX BUFFER expr { BABEL_IFACE->rx_buffer = $3; if (($3<256) || ($3>65535)) cf_error("RX buffer must be in range 256-65535"); } @@ -149,6 +162,11 @@ babel_iface_item: | AUTHENTICATION NONE { BABEL_IFACE->auth_type = BABEL_AUTH_NONE; } | AUTHENTICATION MAC { BABEL_IFACE->auth_type = BABEL_AUTH_MAC; BABEL_IFACE->auth_permissive = 0; } | AUTHENTICATION MAC PERMISSIVE { BABEL_IFACE->auth_type = BABEL_AUTH_MAC; BABEL_IFACE->auth_permissive = 1; } + | RTT MIN expr_us { BABEL_IFACE->rtt_min = $3; } + | RTT MAX expr_us { BABEL_IFACE->rtt_max = $3; } + | RTT COST expr { BABEL_IFACE->rtt_cost = $3; if ($3 >= BABEL_INFINITY) cf_error("RTT cost must be < 65535"); } + | RTT DECAY expr { BABEL_IFACE->rtt_decay = $3; if (($3 < 1) || ($3 > 256)) cf_error("RTT decay must be between 1-256"); } + | SEND TIMESTAMPS bool { BABEL_IFACE->rtt_send = $3; } | password_list ; diff --git a/proto/babel/packets.c b/proto/babel/packets.c index 61c94cc5..f1895655 100644 --- a/proto/babel/packets.c +++ b/proto/babel/packets.c @@ -58,6 +58,13 @@ struct babel_tlv_ihu { u8 addr[0]; } PACKED; +struct babel_subtlv_timestamp { + u8 type; + u8 length; + u32 tstamp; + u32 tstamp_rcvd; /* only used in IHU */ +} PACKED; + struct babel_tlv_router_id { u8 type; u8 length; @@ -161,6 +168,7 @@ struct babel_parse_state { const struct babel_tlv_data* (*get_subtlv_data)(u8 type); struct babel_proto *proto; struct babel_iface *ifa; + btime received_time; ip_addr saddr; ip_addr next_hop_ip4; ip_addr next_hop_ip6; @@ -172,6 +180,7 @@ struct babel_parse_state { u8 def_ip6_prefix_seen; /* def_ip6_prefix is valid */ u8 def_ip4_prefix_seen; /* def_ip4_prefix is valid */ u8 def_ip4_via_ip6_prefix_seen; /* def_ip4_via_ip6_prefix is valid */ + u8 hello_tstamp_seen; /* pkt contains a hello timestamp */ u8 current_tlv_endpos; /* End of self-terminating TLVs (offset from start) */ u8 sadr_enabled; u8 is_unicast; @@ -336,6 +345,7 @@ static int babel_read_update(struct babel_tlv *hdr, union babel_msg *msg, struct static int babel_read_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state); static int babel_read_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state); static int babel_read_source_prefix(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state); +static int babel_read_timestamp(struct babel_tlv *hdr, union babel_msg *msg, struct babel_parse_state *state); static uint babel_write_ack(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len); static uint babel_write_hello(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len); @@ -344,6 +354,7 @@ static uint babel_write_update(struct babel_tlv *hdr, union babel_msg *msg, stru static uint babel_write_route_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len); static uint babel_write_seqno_request(struct babel_tlv *hdr, union babel_msg *msg, struct babel_write_state *state, uint max_len); static int babel_write_source_prefix(struct babel_tlv *hdr, net_addr *net, uint max_len); +static int babel_write_timestamp(struct babel_tlv *hdr, u32 tstamp, u32 tstamp_rcvd, uint max_len); static const struct babel_tlv_data tlv_data[BABEL_TLV_MAX] = { [BABEL_TLV_ACK_REQ] = { @@ -419,6 +430,13 @@ static const struct babel_tlv_data *get_packet_tlv_data(u8 type) return type < sizeof(tlv_data) / sizeof(*tlv_data) ? &tlv_data[type] : NULL; } +static const struct babel_tlv_data timestamp_tlv_data = { + sizeof(struct babel_subtlv_timestamp), + babel_read_timestamp, + NULL, + NULL +}; + static const struct babel_tlv_data source_prefix_tlv_data = { sizeof(struct babel_subtlv_source_prefix), babel_read_source_prefix, @@ -430,6 +448,8 @@ static const struct babel_tlv_data *get_packet_subtlv_data(u8 type) { switch (type) { + case BABEL_SUBTLV_TIMESTAMP: + return ×tamp_tlv_data; case BABEL_SUBTLV_SOURCE_PREFIX: return &source_prefix_tlv_data; @@ -491,16 +511,34 @@ babel_read_hello(struct babel_tlv *hdr, union babel_msg *m, static uint babel_write_hello(struct babel_tlv *hdr, union babel_msg *m, - struct babel_write_state *state UNUSED, uint max_len UNUSED) + struct babel_write_state *state UNUSED, uint max_len) { struct babel_tlv_hello *tlv = (void *) hdr; struct babel_msg_hello *msg = &m->hello; + uint len = sizeof(struct babel_tlv_hello); TLV_HDR0(tlv, BABEL_TLV_HELLO); put_u16(&tlv->seqno, msg->seqno); put_time16(&tlv->interval, msg->interval); - return sizeof(struct babel_tlv_hello); + if (msg->tstamp) + { + /* + * There can be a substantial delay between when the babel_msg was created + * and when it is serialised. We don't want this included in the RTT + * measurement, so replace the timestamp with the current time to get as + * close as possible to on-wire time for the packet. + */ + u32 tstamp = current_time_now() TO_US; + + int l = babel_write_timestamp(hdr, tstamp, 0, max_len); + if (l < 0) + return 0; + + len += l; + } + + return len; } static int @@ -565,6 +603,7 @@ babel_write_ihu(struct babel_tlv *hdr, union babel_msg *m, { struct babel_tlv_ihu *tlv = (void *) hdr; struct babel_msg_ihu *msg = &m->ihu; + uint len = sizeof(*tlv); if (ipa_is_link_local(msg->addr) && max_len < sizeof(struct babel_tlv_ihu) + 8) return 0; @@ -576,12 +615,24 @@ babel_write_ihu(struct babel_tlv *hdr, union babel_msg *m, if (!ipa_is_link_local(msg->addr)) { tlv->ae = BABEL_AE_WILDCARD; - return sizeof(struct babel_tlv_ihu); + goto out; } put_ip6_ll(&tlv->addr, msg->addr); tlv->ae = BABEL_AE_IP6_LL; hdr->length += 8; - return sizeof(struct babel_tlv_ihu) + 8; + len += 8; + +out: + if (msg->tstamp) + { + int l = babel_write_timestamp(hdr, msg->tstamp, msg->tstamp_rcvd, max_len); + if (l < 0) + return 0; + + len += l; + } + + return len; } static int @@ -1249,6 +1300,66 @@ babel_write_source_prefix(struct babel_tlv *hdr, net_addr *n, uint max_len) return len; } +static int +babel_read_timestamp(struct babel_tlv *hdr, union babel_msg *msg, + struct babel_parse_state *state) +{ + struct babel_subtlv_timestamp *tlv = (void *) hdr; + + switch (msg->type) + { + case BABEL_TLV_HELLO: + if (tlv->length < 4) + return PARSE_ERROR; + + msg->hello.tstamp = get_u32(&tlv->tstamp); + msg->hello.pkt_received = state->received_time; + state->hello_tstamp_seen = 1; + break; + + case BABEL_TLV_IHU: + if (tlv->length < 8) + return PARSE_ERROR; + + /* RTT calculation relies on a Hello always being present with an IHU */ + if (!state->hello_tstamp_seen) + break; + + msg->ihu.tstamp = get_u32(&tlv->tstamp); + msg->ihu.tstamp_rcvd = get_u32(&tlv->tstamp_rcvd); + msg->ihu.pkt_received = state->received_time; + break; + + default: + return PARSE_ERROR; + } + + return PARSE_SUCCESS; +} + +static int +babel_write_timestamp(struct babel_tlv *hdr, u32 tstamp, u32 tstamp_rcvd, uint max_len) +{ + struct babel_subtlv_timestamp *tlv = (void *) NEXT_TLV(hdr); + uint len = sizeof(*tlv); + + if (hdr->type == BABEL_TLV_HELLO) + len -= 4; + + if (len > max_len) + return -1; + + TLV_HDR(tlv, BABEL_SUBTLV_TIMESTAMP, len); + hdr->length += len; + + put_u32(&tlv->tstamp, tstamp); + + if (hdr->type == BABEL_TLV_IHU) + put_u32(&tlv->tstamp_rcvd, tstamp_rcvd); + + return len; +} + static inline int babel_read_subtlvs(struct babel_tlv *hdr, union babel_msg *msg, @@ -1518,6 +1629,13 @@ babel_process_packet(struct babel_iface *ifa, .saddr = saddr, .next_hop_ip6 = saddr, .sadr_enabled = babel_sadr_enabled(p), + + /* + * The core updates current_time() after returning from poll(), so this is + * actually the time the packet was received, even though there may have + * been a bit of delay before we got to process it + */ + .received_time = current_time(), }; if ((pkt->magic != BABEL_MAGIC) || (pkt->version != BABEL_VERSION)) diff --git a/proto/bgp/bgp.c b/proto/bgp/bgp.c index a6e9cf83..0a2e8f5a 100644 --- a/proto/bgp/bgp.c +++ b/proto/bgp/bgp.c @@ -86,7 +86,6 @@ * RFC 5065 - AS confederations for BGP * RFC 5082 - Generalized TTL Security Mechanism * RFC 5492 - Capabilities Advertisement with BGP - * RFC 5549 - Advertising IPv4 NLRI with an IPv6 Next Hop * RFC 5575 - Dissemination of Flow Specification Rules * RFC 5668 - 4-Octet AS Specific BGP Extended Community * RFC 6286 - AS-Wide Unique BGP Identifier @@ -101,6 +100,7 @@ * RFC 8203 - BGP Administrative Shutdown Communication * RFC 8212 - Default EBGP Route Propagation Behavior without Policies * RFC 8654 - Extended Message Support for BGP + * RFC 8950 - Advertising IPv4 NLRI with an IPv6 Next Hop * RFC 9072 - Extended Optional Parameters Length for BGP OPEN Message * RFC 9117 - Revised Validation Procedure for BGP Flow Specifications * RFC 9234 - Route Leak Prevention and Detection Using Roles diff --git a/proto/bgp/bgp.h b/proto/bgp/bgp.h index 324df43c..c11433ec 100644 --- a/proto/bgp/bgp.h +++ b/proto/bgp/bgp.h @@ -232,7 +232,7 @@ struct bgp_af_caps { u8 llgr_able; /* Long-lived GR, RFC draft */ u32 llgr_time; /* Long-lived GR stale time */ u8 llgr_flags; /* Long-lived GR per-AF flags */ - u8 ext_next_hop; /* Extended IPv6 next hop, RFC 5549 */ + u8 ext_next_hop; /* Extended IPv6 next hop, RFC 8950 */ u8 add_path; /* Multiple paths support, RFC 7911 */ }; diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 6b728b4e..ee98115d 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -214,6 +214,13 @@ bgp_af_caps_cmp(const void *X, const void *Y) return (x->afi < y->afi) ? -1 : (x->afi > y->afi) ? 1 : 0; } +struct bgp_caps * +bgp_alloc_capabilities(struct bgp_proto *p, int n) +{ + struct bgp_caps *caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps) + n * sizeof(struct bgp_af_caps)); + caps->role = BGP_ROLE_UNDEFINED; + return caps; +} void bgp_prepare_capabilities(struct bgp_conn *conn) @@ -226,13 +233,13 @@ bgp_prepare_capabilities(struct bgp_conn *conn) if (!p->cf->capabilities) { /* Just prepare empty local_caps */ - conn->local_caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps)); + conn->local_caps = bgp_alloc_capabilities(p, 0); 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)); + caps = bgp_alloc_capabilities(p, n); conn->local_caps = caps; caps->as4_support = p->cf->enable_as4; @@ -463,10 +470,7 @@ bgp_read_capabilities(struct bgp_conn *conn, byte *pos, int len) u32 af; if (!conn->remote_caps) - { - caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps) + sizeof(struct bgp_af_caps)); - caps->role = BGP_ROLE_UNDEFINED; - } + caps = bgp_alloc_capabilities(p, 1); else { caps = conn->remote_caps; @@ -502,7 +506,7 @@ bgp_read_capabilities(struct bgp_conn *conn, byte *pos, int len) caps->route_refresh = 1; break; - case 5: /* Extended next hop encoding capability, RFC 5549 */ + case 5: /* Extended next hop encoding capability, RFC 8950 */ if (cl % 6) goto err; @@ -762,7 +766,7 @@ bgp_read_options(struct bgp_conn *conn, byte *pos, uint len, uint rest) /* Prepare empty caps if no capability option was announced */ if (!conn->remote_caps) - conn->remote_caps = mb_allocz(p->p.pool, sizeof(struct bgp_caps)); + conn->remote_caps = bgp_alloc_capabilities(p, 0); return 0; @@ -1262,7 +1266,7 @@ bgp_encode_next_hop_ip(struct bgp_write_state *s, eattr *a, byte *buf, uint size /* * Both IPv4 and IPv6 next hops can be used (with ext_next_hop enabled). This - * is specified in RFC 5549 for IPv4 and in RFC 4798 for IPv6. The difference + * is specified in RFC 8950 for IPv4 and in RFC 4798 for IPv6. The difference * is that IPv4 address is directly encoded with IPv4 NLRI, but as IPv4-mapped * IPv6 address with IPv6 NLRI. */ @@ -1337,7 +1341,7 @@ bgp_encode_next_hop_vpn(struct bgp_write_state *s, eattr *a, byte *buf, uint siz /* * Both IPv4 and IPv6 next hops can be used (with ext_next_hop enabled). This - * is specified in RFC 5549 for VPNv4 and in RFC 4659 for VPNv6. The difference + * is specified in RFC 8950 for VPNv4 and in RFC 4659 for VPNv6. The difference * is that IPv4 address is directly encoded with VPNv4 NLRI, but as IPv4-mapped * IPv6 address with VPNv6 NLRI. */ diff --git a/proto/radv/config.Y b/proto/radv/config.Y index 8d4a3ab9..eeafe6f4 100644 --- a/proto/radv/config.Y +++ b/proto/radv/config.Y @@ -25,6 +25,15 @@ static struct radv_dnssl_config this_radv_dnssl; static list radv_dns_list; /* Used by radv_rdnss and radv_dnssl */ static u8 radv_mult_val; /* Used by radv_mult for second return value */ +static inline void +radv_add_to_custom_list(list *l, int type, const struct bytestring *payload) +{ + if (type < 0 || type > 255) cf_error("RA cusom type must be in range 0-255"); + struct radv_custom_config *cf = cfg_allocz(sizeof(struct radv_custom_config)); + add_tail(l, NODE cf); + cf->type = type; + cf->payload = payload; +} CF_DECLS @@ -33,7 +42,7 @@ CF_KEYWORDS(RADV, PREFIX, INTERFACE, MIN, MAX, RA, DELAY, INTERVAL, SOLICITED, RETRANS, TIMER, CURRENT, HOP, LIMIT, DEFAULT, VALID, PREFERRED, MULT, LIFETIME, SKIP, ONLINK, AUTONOMOUS, RDNSS, DNSSL, NS, DOMAIN, LOCAL, TRIGGER, SENSITIVE, PREFERENCE, LOW, MEDIUM, HIGH, PROPAGATE, ROUTE, - ROUTES, RA_PREFERENCE, RA_LIFETIME) + ROUTES, RA_PREFERENCE, RA_LIFETIME, CUSTOM, OPTION, TYPE, VALUE) CF_ENUM(T_ENUM_RA_PREFERENCE, RA_PREF_, LOW, MEDIUM, HIGH) @@ -41,6 +50,8 @@ CF_ENUM(T_ENUM_RA_PREFERENCE, RA_PREF_, LOW, MEDIUM, HIGH) CF_GRAMMAR +kw_sym: CUSTOM | OPTION | VALUE ; + proto: radv_proto ; radv_proto_start: proto_start RADV @@ -52,6 +63,7 @@ radv_proto_start: proto_start RADV init_list(&RADV_CFG->pref_list); init_list(&RADV_CFG->rdnss_list); init_list(&RADV_CFG->dnssl_list); + init_list(&RADV_CFG->custom_list); }; radv_proto_item: @@ -61,6 +73,7 @@ radv_proto_item: | PREFIX radv_prefix { add_tail(&RADV_CFG->pref_list, NODE this_radv_prefix); } | RDNSS { init_list(&radv_dns_list); } radv_rdnss { add_tail_list(&RADV_CFG->rdnss_list, &radv_dns_list); } | DNSSL { init_list(&radv_dns_list); } radv_dnssl { add_tail_list(&RADV_CFG->dnssl_list, &radv_dns_list); } + | CUSTOM OPTION TYPE expr VALUE BYTESTRING { radv_add_to_custom_list(&RADV_CFG->custom_list, $4, $6); } | TRIGGER net_ip6 { RADV_CFG->trigger = $2; } | PROPAGATE ROUTES bool { RADV_CFG->propagate_routes = $3; } ; @@ -82,6 +95,7 @@ radv_iface_start: init_list(&RADV_IFACE->pref_list); init_list(&RADV_IFACE->rdnss_list); init_list(&RADV_IFACE->dnssl_list); + init_list(&RADV_IFACE->custom_list); RADV_IFACE->min_ra_int = (u32) -1; /* undefined */ RADV_IFACE->max_ra_int = DEFAULT_MAX_RA_INT; @@ -124,8 +138,10 @@ radv_iface_item: | PREFIX radv_prefix { add_tail(&RADV_IFACE->pref_list, NODE this_radv_prefix); } | RDNSS { init_list(&radv_dns_list); } radv_rdnss { add_tail_list(&RADV_IFACE->rdnss_list, &radv_dns_list); } | DNSSL { init_list(&radv_dns_list); } radv_dnssl { add_tail_list(&RADV_IFACE->dnssl_list, &radv_dns_list); } + | CUSTOM OPTION TYPE expr VALUE BYTESTRING { radv_add_to_custom_list(&RADV_IFACE->custom_list, $4, $6); } | RDNSS LOCAL bool { RADV_IFACE->rdnss_local = $3; } | DNSSL LOCAL bool { RADV_IFACE->dnssl_local = $3; } + | CUSTOM OPTION LOCAL bool { RADV_IFACE->custom_local = $4; } ; radv_preference: diff --git a/proto/radv/packets.c b/proto/radv/packets.c index 5cd8b2de..77c98794 100644 --- a/proto/radv/packets.c +++ b/proto/radv/packets.c @@ -82,6 +82,13 @@ struct radv_opt_dnssl char domain[]; }; +struct radv_opt_custom +{ + u8 type; + u8 length; + u8 payload[]; +}; + static int radv_prepare_route(struct radv_iface *ifa, struct radv_route *rt, char **buf, char *bufend) @@ -255,6 +262,34 @@ radv_prepare_dnssl(struct radv_iface *ifa, list *dnssl_list, char **buf, char *b } static int +radv_prepare_custom(struct radv_iface *ifa, list *custom_list, char **buf, char *bufend) +{ + struct radv_custom_config *ccf; + WALK_LIST(ccf, *custom_list) + { + struct radv_opt_custom *op = (void *) *buf; + /* Add 2 octets for type and size and 8 - 1 for ceiling the division up to 8 octets */ + int size = (ccf->payload->length + 2 + 8 - 1) / 8; + if (bufend - *buf < size * 8) + goto too_much; + + memset(op, 0, size * 8); /* Clear buffer so there is no tail garbage */ + op->type = ccf->type; + op->length = size; + memcpy(op->payload, ccf->payload->data, ccf->payload->length); + + *buf += 8 * op->length; + } + + return 0; + + too_much: + log(L_WARN "%s: Too many RA options on interface %s", + ifa->ra->p.name, ifa->iface->name); + return -1; +} + +static int radv_prepare_prefix(struct radv_iface *ifa, struct radv_prefix *px, char **buf, char *bufend) { @@ -352,6 +387,14 @@ radv_prepare_ra(struct radv_iface *ifa) if (radv_prepare_dnssl(ifa, &ic->dnssl_list, &buf, bufend) < 0) goto done; + if (! ic->custom_local) + if (radv_prepare_custom(ifa, &cf->custom_list, &buf, bufend) < 0) + goto done; + + if (radv_prepare_custom(ifa, &ic->custom_list, &buf, bufend) < 0) + goto done; + + if (p->fib_up) { FIB_WALK(&p->routes, struct radv_route, rt) diff --git a/proto/radv/radv.h b/proto/radv/radv.h index 14d40f8a..2baf0bad 100644 --- a/proto/radv/radv.h +++ b/proto/radv/radv.h @@ -51,6 +51,7 @@ struct radv_config list pref_list; /* Global list of prefix configs (struct radv_prefix_config) */ list rdnss_list; /* Global list of RDNSS configs (struct radv_rdnss_config) */ list dnssl_list; /* Global list of DNSSL configs (struct radv_dnssl_config) */ + list custom_list; /* Global list of custom configs (struct radv_custom_config) */ net_addr trigger; /* Prefix of a trigger route, if defined */ u8 propagate_routes; /* Do we propagate more specific routes (RFC 4191)? */ @@ -63,6 +64,7 @@ struct radv_iface_config list pref_list; /* Local list of prefix configs (struct radv_prefix_config) */ list rdnss_list; /* Local list of RDNSS configs (struct radv_rdnss_config) */ list dnssl_list; /* Local list of DNSSL configs (struct radv_dnssl_config) */ + list custom_list; /* Local list of custom configs (struct radv_custom_config) */ u32 min_ra_int; /* Standard options from RFC 4861 */ u32 max_ra_int; @@ -75,6 +77,7 @@ struct radv_iface_config u8 rdnss_local; /* Global list is not used for RDNSS */ u8 dnssl_local; /* Global list is not used for DNSSL */ + u8 custom_local; /* Global list is not used for custom */ u8 managed; /* Standard options from RFC 4861 */ u8 other_config; @@ -122,6 +125,13 @@ struct radv_dnssl_config const char *domain; /* Domain for DNS search list, in processed form */ }; +struct radv_custom_config +{ + node n; + u8 type; /* Identifier of the type of option */ + const struct bytestring *payload; /* Payload of the option */ +}; + /* * One more specific route as per RFC 4191. * diff --git a/sysdep/config.h b/sysdep/config.h index 80b8d671..cf5787d0 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.13" +#define BIRD_VERSION "2.13.1" #endif /* Include parameters determined by configure script */ diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index 7f0d4736..e3298a0f 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -485,7 +485,6 @@ static inline ip_addr rta_get_ipa(struct rtattr *a) return ipa_from_ip6(rta_get_ip6(a)); } -#ifdef HAVE_MPLS_KERNEL static inline ip_addr rta_get_via(struct rtattr *a) { struct rtvia *v = RTA_DATA(a); @@ -496,6 +495,7 @@ static inline ip_addr rta_get_via(struct rtattr *a) return IPA_NONE; } +#ifdef HAVE_MPLS_KERNEL static u32 rta_mpls_stack[MPLS_MAX_LABEL_STACK]; static inline int rta_get_mpls(struct rtattr *a, u32 *stack) { @@ -763,10 +763,8 @@ nl_parse_multipath(struct nl_parse_state *s, struct krt_proto *p, const net_addr if (a[RTA_FLOW]) s->rta_flow = rta_get_u32(a[RTA_FLOW]); -#ifdef HAVE_MPLS_KERNEL if (a[RTA_VIA]) rv->gw = rta_get_via(a[RTA_VIA]); -#endif if (nh->rtnh_flags & RTNH_F_ONLINK) rv->flags |= RNF_ONLINK; @@ -1659,10 +1657,8 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h) if (a[RTA_GATEWAY]) ra->nh.gw = rta_get_ipa(a[RTA_GATEWAY]); -#ifdef HAVE_MPLS_KERNEL if (a[RTA_VIA]) ra->nh.gw = rta_get_via(a[RTA_VIA]); -#endif if (i->rtm_flags & RTNH_F_ONLINK) ra->nh.flags |= RNF_ONLINK; diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index e131ca41..6aedcfb6 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -171,6 +171,19 @@ times_update_real_time(struct timeloop *loop) loop->real_time = ts.tv_sec S + ts.tv_nsec NS; } +btime +current_time_now(void) +{ + struct timespec ts; + int rv; + + rv = clock_gettime(CLOCK_MONOTONIC, &ts); + if (rv < 0) + die("clock_gettime: %m"); + + return ts.tv_sec S + ts.tv_nsec NS; +} + /** * DOC: Sockets diff --git a/test/birdtest.h b/test/birdtest.h index cfeebb98..540092d6 100644 --- a/test/birdtest.h +++ b/test/birdtest.h @@ -37,6 +37,10 @@ int bt_test_suite_base(int (*test_fn)(const void *), const char *test_id, const static inline u64 bt_random(void) { return ((u64) random() & 0xffffffff) | ((u64) random() << 32); } +static inline u32 bt_random_n(u32 max) +{ return random() % max; } + + void bt_log_suite_result(int result, const char *fmt, ...); void bt_log_suite_case_result(int result, const char *fmt, ...); diff --git a/test/bt-utils.c b/test/bt-utils.c index 8496e185..fb42cd35 100644 --- a/test/bt-utils.c +++ b/test/bt-utils.c @@ -219,3 +219,135 @@ bt_bytes_to_hex(char *buf, const byte *in_data, size_t size) sprintf(buf + i*2, "%02x", in_data[i]); } +void +bt_random_net(net_addr *net, int type) +{ + ip4_addr ip4; + ip6_addr ip6; + uint pxlen; + + switch (type) + { + case NET_IP4: + pxlen = bt_random_n(24)+8; + ip4 = ip4_from_u32((u32) bt_random()); + net_fill_ip4(net, ip4_and(ip4, ip4_mkmask(pxlen)), pxlen); + break; + + case NET_IP6: + pxlen = bt_random_n(120)+8; + ip6 = ip6_build(bt_random(), bt_random(), bt_random(), bt_random()); + net_fill_ip6(net, ip6_and(ip6, ip6_mkmask(pxlen)), pxlen); + break; + + default: + die("Net type %d not implemented", type); + } +} + +net_addr * +bt_random_nets(int type, uint n) +{ + net_addr *nets = tmp_alloc(n * sizeof(net_addr)); + + for (uint i = 0; i < n; i++) + bt_random_net(&nets[i], type); + + return nets; +} + +net_addr * +bt_random_net_subset(net_addr *src, uint sn, uint dn) +{ + net_addr *nets = tmp_alloc(dn * sizeof(net_addr)); + + for (uint i = 0; i < dn; i++) + net_copy(&nets[i], &src[bt_random_n(sn)]); + + return nets; +} + +void +bt_read_net(const char *str, net_addr *net, int type) +{ + ip4_addr ip4; + ip6_addr ip6; + uint pxlen; + char addr[64]; + + switch (type) + { + case NET_IP4: + if (sscanf(str, "%[0-9.]/%u", addr, &pxlen) != 2) + goto err; + + if (!ip4_pton(addr, &ip4)) + goto err; + + if (!net_validate_px4(ip4, pxlen)) + goto err; + + net_fill_ip4(net, ip4, pxlen); + break; + + case NET_IP6: + if (sscanf(str, "%[0-9a-fA-F:.]/%u", addr, &pxlen) != 2) + goto err; + + if (!ip6_pton(addr, &ip6)) + goto err; + + if (!net_validate_px6(ip6, pxlen)) + goto err; + + net_fill_ip6(net, ip6, pxlen); + break; + + default: + die("Net type %d not implemented", type); + } + return; + +err: + bt_abort_msg("Invalid network '%s'", str); +} + +net_addr * +bt_read_nets(FILE *f, int type, uint *n) +{ + char str[80]; + + net_addr *nets = tmp_alloc(*n * sizeof(net_addr)); + uint i = 0; + + errno = 0; + while (fgets(str, sizeof(str), f)) + { + if (str[0] == '\n') + break; + + if (i >= *n) + bt_abort_msg("Too many networks"); + + bt_read_net(str, &nets[i], type); + bt_debug("ADD %s\n", str); + i++; + } + bt_syscall(errno, "fgets()"); + + bt_debug("DONE reading %u nets\n", i); + + *n = i; + return nets; +} + +net_addr * +bt_read_net_file(const char *filename, int type, uint *n) +{ + FILE *f = fopen(filename, "r"); + bt_syscall(!f, "fopen(%s)", filename); + net_addr *nets = bt_read_nets(f, type, n); + fclose(f); + + return nets; +} diff --git a/test/bt-utils.h b/test/bt-utils.h index 13d267cc..d29a0b7c 100644 --- a/test/bt-utils.h +++ b/test/bt-utils.h @@ -26,6 +26,12 @@ uint bt_naive_pow(uint base, uint power); void bt_bytes_to_hex(char *buf, const byte *in_data, size_t size); +void bt_random_net(net_addr *net, int type); +net_addr *bt_random_nets(int type, uint n); +net_addr *bt_random_net_subset(net_addr *src, uint sn, uint dn); +void bt_read_net(const char *str, net_addr *net, int type); +net_addr *bt_read_nets(FILE *f, int type, uint *n); +net_addr *bt_read_net_file(const char *filename, int type, uint *n); void bt_bird_init(void); void bt_bird_cleanup(void); |