summary refs log tree commit diff
path: root/src/libnm-systemd-core
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2025-02-12 13:46:50 +0100
committerMichael Biebl <biebl@debian.org>2025-02-12 13:46:50 +0100
commit8bdf070ff046f482f6eb5e2b15ebc216f5d1e3da (patch)
treef706478d189d54c6532e8863d4b0d5ff5575af60 /src/libnm-systemd-core
parent818258cf34b83fbc754633295e1052d4752d7b15 (diff)
New upstream version 1.51.90 upstream/1.51.90
Diffstat (limited to 'src/libnm-systemd-core')
-rw-r--r--src/libnm-systemd-core/nm-sd.c2
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp-duid-internal.h2
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h5
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h3
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c18
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c77
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h14
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/network-common.c2
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c11
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c121
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-device/device-private.c4
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-device/device-private.h8
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h2
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-device/sd-device.c176
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-event/event-source.h3
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c9
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h2
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c267
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c63
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h3
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c52
-rw-r--r--src/libnm-systemd-core/src/systemd/_sd-common.h11
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-device.h30
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h2
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-dhcp6-protocol.h5
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-event.h27
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-id128.h15
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-ndisc.h6
28 files changed, 599 insertions, 341 deletions
diff --git a/src/libnm-systemd-core/nm-sd.c b/src/libnm-systemd-core/nm-sd.c
index b512e3fc..aecbdf94 100644
--- a/src/libnm-systemd-core/nm-sd.c
+++ b/src/libnm-systemd-core/nm-sd.c
@@ -70,7 +70,7 @@ event_create_source(sd_event *event)
 
     source->event = is_default_event ? g_steal_pointer(&event) : sd_event_ref(event);
 
-    source->pollfd = (GPollFD){
+    source->pollfd = (GPollFD) {
         .fd     = sd_event_get_fd(source->event),
         .events = G_IO_IN | G_IO_HUP | G_IO_ERR,
     };
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-duid-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp-duid-internal.h
index f8bc15c4..0d3d6b5c 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-duid-internal.h
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-duid-internal.h
@@ -73,7 +73,7 @@ static inline bool duid_data_size_is_valid(size_t size) {
         return size >= MIN_DUID_DATA_LEN && size <= MAX_DUID_DATA_LEN;
 }
 
-const char *duid_type_to_string(DUIDType t) _const_;
+const char* duid_type_to_string(DUIDType t) _const_;
 int dhcp_duid_to_string_internal(uint16_t type, const void *data, size_t data_size, char **ret);
 
 int dhcp_identifier_set_iaid(
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h
index 3fbfc028..ecd62ea8 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h
@@ -84,9 +84,8 @@ struct sd_dhcp6_client {
         bool send_release;
 };
 
-int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address);
-int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
-                                  const void *packet, size_t len);
+int dhcp6_network_bind_udp_socket(int ifindex, const struct in6_addr *address);
+int dhcp6_network_send_udp_socket(int s, const struct in6_addr *address, const void *packet, size_t len);
 
 int dhcp6_client_send_message(sd_dhcp6_client *client);
 int dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id);
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h
index e76a108f..60cd84f2 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h
@@ -8,6 +8,7 @@
 #include <inttypes.h>
 
 #include "sd-dhcp6-lease.h"
+#include "dns-resolver-internal.h"
 
 #include "dhcp6-option.h"
 #include "dhcp6-protocol.h"
@@ -38,6 +39,8 @@ struct sd_dhcp6_lease {
 
         struct in6_addr *dns;
         size_t dns_count;
+        sd_dns_resolver *dnr;
+        size_t n_dnr;
         char **domains;
         struct in6_addr *ntp;
         size_t ntp_count;
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c
index 7b17dbc1..03732692 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c
@@ -19,9 +19,10 @@
 #include "fd-util.h"
 #include "socket-util.h"
 
-int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
+int dhcp6_network_bind_udp_socket(int ifindex, const struct in6_addr *local_address) {
         union sockaddr_union src = {
                 .in6.sin6_family = AF_INET6,
+                .in6.sin6_addr = *ASSERT_PTR(local_address),
                 .in6.sin6_port = htobe16(DHCP6_PORT_CLIENT),
                 .in6.sin6_scope_id = ifindex,
         };
@@ -29,9 +30,6 @@ int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
         int r;
 
         assert(ifindex > 0);
-        assert(local_address);
-
-        src.in6.sin6_addr = *local_address;
 
         s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_UDP);
         if (s < 0)
@@ -60,20 +58,14 @@ int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
         return TAKE_FD(s);
 }
 
-int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
-                                  const void *packet, size_t len) {
+int dhcp6_network_send_udp_socket(int s, const struct in6_addr *server_address, const void *packet, size_t len) {
         union sockaddr_union dest = {
                 .in6.sin6_family = AF_INET6,
+                .in6.sin6_addr = *ASSERT_PTR(server_address),
                 .in6.sin6_port = htobe16(DHCP6_PORT_SERVER),
         };
-        int r;
-
-        assert(server_address);
 
-        memcpy(&dest.in6.sin6_addr, server_address, sizeof(dest.in6.sin6_addr));
-
-        r = sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in6));
-        if (r < 0)
+        if (sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in6)) < 0)
                 return -errno;
 
         return 0;
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c
index 5fa9b265..4e30c9e5 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c
@@ -208,6 +208,7 @@ bool dhcp6_option_can_request(uint16_t option) {
         case SD_DHCP6_OPTION_V6_DOTS_RI:
         case SD_DHCP6_OPTION_V6_DOTS_ADDRESS:
         case SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF:
+        case SD_DHCP6_OPTION_V6_DNR:
                 return true;
         default:
                 return false;
@@ -822,74 +823,6 @@ int dhcp6_option_parse_addresses(
         return 0;
 }
 
-static int parse_domain(const uint8_t **data, size_t *len, char **ret) {
-        _cleanup_free_ char *domain = NULL;
-        const uint8_t *optval;
-        size_t optlen, n = 0;
-        int r;
-
-        assert(data);
-        assert(len);
-        assert(*data || *len == 0);
-        assert(ret);
-
-        optval = *data;
-        optlen = *len;
-
-        if (optlen <= 1)
-                return -ENODATA;
-
-        for (;;) {
-                const char *label;
-                uint8_t c;
-
-                if (optlen == 0)
-                        break;
-
-                c = *optval;
-                optval++;
-                optlen--;
-
-                if (c == 0)
-                        /* End label */
-                        break;
-                if (c > 63)
-                        return -EBADMSG;
-                if (c > optlen)
-                        return -EMSGSIZE;
-
-                /* Literal label */
-                label = (const char*) optval;
-                optval += c;
-                optlen -= c;
-
-                if (!GREEDY_REALLOC(domain, n + (n != 0) + DNS_LABEL_ESCAPED_MAX))
-                        return -ENOMEM;
-
-                if (n != 0)
-                        domain[n++] = '.';
-
-                r = dns_label_escape(label, c, domain + n, DNS_LABEL_ESCAPED_MAX);
-                if (r < 0)
-                        return r;
-
-                n += r;
-        }
-
-        if (n > 0) {
-                if (!GREEDY_REALLOC(domain, n + 1))
-                        return -ENOMEM;
-
-                domain[n] = '\0';
-        }
-
-        *ret = TAKE_PTR(domain);
-        *data = optval;
-        *len = optlen;
-
-        return n;
-}
-
 int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret) {
         _cleanup_free_ char *domain = NULL;
         int r;
@@ -897,7 +830,7 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **r
         assert(optval || optlen == 0);
         assert(ret);
 
-        r = parse_domain(&optval, &optlen, &domain);
+        r = dns_name_from_wire_format(&optval, &optlen, &domain);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -924,11 +857,11 @@ int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, cha
         while (optlen > 0) {
                 _cleanup_free_ char *name = NULL;
 
-                r = parse_domain(&optval, &optlen, &name);
+                r = dns_name_from_wire_format(&optval, &optlen, &name);
                 if (r < 0)
                         return r;
-                if (r == 0)
-                        continue;
+                if (dns_name_is_root(name)) /* root domain */
+                        return -EBADMSG;
 
                 r = strv_consume(&names, TAKE_PTR(name));
                 if (r < 0)
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h
index c70f9320..39f5040f 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h
@@ -28,9 +28,11 @@ typedef struct DHCP6Message DHCP6Message;
 #define DHCP6_MIN_OPTIONS_SIZE \
         1280 - sizeof(struct ip6_hdr) - sizeof(struct udphdr)
 
-#define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT                 \
-        { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,           \
-              0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 } } }
+#define IN6_ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS                 \
+        ((const struct in6_addr) { { {                              \
+                0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,     \
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02,     \
+         } } } )
 
 enum {
         DHCP6_PORT_SERVER                       = 547,
@@ -150,9 +152,9 @@ typedef enum DHCP6FQDNFlag {
         DHCP6_FQDN_FLAG_N = 1 << 2,
 } DHCP6FQDNFlag;
 
-const char *dhcp6_state_to_string(DHCP6State s) _const_;
-const char *dhcp6_message_type_to_string(DHCP6MessageType s) _const_;
+const char* dhcp6_state_to_string(DHCP6State s) _const_;
+const char* dhcp6_message_type_to_string(DHCP6MessageType s) _const_;
 DHCP6MessageType dhcp6_message_type_from_string(const char *s) _pure_;
-const char *dhcp6_message_status_to_string(DHCP6Status s) _const_;
+const char* dhcp6_message_status_to_string(DHCP6Status s) _const_;
 DHCP6Status dhcp6_message_status_from_string(const char *s) _pure_;
 int dhcp6_message_status_to_errno(DHCP6Status s);
diff --git a/src/libnm-systemd-core/src/libsystemd-network/network-common.c b/src/libnm-systemd-core/src/libsystemd-network/network-common.c
index 8d62e6ff..f61b11f5 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/network-common.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/network-common.c
@@ -3,7 +3,7 @@
 #include "nm-sd-adapt-core.h"
 
 #include "env-util.h"
-#include "format-util.h"
+#include "format-ifname.h"
 #include "network-common.h"
 #include "socket-util.h"
 #include "unaligned.h"
diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c
index a6b55d07..5655c0d7 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c
@@ -749,8 +749,6 @@ static int client_append_mudurl(sd_dhcp6_client *client, uint8_t **buf, size_t *
 
 int dhcp6_client_send_message(sd_dhcp6_client *client) {
         _cleanup_free_ uint8_t *buf = NULL;
-        struct in6_addr all_servers =
-                IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
         struct sd_dhcp6_option *j;
         usec_t elapsed_usec, time_now;
         be16_t elapsed_time;
@@ -845,7 +843,7 @@ int dhcp6_client_send_message(sd_dhcp6_client *client) {
         if (r < 0)
                 return r;
 
-        r = dhcp6_network_send_udp_socket(client->fd, &all_servers, buf, offset);
+        r = dhcp6_network_send_udp_socket(client->fd, &IN6_ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS, buf, offset);
         if (r < 0)
                 return r;
 
@@ -1336,7 +1334,7 @@ static int client_receive_message(
                 return 0;
         }
         if ((size_t) len < sizeof(DHCP6Message)) {
-                log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring");
+                log_dhcp6_client(client, "Too small to be DHCPv6 message: ignoring");
                 return 0;
         }
 
@@ -1412,7 +1410,7 @@ int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
         r = client_send_release(client);
         if (r < 0)
                 log_dhcp6_client_errno(client, r,
-                                       "Failed to send DHCP6 release message, ignoring: %m");
+                                       "Failed to send DHCPv6 release message, ignoring: %m");
 
         client_stop(client, SD_DHCP6_CLIENT_EVENT_STOP);
 
@@ -1423,7 +1421,8 @@ int sd_dhcp6_client_stop(sd_dhcp6_client *client) {
 }
 
 int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
-        assert_return(client, -EINVAL);
+        if (!client)
+                return false;
 
         return client->state != DHCP6_STATE_STOPPED;
 }
diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c
index a42df243..9569af58 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c
@@ -10,7 +10,9 @@
 #include "alloc-util.h"
 #include "dhcp6-internal.h"
 #include "dhcp6-lease-internal.h"
+#include "dns-domain.h"
 #include "network-common.h"
+#include "sort-util.h"
 #include "strv.h"
 #include "unaligned.h"
 
@@ -426,7 +428,7 @@ int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t
         if (r < 0)
                 return r;
 
-        return strv_extend_strv(&lease->domains, domains, true);
+        return strv_extend_strv_consume(&lease->domains, TAKE_PTR(domains), /* filter_duplicates = */ true);
 }
 
 int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) {
@@ -440,6 +442,105 @@ int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) {
         return strv_length(lease->domains);
 }
 
+#if 0 /* NM_IGNORED */
+static int dhcp6_lease_add_dnr(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
+        int r;
+
+        assert(lease);
+
+        _cleanup_(sd_dns_resolver_done) sd_dns_resolver res = {};
+
+        size_t offset = 0;
+
+        /* priority */
+        if (optlen - offset < sizeof(uint16_t))
+                return -EBADMSG;
+        res.priority = unaligned_read_be16(optval + offset);
+        offset += sizeof(uint16_t);
+
+        /* adn */
+        if (optlen - offset < sizeof(uint16_t))
+                return -EBADMSG;
+        size_t ilen = unaligned_read_be16(optval + offset);
+        offset += sizeof(uint16_t);
+        if (offset + ilen > optlen)
+                return -EBADMSG;
+
+        r = dhcp6_option_parse_domainname(optval + offset, ilen, &res.auth_name);
+        if (r < 0)
+                return r;
+        r = dns_name_is_valid_ldh(res.auth_name);
+        if (r < 0)
+                return r;
+        if (!r)
+                return -EBADMSG;
+        offset += ilen;
+
+        /* RFC9463 § 3.1.6: adn only mode */
+        if (offset == optlen)
+                return 0;
+
+        /* addrs */
+        if (optlen - offset < sizeof(uint16_t))
+                return -EBADMSG;
+        ilen = unaligned_read_be16(optval + offset);
+        offset += sizeof(uint16_t);
+        if (offset + ilen > optlen)
+                return -EBADMSG;
+
+        _cleanup_free_ struct in6_addr *addrs = NULL;
+        size_t n_addrs = 0;
+
+        r = dhcp6_option_parse_addresses(optval + offset, ilen, &addrs, &n_addrs);
+        if (r < 0)
+                return r;
+        if (n_addrs == 0)
+                return -EBADMSG;
+        offset += ilen;
+
+        res.addrs = new(union in_addr_union, n_addrs);
+        if (!res.addrs)
+                return -ENOMEM;
+
+        for (size_t i = 0; i < n_addrs; i++) {
+                union in_addr_union addr = {.in6 = addrs[i]};
+                /* RFC9463 § 6.2 client MUST discard multicast and host loopback addresses */
+                if (in_addr_is_multicast(AF_INET6, &addr) ||
+                    in_addr_is_localhost(AF_INET6, &addr))
+                        return -EBADMSG;
+                res.addrs[i] = addr;
+        }
+        res.n_addrs = n_addrs;
+        res.family = AF_INET6;
+
+        /* svc params */
+        r = dnr_parse_svc_params(optval + offset, optlen-offset, &res);
+        if (r < 0)
+                return r;
+
+        /* Append this resolver */
+        if (!GREEDY_REALLOC(lease->dnr, lease->n_dnr+1))
+                return -ENOMEM;
+
+        lease->dnr[lease->n_dnr++] = TAKE_STRUCT(res);
+
+        typesafe_qsort(lease->dnr, lease->n_dnr, dns_resolver_prio_compare);
+
+        return 1;
+}
+
+int sd_dhcp6_lease_get_dnr(sd_dhcp6_lease *lease, sd_dns_resolver **ret) {
+        assert_return(lease, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        if (!lease->dnr)
+                return -ENODATA;
+
+        *ret = lease->dnr;
+        return lease->n_dnr;
+}
+#endif /* NM_IGNORED */
+
 int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
         int r;
 
@@ -765,8 +866,7 @@ static int dhcp6_lease_parse_message(
                                 continue;
                         }
 
-                        dhcp6_ia_free(lease->ia_na);
-                        lease->ia_na = TAKE_PTR(ia);
+                        free_and_replace_full(lease->ia_na, ia, dhcp6_ia_free);
                         break;
                 }
                 case SD_DHCP6_OPTION_IA_PD: {
@@ -790,8 +890,7 @@ static int dhcp6_lease_parse_message(
                                 continue;
                         }
 
-                        dhcp6_ia_free(lease->ia_pd);
-                        lease->ia_pd = TAKE_PTR(ia);
+                        free_and_replace_full(lease->ia_pd, ia, dhcp6_ia_free);
                         break;
                 }
                 case SD_DHCP6_OPTION_RAPID_COMMIT:
@@ -852,7 +951,16 @@ static int dhcp6_lease_parse_message(
 
                         irt = unaligned_be32_sec_to_usec(optval, /* max_as_infinity = */ false);
                         break;
+#if 0 /* NM_IGNORED */
+                case SD_DHCP6_OPTION_V6_DNR:
+                        r = dhcp6_lease_add_dnr(lease, optval, optlen);
+                        if (r < 0)
+                                return log_dhcp6_client_errno(client, r, "Failed to parse DNR option, ignoring: %m");
+                        if (r == 0)
+                                log_dhcp6_client(client, "Received ADN-only DNRv6 option, ignoring.");
 
+                        break;
+#endif /* NM_IGNORED */
                 case SD_DHCP6_OPTION_VENDOR_OPTS:
                         r = dhcp6_lease_add_vendor_option(lease, optval, optlen);
                         if (r < 0)
@@ -906,6 +1014,9 @@ static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
         dhcp6_ia_free(lease->ia_na);
         dhcp6_ia_free(lease->ia_pd);
         free(lease->dns);
+#if 0 /* NM_IGNORED */
+        dns_resolver_done_many(lease->dnr, lease->n_dnr);
+#endif /* NM_IGNORED */
         free(lease->fqdn);
         free(lease->captive_portal);
         strv_free(lease->domains);
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.c b/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.c
index eef3d618..6b17e772 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.c
@@ -714,7 +714,7 @@ static int device_tag(sd_device *device, const char *tag, bool add) {
         assert(device);
         assert(tag);
 
-        r = device_get_device_id(device, &id);
+        r = sd_device_get_device_id(device, &id);
         if (r < 0)
                 return r;
 
@@ -801,7 +801,7 @@ static int device_get_db_path(sd_device *device, char **ret) {
         assert(device);
         assert(ret);
 
-        r = device_get_device_id(device, &id);
+        r = sd_device_get_device_id(device, &id);
         if (r < 0)
                 return r;
 
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.h b/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.h
index e1f3b6e8..eab54203 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.h
+++ b/src/libnm-systemd-core/src/libsystemd/sd-device/device-private.h
@@ -20,10 +20,12 @@ int device_opendir(sd_device *device, const char *subdir, DIR **ret);
 int device_get_property_bool(sd_device *device, const char *key);
 int device_get_property_int(sd_device *device, const char *key, int *ret);
 int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value);
-int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value);
+int device_get_sysattr_unsigned_full(sd_device *device, const char *sysattr, unsigned base, unsigned *ret_value);
+static inline int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value) {
+        return device_get_sysattr_unsigned_full(device, sysattr, 0, ret_value);
+}
 int device_get_sysattr_u32(sd_device *device, const char *sysattr, uint32_t *ret_value);
 int device_get_sysattr_bool(sd_device *device, const char *sysattr);
-int device_get_device_id(sd_device *device, const char **ret);
 int device_get_devlink_priority(sd_device *device, int *ret);
 int device_get_devnode_mode(sd_device *device, mode_t *ret);
 int device_get_devnode_uid(sd_device *device, uid_t *ret);
@@ -73,5 +75,5 @@ int device_read_uevent_file(sd_device *device);
 
 int device_set_action(sd_device *device, sd_device_action_t a);
 sd_device_action_t device_action_from_string(const char *s) _pure_;
-const char *device_action_to_string(sd_device_action_t a) _const_;
+const char* device_action_to_string(sd_device_action_t a) _const_;
 void dump_device_action_table(void);
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h b/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h
index 534a2967..b17993d5 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h
+++ b/src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h
@@ -100,7 +100,7 @@ static inline int devname_from_stat_rdev(const struct stat *st, char **ret) {
         assert(st);
         return devname_from_devnum(st->st_mode, st->st_rdev, ret);
 }
-int device_open_from_devnum(mode_t mode, dev_t devnum, int flags, char **ret);
+int device_open_from_devnum(mode_t mode, dev_t devnum, int flags, char **ret_devname);
 
 char** device_make_log_fields(sd_device *device);
 
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-device/sd-device.c b/src/libnm-systemd-core/src/libsystemd/sd-device/sd-device.c
index 0ca87146..480223d0 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-device/sd-device.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-device/sd-device.c
@@ -243,7 +243,7 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
 
                 r = path_simplify_alloc(_syspath, &syspath);
                 if (r < 0)
-                        return r;
+                        return log_oom_debug();
         }
 
         assert_se(devpath = startswith(syspath, "/sys"));
@@ -354,9 +354,11 @@ _public_ int sd_device_new_from_ifname(sd_device **ret, const char *ifname) {
         assert_return(ret, -EINVAL);
         assert_return(ifname, -EINVAL);
 
-        r = device_new_from_main_ifname(ret, ifname);
-        if (r >= 0)
-                return r;
+        if (ifname_valid(ifname)) {
+                r = device_new_from_main_ifname(ret, ifname);
+                if (r >= 0)
+                        return r;
+        }
 
         r = rtnl_resolve_ifname_full(NULL, RESOLVE_IFNAME_ALTERNATIVE | RESOLVE_IFNAME_NUMERIC, ifname, &main_name, NULL);
         if (r < 0)
@@ -393,25 +395,79 @@ _public_ int sd_device_new_from_ifindex(sd_device **ret, int ifindex) {
         return 0;
 }
 
-static int device_strjoin_new(
+static int device_new_from_path_join(
+                sd_device **device,
+                const char *subsystem,
+                const char *driver_subsystem,
+                const char *sysname,
                 const char *a,
                 const char *b,
                 const char *c,
-                const char *d,
-                sd_device **ret) {
+                const char *d) {
 
-        const char *p;
+        _cleanup_(sd_device_unrefp) sd_device *new_device = NULL;
+        _cleanup_free_ char *p = NULL;
         int r;
 
-        p = strjoina(a, b, c, d);
-        if (access(p, F_OK) < 0)
-                return IN_SET(errno, ENOENT, ENAMETOOLONG) ? 0 : -errno; /* If this sysfs is too long then it doesn't exist either */
+        assert(device);
+        assert(sysname);
+
+        p = path_join(a, b, c, d);
+        if (!p)
+                return -ENOMEM;
+
+        r = sd_device_new_from_syspath(&new_device, p);
+        if (r == -ENODEV)
+                return 0;
+        if (r < 0)
+                return r;
+
+        /* Check if the found device really has the expected subsystem and sysname, for safety. */
+        if (!device_in_subsystem(new_device, subsystem))
+                return 0;
+
+        const char *new_driver_subsystem = NULL;
+        (void) sd_device_get_driver_subsystem(new_device, &new_driver_subsystem);
+
+        if (!streq_ptr(driver_subsystem, new_driver_subsystem))
+                return 0;
+
+        const char *new_sysname;
+        r = sd_device_get_sysname(new_device, &new_sysname);
+        if (r < 0)
+                return r;
+
+        if (!streq(sysname, new_sysname))
+                return 0;
+
+        /* If this is the first device we found, then take it. */
+        if (!*device) {
+                *device = TAKE_PTR(new_device);
+                return 1;
+        }
+
+        /* Unfortunately, (subsystem, sysname) pair is not unique. For examples,
+         *   - /sys/bus/gpio and /sys/class/gpio, both have gpiochip%N. However, these point to different devpaths.
+         *   - /sys/bus/mdio_bus and /sys/class/mdio_bus,
+         *   - /sys/bus/mei and /sys/class/mei,
+         *   - /sys/bus/typec and /sys/class/typec, and so on.
+         * Hence, if we already know a device, then we need to check if it is equivalent to the newly found one. */
+
+        const char *devpath, *new_devpath;
+        r = sd_device_get_devpath(*device, &devpath);
+        if (r < 0)
+                return r;
 
-        r = sd_device_new_from_syspath(ret, p);
+        r = sd_device_get_devpath(new_device, &new_devpath);
         if (r < 0)
                 return r;
 
-        return 1;
+        if (!streq(devpath, new_devpath))
+                return log_debug_errno(SYNTHETIC_ERRNO(ETOOMANYREFS),
+                                       "sd-device: found multiple devices for subsystem=%s and sysname=%s, refusing: %s, %s",
+                                       subsystem, sysname, devpath, new_devpath);
+
+        return 1; /* Fortunately, they are consistent. */
 }
 
 _public_ int sd_device_new_from_subsystem_sysname(
@@ -419,6 +475,7 @@ _public_ int sd_device_new_from_subsystem_sysname(
                 const char *subsystem,
                 const char *sysname) {
 
+        _cleanup_(sd_device_unrefp) sd_device *device = NULL;
         char *name;
         int r;
 
@@ -437,19 +494,15 @@ _public_ int sd_device_new_from_subsystem_sysname(
 
         if (streq(subsystem, "subsystem")) {
                 FOREACH_STRING(s, "/sys/bus/", "/sys/class/") {
-                        r = device_strjoin_new(s, name, NULL, NULL, ret);
+                        r = device_new_from_path_join(&device, subsystem, /* driver_subsystem = */ NULL, sysname, s, name, NULL, NULL);
                         if (r < 0)
                                 return r;
-                        if (r > 0)
-                                return 0;
                 }
 
         } else if (streq(subsystem, "module")) {
-                r = device_strjoin_new("/sys/module/", name, NULL, NULL, ret);
+                r = device_new_from_path_join(&device, subsystem, /* driver_subsystem = */ NULL, sysname, "/sys/module/", name, NULL, NULL);
                 if (r < 0)
                         return r;
-                if (r > 0)
-                        return 0;
 
         } else if (streq(subsystem, "drivers")) {
                 const char *sep;
@@ -461,35 +514,33 @@ _public_ int sd_device_new_from_subsystem_sysname(
                         sep++;
 
                         if (streq(sep, "drivers")) /* If the sysname is "drivers", then it's the drivers directory itself that is meant. */
-                                r = device_strjoin_new("/sys/bus/", subsys, "/drivers", NULL, ret);
+                                r = device_new_from_path_join(&device, subsystem, subsys, "drivers", "/sys/bus/", subsys, "/drivers", NULL);
                         else
-                                r = device_strjoin_new("/sys/bus/", subsys, "/drivers/", sep, ret);
+                                r = device_new_from_path_join(&device, subsystem, subsys, sep, "/sys/bus/", subsys, "/drivers/", sep);
                         if (r < 0)
                                 return r;
-                        if (r > 0)
-                                return 0;
                 }
         }
 
-        r = device_strjoin_new("/sys/bus/", subsystem, "/devices/", name, ret);
+        r = device_new_from_path_join(&device, subsystem, /* driver_subsystem = */ NULL, sysname, "/sys/bus/", subsystem, "/devices/", name);
         if (r < 0)
                 return r;
-        if (r > 0)
-                return 0;
 
-        r = device_strjoin_new("/sys/class/", subsystem, "/", name, ret);
+        r = device_new_from_path_join(&device, subsystem, /* driver_subsystem = */ NULL, sysname, "/sys/class/", subsystem, name, NULL);
         if (r < 0)
                 return r;
-        if (r > 0)
-                return 0;
 
-        r = device_strjoin_new("/sys/firmware/", subsystem, "/", name, ret);
+        /* Note that devices under /sys/firmware/ (e.g. /sys/firmware/devicetree/base/) do not have
+         * subsystem. Hence, pass NULL for subsystem. See issue #35861. */
+        r = device_new_from_path_join(&device, /* subsystem = */ NULL, /* driver_subsystem = */ NULL, sysname, "/sys/firmware/", subsystem, name, NULL);
         if (r < 0)
                 return r;
-        if (r > 0)
-                return 0;
 
-        return -ENODEV;
+        if (!device)
+                return -ENODEV;
+
+        *ret = TAKE_PTR(device);
+        return 0;
 }
 
 _public_ int sd_device_new_from_stat_rdev(sd_device **ret, const struct stat *st) {
@@ -499,10 +550,8 @@ _public_ int sd_device_new_from_stat_rdev(sd_device **ret, const struct stat *st
         return device_new_from_mode_and_devnum(ret, st->st_mode, st->st_rdev);
 }
 
-_public_ int sd_device_new_from_devname(sd_device **ret, const char *devname) {
-        struct stat st;
-        dev_t devnum;
-        mode_t mode;
+static int device_new_from_devname(sd_device **ret, const char *devname, bool strict) {
+        int r;
 
         assert_return(ret, -EINVAL);
         assert_return(devname, -EINVAL);
@@ -510,28 +559,41 @@ _public_ int sd_device_new_from_devname(sd_device **ret, const char *devname) {
         /* This function actually accepts both devlinks and devnames, i.e. both symlinks and device
          * nodes below /dev/. */
 
-        /* Also ignore when the specified path is "/dev". */
-        if (isempty(path_startswith(devname, "/dev")))
+        if (strict && isempty(path_startswith(devname, "/dev/")))
                 return -EINVAL;
 
+        dev_t devnum;
+        mode_t mode;
         if (device_path_parse_major_minor(devname, &mode, &devnum) >= 0)
                 /* Let's shortcut when "/dev/block/maj:min" or "/dev/char/maj:min" is specified.
                  * In that case, we can directly convert the path to syspath, hence it is not necessary
                  * that the specified path exists. So, this works fine without udevd being running. */
                 return device_new_from_mode_and_devnum(ret, mode, devnum);
 
-        if (stat(devname, &st) < 0)
-                return ERRNO_IS_DEVICE_ABSENT(errno) ? -ENODEV : -errno;
+        _cleanup_free_ char *resolved = NULL;
+        struct stat st;
+        r = chase_and_stat(devname, /* root = */ NULL, /* flags = */ 0, &resolved, &st);
+        if (ERRNO_IS_NEG_DEVICE_ABSENT(r))
+                return -ENODEV;
+        if (r < 0)
+                return r;
+
+        if (isempty(path_startswith(resolved, "/dev/")))
+                return -EINVAL;
 
         return sd_device_new_from_stat_rdev(ret, &st);
 }
 
+_public_ int sd_device_new_from_devname(sd_device **ret, const char *devname) {
+        return device_new_from_devname(ret, devname, /* strict = */ true);
+}
+
 _public_ int sd_device_new_from_path(sd_device **ret, const char *path) {
         assert_return(ret, -EINVAL);
         assert_return(path, -EINVAL);
 
-        if (path_startswith(path, "/dev"))
-                return sd_device_new_from_devname(ret, path);
+        if (device_new_from_devname(ret, path, /* strict = */ false) >= 0)
+                return 0;
 
         return device_new_from_syspath(ret, path, /* strict = */ false);
 }
@@ -1211,6 +1273,20 @@ _public_ int sd_device_get_subsystem(sd_device *device, const char **ret) {
 }
 
 #if 0 /* NM_IGNORED */
+_public_ int sd_device_get_driver_subsystem(sd_device *device, const char **ret) {
+        assert_return(device, -EINVAL);
+
+        if (!device_in_subsystem(device, "drivers"))
+                return -ENOENT;
+
+        assert(device->driver_subsystem);
+
+        if (ret)
+                *ret = device->driver_subsystem;
+
+        return 0;
+}
+
 _public_ int sd_device_get_devtype(sd_device *device, const char **devtype) {
         int r;
 
@@ -1226,7 +1302,7 @@ _public_ int sd_device_get_devtype(sd_device *device, const char **devtype) {
         if (devtype)
                 *devtype = device->devtype;
 
-        return !!device->devtype;
+        return 0;
 }
 
 _public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *device, const char *subsystem, const char *devtype, sd_device **ret) {
@@ -1645,9 +1721,8 @@ static int handle_db_line(sd_device *device, char key, const char *value) {
         }
 }
 
-int device_get_device_id(sd_device *device, const char **ret) {
-        assert(device);
-        assert(ret);
+_public_ int sd_device_get_device_id(sd_device *device, const char **ret) {
+        assert_return(device, -EINVAL);
 
         if (!device->device_id) {
                 _cleanup_free_ char *id = NULL;
@@ -1697,7 +1772,8 @@ int device_get_device_id(sd_device *device, const char **ret) {
                 device->device_id = TAKE_PTR(id);
         }
 
-        *ret = device->device_id;
+        if (ret)
+                *ret = device->device_id;
         return 0;
 }
 
@@ -2428,7 +2504,7 @@ int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_valu
         return v > 0;
 }
 
-int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value) {
+int device_get_sysattr_unsigned_full(sd_device *device, const char *sysattr, unsigned base, unsigned *ret_value) {
         const char *value;
         int r;
 
@@ -2437,7 +2513,7 @@ int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned
                 return r;
 
         unsigned v;
-        r = safe_atou(value, &v);
+        r = safe_atou_full(value, base, &v);
         if (r < 0)
                 return log_device_debug_errno(device, r, "Failed to parse '%s' attribute: %m", sysattr);
 
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/event-source.h b/src/libnm-systemd-core/src/libsystemd/sd-event/event-source.h
index f4e38d78..d05bcf05 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-event/event-source.h
+++ b/src/libnm-systemd-core/src/libsystemd/sd-event/event-source.h
@@ -189,6 +189,9 @@ struct inode_data {
          * iteration. */
         int fd;
 
+        /* The path that the fd points to. The field is optional. */
+        char *path;
+
         /* The inotify "watch descriptor" */
         int wd;
 
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c
index ef0c2d2a..ac986e48 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c
@@ -171,4 +171,13 @@ int event_add_child_pidref(
 
         return sd_event_add_child(e, s, pid->pid, options, callback, userdata);
 }
+
+dual_timestamp* event_dual_timestamp_now(sd_event *e, dual_timestamp *ts) {
+        assert(e);
+        assert(ts);
+
+        assert_se(sd_event_now(e, CLOCK_REALTIME, &ts->realtime) >= 0);
+        assert_se(sd_event_now(e, CLOCK_MONOTONIC, &ts->monotonic) >= 0);
+        return ts;
+}
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h
index ad0f2e78..c0db014f 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h
+++ b/src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h
@@ -37,4 +37,6 @@ int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handle
 
 #if 0 /* NM_IGNORED */
 int event_add_child_pidref(sd_event *e, sd_event_source **s, const PidRef *pid, int options, sd_event_child_handler_t callback, void *userdata);
+
+dual_timestamp* event_dual_timestamp_now(sd_event *e, dual_timestamp *ts);
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c
index 6449b85c..b345b145 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c
@@ -27,9 +27,11 @@
 #include "missing_magic.h"
 #include "missing_syscall.h"
 #include "missing_threads.h"
+#include "missing_wait.h"
 #include "origin-id.h"
 #include "path-util.h"
 #include "prioq.h"
+#include "pidfd-util.h"
 #include "process-util.h"
 #include "psi-util.h"
 #include "set.h"
@@ -184,7 +186,7 @@ static thread_local sd_event *default_event = NULL;
 static void source_disconnect(sd_event_source *s);
 static void event_gc_inode_data(sd_event *e, struct inode_data *d);
 
-static sd_event *event_resolve(sd_event *e) {
+static sd_event* event_resolve(sd_event *e) {
         return e == SD_EVENT_DEFAULT ? default_event : e;
 }
 
@@ -340,7 +342,7 @@ static void free_clock_data(struct clock_data *d) {
         prioq_free(d->latest);
 }
 
-static sd_event *event_free(sd_event *e) {
+static sd_event* event_free(sd_event *e) {
         sd_event_source *s;
 
         assert(e);
@@ -445,7 +447,7 @@ fail:
 }
 
 /* Define manually so we can add the origin check */
-_public_ sd_event *sd_event_ref(sd_event *e) {
+_public_ sd_event* sd_event_ref(sd_event *e) {
         if (!e)
                 return NULL;
         if (event_origin_changed(e))
@@ -473,8 +475,13 @@ _public_ sd_event* sd_event_unref(sd_event *e) {
         _unused_ _cleanup_(sd_event_unrefp) sd_event *_ref = sd_event_ref(e);
 
 _public_ sd_event_source* sd_event_source_disable_unref(sd_event_source *s) {
-        if (s)
-                (void) sd_event_source_set_enabled(s, SD_EVENT_OFF);
+        int r;
+
+        r = sd_event_source_set_enabled(s, SD_EVENT_OFF);
+        if (r < 0)
+                log_debug_errno(r, "Failed to disable event source %p (%s): %m",
+                                s, strna(s->description));
+
         return sd_event_source_unref(s);
 }
 
@@ -1072,6 +1079,8 @@ static void source_disconnect(sd_event_source *s) {
 }
 
 static sd_event_source* source_free(sd_event_source *s) {
+        int r;
+
         assert(s);
 
         source_disconnect(s);
@@ -1085,31 +1094,23 @@ static sd_event_source* source_free(sd_event_source *s) {
                 if (s->child.process_owned) {
 
                         if (!s->child.exited) {
-                                bool sent = false;
-
-                                if (s->child.pidfd >= 0) {
-                                        if (pidfd_send_signal(s->child.pidfd, SIGKILL, NULL, 0) < 0) {
-                                                if (errno == ESRCH) /* Already dead */
-                                                        sent = true;
-                                                else if (!ERRNO_IS_NOT_SUPPORTED(errno))
-                                                        log_debug_errno(errno, "Failed to kill process " PID_FMT " via pidfd_send_signal(), re-trying via kill(): %m",
-                                                                        s->child.pid);
-                                        } else
-                                                sent = true;
-                                }
-
-                                if (!sent)
-                                        if (kill(s->child.pid, SIGKILL) < 0)
-                                                if (errno != ESRCH) /* Already dead */
-                                                        log_debug_errno(errno, "Failed to kill process " PID_FMT " via kill(), ignoring: %m",
-                                                                        s->child.pid);
+                                if (s->child.pidfd >= 0)
+                                        r = RET_NERRNO(pidfd_send_signal(s->child.pidfd, SIGKILL, NULL, 0));
+                                else
+                                        r = RET_NERRNO(kill(s->child.pid, SIGKILL));
+                                if (r < 0 && r != -ESRCH)
+                                        log_debug_errno(r, "Failed to kill process " PID_FMT ", ignoring: %m",
+                                                        s->child.pid);
                         }
 
                         if (!s->child.waited) {
                                 siginfo_t si = {};
 
                                 /* Reap the child if we can */
-                                (void) waitid(P_PID, s->child.pid, &si, WEXITED);
+                                if (s->child.pidfd >= 0)
+                                        (void) waitid(P_PIDFD, s->child.pidfd, &si, WEXITED);
+                                else
+                                        (void) waitid(P_PID, s->child.pid, &si, WEXITED);
                         }
                 }
 
@@ -1179,7 +1180,7 @@ static int source_set_pending(sd_event_source *s, bool b) {
         return 1;
 }
 
-static sd_event_source *source_new(sd_event *e, bool floating, EventSourceType type) {
+static sd_event_source* source_new(sd_event *e, bool floating, EventSourceType type) {
 
         /* Let's allocate exactly what we need. Note that the difference of the smallest event source
          * structure to the largest is 144 bytes on x86-64 at the time of writing, i.e. more than two cache
@@ -1577,11 +1578,6 @@ static int child_exit_callback(sd_event_source *s, const siginfo_t *si, void *us
         return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata));
 }
 
-static bool shall_use_pidfd(void) {
-        /* Mostly relevant for debugging, i.e. this is used in test-event.c to test the event loop once with and once without pidfd */
-        return secure_getenv_bool("SYSTEMD_PIDFD") != 0;
-}
-
 _public_ int sd_event_add_child(
                 sd_event *e,
                 sd_event_source **ret,
@@ -1629,34 +1625,29 @@ _public_ int sd_event_add_child(
         if (!s)
                 return -ENOMEM;
 
+        /* We always take a pidfd here if we can, even if we wait for anything else than WEXITED, so that we
+         * pin the PID, and make regular waitid() handling race-free. */
+
+        s->child.pidfd = pidfd_open(pid, 0);
+        if (s->child.pidfd < 0)
+                return -errno;
+
+        s->child.pidfd_owned = true; /* If we allocate the pidfd we own it by default */
+
         s->wakeup = WAKEUP_EVENT_SOURCE;
         s->child.options = options;
         s->child.callback = callback;
         s->userdata = userdata;
         s->enabled = SD_EVENT_ONESHOT;
 
-        /* We always take a pidfd here if we can, even if we wait for anything else than WEXITED, so that we
-         * pin the PID, and make regular waitid() handling race-free. */
-
-        if (shall_use_pidfd()) {
-                s->child.pidfd = pidfd_open(pid, 0);
-                if (s->child.pidfd < 0) {
-                        /* Propagate errors unless the syscall is not supported or blocked */
-                        if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
-                                return -errno;
-                } else
-                        s->child.pidfd_owned = true; /* If we allocate the pidfd we own it by default */
-        } else
-                s->child.pidfd = -EBADF;
-
         if (EVENT_SOURCE_WATCH_PIDFD(s)) {
-                /* We have a pidfd and we only want to watch for exit */
+                /* We only want to watch for exit */
                 r = source_child_pidfd_register(s, s->enabled);
                 if (r < 0)
                         return r;
 
         } else {
-                /* We have no pidfd or we shall wait for some other event than WEXITED */
+                /* We shall wait for some other event than WEXITED */
                 r = event_make_signal_data(e, SIGCHLD, NULL);
                 if (r < 0)
                         return r;
@@ -1686,7 +1677,6 @@ _public_ int sd_event_add_child_pidfd(
                 sd_event_child_handler_t callback,
                 void *userdata) {
 
-
         _cleanup_(source_freep) sd_event_source *s = NULL;
         pid_t pid;
         int r;
@@ -1727,17 +1717,12 @@ _public_ int sd_event_add_child_pidfd(
 
         s->wakeup = WAKEUP_EVENT_SOURCE;
         s->child.pidfd = pidfd;
-        s->child.pid = pid;
         s->child.options = options;
         s->child.callback = callback;
         s->child.pidfd_owned = false; /* If we got the pidfd passed in we don't own it by default (similar to the IO fd case) */
         s->userdata = userdata;
         s->enabled = SD_EVENT_ONESHOT;
 
-        r = hashmap_put(e->child_sources, PID_TO_PTR(pid), s);
-        if (r < 0)
-                return r;
-
         if (EVENT_SOURCE_WATCH_PIDFD(s)) {
                 /* We only want to watch for WEXITED */
                 r = source_child_pidfd_register(s, s->enabled);
@@ -1752,6 +1737,11 @@ _public_ int sd_event_add_child_pidfd(
                 e->need_process_child = true;
         }
 
+        r = hashmap_put(e->child_sources, PID_TO_PTR(pid), s);
+        if (r < 0)
+                return r;
+
+        s->child.pid = pid;
         e->n_online_child_sources++;
 
         if (ret)
@@ -2280,6 +2270,7 @@ static void event_free_inode_data(
                 assert_se(hashmap_remove(d->inotify_data->inodes, d) == d);
         }
 
+        free(d->path);
         free(d);
 }
 
@@ -2423,7 +2414,7 @@ static int inode_data_realize_watch(sd_event *e, struct inode_data *d) {
 
         wd = inotify_add_watch_fd(d->inotify_data->fd, d->fd, combined_mask);
         if (wd < 0)
-                return -errno;
+                return wd;
 
         if (d->wd < 0) {
                 r = hashmap_put(d->inotify_data->wd, INT_TO_PTR(wd), d);
@@ -2445,6 +2436,7 @@ static int inode_data_realize_watch(sd_event *e, struct inode_data *d) {
         return 1;
 }
 
+#if 0 /* NM_IGNORED */
 static int inotify_exit_callback(sd_event_source *s, const struct inotify_event *event, void *userdata) {
         assert(s);
 
@@ -2520,6 +2512,15 @@ static int event_add_inotify_fd_internal(
                 }
 
                 LIST_PREPEND(to_close, e->inode_data_to_close_list, inode_data);
+
+                _cleanup_free_ char *path = NULL;
+                r = fd_get_path(inode_data->fd, &path);
+                if (r < 0 && r != -ENOSYS) { /* The path is optional, hence ignore -ENOSYS. */
+                        event_gc_inode_data(e, inode_data);
+                        return r;
+                }
+
+                free_and_replace(inode_data->path, path);
         }
 
         /* Link our event source to the inode data object */
@@ -2579,6 +2580,7 @@ _public_ int sd_event_add_inotify(
 
         return r;
 }
+#endif /* NM_IGNORED */
 
 static sd_event_source* event_source_free(sd_event_source *s) {
         if (!s)
@@ -2609,18 +2611,18 @@ _public_ int sd_event_source_set_description(sd_event_source *s, const char *des
         return free_and_strdup(&s->description, description);
 }
 
-_public_ int sd_event_source_get_description(sd_event_source *s, const char **description) {
+_public_ int sd_event_source_get_description(sd_event_source *s, const char **ret) {
         assert_return(s, -EINVAL);
-        assert_return(description, -EINVAL);
+        assert_return(ret, -EINVAL);
 
         if (!s->description)
                 return -ENXIO;
 
-        *description = s->description;
+        *ret = s->description;
         return 0;
 }
 
-_public_ sd_event *sd_event_source_get_event(sd_event_source *s) {
+_public_ sd_event* sd_event_source_get_event(sd_event_source *s) {
         assert_return(s, NULL);
         assert_return(!event_origin_changed(s->event), NULL);
 
@@ -2645,7 +2647,7 @@ _public_ int sd_event_source_get_io_fd(sd_event_source *s) {
 }
 
 _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
-        int r;
+        int saved_fd, r;
 
         assert_return(s, -EINVAL);
         assert_return(fd >= 0, -EBADF);
@@ -2655,16 +2657,12 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
         if (s->io.fd == fd)
                 return 0;
 
-        if (event_source_is_offline(s)) {
-                s->io.fd = fd;
-                s->io.registered = false;
-        } else {
-                int saved_fd;
+        saved_fd = s->io.fd;
+        s->io.fd = fd;
 
-                saved_fd = s->io.fd;
-                assert(s->io.registered);
+        assert(event_source_is_offline(s) == !s->io.registered);
 
-                s->io.fd = fd;
+        if (s->io.registered) {
                 s->io.registered = false;
 
                 r = source_io_register(s, s->enabled, s->io.events);
@@ -2677,6 +2675,9 @@ _public_ int sd_event_source_set_io_fd(sd_event_source *s, int fd) {
                 (void) epoll_ctl(s->event->epoll_fd, EPOLL_CTL_DEL, saved_fd, NULL);
         }
 
+        if (s->io.owned)
+                safe_close(saved_fd);
+
         return 0;
 }
 
@@ -2697,13 +2698,13 @@ _public_ int sd_event_source_set_io_fd_own(sd_event_source *s, int own) {
         return 0;
 }
 
-_public_ int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events) {
+_public_ int sd_event_source_get_io_events(sd_event_source *s, uint32_t *ret) {
         assert_return(s, -EINVAL);
-        assert_return(events, -EINVAL);
+        assert_return(ret, -EINVAL);
         assert_return(s->type == SOURCE_IO, -EDOM);
         assert_return(!event_origin_changed(s->event), -ECHILD);
 
-        *events = s->io.events;
+        *ret = s->io.events;
         return 0;
 }
 
@@ -2735,14 +2736,14 @@ _public_ int sd_event_source_set_io_events(sd_event_source *s, uint32_t events)
         return 0;
 }
 
-_public_ int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revents) {
+_public_ int sd_event_source_get_io_revents(sd_event_source *s, uint32_t *ret) {
         assert_return(s, -EINVAL);
-        assert_return(revents, -EINVAL);
+        assert_return(ret, -EINVAL);
         assert_return(s->type == SOURCE_IO, -EDOM);
         assert_return(s->pending, -ENODATA);
         assert_return(!event_origin_changed(s->event), -ECHILD);
 
-        *revents = s->io.revents;
+        *ret = s->io.revents;
         return 0;
 }
 
@@ -2754,11 +2755,12 @@ _public_ int sd_event_source_get_signal(sd_event_source *s) {
         return s->signal.sig;
 }
 
-_public_ int sd_event_source_get_priority(sd_event_source *s, int64_t *priority) {
+_public_ int sd_event_source_get_priority(sd_event_source *s, int64_t *ret) {
         assert_return(s, -EINVAL);
+        assert_return(ret, -EINVAL);
         assert_return(!event_origin_changed(s->event), -ECHILD);
 
-        *priority = s->priority;
+        *ret = s->priority;
         return 0;
 }
 
@@ -2806,6 +2808,13 @@ _public_ int sd_event_source_set_priority(sd_event_source *s, int64_t priority)
                         }
 
                         LIST_PREPEND(to_close, s->event->inode_data_to_close_list, new_inode_data);
+
+                        _cleanup_free_ char *path = NULL;
+                        r = fd_get_path(new_inode_data->fd, &path);
+                        if (r < 0 && r != -ENOSYS)
+                                goto fail;
+
+                        free_and_replace(new_inode_data->path, path);
                 }
 
                 /* Move the event source to the new inode data structure */
@@ -3092,13 +3101,13 @@ _public_ int sd_event_source_set_enabled(sd_event_source *s, int m) {
         return 0;
 }
 
-_public_ int sd_event_source_get_time(sd_event_source *s, uint64_t *usec) {
+_public_ int sd_event_source_get_time(sd_event_source *s, uint64_t *ret) {
         assert_return(s, -EINVAL);
-        assert_return(usec, -EINVAL);
+        assert_return(ret, -EINVAL);
         assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM);
         assert_return(!event_origin_changed(s->event), -ECHILD);
 
-        *usec = s->time.next;
+        *ret = s->time.next;
         return 0;
 }
 
@@ -3142,13 +3151,13 @@ _public_ int sd_event_source_set_time_relative(sd_event_source *s, uint64_t usec
         return sd_event_source_set_time(s, usec);
 }
 
-_public_ int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *usec) {
+_public_ int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *ret) {
         assert_return(s, -EINVAL);
-        assert_return(usec, -EINVAL);
+        assert_return(ret, -EINVAL);
         assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM);
         assert_return(!event_origin_changed(s->event), -ECHILD);
 
-        *usec = s->time.accuracy;
+        *ret = s->time.accuracy;
         return 0;
 }
 
@@ -3174,23 +3183,23 @@ _public_ int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec
         return 0;
 }
 
-_public_ int sd_event_source_get_time_clock(sd_event_source *s, clockid_t *clock) {
+_public_ int sd_event_source_get_time_clock(sd_event_source *s, clockid_t *ret) {
         assert_return(s, -EINVAL);
-        assert_return(clock, -EINVAL);
+        assert_return(ret, -EINVAL);
         assert_return(EVENT_SOURCE_IS_TIME(s->type), -EDOM);
         assert_return(!event_origin_changed(s->event), -ECHILD);
 
-        *clock = event_source_type_to_clock(s->type);
+        *ret = event_source_type_to_clock(s->type);
         return 0;
 }
 
-_public_ int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid) {
+_public_ int sd_event_source_get_child_pid(sd_event_source *s, pid_t *ret) {
         assert_return(s, -EINVAL);
-        assert_return(pid, -EINVAL);
+        assert_return(ret, -EINVAL);
         assert_return(s->type == SOURCE_CHILD, -EDOM);
         assert_return(!event_origin_changed(s->event), -ECHILD);
 
-        *pid = s->child.pid;
+        *ret = s->child.pid;
         return 0;
 }
 
@@ -3225,12 +3234,10 @@ _public_ int sd_event_source_send_child_signal(sd_event_source *s, int sig, cons
                 if (si)
                         copy = *si;
 
-                if (pidfd_send_signal(s->child.pidfd, sig, si ? &copy : NULL, 0) < 0) {
-                        /* Let's propagate the error only if the system call is not implemented or prohibited */
-                        if (!ERRNO_IS_NOT_SUPPORTED(errno) && !ERRNO_IS_PRIVILEGE(errno))
-                                return -errno;
-                } else
-                        return 0;
+                if (pidfd_send_signal(s->child.pidfd, sig, si ? &copy : NULL, 0) < 0)
+                        return -errno;
+
+                return 0;
         }
 
         /* Flags are only supported for pidfd_send_signal(), not for rt_sigqueueinfo(), hence let's refuse
@@ -3290,13 +3297,29 @@ _public_ int sd_event_source_set_child_process_own(sd_event_source *s, int own)
         return 0;
 }
 
-_public_ int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *mask) {
+_public_ int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *ret) {
+        assert_return(s, -EINVAL);
+        assert_return(ret, -EINVAL);
+        assert_return(s->type == SOURCE_INOTIFY, -EDOM);
+        assert_return(!event_origin_changed(s->event), -ECHILD);
+
+        *ret = s->inotify.mask;
+        return 0;
+}
+
+_public_ int sd_event_source_get_inotify_path(sd_event_source *s, const char **ret) {
         assert_return(s, -EINVAL);
-        assert_return(mask, -EINVAL);
+        assert_return(ret, -EINVAL);
         assert_return(s->type == SOURCE_INOTIFY, -EDOM);
         assert_return(!event_origin_changed(s->event), -ECHILD);
 
-        *mask = s->inotify.mask;
+        if (!s->inotify.inode_data)
+                return -ESTALE; /* already disconnected. */
+
+        if (!s->inotify.inode_data->path)
+                return -ENOSYS; /* /proc was not mounted? */
+
+        *ret = s->inotify.inode_data->path;
         return 0;
 }
 
@@ -3839,7 +3862,8 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events, i
                 if (_unlikely_(n != sizeof(si)))
                         return -EIO;
 
-                assert(SIGNAL_VALID(si.ssi_signo));
+                if (_unlikely_(!SIGNAL_VALID(si.ssi_signo)))
+                        return -EIO;
 
                 if (e->signal_sources)
                         s = e->signal_sources[si.ssi_signo];
@@ -4540,7 +4564,7 @@ static int epoll_wait_usec(
         /* epoll_pwait2() was added to Linux 5.11 (2021-02-14) and to glibc in 2.35 (2022-02-03). In contrast
          * to other syscalls we don't bother with our own fallback syscall wrappers on old libcs, since this
          * is not that obvious to implement given the libc and kernel definitions differ in the last
-         * argument. Moreover, the only reason to use it is the more accurate time-outs (which is not a
+         * argument. Moreover, the only reason to use it is the more accurate timeouts (which is not a
          * biggie), let's hence rely on glibc's definitions, and fallback to epoll_pwait() when that's
          * missing. */
 
@@ -4825,13 +4849,13 @@ _public_ int sd_event_dispatch(sd_event *e) {
 
 static void event_log_delays(sd_event *e) {
         char b[ELEMENTSOF(e->delays) * DECIMAL_STR_MAX(unsigned) + 1], *p;
-        size_t l, i;
+        size_t l;
 
         p = b;
         l = sizeof(b);
-        for (i = 0; i < ELEMENTSOF(e->delays); i++) {
-                l = strpcpyf(&p, l, "%u ", e->delays[i]);
-                e->delays[i] = 0;
+        FOREACH_ELEMENT(delay, e->delays) {
+                l = strpcpyf(&p, l, "%u ", *delay);
+                *delay = 0;
         }
         log_debug("Event loop iterations: %s", b);
 }
@@ -4892,7 +4916,6 @@ _public_ int sd_event_loop(sd_event *e) {
         assert_return(!event_origin_changed(e), -ECHILD);
         assert_return(e->state == SD_EVENT_INITIAL, -EBUSY);
 
-
         PROTECT_EVENT(e);
 
         while (e->state != SD_EVENT_FINISHED) {
@@ -4920,7 +4943,7 @@ _public_ int sd_event_get_state(sd_event *e) {
         return e->state;
 }
 
-_public_ int sd_event_get_exit_code(sd_event *e, int *code) {
+_public_ int sd_event_get_exit_code(sd_event *e, int *ret) {
         assert_return(e, -EINVAL);
         assert_return(e = event_resolve(e), -ENOPKG);
         assert_return(!event_origin_changed(e), -ECHILD);
@@ -4928,8 +4951,8 @@ _public_ int sd_event_get_exit_code(sd_event *e, int *code) {
         if (!e->exit_requested)
                 return -ENODATA;
 
-        if (code)
-                *code = e->exit_code;
+        if (ret)
+                *ret = e->exit_code;
         return 0;
 }
 
@@ -4945,10 +4968,10 @@ _public_ int sd_event_exit(sd_event *e, int code) {
         return 0;
 }
 
-_public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
+_public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *ret) {
         assert_return(e, -EINVAL);
         assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(usec, -EINVAL);
+        assert_return(ret, -EINVAL);
         assert_return(!event_origin_changed(e), -ECHILD);
 
         if (!TRIPLE_TIMESTAMP_HAS_CLOCK(clock))
@@ -4956,11 +4979,11 @@ _public_ int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec) {
 
         if (!triple_timestamp_is_set(&e->timestamp)) {
                 /* Implicitly fall back to now() if we never ran before and thus have no cached time. */
-                *usec = now(clock);
+                *ret = now(clock);
                 return 1;
         }
 
-        *usec = triple_timestamp_by_clock(&e->timestamp, clock);
+        *ret = triple_timestamp_by_clock(&e->timestamp, clock);
         return 0;
 }
 
@@ -4989,18 +5012,17 @@ _public_ int sd_event_default(sd_event **ret) {
 }
 
 #if 0 /* NM_IGNORED */
-_public_ int sd_event_get_tid(sd_event *e, pid_t *tid) {
+_public_ int sd_event_get_tid(sd_event *e, pid_t *ret) {
         assert_return(e, -EINVAL);
         assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(tid, -EINVAL);
+        assert_return(ret, -EINVAL);
         assert_return(!event_origin_changed(e), -ECHILD);
 
-        if (e->tid != 0) {
-                *tid = e->tid;
-                return 0;
-        }
+        if (e->tid == 0)
+                return -ENXIO;
 
-        return -ENXIO;
+        *ret = e->tid;
+        return 0;
 }
 
 _public_ int sd_event_set_watchdog(sd_event *e, int b) {
@@ -5230,6 +5252,9 @@ _public_ int sd_event_set_signal_exit(sd_event *e, int b) {
         int r;
 
         assert_return(e, -EINVAL);
+        assert_return(e = event_resolve(e), -ENOPKG);
+        assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
+        assert_return(!event_origin_changed(e), -ECHILD);
 
         if (b) {
                 /* We want to maintain pointers to these event sources, so that we can destroy them when told
@@ -5241,7 +5266,7 @@ _public_ int sd_event_set_signal_exit(sd_event *e, int b) {
                         if (r < 0)
                                 return r;
 
-                        assert(sd_event_source_set_floating(e->sigint_event_source, true) >= 0);
+                        assert_se(sd_event_source_set_floating(e->sigint_event_source, true) >= 0);
                         change = true;
                 }
 
@@ -5249,26 +5274,26 @@ _public_ int sd_event_set_signal_exit(sd_event *e, int b) {
                         r = sd_event_add_signal(e, &e->sigterm_event_source, SIGTERM | SD_EVENT_SIGNAL_PROCMASK, NULL, NULL);
                         if (r < 0) {
                                 if (change) {
-                                        assert(sd_event_source_set_floating(e->sigint_event_source, false) >= 0);
+                                        assert_se(sd_event_source_set_floating(e->sigint_event_source, false) >= 0);
                                         e->sigint_event_source = sd_event_source_unref(e->sigint_event_source);
                                 }
 
                                 return r;
                         }
 
-                        assert(sd_event_source_set_floating(e->sigterm_event_source, true) >= 0);
+                        assert_se(sd_event_source_set_floating(e->sigterm_event_source, true) >= 0);
                         change = true;
                 }
 
         } else {
                 if (e->sigint_event_source) {
-                        assert(sd_event_source_set_floating(e->sigint_event_source, false) >= 0);
+                        assert_se(sd_event_source_set_floating(e->sigint_event_source, false) >= 0);
                         e->sigint_event_source = sd_event_source_unref(e->sigint_event_source);
                         change = true;
                 }
 
                 if (e->sigterm_event_source) {
-                        assert(sd_event_source_set_floating(e->sigterm_event_source, false) >= 0);
+                        assert_se(sd_event_source_set_floating(e->sigterm_event_source, false) >= 0);
                         e->sigterm_event_source = sd_event_source_unref(e->sigterm_event_source);
                         change = true;
                 }
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c
index 58173055..6d515a94 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c
@@ -11,6 +11,8 @@
 #include "hexdecoct.h"
 #include "id128-util.h"
 #include "io-util.h"
+#include "namespace-util.h"
+#include "process-util.h"
 #include "sha256.h"
 #include "stdio-util.h"
 #include "string-util.h"
@@ -273,4 +275,65 @@ sd_id128_t id128_digest(const void *data, size_t size) {
 
         return id128_make_v4_uuid(id);
 }
+
+int id128_get_boot_for_machine(const char *machine, sd_id128_t *ret) {
+        _cleanup_close_ int pidnsfd = -EBADF, mntnsfd = -EBADF, rootfd = -EBADF;
+        _cleanup_close_pair_ int pair[2] = EBADF_PAIR;
+        pid_t pid, child;
+        sd_id128_t id;
+        ssize_t k;
+        int r;
+
+        assert(ret);
+
+        if (isempty(machine))
+                return sd_id128_get_boot(ret);
+
+        r = container_get_leader(machine, &pid);
+        if (r < 0)
+                return r;
+
+        r = namespace_open(pid, &pidnsfd, &mntnsfd, /* ret_netns_fd = */ NULL, /* ret_userns_fd = */ NULL, &rootfd);
+        if (r < 0)
+                return r;
+
+        if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
+                return -errno;
+
+        r = namespace_fork("(sd-bootidns)", "(sd-bootid)", NULL, 0, FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL,
+                           pidnsfd, mntnsfd, -1, -1, rootfd, &child);
+        if (r < 0)
+                return r;
+        if (r == 0) {
+                pair[0] = safe_close(pair[0]);
+
+                r = id128_get_boot(&id);
+                if (r < 0)
+                        _exit(EXIT_FAILURE);
+
+                k = send(pair[1], &id, sizeof(id), MSG_NOSIGNAL);
+                if (k != sizeof(id))
+                        _exit(EXIT_FAILURE);
+
+                _exit(EXIT_SUCCESS);
+        }
+
+        pair[1] = safe_close(pair[1]);
+
+        r = wait_for_terminate_and_check("(sd-bootidns)", child, 0);
+        if (r < 0)
+                return r;
+        if (r != EXIT_SUCCESS)
+                return -EIO;
+
+        k = recv(pair[0], &id, sizeof(id), 0);
+        if (k != sizeof(id))
+                return -EIO;
+
+        if (sd_id128_is_null(id))
+                return -EIO;
+
+        *ret = id;
+        return 0;
+}
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h
index 53ba50a8..458d4307 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h
+++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.h
@@ -49,6 +49,9 @@ int id128_get_product(sd_id128_t *ret);
 
 sd_id128_t id128_digest(const void *data, size_t size);
 
+int id128_get_boot(sd_id128_t *ret);
+int id128_get_boot_for_machine(const char *machine, sd_id128_t *ret);
+
 /* A helper to check for the three relevant cases of "machine ID not initialized" */
 #define ERRNO_IS_NEG_MACHINE_ID_UNSET(r)        \
         IN_SET(r,                               \
diff --git a/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c b/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c
index ff0db776..9f6ef719 100644
--- a/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c
+++ b/src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c
@@ -15,6 +15,7 @@
 #include "hmac.h"
 #include "id128-util.h"
 #include "io-util.h"
+#include "keyring-util.h"
 #include "macro.h"
 #include "missing_syscall.h"
 #include "missing_threads.h"
@@ -176,14 +177,24 @@ int id128_get_machine(const char *root, sd_id128_t *ret) {
 }
 #endif /* NM_IGNORED */
 
+int id128_get_boot(sd_id128_t *ret) {
+        int r;
+
+        assert(ret);
+
+        r = id128_read("/proc/sys/kernel/random/boot_id", ID128_FORMAT_UUID | ID128_REFUSE_NULL, ret);
+        if (r == -ENOENT && proc_mounted() == 0)
+                return -ENOSYS;
+
+        return r;
+}
+
 _public_ int sd_id128_get_boot(sd_id128_t *ret) {
         static thread_local sd_id128_t saved_boot_id = {};
         int r;
 
         if (sd_id128_is_null(saved_boot_id)) {
-                r = id128_read("/proc/sys/kernel/random/boot_id", ID128_FORMAT_UUID | ID128_REFUSE_NULL, &saved_boot_id);
-                if (r == -ENOENT && proc_mounted() == 0)
-                        return -ENOSYS;
+                r = id128_get_boot(&saved_boot_id);
                 if (r < 0)
                         return r;
         }
@@ -199,7 +210,6 @@ static int get_invocation_from_keyring(sd_id128_t *ret) {
         char *d, *p, *g, *u, *e;
         unsigned long perms;
         key_serial_t key;
-        size_t sz = 256;
         uid_t uid;
         gid_t gid;
         int r, c;
@@ -218,24 +228,9 @@ static int get_invocation_from_keyring(sd_id128_t *ret) {
                 return -errno;
         }
 
-        for (;;) {
-                description = new(char, sz);
-                if (!description)
-                        return -ENOMEM;
-
-                c = keyctl(KEYCTL_DESCRIBE, key, (unsigned long) description, sz, 0);
-                if (c < 0)
-                        return -errno;
-
-                if ((size_t) c <= sz)
-                        break;
-
-                sz = c;
-                free(description);
-        }
-
-        /* The kernel returns a final NUL in the string, verify that. */
-        assert(description[c-1] == 0);
+        r = keyring_describe(key, &description);
+        if (r < 0)
+                return r;
 
         /* Chop off the final description string */
         d = strrchr(description, ';');
@@ -387,4 +382,17 @@ _public_ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret)
 
         return sd_id128_get_app_specific(id, app_id, ret);
 }
+
+_public_ int sd_id128_get_invocation_app_specific(sd_id128_t app_id, sd_id128_t *ret) {
+        sd_id128_t id;
+        int r;
+
+        assert_return(ret, -EINVAL);
+
+        r = sd_id128_get_invocation(&id);
+        if (r < 0)
+                return r;
+
+        return sd_id128_get_app_specific(id, app_id, ret);
+}
 #endif /* NM_IGNORED */
diff --git a/src/libnm-systemd-core/src/systemd/_sd-common.h b/src/libnm-systemd-core/src/systemd/_sd-common.h
index d4381d90..5792dd81 100644
--- a/src/libnm-systemd-core/src/systemd/_sd-common.h
+++ b/src/libnm-systemd-core/src/systemd/_sd-common.h
@@ -45,6 +45,10 @@ typedef void (*_sd_destroy_t)(void *userdata);
 #  define _sd_pure_ __attribute__((__pure__))
 #endif
 
+#ifndef _sd_const_
+#  define _sd_const_ __attribute__((__const__))
+#endif
+
 /* Note that strictly speaking __deprecated__ has been available before GCC 6. However, starting with GCC 6
  * it also works on enum values, which we are interested in. Since this is a developer-facing feature anyway
  * (as opposed to build engineer-facing), let's hence conditionalize this to gcc 6, given that the developers
@@ -105,4 +109,11 @@ typedef void (*_sd_destroy_t)(void *userdata);
         _SD_##id##_INT64_MIN = INT64_MIN,    \
         _SD_##id##_INT64_MAX = INT64_MAX
 
+/* In GCC 14 (C23) we can force enums to have the right types, and not solely rely on language extensions anymore */
+#if ((__GNUC__ >= 14) || (__STDC_VERSION__ >= 202311L)) && !defined(__cplusplus)
+#  define _SD_ENUM_TYPE_S64(id) id : int64_t
+#else
+#  define _SD_ENUM_TYPE_S64(id) id
+#endif
+
 #endif
diff --git a/src/libnm-systemd-core/src/systemd/sd-device.h b/src/libnm-systemd-core/src/systemd/sd-device.h
index b67ec0f3..f627ae6d 100644
--- a/src/libnm-systemd-core/src/systemd/sd-device.h
+++ b/src/libnm-systemd-core/src/systemd/sd-device.h
@@ -34,7 +34,7 @@ typedef struct sd_device sd_device;
 typedef struct sd_device_enumerator sd_device_enumerator;
 typedef struct sd_device_monitor sd_device_monitor;
 
-__extension__ typedef enum sd_device_action_t {
+__extension__ typedef enum _SD_ENUM_TYPE_S64(sd_device_action_t) {
         SD_DEVICE_ADD,
         SD_DEVICE_REMOVE,
         SD_DEVICE_CHANGE,
@@ -74,6 +74,7 @@ int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const char *su
 
 int sd_device_get_syspath(sd_device *device, const char **ret);
 int sd_device_get_subsystem(sd_device *device, const char **ret);
+int sd_device_get_driver_subsystem(sd_device *device, const char **ret);
 int sd_device_get_devtype(sd_device *device, const char **ret);
 int sd_device_get_devnum(sd_device *device, dev_t *devnum);
 int sd_device_get_ifindex(sd_device *device, int *ifindex);
@@ -85,21 +86,22 @@ int sd_device_get_sysnum(sd_device *device, const char **ret);
 int sd_device_get_action(sd_device *device, sd_device_action_t *ret);
 int sd_device_get_seqnum(sd_device *device, uint64_t *ret);
 int sd_device_get_diskseq(sd_device *device, uint64_t *ret);
+int sd_device_get_device_id(sd_device *device, const char **ret);
 
 int sd_device_get_is_initialized(sd_device *device);
 int sd_device_get_usec_initialized(sd_device *device, uint64_t *ret);
 int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *ret);
 
-const char *sd_device_get_tag_first(sd_device *device);
-const char *sd_device_get_tag_next(sd_device *device);
-const char *sd_device_get_current_tag_first(sd_device *device);
-const char *sd_device_get_current_tag_next(sd_device *device);
-const char *sd_device_get_devlink_first(sd_device *device);
-const char *sd_device_get_devlink_next(sd_device *device);
-const char *sd_device_get_property_first(sd_device *device, const char **value);
-const char *sd_device_get_property_next(sd_device *device, const char **value);
-const char *sd_device_get_sysattr_first(sd_device *device);
-const char *sd_device_get_sysattr_next(sd_device *device);
+const char* sd_device_get_tag_first(sd_device *device);
+const char* sd_device_get_tag_next(sd_device *device);
+const char* sd_device_get_current_tag_first(sd_device *device);
+const char* sd_device_get_current_tag_next(sd_device *device);
+const char* sd_device_get_devlink_first(sd_device *device);
+const char* sd_device_get_devlink_next(sd_device *device);
+const char* sd_device_get_property_first(sd_device *device, const char **value);
+const char* sd_device_get_property_next(sd_device *device, const char **value);
+const char* sd_device_get_sysattr_first(sd_device *device);
+const char* sd_device_get_sysattr_next(sd_device *device);
 sd_device *sd_device_get_child_first(sd_device *device, const char **ret_suffix);
 sd_device *sd_device_get_child_next(sd_device *device, const char **ret_suffix);
 
@@ -135,6 +137,7 @@ int sd_device_enumerator_add_nomatch_sysname(sd_device_enumerator *enumerator, c
 int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator, const char *tag);
 int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent);
 int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator);
+int sd_device_enumerator_add_all_parents(sd_device_enumerator *enumerator);
 
 /* device monitor */
 
@@ -142,6 +145,9 @@ int sd_device_monitor_new(sd_device_monitor **ret);
 sd_device_monitor *sd_device_monitor_ref(sd_device_monitor *m);
 sd_device_monitor *sd_device_monitor_unref(sd_device_monitor *m);
 
+int sd_device_monitor_get_fd(sd_device_monitor *m);
+int sd_device_monitor_get_events(sd_device_monitor *m);
+int sd_device_monitor_get_timeout(sd_device_monitor *m, uint64_t *ret);
 int sd_device_monitor_set_receive_buffer_size(sd_device_monitor *m, size_t size);
 int sd_device_monitor_attach_event(sd_device_monitor *m, sd_event *event);
 int sd_device_monitor_detach_event(sd_device_monitor *m);
@@ -149,8 +155,10 @@ sd_event *sd_device_monitor_get_event(sd_device_monitor *m);
 sd_event_source *sd_device_monitor_get_event_source(sd_device_monitor *m);
 int sd_device_monitor_set_description(sd_device_monitor *m, const char *description);
 int sd_device_monitor_get_description(sd_device_monitor *m, const char **ret);
+int sd_device_monitor_is_running(sd_device_monitor *m);
 int sd_device_monitor_start(sd_device_monitor *m, sd_device_monitor_handler_t callback, void *userdata);
 int sd_device_monitor_stop(sd_device_monitor *m);
+int sd_device_monitor_receive(sd_device_monitor *m, sd_device **ret);
 
 int sd_device_monitor_filter_add_match_subsystem_devtype(sd_device_monitor *m, const char *subsystem, const char *devtype);
 int sd_device_monitor_filter_add_match_tag(sd_device_monitor *m, const char *tag);
diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h b/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h
index e18d5781..d6bcceb2 100644
--- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h
+++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h
@@ -30,6 +30,7 @@
 _SD_BEGIN_DECLARATIONS;
 
 typedef struct sd_dhcp6_lease sd_dhcp6_lease;
+typedef struct sd_dns_resolver sd_dns_resolver;
 
 int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret);
 int sd_dhcp6_lease_get_t1(sd_dhcp6_lease *lease, uint64_t *ret);
@@ -74,6 +75,7 @@ int sd_dhcp6_lease_get_pd_lifetime_timestamp(
 int sd_dhcp6_lease_has_pd_prefix(sd_dhcp6_lease *lease);
 
 int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret);
+int sd_dhcp6_lease_get_dnr(sd_dhcp6_lease *lease, sd_dns_resolver **ret);
 int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret);
 int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret);
 int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret);
diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp6-protocol.h b/src/libnm-systemd-core/src/systemd/sd-dhcp6-protocol.h
index 78c80f7c..b1d9ca1f 100644
--- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-protocol.h
+++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-protocol.h
@@ -165,8 +165,9 @@ enum {
         SD_DHCP6_OPTION_SLAP_QUAD                  = 140, /* RFC 8948 */
         SD_DHCP6_OPTION_V6_DOTS_RI                 = 141, /* RFC 8973 */
         SD_DHCP6_OPTION_V6_DOTS_ADDRESS            = 142, /* RFC 8973 */
-        SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF         = 143  /* RFC 6153 */
-        /* option codes 144-65535 are unassigned */
+        SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF         = 143,  /* RFC 6153 */
+        SD_DHCP6_OPTION_V6_DNR                     = 144  /* RFC 9463 */
+        /* option codes 145-65535 are unassigned */
 };
 
 _SD_END_DECLARATIONS;
diff --git a/src/libnm-systemd-core/src/systemd/sd-event.h b/src/libnm-systemd-core/src/systemd/sd-event.h
index 49d69759..1e19dd4e 100644
--- a/src/libnm-systemd-core/src/systemd/sd-event.h
+++ b/src/libnm-systemd-core/src/systemd/sd-event.h
@@ -108,12 +108,12 @@ int sd_event_run(sd_event *e, uint64_t usec);
 int sd_event_loop(sd_event *e);
 int sd_event_exit(sd_event *e, int code);
 
-int sd_event_now(sd_event *e, clockid_t clock, uint64_t *usec);
+int sd_event_now(sd_event *e, clockid_t clock, uint64_t *ret);
 
 int sd_event_get_fd(sd_event *e);
 int sd_event_get_state(sd_event *e);
-int sd_event_get_tid(sd_event *e, pid_t *tid);
-int sd_event_get_exit_code(sd_event *e, int *code);
+int sd_event_get_tid(sd_event *e, pid_t *ret);
+int sd_event_get_exit_code(sd_event *e, int *ret);
 int sd_event_set_watchdog(sd_event *e, int b);
 int sd_event_get_watchdog(sd_event *e);
 int sd_event_get_iteration(sd_event *e, uint64_t *ret);
@@ -123,33 +123,33 @@ sd_event_source* sd_event_source_ref(sd_event_source *s);
 sd_event_source* sd_event_source_unref(sd_event_source *s);
 sd_event_source* sd_event_source_disable_unref(sd_event_source *s);
 
-sd_event *sd_event_source_get_event(sd_event_source *s);
+sd_event* sd_event_source_get_event(sd_event_source *s);
 void* sd_event_source_get_userdata(sd_event_source *s);
 void* sd_event_source_set_userdata(sd_event_source *s, void *userdata);
 
 int sd_event_source_set_description(sd_event_source *s, const char *description);
-int sd_event_source_get_description(sd_event_source *s, const char **description);
+int sd_event_source_get_description(sd_event_source *s, const char **ret);
 int sd_event_source_set_prepare(sd_event_source *s, sd_event_handler_t callback);
 int sd_event_source_get_pending(sd_event_source *s);
-int sd_event_source_get_priority(sd_event_source *s, int64_t *priority);
+int sd_event_source_get_priority(sd_event_source *s, int64_t *ret);
 int sd_event_source_set_priority(sd_event_source *s, int64_t priority);
-int sd_event_source_get_enabled(sd_event_source *s, int *enabled);
+int sd_event_source_get_enabled(sd_event_source *s, int *ret);
 int sd_event_source_set_enabled(sd_event_source *s, int enabled);
 int sd_event_source_get_io_fd(sd_event_source *s);
 int sd_event_source_set_io_fd(sd_event_source *s, int fd);
 int sd_event_source_get_io_fd_own(sd_event_source *s);
 int sd_event_source_set_io_fd_own(sd_event_source *s, int own);
-int sd_event_source_get_io_events(sd_event_source *s, uint32_t* events);
+int sd_event_source_get_io_events(sd_event_source *s, uint32_t *ret);
 int sd_event_source_set_io_events(sd_event_source *s, uint32_t events);
-int sd_event_source_get_io_revents(sd_event_source *s, uint32_t* revents);
-int sd_event_source_get_time(sd_event_source *s, uint64_t *usec);
+int sd_event_source_get_io_revents(sd_event_source *s, uint32_t *ret);
+int sd_event_source_get_time(sd_event_source *s, uint64_t *ret);
 int sd_event_source_set_time(sd_event_source *s, uint64_t usec);
 int sd_event_source_set_time_relative(sd_event_source *s, uint64_t usec);
-int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *usec);
+int sd_event_source_get_time_accuracy(sd_event_source *s, uint64_t *ret);
 int sd_event_source_set_time_accuracy(sd_event_source *s, uint64_t usec);
-int sd_event_source_get_time_clock(sd_event_source *s, clockid_t *clock);
+int sd_event_source_get_time_clock(sd_event_source *s, clockid_t *ret);
 int sd_event_source_get_signal(sd_event_source *s);
-int sd_event_source_get_child_pid(sd_event_source *s, pid_t *pid);
+int sd_event_source_get_child_pid(sd_event_source *s, pid_t *ret);
 int sd_event_source_get_child_pidfd(sd_event_source *s);
 int sd_event_source_get_child_pidfd_own(sd_event_source *s);
 int sd_event_source_set_child_pidfd_own(sd_event_source *s, int own);
@@ -161,6 +161,7 @@ int sd_event_source_send_child_signal(sd_event_source *s, int sig, const siginfo
 int sd_event_source_send_child_signal(sd_event_source *s, int sig, const void *si, unsigned flags);
 #endif
 int sd_event_source_get_inotify_mask(sd_event_source *s, uint32_t *ret);
+int sd_event_source_get_inotify_path(sd_event_source *s, const char **ret);
 int sd_event_source_set_memory_pressure_type(sd_event_source *e, const char *ty);
 int sd_event_source_set_memory_pressure_period(sd_event_source *s, uint64_t threshold_usec, uint64_t window_usec);
 int sd_event_source_set_destroy_callback(sd_event_source *s, sd_event_destroy_t callback);
diff --git a/src/libnm-systemd-core/src/systemd/sd-id128.h b/src/libnm-systemd-core/src/systemd/sd-id128.h
index a984a9d8..7be69040 100644
--- a/src/libnm-systemd-core/src/systemd/sd-id128.h
+++ b/src/libnm-systemd-core/src/systemd/sd-id128.h
@@ -37,8 +37,8 @@ union sd_id128 {
 #define SD_ID128_STRING_MAX 33U
 #define SD_ID128_UUID_STRING_MAX 37U
 
-char *sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]);
-char *sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_UUID_STRING_MAX]);
+char* sd_id128_to_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_STRING_MAX]);
+char* sd_id128_to_uuid_string(sd_id128_t id, char s[_SD_ARRAY_STATIC SD_ID128_UUID_STRING_MAX]);
 int sd_id128_from_string(const char *s, sd_id128_t *ret);
 
 #define SD_ID128_TO_STRING(id) sd_id128_to_string((id), (char[SD_ID128_STRING_MAX]) {})
@@ -53,6 +53,7 @@ int sd_id128_get_invocation(sd_id128_t *ret);
 int sd_id128_get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret);
 int sd_id128_get_machine_app_specific(sd_id128_t app_id, sd_id128_t *ret);
 int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret);
+int sd_id128_get_invocation_app_specific(sd_id128_t app_id, sd_id128_t *ret);
 
 #define SD_ID128_ARRAY(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) \
         { .bytes = { 0x##v0, 0x##v1, 0x##v2, 0x##v3, 0x##v4, 0x##v5, 0x##v6, 0x##v7, \
@@ -116,24 +117,24 @@ int sd_id128_get_boot_app_specific(sd_id128_t app_id, sd_id128_t *ret);
 #define SD_ID128_MAKE_UUID_STR(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \
         #a #b #c #d "-" #e #f "-" #g #h "-" #i #j "-" #k #l #m #n #o #p
 
-_sd_pure_ static __inline__ int sd_id128_equal(sd_id128_t a, sd_id128_t b) {
+_sd_const_ static __inline__ int sd_id128_equal(sd_id128_t a, sd_id128_t b) {
         return a.qwords[0] == b.qwords[0] && a.qwords[1] == b.qwords[1];
 }
 
 int sd_id128_string_equal(const char *s, sd_id128_t id);
 
-_sd_pure_ static __inline__ int sd_id128_is_null(sd_id128_t a) {
+_sd_const_ static __inline__ int sd_id128_is_null(sd_id128_t a) {
         return a.qwords[0] == 0 && a.qwords[1] == 0;
 }
 
-_sd_pure_ static __inline__ int sd_id128_is_allf(sd_id128_t a) {
+_sd_const_ static __inline__ int sd_id128_is_allf(sd_id128_t a) {
         return a.qwords[0] == UINT64_C(0xFFFFFFFFFFFFFFFF) && a.qwords[1] == UINT64_C(0xFFFFFFFFFFFFFFFF);
 }
 
 #define SD_ID128_NULL ((const sd_id128_t) { .qwords = { 0, 0 }})
 #define SD_ID128_ALLF ((const sd_id128_t) { .qwords = { UINT64_C(0xFFFFFFFFFFFFFFFF), UINT64_C(0xFFFFFFFFFFFFFFFF) }})
 
-_sd_pure_ static __inline__ int sd_id128_in_setv(sd_id128_t a, va_list ap) {
+_sd_const_ static __inline__ int sd_id128_in_setv(sd_id128_t a, va_list ap) {
         for (;;) {
                 sd_id128_t b = va_arg(ap, sd_id128_t);
 
@@ -145,7 +146,7 @@ _sd_pure_ static __inline__ int sd_id128_in_setv(sd_id128_t a, va_list ap) {
         }
 }
 
-_sd_pure_ static __inline__ int sd_id128_in_set_sentinel(sd_id128_t a, ...) {
+_sd_const_ static __inline__ int sd_id128_in_set_sentinel(sd_id128_t a, ...) {
         va_list ap;
         int r;
 
diff --git a/src/libnm-systemd-core/src/systemd/sd-ndisc.h b/src/libnm-systemd-core/src/systemd/sd-ndisc.h
index 5f4f6caf..85fcf6bc 100644
--- a/src/libnm-systemd-core/src/systemd/sd-ndisc.h
+++ b/src/libnm-systemd-core/src/systemd/sd-ndisc.h
@@ -26,7 +26,9 @@
 #include <sys/types.h>
 
 #include "sd-event.h"
+#include "sd-ndisc-neighbor.h"
 #include "sd-ndisc-protocol.h"
+#include "sd-ndisc-redirect.h"
 #include "sd-ndisc-router.h"
 
 #include "_sd-common.h"
@@ -35,9 +37,11 @@ _SD_BEGIN_DECLARATIONS;
 
 typedef struct sd_ndisc sd_ndisc;
 
-__extension__ typedef enum sd_ndisc_event_t {
+__extension__ typedef enum _SD_ENUM_TYPE_S64(sd_ndisc_event_t) {
         SD_NDISC_EVENT_TIMEOUT,
         SD_NDISC_EVENT_ROUTER,
+        SD_NDISC_EVENT_NEIGHBOR,
+        SD_NDISC_EVENT_REDIRECT,
         _SD_NDISC_EVENT_MAX,
         _SD_NDISC_EVENT_INVALID = -EINVAL,
         _SD_ENUM_FORCE_S64(NDISC_EVENT)