aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOndrej Zajicek (work) <santiago@crfreenet.org>2017-12-07 17:41:09 +0100
committerOndrej Zajicek (work) <santiago@crfreenet.org>2017-12-07 17:41:09 +0100
commit4ff15a75c56531fa2d3858d8250dcef1af4e75b6 (patch)
tree8410799a1bf43210ac4fe600595488a2907149e2
parentcd80c9b0700ef9533a19ab19d66c38a1722c4e08 (diff)
parent98bb80a243b58c43453e9be69d19d0350286549c (diff)
downloadbird-4ff15a75c56531fa2d3858d8250dcef1af4e75b6.tar.gz
Merge commit '98bb80a243b58c43453e9be69d19d0350286549c' into int-new
-rw-r--r--.gitignore1
-rw-r--r--.gitlab-ci.yml349
-rw-r--r--TODO45
-rw-r--r--client/client.c3
-rw-r--r--doc/bird.sgml6
-rw-r--r--lib/net.h12
-rw-r--r--misc/docker/centos-6-amd64/Dockerfile11
-rw-r--r--misc/docker/centos-7-amd64/Dockerfile11
-rw-r--r--misc/docker/debian-7-amd64/Dockerfile12
-rw-r--r--misc/docker/debian-7-i386/Dockerfile12
-rw-r--r--misc/docker/debian-8-amd64/Dockerfile12
-rw-r--r--misc/docker/debian-8-i386/Dockerfile12
-rw-r--r--misc/docker/debian-9-amd64/Dockerfile12
-rw-r--r--misc/docker/debian-9-i386/Dockerfile12
-rw-r--r--misc/docker/debian-testing-amd64/Dockerfile12
-rw-r--r--misc/docker/debian-testing-i386/Dockerfile12
-rw-r--r--misc/docker/fedora-25-amd64/Dockerfile10
-rw-r--r--misc/docker/fedora-26-amd64/Dockerfile10
-rw-r--r--misc/docker/opensuse-42.3-amd64/Dockerfile11
-rw-r--r--misc/docker/ubuntu-14.04-amd64/Dockerfile12
-rw-r--r--misc/docker/ubuntu-16.04-amd64/Dockerfile12
-rw-r--r--nest/iface.c6
-rw-r--r--proto/radv/config.Y4
-rw-r--r--proto/radv/packets.c94
-rw-r--r--proto/radv/radv.c154
-rw-r--r--proto/radv/radv.h25
-rw-r--r--sysdep/bsd/krt-sock.c2
-rw-r--r--sysdep/linux/netlink.c54
28 files changed, 795 insertions, 133 deletions
diff --git a/.gitignore b/.gitignore
index 0b95d3a1..0dcebfd1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,4 @@
/config.status
/configure
/sysdep/autoconf.h.in
+/sysdep/autoconf.h.in~
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 00000000..05989484
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,349 @@
+variables:
+ DEBIAN_FRONTEND: noninteractive
+ LC_ALL: C
+ GIT_STRATEGY: fetch
+ DOCKER_CMD: docker --config="$HOME/.docker/$CI_JOB_ID/"
+ IMG_BASE: registry.labs.nic.cz/labs/bird
+
+stages:
+ - image
+ - build
+
+.docker: &docker_build
+ stage: image
+ allow_failure: true
+ script:
+ - $DOCKER_CMD login -u gitlab-ci-token -p $CI_BUILD_TOKEN registry.labs.nic.cz
+ # Make sure we refresh the base image if it updates (eg. security updates, etc)
+ # If we do just the build, cache is always reused and the freshness of the
+ # base image is never checked. However, pull always asks and updates the
+ # image only if it changed ‒ therefore, the cache is used unless there's a
+ # change.
+ - $DOCKER_CMD pull `sed -ne 's/^FROM //p' "misc/docker/$IMG_NAME/Dockerfile"`
+ - $DOCKER_CMD build -t "bird:$IMG_NAME" "misc/docker/$IMG_NAME"
+ - $DOCKER_CMD tag "bird:$IMG_NAME" "$IMG_BASE:$IMG_NAME"
+ - $DOCKER_CMD push "$IMG_BASE:$IMG_NAME"
+ after_script:
+ - rm -f "$HOME/.docker/$CI_JOB_ID/" # cleanup the credentials
+ tags:
+ # That's Docker in Docker
+ - dind
+
+docker_debian-7-amd64:
+ variables:
+ IMG_NAME: "debian-7-amd64"
+ <<: *docker_build
+
+docker_debian-8-amd64:
+ variables:
+ IMG_NAME: "debian-8-amd64"
+ <<: *docker_build
+
+docker_debian-9-amd64:
+ variables:
+ IMG_NAME: "debian-9-amd64"
+ <<: *docker_build
+
+docker_debian-testing-amd64:
+ variables:
+ IMG_NAME: "debian-testing-amd64"
+ <<: *docker_build
+
+docker_debian-7-i386:
+ variables:
+ IMG_NAME: "debian-7-i386"
+ <<: *docker_build
+
+docker_debian-8-i386:
+ variables:
+ IMG_NAME: "debian-8-i386"
+ <<: *docker_build
+
+docker_debian-9-i386:
+ variables:
+ IMG_NAME: "debian-9-i386"
+ <<: *docker_build
+
+docker_debian-testing-i386:
+ variables:
+ IMG_NAME: "debian-testing-i386"
+ <<: *docker_build
+
+docker_fedora-25-amd64:
+ variables:
+ IMG_NAME: "fedora-25-amd64"
+ <<: *docker_build
+
+docker_fedora-26-amd64:
+ variables:
+ IMG_NAME: "fedora-26-amd64"
+ <<: *docker_build
+
+docker_centos-6-amd64:
+ variables:
+ IMG_NAME: "centos-6-amd64"
+ <<: *docker_build
+
+docker_centos-7-amd64:
+ variables:
+ IMG_NAME: "centos-7-amd64"
+ <<: *docker_build
+
+docker_opensuse-42_3-amd64:
+ variables:
+ IMG_NAME: "opensuse-42.3-amd64"
+ <<: *docker_build
+
+docker_ubuntu-14_04-amd64:
+ variables:
+ IMG_NAME: "ubuntu-14.04-amd64"
+ <<: *docker_build
+
+docker_ubuntu-16_04-amd64:
+ variables:
+ IMG_NAME: "ubuntu-16.04-amd64"
+ <<: *docker_build
+
+.debian-7-i386: &debian-7-i386_env
+ image: registry.labs.nic.cz/labs/bird:debian-7-i386
+ tags:
+ - docker
+ - linux
+ - amd64
+
+.debian-8-i386: &debian-8-i386_env
+ image: registry.labs.nic.cz/labs/bird:debian-8-i386
+ tags:
+ - docker
+ - linux
+ - amd64
+
+.debian-9-i386: &debian-9-i386_env
+ image: registry.labs.nic.cz/labs/bird:debian-9-i386
+ tags:
+ - docker
+ - linux
+ - amd64
+
+.debian-testing-i386: &debian-testing-i386_env
+ image: registry.labs.nic.cz/labs/bird:debian-testing-i386
+ tags:
+ - docker
+ - linux
+ - amd64
+
+.debian-7-amd64: &debian-7-amd64_env
+ image: registry.labs.nic.cz/labs/bird:debian-7-amd64
+ tags:
+ - docker
+ - linux
+ - amd64
+
+.debian-8-amd64: &debian-8-amd64_env
+ image: registry.labs.nic.cz/labs/bird:debian-8-amd64
+ tags:
+ - docker
+ - linux
+ - amd64
+
+.debian-9-amd64: &debian-9-amd64_env
+ image: registry.labs.nic.cz/labs/bird:debian-9-amd64
+ tags:
+ - docker
+ - linux
+ - amd64
+
+.debian-testing-amd64: &debian-testing-amd64_env
+ image: registry.labs.nic.cz/labs/bird:debian-testing-amd64
+ tags:
+ - docker
+ - linux
+ - amd64
+
+.fedora-25-amd64: &fedora-25-amd64_env
+ image: registry.labs.nic.cz/labs/bird:fedora-25-amd64
+ tags:
+ - docker
+ - linux
+ - amd64
+
+.fedora-26-amd64: &fedora-26-amd64_env
+ image: registry.labs.nic.cz/labs/bird:fedora-26-amd64
+ tags:
+ - docker
+ - linux
+ - amd64
+
+.centos-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:
+ - docker
+ - linux
+ - amd64
+
+.opensuse-42_3-amd64: &opensuse-42_3-amd64_env
+ image: registry.labs.nic.cz/labs/bird:opensuse-42.3-amd64
+ tags:
+ - docker
+ - linux
+ - amd64
+
+.ubuntu-14_04-amd64: &ubuntu-14_04-amd64_env
+ image: registry.labs.nic.cz/labs/bird:ubuntu-14.04-amd64
+ tags:
+ - docker
+ - linux
+ - amd64
+
+.ubuntu-16_04-amd64: &ubuntu-16_04-amd64_env
+ image: registry.labs.nic.cz/labs/bird:ubuntu-16.04-amd64
+ tags:
+ - docker
+ - linux
+ - amd64
+
+# TODO We want to copy these BSDs to our own virtual machines, to make sure someone doesn't update them by accident.
+.freebsd-11-i386: &freebsd-11-i386_env
+ variables:
+ CPPFLAGS: "-I/usr/local/include"
+ LDFLAGS: "-L/usr/local/lib"
+ tags:
+ - freebsd
+ - i386
+ #only:
+ #- master
+ #- triggers
+ #- tags
+
+.freebsd-11-amd64: &freebsd-11-amd64_env
+ variables:
+ CPPFLAGS: "-I/usr/local/include"
+ LDFLAGS: "-L/usr/local/lib"
+ tags:
+ - freebsd
+ - amd64
+ #only:
+ #- master
+ #- triggers
+ #- tags
+
+.build: &build_job
+ stage: build
+ script:
+ - autoreconf
+ - ./configure CPPFLAGS="$CPPFLAGS" LDFLAGS="$LDFLAGS"
+ # Detect which make is available
+ - MAKE=make
+ - which gmake 2>/dev/null >/dev/null && MAKE=gmake
+ - $MAKE
+ # Run tests if they are available (eg. don't fail if "check" isn't a valid make target)
+ - $MAKE check || [ "$?" = 2 ]
+
+build-debian-7-amd64:
+ variables:
+ IPV6: "no"
+ <<: *debian-7-amd64_env
+ <<: *build_job
+
+build-debian-8-amd64:
+ variables:
+ IPV6: "no"
+ <<: *debian-8-amd64_env
+ <<: *build_job
+
+build-debian-9-amd64:
+ variables:
+ IPV6: "no"
+ <<: *debian-9-amd64_env
+ <<: *build_job
+
+build-debian-testing-amd64:
+ variables:
+ IPV6: "no"
+ <<: *debian-testing-amd64_env
+ <<: *build_job
+
+build-fedora-25-amd64:
+ variables:
+ IPV6: "no"
+ <<: *fedora-25-amd64_env
+ <<: *build_job
+
+build-fedora-26-amd64:
+ variables:
+ IPV6: "no"
+ <<: *fedora-26-amd64_env
+ <<: *build_job
+
+build-centos-6-amd64:
+ variables:
+ IPV6: "no"
+ <<: *centos-6-amd64_env
+ <<: *build_job
+
+build-centos-7-amd64:
+ variables:
+ IPV6: "no"
+ <<: *centos-7-amd64_env
+ <<: *build_job
+
+build-opensuse-42_3-amd64:
+ variables:
+ IPV6: "no"
+ <<: *opensuse-42_3-amd64_env
+ <<: *build_job
+
+build-ubuntu-14_04-amd64:
+ variables:
+ IPV6: "no"
+ <<: *ubuntu-14_04-amd64_env
+ <<: *build_job
+
+build-ubuntu-16_04-amd64:
+ variables:
+ IPV6: "no"
+ <<: *ubuntu-16_04-amd64_env
+ <<: *build_job
+
+build-debian-7-i386:
+ variables:
+ IPV6: "no"
+ <<: *debian-7-i386_env
+ <<: *build_job
+
+build-debian-8-i386:
+ variables:
+ IPV6: "no"
+ <<: *debian-8-i386_env
+ <<: *build_job
+
+build-debian-9-i386:
+ variables:
+ IPV6: "no"
+ <<: *debian-9-i386_env
+ <<: *build_job
+
+build-debian-testing-i386:
+ variables:
+ IPV6: "no"
+ <<: *debian-testing-i386_env
+ <<: *build_job
+
+build-freebsd-11-amd64:
+ variables:
+ IPV6: "no"
+ <<: *freebsd-11-amd64_env
+ <<: *build_job
+
+build-freebsd-11-i386:
+ variables:
+ IPV6: "no"
+ <<: *freebsd-11-i386_env
+ <<: *build_job
diff --git a/TODO b/TODO
deleted file mode 100644
index 23cd1877..00000000
--- a/TODO
+++ /dev/null
@@ -1,45 +0,0 @@
-Core
-~~~~
-- socket open failure should not be fatal
-- &&,||: priorities
-- static: allow specifying a per-route filter program for setting route attributes?
-
-Globals
-~~~~~~~
-- right usage of DBG vs. debug
-- logging and tracing; use appropriate log levels
-- check incoming packets and log errors!!
-- check log calls for trailing newlines and log levels followed by comma
-- check if all protocols set proper packet priorities and TTL's.
-- try compiling with -Wunused
-- does everybody test return value of sk_open?
-- protocols: implement CLI hooks and per-procotol CLI commands
-- protocols: implement reconfigure hook
-- protocols: use locking
-- check use of system includes and sprintf()
-
-Various ideas
-~~~~~~~~~~~~~
-- client: Ctrl-R eats one more enter
-- bgp: timing of updates?
-- netlink: import Linux route attributes to our rta's, so that they can be filtered?
-- config: executable config files
-- filters: user defined attributes?
-- io: use poll if available
-- route recalculation timing and flap dampening [see RFC2439 for algorithms]
-- aggregate engine: standard route aggregation and summarization [RFC2519]
-- aggregate engine: injection of manually configured pseudo-static routes
-- generate default route if any working BGP connection exists (aggregate engine again?)
-- generate default route to IGP's (aggregate engine yet another time?)
-- look at RFC 2386 (QoS-based routing)
-- cli: show tables?
-
-OSPF
-~~~~
- - check incoming packets using neighbor cache
- - RFC2328 appendix E: Use a better algorithm
- - automatic generation of external route tags (RFC1403)
- - RFC2370 opaque LSA's
- - Limit export rate of external LSAs (like Gated does)
- - Bugfix in link state retransmission list (aging)
- - Graceful OSPF restart - RFC3623
diff --git a/client/client.c b/client/client.c
index 0d4bdf3e..97cf6639 100644
--- a/client/client.c
+++ b/client/client.c
@@ -25,9 +25,10 @@
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
+#include <sys/select.h>
#include <sys/socket.h>
-#include <sys/un.h>
#include <sys/types.h>
+#include <sys/un.h>
#include "nest/bird.h"
#include "lib/resource.h"
diff --git a/doc/bird.sgml b/doc/bird.sgml
index 0ee86c18..4ee91206 100644
--- a/doc/bird.sgml
+++ b/doc/bird.sgml
@@ -3660,6 +3660,12 @@ dsc-iface
as a default router. For <cf/sensitive/ option, see <ref id="radv-trigger" name="trigger">.
Default: 3 * <cf/max ra interval/, <cf/sensitive/ yes.
+ <tag><label id="radv-iface-linger-time">linger time <m/expr/</tag>
+ When a prefix disappears, it is advertised for some time with 0
+ lifetime, to inform clients the prefix is no longer usable. This option
+ sets the time for how long it is advertised (in seconds). Maximum is
+ 3600, 0 means disabled. Default: 300.
+
<tag><label id="radv-iface-default-preference-low">default preference low|medium|high</tag>
This option specifies the Default Router Preference value to advertise
to hosts. Default: medium.
diff --git a/lib/net.h b/lib/net.h
index 332f4c9a..cae3a749 100644
--- a/lib/net.h
+++ b/lib/net.h
@@ -526,6 +526,18 @@ int net_classify(const net_addr *N);
int net_format(const net_addr *N, char *buf, int buflen);
int rd_format(const u64 rd, char *buf, int buflen);
+static inline int ipa_in_net_ip4(ip4_addr a, const net_addr_ip4 *n)
+{ return ip4_zero(ip4_and(ip4_xor(a, n->prefix), ip4_mkmask(n->pxlen))); }
+
+static inline int net_in_net_ip4(const net_addr_ip4 *a, const net_addr_ip4 *b)
+{ return (a->pxlen >= b->pxlen) && ipa_in_net_ip4(a->prefix, b); }
+
+static inline int ipa_in_net_ip6(ip6_addr a, const net_addr_ip6 *n)
+{ return ip6_zero(ip6_and(ip6_xor(a, n->prefix), ip6_mkmask(n->pxlen))); }
+
+static inline int net_in_net_ip6(const net_addr_ip6 *a, const net_addr_ip6 *b)
+{ return (a->pxlen >= b->pxlen) && ipa_in_net_ip6(a->prefix, b); }
+
int ipa_in_netX(const ip_addr A, const net_addr *N);
int net_in_netX(const net_addr *A, const net_addr *N);
diff --git a/misc/docker/centos-6-amd64/Dockerfile b/misc/docker/centos-6-amd64/Dockerfile
new file mode 100644
index 00000000..d92f64f9
--- /dev/null
+++ b/misc/docker/centos-6-amd64/Dockerfile
@@ -0,0 +1,11 @@
+FROM centos:6
+RUN yum -y upgrade
+RUN yum -y install \
+ autoconf \
+ flex \
+ bison \
+ pkgconfig \
+ 'readline-devel' \
+ 'pkgconfig(ncurses)' \
+ gcc \
+ make
diff --git a/misc/docker/centos-7-amd64/Dockerfile b/misc/docker/centos-7-amd64/Dockerfile
new file mode 100644
index 00000000..c01731b2
--- /dev/null
+++ b/misc/docker/centos-7-amd64/Dockerfile
@@ -0,0 +1,11 @@
+FROM centos:7
+RUN yum -y upgrade
+RUN yum -y install \
+ autoconf \
+ flex \
+ bison \
+ pkgconfig \
+ 'readline-devel' \
+ 'pkgconfig(ncurses)' \
+ gcc \
+ make
diff --git a/misc/docker/debian-7-amd64/Dockerfile b/misc/docker/debian-7-amd64/Dockerfile
new file mode 100644
index 00000000..dd5e9236
--- /dev/null
+++ b/misc/docker/debian-7-amd64/Dockerfile
@@ -0,0 +1,12 @@
+FROM debian:wheezy-slim
+ENV DEBIAN_FRONTEND noninteractive
+RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
+RUN apt-get -y update
+RUN apt-get -y upgrade
+RUN apt-get -y install \
+ autoconf \
+ build-essential \
+ flex \
+ bison \
+ ncurses-dev \
+ libreadline-dev
diff --git a/misc/docker/debian-7-i386/Dockerfile b/misc/docker/debian-7-i386/Dockerfile
new file mode 100644
index 00000000..753ff1cb
--- /dev/null
+++ b/misc/docker/debian-7-i386/Dockerfile
@@ -0,0 +1,12 @@
+FROM i386/debian:wheezy-slim
+ENV DEBIAN_FRONTEND noninteractive
+RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
+RUN apt-get -y update
+RUN apt-get -y upgrade
+RUN apt-get -y install \
+ autoconf \
+ build-essential \
+ flex \
+ bison \
+ ncurses-dev \
+ libreadline-dev
diff --git a/misc/docker/debian-8-amd64/Dockerfile b/misc/docker/debian-8-amd64/Dockerfile
new file mode 100644
index 00000000..d0e8ed0a
--- /dev/null
+++ b/misc/docker/debian-8-amd64/Dockerfile
@@ -0,0 +1,12 @@
+FROM debian:jessie-slim
+ENV DEBIAN_FRONTEND noninteractive
+RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
+RUN apt-get -y update
+RUN apt-get -y upgrade
+RUN apt-get -y install \
+ autoconf \
+ build-essential \
+ flex \
+ bison \
+ ncurses-dev \
+ libreadline-dev
diff --git a/misc/docker/debian-8-i386/Dockerfile b/misc/docker/debian-8-i386/Dockerfile
new file mode 100644
index 00000000..9dd6f022
--- /dev/null
+++ b/misc/docker/debian-8-i386/Dockerfile
@@ -0,0 +1,12 @@
+FROM i386/debian:jessie-slim
+ENV DEBIAN_FRONTEND noninteractive
+RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
+RUN apt-get -y update
+RUN apt-get -y upgrade
+RUN apt-get -y install \
+ autoconf \
+ build-essential \
+ flex \
+ bison \
+ ncurses-dev \
+ libreadline-dev
diff --git a/misc/docker/debian-9-amd64/Dockerfile b/misc/docker/debian-9-amd64/Dockerfile
new file mode 100644
index 00000000..e8a17efc
--- /dev/null
+++ b/misc/docker/debian-9-amd64/Dockerfile
@@ -0,0 +1,12 @@
+FROM debian:stretch-slim
+ENV DEBIAN_FRONTEND noninteractive
+RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
+RUN apt-get -y update
+RUN apt-get -y upgrade
+RUN apt-get -y install \
+ autoconf \
+ build-essential \
+ flex \
+ bison \
+ ncurses-dev \
+ libreadline-dev
diff --git a/misc/docker/debian-9-i386/Dockerfile b/misc/docker/debian-9-i386/Dockerfile
new file mode 100644
index 00000000..73e70750
--- /dev/null
+++ b/misc/docker/debian-9-i386/Dockerfile
@@ -0,0 +1,12 @@
+FROM i386/debian:stretch-slim
+ENV DEBIAN_FRONTEND noninteractive
+RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
+RUN apt-get -y update
+RUN apt-get -y upgrade
+RUN apt-get -y install \
+ autoconf \
+ build-essential \
+ flex \
+ bison \
+ ncurses-dev \
+ libreadline-dev
diff --git a/misc/docker/debian-testing-amd64/Dockerfile b/misc/docker/debian-testing-amd64/Dockerfile
new file mode 100644
index 00000000..3b66e3fa
--- /dev/null
+++ b/misc/docker/debian-testing-amd64/Dockerfile
@@ -0,0 +1,12 @@
+FROM debian:testing-slim
+ENV DEBIAN_FRONTEND noninteractive
+RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
+RUN apt-get -y update
+RUN apt-get -y upgrade
+RUN apt-get -y install \
+ autoconf \
+ build-essential \
+ flex \
+ bison \
+ ncurses-dev \
+ libreadline-dev
diff --git a/misc/docker/debian-testing-i386/Dockerfile b/misc/docker/debian-testing-i386/Dockerfile
new file mode 100644
index 00000000..998ce969
--- /dev/null
+++ b/misc/docker/debian-testing-i386/Dockerfile
@@ -0,0 +1,12 @@
+FROM i386/debian:testing-slim
+ENV DEBIAN_FRONTEND noninteractive
+RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
+RUN apt-get -y update
+RUN apt-get -y upgrade
+RUN apt-get -y install \
+ autoconf \
+ build-essential \
+ flex \
+ bison \
+ ncurses-dev \
+ libreadline-dev
diff --git a/misc/docker/fedora-25-amd64/Dockerfile b/misc/docker/fedora-25-amd64/Dockerfile
new file mode 100644
index 00000000..9cc4c3f6
--- /dev/null
+++ b/misc/docker/fedora-25-amd64/Dockerfile
@@ -0,0 +1,10 @@
+FROM fedora:25
+RUN dnf -y upgrade
+RUN dnf -y install \
+ autoconf \
+ flex \
+ bison \
+ pkgconfig \
+ 'readline-devel' \
+ 'pkgconfig(ncurses)' \
+ gcc
diff --git a/misc/docker/fedora-26-amd64/Dockerfile b/misc/docker/fedora-26-amd64/Dockerfile
new file mode 100644
index 00000000..d1e84439
--- /dev/null
+++ b/misc/docker/fedora-26-amd64/Dockerfile
@@ -0,0 +1,10 @@
+FROM fedora:26
+RUN dnf -y upgrade
+RUN dnf -y install \
+ autoconf \
+ flex \
+ bison \
+ pkgconfig \
+ 'readline-devel' \
+ 'pkgconfig(ncurses)' \
+ gcc
diff --git a/misc/docker/opensuse-42.3-amd64/Dockerfile b/misc/docker/opensuse-42.3-amd64/Dockerfile
new file mode 100644
index 00000000..c9f7127d
--- /dev/null
+++ b/misc/docker/opensuse-42.3-amd64/Dockerfile
@@ -0,0 +1,11 @@
+FROM opensuse:42.3
+RUN zypper -n up
+RUN zypper -n install \
+ autoconf \
+ flex \
+ bison \
+ pkgconfig \
+ readline-devel \
+ ncurses-devel \
+ gcc \
+ gmake
diff --git a/misc/docker/ubuntu-14.04-amd64/Dockerfile b/misc/docker/ubuntu-14.04-amd64/Dockerfile
new file mode 100644
index 00000000..ccac3eb4
--- /dev/null
+++ b/misc/docker/ubuntu-14.04-amd64/Dockerfile
@@ -0,0 +1,12 @@
+FROM ubuntu:14.04
+ENV DEBIAN_FRONTEND noninteractive
+RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
+RUN apt-get -y update
+RUN apt-get -y upgrade
+RUN apt-get -y install \
+ autoconf \
+ build-essential \
+ flex \
+ bison \
+ ncurses-dev \
+ libreadline-dev
diff --git a/misc/docker/ubuntu-16.04-amd64/Dockerfile b/misc/docker/ubuntu-16.04-amd64/Dockerfile
new file mode 100644
index 00000000..50a4918c
--- /dev/null
+++ b/misc/docker/ubuntu-16.04-amd64/Dockerfile
@@ -0,0 +1,12 @@
+FROM ubuntu:16.04
+ENV DEBIAN_FRONTEND noninteractive
+RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
+RUN apt-get -y update
+RUN apt-get -y upgrade
+RUN apt-get -y install \
+ autoconf \
+ build-essential \
+ flex \
+ bison \
+ ncurses-dev \
+ libreadline-dev
diff --git a/nest/iface.c b/nest/iface.c
index 01b1aa48..dbc4debe 100644
--- a/nest/iface.c
+++ b/nest/iface.c
@@ -145,9 +145,9 @@ ifa_send_notify(struct proto *p, unsigned c, struct ifa *a)
if (p->ifa_notify && (p->proto_state != PS_DOWN))
{
if (p->debug & D_IFACES)
- log(L_TRACE "%s < %s address %N on interface %s %s",
- p->name, (a->flags & IA_PRIMARY) ? "primary" : "secondary",
- &a->prefix, a->iface->name, (c & IF_CHANGE_UP) ? "added" : "removed");
+ log(L_TRACE "%s < address %N on interface %s %s",
+ p->name, &a->prefix, a->iface->name,
+ (c & IF_CHANGE_UP) ? "added" : "removed");
p->ifa_notify(p, c, a);
}
}
diff --git a/proto/radv/config.Y b/proto/radv/config.Y
index b5f4b5f2..0e43c237 100644
--- a/proto/radv/config.Y
+++ b/proto/radv/config.Y
@@ -27,7 +27,7 @@ static u8 radv_mult_val; /* Used by radv_mult for second return value */
CF_DECLS
CF_KEYWORDS(RADV, PREFIX, INTERFACE, MIN, MAX, RA, DELAY, INTERVAL,
- MANAGED, OTHER, CONFIG, LINK, MTU, REACHABLE, TIME, RETRANS,
+ MANAGED, OTHER, CONFIG, LINGER, LINK, MTU, REACHABLE, TIME, RETRANS,
TIMER, CURRENT, HOP, LIMIT, DEFAULT, VALID, PREFERRED, MULT,
LIFETIME, SKIP, ONLINK, AUTONOMOUS, RDNSS, DNSSL, NS, DOMAIN,
LOCAL, TRIGGER, SENSITIVE, PREFERENCE, LOW, MEDIUM, HIGH)
@@ -80,6 +80,7 @@ radv_iface_start:
RADV_IFACE->max_ra_int = DEFAULT_MAX_RA_INT;
RADV_IFACE->min_delay = DEFAULT_MIN_DELAY;
RADV_IFACE->current_hop_limit = DEFAULT_CURRENT_HOP_LIMIT;
+ RADV_IFACE->linger_time = DEFAULT_LINGER_TIME;
RADV_IFACE->default_lifetime = -1;
RADV_IFACE->default_lifetime_sensitive = 1;
RADV_IFACE->default_preference = RA_PREF_MEDIUM;
@@ -94,6 +95,7 @@ radv_iface_item:
| LINK MTU expr { RADV_IFACE->link_mtu = $3; }
| REACHABLE TIME expr { RADV_IFACE->reachable_time = $3; if ($3 > 3600000) cf_error("Reachable time must be in range 0-3600000"); }
| RETRANS TIMER expr { RADV_IFACE->retrans_timer = $3; }
+ | LINGER TIME expr { RADV_IFACE->linger_time = $3; if ($3 > 3600) cf_error("Linger time must be in range 0-3600"); }
| CURRENT HOP LIMIT expr { RADV_IFACE->current_hop_limit = $4; if ($4 > 255) cf_error("Current hop limit must be in range 0-255"); }
| DEFAULT LIFETIME expr radv_sensitive {
RADV_IFACE->default_lifetime = $3;
diff --git a/proto/radv/packets.c b/proto/radv/packets.c
index 9ea8feee..e07296e1 100644
--- a/proto/radv/packets.c
+++ b/proto/radv/packets.c
@@ -70,36 +70,6 @@ struct radv_opt_dnssl
char domain[];
};
-
-static struct radv_prefix_config default_prefix = {
- .onlink = 1,
- .autonomous = 1,
- .valid_lifetime = DEFAULT_VALID_LIFETIME,
- .preferred_lifetime = DEFAULT_PREFERRED_LIFETIME
-};
-
-
-static struct radv_prefix_config *
-radv_prefix_match(struct radv_iface *ifa, struct ifa *a)
-{
- struct radv_proto *p = ifa->ra;
- struct radv_config *cf = (struct radv_config *) (p->p.cf);
- struct radv_prefix_config *pc;
-
- if (a->scope <= SCOPE_LINK)
- return NULL;
-
- WALK_LIST(pc, ifa->cf->pref_list)
- if (net_in_netX(&a->prefix, (net_addr *) &pc->prefix))
- return pc;
-
- WALK_LIST(pc, cf->pref_list)
- if (net_in_netX(&a->prefix, (net_addr *) &pc->prefix))
- return pc;
-
- return &default_prefix;
-}
-
static int
radv_prepare_rdnss(struct radv_iface *ifa, list *rdnss_list, char **buf, char *bufend)
{
@@ -234,6 +204,36 @@ radv_prepare_dnssl(struct radv_iface *ifa, list *dnssl_list, char **buf, char *b
return -1;
}
+static int
+radv_prepare_prefix(struct radv_iface *ifa, struct radv_prefix *px,
+ char **buf, char *bufend)
+{
+ struct radv_prefix_config *pc = px->cf;
+
+ if (*buf + sizeof(struct radv_opt_prefix) > bufend)
+ {
+ log(L_WARN "%s: Too many prefixes on interface %s",
+ ifa->ra->p.name, ifa->iface->name);
+ return -1;
+ }
+
+ struct radv_opt_prefix *op = (void *) *buf;
+ op->type = OPT_PREFIX;
+ op->length = 4;
+ op->pxlen = px->prefix.pxlen;
+ op->flags = (pc->onlink ? OPT_PX_ONLINK : 0) |
+ (pc->autonomous ? OPT_PX_AUTONOMOUS : 0);
+ op->valid_lifetime = (ifa->ra->active || !pc->valid_lifetime_sensitive) ?
+ htonl(pc->valid_lifetime) : 0;
+ op->preferred_lifetime = (ifa->ra->active || !pc->preferred_lifetime_sensitive) ?
+ htonl(pc->preferred_lifetime) : 0;
+ op->reserved = 0;
+ op->prefix = ip6_hton(px->prefix.prefix);
+ *buf += sizeof(*op);
+
+ return 0;
+}
+
static void
radv_prepare_ra(struct radv_iface *ifa)
{
@@ -269,37 +269,11 @@ radv_prepare_ra(struct radv_iface *ifa)
buf += sizeof (*om);
}
- struct ifa *addr;
- WALK_LIST(addr, ifa->iface->addrs)
+ struct radv_prefix *prefix;
+ WALK_LIST(prefix, ifa->prefixes)
{
- if (addr->prefix.type != NET_IP6)
- continue;
-
- struct radv_prefix_config *pc;
- pc = radv_prefix_match(ifa, addr);
-
- if (!pc || pc->skip)
- continue;
-
- if (buf + sizeof(struct radv_opt_prefix) > bufend)
- {
- log(L_WARN "%s: Too many prefixes on interface %s", p->p.name, ifa->iface->name);
+ if (radv_prepare_prefix(ifa, prefix, &buf, bufend) < 0)
goto done;
- }
-
- struct radv_opt_prefix *op = (void *) buf;
- op->type = OPT_PREFIX;
- op->length = 4;
- op->pxlen = net6_pxlen(&addr->prefix);
- op->flags = (pc->onlink ? OPT_PX_ONLINK : 0) |
- (pc->autonomous ? OPT_PX_AUTONOMOUS : 0);
- op->valid_lifetime = (p->active || !pc->valid_lifetime_sensitive) ?
- htonl(pc->valid_lifetime) : 0;
- op->preferred_lifetime = (p->active || !pc->preferred_lifetime_sensitive) ?
- htonl(pc->preferred_lifetime) : 0;
- op->reserved = 0;
- op->prefix = ip6_hton(net6_prefix(&addr->prefix));
- buf += sizeof(*op);
}
if (! ic->rdnss_local)
@@ -408,7 +382,7 @@ radv_err_hook(sock *sk, int err)
int
radv_sk_open(struct radv_iface *ifa)
{
- sock *sk = sk_new(ifa->ra->p.pool);
+ sock *sk = sk_new(ifa->pool);
sk->type = SK_IP;
sk->subtype = SK_IPV6;
sk->dport = ICMPV6_PROTO;
diff --git a/proto/radv/radv.c b/proto/radv/radv.c
index fe371ab4..c96d7724 100644
--- a/proto/radv/radv.c
+++ b/proto/radv/radv.c
@@ -51,6 +51,16 @@ radv_timer(timer *tm)
RADV_TRACE(D_EVENTS, "Timer fired on %s", ifa->iface->name);
+ /*
+ * If some dead prefixes expired, regenerate the prefix list and the packet.
+ * We do so by pretending there was a change on the interface.
+ *
+ * This sets the timer, but we replace it just at the end of this function
+ * (replacing a timer is fine).
+ */
+ if (ifa->prefix_expires && (ifa->prefix_expires <= current_time()))
+ radv_iface_notify(ifa, RA_EV_GC);
+
radv_send_ra(ifa, 0);
/* Update timer */
@@ -68,7 +78,136 @@ radv_timer(timer *tm)
tm_start(ifa->timer, t);
}
-static char* ev_name[] = { NULL, "Init", "Change", "RS" };
+static struct radv_prefix_config default_prefix = {
+ .onlink = 1,
+ .autonomous = 1,
+ .valid_lifetime = DEFAULT_VALID_LIFETIME,
+ .preferred_lifetime = DEFAULT_PREFERRED_LIFETIME
+};
+
+static struct radv_prefix_config dead_prefix = {
+};
+
+/* Find a corresponding config for the given prefix */
+static struct radv_prefix_config *
+radv_prefix_match(struct radv_iface *ifa, net_addr_ip6 *px)
+{
+ struct radv_proto *p = ifa->ra;
+ struct radv_config *cf = (struct radv_config *) (p->p.cf);
+ struct radv_prefix_config *pc;
+
+ WALK_LIST(pc, ifa->cf->pref_list)
+ if (net_in_net_ip6(px, &pc->prefix))
+ return pc;
+
+ WALK_LIST(pc, cf->pref_list)
+ if (net_in_net_ip6(px, &pc->prefix))
+ return pc;
+
+ return &default_prefix;
+}
+
+/*
+ * Go through the list of prefixes, compare them with configs and decide if we
+ * want them or not.
+ */
+static void
+radv_prepare_prefixes(struct radv_iface *ifa)
+{
+ struct radv_proto *p = ifa->ra;
+ struct radv_iface_config *cf = ifa->cf;
+ struct radv_prefix *pfx;
+
+ /* First mark all the prefixes as unused */
+ WALK_LIST(pfx, ifa->prefixes)
+ pfx->mark = 0;
+
+ /* Find all the prefixes we want to use and make sure they are in the list. */
+ struct ifa *addr;
+ WALK_LIST(addr, ifa->iface->addrs)
+ {
+ if ((addr->prefix.type != NET_IP6) ||
+ (addr->scope <= SCOPE_LINK))
+ continue;
+
+ net_addr_ip6 *prefix = (void *) &addr->prefix;
+ struct radv_prefix_config *pc = radv_prefix_match(ifa, prefix);
+
+ if (!pc || pc->skip)
+ continue;
+
+ /* Do we have it already? */
+ struct radv_prefix *existing = NULL;
+ WALK_LIST(pfx, ifa->prefixes)
+ if (net_equal_ip6(&pfx->prefix, prefix))
+ {
+ existing = pfx;
+ break;
+ }
+
+ if (!existing)
+ {
+ RADV_TRACE(D_EVENTS, "Adding new prefix %N on %s",
+ prefix, ifa->iface->name);
+
+ existing = mb_allocz(ifa->pool, sizeof *existing);
+ net_copy_ip6(&existing->prefix, prefix);
+ add_tail(&ifa->prefixes, NODE existing);
+ }
+
+ /*
+ * Update the information (it may have changed, or even bring a prefix back
+ * to life).
+ */
+ existing->alive = 1;
+ existing->mark = 1;
+ existing->cf = pc;
+ }
+
+ /*
+ * Garbage-collect the prefixes. If something isn't used, it dies (but isn't
+ * dropped just yet). If something is dead and rots there for long enough,
+ * clean it up.
+ */
+ btime now_ = current_time();
+ btime expires = now_ + cf->linger_time S;
+ btime expires_min = 0;
+ struct radv_prefix *next;
+ WALK_LIST_DELSAFE(pfx, next, ifa->prefixes)
+ {
+ if (pfx->alive && !pfx->mark)
+ {
+ RADV_TRACE(D_EVENTS, "Marking prefix %N on %s as dead",
+ pfx->prefix, ifa->iface->name);
+
+ pfx->alive = 0;
+ pfx->expires = expires;
+ pfx->cf = &dead_prefix;
+ }
+
+ if (!pfx->alive)
+ {
+ if (pfx->expires <= now_)
+ {
+ RADV_TRACE(D_EVENTS, "Removing prefix %N on %s",
+ pfx->prefix, ifa->iface->name);
+
+ rem_node(NODE pfx);
+ mb_free(pfx);
+ }
+ else
+ {
+ /* Find minimum expiration time */
+ if (!expires_min || (pfx->expires < expires_min))
+ expires_min = pfx->expires;
+ }
+ }
+ }
+
+ ifa->prefix_expires = expires_min;
+}
+
+static char* ev_name[] = { NULL, "Init", "Change", "RS", "Garbage collect" };
void
radv_iface_notify(struct radv_iface *ifa, int event)
@@ -83,6 +222,7 @@ radv_iface_notify(struct radv_iface *ifa, int event)
switch (event)
{
case RA_EV_CHANGE:
+ case RA_EV_GC:
ifa->plen = 0;
case RA_EV_INIT:
ifa->initial = MAX_INITIAL_RTR_ADVERTISEMENTS;
@@ -92,6 +232,8 @@ radv_iface_notify(struct radv_iface *ifa, int event)
break;
}
+ radv_prepare_prefixes(ifa);
+
/* Update timer */
btime t = ifa->last + ifa->cf->min_delay S - current_time();
tm_start(ifa->timer, t);
@@ -137,16 +279,18 @@ radv_iface_add(struct object_lock *lock)
static void
radv_iface_new(struct radv_proto *p, struct iface *iface, struct radv_iface_config *cf)
{
- pool *pool = p->p.pool;
struct radv_iface *ifa;
RADV_TRACE(D_EVENTS, "Adding interface %s", iface->name);
+ pool *pool = rp_new(p->p.pool, iface->name);
ifa = mb_allocz(pool, sizeof(struct radv_iface));
+ ifa->pool = pool;
ifa->ra = p;
ifa->cf = cf;
ifa->iface = iface;
ifa->addr = iface->llv6;
+ init_list(&ifa->prefixes);
add_tail(&p->iface_list, NODE ifa);
@@ -172,11 +316,7 @@ radv_iface_remove(struct radv_iface *ifa)
rem_node(NODE ifa);
- rfree(ifa->sk);
- rfree(ifa->timer);
- rfree(ifa->lock);
-
- mb_free(ifa);
+ rfree(ifa->pool);
}
static void
diff --git a/proto/radv/radv.h b/proto/radv/radv.h
index 8324bb67..4672e3b2 100644
--- a/proto/radv/radv.h
+++ b/proto/radv/radv.h
@@ -35,6 +35,7 @@
#define DEFAULT_MAX_RA_INT 600
#define DEFAULT_MIN_DELAY 3
#define DEFAULT_CURRENT_HOP_LIMIT 64
+#define DEFAULT_LINGER_TIME 300
#define DEFAULT_VALID_LIFETIME 86400
#define DEFAULT_PREFERRED_LIFETIME 14400
@@ -64,6 +65,9 @@ struct radv_iface_config
u32 max_ra_int;
u32 min_delay;
+ u32 linger_time; /* How long a dead prefix should still be advertised with 0
+ lifetime */
+
u8 rdnss_local; /* Global list is not used for RDNSS */
u8 dnssl_local; /* Global list is not used for DNSSL */
@@ -75,7 +79,7 @@ struct radv_iface_config
u32 current_hop_limit;
u32 default_lifetime;
u8 default_lifetime_sensitive; /* Whether default_lifetime depends on trigger */
- u8 default_preference; /* Default Router Preference (RFC 4191) */
+ u8 default_preference; /* Default Router Preference (RFC 4191) */
};
struct radv_prefix_config
@@ -118,6 +122,19 @@ struct radv_proto
u8 active; /* Whether radv is active w.r.t. triggers */
};
+struct radv_prefix /* One prefix we advertise */
+{
+ node n;
+ net_addr_ip6 prefix;
+
+ u8 alive; /* Is the prefix alive? If not, we advertise it
+ with 0 lifetime, so clients stop using it */
+ u8 mark; /* A temporary mark for processing */
+ btime expires; /* The time when we drop this prefix from
+ advertising. It is valid only if !alive. */
+ struct radv_prefix_config *cf; /* The config tied to this prefix */
+};
+
struct radv_iface
{
node n;
@@ -125,6 +142,9 @@ struct radv_iface
struct radv_iface_config *cf; /* Related config, must be updated in reconfigure */
struct iface *iface;
struct ifa *addr; /* Link-local address of iface */
+ struct pool *pool; /* A pool for interface-specific things */
+ list prefixes; /* The prefixes we advertise (struct radv_prefix) */
+ btime prefix_expires; /* When the soonest prefix expires (0 = none dead) */
timer *timer;
struct object_lock *lock;
@@ -132,12 +152,13 @@ struct radv_iface
btime last; /* Time of last sending of RA */
u16 plen; /* Length of prepared RA in tbuf, or 0 if not valid */
- byte initial; /* List of active ifaces */
+ byte initial; /* How many RAs are still to be sent as initial */
};
#define RA_EV_INIT 1 /* Switch to initial mode */
#define RA_EV_CHANGE 2 /* Change of options or prefixes */
#define RA_EV_RS 3 /* Received RS */
+#define RA_EV_GC 4 /* Internal garbage collection of prefixes */
/* Default Router Preferences (RFC 4191) */
#define RA_PREF_LOW 0x18
diff --git a/sysdep/bsd/krt-sock.c b/sysdep/bsd/krt-sock.c
index 08cfea39..c36ae558 100644
--- a/sysdep/bsd/krt-sock.c
+++ b/sysdep/bsd/krt-sock.c
@@ -165,7 +165,7 @@ struct ks_msg
{
struct rt_msghdr rtm;
struct sockaddr_storage buf[RTAX_MAX];
-};
+} PACKED;
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c
index 6cea2fa9..279f3c9c 100644
--- a/sysdep/linux/netlink.c
+++ b/sysdep/linux/netlink.c
@@ -83,22 +83,26 @@ struct rtvia {
/*
* Structure nl_parse_state keeps state of received route processing. Ideally,
* we could just independently parse received Netlink messages and immediately
- * propagate received routes to the rest of BIRD, but Linux kernel represents
- * and announces IPv6 ECMP routes not as one route with multiple next hops (like
- * RTA_MULTIPATH in IPv4 ECMP), but as a set of routes with the same prefix.
+ * propagate received routes to the rest of BIRD, but older Linux kernel (before
+ * version 4.11) represents and announces IPv6 ECMP routes not as one route with
+ * multiple next hops (like RTA_MULTIPATH in IPv4 ECMP), but as a sequence of
+ * routes with the same prefix. More recent kernels work as with IPv4.
*
* Therefore, BIRD keeps currently processed route in nl_parse_state structure
* and postpones its propagation until we expect it to be final; i.e., when
* non-matching route is received or when the scan ends. When another matching
* route is received, it is merged with the already processed route to form an
* ECMP route. Note that merging is done only for IPv6 (merge == 1), but the
- * postponing is done in both cases (for simplicity). All IPv4 routes are just
- * considered non-matching.
+ * postponing is done in both cases (for simplicity). All IPv4 routes or IPv6
+ * routes with RTA_MULTIPATH set are just considered non-matching.
*
* This is ignored for asynchronous notifications (every notification is handled
* as a separate route). It is not an issue for our routes, as we ignore such
* notifications anyways. But importing alien IPv6 ECMP routes does not work
- * properly.
+ * properly with older kernels.
+ *
+ * Whatever the kernel version is, IPv6 ECMP routes are sent as multiple routes
+ * for the same prefix.
*/
struct nl_parse_state
@@ -348,6 +352,12 @@ static struct nl_want_attrs nexthop_attr_want4[BIRD_RTA_MAX] = {
[RTA_ENCAP] = { 1, 0, 0 },
};
+static struct nl_want_attrs nexthop_attr_want6[BIRD_RTA_MAX] = {
+ [RTA_GATEWAY] = { 1, 1, sizeof(ip6_addr) },
+ [RTA_ENCAP_TYPE]= { 1, 1, sizeof(u16) },
+ [RTA_ENCAP] = { 1, 0, 0 },
+};
+
static struct nl_want_attrs encap_mpls_want[BIRD_RTA_MAX] = {
[RTA_DST] = { 1, 0, 0 },
};
@@ -374,6 +384,7 @@ static struct nl_want_attrs rtm_attr_want6[BIRD_RTA_MAX] = {
[RTA_PRIORITY] = { 1, 1, sizeof(u32) },
[RTA_PREFSRC] = { 1, 1, sizeof(ip6_addr) },
[RTA_METRICS] = { 1, 0, 0 },
+ [RTA_MULTIPATH] = { 1, 0, 0 },
[RTA_FLOW] = { 1, 1, sizeof(u32) },
[RTA_TABLE] = { 1, 1, sizeof(u32) },
[RTA_ENCAP_TYPE]= { 1, 1, sizeof(u16) },
@@ -631,7 +642,7 @@ nl_add_multipath(struct nlmsghdr *h, uint bufsize, struct nexthop *nh, int af)
}
static struct nexthop *
-nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
+nl_parse_multipath(struct krt_proto *p, struct rtattr *ra, int af)
{
/* Temporary buffer for multicast nexthops */
static struct nexthop *nh_buffer;
@@ -670,7 +681,22 @@ nl_parse_multipath(struct krt_proto *p, struct rtattr *ra)
/* Nonexistent RTNH_PAYLOAD ?? */
nl_attr_len = nh->rtnh_len - RTNH_LENGTH(0);
- nl_parse_attrs(RTNH_DATA(nh), nexthop_attr_want4, a, sizeof(a));
+ switch (af)
+ {
+ case AF_INET:
+ if (!nl_parse_attrs(RTNH_DATA(nh), nexthop_attr_want4, a, sizeof(a)))
+ return NULL;
+ break;
+
+ case AF_INET6:
+ if (!nl_parse_attrs(RTNH_DATA(nh), nexthop_attr_want6, a, sizeof(a)))
+ return NULL;
+ break;
+
+ default:
+ return NULL;
+ }
+
if (a[RTA_GATEWAY])
{
rv->gw = rta_get_ipa(a[RTA_GATEWAY]);
@@ -1520,9 +1546,9 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
case RTN_UNICAST:
ra->dest = RTD_UNICAST;
- if (a[RTA_MULTIPATH] && (i->rtm_family == AF_INET))
- {
- struct nexthop *nh = nl_parse_multipath(p, a[RTA_MULTIPATH]);
+ if (a[RTA_MULTIPATH])
+ {
+ struct nexthop *nh = nl_parse_multipath(p, a[RTA_MULTIPATH], i->rtm_family);
if (!nh)
{
log(L_ERR "KRT: Received strange multipath route %N", net->n.addr);
@@ -1699,8 +1725,10 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h)
/*
* Ideally, now we would send the received route to the rest of kernel code.
- * But IPv6 ECMP routes are sent as a sequence of routes, so we postpone it
- * and merge next hops until the end of the sequence.
+ * But IPv6 ECMP routes before 4.11 are sent as a sequence of routes, so we
+ * postpone it and merge next hops until the end of the sequence. Note that
+ * proper multipath updates are rejected by nl_mergable_route(), so it is
+ * always the first case for them.
*/
if (!s->net)