]> 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 2bcb25daad00968e654136c9b4cba3aff2782907..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,7 +294,6 @@ 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;
         uint16_t lifetime_sec;
@@ -299,8 +302,13 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
         int r;
 
         assert(link);
+        assert(link->network);
         assert(rt);
 
+        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");
@@ -308,7 +316,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
         if (lifetime_sec == 0) /* not a default router */
                 return 0;
 
-        r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &timestamp_usec);
+        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");
 
@@ -318,7 +326,7 @@ 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 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;
 
@@ -339,23 +347,29 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
                         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 = lifetime_usec;
-        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;
 
@@ -390,9 +404,13 @@ static int ndisc_router_process_autonomous_prefix(Link *link, sd_ndisc_router *r
         int r;
 
         assert(link);
+        assert(link->network);
         assert(rt);
 
-        r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &timestamp_usec);
+        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");
 
@@ -479,8 +497,12 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
         int r;
 
         assert(link);
+        assert(link->network);
         assert(rt);
 
+        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");
@@ -488,7 +510,7 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) {
         if (lifetime_sec == 0)
                 return 0;
 
-        r = sd_ndisc_router_get_timestamp(rt, clock_boottime_or_monotonic(), &timestamp_usec);
+        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");
 
@@ -516,6 +538,56 @@ 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;
         unsigned preference, prefixlen;
@@ -526,6 +598,9 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) {
 
         assert(link);
 
+        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");
@@ -541,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;
@@ -558,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;
 
@@ -572,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(), &timestamp_usec);
+        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");
 
@@ -619,13 +699,17 @@ static int ndisc_router_process_rdnss(Link *link, sd_ndisc_router *rt) {
         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(), &timestamp_usec);
+        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");
 
@@ -706,17 +790,20 @@ static int ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) {
         struct in6_addr router;
         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(), &timestamp_usec);
+        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");
 
@@ -775,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)
@@ -793,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);
-                        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);
+                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 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);
@@ -846,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;
                 }
         }
@@ -883,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);
@@ -912,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;
@@ -987,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);
@@ -1022,6 +1093,8 @@ int ndisc_configure(Link *link) {
 }
 
 int ndisc_start(Link *link) {
+        int r;
+
         assert(link);
 
         if (!link->ndisc || !link->dhcp6_client)
@@ -1035,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) {
@@ -1047,7 +1168,7 @@ 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->lifetime_usec < time_now)