]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/network/networkd-ndisc.c
time-util: assume CLOCK_BOOTTIME always exists
[thirdparty/systemd.git] / src / network / networkd-ndisc.c
index 956cc273801a2e638e1816e57c2f0a6ca43d0f8d..3078f82a44c1699bb856e19fb0f9626627c8e186 100644 (file)
@@ -6,6 +6,7 @@
 #include <arpa/inet.h>
 #include <netinet/icmp6.h>
 #include <linux/if.h>
+#include <linux/if_arp.h>
 
 #include "sd-ndisc.h"
 
@@ -34,10 +35,19 @@ bool link_ipv6_accept_ra_enabled(Link *link) {
         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);
@@ -90,7 +100,7 @@ static int ndisc_remove(Link *link, struct in6_addr *router) {
                 if (k < 0)
                         r = k;
 
-                route_cancel_request(route);
+                route_cancel_request(route, link);
         }
 
         SET_FOREACH(address, link->addresses) {
@@ -193,13 +203,10 @@ static int ndisc_check_ready(Link *link) {
         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)
@@ -244,13 +251,10 @@ static int ndisc_request_route(Route *in, Link *link, sd_ndisc_router *rt) {
                                   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)
@@ -290,29 +294,39 @@ static int ndisc_request_address(Address *in, Link *link, sd_ndisc_router *rt) {
 }
 
 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, &timestamp_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;
 
@@ -327,33 +341,35 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
         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;
 
@@ -367,7 +383,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
                 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;
 
@@ -380,19 +396,21 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
 }
 
 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, &timestamp_usec);
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
 
@@ -413,23 +431,26 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
                 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");
@@ -446,8 +467,8 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
                 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.
@@ -456,9 +477,7 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
                 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;
                 }
 
@@ -472,15 +491,26 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
 
 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, &timestamp_usec);
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
 
@@ -488,10 +518,6 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
         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();
@@ -499,7 +525,7 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
         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)
@@ -512,21 +538,74 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
         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);
@@ -537,6 +616,11 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
         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;
@@ -554,7 +638,7 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
         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;
 
@@ -568,7 +652,7 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
         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, &timestamp_usec);
         if (r < 0)
                 return log_link_error_errno(link, r, "Failed to get RA timestamp: %m");
 
@@ -582,7 +666,7 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
         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)
@@ -607,35 +691,41 @@ DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
                 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, &timestamp_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;
@@ -651,7 +741,7 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
                 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;
                 }
 
@@ -662,7 +752,7 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
                 *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));
@@ -696,35 +786,40 @@ DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR(
 
 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, &timestamp_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)
@@ -745,12 +840,12 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
                 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)
@@ -767,11 +862,13 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
 }
 
 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)
@@ -785,51 +882,11 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) {
 
                 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);
@@ -838,19 +895,15 @@ static int ndisc_router_process_options(Link *link, sd_ndisc_router *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;
                 }
         }
@@ -875,9 +928,51 @@ static void ndisc_mark(Link *link, const struct in6_addr *router) {
                         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);
@@ -904,30 +999,14 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) {
 
         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;
@@ -979,7 +1058,7 @@ static void ndisc_handler(sd_ndisc *nd, sd_ndisc_event_t event, sd_ndisc_router
         }
 }
 
-int ndisc_configure(Link *link) {
+static int ndisc_configure(Link *link) {
         int r;
 
         assert(link);
@@ -1014,6 +1093,8 @@ int ndisc_configure(Link *link) {
 }
 
 int ndisc_start(Link *link) {
+        int r;
+
         assert(link);
 
         if (!link->ndisc || !link->dhcp6_client)
@@ -1027,7 +1108,55 @@ int ndisc_start(Link *link) {
 
         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) {
@@ -1039,14 +1168,14 @@ 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));
 }