sd-ndisc.c revision 0d43d2fcb7ac5264c739dc2f67f93ed0985a418a
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan This file is part of systemd.
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan Copyright (C) 2014 Intel Corporation. All rights reserved.
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan systemd is free software; you can redistribute it and/or modify it
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan under the terms of the GNU Lesser General Public License as published by
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan the Free Software Foundation; either version 2.1 of the License, or
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan (at your option) any later version.
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan systemd is distributed in the hope that it will be useful, but
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan WITHOUT ANY WARRANTY; without even the implied warranty of
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan Lesser General Public License for more details.
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan You should have received a copy of the GNU Lesser General Public License
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan along with systemd; If not, see <http://www.gnu.org/licenses/>.
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan#define NDISC_ROUTER_SOLICITATION_INTERVAL 4 * USEC_PER_SEC
56f76965098d6cc3ae531ce0a73bda588abdf1d3Dirk Hogan#define ICMP6_RECV_SIZE (IP6_MIN_MTU - sizeof(struct ip6_hdr))
56f76965098d6cc3ae531ce0a73bda588abdf1d3Dirk Hogan sd_ndisc_prefix_autonomous_callback_t prefix_autonomous_callback;
55e32e1d339c1e3417aa96111d48d51eb29be585Dirk Hogan sd_ndisc_prefix_onlink_callback_t prefix_onlink_callback;
7b09ff83c4ae53ff29b28d7c359ac90fe015a3a3Dirk Hogan#define log_ndisc(p, fmt, ...) log_internal(LOG_DEBUG, 0, __FILE__, __LINE__, __func__, "NDisc CLIENT: " fmt, ##__VA_ARGS__)
7b09ff83c4ae53ff29b28d7c359ac90fe015a3a3Dirk Hoganstatic NDiscPrefix *ndisc_prefix_unref(NDiscPrefix *prefix) {
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan LIST_REMOVE(prefixes, prefix->nd->prefixes, prefix);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hoganstatic int ndisc_prefix_new(sd_ndisc *nd, NDiscPrefix **ret) {
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan sd_ndisc_prefix_onlink_callback_t prefix_onlink_callback,
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan sd_ndisc_prefix_autonomous_callback_t prefix_autonomous_callback,
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan nd->prefix_onlink_callback = prefix_onlink_callback;
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan nd->prefix_autonomous_callback = prefix_autonomous_callback;
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hoganint sd_ndisc_set_index(sd_ndisc *nd, int interface_index) {
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hoganint sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr) {
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan memcpy(&nd->mac_addr, mac_addr, sizeof(nd->mac_addr));
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hoganint sd_ndisc_attach_event(sd_ndisc *nd, sd_event *event, int priority) {
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan nd->timeout = sd_event_source_unref(nd->timeout);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes)
3744900be632496920d4c9aca8f94ba6db4dd882Dirk HoganDEFINE_TRIVIAL_CLEANUP_FUNC(sd_ndisc*, sd_ndisc_unref);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan#define _cleanup_sd_ndisc_free_ _cleanup_(sd_ndisc_unrefp)
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hoganint sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *mtu) {
bf428bd5bd8ff463b3438964b14c7f95ee57fc8cDirk Hoganstatic int prefix_match(const struct in6_addr *prefix, uint8_t prefixlen,
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan (prefix->s6_addr[bytes] & mask) != (addr->s6_addr[bytes] & mask))
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hoganstatic int ndisc_prefix_match(sd_ndisc *nd, const struct in6_addr *addr,
56f76965098d6cc3ae531ce0a73bda588abdf1d3Dirk Hogan r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan LIST_FOREACH_SAFE(prefixes, prefix, p, nd->prefixes) {
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan if (prefix_match(&prefix->addr, prefix->len, addr, addr_len) >= 0) {
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hoganstatic int ndisc_prefix_update(sd_ndisc *nd, ssize_t len,
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan if (!(prefix_opt->nd_opt_pi_flags_reserved & (ND_OPT_PI_FLAG_ONLINK | ND_OPT_PI_FLAG_AUTO)))
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan if (in_addr_is_link_local(AF_INET6, (const union in_addr_union *) &prefix_opt->nd_opt_pi_prefix) > 0)
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan lifetime_valid = be32toh(prefix_opt->nd_opt_pi_valid_time);
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan lifetime_preferred = be32toh(prefix_opt->nd_opt_pi_preferred_time);
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan r = ndisc_prefix_match(nd, &prefix_opt->nd_opt_pi_prefix,
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan if (r < 0 && r != -EADDRNOTAVAIL)
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan /* if router advertisment prefix valid timeout is zero, the timeout
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan callback will be called immediately to clean up the prefix */
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan memcpy(&prefix->addr, &prefix_opt->nd_opt_pi_prefix,
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan log_ndisc(nd, "New prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC));
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan if (prefix->len != prefix_opt->nd_opt_pi_prefix_len) {
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan prefixlen = MIN(prefix->len, prefix_opt->nd_opt_pi_prefix_len);
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan log_ndisc(nd, "Prefix length mismatch %d/%d using %d",
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan log_ndisc(nd, "Update prefix "SD_NDISC_ADDRESS_FORMAT_STR"/%d lifetime %d expires in %s",
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan format_timespan(time_string, FORMAT_TIMESPAN_MAX, lifetime_valid * USEC_PER_SEC, USEC_PER_SEC));
dcf0ce40c27bbcd1b429aaf915b5dfa385a59d7eDirk Hogan r = sd_event_now(nd->event, clock_boottime_or_monotonic(), &time_now);
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan prefix->valid_until = time_now + lifetime_valid * USEC_PER_SEC;
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan if ((prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK) && nd->prefix_onlink_callback)
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan nd->prefix_onlink_callback(nd, &prefix->addr, prefix->len, prefix->valid_until, nd->userdata);
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan if ((prefix_opt->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO) && nd->prefix_autonomous_callback)
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan nd->prefix_autonomous_callback(nd, &prefix->addr, prefix->len, lifetime_preferred, lifetime_valid,
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hoganstatic int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra,
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan log_ndisc(nd, "Router Advertisement below minimum length");
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan while (len != 0 && len >= opt_hdr->nd_opt_len * NDISC_OPT_LEN_UNITS) {
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan log_ndisc(nd, "Router Advertisement link MTU %d using %d",
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan len -= opt_hdr->nd_opt_len * NDISC_OPT_LEN_UNITS;
3744900be632496920d4c9aca8f94ba6db4dd882Dirk Hogan log_ndisc(nd, "Router Advertisement contains %zd bytes of trailing garbage", len);
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hoganstatic int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
9f80f4f537152f3b88b0c3327601c9215474d9f2Dirk Hogan _cleanup_free_ struct nd_router_advert *ra = NULL;
unsigned lifetime;
assert(s);
return -errno;
else if (buflen < 0)
return -EIO;
if (!ra)
return -ENOMEM;
if (len < 0) {
return -errno;
} else if (router_len == 0)
log_ndisc(nd, "Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t)router_len);
switch (pref) {
case ND_RA_FLAG_PREF_LOW:
case ND_RA_FLAG_PREF_HIGH:
lifetime);
assert(s);
next_timeout, 0,
return -EBUSY;
return -EINVAL;
goto error;
goto error;
goto error;
goto error;
goto error;