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, ×tamp); + 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