Backports the following commits from systemd 234:

ce905cb44620e979862e63743133d354c90d28df icmp6-util: Bind Router Advertisement socket
9dab3e066bb5dfeefa28854ab7f95648eaf0d75d sd-radv: Receive Router Solicitations
d0d6045f658ac3e043235aee68a540650e0f98e0 sd-radv: Send Router Advertisments
dd39628ac19c691917aeeb01babaef411f146039 sd-radv: Implement Router Advertisement timeout handling
938c9ff7d374086b996d6a9a484f88ebd94731a3 sd-radv: Add Router Advertisement functionality
b076a89bb331b91a7cafcd77fb3c13484805b24f sd-radv: Add Router Advertisement prefix handling
1e4a54de3a79a0ba764e482122e381398eaf3e25 icmp6-util: Move multicast address definitions
42a7accc3dec5350f0e88ffa145a55293b6fd0e9 sd-ndisc.c: Move Router Solicitation sending after timer computaion
7b72b034e7c406209dfc55643b79ec0cb2636e4f sd-ndisc: Implement Router Solicitation backoff method
33261358b2a16d895d7beb5b34ebdacadb56cb56 sd-ndisc: Reset counter for sent Router Solicitations (#5874)
b40e6a51d55531314666e866d7f9813fce385023 networkd: RFC compliant autonomous prefix handling (#5636)

 Makefile.am                             |   3 +
 src/libsystemd-network/icmp6-util.c     | 117 ++++++++++-
 src/libsystemd-network/icmp6-util.h     |  13 ++
 src/libsystemd-network/ndisc-internal.h |   7 +-
 src/libsystemd-network/radv-internal.h  |  88 +++++++++
 src/libsystemd-network/sd-ndisc.c       | 184 ++++++++---------
 src/libsystemd-network/sd-radv.c        | 653 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/libsystemd-network/test-ndisc-rs.c  |  15 ++
 src/network/networkd-ndisc.c            |  30 ++-
 src/systemd/sd-radv.h                   |  81 ++++++++
 10 files changed, 1086 insertions(+), 105 deletions(-)
 create mode 100644 src/libsystemd-network/radv-internal.h
 create mode 100644 src/libsystemd-network/sd-radv.c
 create mode 100644 src/systemd/sd-radv.h

diff -rupN systemd-base/Makefile.am systemd/Makefile.am
--- systemd-base/Makefile.am	2017-09-18 14:39:25.988517310 -0700
+++ systemd/Makefile.am	2017-09-18 14:58:25.561666434 -0700
@@ -3629,6 +3629,7 @@ libsystemd_network_la_SOURCES = \
 	src/systemd/sd-ipv4ll.h \
 	src/systemd/sd-ipv4acd.h \
 	src/systemd/sd-ndisc.h \
+	src/systemd/sd-radv.h \
 	src/systemd/sd-dhcp6-client.h \
 	src/systemd/sd-dhcp6-lease.h \
 	src/systemd/sd-lldp.h \
@@ -3652,6 +3653,8 @@ libsystemd_network_la_SOURCES = \
 	src/libsystemd-network/ndisc-internal.h \
 	src/libsystemd-network/ndisc-router.h \
 	src/libsystemd-network/ndisc-router.c \
+	src/libsystemd-network/sd-radv.c \
+	src/libsystemd-network/radv-internal.h \
 	src/libsystemd-network/icmp6-util.h \
 	src/libsystemd-network/icmp6-util.c \
 	src/libsystemd-network/sd-dhcp6-client.c \
diff -rupN systemd-base/src/libsystemd-network/icmp6-util.c systemd/src/libsystemd-network/icmp6-util.c
--- systemd-base/src/libsystemd-network/icmp6-util.c	2017-09-18 14:39:26.016590111 -0700
+++ systemd/src/libsystemd-network/icmp6-util.c	2017-09-18 15:06:31.020575497 -0700
@@ -32,6 +32,7 @@
 #include "fd-util.h"
 #include "icmp6-util.h"
 #include "socket-util.h"
+#include "in-addr-util.h"
 
 #define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \
         { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
@@ -41,12 +42,9 @@
         { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }
 
-int icmp6_bind_router_solicitation(int index) {
-        struct icmp6_filter filter = { };
-        struct ipv6_mreq mreq = {
-                .ipv6mr_multiaddr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
-                .ipv6mr_interface = index,
-        };
+static int icmp6_bind_router_message(const struct icmp6_filter *filter,
+                                     const struct ipv6_mreq *mreq) {
+        int index = mreq->ipv6mr_interface;
         _cleanup_close_ int s = -1;
         char ifname[IF_NAMESIZE] = "";
         static const int zero = 0, one = 1, hops = 255;
@@ -56,9 +54,11 @@ int icmp6_bind_router_solicitation(int i
         if (s < 0)
                 return -errno;
 
-        ICMP6_FILTER_SETBLOCKALL(&filter);
-        ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
-        r = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter));
+        r = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, filter, sizeof(*filter));
+        if (r < 0)
+                return -errno;
+
+        r = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, mreq, sizeof(*mreq));
         if (r < 0)
                 return -errno;
 
@@ -78,7 +78,7 @@ int icmp6_bind_router_solicitation(int i
         if (r < 0)
                 return -errno;
 
-        r = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+        r = setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &hops, sizeof(hops));
         if (r < 0)
                 return -errno;
 
@@ -102,6 +102,32 @@ int icmp6_bind_router_solicitation(int i
         return r;
 }
 
+int icmp6_bind_router_solicitation(int index) {
+        struct icmp6_filter filter = {};
+        struct ipv6_mreq mreq = {
+                .ipv6mr_multiaddr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
+                .ipv6mr_interface = index,
+        };
+
+        ICMP6_FILTER_SETBLOCKALL(&filter);
+        ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
+
+        return icmp6_bind_router_message(&filter, &mreq);
+}
+
+int icmp6_bind_router_advertisement(int index) {
+        struct icmp6_filter filter = {};
+        struct ipv6_mreq mreq = {
+                .ipv6mr_multiaddr = IN6ADDR_ALL_ROUTERS_MULTICAST_INIT,
+                .ipv6mr_interface = index,
+        };
+
+        ICMP6_FILTER_SETBLOCKALL(&filter);
+        ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
+
+        return icmp6_bind_router_message(&filter, &mreq);
+}
+
 int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) {
         struct sockaddr_in6 dst = {
                 .sin6_family = AF_INET6,
@@ -139,3 +165,74 @@ int icmp6_send_router_solicitation(int s
 
         return 0;
 }
+
+int icmp6_receive(int fd, void *buffer, size_t size, struct in6_addr *dst,
+                  triple_timestamp *timestamp) {
+        union {
+                struct cmsghdr cmsghdr;
+                uint8_t buf[CMSG_SPACE(sizeof(int)) + /* ttl */
+                            CMSG_SPACE(sizeof(struct timeval))];
+        } control = {};
+        struct iovec iov = {};
+        union sockaddr_union sa = {};
+        struct msghdr msg = {
+                .msg_name = &sa.sa,
+                .msg_namelen = sizeof(sa),
+                .msg_iov = &iov,
+                .msg_iovlen = 1,
+                .msg_control = &control,
+                .msg_controllen = sizeof(control),
+        };
+        struct cmsghdr *cmsg;
+        ssize_t len;
+
+        iov.iov_base = buffer;
+        iov.iov_len = size;
+
+        len = recvmsg(fd, &msg, MSG_DONTWAIT);
+        if (len < 0) {
+                if (errno == EAGAIN || errno == EINTR)
+                        return 0;
+
+                return -errno;
+        }
+
+        if ((size_t) len != size)
+                return -EINVAL;
+
+        if (msg.msg_namelen == sizeof(struct sockaddr_in6) &&
+            sa.in6.sin6_family == AF_INET6)  {
+
+                *dst = sa.in6.sin6_addr;
+                if (in_addr_is_link_local(AF_INET6, (union in_addr_union*) dst) <= 0)
+                        return -EADDRNOTAVAIL;
+
+        } else if (msg.msg_namelen > 0)
+                return -EPFNOSUPPORT;
+
+        /* namelen == 0 only happens when running the test-suite over a socketpair */
+
+        assert(!(msg.msg_flags & MSG_CTRUNC));
+        assert(!(msg.msg_flags & MSG_TRUNC));
+
+        CMSG_FOREACH(cmsg, &msg) {
+                if (cmsg->cmsg_level == SOL_IPV6 &&
+                    cmsg->cmsg_type == IPV6_HOPLIMIT &&
+                    cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
+                        int hops = *(int*) CMSG_DATA(cmsg);
+
+                        if (hops != 255)
+                                return -EMULTIHOP;
+                }
+
+                if (cmsg->cmsg_level == SOL_SOCKET &&
+                    cmsg->cmsg_type == SO_TIMESTAMP &&
+                    cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
+                        triple_timestamp_from_realtime(timestamp, timeval_load((struct timeval*) CMSG_DATA(cmsg)));
+        }
+
+        if (!triple_timestamp_is_set(timestamp))
+                triple_timestamp_get(timestamp);
+
+        return 0;
+}
diff -rupN systemd-base/src/libsystemd-network/icmp6-util.h systemd/src/libsystemd-network/icmp6-util.h
--- systemd-base/src/libsystemd-network/icmp6-util.h	2017-09-18 14:39:26.016590111 -0700
+++ systemd/src/libsystemd-network/icmp6-util.h	2017-09-18 15:06:31.020575497 -0700
@@ -21,5 +21,18 @@
 
 #include <net/ethernet.h>
 
+#include "time-util.h"
+
+#define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \
+        { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } } }
+
+#define IN6ADDR_ALL_NODES_MULTICAST_INIT \
+        { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }
+
 int icmp6_bind_router_solicitation(int index);
+int icmp6_bind_router_advertisement(int index);
 int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr);
+int icmp6_receive(int fd, void *buffer, size_t size, struct in6_addr *dst,
+                  triple_timestamp *timestamp);
diff -rupN systemd-base/src/libsystemd-network/ndisc-internal.h systemd/src/libsystemd-network/ndisc-internal.h
--- systemd-base/src/libsystemd-network/ndisc-internal.h	2017-09-18 14:39:26.016590111 -0700
+++ systemd/src/libsystemd-network/ndisc-internal.h	2017-09-18 14:55:04.905317356 -0700
@@ -23,6 +23,10 @@
 
 #include "sd-ndisc.h"
 
+#define NDISC_ROUTER_SOLICITATION_INTERVAL (4U * USEC_PER_SEC)
+#define NDISC_MAX_ROUTER_SOLICITATION_INTERVAL (3600U * USEC_PER_SEC)
+#define NDISC_MAX_ROUTER_SOLICITATIONS 3U
+
 struct sd_ndisc {
         unsigned n_ref;
 
@@ -38,8 +42,9 @@ struct sd_ndisc {
 
         sd_event_source *recv_event_source;
         sd_event_source *timeout_event_source;
+        sd_event_source *timeout_no_ra;
 
-        unsigned nd_sent;
+        usec_t retransmit_time;
 
         sd_ndisc_callback_t callback;
         void *userdata;
diff -rupN systemd-base/src/libsystemd-network/radv-internal.h systemd/src/libsystemd-network/radv-internal.h
--- systemd-base/src/libsystemd-network/radv-internal.h	1969-12-31 16:00:00.000000000 -0800
+++ systemd/src/libsystemd-network/radv-internal.h	2017-09-18 15:00:40.022354604 -0700
@@ -0,0 +1,88 @@
+#pragma once
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2017 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include "sd-radv.h"
+
+#include "log.h"
+#include "list.h"
+#include "sparse-endian.h"
+
+#define SD_RADV_DEFAULT_MIN_TIMEOUT_USEC        (200*USEC_PER_SEC)
+#define SD_RADV_DEFAULT_MAX_TIMEOUT_USEC        (600*USEC_PER_SEC)
+assert_cc(SD_RADV_DEFAULT_MIN_TIMEOUT_USEC <= SD_RADV_DEFAULT_MAX_TIMEOUT_USEC)
+
+#define SD_RADV_MAX_INITIAL_RTR_ADVERT_INTERVAL_USEC (16*USEC_PER_SEC)
+#define SD_RADV_MAX_INITIAL_RTR_ADVERTISEMENTS  3
+#define SD_RADV_MAX_FINAL_RTR_ADVERTISEMENTS    3
+#define SD_RADV_MIN_DELAY_BETWEEN_RAS           3
+#define SD_RADV_MAX_RA_DELAY_TIME_USEC          (500*USEC_PER_MSEC)
+
+enum RAdvState {
+        SD_RADV_STATE_IDLE                      = 0,
+        SD_RADV_STATE_ADVERTISING               = 1,
+};
+typedef enum RAdvState RAdvState;
+
+struct sd_radv {
+        unsigned n_ref;
+        RAdvState state;
+
+        int ifindex;
+
+        sd_event *event;
+        int event_priority;
+
+        struct ether_addr mac_addr;
+        uint8_t hop_limit;
+        uint8_t flags;
+        uint32_t mtu;
+        uint16_t lifetime;
+
+        int fd;
+        unsigned ra_sent;
+        sd_event_source *recv_event_source;
+        sd_event_source *timeout_event_source;
+
+        unsigned n_prefixes;
+        LIST_HEAD(sd_radv_prefix, prefixes);
+};
+
+struct sd_radv_prefix {
+        unsigned n_ref;
+
+        struct {
+                uint8_t type;
+                uint8_t length;
+                uint8_t prefixlen;
+                uint8_t flags;
+                be32_t valid_lifetime;
+                be32_t preferred_lifetime;
+                uint32_t reserved;
+                struct in6_addr in6_addr;
+        } _packed_ opt;
+
+        LIST_FIELDS(struct sd_radv_prefix, prefix);
+};
+
+#define log_radv_full(level, error, fmt, ...) log_internal(level, error, __FILE__, __LINE__, __func__, "RADV: " fmt, ##__VA_ARGS__)
+#define log_radv_errno(error, fmt, ...) log_radv_full(LOG_DEBUG, error, fmt, ##__VA_ARGS__)
+#define log_radv_warning_errno(error, fmt, ...) log_radv_full(LOG_WARNING, error, fmt, ##__VA_ARGS__)
+#define log_radv(fmt, ...) log_radv_errno(0, fmt, ##__VA_ARGS__)
diff -rupN systemd-base/src/libsystemd-network/sd-ndisc.c systemd/src/libsystemd-network/sd-ndisc.c
--- systemd-base/src/libsystemd-network/sd-ndisc.c	2017-09-18 14:39:26.016590111 -0700
+++ systemd/src/libsystemd-network/sd-ndisc.c	2017-09-18 15:00:40.038396204 -0700
@@ -28,12 +28,12 @@
 #include "in-addr-util.h"
 #include "ndisc-internal.h"
 #include "ndisc-router.h"
+#include "random-util.h"
 #include "socket-util.h"
 #include "string-util.h"
 #include "util.h"
 
-#define NDISC_ROUTER_SOLICITATION_INTERVAL (4U * USEC_PER_SEC)
-#define NDISC_MAX_ROUTER_SOLICITATIONS 3U
+#define NDISC_TIMEOUT_NO_RA_USEC (NDISC_ROUTER_SOLICITATION_INTERVAL * NDISC_MAX_ROUTER_SOLICITATIONS)
 
 static void ndisc_callback(sd_ndisc *ndisc, sd_ndisc_event event, sd_ndisc_router *rt) {
         assert(ndisc);
@@ -129,6 +129,8 @@ static int ndisc_reset(sd_ndisc *nd) {
         assert(nd);
 
         nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source);
+        nd->timeout_no_ra = sd_event_source_unref(nd->timeout_no_ra);
+        nd->retransmit_time = 0;
         nd->recv_event_source = sd_event_source_unref(nd->recv_event_source);
         nd->fd = safe_close(nd->fd);
 
@@ -221,23 +223,9 @@ static int ndisc_handle_datagram(sd_ndis
 static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
         _cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL;
         sd_ndisc *nd = userdata;
-        union {
-                struct cmsghdr cmsghdr;
-                uint8_t buf[CMSG_SPACE(sizeof(int)) + /* ttl */
-                            CMSG_SPACE(sizeof(struct timeval))];
-        } control = {};
-        struct iovec iov = {};
-        union sockaddr_union sa = {};
-        struct msghdr msg = {
-                .msg_name = &sa.sa,
-                .msg_namelen = sizeof(sa),
-                .msg_iov = &iov,
-                .msg_iovlen = 1,
-                .msg_control = &control,
-                .msg_controllen = sizeof(control),
-        };
-        struct cmsghdr *cmsg;
-        ssize_t len, buflen;
+        ssize_t buflen;
+        int r;
+        _cleanup_free_ char *addr = NULL;
 
         assert(s);
         assert(nd);
@@ -251,110 +239,90 @@ static int ndisc_recv(sd_event_source *s
         if (!rt)
                 return -ENOMEM;
 
-        iov.iov_base = NDISC_ROUTER_RAW(rt);
-        iov.iov_len = rt->raw_size;
-
-        len = recvmsg(fd, &msg, MSG_DONTWAIT);
-        if (len < 0) {
-                if (errno == EAGAIN || errno == EINTR)
-                        return 0;
-
-                return log_ndisc_errno(errno, "Could not receive message from ICMPv6 socket: %m");
-        }
-
-        if ((size_t) len != rt->raw_size) {
-                log_ndisc("Packet size mismatch.");
-                return -EINVAL;
-        }
-
-        if (msg.msg_namelen == sizeof(struct sockaddr_in6) &&
-            sa.in6.sin6_family == AF_INET6)  {
-
-                if (in_addr_is_link_local(AF_INET6, (union in_addr_union*) &sa.in6.sin6_addr) <= 0) {
-                        _cleanup_free_ char *addr = NULL;
-
-                        (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &sa.in6.sin6_addr, &addr);
-                        log_ndisc("Received RA from non-link-local address %s. Ignoring.", strna(addr));
-                        return 0;
-                }
-
-                rt->address = sa.in6.sin6_addr;
-
-        } else if (msg.msg_namelen > 0) {
-                log_ndisc("Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t) msg.msg_namelen);
-                return -EINVAL;
-        }
-
-        /* namelen == 0 only happens when running the test-suite over a socketpair */
-
-        assert(!(msg.msg_flags & MSG_CTRUNC));
-        assert(!(msg.msg_flags & MSG_TRUNC));
-
-        CMSG_FOREACH(cmsg, &msg) {
-                if (cmsg->cmsg_level == SOL_IPV6 &&
-                    cmsg->cmsg_type == IPV6_HOPLIMIT &&
-                    cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
-                        int hops = *(int*) CMSG_DATA(cmsg);
-
-                        if (hops != 255) {
-                                log_ndisc("Received RA with invalid hop limit %d. Ignoring.", hops);
-                                return 0;
-                        }
+        r = icmp6_receive(fd, NDISC_ROUTER_RAW(rt), rt->raw_size, &rt->address,
+                     &rt->timestamp);
+        if (r < 0) {
+                switch (r) {
+                case -EADDRNOTAVAIL:
+                        (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &rt->address, &addr);
+                        log_ndisc("Received RA from non-link-local address %s. Ignoring", addr);
+                        break;
+
+                case -EMULTIHOP:
+                        log_ndisc("Received RA with invalid hop limit. Ignoring.");
+                        break;
+
+                case -EPFNOSUPPORT:
+                        log_ndisc("Received invalid source address from ICMPv6 socket.");
+                        break;
                 }
 
-                if (cmsg->cmsg_level == SOL_SOCKET &&
-                    cmsg->cmsg_type == SO_TIMESTAMP &&
-                    cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
-                        triple_timestamp_from_realtime(&rt->timestamp, timeval_load((struct timeval*) CMSG_DATA(cmsg)));
+                return 0;
         }
 
-        if (!triple_timestamp_is_set(&rt->timestamp))
-                triple_timestamp_get(&rt->timestamp);
-
         nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source);
 
         return ndisc_handle_datagram(nd, rt);
 }
 
+static usec_t ndisc_timeout_compute_random(usec_t val) {
+        /* compute a time that is random within ±10% of the given value */
+        return val - val / 10 +
+                (random_u64() % (2 * USEC_PER_SEC)) * val / 10 / USEC_PER_SEC;
+}
+
 static int ndisc_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
         sd_ndisc *nd = userdata;
-        usec_t time_now, next_timeout;
+        usec_t time_now;
         int r;
+        char time_string[FORMAT_TIMESPAN_MAX];
 
         assert(s);
         assert(nd);
         assert(nd->event);
 
-        if (nd->nd_sent >= NDISC_MAX_ROUTER_SOLICITATIONS) {
-                nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source);
-                ndisc_callback(nd, SD_NDISC_EVENT_TIMEOUT, NULL);
-                return 0;
+        assert_se(sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
+
+        nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source);
+
+        if (!nd->retransmit_time)
+                nd->retransmit_time = ndisc_timeout_compute_random(NDISC_ROUTER_SOLICITATION_INTERVAL);
+        else {
+                if (nd->retransmit_time > NDISC_MAX_ROUTER_SOLICITATION_INTERVAL / 2)
+                        nd->retransmit_time = ndisc_timeout_compute_random(NDISC_MAX_ROUTER_SOLICITATION_INTERVAL);
+                else
+                        nd->retransmit_time += ndisc_timeout_compute_random(nd->retransmit_time);
         }
 
-        r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr);
-        if (r < 0) {
-                log_ndisc_errno(r, "Error sending Router Solicitation: %m");
+        r = sd_event_add_time(nd->event, &nd->timeout_event_source,
+                              clock_boottime_or_monotonic(),
+                              time_now + nd->retransmit_time,
+                              10 * USEC_PER_MSEC, ndisc_timeout, nd);
+        if (r < 0)
                 goto fail;
-        }
 
-        log_ndisc("Sent Router Solicitation");
-        nd->nd_sent++;
+        r = sd_event_source_set_priority(nd->timeout_event_source, nd->event_priority);
+        if (r < 0)
+                goto fail;
 
-        assert_se(sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
-        next_timeout = time_now + NDISC_ROUTER_SOLICITATION_INTERVAL;
+        (void) sd_event_source_set_description(nd->timeout_event_source, "ndisc-timeout-no-ra");
 
-        r = sd_event_source_set_time(nd->timeout_event_source, next_timeout);
+        r = sd_event_source_set_enabled(nd->timeout_event_source, SD_EVENT_ONESHOT);
         if (r < 0) {
-                log_ndisc_errno(r, "Error updating timer: %m");
+                log_ndisc_errno(r, "Error reenabling timer: %m");
                 goto fail;
         }
 
-        r = sd_event_source_set_enabled(nd->timeout_event_source, SD_EVENT_ONESHOT);
+        r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr);
         if (r < 0) {
-                log_ndisc_errno(r, "Error reenabling timer: %m");
+                log_ndisc_errno(r, "Error sending Router Solicitation: %m");
                 goto fail;
         }
 
+        log_ndisc("Sent Router Solicitation, next solicitation in %s",
+                  format_timespan(time_string, FORMAT_TIMESPAN_MAX,
+                                  nd->retransmit_time, USEC_PER_SEC));
+
         return 0;
 
 fail:
@@ -362,6 +330,20 @@ fail:
         return 0;
 }
 
+static int ndisc_timeout_no_ra(sd_event_source *s, uint64_t usec, void *userdata) {
+        sd_ndisc *nd = userdata;
+
+        assert(s);
+        assert(nd);
+
+        log_ndisc("No RA received before link confirmation timeout");
+
+        nd->timeout_no_ra = sd_event_source_unref(nd->timeout_no_ra);
+        ndisc_callback(nd, SD_NDISC_EVENT_TIMEOUT, NULL);
+
+        return 0;
+}
+
 _public_ int sd_ndisc_stop(sd_ndisc *nd) {
         assert_return(nd, -EINVAL);
 
@@ -376,6 +358,7 @@ _public_ int sd_ndisc_stop(sd_ndisc *nd)
 
 _public_ int sd_ndisc_start(sd_ndisc *nd) {
         int r;
+        usec_t time_now;
 
         assert_return(nd, -EINVAL);
         assert_return(nd->event, -EINVAL);
@@ -387,6 +370,10 @@ _public_ int sd_ndisc_start(sd_ndisc *nd
         assert(!nd->recv_event_source);
         assert(!nd->timeout_event_source);
 
+        r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
+        if (r < 0)
+                goto fail;
+
         nd->fd = icmp6_bind_router_solicitation(nd->ifindex);
         if (nd->fd < 0)
                 return nd->fd;
@@ -411,6 +398,19 @@ _public_ int sd_ndisc_start(sd_ndisc *nd
 
         (void) sd_event_source_set_description(nd->timeout_event_source, "ndisc-timeout");
 
+        r = sd_event_add_time(nd->event, &nd->timeout_no_ra,
+                              clock_boottime_or_monotonic(),
+                              time_now + NDISC_TIMEOUT_NO_RA_USEC,
+                              10 * USEC_PER_MSEC, ndisc_timeout_no_ra, nd);
+        if (r < 0)
+                goto fail;
+
+        r = sd_event_source_set_priority(nd->timeout_no_ra, nd->event_priority);
+        if (r < 0)
+                goto fail;
+
+        (void) sd_event_source_set_description(nd->timeout_no_ra, "ndisc-timeout-no-ra");
+
         log_ndisc("Started IPv6 Router Solicitation client");
         return 1;
 
diff -rupN systemd-base/src/libsystemd-network/sd-radv.c systemd/src/libsystemd-network/sd-radv.c
--- systemd-base/src/libsystemd-network/sd-radv.c	1969-12-31 16:00:00.000000000 -0800
+++ systemd/src/libsystemd-network/sd-radv.c	2017-09-18 15:00:40.022354604 -0700
@@ -0,0 +1,653 @@
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2017 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <netinet/icmp6.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <linux/in6.h>
+
+#include "sd-radv.h"
+
+#include "macro.h"
+#include "alloc-util.h"
+#include "fd-util.h"
+#include "icmp6-util.h"
+#include "in-addr-util.h"
+#include "radv-internal.h"
+#include "socket-util.h"
+#include "string-util.h"
+#include "util.h"
+#include "random-util.h"
+
+_public_ int sd_radv_new(sd_radv **ret) {
+        _cleanup_(sd_radv_unrefp) sd_radv *ra = NULL;
+
+        assert_return(ret, -EINVAL);
+
+        ra = new0(sd_radv, 1);
+        if (!ra)
+                return -ENOMEM;
+
+        ra->n_ref = 1;
+        ra->fd = -1;
+
+        LIST_HEAD_INIT(ra->prefixes);
+
+        *ret = ra;
+        ra = NULL;
+
+        return 0;
+}
+
+_public_ int sd_radv_attach_event(sd_radv *ra, sd_event *event, int64_t priority) {
+        int r;
+
+        assert_return(ra, -EINVAL);
+        assert_return(!ra->event, -EBUSY);
+
+        if (event)
+                ra->event = sd_event_ref(event);
+        else {
+                r = sd_event_default(&ra->event);
+                if (r < 0)
+                        return 0;
+        }
+
+        ra->event_priority = priority;
+
+        return 0;
+}
+
+_public_ int sd_radv_detach_event(sd_radv *ra) {
+
+        assert_return(ra, -EINVAL);
+
+        ra->event = sd_event_unref(ra->event);
+        return 0;
+}
+
+_public_ sd_event *sd_radv_get_event(sd_radv *ra) {
+        assert_return(ra, NULL);
+
+        return ra->event;
+}
+
+static void radv_reset(sd_radv *ra) {
+
+        ra->timeout_event_source =
+                sd_event_source_unref(ra->timeout_event_source);
+
+        ra->recv_event_source =
+                sd_event_source_unref(ra->recv_event_source);
+
+        ra->ra_sent = 0;
+}
+
+_public_ sd_radv *sd_radv_ref(sd_radv *ra) {
+        if (!ra)
+                return NULL;
+
+        assert(ra->n_ref > 0);
+        ra->n_ref++;
+
+        return ra;
+}
+
+_public_ sd_radv *sd_radv_unref(sd_radv *ra) {
+        if (!ra)
+                return NULL;
+
+        assert(ra->n_ref > 0);
+        ra->n_ref--;
+
+        if (ra->n_ref > 0)
+                return NULL;
+
+        while (ra->prefixes) {
+                sd_radv_prefix *p = ra->prefixes;
+
+                LIST_REMOVE(prefix, ra->prefixes, p);
+                sd_radv_prefix_unref(p);
+        }
+
+        radv_reset(ra);
+
+        sd_radv_detach_event(ra);
+        return mfree(ra);
+}
+
+static int radv_send(sd_radv *ra, const struct in6_addr *dst,
+                     const uint32_t router_lifetime) {
+        static const struct ether_addr mac_zero = {};
+        sd_radv_prefix *p;
+        struct sockaddr_in6 dst_addr = {
+                .sin6_family = AF_INET6,
+                .sin6_addr = IN6ADDR_ALL_NODES_MULTICAST_INIT,
+        };
+        struct nd_router_advert adv = {};
+        struct {
+                struct nd_opt_hdr opthdr;
+                struct ether_addr slladdr;
+        } _packed_ opt_mac = {
+                .opthdr = {
+                        .nd_opt_type = ND_OPT_SOURCE_LINKADDR,
+                        .nd_opt_len = (sizeof(struct nd_opt_hdr) +
+                                       sizeof(struct ether_addr) - 1) /8 + 1,
+                },
+        };
+        struct nd_opt_mtu opt_mtu =  {
+                .nd_opt_mtu_type = ND_OPT_MTU,
+                .nd_opt_mtu_len = 1,
+        };
+        /* Reserve iov space for RA header, linkaddr, MTU + N prefixes */
+        struct iovec iov[3 + ra->n_prefixes];
+        struct msghdr msg = {
+                .msg_name = &dst_addr,
+                .msg_namelen = sizeof(dst_addr),
+                .msg_iov = iov,
+        };
+
+        if (dst && !in_addr_is_null(AF_INET6, (union in_addr_union*) dst))
+                dst_addr.sin6_addr = *dst;
+
+        adv.nd_ra_type = ND_ROUTER_ADVERT;
+        adv.nd_ra_curhoplimit = ra->hop_limit;
+        adv.nd_ra_flags_reserved = ra->flags;
+        adv.nd_ra_router_lifetime = htobe16(router_lifetime);
+        iov[msg.msg_iovlen].iov_base = &adv;
+        iov[msg.msg_iovlen].iov_len = sizeof(adv);
+        msg.msg_iovlen++;
+
+        /* MAC address is optional, either because the link does not use L2
+           addresses or load sharing is desired. See RFC 4861, Section 4.2 */
+        if (memcmp(&mac_zero, &ra->mac_addr, sizeof(mac_zero))) {
+                opt_mac.slladdr = ra->mac_addr;
+                iov[msg.msg_iovlen].iov_base = &opt_mac;
+                iov[msg.msg_iovlen].iov_len = sizeof(opt_mac);
+                msg.msg_iovlen++;
+        }
+
+        if (ra->mtu) {
+                opt_mtu.nd_opt_mtu_mtu = htobe32(ra->mtu);
+                iov[msg.msg_iovlen].iov_base = &opt_mtu;
+                iov[msg.msg_iovlen].iov_len = sizeof(opt_mtu);
+                msg.msg_iovlen++;
+        }
+
+        LIST_FOREACH(prefix, p, ra->prefixes) {
+                iov[msg.msg_iovlen].iov_base = &p->opt;
+                iov[msg.msg_iovlen].iov_len = sizeof(p->opt);
+                msg.msg_iovlen++;
+        }
+
+        if (sendmsg(ra->fd, &msg, 0) < 0)
+                return -errno;
+
+        return 0;
+}
+
+static int radv_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
+        sd_radv *ra = userdata;
+        _cleanup_free_ char *addr = NULL;
+        struct in6_addr src;
+        triple_timestamp timestamp;
+        int r;
+        ssize_t buflen;
+        _cleanup_free_ char *buf = NULL;
+
+        assert(s);
+        assert(ra);
+        assert(ra->event);
+
+        buflen = next_datagram_size_fd(fd);
+
+        if ((unsigned) buflen < sizeof(struct nd_router_solicit))
+                return log_radv("Too short packet received");
+
+        buf = new0(char, buflen);
+        if (!buf)
+                return 0;
+
+        r = icmp6_receive(fd, buf, buflen, &src, &timestamp);
+        if (r < 0) {
+                switch (r) {
+                case -EADDRNOTAVAIL:
+                        (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &src, &addr);
+                        log_radv("Received RS from non-link-local address %s. Ignoring", addr);
+                        break;
+
+                case -EMULTIHOP:
+                        log_radv("Received RS with invalid hop limit. Ignoring.");
+                        break;
+
+                case -EPFNOSUPPORT:
+                        log_radv("Received invalid source address from ICMPv6 socket. Ignoring.");
+                        break;
+
+                default:
+                        log_radv_warning_errno(r, "Error receiving from ICMPv6 socket: %m");
+                        break;
+                }
+
+                return 0;
+        }
+
+        (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &src, &addr);
+
+        r = radv_send(ra, &src, ra->lifetime);
+        if (r < 0)
+                log_radv_warning_errno(r, "Unable to send solicited Router Advertisment to %s: %m", addr);
+        else
+                log_radv("Sent solicited Router Advertisement to %s", addr);
+
+        return 0;
+}
+
+static usec_t radv_compute_timeout(usec_t min, usec_t max) {
+        assert_return(min <= max, SD_RADV_DEFAULT_MIN_TIMEOUT_USEC);
+
+        return min + (random_u32() % (max - min));
+}
+
+static int radv_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+        int r;
+        sd_radv *ra = userdata;
+        usec_t min_timeout = SD_RADV_DEFAULT_MIN_TIMEOUT_USEC;
+        usec_t max_timeout = SD_RADV_DEFAULT_MAX_TIMEOUT_USEC;
+        usec_t time_now, timeout;
+        char time_string[FORMAT_TIMESPAN_MAX];
+
+        assert(s);
+        assert(ra);
+        assert(ra->event);
+
+        ra->timeout_event_source = sd_event_source_unref(ra->timeout_event_source);
+
+        r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now);
+        if (r < 0)
+                goto fail;
+
+        r = radv_send(ra, NULL, ra->lifetime);
+        if (r < 0)
+                log_radv_warning_errno(r, "Unable to send Router Advertisement: %m");
+
+        /* RFC 4861, Section 6.2.4, sending initial Router Advertisements */
+        if (ra->ra_sent < SD_RADV_MAX_INITIAL_RTR_ADVERTISEMENTS) {
+                max_timeout = SD_RADV_MAX_INITIAL_RTR_ADVERT_INTERVAL_USEC;
+                min_timeout = SD_RADV_MAX_INITIAL_RTR_ADVERT_INTERVAL_USEC / 3;
+        }
+
+        timeout = radv_compute_timeout(min_timeout, max_timeout);
+
+        log_radv("Next Router Advertisement in %s",
+                 format_timespan(time_string, FORMAT_TIMESPAN_MAX,
+                                 timeout, USEC_PER_SEC));
+
+        r = sd_event_add_time(ra->event, &ra->timeout_event_source,
+                              clock_boottime_or_monotonic(),
+                              time_now + timeout, MSEC_PER_SEC,
+                              radv_timeout, ra);
+        if (r < 0)
+                goto fail;
+
+        r = sd_event_source_set_priority(ra->timeout_event_source,
+                                         ra->event_priority);
+        if (r < 0)
+                goto fail;
+
+        r = sd_event_source_set_description(ra->timeout_event_source,
+                                            "radv-timeout");
+        if (r < 0)
+                goto fail;
+
+        ra->ra_sent++;
+
+fail:
+        if (r < 0)
+                sd_radv_stop(ra);
+
+        return 0;
+}
+
+_public_ int sd_radv_stop(sd_radv *ra) {
+        int r;
+
+        assert_return(ra, -EINVAL);
+
+        log_radv("Stopping IPv6 Router Advertisement daemon");
+
+        /* RFC 4861, Section 6.2.5, send at least one Router Advertisement
+           with zero lifetime  */
+        r = radv_send(ra, NULL, 0);
+        if (r < 0)
+                log_radv_warning_errno(r, "Unable to send last Router Advertisement with router lifetime set to zero: %m");
+
+        radv_reset(ra);
+        ra->fd = safe_close(ra->fd);
+        ra->state = SD_RADV_STATE_IDLE;
+
+        return 0;
+}
+
+_public_ int sd_radv_start(sd_radv *ra) {
+        int r = 0;
+
+        assert_return(ra, -EINVAL);
+        assert_return(ra->event, -EINVAL);
+        assert_return(ra->ifindex > 0, -EINVAL);
+
+        if (ra->state != SD_RADV_STATE_IDLE)
+                return 0;
+
+        r = sd_event_add_time(ra->event, &ra->timeout_event_source,
+                              clock_boottime_or_monotonic(), 0, 0,
+                              radv_timeout, ra);
+        if (r < 0)
+                goto fail;
+
+        r = sd_event_source_set_priority(ra->timeout_event_source,
+                                         ra->event_priority);
+        if (r < 0)
+                goto fail;
+
+        (void) sd_event_source_set_description(ra->timeout_event_source,
+                                               "radv-timeout");
+
+        r = icmp6_bind_router_advertisement(ra->ifindex);
+        if (r < 0)
+                goto fail;
+
+        ra->fd = r;
+
+        r = sd_event_add_io(ra->event, &ra->recv_event_source, ra->fd, EPOLLIN, radv_recv, ra);
+        if (r < 0)
+                goto fail;
+
+        r = sd_event_source_set_priority(ra->recv_event_source, ra->event_priority);
+        if (r < 0)
+                goto fail;
+
+        (void) sd_event_source_set_description(ra->recv_event_source, "radv-receive-message");
+
+        ra->state = SD_RADV_STATE_ADVERTISING;
+
+        log_radv("Started IPv6 Router Advertisement daemon");
+
+        return 0;
+
+ fail:
+        radv_reset(ra);
+
+        return r;
+}
+
+_public_ int sd_radv_set_ifindex(sd_radv *ra, int ifindex) {
+        assert_return(ra, -EINVAL);
+        assert_return(ifindex >= -1, -EINVAL);
+
+        if (ra->state != SD_RADV_STATE_IDLE)
+                return -EBUSY;
+
+        ra->ifindex = ifindex;
+
+        return 0;
+}
+
+_public_ int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr) {
+        assert_return(ra, -EINVAL);
+
+        if (ra->state != SD_RADV_STATE_IDLE)
+                return -EBUSY;
+
+        if (mac_addr)
+                ra->mac_addr = *mac_addr;
+        else
+                zero(ra->mac_addr);
+
+        return 0;
+}
+
+_public_ int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu) {
+        assert_return(ra, -EINVAL);
+        assert_return(mtu >= 1280, -EINVAL);
+
+        if (ra->state != SD_RADV_STATE_IDLE)
+                return -EBUSY;
+
+        ra->mtu = mtu;
+
+        return 0;
+}
+
+_public_ int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit) {
+        assert_return(ra, -EINVAL);
+
+        if (ra->state != SD_RADV_STATE_IDLE)
+                return -EBUSY;
+
+        ra->hop_limit = hop_limit;
+
+        return 0;
+}
+
+_public_ int sd_radv_set_router_lifetime(sd_radv *ra, uint32_t router_lifetime) {
+        assert_return(ra, -EINVAL);
+
+        if (ra->state != SD_RADV_STATE_IDLE)
+                return -EBUSY;
+
+        /* RFC 4191, Section 2.2, "...If the Router Lifetime is zero, the
+           preference value MUST be set to (00) by the sender..." */
+        if (router_lifetime == 0 &&
+            (ra->flags & (0x3 << 3)) != (SD_NDISC_PREFERENCE_MEDIUM << 3))
+                return -ETIME;
+
+        ra->lifetime = router_lifetime;
+
+        return 0;
+}
+
+_public_ int sd_radv_set_managed_information(sd_radv *ra, int managed) {
+        assert_return(ra, -EINVAL);
+
+        if (ra->state != SD_RADV_STATE_IDLE)
+                return -EBUSY;
+
+        SET_FLAG(ra->flags, ND_RA_FLAG_MANAGED, managed);
+
+        return 0;
+}
+
+_public_ int sd_radv_set_other_information(sd_radv *ra, int other) {
+        assert_return(ra, -EINVAL);
+
+        if (ra->state != SD_RADV_STATE_IDLE)
+                return -EBUSY;
+
+        SET_FLAG(ra->flags, ND_RA_FLAG_OTHER, other);
+
+        return 0;
+}
+
+_public_ int sd_radv_set_preference(sd_radv *ra, unsigned preference) {
+        int r = 0;
+
+        assert_return(ra, -EINVAL);
+        assert_return(IN_SET(preference,
+                             SD_NDISC_PREFERENCE_LOW,
+                             SD_NDISC_PREFERENCE_MEDIUM,
+                             SD_NDISC_PREFERENCE_HIGH), -EINVAL);
+
+        ra->flags = (ra->flags & ~(0x3 << 3)) | (preference << 3);
+
+        return r;
+}
+
+_public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) {
+        sd_radv_prefix *cur;
+        _cleanup_free_ char *addr_p = NULL;
+
+        assert_return(ra, -EINVAL);
+
+        if (!p)
+                return -EINVAL;
+
+        LIST_FOREACH(prefix, cur, ra->prefixes) {
+                int r;
+
+                r = in_addr_prefix_intersect(AF_INET6,
+                                             (union in_addr_union*) &cur->opt.in6_addr,
+                                             cur->opt.prefixlen,
+                                             (union in_addr_union*) &p->opt.in6_addr,
+                                             p->opt.prefixlen);
+                if (r > 0) {
+                        _cleanup_free_ char *addr_cur = NULL;
+
+                        (void) in_addr_to_string(AF_INET6,
+                                                 (union in_addr_union*) &cur->opt.in6_addr,
+                                                 &addr_cur);
+                        (void) in_addr_to_string(AF_INET6,
+                                                 (union in_addr_union*) &p->opt.in6_addr,
+                                                 &addr_p);
+
+                        log_radv("IPv6 prefix %s/%u already configured, ignoring %s/%u",
+                                 addr_cur, cur->opt.prefixlen,
+                                 addr_p, p->opt.prefixlen);
+
+                        return -EEXIST;
+                }
+        }
+
+        p = sd_radv_prefix_ref(p);
+
+        LIST_APPEND(prefix, ra->prefixes, p);
+
+        ra->n_prefixes++;
+
+        (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &p->opt.in6_addr, &addr_p);
+        log_radv("Added prefix %s/%d", addr_p, p->opt.prefixlen);
+
+        return 0;
+}
+
+_public_ int sd_radv_prefix_new(sd_radv_prefix **ret) {
+        _cleanup_(sd_radv_prefix_unrefp) sd_radv_prefix *p = NULL;
+
+        assert_return(ret, -EINVAL);
+
+        p = new0(sd_radv_prefix, 1);
+        if (!p)
+                return -ENOMEM;
+
+        p->n_ref = 1;
+
+        p->opt.type = ND_OPT_PREFIX_INFORMATION;
+        p->opt.length = (sizeof(p->opt) - 1) /8 + 1;
+
+        p->opt.prefixlen = 64;
+
+        /* RFC 4861, Section 6.2.1 */
+        SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_ONLINK, true);
+        SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_AUTO, true);
+        p->opt.preferred_lifetime = htobe32(604800);
+        p->opt.valid_lifetime = htobe32(2592000);
+
+        LIST_INIT(prefix, p);
+
+        *ret = p;
+        p = NULL;
+
+        return 0;
+}
+
+_public_ sd_radv_prefix *sd_radv_prefix_ref(sd_radv_prefix *p) {
+        if (!p)
+                return NULL;
+
+        assert(p->n_ref > 0);
+        p->n_ref++;
+
+        return p;
+}
+
+_public_ sd_radv_prefix *sd_radv_prefix_unref(sd_radv_prefix *p) {
+        if (!p)
+                return NULL;
+
+        assert(p->n_ref > 0);
+        p->n_ref--;
+
+        if (p->n_ref > 0)
+                return NULL;
+
+        return mfree(p);
+}
+
+_public_ int sd_radv_prefix_set_prefix(sd_radv_prefix *p, struct in6_addr *in6_addr,
+                                       unsigned char prefixlen) {
+        assert_return(p, -EINVAL);
+        assert_return(in6_addr, -EINVAL);
+
+        if (prefixlen < 3 || prefixlen > 128)
+                return -EINVAL;
+
+        if (prefixlen > 64)
+                /* unusual but allowed, log it */
+                log_radv("Unusual prefix length %d greater than 64", prefixlen);
+
+        p->opt.in6_addr = *in6_addr;
+        p->opt.prefixlen = prefixlen;
+
+        return 0;
+}
+
+_public_ int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink) {
+        assert_return(p, -EINVAL);
+
+        SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_ONLINK, onlink);
+
+        return 0;
+}
+
+_public_ int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix *p,
+                                                          int address_autoconfiguration) {
+        assert_return(p, -EINVAL);
+
+        SET_FLAG(p->opt.flags, ND_OPT_PI_FLAG_AUTO, address_autoconfiguration);
+
+        return 0;
+}
+
+_public_ int sd_radv_prefix_set_valid_lifetime(sd_radv_prefix *p,
+                                               uint32_t valid_lifetime) {
+        assert_return(p, -EINVAL);
+
+        p->opt.valid_lifetime = htobe32(valid_lifetime);
+
+        return 0;
+}
+
+_public_ int sd_radv_prefix_set_preferred_lifetime(sd_radv_prefix *p,
+                                                   uint32_t preferred_lifetime) {
+        assert_return(p, -EINVAL);
+
+        p->opt.preferred_lifetime = htobe32(preferred_lifetime);
+
+        return 0;
+}
diff -rupN systemd-base/src/libsystemd-network/test-ndisc-rs.c systemd/src/libsystemd-network/test-ndisc-rs.c
--- systemd-base/src/libsystemd-network/test-ndisc-rs.c	2017-09-18 14:39:26.016590111 -0700
+++ systemd/src/libsystemd-network/test-ndisc-rs.c	2017-09-18 15:07:09.099322638 -0700
@@ -193,6 +193,21 @@ int icmp6_bind_router_solicitation(int i
         return test_fd[0];
 }
 
+int icmp6_bind_router_advertisement(int index) {
+
+        return -ENOSYS;
+}
+
+int icmp6_receive(int fd, void *iov_base, size_t iov_len,
+                  struct in6_addr *dst, triple_timestamp *timestamp) {
+        assert (read (fd, iov_base, iov_len) == (ssize_t)iov_len);
+
+        if (timestamp)
+                triple_timestamp_get(timestamp);
+
+        return 0;
+}
+
 static int send_ra(uint8_t flags) {
         uint8_t advertisement[] = {
                 0x86, 0x00, 0xde, 0x83, 0x40, 0xc0, 0x00, 0xb4,
diff -rupN systemd-base/src/network/networkd-ndisc.c systemd/src/network/networkd-ndisc.c
--- systemd-base/src/network/networkd-ndisc.c	2017-09-18 14:39:26.020600510 -0700
+++ systemd/src/network/networkd-ndisc.c	2017-09-18 14:39:40.690643380 -0700
@@ -27,6 +27,7 @@
 
 #define NDISC_DNSSL_MAX 64U
 #define NDISC_RDNSS_MAX 64U
+#define NDISC_PREFIX_LFT_MIN 7200U
 
 static int ndisc_netlink_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
         _cleanup_link_unref_ Link *link = userdata;
@@ -152,13 +153,21 @@ static void ndisc_router_process_default
 
 static void ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) {
         _cleanup_address_free_ Address *address = NULL;
-        uint32_t lifetime_valid, lifetime_preferred;
+        Address *existing_address;
+        uint32_t lifetime_valid, lifetime_preferred, lifetime_remaining;
+        usec_t time_now;
         unsigned prefixlen;
         int r;
 
         assert(link);
         assert(rt);
 
+        r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
+        if (r < 0) {
+                log_link_warning_errno(link, r, "Failed to get RA timestamp: %m");
+                return;
+        }
+
         r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
         if (r < 0) {
                 log_link_error_errno(link, r, "Failed to get prefix length: %m");
@@ -207,7 +216,24 @@ static void ndisc_router_process_autonom
         address->prefixlen = prefixlen;
         address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
         address->cinfo.ifa_prefered = lifetime_preferred;
-        address->cinfo.ifa_valid = lifetime_valid;
+
+        /* see RFC4862 section 5.5.3.e */
+        r = address_get(link, address->family, &address->in_addr, address->prefixlen, &existing_address);
+        if (r > 0) {
+                lifetime_remaining = existing_address->cinfo.tstamp / 100 + existing_address->cinfo.ifa_valid - time_now / USEC_PER_SEC;
+                if (lifetime_valid > NDISC_PREFIX_LFT_MIN || lifetime_valid > lifetime_remaining)
+                        address->cinfo.ifa_valid = lifetime_valid;
+                else if (lifetime_remaining <= NDISC_PREFIX_LFT_MIN)
+                        address->cinfo.ifa_valid = lifetime_remaining;
+                else
+                        address->cinfo.ifa_valid = NDISC_PREFIX_LFT_MIN;
+        } else if (lifetime_valid > 0)
+                address->cinfo.ifa_valid = lifetime_valid;
+        else
+                return; /* see RFC4862 section 5.5.3.d */
+
+        if (address->cinfo.ifa_valid == 0)
+                return;
 
         r = address_configure(address, link, ndisc_netlink_handler, true);
         if (r < 0) {
diff -rupN systemd-base/src/systemd/sd-radv.h systemd/src/systemd/sd-radv.h
--- systemd-base/src/systemd/sd-radv.h	1969-12-31 16:00:00.000000000 -0800
+++ systemd/src/systemd/sd-radv.h	2017-09-18 14:58:44.807575602 -0700
@@ -0,0 +1,81 @@
+#ifndef foosdradvfoo
+#define foosdradvfoo
+
+/***
+  This file is part of systemd.
+
+  Copyright (C) 2017 Intel Corporation. All rights reserved.
+
+  systemd is free software; you can redistribute it and/or modify it
+  under the terms of the GNU Lesser General Public License as published by
+  the Free Software Foundation; either version 2.1 of the License, or
+  (at your option) any later version.
+
+  systemd is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  Lesser General Public License for more details.
+
+  You should have received a copy of the GNU Lesser General Public License
+  along with systemd; If not, see <http://www.gnu.org/licenses/>.
+***/
+
+#include <inttypes.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+
+#include "sd-ndisc.h"
+
+#include "sd-event.h"
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+typedef struct sd_radv sd_radv;
+typedef struct sd_radv_prefix sd_radv_prefix;
+
+/* Router Advertisment */
+int sd_radv_new(sd_radv **ret);
+sd_radv *sd_radv_ref(sd_radv *ra);
+sd_radv *sd_radv_unref(sd_radv *ra);
+
+int sd_radv_attach_event(sd_radv *ra, sd_event *event, int64_t priority);
+int sd_radv_detach_event(sd_radv *nd);
+sd_event *sd_radv_get_event(sd_radv *ra);
+
+int sd_radv_start(sd_radv *ra);
+int sd_radv_stop(sd_radv *ra);
+
+int sd_radv_set_ifindex(sd_radv *ra, int interface_index);
+int sd_radv_set_mac(sd_radv *ra, const struct ether_addr *mac_addr);
+int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu);
+int sd_radv_set_hop_limit(sd_radv *ra, uint8_t hop_limit);
+int sd_radv_set_router_lifetime(sd_radv *ra, uint32_t router_lifetime);
+int sd_radv_set_managed_information(sd_radv *ra, int managed);
+int sd_radv_set_other_information(sd_radv *ra, int other);
+int sd_radv_set_preference(sd_radv *ra, unsigned preference);
+int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p);
+
+/* Advertised prefixes */
+int sd_radv_prefix_new(sd_radv_prefix **ret);
+sd_radv_prefix *sd_radv_prefix_ref(sd_radv_prefix *ra);
+sd_radv_prefix *sd_radv_prefix_unref(sd_radv_prefix *ra);
+
+int sd_radv_prefix_set_prefix(sd_radv_prefix *p, struct in6_addr *in6_addr,
+                              unsigned char prefixlen);
+int sd_radv_prefix_set_onlink(sd_radv_prefix *p, int onlink);
+int sd_radv_prefix_set_address_autoconfiguration(sd_radv_prefix *p,
+                                                 int address_autoconfiguration);
+int sd_radv_prefix_set_valid_lifetime(sd_radv_prefix *p,
+                                      uint32_t valid_lifetime);
+int sd_radv_prefix_set_preferred_lifetime(sd_radv_prefix *p,
+                                          uint32_t preferred_lifetime);
+
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv, sd_radv_unref);
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_radv_prefix, sd_radv_prefix_unref);
+
+_SD_END_DECLARATIONS;
+
+#endif