Merge commit '98bb80a243b58c43453e9be69d19d0350286549c' into int-new

This commit is contained in:
Ondrej Zajicek (work) 2017-12-07 17:41:09 +01:00
commit 4ff15a75c5
28 changed files with 795 additions and 133 deletions

1
.gitignore vendored
View file

@ -11,3 +11,4 @@
/config.status
/configure
/sysdep/autoconf.h.in
/sysdep/autoconf.h.in~

349
.gitlab-ci.yml Normal file
View file

@ -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

45
TODO
View file

@ -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

View file

@ -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"

View file

@ -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.

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,12 @@
FROM i386/debian:stretch-slim
ENV DEBIAN_FRONTEND noninteractive
RUN sed -i 's/deb.debian.org/ftp.cz.debian.org/' /etc/apt/sources.list
RUN apt-get -y update
RUN apt-get -y upgrade
RUN apt-get -y install \
autoconf \
build-essential \
flex \
bison \
ncurses-dev \
libreadline-dev

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,10 @@
FROM fedora:25
RUN dnf -y upgrade
RUN dnf -y install \
autoconf \
flex \
bison \
pkgconfig \
'readline-devel' \
'pkgconfig(ncurses)' \
gcc

View file

@ -0,0 +1,10 @@
FROM fedora:26
RUN dnf -y upgrade
RUN dnf -y install \
autoconf \
flex \
bison \
pkgconfig \
'readline-devel' \
'pkgconfig(ncurses)' \
gcc

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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))

View file

@ -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)