diff options
Diffstat (limited to 'src/libsystemd-network/sd-ndisc.c')
-rw-r--r-- | src/libsystemd-network/sd-ndisc.c | 633 |
1 files changed, 171 insertions, 462 deletions
diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c index fb4ef55673..07b0d7f704 100644 --- a/src/libsystemd-network/sd-ndisc.c +++ b/src/libsystemd-network/sd-ndisc.c @@ -19,157 +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 4 * USEC_PER_SEC -#define NDISC_MAX_ROUTER_SOLICITATIONS 3 - -enum NDiscState { - NDISC_STATE_IDLE, - NDISC_STATE_SOLICITATION_SENT, - NDISC_STATE_ADVERTISMENT_LISTEN, - _NDISC_STATE_MAX, - _NDISC_STATE_INVALID = -1, -}; - -#define IP6_MIN_MTU (unsigned)1280 -#define ICMP6_RECV_SIZE (IP6_MIN_MTU - sizeof(struct ip6_hdr)) -#define NDISC_OPT_LEN_UNITS 8 - -#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 - -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; - sd_event *event; - int event_priority; - int index; - struct ether_addr mac_addr; - uint32_t mtu; - LIST_HEAD(NDiscPrefix, prefixes); - int fd; - sd_event_source *recv; - sd_event_source *timeout; - int 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(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "NDisc CLIENT: " fmt, ##__VA_ARGS__) - -static NDiscPrefix *ndisc_prefix_unref(NDiscPrefix *prefix) { - - if (!prefix) - return NULL; - - assert(prefix->n_ref > 0); - prefix->n_ref--; +#define NDISC_ROUTER_SOLICITATION_INTERVAL (4U * USEC_PER_SEC) +#define NDISC_MAX_ROUTER_SOLICITATIONS 3U - if (prefix->n_ref > 0) - return NULL; +static void ndisc_callback(sd_ndisc *ndisc, sd_ndisc_event event, sd_ndisc_router *rt) { + assert(ndisc); - if (prefix->nd) - LIST_REMOVE(prefixes, prefix->nd->prefixes, prefix); + log_ndisc("Invoking callback for '%c'.", event); - free(prefix); + if (!ndisc->callback) + return; - return NULL; + ndisc->callback(ndisc, event, rt, ndisc->userdata); } -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; -} +_public_ int sd_ndisc_set_callback( + sd_ndisc *nd, + sd_ndisc_callback_t callback, + void *userdata) { -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(nd); + 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_index(sd_ndisc *nd, int interface_index) { - assert(nd); - assert(interface_index >= -1); - - nd->index = interface_index; +_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) { - assert(nd); +_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) @@ -185,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) { - assert(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; @@ -210,18 +125,17 @@ sd_ndisc *sd_ndisc_ref(sd_ndisc *nd) { return nd; } -static int ndisc_init(sd_ndisc *nd) { +static int ndisc_reset(sd_ndisc *nd) { assert(nd); - nd->recv = sd_event_source_unref(nd->recv); - nd->fd = asynchronous_close(nd->fd); - nd->timeout = sd_event_source_unref(nd->timeout); + 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; @@ -232,251 +146,87 @@ sd_ndisc *sd_ndisc_unref(sd_ndisc *nd) { if (nd->n_ref > 0) return NULL; - ndisc_init(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(ret); + assert_return(ret, -EINVAL); nd = new0(sd_ndisc, 1); if (!nd) return -ENOMEM; nd->n_ref = 1; - - nd->index = -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_return(prefix, -EINVAL); - assert_return(addr, -EINVAL); - - 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); - - if (len < prefix_opt->nd_opt_pi_len) - return -ENOMSG; - - if (!(prefix_opt->nd_opt_pi_flags_reserved & (ND_OPT_PI_FLAG_ONLINK | ND_OPT_PI_FLAG_AUTO))) - return 0; + assert(rt); - if (in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &prefix_opt->nd_opt_pi_prefix) > 0) + r = ndisc_router_parse(rt); + if (r == -EBADMSG) /* Bad packet */ 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 advertisment 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, ssize_t len) { - void *opt; - struct nd_opt_hdr *opt_hdr; - - assert_return(nd, -EINVAL); - assert_return(ra, -EINVAL); - - len -= sizeof(*ra); - if (len < NDISC_OPT_LEN_UNITS) { - log_ndisc(nd, "Router Advertisement below minimum length"); - - return -ENOMSG; - } - - 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; - uint32_t mtu; - struct nd_opt_prefix_info *opt_prefix; - - if (opt_hdr->nd_opt_len == 0) - return -ENOMSG; - - 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 *)((char *)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_advertisment_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 = {}; @@ -489,10 +239,7 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r .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); @@ -500,32 +247,47 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r buflen = next_datagram_size_fd(fd); if (buflen < 0) - return buflen; + return log_ndisc_errno(buflen, "Failed to determine datagram size to read: %m"); - iov.iov_len = buflen; - - 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; - log_ndisc(nd, "Could not receive message from ICMPv6 socket: %m"); - return -errno; - } else if ((size_t)len < sizeof(struct nd_router_advert)) { - return 0; - } else 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; + 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)); @@ -534,180 +296,127 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r 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 (ra->nd_ra_type != ND_ROUTER_ADVERT) - return 0; - - if (ra->nd_ra_code != 0) - return 0; - - nd->timeout = sd_event_source_unref(nd->timeout); - - nd->state = NDISC_STATE_ADVERTISMENT_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; - switch (pref) { - case ND_RA_FLAG_PREF_LOW: - case ND_RA_FLAG_PREF_HIGH: - break; - default: - pref = ND_RA_FLAG_PREF_MEDIUM; - 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))); } - lifetime = be16toh(ra->nd_ra_router_lifetime); + if (!triple_timestamp_is_set(&rt->timestamp)) + triple_timestamp_get(&rt->timestamp); - 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); + nd->timeout_event_source = sd_event_source_unref(nd->timeout_event_source); - r = ndisc_ra_parse(nd, ra, len); - if (r < 0) { - log_ndisc(nd, "Could not parse Router Advertisement: %s", strerror(-r)); - 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; - uint64_t time_now, next_timeout; + usec_t time_now, next_timeout; int r; assert(s); assert(nd); assert(nd->event); - nd->timeout = sd_event_source_unref(nd->timeout); - if (nd->nd_sent >= NDISC_MAX_ROUTER_SOLICITATIONS) { - if (nd->callback) - nd->callback(nd, SD_NDISC_EVENT_TIMEOUT, nd->userdata); - nd->state = NDISC_STATE_ADVERTISMENT_LISTEN; - } else { - r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr); - if (r < 0) - log_ndisc(nd, "Error sending Router Solicitation"); - 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, clock_boottime_or_monotonic(), - next_timeout, 0, - ndisc_router_solicitation_timeout, nd); - if (r < 0) { - /* we cannot continue if we are unable to rearm the timer */ - sd_ndisc_stop(nd); - return 0; - } + 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, nd->event_priority); - if (r < 0) - return 0; + 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; + } - r = sd_event_source_set_description(nd->timeout, "ndisc-timeout"); - if (r < 0) - return 0; + 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; + +fail: + sd_ndisc_stop(nd); + return 0; } -int sd_ndisc_stop(sd_ndisc *nd) { +_public_ int sd_ndisc_stop(sd_ndisc *nd) { assert_return(nd, -EINVAL); - assert_return(nd->event, -EINVAL); - - log_ndisc(client, "Stop NDisc"); - ndisc_init(nd); - - nd->state = NDISC_STATE_IDLE; + if (nd->fd < 0) + return 0; - if (nd->callback) - nd->callback(nd, SD_NDISC_EVENT_STOP, nd->userdata); + log_ndisc("Stopping IPv6 Router Solicitation client"); - return 0; + ndisc_reset(nd); + return 1; } -int sd_ndisc_router_discovery_start(sd_ndisc *nd) { +_public_ int sd_ndisc_start(sd_ndisc *nd) { int r; - assert(nd); - assert(nd->event); - - if (nd->state != NDISC_STATE_IDLE) - return -EBUSY; + assert_return(nd, -EINVAL); + assert_return(nd->event, -EINVAL); + assert_return(nd->ifindex > 0, -EINVAL); - if (nd->index < 1) - return -EINVAL; + if (nd->fd >= 0) + return 0; - r = icmp6_bind_router_solicitation(nd->index); - if (r < 0) - return r; + assert(!nd->recv_event_source); + assert(!nd->timeout_event_source); - nd->fd = r; + nd->fd = icmp6_bind_router_solicitation(nd->ifindex); + if (nd->fd < 0) + return nd->fd; - r = sd_event_add_io(nd->event, &nd->recv, nd->fd, EPOLLIN, - ndisc_router_advertisment_recv, nd); + r = sd_event_add_io(nd->event, &nd->recv_event_source, nd->fd, EPOLLIN, ndisc_recv, nd); if (r < 0) - goto error; + goto fail; - r = sd_event_source_set_priority(nd->recv, nd->event_priority); + r = sd_event_source_set_priority(nd->recv_event_source, nd->event_priority); if (r < 0) - goto error; + goto fail; - r = sd_event_source_set_description(nd->recv, "ndisc-receive-message"); - if (r < 0) - goto error; + (void) sd_event_source_set_description(nd->recv_event_source, "ndisc-receive-message"); - r = sd_event_add_time(nd->event, &nd->timeout, 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 error; + goto fail; - r = sd_event_source_set_priority(nd->timeout, nd->event_priority); + r = sd_event_source_set_priority(nd->timeout_event_source, nd->event_priority); if (r < 0) - goto error; + goto fail; - r = sd_event_source_set_description(nd->timeout, "ndisc-timeout"); -error: - if (r < 0) - ndisc_init(nd); - else - log_ndisc(client, "Start Router Solicitation"); + (void) sd_event_source_set_description(nd->timeout_event_source, "ndisc-timeout"); + + log_ndisc("Started IPv6 Router Solicitation client"); + return 1; +fail: + ndisc_reset(nd); return r; } |