From 3ad0c5d8a4e2e2fa7ffcccd7f3457f577908494e Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Thu, 15 Oct 2015 19:25:31 +0200 Subject: sd-icmp6-nd: rename files to sd-ndisc The actual code rename will follow. The reason for the change of name is to make it simpler and more uniform with how we name other libraries (we don't include the underlying protocol). The new name also matches the naming in the kernel (which is particularly relevent here as we expect to let the kernel do some parts of the protocol and we do others). --- src/libsystemd-network/network-internal.c | 2 +- src/libsystemd-network/sd-icmp6-nd.c | 720 ------------------------------ src/libsystemd-network/sd-ndisc.c | 720 ++++++++++++++++++++++++++++++ src/libsystemd-network/test-icmp6-rs.c | 357 --------------- src/libsystemd-network/test-ndisc-rs.c | 357 +++++++++++++++ src/network/networkd-dhcp6.c | 2 +- src/network/networkd-link.h | 2 +- src/systemd/sd-icmp6-nd.h | 79 ---- src/systemd/sd-ndisc.h | 79 ++++ 9 files changed, 1159 insertions(+), 1159 deletions(-) delete mode 100644 src/libsystemd-network/sd-icmp6-nd.c create mode 100644 src/libsystemd-network/sd-ndisc.c delete mode 100644 src/libsystemd-network/test-icmp6-rs.c create mode 100644 src/libsystemd-network/test-ndisc-rs.c delete mode 100644 src/systemd/sd-icmp6-nd.h create mode 100644 src/systemd/sd-ndisc.h (limited to 'src') diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index 2a62af2fd4..a33affb773 100644 --- a/src/libsystemd-network/network-internal.c +++ b/src/libsystemd-network/network-internal.c @@ -32,7 +32,7 @@ #include "conf-parser.h" #include "condition.h" #include "network-internal.h" -#include "sd-icmp6-nd.h" +#include "sd-ndisc.h" const char *net_get_name(struct udev_device *device) { const char *name, *field; diff --git a/src/libsystemd-network/sd-icmp6-nd.c b/src/libsystemd-network/sd-icmp6-nd.c deleted file mode 100644 index f014cac628..0000000000 --- a/src/libsystemd-network/sd-icmp6-nd.c +++ /dev/null @@ -1,720 +0,0 @@ -/*** - This file is part of systemd. - - Copyright (C) 2014 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 . -***/ - -#include -#include -#include -#include -#include -#include - -#include "socket-util.h" -#include "async.h" - -#include "dhcp6-internal.h" -#include "sd-icmp6-nd.h" - -#define ICMP6_ROUTER_SOLICITATION_INTERVAL 4 * USEC_PER_SEC -#define ICMP6_MAX_ROUTER_SOLICITATIONS 3 - -enum icmp6_nd_state { - ICMP6_NEIGHBOR_DISCOVERY_IDLE = 0, - ICMP6_ROUTER_SOLICITATION_SENT = 10, - ICMP6_ROUTER_ADVERTISMENT_LISTEN = 11, -}; - -#define IP6_MIN_MTU (unsigned)1280 -#define ICMP6_ND_RECV_SIZE (IP6_MIN_MTU - sizeof(struct ip6_hdr)) -#define ICMP6_OPT_LEN_UNITS 8 - -typedef struct ICMP6Prefix ICMP6Prefix; - -struct ICMP6Prefix { - unsigned n_ref; - - LIST_FIELDS(ICMP6Prefix, prefixes); - - uint8_t len; - sd_event_source *timeout_valid; - struct in6_addr addr; -}; - -struct sd_icmp6_nd { - unsigned n_ref; - - enum icmp6_nd_state state; - sd_event *event; - int event_priority; - int index; - struct ether_addr mac_addr; - uint32_t mtu; - ICMP6Prefix *expired_prefix; - LIST_HEAD(ICMP6Prefix, prefixes); - int fd; - sd_event_source *recv; - sd_event_source *timeout; - int nd_sent; - sd_icmp6_nd_callback_t callback; - void *userdata; -}; - -#define log_icmp6_nd(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "ICMPv6 CLIENT: " fmt, ##__VA_ARGS__) - -static ICMP6Prefix *icmp6_prefix_unref(ICMP6Prefix *prefix) { - - if (!prefix) - return NULL; - - assert(prefix->n_ref > 0); - prefix->n_ref--; - - if (prefix->n_ref > 0) - return NULL; - - prefix->timeout_valid = sd_event_source_unref(prefix->timeout_valid); - free(prefix); - return NULL; -} - -static int icmp6_prefix_new(ICMP6Prefix **ret) { - _cleanup_free_ ICMP6Prefix *prefix = NULL; - - assert(ret); - - prefix = new0(ICMP6Prefix, 1); - if (!prefix) - return -ENOMEM; - - prefix->n_ref = 1; - LIST_INIT(prefixes, prefix); - - *ret = prefix; - prefix = NULL; - - return 0; -} - -static void icmp6_nd_notify(sd_icmp6_nd *nd, int event) { - if (nd->callback) - nd->callback(nd, event, nd->userdata); -} - -int sd_icmp6_nd_set_callback(sd_icmp6_nd *nd, sd_icmp6_nd_callback_t callback, - void *userdata) { - assert(nd); - - nd->callback = callback; - nd->userdata = userdata; - - return 0; -} - -int sd_icmp6_nd_set_index(sd_icmp6_nd *nd, int interface_index) { - assert(nd); - assert(interface_index >= -1); - - nd->index = interface_index; - - return 0; -} - -int sd_icmp6_nd_set_mac(sd_icmp6_nd *nd, const struct ether_addr *mac_addr) { - assert(nd); - - if (mac_addr) - memcpy(&nd->mac_addr, mac_addr, sizeof(nd->mac_addr)); - else - zero(nd->mac_addr); - - return 0; - -} - -int sd_icmp6_nd_attach_event(sd_icmp6_nd *nd, sd_event *event, int priority) { - int r; - - assert_return(nd, -EINVAL); - assert_return(!nd->event, -EBUSY); - - if (event) - nd->event = sd_event_ref(event); - else { - r = sd_event_default(&nd->event); - if (r < 0) - return 0; - } - - nd->event_priority = priority; - - return 0; -} - -int sd_icmp6_nd_detach_event(sd_icmp6_nd *nd) { - assert_return(nd, -EINVAL); - - nd->event = sd_event_unref(nd->event); - - return 0; -} - -sd_event *sd_icmp6_nd_get_event(sd_icmp6_nd *nd) { - assert(nd); - - return nd->event; -} - -sd_icmp6_nd *sd_icmp6_nd_ref(sd_icmp6_nd *nd) { - - if (!nd) - return NULL; - - assert(nd->n_ref > 0); - nd->n_ref++; - - return nd; -} - -static int icmp6_nd_init(sd_icmp6_nd *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); - - return 0; -} - -sd_icmp6_nd *sd_icmp6_nd_unref(sd_icmp6_nd *nd) { - ICMP6Prefix *prefix, *p; - - if (!nd) - return NULL; - - assert(nd->n_ref > 0); - nd->n_ref--; - - if (nd->n_ref > 0) - return NULL; - - icmp6_nd_init(nd); - sd_icmp6_nd_detach_event(nd); - - LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) { - LIST_REMOVE(prefixes, nd->prefixes, prefix); - - prefix = icmp6_prefix_unref(prefix); - } - - free(nd); - - return NULL; -} - -DEFINE_TRIVIAL_CLEANUP_FUNC(sd_icmp6_nd*, sd_icmp6_nd_unref); -#define _cleanup_sd_icmp6_nd_free_ _cleanup_(sd_icmp6_nd_unrefp) - -int sd_icmp6_nd_new(sd_icmp6_nd **ret) { - _cleanup_sd_icmp6_nd_free_ sd_icmp6_nd *nd = NULL; - - assert(ret); - - nd = new0(sd_icmp6_nd, 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_icmp6_ra_get_mtu(sd_icmp6_nd *nd, uint32_t *mtu) { - assert_return(nd, -EINVAL); - assert_return(mtu, -EINVAL); - - if (nd->mtu == 0) - return -ENOMSG; - - *mtu = nd->mtu; - - return 0; -} - -static int icmp6_ra_prefix_timeout(sd_event_source *s, uint64_t usec, - void *userdata) { - sd_icmp6_nd *nd = userdata; - ICMP6Prefix *prefix, *p; - - assert(nd); - - LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) { - if (prefix->timeout_valid != s) - continue; - - log_icmp6_nd(nd, "Prefix expired "SD_ICMP6_ND_ADDRESS_FORMAT_STR"/%d", - SD_ICMP6_ND_ADDRESS_FORMAT_VAL(prefix->addr), - prefix->len); - - LIST_REMOVE(prefixes, nd->prefixes, prefix); - - nd->expired_prefix = prefix; - icmp6_nd_notify(nd, - SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED); - nd->expired_prefix = NULL; - - prefix = icmp6_prefix_unref(prefix); - - break; - } - - return 0; -} - -static int icmp6_ra_prefix_set_timeout(sd_icmp6_nd *nd, - ICMP6Prefix *prefix, - usec_t valid) { - usec_t time_now; - int r; - - assert_return(prefix, -EINVAL); - - r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) - return r; - - prefix->timeout_valid = sd_event_source_unref(prefix->timeout_valid); - - r = sd_event_add_time(nd->event, &prefix->timeout_valid, - clock_boottime_or_monotonic(), time_now + valid, - USEC_PER_SEC, icmp6_ra_prefix_timeout, nd); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(prefix->timeout_valid, - nd->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(prefix->timeout_valid, - "icmp6-prefix-timeout"); - -error: - if (r < 0) - prefix->timeout_valid = - sd_event_source_unref(prefix->timeout_valid); - - return r; -} - -static int icmp6_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); - - if (memcmp(prefix, addr, bytes) != 0 || - (prefix->s6_addr[bytes] & mask) != (addr->s6_addr[bytes] & mask)) - return -EADDRNOTAVAIL; - - return 0; -} - -static int icmp6_ra_prefix_match(ICMP6Prefix *head, const struct in6_addr *addr, - uint8_t addr_len, ICMP6Prefix **result) { - ICMP6Prefix *prefix; - - LIST_FOREACH(prefixes, prefix, head) { - if (icmp6_prefix_match(&prefix->addr, prefix->len, addr, - addr_len) >= 0) { - *result = prefix; - return 0; - } - } - - return -EADDRNOTAVAIL; -} - -int sd_icmp6_prefix_match(struct in6_addr *prefix, uint8_t prefixlen, - struct in6_addr *addr) { - return icmp6_prefix_match(prefix, prefixlen, addr, - sizeof(addr->s6_addr) * 8); -} - -int sd_icmp6_ra_get_prefixlen(sd_icmp6_nd *nd, const struct in6_addr *addr, - uint8_t *prefixlen) { - int r; - ICMP6Prefix *prefix; - - assert_return(nd, -EINVAL); - assert_return(addr, -EINVAL); - assert_return(prefixlen, -EINVAL); - - r = icmp6_ra_prefix_match(nd->prefixes, addr, - sizeof(addr->s6_addr) * 8, &prefix); - if (r < 0) - return r; - - *prefixlen = prefix->len; - - return 0; -} - -int sd_icmp6_ra_get_expired_prefix(sd_icmp6_nd *nd, struct in6_addr **addr, uint8_t *prefixlen) { - assert_return(nd, -EINVAL); - assert_return(addr, -EINVAL); - assert_return(prefixlen, -EINVAL); - - if (!nd->expired_prefix) - return -EADDRNOTAVAIL; - - *addr = &nd->expired_prefix->addr; - *prefixlen = nd->expired_prefix->len; - - return 0; -} - -static int icmp6_ra_prefix_update(sd_icmp6_nd *nd, ssize_t len, - const struct nd_opt_prefix_info *prefix_opt) { - int r; - ICMP6Prefix *prefix; - uint32_t lifetime; - char time_string[FORMAT_TIMESPAN_MAX]; - - assert_return(nd, -EINVAL); - assert_return(prefix_opt, -EINVAL); - - if (len < prefix_opt->nd_opt_pi_len) - return -ENOMSG; - - if (!(prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK)) - return 0; - - lifetime = be32toh(prefix_opt->nd_opt_pi_valid_time); - - r = icmp6_ra_prefix_match(nd->prefixes, - &prefix_opt->nd_opt_pi_prefix, - prefix_opt->nd_opt_pi_prefix_len, &prefix); - - if (r < 0 && r != -EADDRNOTAVAIL) - return r; - - /* if router advertisment prefix valid timeout is zero, the timeout - callback will be called immediately to clean up the prefix */ - - if (r == -EADDRNOTAVAIL) { - r = icmp6_prefix_new(&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_icmp6_nd(nd, "New prefix "SD_ICMP6_ND_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s", - SD_ICMP6_ND_ADDRESS_FORMAT_VAL(prefix->addr), - prefix->len, lifetime, - format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime * 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_icmp6_nd(nd, "Prefix length mismatch %d/%d using %d", - prefix->len, - prefix_opt->nd_opt_pi_prefix_len, - prefixlen); - - prefix->len = prefixlen; - } - - log_icmp6_nd(nd, "Update prefix "SD_ICMP6_ND_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s", - SD_ICMP6_ND_ADDRESS_FORMAT_VAL(prefix->addr), - prefix->len, lifetime, - format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime * USEC_PER_SEC, USEC_PER_SEC)); - } - - r = icmp6_ra_prefix_set_timeout(nd, prefix, lifetime * USEC_PER_SEC); - - return r; -} - -static int icmp6_ra_parse(sd_icmp6_nd *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 < ICMP6_OPT_LEN_UNITS) { - log_icmp6_nd(nd, "Router Advertisement below minimum length"); - - return -ENOMSG; - } - - opt = ra + 1; - opt_hdr = opt; - - while (len != 0 && len >= opt_hdr->nd_opt_len * ICMP6_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_icmp6_nd(nd, "Router Advertisement link MTU %d using %d", - mtu, nd->mtu); - } - - break; - - case ND_OPT_PREFIX_INFORMATION: - opt_prefix = opt; - - icmp6_ra_prefix_update(nd, len, opt_prefix); - - break; - } - - len -= opt_hdr->nd_opt_len * ICMP6_OPT_LEN_UNITS; - opt = (void *)((char *)opt + - opt_hdr->nd_opt_len * ICMP6_OPT_LEN_UNITS); - opt_hdr = opt; - } - - if (len > 0) - log_icmp6_nd(nd, "Router Advertisement contains %zd bytes of trailing garbage", len); - - return 0; -} - -static int icmp6_router_advertisment_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) { - sd_icmp6_nd *nd = userdata; - int r, buflen = 0; - ssize_t len; - _cleanup_free_ struct nd_router_advert *ra = NULL; - int event = SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_NONE; - - assert(s); - assert(nd); - assert(nd->event); - - r = ioctl(fd, FIONREAD, &buflen); - if (r < 0 || buflen <= 0) - buflen = ICMP6_ND_RECV_SIZE; - - ra = malloc(buflen); - if (!ra) - return -ENOMEM; - - len = read(fd, ra, buflen); - if (len < 0) { - log_icmp6_nd(nd, "Could not receive message from UDP socket: %m"); - 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 = ICMP6_ROUTER_ADVERTISMENT_LISTEN; - - if (ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER ) - event = SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER; - - if (ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) - event = SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_MANAGED; - - log_icmp6_nd(nd, "Received Router Advertisement flags %s/%s", - ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED? "MANAGED": "none", - ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER? "OTHER": "none"); - - if (event != SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_NONE) { - r = icmp6_ra_parse(nd, ra, len); - if (r < 0) { - log_icmp6_nd(nd, "Could not parse Router Advertisement: %s", - strerror(-r)); - return 0; - } - } - - icmp6_nd_notify(nd, event); - - return 0; -} - -static int icmp6_router_solicitation_timeout(sd_event_source *s, uint64_t usec, void *userdata) { - sd_icmp6_nd *nd = userdata; - uint64_t time_now, next_timeout; - struct ether_addr unset = { }; - struct ether_addr *addr = NULL; - int r; - - assert(s); - assert(nd); - assert(nd->event); - - nd->timeout = sd_event_source_unref(nd->timeout); - - if (nd->nd_sent >= ICMP6_MAX_ROUTER_SOLICITATIONS) { - icmp6_nd_notify(nd, SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_TIMEOUT); - nd->state = ICMP6_ROUTER_ADVERTISMENT_LISTEN; - } else { - if (memcmp(&nd->mac_addr, &unset, sizeof(struct ether_addr))) - addr = &nd->mac_addr; - - r = dhcp_network_icmp6_send_router_solicitation(nd->fd, addr); - if (r < 0) - log_icmp6_nd(nd, "Error sending Router Solicitation"); - else { - nd->state = ICMP6_ROUTER_SOLICITATION_SENT; - log_icmp6_nd(nd, "Sent Router Solicitation"); - } - - nd->nd_sent++; - - r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now); - if (r < 0) { - icmp6_nd_notify(nd, r); - return 0; - } - - next_timeout = time_now + ICMP6_ROUTER_SOLICITATION_INTERVAL; - - r = sd_event_add_time(nd->event, &nd->timeout, clock_boottime_or_monotonic(), - next_timeout, 0, - icmp6_router_solicitation_timeout, nd); - if (r < 0) { - icmp6_nd_notify(nd, r); - return 0; - } - - r = sd_event_source_set_priority(nd->timeout, - nd->event_priority); - if (r < 0) { - icmp6_nd_notify(nd, r); - return 0; - } - - r = sd_event_source_set_description(nd->timeout, "icmp6-timeout"); - if (r < 0) { - icmp6_nd_notify(nd, r); - return 0; - } - } - - return 0; -} - -int sd_icmp6_nd_stop(sd_icmp6_nd *nd) { - assert_return(nd, -EINVAL); - assert_return(nd->event, -EINVAL); - - log_icmp6_nd(client, "Stop ICMPv6"); - - icmp6_nd_init(nd); - - nd->state = ICMP6_NEIGHBOR_DISCOVERY_IDLE; - - return 0; -} - -int sd_icmp6_router_solicitation_start(sd_icmp6_nd *nd) { - int r; - - assert(nd); - assert(nd->event); - - if (nd->state != ICMP6_NEIGHBOR_DISCOVERY_IDLE) - return -EINVAL; - - if (nd->index < 1) - return -EINVAL; - - r = dhcp_network_icmp6_bind_router_solicitation(nd->index); - if (r < 0) - return r; - - nd->fd = r; - - r = sd_event_add_io(nd->event, &nd->recv, nd->fd, EPOLLIN, - icmp6_router_advertisment_recv, nd); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(nd->recv, nd->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(nd->recv, "icmp6-receive-message"); - if (r < 0) - goto error; - - r = sd_event_add_time(nd->event, &nd->timeout, clock_boottime_or_monotonic(), - 0, 0, icmp6_router_solicitation_timeout, nd); - if (r < 0) - goto error; - - r = sd_event_source_set_priority(nd->timeout, nd->event_priority); - if (r < 0) - goto error; - - r = sd_event_source_set_description(nd->timeout, "icmp6-timeout"); -error: - if (r < 0) - icmp6_nd_init(nd); - else - log_icmp6_nd(client, "Start Router Solicitation"); - - return r; -} diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c new file mode 100644 index 0000000000..8545344fd7 --- /dev/null +++ b/src/libsystemd-network/sd-ndisc.c @@ -0,0 +1,720 @@ +/*** + This file is part of systemd. + + Copyright (C) 2014 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 . +***/ + +#include +#include +#include +#include +#include +#include + +#include "socket-util.h" +#include "async.h" + +#include "dhcp6-internal.h" +#include "sd-ndisc.h" + +#define ICMP6_ROUTER_SOLICITATION_INTERVAL 4 * USEC_PER_SEC +#define ICMP6_MAX_ROUTER_SOLICITATIONS 3 + +enum icmp6_nd_state { + ICMP6_NEIGHBOR_DISCOVERY_IDLE = 0, + ICMP6_ROUTER_SOLICITATION_SENT = 10, + ICMP6_ROUTER_ADVERTISMENT_LISTEN = 11, +}; + +#define IP6_MIN_MTU (unsigned)1280 +#define ICMP6_ND_RECV_SIZE (IP6_MIN_MTU - sizeof(struct ip6_hdr)) +#define ICMP6_OPT_LEN_UNITS 8 + +typedef struct ICMP6Prefix ICMP6Prefix; + +struct ICMP6Prefix { + unsigned n_ref; + + LIST_FIELDS(ICMP6Prefix, prefixes); + + uint8_t len; + sd_event_source *timeout_valid; + struct in6_addr addr; +}; + +struct sd_icmp6_nd { + unsigned n_ref; + + enum icmp6_nd_state state; + sd_event *event; + int event_priority; + int index; + struct ether_addr mac_addr; + uint32_t mtu; + ICMP6Prefix *expired_prefix; + LIST_HEAD(ICMP6Prefix, prefixes); + int fd; + sd_event_source *recv; + sd_event_source *timeout; + int nd_sent; + sd_icmp6_nd_callback_t callback; + void *userdata; +}; + +#define log_icmp6_nd(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "ICMPv6 CLIENT: " fmt, ##__VA_ARGS__) + +static ICMP6Prefix *icmp6_prefix_unref(ICMP6Prefix *prefix) { + + if (!prefix) + return NULL; + + assert(prefix->n_ref > 0); + prefix->n_ref--; + + if (prefix->n_ref > 0) + return NULL; + + prefix->timeout_valid = sd_event_source_unref(prefix->timeout_valid); + free(prefix); + return NULL; +} + +static int icmp6_prefix_new(ICMP6Prefix **ret) { + _cleanup_free_ ICMP6Prefix *prefix = NULL; + + assert(ret); + + prefix = new0(ICMP6Prefix, 1); + if (!prefix) + return -ENOMEM; + + prefix->n_ref = 1; + LIST_INIT(prefixes, prefix); + + *ret = prefix; + prefix = NULL; + + return 0; +} + +static void icmp6_nd_notify(sd_icmp6_nd *nd, int event) { + if (nd->callback) + nd->callback(nd, event, nd->userdata); +} + +int sd_icmp6_nd_set_callback(sd_icmp6_nd *nd, sd_icmp6_nd_callback_t callback, + void *userdata) { + assert(nd); + + nd->callback = callback; + nd->userdata = userdata; + + return 0; +} + +int sd_icmp6_nd_set_index(sd_icmp6_nd *nd, int interface_index) { + assert(nd); + assert(interface_index >= -1); + + nd->index = interface_index; + + return 0; +} + +int sd_icmp6_nd_set_mac(sd_icmp6_nd *nd, const struct ether_addr *mac_addr) { + assert(nd); + + if (mac_addr) + memcpy(&nd->mac_addr, mac_addr, sizeof(nd->mac_addr)); + else + zero(nd->mac_addr); + + return 0; + +} + +int sd_icmp6_nd_attach_event(sd_icmp6_nd *nd, sd_event *event, int priority) { + int r; + + assert_return(nd, -EINVAL); + assert_return(!nd->event, -EBUSY); + + if (event) + nd->event = sd_event_ref(event); + else { + r = sd_event_default(&nd->event); + if (r < 0) + return 0; + } + + nd->event_priority = priority; + + return 0; +} + +int sd_icmp6_nd_detach_event(sd_icmp6_nd *nd) { + assert_return(nd, -EINVAL); + + nd->event = sd_event_unref(nd->event); + + return 0; +} + +sd_event *sd_icmp6_nd_get_event(sd_icmp6_nd *nd) { + assert(nd); + + return nd->event; +} + +sd_icmp6_nd *sd_icmp6_nd_ref(sd_icmp6_nd *nd) { + + if (!nd) + return NULL; + + assert(nd->n_ref > 0); + nd->n_ref++; + + return nd; +} + +static int icmp6_nd_init(sd_icmp6_nd *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); + + return 0; +} + +sd_icmp6_nd *sd_icmp6_nd_unref(sd_icmp6_nd *nd) { + ICMP6Prefix *prefix, *p; + + if (!nd) + return NULL; + + assert(nd->n_ref > 0); + nd->n_ref--; + + if (nd->n_ref > 0) + return NULL; + + icmp6_nd_init(nd); + sd_icmp6_nd_detach_event(nd); + + LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) { + LIST_REMOVE(prefixes, nd->prefixes, prefix); + + prefix = icmp6_prefix_unref(prefix); + } + + free(nd); + + return NULL; +} + +DEFINE_TRIVIAL_CLEANUP_FUNC(sd_icmp6_nd*, sd_icmp6_nd_unref); +#define _cleanup_sd_icmp6_nd_free_ _cleanup_(sd_icmp6_nd_unrefp) + +int sd_icmp6_nd_new(sd_icmp6_nd **ret) { + _cleanup_sd_icmp6_nd_free_ sd_icmp6_nd *nd = NULL; + + assert(ret); + + nd = new0(sd_icmp6_nd, 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_icmp6_ra_get_mtu(sd_icmp6_nd *nd, uint32_t *mtu) { + assert_return(nd, -EINVAL); + assert_return(mtu, -EINVAL); + + if (nd->mtu == 0) + return -ENOMSG; + + *mtu = nd->mtu; + + return 0; +} + +static int icmp6_ra_prefix_timeout(sd_event_source *s, uint64_t usec, + void *userdata) { + sd_icmp6_nd *nd = userdata; + ICMP6Prefix *prefix, *p; + + assert(nd); + + LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) { + if (prefix->timeout_valid != s) + continue; + + log_icmp6_nd(nd, "Prefix expired "SD_ICMP6_ND_ADDRESS_FORMAT_STR"/%d", + SD_ICMP6_ND_ADDRESS_FORMAT_VAL(prefix->addr), + prefix->len); + + LIST_REMOVE(prefixes, nd->prefixes, prefix); + + nd->expired_prefix = prefix; + icmp6_nd_notify(nd, + SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED); + nd->expired_prefix = NULL; + + prefix = icmp6_prefix_unref(prefix); + + break; + } + + return 0; +} + +static int icmp6_ra_prefix_set_timeout(sd_icmp6_nd *nd, + ICMP6Prefix *prefix, + usec_t valid) { + usec_t time_now; + int r; + + assert_return(prefix, -EINVAL); + + r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now); + if (r < 0) + return r; + + prefix->timeout_valid = sd_event_source_unref(prefix->timeout_valid); + + r = sd_event_add_time(nd->event, &prefix->timeout_valid, + clock_boottime_or_monotonic(), time_now + valid, + USEC_PER_SEC, icmp6_ra_prefix_timeout, nd); + if (r < 0) + goto error; + + r = sd_event_source_set_priority(prefix->timeout_valid, + nd->event_priority); + if (r < 0) + goto error; + + r = sd_event_source_set_description(prefix->timeout_valid, + "icmp6-prefix-timeout"); + +error: + if (r < 0) + prefix->timeout_valid = + sd_event_source_unref(prefix->timeout_valid); + + return r; +} + +static int icmp6_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); + + if (memcmp(prefix, addr, bytes) != 0 || + (prefix->s6_addr[bytes] & mask) != (addr->s6_addr[bytes] & mask)) + return -EADDRNOTAVAIL; + + return 0; +} + +static int icmp6_ra_prefix_match(ICMP6Prefix *head, const struct in6_addr *addr, + uint8_t addr_len, ICMP6Prefix **result) { + ICMP6Prefix *prefix; + + LIST_FOREACH(prefixes, prefix, head) { + if (icmp6_prefix_match(&prefix->addr, prefix->len, addr, + addr_len) >= 0) { + *result = prefix; + return 0; + } + } + + return -EADDRNOTAVAIL; +} + +int sd_icmp6_prefix_match(struct in6_addr *prefix, uint8_t prefixlen, + struct in6_addr *addr) { + return icmp6_prefix_match(prefix, prefixlen, addr, + sizeof(addr->s6_addr) * 8); +} + +int sd_icmp6_ra_get_prefixlen(sd_icmp6_nd *nd, const struct in6_addr *addr, + uint8_t *prefixlen) { + int r; + ICMP6Prefix *prefix; + + assert_return(nd, -EINVAL); + assert_return(addr, -EINVAL); + assert_return(prefixlen, -EINVAL); + + r = icmp6_ra_prefix_match(nd->prefixes, addr, + sizeof(addr->s6_addr) * 8, &prefix); + if (r < 0) + return r; + + *prefixlen = prefix->len; + + return 0; +} + +int sd_icmp6_ra_get_expired_prefix(sd_icmp6_nd *nd, struct in6_addr **addr, uint8_t *prefixlen) { + assert_return(nd, -EINVAL); + assert_return(addr, -EINVAL); + assert_return(prefixlen, -EINVAL); + + if (!nd->expired_prefix) + return -EADDRNOTAVAIL; + + *addr = &nd->expired_prefix->addr; + *prefixlen = nd->expired_prefix->len; + + return 0; +} + +static int icmp6_ra_prefix_update(sd_icmp6_nd *nd, ssize_t len, + const struct nd_opt_prefix_info *prefix_opt) { + int r; + ICMP6Prefix *prefix; + uint32_t lifetime; + char time_string[FORMAT_TIMESPAN_MAX]; + + assert_return(nd, -EINVAL); + assert_return(prefix_opt, -EINVAL); + + if (len < prefix_opt->nd_opt_pi_len) + return -ENOMSG; + + if (!(prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK)) + return 0; + + lifetime = be32toh(prefix_opt->nd_opt_pi_valid_time); + + r = icmp6_ra_prefix_match(nd->prefixes, + &prefix_opt->nd_opt_pi_prefix, + prefix_opt->nd_opt_pi_prefix_len, &prefix); + + if (r < 0 && r != -EADDRNOTAVAIL) + return r; + + /* if router advertisment prefix valid timeout is zero, the timeout + callback will be called immediately to clean up the prefix */ + + if (r == -EADDRNOTAVAIL) { + r = icmp6_prefix_new(&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_icmp6_nd(nd, "New prefix "SD_ICMP6_ND_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s", + SD_ICMP6_ND_ADDRESS_FORMAT_VAL(prefix->addr), + prefix->len, lifetime, + format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime * 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_icmp6_nd(nd, "Prefix length mismatch %d/%d using %d", + prefix->len, + prefix_opt->nd_opt_pi_prefix_len, + prefixlen); + + prefix->len = prefixlen; + } + + log_icmp6_nd(nd, "Update prefix "SD_ICMP6_ND_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s", + SD_ICMP6_ND_ADDRESS_FORMAT_VAL(prefix->addr), + prefix->len, lifetime, + format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime * USEC_PER_SEC, USEC_PER_SEC)); + } + + r = icmp6_ra_prefix_set_timeout(nd, prefix, lifetime * USEC_PER_SEC); + + return r; +} + +static int icmp6_ra_parse(sd_icmp6_nd *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 < ICMP6_OPT_LEN_UNITS) { + log_icmp6_nd(nd, "Router Advertisement below minimum length"); + + return -ENOMSG; + } + + opt = ra + 1; + opt_hdr = opt; + + while (len != 0 && len >= opt_hdr->nd_opt_len * ICMP6_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_icmp6_nd(nd, "Router Advertisement link MTU %d using %d", + mtu, nd->mtu); + } + + break; + + case ND_OPT_PREFIX_INFORMATION: + opt_prefix = opt; + + icmp6_ra_prefix_update(nd, len, opt_prefix); + + break; + } + + len -= opt_hdr->nd_opt_len * ICMP6_OPT_LEN_UNITS; + opt = (void *)((char *)opt + + opt_hdr->nd_opt_len * ICMP6_OPT_LEN_UNITS); + opt_hdr = opt; + } + + if (len > 0) + log_icmp6_nd(nd, "Router Advertisement contains %zd bytes of trailing garbage", len); + + return 0; +} + +static int icmp6_router_advertisment_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + sd_icmp6_nd *nd = userdata; + int r, buflen = 0; + ssize_t len; + _cleanup_free_ struct nd_router_advert *ra = NULL; + int event = SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_NONE; + + assert(s); + assert(nd); + assert(nd->event); + + r = ioctl(fd, FIONREAD, &buflen); + if (r < 0 || buflen <= 0) + buflen = ICMP6_ND_RECV_SIZE; + + ra = malloc(buflen); + if (!ra) + return -ENOMEM; + + len = read(fd, ra, buflen); + if (len < 0) { + log_icmp6_nd(nd, "Could not receive message from UDP socket: %m"); + 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 = ICMP6_ROUTER_ADVERTISMENT_LISTEN; + + if (ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER ) + event = SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER; + + if (ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) + event = SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_MANAGED; + + log_icmp6_nd(nd, "Received Router Advertisement flags %s/%s", + ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED? "MANAGED": "none", + ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER? "OTHER": "none"); + + if (event != SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_NONE) { + r = icmp6_ra_parse(nd, ra, len); + if (r < 0) { + log_icmp6_nd(nd, "Could not parse Router Advertisement: %s", + strerror(-r)); + return 0; + } + } + + icmp6_nd_notify(nd, event); + + return 0; +} + +static int icmp6_router_solicitation_timeout(sd_event_source *s, uint64_t usec, void *userdata) { + sd_icmp6_nd *nd = userdata; + uint64_t time_now, next_timeout; + struct ether_addr unset = { }; + struct ether_addr *addr = NULL; + int r; + + assert(s); + assert(nd); + assert(nd->event); + + nd->timeout = sd_event_source_unref(nd->timeout); + + if (nd->nd_sent >= ICMP6_MAX_ROUTER_SOLICITATIONS) { + icmp6_nd_notify(nd, SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_TIMEOUT); + nd->state = ICMP6_ROUTER_ADVERTISMENT_LISTEN; + } else { + if (memcmp(&nd->mac_addr, &unset, sizeof(struct ether_addr))) + addr = &nd->mac_addr; + + r = dhcp_network_icmp6_send_router_solicitation(nd->fd, addr); + if (r < 0) + log_icmp6_nd(nd, "Error sending Router Solicitation"); + else { + nd->state = ICMP6_ROUTER_SOLICITATION_SENT; + log_icmp6_nd(nd, "Sent Router Solicitation"); + } + + nd->nd_sent++; + + r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now); + if (r < 0) { + icmp6_nd_notify(nd, r); + return 0; + } + + next_timeout = time_now + ICMP6_ROUTER_SOLICITATION_INTERVAL; + + r = sd_event_add_time(nd->event, &nd->timeout, clock_boottime_or_monotonic(), + next_timeout, 0, + icmp6_router_solicitation_timeout, nd); + if (r < 0) { + icmp6_nd_notify(nd, r); + return 0; + } + + r = sd_event_source_set_priority(nd->timeout, + nd->event_priority); + if (r < 0) { + icmp6_nd_notify(nd, r); + return 0; + } + + r = sd_event_source_set_description(nd->timeout, "icmp6-timeout"); + if (r < 0) { + icmp6_nd_notify(nd, r); + return 0; + } + } + + return 0; +} + +int sd_icmp6_nd_stop(sd_icmp6_nd *nd) { + assert_return(nd, -EINVAL); + assert_return(nd->event, -EINVAL); + + log_icmp6_nd(client, "Stop ICMPv6"); + + icmp6_nd_init(nd); + + nd->state = ICMP6_NEIGHBOR_DISCOVERY_IDLE; + + return 0; +} + +int sd_icmp6_router_solicitation_start(sd_icmp6_nd *nd) { + int r; + + assert(nd); + assert(nd->event); + + if (nd->state != ICMP6_NEIGHBOR_DISCOVERY_IDLE) + return -EINVAL; + + if (nd->index < 1) + return -EINVAL; + + r = dhcp_network_icmp6_bind_router_solicitation(nd->index); + if (r < 0) + return r; + + nd->fd = r; + + r = sd_event_add_io(nd->event, &nd->recv, nd->fd, EPOLLIN, + icmp6_router_advertisment_recv, nd); + if (r < 0) + goto error; + + r = sd_event_source_set_priority(nd->recv, nd->event_priority); + if (r < 0) + goto error; + + r = sd_event_source_set_description(nd->recv, "icmp6-receive-message"); + if (r < 0) + goto error; + + r = sd_event_add_time(nd->event, &nd->timeout, clock_boottime_or_monotonic(), + 0, 0, icmp6_router_solicitation_timeout, nd); + if (r < 0) + goto error; + + r = sd_event_source_set_priority(nd->timeout, nd->event_priority); + if (r < 0) + goto error; + + r = sd_event_source_set_description(nd->timeout, "icmp6-timeout"); +error: + if (r < 0) + icmp6_nd_init(nd); + else + log_icmp6_nd(client, "Start Router Solicitation"); + + return r; +} diff --git a/src/libsystemd-network/test-icmp6-rs.c b/src/libsystemd-network/test-icmp6-rs.c deleted file mode 100644 index 27b0ef4572..0000000000 --- a/src/libsystemd-network/test-icmp6-rs.c +++ /dev/null @@ -1,357 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -/*** - This file is part of systemd. - - Copyright (C) 2014 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 . -***/ - -#include - -#include "socket-util.h" - -#include "dhcp6-internal.h" -#include "sd-icmp6-nd.h" - -static struct ether_addr mac_addr = { - .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'} -}; - -static bool verbose = false; -static sd_event_source *test_hangcheck; -static int test_fd[2]; - -typedef int (*send_ra_t)(uint8_t flags); -static send_ra_t send_ra_function; - -static int test_rs_hangcheck(sd_event_source *s, uint64_t usec, - void *userdata) { - assert_se(false); - - return 0; -} - -int dhcp_network_icmp6_bind_router_solicitation(int index) { - assert_se(index == 42); - - if (socketpair(AF_UNIX, SOCK_DGRAM, 0, test_fd) < 0) - return -errno; - - return test_fd[0]; -} - -static int send_ra_short_prefix(uint8_t flags) { - uint8_t advertisement[] = { - 0x86, 0x00, 0xbe, 0xd7, 0x40, 0xc0, 0x00, 0xb4, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x03, 0x04, 0x34, 0xc0, 0x00, 0x00, 0x01, 0xf4, - 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - }; - - assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) == - sizeof(advertisement)); - - return 0; -} - -static void test_short_prefix_cb(sd_icmp6_nd *nd, int event, void *userdata) { - sd_event *e = userdata; - struct { - struct in6_addr addr; - uint8_t prefixlen; - bool success; - } addrs[] = { - { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, - 52, true }, - { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0x0d, 0xad, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, - 64, false }, - { { { { 0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, - 60, true }, - { { { { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x9d, 0xab, 0xcd, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, - 64, true }, - { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xed, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }, - 52, true }, - }; - uint8_t prefixlen; - unsigned int i; - - for (i = 0; i < ELEMENTSOF(addrs); i++) { - printf(" %s prefix %02x%02x:%02x%02x:%02x%02x:%02x%02x", - __FUNCTION__, - addrs[i].addr.s6_addr[0], addrs[i].addr.s6_addr[1], - addrs[i].addr.s6_addr[2], addrs[i].addr.s6_addr[3], - addrs[i].addr.s6_addr[4], addrs[i].addr.s6_addr[5], - addrs[i].addr.s6_addr[6], addrs[i].addr.s6_addr[7]); - - if (addrs[i].success) { - assert_se(sd_icmp6_ra_get_prefixlen(nd, &addrs[i].addr, - &prefixlen) >= 0); - assert_se(addrs[i].prefixlen == prefixlen); - printf("/%d onlink\n", prefixlen); - } else { - assert_se(sd_icmp6_ra_get_prefixlen(nd, &addrs[i].addr, - &prefixlen) == -EADDRNOTAVAIL); - printf("/128 offlink\n"); - } - } - - sd_event_exit(e, 0); -} - -static int send_ra_prefixes(uint8_t flags) { - uint8_t advertisement[] = { - 0x86, 0x00, 0xbe, 0xd7, 0x40, 0xc0, 0x00, 0xb4, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x04, 0x3f, 0xc0, 0x00, 0x00, 0x01, 0xf4, - 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x04, 0x40, 0x00, 0x00, 0x00, 0x02, 0x58, - 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0x0d, 0xad, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x04, 0x3c, 0x80, 0x00, 0x00, 0x03, 0x84, - 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x03, 0x84, - 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x9d, 0xab, 0xcd, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, - 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x1f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, - 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, - 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53 - }; - - assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) == - sizeof(advertisement)); - - return 0; -} - -static void test_prefixes_cb(sd_icmp6_nd *nd, int event, void *userdata) { - sd_event *e = userdata; - struct { - struct in6_addr addr; - uint8_t prefixlen; - bool success; - } addrs[] = { - { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, - 63, true }, - { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0x0d, 0xad, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, - 64, false }, - { { { { 0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, - 60, true }, - { { { { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x9d, 0xab, 0xcd, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, - 64, true }, - { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xed, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }, - 63, false }, - }; - uint8_t prefixlen; - unsigned int i; - - for (i = 0; i < ELEMENTSOF(addrs); i++) { - printf(" %s prefix %02x%02x:%02x%02x:%02x%02x:%02x%02x", - __FUNCTION__, - addrs[i].addr.s6_addr[0], addrs[i].addr.s6_addr[1], - addrs[i].addr.s6_addr[2], addrs[i].addr.s6_addr[3], - addrs[i].addr.s6_addr[4], addrs[i].addr.s6_addr[5], - addrs[i].addr.s6_addr[6], addrs[i].addr.s6_addr[7]); - - if (addrs[i].success) { - assert_se(sd_icmp6_ra_get_prefixlen(nd, &addrs[i].addr, - &prefixlen) >= 0); - assert_se(addrs[i].prefixlen == prefixlen); - printf("/%d onlink\n", prefixlen); - } else { - assert_se(sd_icmp6_ra_get_prefixlen(nd, &addrs[i].addr, - &prefixlen) == -EADDRNOTAVAIL); - printf("/128 offlink\n"); - } - } - - send_ra_function = send_ra_short_prefix; - assert_se(sd_icmp6_nd_set_callback(nd, test_short_prefix_cb, e) >= 0); - assert_se(sd_icmp6_nd_stop(nd) >= 0); - assert_se(sd_icmp6_router_solicitation_start(nd) >= 0); -} - -static void test_prefixes(void) { - sd_event *e; - sd_icmp6_nd *nd; - - if (verbose) - printf("* %s\n", __FUNCTION__); - - send_ra_function = send_ra_prefixes; - - assert_se(sd_event_new(&e) >= 0); - - assert_se(sd_icmp6_nd_new(&nd) >= 0); - assert_se(nd); - - assert_se(sd_icmp6_nd_attach_event(nd, e, 0) >= 0); - - assert_se(sd_icmp6_nd_set_index(nd, 42) >= 0); - assert_se(sd_icmp6_nd_set_mac(nd, &mac_addr) >= 0); - assert_se(sd_icmp6_nd_set_callback(nd, test_prefixes_cb, e) >= 0); - - assert_se(sd_icmp6_router_solicitation_start(nd) >= 0); - - sd_event_loop(e); - - nd = sd_icmp6_nd_unref(nd); - assert_se(!nd); - - close(test_fd[1]); - - sd_event_unref(e); -} - -static int send_ra(uint8_t flags) { - uint8_t advertisement[] = { - 0x86, 0x00, 0xde, 0x83, 0x40, 0xc0, 0x00, 0xb4, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x01, 0xf4, - 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, - 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x1f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, - 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, - 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x01, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53, - }; - - advertisement[5] = flags; - - assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) == - sizeof(advertisement)); - - if (verbose) - printf(" sent RA with flag 0x%02x\n", flags); - - return 0; -} - -int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) { - return send_ra_function(0); -} - -static void test_rs_done(sd_icmp6_nd *nd, int event, void *userdata) { - sd_event *e = userdata; - static int idx = 0; - struct { - uint8_t flag; - int event; - } flag_event[] = { - { 0, SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_NONE }, - { ND_RA_FLAG_OTHER, SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER }, - { ND_RA_FLAG_MANAGED, SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_MANAGED } - }; - uint32_t mtu; - - assert_se(nd); - - assert_se(event == flag_event[idx].event); - idx++; - - if (verbose) - printf(" got event %d\n", event); - - if (idx < 3) { - send_ra(flag_event[idx].flag); - return; - } - - assert_se(sd_icmp6_ra_get_mtu(nd, &mtu) == -ENOMSG); - - sd_event_exit(e, 0); -} - -static void test_rs(void) { - sd_event *e; - sd_icmp6_nd *nd; - usec_t time_now = now(clock_boottime_or_monotonic()); - - if (verbose) - printf("* %s\n", __FUNCTION__); - - send_ra_function = send_ra; - - assert_se(sd_event_new(&e) >= 0); - - assert_se(sd_icmp6_nd_new(&nd) >= 0); - assert_se(nd); - - assert_se(sd_icmp6_nd_attach_event(nd, e, 0) >= 0); - - assert_se(sd_icmp6_nd_set_index(nd, 42) >= 0); - assert_se(sd_icmp6_nd_set_mac(nd, &mac_addr) >= 0); - assert_se(sd_icmp6_nd_set_callback(nd, test_rs_done, e) >= 0); - - assert_se(sd_event_add_time(e, &test_hangcheck, clock_boottime_or_monotonic(), - time_now + 2 *USEC_PER_SEC, 0, - test_rs_hangcheck, NULL) >= 0); - - assert_se(sd_icmp6_nd_stop(nd) >= 0); - assert_se(sd_icmp6_router_solicitation_start(nd) >= 0); - assert_se(sd_icmp6_nd_stop(nd) >= 0); - - assert_se(sd_icmp6_router_solicitation_start(nd) >= 0); - - sd_event_loop(e); - - test_hangcheck = sd_event_source_unref(test_hangcheck); - - nd = sd_icmp6_nd_unref(nd); - assert_se(!nd); - - close(test_fd[1]); - - sd_event_unref(e); -} - -int main(int argc, char *argv[]) { - - log_set_max_level(LOG_DEBUG); - log_parse_environment(); - log_open(); - - test_rs(); - test_prefixes(); - - return 0; -} diff --git a/src/libsystemd-network/test-ndisc-rs.c b/src/libsystemd-network/test-ndisc-rs.c new file mode 100644 index 0000000000..49305e297b --- /dev/null +++ b/src/libsystemd-network/test-ndisc-rs.c @@ -0,0 +1,357 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright (C) 2014 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 . +***/ + +#include + +#include "socket-util.h" + +#include "dhcp6-internal.h" +#include "sd-ndisc.h" + +static struct ether_addr mac_addr = { + .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'} +}; + +static bool verbose = false; +static sd_event_source *test_hangcheck; +static int test_fd[2]; + +typedef int (*send_ra_t)(uint8_t flags); +static send_ra_t send_ra_function; + +static int test_rs_hangcheck(sd_event_source *s, uint64_t usec, + void *userdata) { + assert_se(false); + + return 0; +} + +int dhcp_network_icmp6_bind_router_solicitation(int index) { + assert_se(index == 42); + + if (socketpair(AF_UNIX, SOCK_DGRAM, 0, test_fd) < 0) + return -errno; + + return test_fd[0]; +} + +static int send_ra_short_prefix(uint8_t flags) { + uint8_t advertisement[] = { + 0x86, 0x00, 0xbe, 0xd7, 0x40, 0xc0, 0x00, 0xb4, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + 0x03, 0x04, 0x34, 0xc0, 0x00, 0x00, 0x01, 0xf4, + 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) == + sizeof(advertisement)); + + return 0; +} + +static void test_short_prefix_cb(sd_icmp6_nd *nd, int event, void *userdata) { + sd_event *e = userdata; + struct { + struct in6_addr addr; + uint8_t prefixlen; + bool success; + } addrs[] = { + { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + 52, true }, + { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0x0d, 0xad, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + 64, false }, + { { { { 0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + 60, true }, + { { { { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x9d, 0xab, 0xcd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + 64, true }, + { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xed, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }, + 52, true }, + }; + uint8_t prefixlen; + unsigned int i; + + for (i = 0; i < ELEMENTSOF(addrs); i++) { + printf(" %s prefix %02x%02x:%02x%02x:%02x%02x:%02x%02x", + __FUNCTION__, + addrs[i].addr.s6_addr[0], addrs[i].addr.s6_addr[1], + addrs[i].addr.s6_addr[2], addrs[i].addr.s6_addr[3], + addrs[i].addr.s6_addr[4], addrs[i].addr.s6_addr[5], + addrs[i].addr.s6_addr[6], addrs[i].addr.s6_addr[7]); + + if (addrs[i].success) { + assert_se(sd_icmp6_ra_get_prefixlen(nd, &addrs[i].addr, + &prefixlen) >= 0); + assert_se(addrs[i].prefixlen == prefixlen); + printf("/%d onlink\n", prefixlen); + } else { + assert_se(sd_icmp6_ra_get_prefixlen(nd, &addrs[i].addr, + &prefixlen) == -EADDRNOTAVAIL); + printf("/128 offlink\n"); + } + } + + sd_event_exit(e, 0); +} + +static int send_ra_prefixes(uint8_t flags) { + uint8_t advertisement[] = { + 0x86, 0x00, 0xbe, 0xd7, 0x40, 0xc0, 0x00, 0xb4, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x04, 0x3f, 0xc0, 0x00, 0x00, 0x01, 0xf4, + 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x04, 0x40, 0x00, 0x00, 0x00, 0x02, 0x58, + 0x00, 0x00, 0x02, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0x0d, 0xad, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x04, 0x3c, 0x80, 0x00, 0x00, 0x03, 0x84, + 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x03, 0x84, + 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x9d, 0xab, 0xcd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, + 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x1f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, + 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, + 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53 + }; + + assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) == + sizeof(advertisement)); + + return 0; +} + +static void test_prefixes_cb(sd_icmp6_nd *nd, int event, void *userdata) { + sd_event *e = userdata; + struct { + struct in6_addr addr; + uint8_t prefixlen; + bool success; + } addrs[] = { + { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + 63, true }, + { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0x0d, 0xad, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + 64, false }, + { { { { 0x20, 0x01, 0x0d, 0xb8, 0x0b, 0x16, 0xd0, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + 60, true }, + { { { { 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x9d, 0xab, 0xcd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } } }, + 64, true }, + { { { { 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xed, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } } }, + 63, false }, + }; + uint8_t prefixlen; + unsigned int i; + + for (i = 0; i < ELEMENTSOF(addrs); i++) { + printf(" %s prefix %02x%02x:%02x%02x:%02x%02x:%02x%02x", + __FUNCTION__, + addrs[i].addr.s6_addr[0], addrs[i].addr.s6_addr[1], + addrs[i].addr.s6_addr[2], addrs[i].addr.s6_addr[3], + addrs[i].addr.s6_addr[4], addrs[i].addr.s6_addr[5], + addrs[i].addr.s6_addr[6], addrs[i].addr.s6_addr[7]); + + if (addrs[i].success) { + assert_se(sd_icmp6_ra_get_prefixlen(nd, &addrs[i].addr, + &prefixlen) >= 0); + assert_se(addrs[i].prefixlen == prefixlen); + printf("/%d onlink\n", prefixlen); + } else { + assert_se(sd_icmp6_ra_get_prefixlen(nd, &addrs[i].addr, + &prefixlen) == -EADDRNOTAVAIL); + printf("/128 offlink\n"); + } + } + + send_ra_function = send_ra_short_prefix; + assert_se(sd_icmp6_nd_set_callback(nd, test_short_prefix_cb, e) >= 0); + assert_se(sd_icmp6_nd_stop(nd) >= 0); + assert_se(sd_icmp6_router_solicitation_start(nd) >= 0); +} + +static void test_prefixes(void) { + sd_event *e; + sd_icmp6_nd *nd; + + if (verbose) + printf("* %s\n", __FUNCTION__); + + send_ra_function = send_ra_prefixes; + + assert_se(sd_event_new(&e) >= 0); + + assert_se(sd_icmp6_nd_new(&nd) >= 0); + assert_se(nd); + + assert_se(sd_icmp6_nd_attach_event(nd, e, 0) >= 0); + + assert_se(sd_icmp6_nd_set_index(nd, 42) >= 0); + assert_se(sd_icmp6_nd_set_mac(nd, &mac_addr) >= 0); + assert_se(sd_icmp6_nd_set_callback(nd, test_prefixes_cb, e) >= 0); + + assert_se(sd_icmp6_router_solicitation_start(nd) >= 0); + + sd_event_loop(e); + + nd = sd_icmp6_nd_unref(nd); + assert_se(!nd); + + close(test_fd[1]); + + sd_event_unref(e); +} + +static int send_ra(uint8_t flags) { + uint8_t advertisement[] = { + 0x86, 0x00, 0xde, 0x83, 0x40, 0xc0, 0x00, 0xb4, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x01, 0xf4, + 0x00, 0x00, 0x01, 0xb8, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x19, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, + 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x1f, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, + 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, + 0x72, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53, + }; + + advertisement[5] = flags; + + assert_se(write(test_fd[1], advertisement, sizeof(advertisement)) == + sizeof(advertisement)); + + if (verbose) + printf(" sent RA with flag 0x%02x\n", flags); + + return 0; +} + +int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) { + return send_ra_function(0); +} + +static void test_rs_done(sd_icmp6_nd *nd, int event, void *userdata) { + sd_event *e = userdata; + static int idx = 0; + struct { + uint8_t flag; + int event; + } flag_event[] = { + { 0, SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_NONE }, + { ND_RA_FLAG_OTHER, SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER }, + { ND_RA_FLAG_MANAGED, SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_MANAGED } + }; + uint32_t mtu; + + assert_se(nd); + + assert_se(event == flag_event[idx].event); + idx++; + + if (verbose) + printf(" got event %d\n", event); + + if (idx < 3) { + send_ra(flag_event[idx].flag); + return; + } + + assert_se(sd_icmp6_ra_get_mtu(nd, &mtu) == -ENOMSG); + + sd_event_exit(e, 0); +} + +static void test_rs(void) { + sd_event *e; + sd_icmp6_nd *nd; + usec_t time_now = now(clock_boottime_or_monotonic()); + + if (verbose) + printf("* %s\n", __FUNCTION__); + + send_ra_function = send_ra; + + assert_se(sd_event_new(&e) >= 0); + + assert_se(sd_icmp6_nd_new(&nd) >= 0); + assert_se(nd); + + assert_se(sd_icmp6_nd_attach_event(nd, e, 0) >= 0); + + assert_se(sd_icmp6_nd_set_index(nd, 42) >= 0); + assert_se(sd_icmp6_nd_set_mac(nd, &mac_addr) >= 0); + assert_se(sd_icmp6_nd_set_callback(nd, test_rs_done, e) >= 0); + + assert_se(sd_event_add_time(e, &test_hangcheck, clock_boottime_or_monotonic(), + time_now + 2 *USEC_PER_SEC, 0, + test_rs_hangcheck, NULL) >= 0); + + assert_se(sd_icmp6_nd_stop(nd) >= 0); + assert_se(sd_icmp6_router_solicitation_start(nd) >= 0); + assert_se(sd_icmp6_nd_stop(nd) >= 0); + + assert_se(sd_icmp6_router_solicitation_start(nd) >= 0); + + sd_event_loop(e); + + test_hangcheck = sd_event_source_unref(test_hangcheck); + + nd = sd_icmp6_nd_unref(nd); + assert_se(!nd); + + close(test_fd[1]); + + sd_event_unref(e); +} + +int main(int argc, char *argv[]) { + + log_set_max_level(LOG_DEBUG); + log_parse_environment(); + log_open(); + + test_rs(); + test_prefixes(); + + return 0; +} diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index fb93e6606e..e572ad4595 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -25,7 +25,7 @@ #include "networkd-link.h" #include "network-internal.h" -#include "sd-icmp6-nd.h" +#include "sd-ndisc.h" #include "sd-dhcp6-client.h" static int dhcp6_lease_address_acquired(sd_dhcp6_client *client, Link *link); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index af2ba11701..5bba313049 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -26,7 +26,7 @@ #include "sd-dhcp-client.h" #include "sd-dhcp-server.h" #include "sd-ipv4ll.h" -#include "sd-icmp6-nd.h" +#include "sd-ndisc.h" #include "sd-dhcp6-client.h" #include "sd-lldp.h" diff --git a/src/systemd/sd-icmp6-nd.h b/src/systemd/sd-icmp6-nd.h deleted file mode 100644 index cb6c24a0cb..0000000000 --- a/src/systemd/sd-icmp6-nd.h +++ /dev/null @@ -1,79 +0,0 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - -#ifndef foosdicmp6ndfoo -#define foosdicmp6ndfoo - -/*** - This file is part of systemd. - - Copyright (C) 2014 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 . -***/ - -#include - -#include "sd-event.h" - -enum { - SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_NONE = 0, - SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_TIMEOUT = 1, - SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER = 2, - SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_MANAGED = 3, - SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED = 4, -}; - -typedef struct sd_icmp6_nd sd_icmp6_nd; - -typedef void(*sd_icmp6_nd_callback_t)(sd_icmp6_nd *nd, int event, - void *userdata); - -int sd_icmp6_nd_set_callback(sd_icmp6_nd *nd, sd_icmp6_nd_callback_t cb, - void *userdata); -int sd_icmp6_nd_set_index(sd_icmp6_nd *nd, int interface_index); -int sd_icmp6_nd_set_mac(sd_icmp6_nd *nd, const struct ether_addr *mac_addr); - -int sd_icmp6_nd_attach_event(sd_icmp6_nd *nd, sd_event *event, int priority); -int sd_icmp6_nd_detach_event(sd_icmp6_nd *nd); -sd_event *sd_icmp6_nd_get_event(sd_icmp6_nd *nd); - -sd_icmp6_nd *sd_icmp6_nd_ref(sd_icmp6_nd *nd); -sd_icmp6_nd *sd_icmp6_nd_unref(sd_icmp6_nd *nd); -int sd_icmp6_nd_new(sd_icmp6_nd **ret); - -int sd_icmp6_prefix_match(struct in6_addr *prefix, uint8_t prefixlen, - struct in6_addr *addr); - -int sd_icmp6_ra_get_mtu(sd_icmp6_nd *nd, uint32_t *mtu); -int sd_icmp6_ra_get_prefixlen(sd_icmp6_nd *nd, const struct in6_addr *addr, - uint8_t *prefixlen); -int sd_icmp6_ra_get_expired_prefix(sd_icmp6_nd *nd, struct in6_addr **addr, - uint8_t *prefixlen); - -int sd_icmp6_nd_stop(sd_icmp6_nd *nd); -int sd_icmp6_router_solicitation_start(sd_icmp6_nd *nd); - -#define SD_ICMP6_ND_ADDRESS_FORMAT_STR "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x" - -#define SD_ICMP6_ND_ADDRESS_FORMAT_VAL(address) \ - be16toh((address).s6_addr16[0]), \ - be16toh((address).s6_addr16[1]), \ - be16toh((address).s6_addr16[2]), \ - be16toh((address).s6_addr16[3]), \ - be16toh((address).s6_addr16[4]), \ - be16toh((address).s6_addr16[5]), \ - be16toh((address).s6_addr16[6]), \ - be16toh((address).s6_addr16[7]) - -#endif diff --git a/src/systemd/sd-ndisc.h b/src/systemd/sd-ndisc.h new file mode 100644 index 0000000000..240feb7bbe --- /dev/null +++ b/src/systemd/sd-ndisc.h @@ -0,0 +1,79 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foosdndiscfoo +#define foosdndiscfoo + +/*** + This file is part of systemd. + + Copyright (C) 2014 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 . +***/ + +#include + +#include "sd-event.h" + +enum { + SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_NONE = 0, + SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_TIMEOUT = 1, + SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_OTHER = 2, + SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_MANAGED = 3, + SD_ICMP6_ND_EVENT_ROUTER_ADVERTISMENT_PREFIX_EXPIRED = 4, +}; + +typedef struct sd_icmp6_nd sd_icmp6_nd; + +typedef void(*sd_icmp6_nd_callback_t)(sd_icmp6_nd *nd, int event, + void *userdata); + +int sd_icmp6_nd_set_callback(sd_icmp6_nd *nd, sd_icmp6_nd_callback_t cb, + void *userdata); +int sd_icmp6_nd_set_index(sd_icmp6_nd *nd, int interface_index); +int sd_icmp6_nd_set_mac(sd_icmp6_nd *nd, const struct ether_addr *mac_addr); + +int sd_icmp6_nd_attach_event(sd_icmp6_nd *nd, sd_event *event, int priority); +int sd_icmp6_nd_detach_event(sd_icmp6_nd *nd); +sd_event *sd_icmp6_nd_get_event(sd_icmp6_nd *nd); + +sd_icmp6_nd *sd_icmp6_nd_ref(sd_icmp6_nd *nd); +sd_icmp6_nd *sd_icmp6_nd_unref(sd_icmp6_nd *nd); +int sd_icmp6_nd_new(sd_icmp6_nd **ret); + +int sd_icmp6_prefix_match(struct in6_addr *prefix, uint8_t prefixlen, + struct in6_addr *addr); + +int sd_icmp6_ra_get_mtu(sd_icmp6_nd *nd, uint32_t *mtu); +int sd_icmp6_ra_get_prefixlen(sd_icmp6_nd *nd, const struct in6_addr *addr, + uint8_t *prefixlen); +int sd_icmp6_ra_get_expired_prefix(sd_icmp6_nd *nd, struct in6_addr **addr, + uint8_t *prefixlen); + +int sd_icmp6_nd_stop(sd_icmp6_nd *nd); +int sd_icmp6_router_solicitation_start(sd_icmp6_nd *nd); + +#define SD_ICMP6_ND_ADDRESS_FORMAT_STR "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x" + +#define SD_ICMP6_ND_ADDRESS_FORMAT_VAL(address) \ + be16toh((address).s6_addr16[0]), \ + be16toh((address).s6_addr16[1]), \ + be16toh((address).s6_addr16[2]), \ + be16toh((address).s6_addr16[3]), \ + be16toh((address).s6_addr16[4]), \ + be16toh((address).s6_addr16[5]), \ + be16toh((address).s6_addr16[6]), \ + be16toh((address).s6_addr16[7]) + +#endif -- cgit v1.2.3-54-g00ecf