]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Merge commit '98bb80a243b58c43453e9be69d19d0350286549c' into int-new
authorOndrej Zajicek (work) <santiago@crfreenet.org>
Thu, 7 Dec 2017 16:41:09 +0000 (17:41 +0100)
committerOndrej Zajicek (work) <santiago@crfreenet.org>
Thu, 7 Dec 2017 16:41:09 +0000 (17:41 +0100)
1  2 
.gitlab-ci.yml
doc/bird.sgml
lib/net.h
nest/iface.c
proto/radv/config.Y
proto/radv/packets.c
proto/radv/radv.c
proto/radv/radv.h
sysdep/bsd/krt-sock.c
sysdep/linux/netlink.c

diff --cc .gitlab-ci.yml
index 0000000000000000000000000000000000000000,c9863a1c257324210bede458705e97041ebb13ed..059894841604db9126008e369e1631c939f3d091
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,451 +1,349 @@@
 -  - ./configure --enable-ipv6=$IPV6 CPPFLAGS="$CPPFLAGS" LDFLAGS="$LDFLAGS"
+ 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
 -build-debian-7-amd64-v6:
 -  variables:
 -    IPV6: "yes"
 -  <<: *debian-7-amd64_env
 -  <<: *build_job
 -
 -build-debian-8-amd64-v6:
 -  variables:
 -    IPV6: "yes"
 -  <<: *debian-8-amd64_env
 -  <<: *build_job
 -
 -build-debian-9-amd64-v6:
 -  variables:
 -    IPV6: "yes"
 -  <<: *debian-9-amd64_env
 -  <<: *build_job
 -
 -build-debian-testing-amd64-v6:
 -  variables:
 -    IPV6: "yes"
 -  <<: *debian-testing-amd64_env
 -  <<: *build_job
 -
++  - ./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-v6:
 -  variables:
 -    IPV6: "yes"
 -  <<: *fedora-25-amd64_env
 -  <<: *build_job
 -
+ build-fedora-25-amd64:
+   variables:
+     IPV6: "no"
+   <<: *fedora-25-amd64_env
+   <<: *build_job
 -build-fedora-26-amd64-v6:
 -  variables:
 -    IPV6: "yes"
 -  <<: *fedora-26-amd64_env
 -  <<: *build_job
 -
+ build-fedora-26-amd64:
+   variables:
+     IPV6: "no"
+   <<: *fedora-26-amd64_env
+   <<: *build_job
 -build-centos-6-amd64-v6:
 -  variables:
 -    IPV6: "yes"
 -  <<: *centos-6-amd64_env
 -  <<: *build_job
 -
+ build-centos-6-amd64:
+   variables:
+     IPV6: "no"
+   <<: *centos-6-amd64_env
+   <<: *build_job
 -build-centos-7-amd64-v6:
 -  variables:
 -    IPV6: "yes"
 -  <<: *centos-7-amd64_env
 -  <<: *build_job
 -
+ build-centos-7-amd64:
+   variables:
+     IPV6: "no"
+   <<: *centos-7-amd64_env
+   <<: *build_job
 -build-opensuse-42_3-amd64-v6:
 -  variables:
 -    IPV6: "yes"
 -  <<: *opensuse-42_3-amd64_env
 -  <<: *build_job
 -
+ build-opensuse-42_3-amd64:
+   variables:
+     IPV6: "no"
+   <<: *opensuse-42_3-amd64_env
+   <<: *build_job
 -build-ubuntu-14_04-amd64-v6:
 -  variables:
 -    IPV6: "yes"
 -  <<: *ubuntu-14_04-amd64_env
 -  <<: *build_job
 -
+ build-ubuntu-14_04-amd64:
+   variables:
+     IPV6: "no"
+   <<: *ubuntu-14_04-amd64_env
+   <<: *build_job
 -build-ubuntu-16_04-amd64-v6:
 -  variables:
 -    IPV6: "yes"
 -  <<: *ubuntu-16_04-amd64_env
 -  <<: *build_job
 -
+ build-ubuntu-16_04-amd64:
+   variables:
+     IPV6: "no"
+   <<: *ubuntu-16_04-amd64_env
+   <<: *build_job
 -build-debian-7-i386-v6:
 -  variables:
 -    IPV6: "yes"
 -  <<: *debian-7-i386_env
 -  <<: *build_job
 -
+ build-debian-7-i386:
+   variables:
+     IPV6: "no"
+   <<: *debian-7-i386_env
+   <<: *build_job
 -build-debian-8-i386-v6:
 -  variables:
 -    IPV6: "yes"
 -  <<: *debian-8-i386_env
 -  <<: *build_job
 -
+ build-debian-8-i386:
+   variables:
+     IPV6: "no"
+   <<: *debian-8-i386_env
+   <<: *build_job
 -build-debian-9-i386-v6:
 -  variables:
 -    IPV6: "yes"
 -  <<: *debian-9-i386_env
 -  <<: *build_job
 -
+ build-debian-9-i386:
+   variables:
+     IPV6: "no"
+   <<: *debian-9-i386_env
+   <<: *build_job
 -build-debian-testing-i386-v6:
 -  variables:
 -    IPV6: "yes"
 -  <<: *debian-testing-i386_env
 -  <<: *build_job
 -
+ build-debian-testing-i386:
+   variables:
+     IPV6: "no"
+   <<: *debian-testing-i386_env
+   <<: *build_job
 -build-freebsd-11-amd64-v6:
 -  variables:
 -    IPV6: "yes"
 -  <<: *freebsd-11-amd64_env
 -  <<: *build_job
 -
+ build-freebsd-11-amd64:
+   variables:
+     IPV6: "no"
+   <<: *freebsd-11-amd64_env
+   <<: *build_job
 -
 -build-freebsd-i386-v6:
 -  variables:
 -    IPV6: "yes"
 -  <<: *freebsd-11-i386_env
 -  <<: *build_job
+ build-freebsd-11-i386:
+   variables:
+     IPV6: "no"
+   <<: *freebsd-11-i386_env
+   <<: *build_job
diff --cc doc/bird.sgml
Simple merge
diff --cc lib/net.h
index 332f4c9a881ef9e7ea9e9adb59382ab6c2106be6,0000000000000000000000000000000000000000..cae3a7498f7cbd6fbb5dffed6991a911442c9d17
mode 100644,000000..100644
--- /dev/null
+++ b/lib/net.h
@@@ -1,533 -1,0 +1,545 @@@
 +/*
 + *    BIRD Internet Routing Daemon -- Network addresses
 + *
 + *    (c) 2015 Ondrej Zajicek <santiago@crfreenet.org>
 + *    (c) 2015 CZ.NIC z.s.p.o.
 + *
 + *    Can be freely distributed and used under the terms of the GNU GPL.
 + */
 +
 +#ifndef _BIRD_NET_H_
 +#define _BIRD_NET_H_
 +
 +#include "lib/ip.h"
 +
 +
 +#define NET_IP4               1
 +#define NET_IP6               2
 +#define NET_VPN4      3
 +#define NET_VPN6      4
 +#define NET_ROA4      5
 +#define NET_ROA6      6
 +#define NET_FLOW4     7
 +#define NET_FLOW6     8
 +#define NET_MPLS      9
 +#define NET_MAX               10
 +
 +#define NB_IP4                (1 << NET_IP4)
 +#define NB_IP6                (1 << NET_IP6)
 +#define NB_VPN4               (1 << NET_VPN4)
 +#define NB_VPN6               (1 << NET_VPN6)
 +#define NB_ROA4               (1 << NET_ROA4)
 +#define NB_ROA6               (1 << NET_ROA6)
 +#define NB_FLOW4      (1 << NET_FLOW4)
 +#define NB_FLOW6      (1 << NET_FLOW6)
 +#define NB_MPLS               (1 << NET_MPLS)
 +
 +#define NB_IP         (NB_IP4 | NB_IP6)
 +#define NB_VPN                (NB_VPN4 | NB_VPN6)
 +#define NB_FLOW               (NB_FLOW4 | NB_FLOW6)
 +#define NB_DEST               (NB_IP | NB_VPN | NB_MPLS)
 +#define NB_ANY                0xffffffff
 +
 +
 +typedef struct net_addr {
 +  u8 type;
 +  u8 pxlen;
 +  u16 length;
 +  u8 data[16];
 +  u64 align[0];
 +} net_addr;
 +
 +typedef struct net_addr_ip4 {
 +  u8 type;
 +  u8 pxlen;
 +  u16 length;
 +  ip4_addr prefix;
 +} net_addr_ip4;
 +
 +typedef struct net_addr_ip6 {
 +  u8 type;
 +  u8 pxlen;
 +  u16 length;
 +  ip6_addr prefix;
 +} net_addr_ip6;
 +
 +typedef struct net_addr_vpn4 {
 +  u8 type;
 +  u8 pxlen;
 +  u16 length;
 +  ip4_addr prefix;
 +  u64 rd;
 +} net_addr_vpn4;
 +
 +typedef struct net_addr_vpn6 {
 +  u8 type;
 +  u8 pxlen;
 +  u16 length;
 +  ip6_addr prefix;
 +  u64 rd;
 +} net_addr_vpn6;
 +
 +typedef struct net_addr_roa4 {
 +  u8 type;
 +  u8 pxlen;
 +  u16 length;
 +  ip4_addr prefix;
 +  u32 max_pxlen;
 +  u32 asn;
 +} net_addr_roa4;
 +
 +typedef struct net_addr_roa6 {
 +  u8 type;
 +  u8 pxlen;
 +  u16 length;
 +  ip6_addr prefix;
 +  u32 max_pxlen;
 +  u32 asn;
 +} net_addr_roa6;
 +
 +typedef struct net_addr_flow4 {
 +  u8 type;
 +  u8 pxlen;
 +  u16 length;
 +  ip4_addr prefix;
 +  byte data[0];
 +} net_addr_flow4;
 +
 +typedef struct net_addr_flow6 {
 +  u8 type;
 +  u8 pxlen;
 +  u16 length;
 +  ip6_addr prefix;
 +  byte data[0];
 +} net_addr_flow6;
 +
 +typedef struct net_addr_mpls {
 +  u8 type;
 +  u8 pxlen;
 +  u16 length;
 +  u32 label;
 +} net_addr_mpls;
 +
 +typedef union net_addr_union {
 +  net_addr n;
 +  net_addr_ip4 ip4;
 +  net_addr_ip6 ip6;
 +  net_addr_vpn4 vpn4;
 +  net_addr_vpn6 vpn6;
 +  net_addr_roa4 roa4;
 +  net_addr_roa6 roa6;
 +  net_addr_flow4 flow4;
 +  net_addr_flow6 flow6;
 +  net_addr_mpls mpls;
 +} net_addr_union;
 +
 +
 +extern const char * const net_label[];
 +extern const u16 net_addr_length[];
 +extern const u8  net_max_prefix_length[];
 +extern const u16 net_max_text_length[];
 +
 +#define NET_MAX_TEXT_LENGTH   256
 +
 +
 +#define NET_ADDR_IP4(prefix,pxlen) \
 +  ((net_addr_ip4) { NET_IP4, pxlen, sizeof(net_addr_ip4), prefix })
 +
 +#define NET_ADDR_IP6(prefix,pxlen) \
 +  ((net_addr_ip6) { NET_IP6, pxlen, sizeof(net_addr_ip6), prefix })
 +
 +#define NET_ADDR_VPN4(prefix,pxlen,rd) \
 +  ((net_addr_vpn4) { NET_VPN4, pxlen, sizeof(net_addr_vpn4), prefix, rd })
 +
 +#define NET_ADDR_VPN6(prefix,pxlen,rd) \
 +  ((net_addr_vpn6) { NET_VPN6, pxlen, sizeof(net_addr_vpn6), prefix, rd })
 +
 +#define NET_ADDR_ROA4(prefix,pxlen,max_pxlen,asn) \
 +  ((net_addr_roa4) { NET_ROA4, pxlen, sizeof(net_addr_roa4), prefix, max_pxlen, asn })
 +
 +#define NET_ADDR_ROA6(prefix,pxlen,max_pxlen,asn) \
 +  ((net_addr_roa6) { NET_ROA6, pxlen, sizeof(net_addr_roa6), prefix, max_pxlen, asn })
 +
 +#define NET_ADDR_FLOW4(prefix,pxlen,dlen) \
 +  ((net_addr_flow4) { NET_FLOW4, pxlen, sizeof(net_addr_ip4) + dlen, prefix })
 +
 +#define NET_ADDR_FLOW6(prefix,pxlen,dlen) \
 +  ((net_addr_flow6) { NET_FLOW6, pxlen, sizeof(net_addr_ip6) + dlen, prefix })
 +
 +#define NET_ADDR_MPLS(label) \
 +  ((net_addr_mpls) { NET_MPLS, 20, sizeof(net_addr_mpls), label })
 +
 +
 +static inline void net_fill_ip4(net_addr *a, ip4_addr prefix, uint pxlen)
 +{ *(net_addr_ip4 *)a = NET_ADDR_IP4(prefix, pxlen); }
 +
 +static inline void net_fill_ip6(net_addr *a, ip6_addr prefix, uint pxlen)
 +{ *(net_addr_ip6 *)a = NET_ADDR_IP6(prefix, pxlen); }
 +
 +static inline void net_fill_vpn4(net_addr *a, ip4_addr prefix, uint pxlen, u64 rd)
 +{ *(net_addr_vpn4 *)a = NET_ADDR_VPN4(prefix, pxlen, rd); }
 +
 +static inline void net_fill_vpn6(net_addr *a, ip6_addr prefix, uint pxlen, u64 rd)
 +{ *(net_addr_vpn6 *)a = NET_ADDR_VPN6(prefix, pxlen, rd); }
 +
 +static inline void net_fill_roa4(net_addr *a, ip4_addr prefix, uint pxlen, uint max_pxlen, u32 asn)
 +{ *(net_addr_roa4 *)a = NET_ADDR_ROA4(prefix, pxlen, max_pxlen, asn); }
 +
 +static inline void net_fill_roa6(net_addr *a, ip6_addr prefix, uint pxlen, uint max_pxlen, u32 asn)
 +{ *(net_addr_roa6 *)a = NET_ADDR_ROA6(prefix, pxlen, max_pxlen, asn); }
 +
 +static inline void net_fill_mpls(net_addr *a, u32 label)
 +{ *(net_addr_mpls *)a = NET_ADDR_MPLS(label); }
 +
 +static inline void net_fill_ipa(net_addr *a, ip_addr prefix, uint pxlen)
 +{
 +  if (ipa_is_ip4(prefix))
 +    net_fill_ip4(a, ipa_to_ip4(prefix), pxlen);
 +  else
 +    net_fill_ip6(a, ipa_to_ip6(prefix), pxlen);
 +}
 +
 +static inline void net_fill_ip_host(net_addr *a, ip_addr prefix)
 +{
 +  if (ipa_is_ip4(prefix))
 +    net_fill_ip4(a, ipa_to_ip4(prefix), IP4_MAX_PREFIX_LENGTH);
 +  else
 +    net_fill_ip6(a, ipa_to_ip6(prefix), IP6_MAX_PREFIX_LENGTH);
 +}
 +
 +static inline void net_fill_flow4(net_addr *a, ip4_addr prefix, uint pxlen, byte *data, uint dlen)
 +{
 +  net_addr_flow4 *f = (void *) a;
 +  *f = NET_ADDR_FLOW4(prefix, pxlen, dlen);
 +  memcpy(f->data, data, dlen);
 +}
 +
 +static inline void net_fill_flow6(net_addr *a, ip6_addr prefix, uint pxlen, byte *data, uint dlen)
 +{
 +  net_addr_flow6 *f = (void *) a;
 +  *f = NET_ADDR_FLOW6(prefix, pxlen, dlen);
 +  memcpy(f->data, data, dlen);
 +}
 +
 +static inline int net_val_match(u8 type, u32 mask)
 +{ return !!((1 << type) & mask); }
 +
 +static inline int net_type_match(const net_addr *a, u32 mask)
 +{ return net_val_match(a->type, mask); }
 +
 +static inline int net_is_ip(const net_addr *a)
 +{ return (a->type == NET_IP4) || (a->type == NET_IP6); }
 +
 +static inline int net_is_roa(const net_addr *a)
 +{ return (a->type == NET_ROA4) || (a->type == NET_ROA6); }
 +
 +static inline int net_is_vpn(const net_addr *a)
 +{ return (a->type == NET_VPN4) || (a->type == NET_VPN6); }
 +
 +
 +static inline ip4_addr net4_prefix(const net_addr *a)
 +{ return ((net_addr_ip4 *) a)->prefix; }
 +
 +static inline ip6_addr net6_prefix(const net_addr *a)
 +{ return ((net_addr_ip6 *) a)->prefix; }
 +
 +static inline ip_addr net_prefix(const net_addr *a)
 +{
 +  switch (a->type)
 +  {
 +  case NET_IP4:
 +  case NET_VPN4:
 +  case NET_ROA4:
 +  case NET_FLOW4:
 +    return ipa_from_ip4(net4_prefix(a));
 +
 +  case NET_IP6:
 +  case NET_VPN6:
 +  case NET_ROA6:
 +  case NET_FLOW6:
 +    return ipa_from_ip6(net6_prefix(a));
 +
 +  case NET_MPLS:
 +  default:
 +    return IPA_NONE;
 +  }
 +}
 +
 +static inline u32 net_mpls(const net_addr *a)
 +{
 +  if (a->type == NET_MPLS)
 +    return ((net_addr_mpls *) a)->label;
 +
 +  bug("Can't call net_mpls on non-mpls net_addr");
 +}
 +
 +static inline uint net4_pxlen(const net_addr *a)
 +{ return a->pxlen; }
 +
 +static inline uint net6_pxlen(const net_addr *a)
 +{ return a->pxlen; }
 +
 +static inline uint net_pxlen(const net_addr *a)
 +{ return a->pxlen; }
 +
 +ip_addr net_pxmask(const net_addr *a);
 +
 +static inline u64 net_rd(const net_addr *a)
 +{
 +  switch (a->type)
 +  {
 +  case NET_VPN4:
 +    return ((net_addr_vpn4 *)a)->rd;
 +  case NET_VPN6:
 +    return ((net_addr_vpn6 *)a)->rd;
 +  }
 +  return 0;
 +}
 +
 +
 +static inline int net_equal(const net_addr *a, const net_addr *b)
 +{ return (a->length == b->length) && !memcmp(a, b, a->length); }
 +
 +static inline int net_equal_ip4(const net_addr_ip4 *a, const net_addr_ip4 *b)
 +{ return !memcmp(a, b, sizeof(net_addr_ip4)); }
 +
 +static inline int net_equal_ip6(const net_addr_ip6 *a, const net_addr_ip6 *b)
 +{ return !memcmp(a, b, sizeof(net_addr_ip6)); }
 +
 +static inline int net_equal_vpn4(const net_addr_vpn4 *a, const net_addr_vpn4 *b)
 +{ return !memcmp(a, b, sizeof(net_addr_vpn4)); }
 +
 +static inline int net_equal_vpn6(const net_addr_vpn6 *a, const net_addr_vpn6 *b)
 +{ return !memcmp(a, b, sizeof(net_addr_vpn6)); }
 +
 +static inline int net_equal_roa4(const net_addr_roa4 *a, const net_addr_roa4 *b)
 +{ return !memcmp(a, b, sizeof(net_addr_roa4)); }
 +
 +static inline int net_equal_roa6(const net_addr_roa6 *a, const net_addr_roa6 *b)
 +{ return !memcmp(a, b, sizeof(net_addr_roa6)); }
 +
 +static inline int net_equal_flow4(const net_addr_flow4 *a, const net_addr_flow4 *b)
 +{ return net_equal((const net_addr *) a, (const net_addr *) b); }
 +
 +static inline int net_equal_flow6(const net_addr_flow6 *a, const net_addr_flow6 *b)
 +{ return net_equal((const net_addr *) a, (const net_addr *) b); }
 +
 +static inline int net_equal_mpls(const net_addr_mpls *a, const net_addr_mpls *b)
 +{ return !memcmp(a, b, sizeof(net_addr_mpls)); }
 +
 +
 +static inline int net_equal_prefix_roa4(const net_addr_roa4 *a, const net_addr_roa4 *b)
 +{ return ip4_equal(a->prefix, b->prefix) && (a->pxlen == b->pxlen); }
 +
 +static inline int net_equal_prefix_roa6(const net_addr_roa6 *a, const net_addr_roa6 *b)
 +{ return ip6_equal(a->prefix, b->prefix) && (a->pxlen == b->pxlen); }
 +
 +
 +static inline int net_zero_ip4(const net_addr_ip4 *a)
 +{ return !a->pxlen && ip4_zero(a->prefix); }
 +
 +static inline int net_zero_ip6(const net_addr_ip6 *a)
 +{ return !a->pxlen && ip6_zero(a->prefix); }
 +
 +static inline int net_zero_vpn4(const net_addr_vpn4 *a)
 +{ return !a->pxlen && ip4_zero(a->prefix) && !a->rd; }
 +
 +static inline int net_zero_vpn6(const net_addr_vpn6 *a)
 +{ return !a->pxlen && ip6_zero(a->prefix) && !a->rd; }
 +
 +static inline int net_zero_roa4(const net_addr_roa4 *a)
 +{ return !a->pxlen && ip4_zero(a->prefix) && !a->max_pxlen && !a->asn; }
 +
 +static inline int net_zero_roa6(const net_addr_roa6 *a)
 +{ return !a->pxlen && ip6_zero(a->prefix) && !a->max_pxlen && !a->asn; }
 +
 +static inline int net_zero_flow4(const net_addr_flow4 *a)
 +{ return !a->pxlen && ip4_zero(a->prefix) && !a->data; }
 +
 +static inline int net_zero_flow6(const net_addr_flow6 *a)
 +{ return !a->pxlen && ip6_zero(a->prefix) && !a->data; }
 +
 +static inline int net_zero_mpls(const net_addr_mpls *a)
 +{ return !a->label; }
 +
 +
 +static inline int net_compare_ip4(const net_addr_ip4 *a, const net_addr_ip4 *b)
 +{ return ip4_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen); }
 +
 +static inline int net_compare_ip6(const net_addr_ip6 *a, const net_addr_ip6 *b)
 +{ return ip6_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen); }
 +
 +static inline int net_compare_vpn4(const net_addr_vpn4 *a, const net_addr_vpn4 *b)
 +{ return u64_cmp(a->rd, b->rd) ?: ip4_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen); }
 +
 +static inline int net_compare_vpn6(const net_addr_vpn6 *a, const net_addr_vpn6 *b)
 +{ return u64_cmp(a->rd, b->rd) ?: ip6_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen); }
 +
 +static inline int net_compare_roa4(const net_addr_roa4 *a, const net_addr_roa4 *b)
 +{ return ip4_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen) ?: uint_cmp(a->max_pxlen, b->max_pxlen) ?: uint_cmp(a->asn, b->asn); }
 +
 +static inline int net_compare_roa6(const net_addr_roa6 *a, const net_addr_roa6 *b)
 +{ return ip6_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen) ?: uint_cmp(a->max_pxlen, b->max_pxlen) ?: uint_cmp(a->asn, b->asn); }
 +
 +static inline int net_compare_flow4(const net_addr_flow4 *a, const net_addr_flow4 *b)
 +{ return ip4_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen) ?: uint_cmp(a->length, b->length) ?: memcmp(a->data, b->data, a->length - sizeof(net_addr_flow4)); }
 +
 +static inline int net_compare_flow6(const net_addr_flow6 *a, const net_addr_flow6 *b)
 +{ return ip6_compare(a->prefix, b->prefix) ?: uint_cmp(a->pxlen, b->pxlen) ?: uint_cmp(a->length, b->length) ?: memcmp(a->data, b->data, a->length - sizeof(net_addr_flow6)); }
 +
 +static inline int net_compare_mpls(const net_addr_mpls *a, const net_addr_mpls *b)
 +{ return uint_cmp(a->label, b->label); }
 +
 +int net_compare(const net_addr *a, const net_addr *b);
 +
 +
 +static inline void net_copy(net_addr *dst, const net_addr *src)
 +{ memcpy(dst, src, src->length); }
 +
 +static inline void net_copy_ip4(net_addr_ip4 *dst, const net_addr_ip4 *src)
 +{ memcpy(dst, src, sizeof(net_addr_ip4)); }
 +
 +static inline void net_copy_ip6(net_addr_ip6 *dst, const net_addr_ip6 *src)
 +{ memcpy(dst, src, sizeof(net_addr_ip6)); }
 +
 +static inline void net_copy_vpn4(net_addr_vpn4 *dst, const net_addr_vpn4 *src)
 +{ memcpy(dst, src, sizeof(net_addr_vpn4)); }
 +
 +static inline void net_copy_vpn6(net_addr_vpn6 *dst, const net_addr_vpn6 *src)
 +{ memcpy(dst, src, sizeof(net_addr_vpn6)); }
 +
 +static inline void net_copy_roa4(net_addr_roa4 *dst, const net_addr_roa4 *src)
 +{ memcpy(dst, src, sizeof(net_addr_roa4)); }
 +
 +static inline void net_copy_roa6(net_addr_roa6 *dst, const net_addr_roa6 *src)
 +{ memcpy(dst, src, sizeof(net_addr_roa6)); }
 +
 +static inline void net_copy_flow4(net_addr_flow4 *dst, const net_addr_flow4 *src)
 +{ memcpy(dst, src, src->length); }
 +
 +static inline void net_copy_flow6(net_addr_flow6 *dst, const net_addr_flow6 *src)
 +{ memcpy(dst, src, src->length); }
 +
 +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 net_hash_ip4(const net_addr_ip4 *n)
 +{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
 +
 +static inline u32 net_hash_ip6(const net_addr_ip6 *n)
 +{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
 +
 +static inline u32 net_hash_vpn4(const net_addr_vpn4 *n)
 +{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26) ^ u64_hash(n->rd); }
 +
 +static inline u32 net_hash_vpn6(const net_addr_vpn6 *n)
 +{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26) ^ u64_hash(n->rd); }
 +
 +static inline u32 net_hash_roa4(const net_addr_roa4 *n)
 +{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
 +
 +static inline u32 net_hash_roa6(const net_addr_roa6 *n)
 +{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
 +
 +static inline u32 net_hash_flow4(const net_addr_flow4 *n)
 +{ return ip4_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
 +
 +static inline u32 net_hash_flow6(const net_addr_flow6 *n)
 +{ return ip6_hash(n->prefix) ^ ((u32) n->pxlen << 26); }
 +
 +static inline u32 net_hash_mpls(const net_addr_mpls *n)
 +{ return n->label; }
 +
 +u32 net_hash(const net_addr *a);
 +
 +
 +static inline int net_validate_px4(const ip4_addr prefix, uint pxlen)
 +{
 +  return (pxlen <= IP4_MAX_PREFIX_LENGTH) &&
 +    ip4_zero(ip4_and(prefix, ip4_not(ip4_mkmask(pxlen))));
 +}
 +
 +static inline int net_validate_px6(const ip6_addr prefix, uint pxlen)
 +{
 +  return (pxlen <= IP6_MAX_PREFIX_LENGTH) &&
 +    ip6_zero(ip6_and(prefix, ip6_not(ip6_mkmask(pxlen))));
 +}
 +
 +static inline int net_validate_ip4(const net_addr_ip4 *n)
 +{ return net_validate_px4(n->prefix, n->pxlen); }
 +
 +static inline int net_validate_ip6(const net_addr_ip6 *n)
 +{ return net_validate_px6(n->prefix, n->pxlen); }
 +
 +static inline int net_validate_vpn4(const net_addr_vpn4 *n)
 +{ return net_validate_px4(n->prefix, n->pxlen); }
 +
 +static inline int net_validate_vpn6(const net_addr_vpn6 *n)
 +{ return  net_validate_px6(n->prefix, n->pxlen); }
 +
 +static inline int net_validate_roa4(const net_addr_roa4 *n)
 +{
 +  return net_validate_px4(n->prefix, n->pxlen) &&
 +     (n->pxlen <= n->max_pxlen) && (n->max_pxlen <= IP4_MAX_PREFIX_LENGTH);
 +}
 +
 +static inline int net_validate_roa6(const net_addr_roa6 *n)
 +{
 +  return net_validate_px6(n->prefix, n->pxlen) &&
 +    (n->pxlen <= n->max_pxlen) && (n->max_pxlen <= IP6_MAX_PREFIX_LENGTH);
 +}
 +
 +// FIXME: Better check, call flow_validate?
 +static inline int net_validate_flow4(const net_addr_flow4 *n)
 +{ return net_validate_px4(n->prefix, n->pxlen); }
 +
 +static inline int net_validate_flow6(const net_addr_flow6 *n)
 +{ return net_validate_px6(n->prefix, n->pxlen); }
 +
 +static inline int net_validate_mpls(const net_addr_mpls *n)
 +{ return n->label < (1 << 20); }
 +
 +int net_validate(const net_addr *N);
 +
 +
 +static inline void net_normalize_ip4(net_addr_ip4 *n)
 +{ n->prefix = ip4_and(n->prefix, ip4_mkmask(n->pxlen)); }
 +
 +static inline void net_normalize_ip6(net_addr_ip6 *n)
 +{ n->prefix = ip6_and(n->prefix, ip6_mkmask(n->pxlen)); }
 +
 +static inline void net_normalize_vpn4(net_addr_vpn4 *n)
 +{ net_normalize_ip4((net_addr_ip4 *) n); }
 +
 +static inline void net_normalize_vpn6(net_addr_vpn6 *n)
 +{ net_normalize_ip6((net_addr_ip6 *) n); }
 +
 +void net_normalize(net_addr *N);
 +
 +
 +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);
 +
 +
 +#endif
diff --cc nest/iface.c
index 01b1aa48114b017e4ffcedd89daede63f3340db2,ff362938e8922798a625a47f66e06116fac59e5d..dbc4debe4ccb67efe169dcfb821502bfa162f56e
@@@ -142,12 -138,13 +142,12 @@@ if_copy(struct iface *to, struct iface 
  static inline void
  ifa_send_notify(struct proto *p, unsigned c, struct ifa *a)
  {
 -  if (p->ifa_notify)
 +  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 <%s address %I/%d on interface %s %s",
 -          p->name, (a->flags & IA_PRIMARY) ? " primary" : "",
 -          a->prefix, a->pxlen, a->iface->name,
++      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);
      }
  }
index b5f4b5f24b08be55f98f10da5cc2dc5fbd9b9a41,0ff84aeb585e793df6fe842106b7399e645bdacb..0e43c237caef191602eb841217ee17e6a3f135e4
@@@ -91,14 -94,15 +92,15 @@@ radv_iface_item
   | MIN DELAY expr { RADV_IFACE->min_delay = $3; if ($3 <= 0) cf_error("Min delay must be positive"); }
   | MANAGED bool { RADV_IFACE->managed = $2; }
   | OTHER CONFIG bool { RADV_IFACE->other_config = $3; }
 - | LINK MTU expr { RADV_IFACE->link_mtu = $3; if ($3 < 0) cf_error("Link MTU must be 0 or positive"); }
 - | REACHABLE TIME expr { RADV_IFACE->reachable_time = $3; if (($3 < 0) || ($3 > 3600000)) cf_error("Reachable time must be in range 0-3600000"); }
 - | RETRANS TIMER expr { RADV_IFACE->retrans_timer = $3; if ($3 < 0) cf_error("Retrans timer must be 0 or positive"); }
 - | LINGER TIME expr { RADV_IFACE->linger_time = $3; if (($3 < 0) || ($3 > 3600)) cf_error("Linger time must be in range 0-3600"); }
 - | CURRENT HOP LIMIT expr { RADV_IFACE->current_hop_limit = $4; if (($4 < 0) || ($4 > 255))  cf_error("Current hop limit must be in range 0-255"); }
 + | 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;
 -     if (($3 < 0) || ($3 > 9000))  cf_error("Default lifetime must be in range 0-9000");
 -     if ($4 != -1) RADV_IFACE->default_lifetime_sensitive = $4;
 +     if ($3 > 9000)  cf_error("Default lifetime must be in range 0-9000");
 +     if ($4 != (uint) -1) RADV_IFACE->default_lifetime_sensitive = $4;
     }
   | DEFAULT PREFERENCE radv_preference { RADV_IFACE->default_preference = $3; }
   | PREFIX radv_prefix { add_tail(&RADV_IFACE->pref_list, NODE this_radv_prefix); }
index 9ea8feee2623e8be774b6a811e54948e3aa5a0b9,19d71f97700d7bcfedac6ea5e24ce09eb7842a37..e07296e107021714f039f35d39ad5001407104fe
@@@ -234,6 -205,37 +204,36 @@@ radv_prepare_dnssl(struct radv_iface *i
    return -1;
  }
  
 -radv_prepare_prefix(struct radv_iface *ifa, struct radv_prefix *prefix,
+ static int
 -  struct radv_prefix_config *pc = prefix->cf;
++radv_prepare_prefix(struct radv_iface *ifa, struct radv_prefix *px,
+                   char **buf, char *bufend)
+ {
 -  op->pxlen = prefix->len;
++  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->prefix = prefix->prefix;
 -  ipa_hton(op->prefix);
++  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)
  {
@@@ -408,9 -384,8 +382,9 @@@ 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;
    sk->saddr = ifa->addr->ip;
  
index fe371ab40a9517cf443687fd4c9875868120f6bf,227c8ef6230562887972129a1d8162b739e85a0a..c96d772488a063784286cb79fd1541c041481746
@@@ -51,24 -51,160 +51,163 @@@ radv_timer(timer *tm
  
    RADV_TRACE(D_EVENTS, "Timer fired on %s", ifa->iface->name);
  
 -  if (ifa->prefix_expires && (ifa->prefix_expires <= now))
+   /*
+    * 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 */
 -  ifa->last = now;
 -  unsigned after = ifa->cf->min_ra_int;
 -  after += random() % (ifa->cf->max_ra_int - ifa->cf->min_ra_int + 1);
 +  ifa->last = current_time();
 +  btime t = ifa->cf->min_ra_int S;
 +  btime r = (ifa->cf->max_ra_int - ifa->cf->min_ra_int) S;
 +  t += random() % (r + 1);
  
    if (ifa->initial)
 +  {
 +    t = MIN(t, MAX_INITIAL_RTR_ADVERT_INTERVAL);
      ifa->initial--;
 +  }
  
 -  if (ifa->initial)
 -    after = MIN(after, MAX_INITIAL_RTR_ADVERT_INTERVAL);
 -
 -  tm_start(ifa->timer, after);
 +  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, struct ifa *a)
++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;
 -  if (a->scope <= SCOPE_LINK)
 -    return NULL;
 -
+   WALK_LIST(pc, ifa->cf->pref_list)
 -    if ((a->pxlen >= pc->pxlen) && ipa_in_net(a->prefix, pc->prefix, pc->pxlen))
++    if (net_in_net_ip6(px, &pc->prefix))
+       return pc;
+   WALK_LIST(pc, cf->pref_list)
 -    if ((a->pxlen >= pc->pxlen) && ipa_in_net(a->prefix, pc->prefix, pc->pxlen))
++    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)
+   {
 -    struct radv_prefix_config *pc = radv_prefix_match(ifa, addr);
++    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 ((pfx->len == addr->pxlen) && ipa_equal(pfx->prefix, addr->prefix))
++      if (net_equal_ip6(&pfx->prefix, prefix))
+       {
+       existing = pfx;
+       break;
+       }
+     if (!existing)
+     {
 -      RADV_TRACE(D_EVENTS, "Adding new prefix %I/%d on %s",
 -               addr->prefix, addr->pxlen, ifa->iface->name);
++      RADV_TRACE(D_EVENTS, "Adding new prefix %N on %s",
++               prefix, ifa->iface->name);
+       existing = mb_allocz(ifa->pool, sizeof *existing);
 -      existing->prefix = addr->prefix;
 -      existing->len = addr->pxlen;
++      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.
+    */
 -  bird_clock_t expires = now + cf->linger_time;
 -  bird_clock_t expires_min = 0;
++  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 %I/$d on %s as dead",
 -               pfx->prefix, pfx->len, ifa->iface->name);
++      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)
++      if (pfx->expires <= now_)
+       {
 -      RADV_TRACE(D_EVENTS, "Removing prefix %I/%d on %s",
 -                 pfx->prefix, pfx->len, ifa->iface->name);
++      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)
      break;
    }
  
+   radv_prepare_prefixes(ifa);
    /* Update timer */
 -  unsigned delta = now - ifa->last;
 -  unsigned after = 0;
 -
 -  if (delta < ifa->cf->min_delay)
 -    after = ifa->cf->min_delay - delta;
 -
 -  tm_start(ifa->timer, after);
 +  btime t = ifa->last + ifa->cf->min_delay S - current_time();
 +  tm_start(ifa->timer, t);
  }
  
  static void
@@@ -146,7 -302,7 +289,8 @@@ radv_iface_new(struct radv_proto *p, st
    ifa->ra = p;
    ifa->cf = cf;
    ifa->iface = iface;
 +  ifa->addr = iface->llv6;
+   init_list(&ifa->prefixes);
  
    add_tail(&p->iface_list, NODE ifa);
  
index 8324bb67ad589e57c0ab4beb5f39916f5e539672,60b9980f1246545a5ffd9c2ef29e16fde790bbd2..4672e3b2eb4d5855139b4ae1ad0c948736c4a7b2
@@@ -118,6 -125,19 +122,19 @@@ struct radv_prot
    u8 active;                  /* Whether radv is active w.r.t. triggers */
  };
  
 -  ip_addr prefix;
 -  u8 len;
+ struct radv_prefix            /* One prefix we advertise */
+ {
+   node n;
 -  bird_clock_t expires;               /* The time when we drop this prefix from
++  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;
    struct radv_iface_config *cf;       /* Related config, must be updated in reconfigure */
    struct iface *iface;
    struct ifa *addr;           /* Link-local address of iface */
 -  bird_clock_t prefix_expires;        /* When the soonest prefix expires (0 = none dead) */
+   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;
    sock *sk;
  
 -  bird_clock_t last;          /* Time of last sending of RA */
 +  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 */
Simple merge
index 6cea2fa99461e8895382f6db990b0524dbcea54d,3658c46bac399493b0ee1cf69a55af1c02604965..279f3c9c811f8801feb7b850cf8348bd98e6c3e4
@@@ -338,20 -319,22 +342,26 @@@ static struct nl_want_attrs ifa_attr_wa
    [IFA_LOCAL]   = { 1, 1, sizeof(ip6_addr) },
    [IFA_FLAGS]   = { 1, 1, sizeof(u32) },
  };
 -#endif
  
  
 -#define BIRD_RTA_MAX  (RTA_TABLE+1)
 +#define BIRD_RTA_MAX  (RTA_ENCAP+1)
  
 -#ifndef IPV6
 -static struct nl_want_attrs mpnh_attr_want4[BIRD_RTA_MAX] = {
 +static struct nl_want_attrs nexthop_attr_want4[BIRD_RTA_MAX] = {
    [RTA_GATEWAY]         = { 1, 1, sizeof(ip4_addr) },
 +  [RTA_ENCAP_TYPE]= { 1, 1, sizeof(u16) },
 +  [RTA_ENCAP]   = { 1, 0, 0 },
  };
 -#else
 -static struct nl_want_attrs mpnh_attr_want6[BIRD_RTA_MAX] = {
 +
++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 },
  };
 -#endif
  
 -#ifndef IPV6
  static struct nl_want_attrs rtm_attr_want4[BIRD_RTA_MAX] = {
    [RTA_DST]     = { 1, 1, sizeof(ip4_addr) },
    [RTA_OIF]     = { 1, 1, sizeof(u32) },
@@@ -374,23 -355,11 +384,24 @@@ static struct nl_want_attrs rtm_attr_wa
    [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) },
 +  [RTA_ENCAP]   = { 1, 0, 0 },
 +};
 +
 +static struct nl_want_attrs rtm_attr_want_mpls[BIRD_RTA_MAX] = {
 +  [RTA_DST]     = { 1, 1, sizeof(u32) },
 +  [RTA_IIF]     = { 1, 1, sizeof(u32) },
 +  [RTA_OIF]     = { 1, 1, sizeof(u32) },
 +  [RTA_PRIORITY]  = { 1, 1, sizeof(u32) },
 +  [RTA_METRICS]         = { 1, 0, 0 },
 +  [RTA_FLOW]    = { 1, 1, sizeof(u32) },
 +  [RTA_TABLE]   = { 1, 1, sizeof(u32) },
 +  [RTA_VIA]     = { 1, 0, 0 },
 +  [RTA_NEWDST]          = { 1, 0, 0 },
  };
 -#endif
  
  
  static int
@@@ -630,11 -487,11 +641,11 @@@ nl_add_multipath(struct nlmsghdr *h, ui
    nl_close_attr(h, a);
  }
  
 -static struct mpnh *
 +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 mpnh *nh_buffer;
 +  static struct nexthop *nh_buffer;
    static int nh_buf_size;     /* in number of structures */
    static int nh_buf_used;
  
  
        /* 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)
+         {
 -#ifndef IPV6
+       case AF_INET:
 -        if (!nl_parse_attrs(RTNH_DATA(nh), mpnh_attr_want4, a, sizeof(a)))
++        if (!nl_parse_attrs(RTNH_DATA(nh), nexthop_attr_want4, a, sizeof(a)))
+           return NULL;
+         break;
 -#else
++
+       case AF_INET6:
 -        if (!nl_parse_attrs(RTNH_DATA(nh), mpnh_attr_want6, a, sizeof(a)))
++        if (!nl_parse_attrs(RTNH_DATA(nh), nexthop_attr_want6, a, sizeof(a)))
+           return NULL;
+         break;
 -#endif
++
+       default:
+         return NULL;
+       }
        if (a[RTA_GATEWAY])
        {
 -        memcpy(&rv->gw, RTA_DATA(a[RTA_GATEWAY]), sizeof(rv->gw));
 -        ipa_ntoh(rv->gw);
 +        rv->gw = rta_get_ipa(a[RTA_GATEWAY]);
 +
 +        if (nh->rtnh_flags & RTNH_F_ONLINK)
 +          rv->flags |= RNF_ONLINK;
  
 -        neighbor *ng = neigh_find2(&p->p, &rv->gw, rv->iface,
 -                                   (nh->rtnh_flags & RTNH_F_ONLINK) ? NEF_ONLINK : 0);
 -        if (!ng || (ng->scope == SCOPE_HOST))
 +        neighbor *nbr;
 +        nbr = neigh_find2(&p->p, &rv->gw, rv->iface,
 +                          (rv->flags & RNF_ONLINK) ? NEF_ONLINK : 0);
 +        if (!nbr || (nbr->scope == SCOPE_HOST))
            return NULL;
        }
        else
@@@ -1518,14 -1266,15 +1544,14 @@@ nl_parse_route(struct nl_parse_state *s
    switch (i->rtm_type)
      {
      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])
 -      {
 -        ra->dest = RTD_MULTIPATH;
 -        ra->nexthops = nl_parse_multipath(p, a[RTA_MULTIPATH], i->rtm_family);
 -        if (!ra->nexthops)
++        {
++        struct nexthop *nh = nl_parse_multipath(p, a[RTA_MULTIPATH], i->rtm_family);
 +        if (!nh)
            {
 -            log(L_ERR "KRT: Received strange multipath route %I/%d",
 -                net->n.prefix, net->n.pxlen);
 +            log(L_ERR "KRT: Received strange multipath route %N", net->n.addr);
              return;
            }