summaryrefslogtreecommitdiff
path: root/src/libsystemd-network/sd-ndisc.c
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2016-06-02 20:38:12 +0200
committerLennart Poettering <lennart@poettering.net>2016-06-06 20:11:38 +0200
commit1e7a0e21c97ac1bbc743009e5ec8c12bc6200e19 (patch)
treed05339bc2b110ac97f180c63aaf82877b0c80570 /src/libsystemd-network/sd-ndisc.c
parent1f152e4b41d730c7bbe23d8cbe45b7d342ba8c13 (diff)
network: beef up ipv6 RA support considerably
This reworks sd-ndisc and networkd substantially to support IPv6 RA much more comprehensively. Since the API is extended quite a bit networkd has been ported over too, and the patch is not as straight-forward as one could wish. The rework includes: - Support for DNSSL, RDNSS and RA routing options in sd-ndisc and networkd. Two new configuration options have been added to networkd to make this configurable. - sd-ndisc now exposes an sd_ndisc_router object that encapsulates a full RA message, and has direct, friendly acessor functions for the singleton RA properties, as well as an iterative interface to iterate through known and unsupported options. The router object may either be retrieved from the wire, or generated from raw data. In many ways the sd-ndisc API now matches the sd-lldp API, except that no implicit database of seen data is kept. (Note that sd-ndisc actually had a half-written, but unused implementaiton of such a store, which is removed now.) - sd-ndisc will now collect the reception timestamps of RA, which is useful to make sd_ndisc_router fully descriptive of what it covers. Fixes: #1079
Diffstat (limited to 'src/libsystemd-network/sd-ndisc.c')
-rw-r--r--src/libsystemd-network/sd-ndisc.c551
1 files changed, 133 insertions, 418 deletions
diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c
index ccb8002173..ea3fe369ce 100644
--- a/src/libsystemd-network/sd-ndisc.c
+++ b/src/libsystemd-network/sd-ndisc.c
@@ -19,165 +19,71 @@
#include <netinet/icmp6.h>
#include <netinet/in.h>
-#include <netinet/ip6.h>
-#include <stdbool.h>
-#include <string.h>
-#include <sys/ioctl.h>
#include "sd-ndisc.h"
#include "alloc-util.h"
-#include "async.h"
+#include "fd-util.h"
#include "icmp6-util.h"
#include "in-addr-util.h"
-#include "list.h"
+#include "ndisc-internal.h"
+#include "ndisc-router.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_MAX_ROUTER_SOLICITATIONS 3U
-enum NDiscState {
- NDISC_STATE_IDLE,
- NDISC_STATE_SOLICITATION_SENT,
- NDISC_STATE_ADVERTISEMENT_LISTEN,
- _NDISC_STATE_MAX,
- _NDISC_STATE_INVALID = -1,
-};
+static void ndisc_callback(sd_ndisc *ndisc, sd_ndisc_event event, sd_ndisc_router *rt) {
+ assert(ndisc);
-#define IP6_MIN_MTU 1280U
-#define ICMP6_RECV_SIZE (IP6_MIN_MTU - sizeof(struct ip6_hdr))
-#define NDISC_OPT_LEN_UNITS 8U
+ log_ndisc("Invoking callback for '%c'.", event);
-#define ND_RA_FLAG_PREF 0x18
-#define ND_RA_FLAG_PREF_LOW 0x03
-#define ND_RA_FLAG_PREF_MEDIUM 0x0
-#define ND_RA_FLAG_PREF_HIGH 0x1
-#define ND_RA_FLAG_PREF_INVALID 0x2
+ if (!ndisc->callback)
+ return;
-typedef struct NDiscPrefix NDiscPrefix;
-
-struct NDiscPrefix {
- unsigned n_ref;
-
- sd_ndisc *nd;
-
- LIST_FIELDS(NDiscPrefix, prefixes);
-
- uint8_t len;
- usec_t valid_until;
- struct in6_addr addr;
-};
-
-struct sd_ndisc {
- unsigned n_ref;
-
- enum NDiscState state;
- int ifindex;
- int fd;
-
- sd_event *event;
- int event_priority;
-
- struct ether_addr mac_addr;
- uint32_t mtu;
-
- LIST_HEAD(NDiscPrefix, prefixes);
-
- sd_event_source *recv_event_source;
- sd_event_source *timeout_event_source;
-
- unsigned nd_sent;
-
- sd_ndisc_router_callback_t router_callback;
- sd_ndisc_prefix_autonomous_callback_t prefix_autonomous_callback;
- sd_ndisc_prefix_onlink_callback_t prefix_onlink_callback;
- sd_ndisc_callback_t callback;
- void *userdata;
-};
-
-#define log_ndisc_errno(p, error, fmt, ...) log_internal(LOG_DEBUG, error, __FILE__, __LINE__, __func__, "NDisc CLIENT: " fmt, ##__VA_ARGS__)
-#define log_ndisc(p, fmt, ...) log_ndisc_errno(p, 0, fmt, ##__VA_ARGS__)
-
-static NDiscPrefix *ndisc_prefix_unref(NDiscPrefix *prefix) {
-
- if (!prefix)
- return NULL;
-
- assert(prefix->n_ref > 0);
- prefix->n_ref--;
-
- if (prefix->n_ref > 0)
- return NULL;
-
- if (prefix->nd)
- LIST_REMOVE(prefixes, prefix->nd->prefixes, prefix);
-
- free(prefix);
-
- return NULL;
-}
-
-static int ndisc_prefix_new(sd_ndisc *nd, NDiscPrefix **ret) {
- NDiscPrefix *prefix;
-
- assert(ret);
-
- prefix = new0(NDiscPrefix, 1);
- if (!prefix)
- return -ENOMEM;
-
- prefix->n_ref = 1;
- LIST_INIT(prefixes, prefix);
- prefix->nd = nd;
-
- *ret = prefix;
- return 0;
+ ndisc->callback(ndisc, event, rt, ndisc->userdata);
}
-int sd_ndisc_set_callback(
+_public_ int sd_ndisc_set_callback(
sd_ndisc *nd,
- sd_ndisc_router_callback_t router_callback,
- sd_ndisc_prefix_onlink_callback_t prefix_onlink_callback,
- sd_ndisc_prefix_autonomous_callback_t prefix_autonomous_callback,
sd_ndisc_callback_t callback,
void *userdata) {
assert_return(nd, -EINVAL);
- nd->router_callback = router_callback;
- nd->prefix_onlink_callback = prefix_onlink_callback;
- nd->prefix_autonomous_callback = prefix_autonomous_callback;
nd->callback = callback;
nd->userdata = userdata;
return 0;
}
-int sd_ndisc_set_ifindex(sd_ndisc *nd, int ifindex) {
+_public_ int sd_ndisc_set_ifindex(sd_ndisc *nd, int ifindex) {
assert_return(nd, -EINVAL);
assert_return(ifindex > 0, -EINVAL);
+ assert_return(nd->fd < 0, -EBUSY);
nd->ifindex = ifindex;
return 0;
}
-int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr) {
+_public_ int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr) {
assert_return(nd, -EINVAL);
if (mac_addr)
- memcpy(&nd->mac_addr, mac_addr, sizeof(nd->mac_addr));
+ nd->mac_addr = *mac_addr;
else
zero(nd->mac_addr);
return 0;
-
}
-int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority) {
+_public_ int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority) {
int r;
assert_return(nd, -EINVAL);
+ assert_return(nd->fd < 0, -EBUSY);
assert_return(!nd->event, -EBUSY);
if (event)
@@ -193,21 +99,22 @@ int sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int64_t priority) {
return 0;
}
-int sd_ndisc_detach_event(sd_ndisc *nd) {
+_public_ int sd_ndisc_detach_event(sd_ndisc *nd) {
+
assert_return(nd, -EINVAL);
+ assert_return(nd->fd < 0, -EBUSY);
nd->event = sd_event_unref(nd->event);
-
return 0;
}
-sd_event *sd_ndisc_get_event(sd_ndisc *nd) {
+_public_ sd_event *sd_ndisc_get_event(sd_ndisc *nd) {
assert_return(nd, NULL);
return nd->event;
}
-sd_ndisc *sd_ndisc_ref(sd_ndisc *nd) {
+_public_ sd_ndisc *sd_ndisc_ref(sd_ndisc *nd) {
if (!nd)
return NULL;
@@ -221,15 +128,14 @@ sd_ndisc *sd_ndisc_ref(sd_ndisc *nd) {
static int ndisc_reset(sd_ndisc *nd) {
assert(nd);
- nd->recv_event_source = sd_event_source_unref(nd->recv_event_source);
- nd->fd = asynchronous_close(nd->fd);
nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source);
+ nd->recv_event_source = sd_event_source_unref(nd->recv_event_source);
+ nd->fd = safe_close(nd->fd);
return 0;
}
-sd_ndisc *sd_ndisc_unref(sd_ndisc *nd) {
- NDiscPrefix *prefix, *p;
+_public_ sd_ndisc *sd_ndisc_unref(sd_ndisc *nd) {
if (!nd)
return NULL;
@@ -242,16 +148,12 @@ sd_ndisc *sd_ndisc_unref(sd_ndisc *nd) {
ndisc_reset(nd);
sd_ndisc_detach_event(nd);
-
- LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes)
- prefix = ndisc_prefix_unref(prefix);
-
free(nd);
return NULL;
}
-int sd_ndisc_new(sd_ndisc **ret) {
+_public_ int sd_ndisc_new(sd_ndisc **ret) {
_cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL;
assert_return(ret, -EINVAL);
@@ -261,223 +163,70 @@ int sd_ndisc_new(sd_ndisc **ret) {
return -ENOMEM;
nd->n_ref = 1;
- nd->ifindex = -1;
nd->fd = -1;
- LIST_HEAD_INIT(nd->prefixes);
-
*ret = nd;
nd = NULL;
return 0;
}
-int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *mtu) {
+_public_ int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *mtu) {
assert_return(nd, -EINVAL);
assert_return(mtu, -EINVAL);
if (nd->mtu == 0)
- return -ENOMSG;
+ return -ENODATA;
*mtu = nd->mtu;
return 0;
}
-static int prefix_match(const struct in6_addr *prefix, uint8_t prefixlen,
- const struct in6_addr *addr,
- uint8_t addr_prefixlen) {
- uint8_t bytes, mask, len;
-
- assert(prefix);
- assert(addr);
-
- len = MIN(prefixlen, addr_prefixlen);
-
- bytes = len / 8;
- mask = 0xff << (8 - len % 8);
+_public_ int sd_ndisc_get_hop_limit(sd_ndisc *nd, uint8_t *ret) {
+ assert_return(nd, -EINVAL);
+ assert_return(ret, -EINVAL);
- if (memcmp(prefix, addr, bytes) != 0 ||
- (prefix->s6_addr[bytes] & mask) != (addr->s6_addr[bytes] & mask))
- return -EADDRNOTAVAIL;
+ if (nd->hop_limit == 0)
+ return -ENODATA;
+ *ret = nd->hop_limit;
return 0;
}
-static int ndisc_prefix_match(sd_ndisc *nd, const struct in6_addr *addr,
- uint8_t addr_len, NDiscPrefix **result) {
- NDiscPrefix *prefix, *p;
- usec_t time_now;
- int r;
-
- assert(nd);
-
- r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
- if (r < 0)
- return r;
-
- LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) {
- if (prefix->valid_until < time_now) {
- prefix = ndisc_prefix_unref(prefix);
- continue;
- }
-
- if (prefix_match(&prefix->addr, prefix->len, addr, addr_len) >= 0) {
- *result = prefix;
- return 0;
- }
- }
-
- return -EADDRNOTAVAIL;
-}
-
-static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len,
- const struct nd_opt_prefix_info *prefix_opt) {
- NDiscPrefix *prefix;
- uint32_t lifetime_valid, lifetime_preferred;
- usec_t time_now;
- char time_string[FORMAT_TIMESPAN_MAX];
+static int ndisc_handle_datagram(sd_ndisc *nd, sd_ndisc_router *rt) {
int r;
assert(nd);
- assert(prefix_opt);
+ assert(rt);
- if (len < prefix_opt->nd_opt_pi_len)
- return -EBADMSG;
-
- if (!(prefix_opt->nd_opt_pi_flags_reserved & (ND_OPT_PI_FLAG_ONLINK | ND_OPT_PI_FLAG_AUTO)))
+ r = ndisc_router_parse(rt);
+ if (r == -EBADMSG) /* Bad packet */
return 0;
-
- if (in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &prefix_opt->nd_opt_pi_prefix) > 0)
- return 0;
-
- lifetime_valid = be32toh(prefix_opt->nd_opt_pi_valid_time);
- lifetime_preferred = be32toh(prefix_opt->nd_opt_pi_preferred_time);
-
- if (lifetime_valid < lifetime_preferred)
- return 0;
-
- r = ndisc_prefix_match(nd, &prefix_opt->nd_opt_pi_prefix,
- prefix_opt->nd_opt_pi_prefix_len, &prefix);
- if (r < 0) {
- if (r != -EADDRNOTAVAIL)
- return r;
-
- /* if router advertisement prefix valid timeout is zero, the timeout
- callback will be called immediately to clean up the prefix */
-
- r = ndisc_prefix_new(nd, &prefix);
- if (r < 0)
- return r;
-
- prefix->len = prefix_opt->nd_opt_pi_prefix_len;
-
- memcpy(&prefix->addr, &prefix_opt->nd_opt_pi_prefix,
- sizeof(prefix->addr));
-
- log_ndisc(nd, "New prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
- SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr),
- prefix->len, lifetime_valid,
- format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC));
-
- LIST_PREPEND(prefixes, nd->prefixes, prefix);
-
- } else {
- if (prefix->len != prefix_opt->nd_opt_pi_prefix_len) {
- uint8_t prefixlen;
-
- prefixlen = MIN(prefix->len, prefix_opt->nd_opt_pi_prefix_len);
-
- log_ndisc(nd, "Prefix length mismatch %d/%d using %d",
- prefix->len,
- prefix_opt->nd_opt_pi_prefix_len,
- prefixlen);
-
- prefix->len = prefixlen;
- }
-
- log_ndisc(nd, "Update prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
- SD_NDISC_ADDRESS_FORMAT_VAL(prefix->addr),
- prefix->len, lifetime_valid,
- format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC));
- }
-
- r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
if (r < 0)
- return r;
-
- prefix->valid_until = time_now + lifetime_valid * USEC_PER_SEC;
-
- if ((prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) && nd->prefix_onlink_callback)
- nd->prefix_onlink_callback(nd, &prefix->addr, prefix->len, prefix->valid_until, nd->userdata);
-
- if ((prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) && nd->prefix_autonomous_callback)
- nd->prefix_autonomous_callback(nd, &prefix->addr, prefix->len, lifetime_preferred, lifetime_valid,
- nd->userdata);
-
- return 0;
-}
-
-static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra, size_t len) {
- struct nd_opt_hdr *opt_hdr;
- void *opt;
-
- assert(nd);
- assert(ra);
-
- if (len < sizeof(struct nd_router_advert) + NDISC_OPT_LEN_UNITS) {
- log_ndisc(nd, "Router Advertisement below minimum length");
- return -EBADMSG;
- }
-
- len -= sizeof(struct nd_router_advert);
- opt = ra + 1;
- opt_hdr = opt;
-
- while (len != 0 && len >= opt_hdr->nd_opt_len * NDISC_OPT_LEN_UNITS) {
- struct nd_opt_mtu *opt_mtu;
- struct nd_opt_prefix_info *opt_prefix;
- uint32_t mtu;
-
- if (opt_hdr->nd_opt_len == 0)
- return -EBADMSG;
-
- switch (opt_hdr->nd_opt_type) {
-
- case ND_OPT_MTU:
- opt_mtu = opt;
-
- mtu = be32toh(opt_mtu->nd_opt_mtu_mtu);
-
- if (mtu != nd->mtu) {
- nd->mtu = MAX(mtu, IP6_MIN_MTU);
- log_ndisc(nd, "Router Advertisement link MTU %d using %d", mtu, nd->mtu);
- }
-
- break;
-
- case ND_OPT_PREFIX_INFORMATION:
- opt_prefix = opt;
- ndisc_prefix_update(nd, len, opt_prefix);
- break;
- }
+ return 0;
- len -= opt_hdr->nd_opt_len * NDISC_OPT_LEN_UNITS;
- opt = (void*) ((uint8_t*) opt + opt_hdr->nd_opt_len * NDISC_OPT_LEN_UNITS);
- opt_hdr = opt;
- }
+ /* Update global variables we keep */
+ if (rt->mtu > 0)
+ nd->mtu = rt->mtu;
+ if (rt->hop_limit > 0)
+ nd->hop_limit = rt->hop_limit;
- if (len > 0)
- log_ndisc(nd, "Router Advertisement contains %zd bytes of trailing garbage", len);
+ log_ndisc("Received Router Advertisement: flags %s preference %s lifetime %" PRIu16 " sec",
+ rt->flags & ND_RA_FLAG_MANAGED ? "MANAGED" : rt->flags & ND_RA_FLAG_OTHER ? "OTHER" : "none",
+ rt->preference == SD_NDISC_PREFERENCE_HIGH ? "high" : rt->preference == SD_NDISC_PREFERENCE_LOW ? "low" : "medium",
+ rt->lifetime);
+ ndisc_callback(nd, SD_NDISC_EVENT_ROUTER, rt);
return 0;
}
-static int ndisc_router_advertisement_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
- _cleanup_free_ struct nd_router_advert *ra = NULL;
+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_LEN(sizeof(int))];
+ uint8_t buf[CMSG_SPACE(sizeof(int)) + /* ttl */
+ CMSG_SPACE(sizeof(struct timeval))];
} control = {};
struct iovec iov = {};
union sockaddr_union sa = {};
@@ -490,10 +239,7 @@ static int ndisc_router_advertisement_recv(sd_event_source *s, int fd, uint32_t
.msg_controllen = sizeof(control),
};
struct cmsghdr *cmsg;
- struct in6_addr *gw;
- unsigned lifetime;
ssize_t len, buflen;
- int r, pref, stateful;
assert(s);
assert(nd);
@@ -501,35 +247,47 @@ static int ndisc_router_advertisement_recv(sd_event_source *s, int fd, uint32_t
buflen = next_datagram_size_fd(fd);
if (buflen < 0)
- return buflen;
-
- iov.iov_len = buflen;
+ return log_ndisc_errno(buflen, "Failed to determine datagram size to read: %m");
- ra = malloc(iov.iov_len);
- if (!ra)
+ rt = ndisc_router_new(buflen);
+ if (!rt)
return -ENOMEM;
- iov.iov_base = ra;
+ iov.iov_base = NDISC_ROUTER_RAW(rt);
+ iov.iov_len = rt->raw_size;
- len = recvmsg(fd, &msg, 0);
+ len = recvmsg(fd, &msg, MSG_DONTWAIT);
if (len < 0) {
if (errno == EAGAIN || errno == EINTR)
return 0;
- return log_ndisc_errno(nd, errno, "Could not receive message from ICMPv6 socket: %m");
+ return log_ndisc_errno(errno, "Could not receive message from ICMPv6 socket: %m");
}
- if ((size_t) len < sizeof(struct nd_router_advert)) {
- log_ndisc(nd, "Too small to be a router advertisement: ignoring");
- return 0;
+
+ if ((size_t) len != rt->raw_size) {
+ log_ndisc("Packet size mismatch.");
+ return -EINVAL;
}
- if (msg.msg_namelen == 0)
- gw = NULL; /* only happens when running the test-suite over a socketpair */
- else if (msg.msg_namelen != sizeof(sa.in6)) {
- log_ndisc(nd, "Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t)msg.msg_namelen);
- return 0;
- } else
- gw = &sa.in6.sin6_addr;
+ 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));
@@ -538,61 +296,29 @@ static int ndisc_router_advertisement_recv(sd_event_source *s, int fd, uint32_t
if (cmsg->cmsg_level == SOL_IPV6 &&
cmsg->cmsg_type == IPV6_HOPLIMIT &&
cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
- int hops = *(int*)CMSG_DATA(cmsg);
+ int hops = *(int*) CMSG_DATA(cmsg);
if (hops != 255) {
- log_ndisc(nd, "Received RA with invalid hop limit %d. Ignoring.", hops);
+ log_ndisc("Received RA with invalid hop limit %d. Ignoring.", hops);
return 0;
}
-
- break;
}
- }
- if (gw && !in_addr_is_link_local(AF_INET6, (const union in_addr_union*) gw)) {
- _cleanup_free_ char *addr = NULL;
-
- (void)in_addr_to_string(AF_INET6, (const union in_addr_union*) gw, &addr);
-
- log_ndisc(nd, "Received RA from non-link-local address %s. Ignoring.", strna(addr));
- return 0;
+ 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(CMSG_DATA(cmsg)));
}
- if (ra->nd_ra_type != ND_ROUTER_ADVERT)
- return 0;
-
- if (ra->nd_ra_code != 0)
- 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);
- nd->state = NDISC_STATE_ADVERTISEMENT_LISTEN;
-
- stateful = ra->nd_ra_flags_reserved & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER);
- pref = (ra->nd_ra_flags_reserved & ND_RA_FLAG_PREF) >> 3;
-
- if (!IN_SET(pref, ND_RA_FLAG_PREF_LOW, ND_RA_FLAG_PREF_HIGH))
- pref = ND_RA_FLAG_PREF_MEDIUM;
-
- lifetime = be16toh(ra->nd_ra_router_lifetime);
-
- log_ndisc(nd, "Received Router Advertisement: flags %s preference %s lifetime %u sec",
- stateful & ND_RA_FLAG_MANAGED ? "MANAGED" : stateful & ND_RA_FLAG_OTHER ? "OTHER" : "none",
- pref == ND_RA_FLAG_PREF_HIGH ? "high" : pref == ND_RA_FLAG_PREF_LOW ? "low" : "medium",
- lifetime);
- r = ndisc_ra_parse(nd, ra, (size_t) len);
- if (r < 0) {
- log_ndisc_errno(nd, r, "Could not parse Router Advertisement: %m");
- return 0;
- }
-
- if (nd->router_callback)
- nd->router_callback(nd, stateful, gw, lifetime, pref, nd->userdata);
-
- return 0;
+ return ndisc_handle_datagram(nd, rt);
}
-static int ndisc_router_solicitation_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
+static int ndisc_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
sd_ndisc *nd = userdata;
usec_t time_now, next_timeout;
int r;
@@ -601,43 +327,34 @@ static int ndisc_router_solicitation_timeout(sd_event_source *s, uint64_t usec,
assert(nd);
assert(nd->event);
- nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source);
-
if (nd->nd_sent >= NDISC_MAX_ROUTER_SOLICITATIONS) {
- if (nd->callback)
- nd->callback(nd, SD_NDISC_EVENT_TIMEOUT, nd->userdata);
- nd->state = NDISC_STATE_ADVERTISEMENT_LISTEN;
- } else {
- r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr);
- if (r < 0) {
- log_ndisc_errno(nd, r, "Error sending Router Solicitation: %m");
- goto fail;
- } else {
- nd->state = NDISC_STATE_SOLICITATION_SENT;
- log_ndisc(nd, "Sent Router Solicitation");
- }
-
- nd->nd_sent++;
+ 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);
+ r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr);
+ if (r < 0) {
+ log_ndisc_errno(r, "Error sending Router Solicitation: %m");
+ goto fail;
+ }
- next_timeout = time_now + NDISC_ROUTER_SOLICITATION_INTERVAL;
+ log_ndisc("Sent Router Solicitation");
+ nd->nd_sent++;
- r = sd_event_add_time(nd->event, &nd->timeout_event_source, clock_boottime_or_monotonic(),
- next_timeout, 0,
- ndisc_router_solicitation_timeout, nd);
- if (r < 0) {
- log_ndisc_errno(nd, r, "Failed to allocate timer event: %m");
- goto fail;
- }
+ assert_se(sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
+ next_timeout = time_now + NDISC_ROUTER_SOLICITATION_INTERVAL;
- r = sd_event_source_set_priority(nd->timeout_event_source, nd->event_priority);
- if (r < 0) {
- log_ndisc_errno(nd, r, "Cannot set timer priority: %m");
- goto fail;
- }
+ r = sd_event_source_set_time(nd->timeout_event_source, next_timeout);
+ if (r < 0) {
+ log_ndisc_errno(r, "Error updating timer: %m");
+ goto fail;
+ }
- (void) sd_event_source_set_description(nd->timeout_event_source, "ndisc-timeout");
+ r = sd_event_source_set_enabled(nd->timeout_event_source, SD_EVENT_ONESHOT);
+ if (r < 0) {
+ log_ndisc_errno(r, "Error reenabling timer: %m");
+ goto fail;
}
return 0;
@@ -647,38 +364,36 @@ fail:
return 0;
}
-int sd_ndisc_stop(sd_ndisc *nd) {
+_public_ int sd_ndisc_stop(sd_ndisc *nd) {
assert_return(nd, -EINVAL);
- if (nd->state == NDISC_STATE_IDLE)
+ if (nd->fd < 0)
return 0;
- log_ndisc(nd, "Stopping IPv6 Router Solicitation client");
+ log_ndisc("Stopping IPv6 Router Solicitation client");
ndisc_reset(nd);
- nd->state = NDISC_STATE_IDLE;
-
- if (nd->callback)
- nd->callback(nd, SD_NDISC_EVENT_STOP, nd->userdata);
-
- return 0;
+ return 1;
}
-int sd_ndisc_router_discovery_start(sd_ndisc *nd) {
+_public_ int sd_ndisc_start(sd_ndisc *nd) {
int r;
assert_return(nd, -EINVAL);
assert_return(nd->event, -EINVAL);
assert_return(nd->ifindex > 0, -EINVAL);
- assert_return(nd->state == NDISC_STATE_IDLE, -EBUSY);
- r = icmp6_bind_router_solicitation(nd->ifindex);
- if (r < 0)
- return r;
+ if (nd->fd >= 0)
+ return 0;
- nd->fd = r;
+ assert(!nd->recv_event_source);
+ assert(!nd->timeout_event_source);
- r = sd_event_add_io(nd->event, &nd->recv_event_source, nd->fd, EPOLLIN, ndisc_router_advertisement_recv, nd);
+ nd->fd = icmp6_bind_router_solicitation(nd->ifindex);
+ if (nd->fd < 0)
+ return nd->fd;
+
+ r = sd_event_add_io(nd->event, &nd->recv_event_source, nd->fd, EPOLLIN, ndisc_recv, nd);
if (r < 0)
goto fail;
@@ -688,7 +403,7 @@ int sd_ndisc_router_discovery_start(sd_ndisc *nd) {
(void) sd_event_source_set_description(nd->recv_event_source, "ndisc-receive-message");
- r = sd_event_add_time(nd->event, &nd->timeout_event_source, clock_boottime_or_monotonic(), 0, 0, ndisc_router_solicitation_timeout, nd);
+ r = sd_event_add_time(nd->event, &nd->timeout_event_source, clock_boottime_or_monotonic(), 0, 0, ndisc_timeout, nd);
if (r < 0)
goto fail;
@@ -698,8 +413,8 @@ int sd_ndisc_router_discovery_start(sd_ndisc *nd) {
(void) sd_event_source_set_description(nd->timeout_event_source, "ndisc-timeout");
- log_ndisc(ns, "Started IPv6 Router Solicitation client");
- return 0;
+ log_ndisc("Started IPv6 Router Solicitation client");
+ return 1;
fail:
ndisc_reset(nd);