#include <arpa/inet.h>
#include <netinet/icmp6.h>
#include <linux/if.h>
+#include <linux/if_arp.h>
#include "sd-ndisc.h"
if (link->flags & IFF_LOOPBACK)
return false;
+ if (link->iftype == ARPHRD_CAN)
+ return false;
+
+ if (link->hw_addr.length != ETH_ALEN && !streq_ptr(link->kind, "wwan"))
+ /* Currently, only interfaces whose MAC address length is ETH_ALEN are supported.
+ * Note, wwan interfaces may be assigned MAC address slightly later.
+ * Hence, let's wait for a while.*/
+ return false;
+
if (!link->network)
return false;
- if (!link_ipv6ll_enabled(link))
+ if (!link_may_have_ipv6ll(link))
return false;
assert(link->network->ipv6_accept_ra >= 0);
if (k < 0)
r = k;
- route_cancel_request(route);
+ route_cancel_request(route, link);
}
SET_FOREACH(address, link->addresses) {
return 0;
}
-static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+static int ndisc_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Route *route) {
int r;
assert(link);
- assert(link->ndisc_messages > 0);
-
- link->ndisc_messages--;
r = route_configure_handler_internal(rtnl, m, link, "Could not set NDisc route");
if (r <= 0)
ndisc_route_handler, NULL);
}
-static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
+static int ndisc_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, Address *address) {
int r;
assert(link);
- assert(link->ndisc_messages > 0);
-
- link->ndisc_messages--;
r = address_configure_handler_internal(rtnl, m, link, "Could not set NDisc address");
if (r <= 0)
}
static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
- _cleanup_(route_freep) Route *route = NULL;
+ usec_t lifetime_usec, timestamp_usec;
struct in6_addr gateway;
- uint32_t mtu = 0;
+ uint16_t lifetime_sec;
unsigned preference;
- uint16_t lifetime;
- usec_t time_now;
+ uint32_t mtu = 0;
int r;
assert(link);
+ assert(link->network);
assert(rt);
- r = sd_ndisc_router_get_lifetime(rt, &lifetime);
+ if (!link->network->ipv6_accept_ra_use_gateway &&
+ hashmap_isempty(link->network->routes_by_section))
+ return 0;
+
+ r = sd_ndisc_router_get_lifetime(rt, &lifetime_sec);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get gateway lifetime from RA: %m");
- if (lifetime == 0) /* not a default router */
+ if (lifetime_sec == 0) /* not a default router */
return 0;
+ r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, ×tamp_usec);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
+
+ lifetime_usec = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC);
+
r = sd_ndisc_router_get_address(rt, &gateway);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get gateway address from RA: %m");
- if (link_get_ipv6_address(link, &gateway, NULL) >= 0) {
+ if (link_get_ipv6_address(link, &gateway, 0, NULL) >= 0) {
if (DEBUG_LOGGING) {
_cleanup_free_ char *buffer = NULL;
if (r < 0)
return log_link_error_errno(link, r, "Failed to get default router preference from RA: %m");
- r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
- if (r < 0)
- return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
-
if (link->network->ipv6_accept_ra_use_mtu) {
r = sd_ndisc_router_get_mtu(rt, &mtu);
if (r < 0 && r != -ENODATA)
return log_link_error_errno(link, r, "Failed to get default router MTU from RA: %m");
}
- r = route_new(&route);
- if (r < 0)
- return log_oom();
+ if (link->network->ipv6_accept_ra_use_gateway) {
+ _cleanup_(route_freep) Route *route = NULL;
- route->family = AF_INET6;
- route->pref = preference;
- route->gw_family = AF_INET6;
- route->gw.in6 = gateway;
- route->lifetime = usec_add(time_now, lifetime * USEC_PER_SEC);
- route->mtu = mtu;
+ r = route_new(&route);
+ if (r < 0)
+ return log_oom();
- r = ndisc_request_route(TAKE_PTR(route), link, rt);
- if (r < 0)
- return log_link_error_errno(link, r, "Could not request default route: %m");
+ route->family = AF_INET6;
+ route->pref = preference;
+ route->gw_family = AF_INET6;
+ route->gw.in6 = gateway;
+ route->lifetime_usec = lifetime_usec;
+ route->mtu = mtu;
+
+ r = ndisc_request_route(TAKE_PTR(route), link, rt);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not request default route: %m");
+ }
Route *route_gw;
HASHMAP_FOREACH(route_gw, link->network->routes_by_section) {
+ _cleanup_(route_freep) Route *route = NULL;
+
if (!route_gw->gateway_from_dhcp_or_ra)
continue;
route->gw.in6 = gateway;
if (!route->pref_set)
route->pref = preference;
- route->lifetime = usec_add(time_now, lifetime * USEC_PER_SEC);
+ route->lifetime_usec = lifetime_usec;
if (route->mtu == 0)
route->mtu = mtu;
}
static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *rt) {
- uint32_t lifetime_valid, lifetime_preferred;
+ uint32_t lifetime_valid_sec, lifetime_preferred_sec;
+ usec_t lifetime_valid_usec, lifetime_preferred_usec, timestamp_usec;
_cleanup_set_free_ Set *addresses = NULL;
struct in6_addr prefix, *a;
unsigned prefixlen;
- usec_t time_now;
int r;
assert(link);
+ assert(link->network);
assert(rt);
- /* Do not use clock_boottime_or_monotonic() here, as the kernel internally manages cstamp and
- * tstamp with jiffies, and it is not increased while the system is suspended. */
- r = sd_ndisc_router_get_timestamp(rt, CLOCK_MONOTONIC, &time_now);
+ if (!link->network->ipv6_accept_ra_use_autonomous_prefix)
+ return 0;
+
+ r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, ×tamp_usec);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
return 0;
}
- r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid);
+ r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_valid_sec);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get prefix valid lifetime: %m");
- if (lifetime_valid == 0) {
+ if (lifetime_valid_sec == 0) {
log_link_debug(link, "Ignoring prefix as its valid lifetime is zero.");
return 0;
}
- r = sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred);
+ r = sd_ndisc_router_prefix_get_preferred_lifetime(rt, &lifetime_preferred_sec);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get prefix preferred lifetime: %m");
/* The preferred lifetime is never greater than the valid lifetime */
- if (lifetime_preferred > lifetime_valid)
+ if (lifetime_preferred_sec > lifetime_valid_sec)
return 0;
+ lifetime_valid_usec = usec_add(lifetime_valid_sec * USEC_PER_SEC, timestamp_usec);
+ lifetime_preferred_usec = usec_add(lifetime_preferred_sec * USEC_PER_SEC, timestamp_usec);
+
r = ndisc_generate_addresses(link, &prefix, prefixlen, &addresses);
if (r < 0)
return log_link_error_errno(link, r, "Failed to generate SLAAC addresses: %m");
address->in_addr.in6 = *a;
address->prefixlen = prefixlen;
address->flags = IFA_F_NOPREFIXROUTE|IFA_F_MANAGETEMPADDR;
- address->cinfo.ifa_valid = lifetime_valid;
- address->cinfo.ifa_prefered = lifetime_preferred;
+ address->lifetime_valid_usec = lifetime_valid_usec;
+ address->lifetime_preferred_usec = lifetime_preferred_usec;
/* See RFC4862, section 5.5.3.e. But the following logic is deviated from RFC4862 by
* honoring all valid lifetimes to improve the reaction of SLAAC to renumbering events.
if (r > 0) {
/* If the address is already assigned, but not valid anymore, then refuse to
* update the address, and it will be removed. */
- if (e->cinfo.ifa_valid != CACHE_INFO_INFINITY_LIFE_TIME &&
- usec_add(e->cinfo.tstamp / 100 * USEC_PER_SEC,
- e->cinfo.ifa_valid * USEC_PER_SEC) < time_now)
+ if (e->lifetime_valid_usec < timestamp_usec)
continue;
}
static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
_cleanup_(route_freep) Route *route = NULL;
- usec_t time_now;
- uint32_t lifetime;
+ usec_t timestamp_usec;
+ uint32_t lifetime_sec;
unsigned prefixlen;
int r;
assert(link);
+ assert(link->network);
assert(rt);
- r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
+ if (!link->network->ipv6_accept_ra_use_onlink_prefix)
+ return 0;
+
+ r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime_sec);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to get prefix lifetime: %m");
+
+ if (lifetime_sec == 0)
+ return 0;
+
+ r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, ×tamp_usec);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
if (r < 0)
return log_link_error_errno(link, r, "Failed to get prefix length: %m");
- r = sd_ndisc_router_prefix_get_valid_lifetime(rt, &lifetime);
- if (r < 0)
- return log_link_error_errno(link, r, "Failed to get prefix lifetime: %m");
-
r = route_new(&route);
if (r < 0)
return log_oom();
route->family = AF_INET6;
route->flags = RTM_F_PREFIX;
route->dst_prefixlen = prefixlen;
- route->lifetime = usec_add(time_now, lifetime * USEC_PER_SEC);
+ route->lifetime_usec = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC);
r = sd_ndisc_router_prefix_get_address(rt, &route->dst.in6);
if (r < 0)
return 0;
}
+static int ndisc_router_process_prefix(Link *link, sd_ndisc_router *rt) {
+ unsigned prefixlen;
+ struct in6_addr a;
+ uint8_t flags;
+ int r;
+
+ assert(link);
+ assert(link->network);
+ assert(rt);
+
+ r = sd_ndisc_router_prefix_get_address(rt, &a);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to get prefix address: %m");
+
+ r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to get prefix length: %m");
+
+ if (in6_prefix_is_filtered(&a, prefixlen, link->network->ndisc_allow_listed_prefix, link->network->ndisc_deny_listed_prefix)) {
+ if (DEBUG_LOGGING) {
+ _cleanup_free_ char *b = NULL;
+
+ (void) in6_addr_prefix_to_string(&a, prefixlen, &b);
+ if (!set_isempty(link->network->ndisc_allow_listed_prefix))
+ log_link_debug(link, "Prefix '%s' is not in allow list, ignoring", strna(b));
+ else
+ log_link_debug(link, "Prefix '%s' is in deny list, ignoring", strna(b));
+ }
+ return 0;
+ }
+
+ r = sd_ndisc_router_prefix_get_flags(rt, &flags);
+ if (r < 0)
+ return log_link_error_errno(link, r, "Failed to get RA prefix flags: %m");
+
+ if (FLAGS_SET(flags, ND_OPT_PI_FLAG_ONLINK)) {
+ r = ndisc_router_process_onlink_prefix(link, rt);
+ if (r < 0)
+ return r;
+ }
+
+ if (FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO)) {
+ r = ndisc_router_process_autonomous_prefix(link, rt);
+ if (r < 0)
+ return r;
+ }
+
+ return 0;
+}
+
static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
_cleanup_(route_freep) Route *route = NULL;
- struct in6_addr gateway, dst;
- uint32_t lifetime;
unsigned preference, prefixlen;
- usec_t time_now;
+ struct in6_addr gateway, dst;
+ uint32_t lifetime_sec;
+ usec_t timestamp_usec;
int r;
assert(link);
- r = sd_ndisc_router_route_get_lifetime(rt, &lifetime);
+ if (!link->network->ipv6_accept_ra_use_route_prefix)
+ return 0;
+
+ r = sd_ndisc_router_route_get_lifetime(rt, &lifetime_sec);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get route lifetime from RA: %m");
- if (lifetime == 0)
+ if (lifetime_sec == 0)
return 0;
r = sd_ndisc_router_route_get_address(rt, &dst);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get route prefix length: %m");
+ if (in6_addr_is_null(&dst) && prefixlen == 0) {
+ log_link_debug(link, "Route prefix is ::/0, ignoring");
+ return 0;
+ }
+
if (in6_prefix_is_filtered(&dst, prefixlen, link->network->ndisc_allow_listed_route_prefix, link->network->ndisc_deny_listed_route_prefix)) {
if (DEBUG_LOGGING) {
_cleanup_free_ char *buf = NULL;
if (r < 0)
return log_link_error_errno(link, r, "Failed to get gateway address from RA: %m");
- if (link_get_ipv6_address(link, &gateway, NULL) >= 0) {
+ if (link_get_ipv6_address(link, &gateway, 0, NULL) >= 0) {
if (DEBUG_LOGGING) {
_cleanup_free_ char *buf = NULL;
if (r < 0)
return log_link_error_errno(link, r, "Failed to get default router preference from RA: %m");
- r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
+ r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, ×tamp_usec);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
route->gw_family = AF_INET6;
route->dst.in6 = dst;
route->dst_prefixlen = prefixlen;
- route->lifetime = usec_add(time_now, lifetime * USEC_PER_SEC);
+ route->lifetime_usec = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC);
r = ndisc_request_route(TAKE_PTR(route), link, rt);
if (r < 0)
free);
static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
- uint32_t lifetime;
+ usec_t lifetime_usec, timestamp_usec;
+ uint32_t lifetime_sec;
const struct in6_addr *a;
struct in6_addr router;
- usec_t time_now;
bool updated = false;
int n, r;
assert(link);
+ assert(link->network);
assert(rt);
+ if (!link->network->ipv6_accept_ra_use_dns)
+ return 0;
+
r = sd_ndisc_router_get_address(rt, &router);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get router address from RA: %m");
- r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
+ r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, ×tamp_usec);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
- r = sd_ndisc_router_rdnss_get_lifetime(rt, &lifetime);
+ r = sd_ndisc_router_rdnss_get_lifetime(rt, &lifetime_sec);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get RDNSS lifetime: %m");
+ if (lifetime_sec == 0)
+ return 0;
+
+ lifetime_usec = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC);
+
n = sd_ndisc_router_rdnss_get_addresses(rt, &a);
if (n < 0)
return log_link_error_errno(link, n, "Failed to get RDNSS addresses: %m");
- if (lifetime == 0)
- return 0;
-
if (n >= (int) NDISC_RDNSS_MAX) {
log_link_warning(link, "Too many RDNSS records per link. Only first %i records will be used.", NDISC_RDNSS_MAX);
n = NDISC_RDNSS_MAX;
if (rdnss) {
rdnss->marked = false;
rdnss->router = router;
- rdnss->valid_until = usec_add(time_now, lifetime * USEC_PER_SEC);
+ rdnss->lifetime_usec = lifetime_usec;
continue;
}
*x = (NDiscRDNSS) {
.address = a[j],
.router = router,
- .valid_until = usec_add(time_now, lifetime * USEC_PER_SEC),
+ .lifetime_usec = lifetime_usec,
};
r = set_ensure_consume(&link->ndisc_rdnss, &ndisc_rdnss_hash_ops, TAKE_PTR(x));
static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
_cleanup_strv_free_ char **l = NULL;
+ usec_t lifetime_usec, timestamp_usec;
struct in6_addr router;
- uint32_t lifetime;
- usec_t time_now;
+ uint32_t lifetime_sec;
bool updated = false;
- char **j;
int r;
assert(link);
+ assert(link->network);
assert(rt);
+ if (link->network->ipv6_accept_ra_use_domains == DHCP_USE_DOMAINS_NO)
+ return 0;
+
r = sd_ndisc_router_get_address(rt, &router);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get router address from RA: %m");
- r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &time_now);
+ r = sd_ndisc_router_get_timestamp(rt, CLOCK_BOOTTIME, ×tamp_usec);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
- r = sd_ndisc_router_dnssl_get_lifetime(rt, &lifetime);
+ r = sd_ndisc_router_dnssl_get_lifetime(rt, &lifetime_sec);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get DNSSL lifetime: %m");
+ if (lifetime_sec == 0)
+ return 0;
+
+ lifetime_usec = usec_add(timestamp_usec, lifetime_sec * USEC_PER_SEC);
+
r = sd_ndisc_router_dnssl_get_domains(rt, &l);
if (r < 0)
return log_link_error_errno(link, r, "Failed to get DNSSL addresses: %m");
- if (lifetime == 0)
- return 0;
-
if (strv_length(l) >= NDISC_DNSSL_MAX) {
log_link_warning(link, "Too many DNSSL records per link. Only first %i records will be used.", NDISC_DNSSL_MAX);
STRV_FOREACH(j, l + NDISC_DNSSL_MAX)
if (dnssl) {
dnssl->marked = false;
dnssl->router = router;
- dnssl->valid_until = usec_add(time_now, lifetime * USEC_PER_SEC);
+ dnssl->lifetime_usec = lifetime_usec;
continue;
}
s->router = router;
- s->valid_until = usec_add(time_now, lifetime * USEC_PER_SEC);
+ s->lifetime_usec = lifetime_usec;
r = set_ensure_consume(&link->ndisc_dnssl, &ndisc_dnssl_hash_ops, TAKE_PTR(s));
if (r < 0)
}
static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
+ int r;
+
assert(link);
assert(link->network);
assert(rt);
- for (int r = sd_ndisc_router_option_rewind(rt); ; r = sd_ndisc_router_option_next(rt)) {
+ for (r = sd_ndisc_router_option_rewind(rt); ; r = sd_ndisc_router_option_next(rt)) {
uint8_t type;
if (r < 0)
switch (type) {
- case SD_NDISC_OPTION_PREFIX_INFORMATION: {
- unsigned prefixlen;
- struct in6_addr a;
- uint8_t flags;
-
- r = sd_ndisc_router_prefix_get_address(rt, &a);
- if (r < 0)
- return log_link_error_errno(link, r, "Failed to get prefix address: %m");
-
- r = sd_ndisc_router_prefix_get_prefixlen(rt, &prefixlen);
+ case SD_NDISC_OPTION_PREFIX_INFORMATION:
+ r = ndisc_router_process_prefix(link, rt);
if (r < 0)
- return log_link_error_errno(link, r, "Failed to get prefix length: %m");
-
- if (in6_prefix_is_filtered(&a, prefixlen, link->network->ndisc_allow_listed_prefix, link->network->ndisc_deny_listed_prefix)) {
- if (DEBUG_LOGGING) {
- _cleanup_free_ char *b = NULL;
-
- (void) in6_addr_prefix_to_string(&a, prefixlen, &b);
- if (!set_isempty(link->network->ndisc_allow_listed_prefix))
- log_link_debug(link, "Prefix '%s' is not in allow list, ignoring", strna(b));
- else
- log_link_debug(link, "Prefix '%s' is in deny list, ignoring", strna(b));
- }
- break;
- }
-
- r = sd_ndisc_router_prefix_get_flags(rt, &flags);
- if (r < 0)
- return log_link_error_errno(link, r, "Failed to get RA prefix flags: %m");
-
- if (link->network->ipv6_accept_ra_use_onlink_prefix &&
- FLAGS_SET(flags, ND_OPT_PI_FLAG_ONLINK)) {
- r = ndisc_router_process_onlink_prefix(link, rt);
- if (r < 0)
- return r;
- }
-
- if (link->network->ipv6_accept_ra_use_autonomous_prefix &&
- FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO)) {
- r = ndisc_router_process_autonomous_prefix(link, rt);
- if (r < 0)
- return r;
- }
+ return r;
break;
- }
case SD_NDISC_OPTION_ROUTE_INFORMATION:
r = ndisc_router_process_route(link, rt);
break;
case SD_NDISC_OPTION_RDNSS:
- if (link->network->ipv6_accept_ra_use_dns) {
- r = ndisc_router_process_rdnss(link, rt);
- if (r < 0)
- return r;
- }
+ r = ndisc_router_process_rdnss(link, rt);
+ if (r < 0)
+ return r;
break;
case SD_NDISC_OPTION_DNSSL:
- if (link->network->ipv6_accept_ra_use_dns) {
- r = ndisc_router_process_dnssl(link, rt);
- if (r < 0)
- return r;
- }
+ r = ndisc_router_process_dnssl(link, rt);
+ if (r < 0)
+ return r;
break;
}
}
dnssl->marked = true;
}
+static int ndisc_start_dhcp6_client(Link *link, sd_ndisc_router *rt) {
+ int r;
+
+ assert(link);
+ assert(link->network);
+
+ switch (link->network->ipv6_accept_ra_start_dhcp6_client) {
+ case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO:
+ return 0;
+
+ case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_YES: {
+ uint64_t flags;
+
+ r = sd_ndisc_router_get_flags(rt, &flags);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to get RA flags: %m");
+
+ if ((flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER)) == 0)
+ return 0;
+
+ /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags.
+ * Note, if both managed and other information bits are set, then ignore other
+ * information bit. See RFC 4861. */
+ r = dhcp6_start_on_ra(link, !(flags & ND_RA_FLAG_MANAGED));
+ break;
+ }
+ case IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS:
+ /* When IPv6AcceptRA.DHCPv6Client=always, start dhcp6 client in managed mode
+ * even if the router flags have neither M nor O flags. */
+ r = dhcp6_start_on_ra(link, /* information_request = */ false);
+ break;
+
+ default:
+ assert_not_reached();
+ }
+
+ if (r < 0)
+ return log_link_error_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m");
+
+ log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request");
+ return 0;
+}
+
static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
struct in6_addr router;
- uint64_t flags;
int r;
assert(link);
ndisc_mark(link, &router);
- r = sd_ndisc_router_get_flags(rt, &flags);
+ r = ndisc_start_dhcp6_client(link, rt);
if (r < 0)
- return log_link_error_errno(link, r, "Failed to get RA flags: %m");
-
- if ((flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER) &&
- link->network->ipv6_accept_ra_start_dhcp6_client != IPV6_ACCEPT_RA_START_DHCP6_CLIENT_NO) ||
- link->network->ipv6_accept_ra_start_dhcp6_client == IPV6_ACCEPT_RA_START_DHCP6_CLIENT_ALWAYS) {
-
- if (flags & (ND_RA_FLAG_MANAGED | ND_RA_FLAG_OTHER))
- /* (re)start DHCPv6 client in stateful or stateless mode according to RA flags */
- r = dhcp6_request_information(link, !(flags & ND_RA_FLAG_MANAGED));
- else
- /* When IPv6AcceptRA.DHCPv6Client=always, start dhcp6 client in managed mode
- * even if router does not have M or O flag. */
- r = dhcp6_request_information(link, false);
- if (r < 0 && r != -EBUSY)
- return log_link_error_errno(link, r, "Could not acquire DHCPv6 lease on NDisc request: %m");
- else
- log_link_debug(link, "Acquiring DHCPv6 lease on NDisc request");
- }
+ return r;
r = ndisc_router_process_default(link, rt);
if (r < 0)
return r;
+
r = ndisc_router_process_options(link, rt);
if (r < 0)
return r;
}
}
-int ndisc_configure(Link *link) {
+static int ndisc_configure(Link *link) {
int r;
assert(link);
}
int ndisc_start(Link *link) {
+ int r;
+
assert(link);
if (!link->ndisc || !link->dhcp6_client)
log_link_debug(link, "Discovering IPv6 routers");
- return sd_ndisc_start(link->ndisc);
+ r = sd_ndisc_start(link->ndisc);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
+static int ndisc_process_request(Request *req, Link *link, void *userdata) {
+ int r;
+
+ assert(link);
+
+ if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED))
+ return 0;
+
+ if (link->hw_addr.length != ETH_ALEN || hw_addr_is_null(&link->hw_addr))
+ /* No MAC address is assigned to the hardware, or non-supported MAC address length. */
+ return 0;
+
+ r = ndisc_configure(link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to configure IPv6 Router Discovery: %m");
+
+ r = ndisc_start(link);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to start IPv6 Router Discovery: %m");
+
+ log_link_debug(link, "IPv6 Router Discovery is configured%s.",
+ r > 0 ? " and started" : "");
+ return 1;
+}
+
+int link_request_ndisc(Link *link) {
+ int r;
+
+ assert(link);
+
+ if (!link_ipv6_accept_ra_enabled(link))
+ return 0;
+
+ if (link->ndisc)
+ return 0;
+
+ r = link_queue_request(link, REQUEST_TYPE_NDISC, ndisc_process_request, NULL);
+ if (r < 0)
+ return log_link_warning_errno(link, r, "Failed to request configuring of the IPv6 Router Discovery: %m");
+
+ log_link_debug(link, "Requested configuring of the IPv6 Router Discovery.");
+ return 0;
}
void ndisc_vacuum(Link *link) {
/* Removes all RDNSS and DNSSL entries whose validity time has passed */
- time_now = now(clock_boottime_or_monotonic());
+ time_now = now(CLOCK_BOOTTIME);
SET_FOREACH(r, link->ndisc_rdnss)
- if (r->valid_until < time_now)
+ if (r->lifetime_usec < time_now)
free(set_remove(link->ndisc_rdnss, r));
SET_FOREACH(d, link->ndisc_dnssl)
- if (d->valid_until < time_now)
+ if (d->lifetime_usec < time_now)
free(set_remove(link->ndisc_dnssl, d));
}