about summary refs log tree commit diff
path: root/src/libnm-systemd-core
diff options
context:
space:
mode:
Diffstat (limited to 'src/libnm-systemd-core')
-rw-r--r--src/libnm-systemd-core/meson.build2
-rw-r--r--src/libnm-systemd-core/nm-sd-utils-dhcp.h2
-rw-r--r--src/libnm-systemd-core/nm-sd.h2
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/arp-util.c34
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/arp-util.h4
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c6
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp-internal.h4
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp-lease-internal.h6
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp-network.c66
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp-packet.c33
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h8
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-lease-internal.h16
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c4
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c189
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h120
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.c50
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/lldp-network.c21
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/network-common.h4
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c61
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-lease.c96
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c316
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c103
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-ipv4acd.c616
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-ipv4ll.c382
-rw-r--r--src/libnm-systemd-core/src/libsystemd-network/sd-lldp-rx.c29
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-event/event-source.h6
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c24
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h26
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-event/sd-event.c204
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-id128/id128-util.c2
-rw-r--r--src/libnm-systemd-core/src/libsystemd/sd-id128/sd-id128.c43
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-dhcp-client.h117
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-dhcp-lease.h7
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h169
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h4
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-event.h2
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-ipv4acd.h63
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-ipv4ll.h65
-rw-r--r--src/libnm-systemd-core/src/systemd/sd-ndisc.h3
39 files changed, 1237 insertions, 1672 deletions
diff --git a/src/libnm-systemd-core/meson.build b/src/libnm-systemd-core/meson.build
index fb472a76..1fa94486 100644
--- a/src/libnm-systemd-core/meson.build
+++ b/src/libnm-systemd-core/meson.build
@@ -18,8 +18,6 @@ libnm_systemd_core = static_library(
     'src/libsystemd-network/sd-dhcp-lease.c',
     'src/libsystemd-network/sd-dhcp6-client.c',
     'src/libsystemd-network/sd-dhcp6-lease.c',
-    'src/libsystemd-network/sd-ipv4acd.c',
-    'src/libsystemd-network/sd-ipv4ll.c',
     'src/libsystemd-network/sd-lldp-rx.c',
     'src/libsystemd/sd-event/event-util.c',
     'src/libsystemd/sd-event/sd-event.c',
diff --git a/src/libnm-systemd-core/nm-sd-utils-dhcp.h b/src/libnm-systemd-core/nm-sd-utils-dhcp.h
index 5d126d5b..6487a90d 100644
--- a/src/libnm-systemd-core/nm-sd-utils-dhcp.h
+++ b/src/libnm-systemd-core/nm-sd-utils-dhcp.h
@@ -11,7 +11,7 @@
 typedef struct {
     uint8_t code;
     uint8_t data_len;
-    void *  data;
+    void   *data;
 } nm_sd_dhcp_option;
 
 int nm_sd_dhcp_lease_get_private_options(sd_dhcp_lease *lease, nm_sd_dhcp_option **out_options);
diff --git a/src/libnm-systemd-core/nm-sd.h b/src/libnm-systemd-core/nm-sd.h
index 0ec2f16e..421f5372 100644
--- a/src/libnm-systemd-core/nm-sd.h
+++ b/src/libnm-systemd-core/nm-sd.h
@@ -8,9 +8,7 @@
 
 #include "src/systemd/sd-dhcp-client.h"
 #include "src/systemd/sd-dhcp6-client.h"
-#include "src/systemd/sd-ipv4ll.h"
 #include "src/systemd/sd-lldp-rx.h"
-#include "src/systemd/sd-lldp.h"
 
 /*****************************************************************************/
 
diff --git a/src/libnm-systemd-core/src/libsystemd-network/arp-util.c b/src/libnm-systemd-core/src/libsystemd-network/arp-util.c
index 46c4a8cb..c8a73111 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/arp-util.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/arp-util.c
@@ -16,7 +16,7 @@
 #include "unaligned.h"
 #include "util.h"
 
-int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *eth_mac) {
+int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *mac) {
         struct sock_filter filter[] = {
                 BPF_STMT(BPF_LD + BPF_W + BPF_LEN, 0),                                         /* A <- packet length */
                 BPF_JUMP(BPF_JMP + BPF_JGE + BPF_K, sizeof(struct ether_arp), 1, 0),           /* packet >= arp packet ? */
@@ -38,30 +38,21 @@ int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *
                 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARPOP_REPLY, 1, 0),                        /* protocol == reply ? */
                 BPF_STMT(BPF_RET + BPF_K, 0),                                                  /* ignore */
                 /* Sender Hardware Address must be different from our own */
-                BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be32(&eth_mac->ether_addr_octet[0])),/* A <- 4 bytes of client's MAC */
-                BPF_STMT(BPF_MISC + BPF_TAX, 0),                                               /* X <- A */
+                BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be32(&mac->ether_addr_octet[0])),   /* X <- 4 bytes of client's MAC */
                 BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_sha)),       /* A <- 4 bytes of SHA */
-                BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0),                                        /* A xor X */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 6),                                  /* A == 0 ? */
-                BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be16(&eth_mac->ether_addr_octet[4])),/* A <- remainder of client's MAC */
-                BPF_STMT(BPF_MISC + BPF_TAX, 0),                                               /* X <- A */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 4),                                  /* A == X ? */
+                BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be16(&mac->ether_addr_octet[4])),   /* X <- remainder of client's MAC */
                 BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(struct ether_arp, arp_sha) + 4),   /* A <- remainder of SHA */
-                BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0),                                        /* A xor X */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1),                                  /* A == 0 ? */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 1),                                  /* A == X ? */
                 BPF_STMT(BPF_RET + BPF_K, 0),                                                  /* ignore */
                 /* Sender Protocol Address or Target Protocol Address must be equal to the one we care about */
-                BPF_STMT(BPF_LD + BPF_IMM, htobe32(a->s_addr)),                                /* A <- clients IP */
-                BPF_STMT(BPF_MISC + BPF_TAX, 0),                                               /* X <- A */
+                BPF_STMT(BPF_LDX + BPF_IMM, htobe32(a->s_addr)),                               /* X <- clients IP */
                 BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_spa)),       /* A <- SPA */
-                BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0),                                        /* X xor A */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1),                                  /* A == 0 ? */
-                BPF_STMT(BPF_RET + BPF_K, 65535),                                              /* return all */
-                BPF_STMT(BPF_LD + BPF_IMM, htobe32(a->s_addr)),                                /* A <- clients IP */
-                BPF_STMT(BPF_MISC + BPF_TAX, 0),                                               /* X <- A */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 1),                                  /* A == X ? */
+                BPF_STMT(BPF_RET + BPF_K, UINT32_MAX),                                         /* accept */
                 BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ether_arp, arp_tpa)),       /* A <- TPA */
-                BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0),                                        /* X xor A */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 0, 1),                                  /* A == 0 ? */
-                BPF_STMT(BPF_RET + BPF_K, 65535),                                              /* return all */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 0, 1),                                  /* A == 0 ? */
+                BPF_STMT(BPF_RET + BPF_K, UINT32_MAX),                                         /* accept */
                 BPF_STMT(BPF_RET + BPF_K, 0),                                                  /* ignore */
         };
         struct sock_fprog fprog = {
@@ -77,7 +68,7 @@ int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *
         return 0;
 }
 
-int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *eth_mac) {
+int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *mac) {
         union sockaddr_union link = {
                 .ll.sll_family   = AF_PACKET,
                 .ll.sll_protocol = htobe16(ETH_P_ARP),
@@ -89,12 +80,13 @@ int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const stru
         int r;
 
         assert(ifindex > 0);
+        assert(mac);
 
         s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
         if (s < 0)
                 return -errno;
 
-        r = arp_update_filter(s, a, eth_mac);
+        r = arp_update_filter(s, a, mac);
         if (r < 0)
                 return r;
 
diff --git a/src/libnm-systemd-core/src/libsystemd-network/arp-util.h b/src/libnm-systemd-core/src/libsystemd-network/arp-util.h
index e8615a15..b66a81bf 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/arp-util.h
+++ b/src/libnm-systemd-core/src/libsystemd-network/arp-util.h
@@ -11,8 +11,8 @@
 #include "socket-util.h"
 #include "sparse-endian.h"
 
-int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *eth_mac);
-int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *eth_mac);
+int arp_update_filter(int fd, const struct in_addr *a, const struct ether_addr *mac);
+int arp_network_bind_raw_socket(int ifindex, const struct in_addr *a, const struct ether_addr *mac);
 
 int arp_send_packet(
                 int fd,
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c
index bd47ff41..74aa19de 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-identifier.c
@@ -11,7 +11,7 @@
 
 #include "dhcp-identifier.h"
 #include "dhcp6-protocol.h"
-#include "network-util.h"
+#include "netif-util.h"
 #include "siphash24.h"
 #include "sparse-endian.h"
 #include "stat-util.h"
@@ -200,7 +200,7 @@ int dhcp_identifier_set_iaid(
                         /* device is under renaming */
                         return -EBUSY;
 
-                name = net_get_name_persistent(device);
+                name = net_get_persistent_name(device);
         }
 
         if (name)
@@ -214,7 +214,7 @@ int dhcp_identifier_set_iaid(
         if (legacy_unstable_byteorder)
                 /* for historical reasons (a bug), the bits were swapped and thus
                  * the result was endianness dependent. Preserve that behavior. */
-                id32 = __bswap_32(id32);
+                id32 = bswap_32(id32);
         else
                 /* the fixed behavior returns a stable byte order. Since LE is expected
                  * to be more common, swap the bytes on LE to give the same as legacy
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp-internal.h
index 6538f05b..466d8e4b 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-internal.h
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-internal.h
@@ -53,8 +53,8 @@ typedef int (*dhcp_option_callback_t)(uint8_t code, uint8_t len,
 int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_callback_t cb, void *userdata, char **error_message);
 
 int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
-                      uint8_t type, uint16_t arp_type, size_t optlen,
-                      size_t *optoffset);
+                      uint8_t type, uint16_t arp_type, uint8_t hlen, const uint8_t *chaddr,
+                      size_t optlen, size_t *optoffset);
 
 uint16_t dhcp_packet_checksum(uint8_t *buf, size_t len);
 
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-lease-internal.h b/src/libnm-systemd-core/src/libsystemd-network/dhcp-lease-internal.h
index 35a74dd7..992ac9f3 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-lease-internal.h
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-lease-internal.h
@@ -70,6 +70,12 @@ struct sd_dhcp_lease {
 
         char *timezone;
 
+        uint8_t sixrd_ipv4masklen;
+        uint8_t sixrd_prefixlen;
+        struct in6_addr sixrd_prefix;
+        struct in_addr *sixrd_br_addresses;
+        size_t sixrd_n_br_addresses;
+
         LIST_HEAD(struct sd_dhcp_raw_option, private_options);
 };
 
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-network.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp-network.c
index bbfdeb0f..1c5f3427 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-network.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-network.c
@@ -60,24 +60,20 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
 
                 /* We only support MAC address length to be either 0 or 6 (ETH_ALEN). Optionally
                  * compare chaddr for ETH_ALEN bytes. */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETH_ALEN, 0, 12),                                  /* A (the MAC address length) == ETH_ALEN ? */
-                BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be32(&eth_mac->ether_addr_octet[0])),        /* A <- 4 bytes of client's MAC */
-                BPF_STMT(BPF_MISC + BPF_TAX, 0),                                                       /* X <- A */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETH_ALEN, 0, 8),                                   /* A (the MAC address length) == ETH_ALEN ? */
+                BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be32(&eth_mac->ether_addr_octet[0])),       /* X <- 4 bytes of client's MAC */
                 BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr)),                 /* A <- 4 bytes of MAC from dhcp.chaddr */
-                BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0),                                                /* A xor X */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0),                                          /* A == 0 ? */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 1, 0),                                          /* A == X ? */
                 BPF_STMT(BPF_RET + BPF_K, 0),                                                          /* ignore */
-                BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be16(&eth_mac->ether_addr_octet[4])),        /* A <- remainder of client's MAC */
-                BPF_STMT(BPF_MISC + BPF_TAX, 0),                                                       /* X <- A */
+                BPF_STMT(BPF_LDX + BPF_IMM, unaligned_read_be16(&eth_mac->ether_addr_octet[4])),       /* X <- remainder of client's MAC */
                 BPF_STMT(BPF_LD + BPF_H + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr) + 4),             /* A <- remainder of MAC from dhcp.chaddr */
-                BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0),                                                /* A xor X */
-                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0),                                          /* A == 0 ? */
+                BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_X, 0, 1, 0),                                          /* A == X ? */
                 BPF_STMT(BPF_RET + BPF_K, 0),                                                          /* ignore */
 
                 BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.magic)),  /* A <- DHCP magic cookie */
                 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_MAGIC_COOKIE, 1, 0),          /* cookie == DHCP magic cookie ? */
                 BPF_STMT(BPF_RET + BPF_K, 0),                                          /* ignore */
-                BPF_STMT(BPF_RET + BPF_K, 65535),                                      /* return all */
+                BPF_STMT(BPF_RET + BPF_K, UINT32_MAX),                                 /* accept */
         };
         struct sock_fprog fprog = {
                 .len = ELEMENTSOF(filter),
@@ -117,10 +113,17 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link,
         return TAKE_FD(s);
 }
 
-int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link, uint32_t xid,
-                                 const uint8_t *mac_addr, size_t mac_addr_len,
-                                 const uint8_t *bcast_addr, size_t bcast_addr_len,
-                                 uint16_t arp_type, uint16_t port) {
+int dhcp_network_bind_raw_socket(
+                int ifindex,
+                union sockaddr_union *link,
+                uint32_t xid,
+                const uint8_t *mac_addr,
+                size_t mac_addr_len,
+                const uint8_t *bcast_addr,
+                size_t bcast_addr_len,
+                uint16_t arp_type,
+                uint16_t port) {
+
         static const uint8_t eth_bcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
         /* Default broadcast address for IPoIB */
         static const uint8_t ib_bcast[] = {
@@ -174,7 +177,6 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int
                 r = setsockopt_int(s, IPPROTO_IP, IP_TOS, ip_service_type);
         else
                 r = setsockopt_int(s, IPPROTO_IP, IP_TOS, IPTOS_CLASS_CS6);
-
         if (r < 0)
                 return r;
 
@@ -206,43 +208,49 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port, int
                         return r;
         }
 
-        r = bind(s, &src.sa, sizeof(src.in));
-        if (r < 0)
+        if (bind(s, &src.sa, sizeof(src.in)) < 0)
                 return -errno;
 
         return TAKE_FD(s);
 }
 
-int dhcp_network_send_raw_socket(int s, const union sockaddr_union *link,
-                                 const void *packet, size_t len) {
-        int r;
+int dhcp_network_send_raw_socket(
+                int s,
+                const union sockaddr_union *link,
+                const void *packet,
+                size_t len) {
+
+        /* Do not add assert(s >= 0) here, as this is called in fuzz-dhcp-server, and in that case this
+         * function should fail with negative errno. */
 
         assert(link);
         assert(packet);
-        assert(len);
+        assert(len > 0);
 
-        r = sendto(s, packet, len, 0, &link->sa, SOCKADDR_LL_LEN(link->ll));
-        if (r < 0)
+        if (sendto(s, packet, len, 0, &link->sa, SOCKADDR_LL_LEN(link->ll)) < 0)
                 return -errno;
 
         return 0;
 }
 
-int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port,
-                                 const void *packet, size_t len) {
+int dhcp_network_send_udp_socket(
+                int s,
+                be32_t address,
+                uint16_t port,
+                const void *packet,
+                size_t len) {
+
         union sockaddr_union dest = {
                 .in.sin_family = AF_INET,
                 .in.sin_port = htobe16(port),
                 .in.sin_addr.s_addr = address,
         };
-        int r;
 
         assert(s >= 0);
         assert(packet);
-        assert(len);
+        assert(len > 0);
 
-        r = sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in));
-        if (r < 0)
+        if (sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in)) < 0)
                 return -errno;
 
         return 0;
diff --git a/src/libnm-systemd-core/src/libsystemd-network/dhcp-packet.c b/src/libnm-systemd-core/src/libsystemd-network/dhcp-packet.c
index eeadd562..25a69a61 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp-packet.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp-packet.c
@@ -12,21 +12,44 @@
 
 #include "dhcp-internal.h"
 #include "dhcp-protocol.h"
+#include "memory-util.h"
 
 #define DHCP_CLIENT_MIN_OPTIONS_SIZE            312
 
-int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid,
-                      uint8_t type, uint16_t arp_type, size_t optlen,
-                      size_t *optoffset) {
+int dhcp_message_init(
+                DHCPMessage *message,
+                uint8_t op,
+                uint32_t xid,
+                uint8_t type,
+                uint16_t arp_type,
+                uint8_t hlen,
+                const uint8_t *chaddr,
+                size_t optlen,
+                size_t *optoffset) {
+
         size_t offset = 0;
         int r;
 
         assert(IN_SET(op, BOOTREQUEST, BOOTREPLY));
-        assert(IN_SET(arp_type, ARPHRD_ETHER, ARPHRD_INFINIBAND));
+        assert(chaddr || hlen == 0);
 
         message->op = op;
         message->htype = arp_type;
-        message->hlen = (arp_type == ARPHRD_ETHER) ? ETHER_ADDR_LEN : 0;
+
+        /* RFC2131 section 4.1.1:
+           The client MUST include its hardware address in the ’chaddr’ field, if
+           necessary for delivery of DHCP reply messages.
+
+           RFC 4390 section 2.1:
+           A DHCP client, when working over an IPoIB interface, MUST follow the
+           following rules:
+           "htype" (hardware address type) MUST be 32 [ARPPARAM].
+           "hlen" (hardware address length) MUST be 0.
+           "chaddr" (client hardware address) field MUST be zeroed.
+         */
+        message->hlen = (arp_type == ARPHRD_INFINIBAND) ? 0 : hlen;
+        memcpy_safe(message->chaddr, chaddr, message->hlen);
+
         message->xid = htobe32(xid);
         message->magic = htobe32(DHCP_MAGIC_COOKIE);
 
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 4eb56aa8..f9434098 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-internal.h
@@ -11,6 +11,7 @@
 #include "sd-event.h"
 #include "sd-dhcp6-client.h"
 
+#include "dhcp6-protocol.h"
 #include "hashmap.h"
 #include "list.h"
 #include "macro.h"
@@ -93,6 +94,7 @@ typedef struct DHCP6IA {
 
 typedef struct sd_dhcp6_client sd_dhcp6_client;
 
+bool dhcp6_option_can_request(uint16_t option);
 int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
                         size_t optlen, const void *optval);
 int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia);
@@ -129,6 +131,12 @@ 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 client_parse_message(
+                sd_dhcp6_client *client,
+                DHCP6Message *message,
+                size_t len,
+                sd_dhcp6_lease *lease);
+
 const char *dhcp6_message_type_to_string(int s) _const_;
 int dhcp6_message_type_from_string(const char *s) _pure_;
 const char *dhcp6_message_status_to_string(int s) _const_;
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 8801497b..8ae2ecd6 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
@@ -14,10 +14,14 @@
 struct sd_dhcp6_lease {
         unsigned n_ref;
 
+        uint8_t *clientid;
+        size_t clientid_len;
         uint8_t *serverid;
         size_t serverid_len;
         uint8_t preference;
         bool rapid_commit;
+        triple_timestamp timestamp;
+        struct in6_addr server_address;
 
         DHCP6IA ia;
         DHCP6IA pd;
@@ -39,21 +43,19 @@ struct sd_dhcp6_lease {
 int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire);
 DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia);
 
-int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id,
-                             size_t len);
-int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **id, size_t *len);
+int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len);
+int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len);
+int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len);
+int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len);
 int dhcp6_lease_set_preference(sd_dhcp6_lease *lease, uint8_t preference);
 int dhcp6_lease_get_preference(sd_dhcp6_lease *lease, uint8_t *preference);
 int dhcp6_lease_set_rapid_commit(sd_dhcp6_lease *lease);
 int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit);
 
-int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid);
-int dhcp6_lease_get_pd_iaid(sd_dhcp6_lease *lease, be32_t *iaid);
-
 int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
 int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
 int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
-int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) ;
+int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
 int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
 
 int dhcp6_lease_new(sd_dhcp6_lease **ret);
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 b6816de4..beace4c7 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-network.c
@@ -49,6 +49,10 @@ int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
         if (r < 0)
                 return r;
 
+        r = setsockopt_int(s, SOL_SOCKET, SO_TIMESTAMP, true);
+        if (r < 0)
+                return r;
+
         r = bind(s, &src.sa, sizeof(src.in6));
         if (r < 0)
                 return -errno;
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 4249f90d..237771e6 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-option.c
@@ -26,6 +26,193 @@
 #define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd))
 #define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta))
 
+bool dhcp6_option_can_request(uint16_t option) {
+        /* See Client ORO field in
+         * https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-2 */
+
+        switch (option) {
+        case SD_DHCP6_OPTION_CLIENTID:
+        case SD_DHCP6_OPTION_SERVERID:
+        case SD_DHCP6_OPTION_IA_NA:
+        case SD_DHCP6_OPTION_IA_TA:
+        case SD_DHCP6_OPTION_IAADDR:
+        case SD_DHCP6_OPTION_ORO:
+        case SD_DHCP6_OPTION_PREFERENCE:
+        case SD_DHCP6_OPTION_ELAPSED_TIME:
+        case SD_DHCP6_OPTION_RELAY_MSG:
+        case SD_DHCP6_OPTION_AUTH:
+        case SD_DHCP6_OPTION_UNICAST:
+        case SD_DHCP6_OPTION_STATUS_CODE:
+        case SD_DHCP6_OPTION_RAPID_COMMIT:
+        case SD_DHCP6_OPTION_USER_CLASS:
+        case SD_DHCP6_OPTION_VENDOR_CLASS:
+                return false;
+        case SD_DHCP6_OPTION_VENDOR_OPTS:
+                return true;
+        case SD_DHCP6_OPTION_INTERFACE_ID:
+        case SD_DHCP6_OPTION_RECONF_MSG:
+        case SD_DHCP6_OPTION_RECONF_ACCEPT:
+                return false;
+        case SD_DHCP6_OPTION_SIP_SERVER_DOMAIN_NAME:
+        case SD_DHCP6_OPTION_SIP_SERVER_ADDRESS:
+        case SD_DHCP6_OPTION_DNS_SERVERS:
+        case SD_DHCP6_OPTION_DOMAIN_LIST:
+                return true;
+        case SD_DHCP6_OPTION_IA_PD:
+        case SD_DHCP6_OPTION_IA_PD_PREFIX:
+                return false;
+        case SD_DHCP6_OPTION_NIS_SERVERS:
+        case SD_DHCP6_OPTION_NISP_SERVERS:
+        case SD_DHCP6_OPTION_NIS_DOMAIN_NAME:
+        case SD_DHCP6_OPTION_NISP_DOMAIN_NAME:
+        case SD_DHCP6_OPTION_SNTP_SERVERS:
+        case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
+        case SD_DHCP6_OPTION_BCMCS_SERVER_D:
+        case SD_DHCP6_OPTION_BCMCS_SERVER_A:
+        case SD_DHCP6_OPTION_GEOCONF_CIVIC:
+                return true;
+        case SD_DHCP6_OPTION_REMOTE_ID:
+        case SD_DHCP6_OPTION_SUBSCRIBER_ID:
+                return false;
+        case SD_DHCP6_OPTION_CLIENT_FQDN:
+        case SD_DHCP6_OPTION_PANA_AGENT:
+        case SD_DHCP6_OPTION_NEW_POSIX_TIMEZONE:
+        case SD_DHCP6_OPTION_NEW_TZDB_TIMEZONE:
+                return true;
+        case SD_DHCP6_OPTION_ERO:
+        case SD_DHCP6_OPTION_LQ_QUERY:
+        case SD_DHCP6_OPTION_CLIENT_DATA:
+        case SD_DHCP6_OPTION_CLT_TIME:
+        case SD_DHCP6_OPTION_LQ_RELAY_DATA:
+        case SD_DHCP6_OPTION_LQ_CLIENT_LINK:
+                return false;
+        case SD_DHCP6_OPTION_MIP6_HNIDF:
+        case SD_DHCP6_OPTION_MIP6_VDINF:
+        case SD_DHCP6_OPTION_V6_LOST:
+        case SD_DHCP6_OPTION_CAPWAP_AC_V6:
+                return true;
+        case SD_DHCP6_OPTION_RELAY_ID:
+                return false;
+        case SD_DHCP6_OPTION_IPV6_ADDRESS_MOS:
+        case SD_DHCP6_OPTION_IPV6_FQDN_MOS:
+        case SD_DHCP6_OPTION_NTP_SERVER:
+        case SD_DHCP6_OPTION_V6_ACCESS_DOMAIN:
+        case SD_DHCP6_OPTION_SIP_UA_CS_LIST:
+        case SD_DHCP6_OPTION_BOOTFILE_URL:
+        case SD_DHCP6_OPTION_BOOTFILE_PARAM:
+                return true;
+        case SD_DHCP6_OPTION_CLIENT_ARCH_TYPE:
+                return false;
+        case SD_DHCP6_OPTION_NII:
+        case SD_DHCP6_OPTION_GEOLOCATION:
+        case SD_DHCP6_OPTION_AFTR_NAME:
+        case SD_DHCP6_OPTION_ERP_LOCAL_DOMAIN_NAME:
+                return true;
+        case SD_DHCP6_OPTION_RSOO:
+                return false;
+        case SD_DHCP6_OPTION_PD_EXCLUDE:
+                return true;
+        case SD_DHCP6_OPTION_VSS:
+                return false;
+        case SD_DHCP6_OPTION_MIP6_IDINF:
+        case SD_DHCP6_OPTION_MIP6_UDINF:
+        case SD_DHCP6_OPTION_MIP6_HNP:
+        case SD_DHCP6_OPTION_MIP6_HAA:
+        case SD_DHCP6_OPTION_MIP6_HAF:
+        case SD_DHCP6_OPTION_RDNSS_SELECTION:
+        case SD_DHCP6_OPTION_KRB_PRINCIPAL_NAME:
+        case SD_DHCP6_OPTION_KRB_REALM_NAME:
+        case SD_DHCP6_OPTION_KRB_DEFAULT_REALM_NAME:
+        case SD_DHCP6_OPTION_KRB_KDC:
+                return true;
+        case SD_DHCP6_OPTION_CLIENT_LINKLAYER_ADDR:
+        case SD_DHCP6_OPTION_LINK_ADDRESS:
+        case SD_DHCP6_OPTION_RADIUS:
+                return false;
+        case SD_DHCP6_OPTION_SOL_MAX_RT:
+        case SD_DHCP6_OPTION_INF_MAX_RT:
+        case SD_DHCP6_OPTION_ADDRSEL:
+        case SD_DHCP6_OPTION_ADDRSEL_TABLE:
+        case SD_DHCP6_OPTION_V6_PCP_SERVER:
+                return true;
+        case SD_DHCP6_OPTION_DHCPV4_MSG:
+                return false;
+        case SD_DHCP6_OPTION_DHCP4_O_DHCP6_SERVER:
+                return true;
+        case SD_DHCP6_OPTION_S46_RULE:
+                return false;
+        case SD_DHCP6_OPTION_S46_BR:
+                return true;
+        case SD_DHCP6_OPTION_S46_DMR:
+        case SD_DHCP6_OPTION_S46_V4V6BIND:
+        case SD_DHCP6_OPTION_S46_PORTPARAMS:
+                return false;
+        case SD_DHCP6_OPTION_S46_CONT_MAPE:
+        case SD_DHCP6_OPTION_S46_CONT_MAPT:
+        case SD_DHCP6_OPTION_S46_CONT_LW:
+        case SD_DHCP6_OPTION_4RD:
+        case SD_DHCP6_OPTION_4RD_MAP_RULE:
+        case SD_DHCP6_OPTION_4RD_NON_MAP_RULE:
+                return true;
+        case SD_DHCP6_OPTION_LQ_BASE_TIME:
+        case SD_DHCP6_OPTION_LQ_START_TIME:
+        case SD_DHCP6_OPTION_LQ_END_TIME:
+                return false;
+        case SD_DHCP6_OPTION_CAPTIVE_PORTAL:
+        case SD_DHCP6_OPTION_MPL_PARAMETERS:
+                return true;
+        case SD_DHCP6_OPTION_ANI_ATT:
+        case SD_DHCP6_OPTION_ANI_NETWORK_NAME:
+        case SD_DHCP6_OPTION_ANI_AP_NAME:
+        case SD_DHCP6_OPTION_ANI_AP_BSSID:
+        case SD_DHCP6_OPTION_ANI_OPERATOR_ID:
+        case SD_DHCP6_OPTION_ANI_OPERATOR_REALM:
+                return false;
+        case SD_DHCP6_OPTION_S46_PRIORITY:
+                return true;
+        case SD_DHCP6_OPTION_MUD_URL_V6:
+                return false;
+        case SD_DHCP6_OPTION_V6_PREFIX64:
+                return true;
+        case SD_DHCP6_OPTION_F_BINDING_STATUS:
+        case SD_DHCP6_OPTION_F_CONNECT_FLAGS:
+        case SD_DHCP6_OPTION_F_DNS_REMOVAL_INFO:
+        case SD_DHCP6_OPTION_F_DNS_HOST_NAME:
+        case SD_DHCP6_OPTION_F_DNS_ZONE_NAME:
+        case SD_DHCP6_OPTION_F_DNS_FLAGS:
+        case SD_DHCP6_OPTION_F_EXPIRATION_TIME:
+        case SD_DHCP6_OPTION_F_MAX_UNACKED_BNDUPD:
+        case SD_DHCP6_OPTION_F_MCLT:
+        case SD_DHCP6_OPTION_F_PARTNER_LIFETIME:
+        case SD_DHCP6_OPTION_F_PARTNER_LIFETIME_SENT:
+        case SD_DHCP6_OPTION_F_PARTNER_DOWN_TIME:
+        case SD_DHCP6_OPTION_F_PARTNER_RAW_CLT_TIME:
+        case SD_DHCP6_OPTION_F_PROTOCOL_VERSION:
+        case SD_DHCP6_OPTION_F_KEEPALIVE_TIME:
+        case SD_DHCP6_OPTION_F_RECONFIGURE_DATA:
+        case SD_DHCP6_OPTION_F_RELATIONSHIP_NAME:
+        case SD_DHCP6_OPTION_F_SERVER_FLAGS:
+        case SD_DHCP6_OPTION_F_SERVER_STATE:
+        case SD_DHCP6_OPTION_F_START_TIME_OF_STATE:
+        case SD_DHCP6_OPTION_F_STATE_EXPIRATION_TIME:
+        case SD_DHCP6_OPTION_RELAY_PORT:
+                return false;
+        case SD_DHCP6_OPTION_V6_SZTP_REDIRECT:
+        case SD_DHCP6_OPTION_S46_BIND_IPV6_PREFIX:
+                return true;
+        case SD_DHCP6_OPTION_IA_LL:
+        case SD_DHCP6_OPTION_LLADDR:
+        case SD_DHCP6_OPTION_SLAP_QUAD:
+                return false;
+        case SD_DHCP6_OPTION_V6_DOTS_RI:
+        case SD_DHCP6_OPTION_V6_DOTS_ADDRESS:
+        case SD_DHCP6_OPTION_IPV6_ADDRESS_ANDSF:
+                return true;
+        default:
+                return false;
+        }
+}
+
 static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, size_t optlen) {
         DHCP6Option *option;
 
@@ -275,7 +462,7 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
         if (dns_name_is_single_label(fqdn))
                 r--;
 
-        r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_FQDN, 1 + r, buffer);
+        r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_CLIENT_FQDN, 1 + r, buffer);
 
         return r;
 }
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 c7003638..5d2af439 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h
+++ b/src/libnm-systemd-core/src/libsystemd-network/dhcp6-protocol.h
@@ -5,6 +5,7 @@
   Copyright © 2014 Intel Corporation. All rights reserved.
 ***/
 
+#include <errno.h>
 #include <netinet/ip6.h>
 #include <netinet/udp.h>
 
@@ -36,57 +37,83 @@ enum {
         DHCP6_PORT_CLIENT                       = 546,
 };
 
-#define DHCP6_INF_TIMEOUT                       1 * USEC_PER_SEC
-#define DHCP6_INF_MAX_RT                        120 * USEC_PER_SEC
-#define DHCP6_SOL_MAX_DELAY                     1 * USEC_PER_SEC
-#define DHCP6_SOL_TIMEOUT                       1 * USEC_PER_SEC
-#define DHCP6_SOL_MAX_RT                        120 * USEC_PER_SEC
-#define DHCP6_REQ_TIMEOUT                       1 * USEC_PER_SEC
-#define DHCP6_REQ_MAX_RT                        120 * USEC_PER_SEC
+#define DHCP6_INF_TIMEOUT                       (1 * USEC_PER_SEC)
+#define DHCP6_INF_MAX_RT                        (120 * USEC_PER_SEC)
+#define DHCP6_SOL_MAX_DELAY                     (1 * USEC_PER_SEC)
+#define DHCP6_SOL_TIMEOUT                       (1 * USEC_PER_SEC)
+#define DHCP6_SOL_MAX_RT                        (120 * USEC_PER_SEC)
+#define DHCP6_REQ_TIMEOUT                       (1 * USEC_PER_SEC)
+#define DHCP6_REQ_MAX_RT                        (120 * USEC_PER_SEC)
 #define DHCP6_REQ_MAX_RC                        10
-#define DHCP6_REN_TIMEOUT                       10 * USEC_PER_SEC
-#define DHCP6_REN_MAX_RT                        600 * USEC_PER_SEC
-#define DHCP6_REB_TIMEOUT                       10 * USEC_PER_SEC
-#define DHCP6_REB_MAX_RT                        600 * USEC_PER_SEC
+#define DHCP6_REN_TIMEOUT                       (10 * USEC_PER_SEC)
+#define DHCP6_REN_MAX_RT                        (600 * USEC_PER_SEC)
+#define DHCP6_REB_TIMEOUT                       (10 * USEC_PER_SEC)
+#define DHCP6_REB_MAX_RT                        (600 * USEC_PER_SEC)
 
-enum DHCP6State {
-        DHCP6_STATE_STOPPED                     = 0,
-        DHCP6_STATE_INFORMATION_REQUEST         = 1,
-        DHCP6_STATE_SOLICITATION                = 2,
-        DHCP6_STATE_REQUEST                     = 3,
-        DHCP6_STATE_BOUND                       = 4,
-        DHCP6_STATE_RENEW                       = 5,
-        DHCP6_STATE_REBIND                      = 6,
-};
+typedef enum DHCP6State {
+        DHCP6_STATE_STOPPED,
+        DHCP6_STATE_INFORMATION_REQUEST,
+        DHCP6_STATE_SOLICITATION,
+        DHCP6_STATE_REQUEST,
+        DHCP6_STATE_BOUND,
+        DHCP6_STATE_RENEW,
+        DHCP6_STATE_REBIND,
+        _DHCP6_STATE_MAX,
+        _DHCP6_STATE_INVALID = -EINVAL,
+} DHCP6State;
 
-enum {
-        DHCP6_SOLICIT                           = 1,
-        DHCP6_ADVERTISE                         = 2,
-        DHCP6_REQUEST                           = 3,
-        DHCP6_CONFIRM                           = 4,
-        DHCP6_RENEW                             = 5,
-        DHCP6_REBIND                            = 6,
-        DHCP6_REPLY                             = 7,
-        DHCP6_RELEASE                           = 8,
-        DHCP6_DECLINE                           = 9,
-        DHCP6_RECONFIGURE                       = 10,
-        DHCP6_INFORMATION_REQUEST               = 11,
-        DHCP6_RELAY_FORW                        = 12,
-        DHCP6_RELAY_REPL                        = 13,
-        _DHCP6_MESSAGE_MAX                      = 14,
-};
+/* https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-1 */
+typedef enum DHCP6MessageType {
+        DHCP6_MESSAGE_SOLICIT                   = 1,  /* RFC 8415 */
+        DHCP6_MESSAGE_ADVERTISE                 = 2,  /* RFC 8415 */
+        DHCP6_MESSAGE_REQUEST                   = 3,  /* RFC 8415 */
+        DHCP6_MESSAGE_CONFIRM                   = 4,  /* RFC 8415 */
+        DHCP6_MESSAGE_RENEW                     = 5,  /* RFC 8415 */
+        DHCP6_MESSAGE_REBIND                    = 6,  /* RFC 8415 */
+        DHCP6_MESSAGE_REPLY                     = 7,  /* RFC 8415 */
+        DHCP6_MESSAGE_RELEASE                   = 8,  /* RFC 8415 */
+        DHCP6_MESSAGE_DECLINE                   = 9,  /* RFC 8415 */
+        DHCP6_MESSAGE_RECONFIGURE               = 10, /* RFC 8415 */
+        DHCP6_MESSAGE_INFORMATION_REQUEST       = 11, /* RFC 8415 */
+        DHCP6_MESSAGE_RELAY_FORWARD             = 12, /* RFC 8415 */
+        DHCP6_MESSAGE_RELAY_REPLY               = 13, /* RFC 8415 */
+        DHCP6_MESSAGE_LEASE_QUERY               = 14, /* RFC 5007 */
+        DHCP6_MESSAGE_LEASE_QUERY_REPLY         = 15, /* RFC 5007 */
+        DHCP6_MESSAGE_LEASE_QUERY_DONE          = 16, /* RFC 5460 */
+        DHCP6_MESSAGE_LEASE_QUERY_DATA          = 17, /* RFC 5460 */
+        DHCP6_MESSAGE_RECONFIGURE_REQUEST       = 18, /* RFC 6977 */
+        DHCP6_MESSAGE_RECONFIGURE_REPLY         = 19, /* RFC 6977 */
+        DHCP6_MESSAGE_DHCPV4_QUERY              = 20, /* RFC 7341 */
+        DHCP6_MESSAGE_DHCPV4_RESPONSE           = 21, /* RFC 7341 */
+        DHCP6_MESSAGE_ACTIVE_LEASE_QUERY        = 22, /* RFC 7653 */
+        DHCP6_MESSAGE_START_TLS                 = 23, /* RFC 7653 */
+        DHCP6_MESSAGE_BINDING_UPDATE            = 24, /* RFC 8156 */
+        DHCP6_MESSAGE_BINDING_REPLY             = 25, /* RFC 8156 */
+        DHCP6_MESSAGE_POOL_REQUEST              = 26, /* RFC 8156 */
+        DHCP6_MESSAGE_POOL_RESPONSE             = 27, /* RFC 8156 */
+        DHCP6_MESSAGE_UPDATE_REQUEST            = 28, /* RFC 8156 */
+        DHCP6_MESSAGE_UPDATE_REQUEST_ALL        = 29, /* RFC 8156 */
+        DHCP6_MESSAGE_UPDATE_DONE               = 30, /* RFC 8156 */
+        DHCP6_MESSAGE_CONNECT                   = 31, /* RFC 8156 */
+        DHCP6_MESSAGE_CONNECT_REPLY             = 32, /* RFC 8156 */
+        DHCP6_MESSAGE_DISCONNECT                = 33, /* RFC 8156 */
+        DHCP6_MESSAGE_STATE                     = 34, /* RFC 8156 */
+        DHCP6_MESSAGE_CONTACT                   = 35, /* RFC 8156 */
+        _DHCP6_MESSAGE_TYPE_MAX,
+        _DHCP6_MESSAGE_TYPE_INVALID = -EINVAL,
+} DHCP6MessageType;
 
-enum {
+typedef enum DHCP6NTPSubOption {
         DHCP6_NTP_SUBOPTION_SRV_ADDR            = 1,
         DHCP6_NTP_SUBOPTION_MC_ADDR             = 2,
         DHCP6_NTP_SUBOPTION_SRV_FQDN            = 3,
-};
+} DHCP6NTPSubOption;
 
 /*
  * RFC 8415, RFC 5007 and RFC 7653 status codes:
  * https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-5
  */
-enum {
+typedef enum DHCP6Status {
         DHCP6_STATUS_SUCCESS                      = 0,
         DHCP6_STATUS_UNSPEC_FAIL                  = 1,
         DHCP6_STATUS_NO_ADDRS_AVAIL               = 2,
@@ -110,11 +137,12 @@ enum {
         DHCP6_STATUS_SERVER_SHUTTING_DOWN         = 20,
         DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED     = 21,
         DHCP6_STATUS_EXCESSIVE_TIME_SKEW          = 22,
-        _DHCP6_STATUS_MAX                         = 23,
-};
+        _DHCP6_STATUS_MAX,
+        _DHCP6_STATUS_INVALID = -EINVAL,
+} DHCP6Status;
 
-enum {
-        DHCP6_FQDN_FLAG_S = (1 << 0),
-        DHCP6_FQDN_FLAG_O = (1 << 1),
-        DHCP6_FQDN_FLAG_N = (1 << 2),
-};
+typedef enum DHCP6FQDNFlag {
+        DHCP6_FQDN_FLAG_S = 1 << 0,
+        DHCP6_FQDN_FLAG_O = 1 << 1,
+        DHCP6_FQDN_FLAG_N = 1 << 2,
+} DHCP6FQDNFlag;
diff --git a/src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.c b/src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.c
index 3d6d7e42..b4d7c327 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/lldp-neighbor.c
@@ -47,7 +47,7 @@ int lldp_neighbor_prioq_compare_func(const void *a, const void *b) {
         return CMP(x->until, y->until);
 }
 
-_public_ sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) {
+sd_lldp_neighbor *sd_lldp_neighbor_ref(sd_lldp_neighbor *n) {
         if (!n)
                 return NULL;
 
@@ -72,7 +72,7 @@ static sd_lldp_neighbor *lldp_neighbor_free(sd_lldp_neighbor *n) {
         return mfree(n);
 }
 
-_public_ sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) {
+sd_lldp_neighbor *sd_lldp_neighbor_unref(sd_lldp_neighbor *n) {
 
         /* Drops one reference from the neighbor. Note that the object is not freed unless it is already unlinked from
          * the sd_lldp object. */
@@ -360,7 +360,7 @@ bool lldp_neighbor_equal(const sd_lldp_neighbor *a, const sd_lldp_neighbor *b) {
         return memcmp(LLDP_NEIGHBOR_RAW(a), LLDP_NEIGHBOR_RAW(b), a->raw_size) == 0;
 }
 
-_public_ int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) {
+int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct ether_addr* address) {
         assert_return(n, -EINVAL);
         assert_return(address, -EINVAL);
 
@@ -368,7 +368,7 @@ _public_ int sd_lldp_neighbor_get_source_address(sd_lldp_neighbor *n, struct eth
         return 0;
 }
 
-_public_ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) {
+int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struct ether_addr* address) {
         assert_return(n, -EINVAL);
         assert_return(address, -EINVAL);
 
@@ -376,7 +376,7 @@ _public_ int sd_lldp_neighbor_get_destination_address(sd_lldp_neighbor *n, struc
         return 0;
 }
 
-_public_ int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
+int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
         assert_return(n, -EINVAL);
         assert_return(ret, -EINVAL);
         assert_return(size, -EINVAL);
@@ -387,7 +387,7 @@ _public_ int sd_lldp_neighbor_get_raw(sd_lldp_neighbor *n, const void **ret, siz
         return 0;
 }
 
-_public_ int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
+int sd_lldp_neighbor_get_chassis_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
         assert_return(n, -EINVAL);
         assert_return(type, -EINVAL);
         assert_return(ret, -EINVAL);
@@ -440,7 +440,7 @@ static int format_network_address(const void *data, size_t sz, char **ret) {
         return 1;
 }
 
-_public_ int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) {
+int sd_lldp_neighbor_get_chassis_id_as_string(sd_lldp_neighbor *n, const char **ret) {
         char *k;
         int r;
 
@@ -496,7 +496,7 @@ done:
         return 0;
 }
 
-_public_ int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
+int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, const void **ret, size_t *size) {
         assert_return(n, -EINVAL);
         assert_return(type, -EINVAL);
         assert_return(ret, -EINVAL);
@@ -511,7 +511,7 @@ _public_ int sd_lldp_neighbor_get_port_id(sd_lldp_neighbor *n, uint8_t *type, co
         return 0;
 }
 
-_public_ int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) {
+int sd_lldp_neighbor_get_port_id_as_string(sd_lldp_neighbor *n, const char **ret) {
         char *k;
         int r;
 
@@ -566,7 +566,7 @@ done:
         return 0;
 }
 
-_public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) {
+int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) {
         assert_return(n, -EINVAL);
         assert_return(ret_sec, -EINVAL);
 
@@ -574,7 +574,7 @@ _public_ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec) {
         return 0;
 }
 
-_public_ int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) {
+int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret) {
         assert_return(n, -EINVAL);
         assert_return(ret, -EINVAL);
 
@@ -585,7 +585,7 @@ _public_ int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **
         return 0;
 }
 
-_public_ int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) {
+int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret) {
         assert_return(n, -EINVAL);
         assert_return(ret, -EINVAL);
 
@@ -596,7 +596,7 @@ _public_ int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const
         return 0;
 }
 
-_public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) {
+int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret) {
         assert_return(n, -EINVAL);
         assert_return(ret, -EINVAL);
 
@@ -607,7 +607,7 @@ _public_ int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const ch
         return 0;
 }
 
-_public_ int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret) {
+int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret) {
         assert_return(n, -EINVAL);
         assert_return(ret, -EINVAL);
 
@@ -618,7 +618,7 @@ _public_ int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret)
         return 0;
 }
 
-_public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
+int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
         assert_return(n, -EINVAL);
         assert_return(ret, -EINVAL);
 
@@ -629,7 +629,7 @@ _public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint1
         return 0;
 }
 
-_public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
+int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
         assert_return(n, -EINVAL);
         assert_return(ret, -EINVAL);
 
@@ -640,7 +640,7 @@ _public_ int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint
         return 0;
 }
 
-_public_ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
+int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw, size_t raw_size) {
         _cleanup_(sd_lldp_neighbor_unrefp) sd_lldp_neighbor *n = NULL;
         int r;
 
@@ -661,7 +661,7 @@ _public_ int sd_lldp_neighbor_from_raw(sd_lldp_neighbor **ret, const void *raw,
         return r;
 }
 
-_public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
+int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
         assert_return(n, -EINVAL);
 
         assert(n->raw_size >= sizeof(struct ether_header));
@@ -670,7 +670,7 @@ _public_ int sd_lldp_neighbor_tlv_rewind(sd_lldp_neighbor *n) {
         return n->rindex < n->raw_size;
 }
 
-_public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
+int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
         size_t length;
 
         assert_return(n, -EINVAL);
@@ -689,7 +689,7 @@ _public_ int sd_lldp_neighbor_tlv_next(sd_lldp_neighbor *n) {
         return n->rindex < n->raw_size;
 }
 
-_public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
+int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
         assert_return(n, -EINVAL);
         assert_return(type, -EINVAL);
 
@@ -703,7 +703,7 @@ _public_ int sd_lldp_neighbor_tlv_get_type(sd_lldp_neighbor *n, uint8_t *type) {
         return 0;
 }
 
-_public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) {
+int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) {
         uint8_t k;
         int r;
 
@@ -716,7 +716,7 @@ _public_ int sd_lldp_neighbor_tlv_is_type(sd_lldp_neighbor *n, uint8_t type) {
         return type == k;
 }
 
-_public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype) {
+int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t *subtype) {
         const uint8_t *d;
         size_t length;
         int r;
@@ -745,7 +745,7 @@ _public_ int sd_lldp_neighbor_tlv_get_oui(sd_lldp_neighbor *n, uint8_t oui[_SD_A
         return 0;
 }
 
-_public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype) {
+int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[_SD_ARRAY_STATIC 3], uint8_t subtype) {
         uint8_t k[3], st;
         int r;
 
@@ -758,7 +758,7 @@ _public_ int sd_lldp_neighbor_tlv_is_oui(sd_lldp_neighbor *n, const uint8_t oui[
         return memcmp(k, oui, 3) == 0 && st == subtype;
 }
 
-_public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
+int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret, size_t *size) {
         size_t length;
 
         assert_return(n, -EINVAL);
@@ -780,7 +780,7 @@ _public_ int sd_lldp_neighbor_tlv_get_raw(sd_lldp_neighbor *n, const void **ret,
         return 0;
 }
 
-_public_ int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) {
+int sd_lldp_neighbor_get_timestamp(sd_lldp_neighbor *n, clockid_t clock, uint64_t *ret) {
         assert_return(n, -EINVAL);
         assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
         assert_return(clock_supported(clock), -EOPNOTSUPP);
diff --git a/src/libnm-systemd-core/src/libsystemd-network/lldp-network.c b/src/libnm-systemd-core/src/libsystemd-network/lldp-network.c
index 34a4f3f0..00eb9703 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/lldp-network.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/lldp-network.c
@@ -11,7 +11,6 @@
 #include "socket-util.h"
 
 int lldp_network_bind_raw_socket(int ifindex) {
-
         static const struct sock_filter filter[] = {
                 BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(struct ethhdr, h_dest)),      /* A <- 4 bytes of destination MAC */
                 BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0x0180c200, 1, 0),                    /* A != 01:80:c2:00 */
@@ -26,26 +25,21 @@ int lldp_network_bind_raw_socket(int ifindex) {
                 BPF_STMT(BPF_RET + BPF_K, 0),                                             /* drop packet */
                 BPF_STMT(BPF_RET + BPF_K, UINT32_MAX),                                    /* accept packet */
         };
-
         static const struct sock_fprog fprog = {
                 .len = ELEMENTSOF(filter),
                 .filter = (struct sock_filter*) filter,
         };
-
         struct packet_mreq mreq = {
                 .mr_ifindex = ifindex,
                 .mr_type = PACKET_MR_MULTICAST,
                 .mr_alen = ETH_ALEN,
                 .mr_address = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x00 }
         };
-
         union sockaddr_union saddrll = {
                 .ll.sll_family = AF_PACKET,
                 .ll.sll_ifindex = ifindex,
         };
-
         _cleanup_close_ int fd = -1;
-        int r;
 
         assert(ifindex > 0);
 
@@ -54,29 +48,24 @@ int lldp_network_bind_raw_socket(int ifindex) {
         if (fd < 0)
                 return -errno;
 
-        r = setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
-        if (r < 0)
+        if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0)
                 return -errno;
 
         /* customer bridge */
-        r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
-        if (r < 0)
+        if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
                 return -errno;
 
         /* non TPMR bridge */
         mreq.mr_address[ETH_ALEN - 1] = 0x03;
-        r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
-        if (r < 0)
+        if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
                 return -errno;
 
         /* nearest bridge */
         mreq.mr_address[ETH_ALEN - 1] = 0x0E;
-        r = setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
-        if (r < 0)
+        if (setsockopt(fd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0)
                 return -errno;
 
-        r = bind(fd, &saddrll.sa, sizeof(saddrll.ll));
-        if (r < 0)
+        if (bind(fd, &saddrll.sa, sizeof(saddrll.ll)) < 0)
                 return -errno;
 
         return TAKE_FD(fd);
diff --git a/src/libnm-systemd-core/src/libsystemd-network/network-common.h b/src/libnm-systemd-core/src/libsystemd-network/network-common.h
index d43b76d4..2b0e3b56 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/network-common.h
+++ b/src/libnm-systemd-core/src/libsystemd-network/network-common.h
@@ -8,8 +8,10 @@
                 int _e = (error);                                       \
                 if (DEBUG_LOGGING) {                                    \
                         const char *_n = NULL;                          \
+                        type *_v = (val);                               \
                                                                         \
-                        (void) type##_get_ifname(val, &_n);             \
+                        if (_v)                                         \
+                                (void) type##_get_ifname(_v, &_n);      \
                         log_interface_full_errno_zerook(                \
                                 _n, LOG_DEBUG, _e, prefix fmt,          \
                                 ##__VA_ARGS__);                         \
diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c
index cb984b6e..1600f806 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-client.c
@@ -837,7 +837,8 @@ static int client_message_init(
                 return -ENOMEM;
 
         r = dhcp_message_init(&packet->dhcp, BOOTREQUEST, client->xid, type,
-                              client->arp_type, optlen, &optoffset);
+                              client->arp_type, client->mac_addr_len, client->mac_addr,
+                              optlen, &optoffset);
         if (r < 0)
                 return r;
 
@@ -853,7 +854,7 @@ static int client_message_init(
         secs = ((time_now - client->start_time) / USEC_PER_SEC) ? : 1;
         packet->dhcp.secs = htobe16(secs);
 
-        /* RFC2132 section 4.1
+        /* RFC2131 section 4.1
            A client that cannot receive unicast IP datagrams until its protocol
            software has been configured with an IP address SHOULD set the
            BROADCAST bit in the 'flags' field to 1 in any DHCPDISCOVER or
@@ -867,15 +868,6 @@ static int client_message_init(
         if (client->request_broadcast || client->arp_type != ARPHRD_ETHER)
                 packet->dhcp.flags = htobe16(0x8000);
 
-        /* RFC2132 section 4.1.1:
-           The client MUST include its hardware address in the ’chaddr’ field, if
-           necessary for delivery of DHCP reply messages.  Non-Ethernet
-           interfaces will leave 'chaddr' empty and use the client identifier
-           instead (eg, RFC 4390 section 2.1).
-         */
-        if (client->arp_type == ARPHRD_ETHER)
-                memcpy(&packet->dhcp.chaddr, &client->mac_addr, ETH_ALEN);
-
         /* If no client identifier exists, construct an RFC 4361-compliant one */
         if (client->client_id_len == 0) {
                 size_t duid_len;
@@ -1935,13 +1927,13 @@ static int client_receive_message_udp(
         assert(client);
 
         buflen = next_datagram_size_fd(fd);
-        if (buflen == -ENETDOWN)
-                /* the link is down. Don't return an error or the I/O event
-                   source will be disconnected and we won't be able to receive
-                   packets again when the link comes back. */
+        if (buflen < 0) {
+                if (ERRNO_IS_TRANSIENT(buflen) || ERRNO_IS_DISCONNECT(buflen))
+                        return 0;
+
+                log_dhcp_client_errno(client, buflen, "Failed to determine datagram size to read, ignoring: %m");
                 return 0;
-        if (buflen < 0)
-                return buflen;
+        }
 
         message = malloc0(buflen);
         if (!message)
@@ -1949,12 +1941,11 @@ static int client_receive_message_udp(
 
         len = recv(fd, message, buflen, 0);
         if (len < 0) {
-                /* see comment above for why we shouldn't error out on ENETDOWN. */
-                if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN))
+                if (ERRNO_IS_TRANSIENT(errno) || ERRNO_IS_DISCONNECT(errno))
                         return 0;
 
-                return log_dhcp_client_errno(client, errno,
-                                             "Could not receive message from UDP socket: %m");
+                log_dhcp_client_errno(client, errno, "Could not receive message from UDP socket, ignoring: %m");
+                return 0;
         }
         if ((size_t) len < sizeof(DHCPMessage)) {
                 log_dhcp_client(client, "Too small to be a DHCP message: ignoring");
@@ -2000,7 +1991,9 @@ static int client_receive_message_udp(
                 return 0;
         }
 
-        return client_handle_message(client, message, len);
+        log_dhcp_client(client, "Received message from UDP socket, processing.");
+        (void) client_handle_message(client, message, len);
+        return 0;
 }
 
 static int client_receive_message_raw(
@@ -2028,10 +2021,13 @@ static int client_receive_message_raw(
         assert(client);
 
         buflen = next_datagram_size_fd(fd);
-        if (buflen == -ENETDOWN)
+        if (buflen < 0) {
+                if (ERRNO_IS_TRANSIENT(buflen) || ERRNO_IS_DISCONNECT(buflen))
+                        return 0;
+
+                log_dhcp_client_errno(client, buflen, "Failed to determine datagram size to read, ignoring: %m");
                 return 0;
-        if (buflen < 0)
-                return buflen;
+        }
 
         packet = malloc0(buflen);
         if (!packet)
@@ -2040,12 +2036,13 @@ static int client_receive_message_raw(
         iov = IOVEC_MAKE(packet, buflen);
 
         len = recvmsg_safe(fd, &msg, 0);
-        if (IN_SET(len, -EAGAIN, -EINTR, -ENETDOWN))
-                return 0;
-        if (len < 0)
-                return log_dhcp_client_errno(client, len,
-                                             "Could not receive message from raw socket: %m");
+        if (len < 0) {
+                if (ERRNO_IS_TRANSIENT(len) || ERRNO_IS_DISCONNECT(len))
+                        return 0;
 
+                log_dhcp_client_errno(client, len, "Could not receive message from raw socket, ignoring: %m");
+                return 0;
+        }
         if ((size_t) len < sizeof(DHCPPacket))
                 return 0;
 
@@ -2061,7 +2058,9 @@ static int client_receive_message_raw(
 
         len -= DHCP_IP_UDP_SIZE;
 
-        return client_handle_message(client, &packet->dhcp, len);
+        log_dhcp_client(client, "Received message from RAW socket, processing.");
+        (void) client_handle_message(client, &packet->dhcp, len);
+        return 0;
 }
 
 int sd_dhcp_client_send_renew(sd_dhcp_client *client) {
diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-lease.c b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-lease.c
index 4cd9295c..7a250534 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/sd-dhcp-lease.c
@@ -107,12 +107,13 @@ int sd_dhcp_lease_get_servers(
         assert_return(lease, -EINVAL);
         assert_return(what >= 0, -EINVAL);
         assert_return(what < _SD_DHCP_LEASE_SERVER_TYPE_MAX, -EINVAL);
-        assert_return(addr, -EINVAL);
 
         if (lease->servers[what].size <= 0)
                 return -ENODATA;
 
-        *addr = lease->servers[what].addr;
+        if (addr)
+                *addr = lease->servers[what].addr;
+
         return (int) lease->servers[what].size;
 }
 
@@ -252,6 +253,33 @@ int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains) {
         return -ENODATA;
 }
 
+int sd_dhcp_lease_get_6rd(
+                sd_dhcp_lease *lease,
+                uint8_t *ret_ipv4masklen,
+                uint8_t *ret_prefixlen,
+                struct in6_addr *ret_prefix,
+                const struct in_addr **ret_br_addresses,
+                size_t *ret_n_br_addresses) {
+
+        assert_return(lease, -EINVAL);
+
+        if (lease->sixrd_n_br_addresses <= 0)
+                return -ENODATA;
+
+        if (ret_ipv4masklen)
+                *ret_ipv4masklen = lease->sixrd_ipv4masklen;
+        if (ret_prefixlen)
+                *ret_prefixlen = lease->sixrd_prefixlen;
+        if (ret_prefix)
+                *ret_prefix = lease->sixrd_prefix;
+        if (ret_br_addresses)
+                *ret_br_addresses = lease->sixrd_br_addresses;
+        if (ret_n_br_addresses)
+                *ret_n_br_addresses = lease->sixrd_n_br_addresses;
+
+        return 0;
+}
+
 int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len) {
         assert_return(lease, -EINVAL);
         assert_return(data, -EINVAL);
@@ -290,6 +318,7 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) {
         free(lease->client_id);
         free(lease->vendor_specific);
         strv_free(lease->search_domains);
+        free(lease->sixrd_br_addresses);
         return mfree(lease);
 }
 
@@ -535,6 +564,61 @@ static int lease_parse_classless_routes(
         return 0;
 }
 
+static int lease_parse_6rd(sd_dhcp_lease *lease, const uint8_t *option, size_t len) {
+        uint8_t ipv4masklen, prefixlen;
+        struct in6_addr prefix;
+        _cleanup_free_ struct in_addr *br_addresses = NULL;
+        size_t n_br_addresses;
+
+        assert(lease);
+        assert(option);
+
+        /* See RFC 5969 Section 7.1.1 */
+
+        if (lease->sixrd_n_br_addresses > 0)
+                /* Multiple 6rd option?? */
+                return -EINVAL;
+
+        /* option-length: The length of the DHCP option in octets (22 octets with one BR IPv4 address). */
+        if (len < 2 + sizeof(struct in6_addr) + sizeof(struct in_addr) ||
+            (len - 2 - sizeof(struct in6_addr)) % sizeof(struct in_addr) != 0)
+                return -EINVAL;
+
+        /* IPv4MaskLen: The number of high-order bits that are identical across all CE IPv4 addresses
+         *              within a given 6rd domain. This may be any value between 0 and 32. Any value
+         *              greater than 32 is invalid. */
+        ipv4masklen = option[0];
+        if (ipv4masklen > 32)
+                return -EINVAL;
+
+        /* 6rdPrefixLen: The IPv6 prefix length of the SP's 6rd IPv6 prefix in number of bits. For the
+         *               purpose of bounds checking by DHCP option processing, the sum of
+         *               (32 - IPv4MaskLen) + 6rdPrefixLen MUST be less than or equal to 128. */
+        prefixlen = option[1];
+        if (32 - ipv4masklen + prefixlen > 128)
+                return -EINVAL;
+
+        /* 6rdPrefix: The service provider's 6rd IPv6 prefix represented as a 16-octet IPv6 address.
+         *            The bits in the prefix after the 6rdPrefixlen number of bits are reserved and
+         *            MUST be initialized to zero by the sender and ignored by the receiver. */
+        memcpy(&prefix, option + 2, sizeof(struct in6_addr));
+        (void) in6_addr_mask(&prefix, prefixlen);
+
+        /* 6rdBRIPv4Address: One or more IPv4 addresses of the 6rd Border Relay(s) for a given 6rd domain. */
+        n_br_addresses = (len - 2 - sizeof(struct in6_addr)) / sizeof(struct in_addr);
+        br_addresses = newdup(struct in_addr, option + 2 + sizeof(struct in6_addr), n_br_addresses);
+        if (!br_addresses)
+                return -ENOMEM;
+
+        lease->sixrd_ipv4masklen = ipv4masklen;
+        lease->sixrd_prefixlen = prefixlen;
+        lease->sixrd_prefix = prefix;
+        lease->sixrd_br_addresses = TAKE_PTR(br_addresses);
+        lease->sixrd_n_br_addresses = n_br_addresses;
+
+        return 0;
+}
+
 int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void *userdata) {
         sd_dhcp_lease *lease = userdata;
         int r;
@@ -693,7 +777,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
                 }
 
                 if (!timezone_is_valid(tz, LOG_DEBUG)) {
-                        log_debug_errno(r, "Timezone is not valid, ignoring: %m");
+                        log_debug("Timezone is not valid, ignoring.");
                         return 0;
                 }
 
@@ -720,6 +804,12 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
                 lease->vendor_specific_len = len;
                 break;
 
+        case SD_DHCP_OPTION_6RD:
+                r = lease_parse_6rd(lease, option, len);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse 6rd option, ignoring: %m");
+                break;
+
         case SD_DHCP_OPTION_PRIVATE_BASE ... SD_DHCP_OPTION_PRIVATE_LAST:
                 r = dhcp_lease_insert_private_option(lease, code, option, len);
                 if (r < 0)
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 5398bdff..0c2206ff 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
@@ -27,6 +27,7 @@
 #include "hexdecoct.h"
 #include "hostname-util.h"
 #include "in-addr-util.h"
+#include "io-util.h"
 #include "network-common.h"
 #include "random-util.h"
 #include "socket-util.h"
@@ -41,16 +42,16 @@
 #define IRT_MINIMUM (600 * USEC_PER_SEC)
 
 /* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
-enum {
-        DHCP6_REQUEST_IA_NA                     = 1,
-        DHCP6_REQUEST_IA_TA                     = 2, /* currently not used */
-        DHCP6_REQUEST_IA_PD                     = 4,
-};
+typedef enum DHCP6RequestIA {
+        DHCP6_REQUEST_IA_NA = 1 << 0,
+        DHCP6_REQUEST_IA_TA = 1 << 1, /* currently not used */
+        DHCP6_REQUEST_IA_PD = 1 << 2,
+} DHCP6RequestIA;
 
 struct sd_dhcp6_client {
         unsigned n_ref;
 
-        enum DHCP6State state;
+        DHCP6State state;
         sd_event *event;
         int event_priority;
         int ifindex;
@@ -64,7 +65,7 @@ struct sd_dhcp6_client {
         DHCP6IA ia_pd;
         sd_event_source *timeout_t1;
         sd_event_source *timeout_t2;
-        unsigned request;
+        DHCP6RequestIA request_ia;
         be32_t transaction_id;
         usec_t transaction_start;
         struct sd_dhcp6_lease *lease;
@@ -102,20 +103,42 @@ static const uint16_t default_req_opts[] = {
         SD_DHCP6_OPTION_SNTP_SERVERS,
 };
 
-const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
-        [DHCP6_SOLICIT] = "SOLICIT",
-        [DHCP6_ADVERTISE] = "ADVERTISE",
-        [DHCP6_REQUEST] = "REQUEST",
-        [DHCP6_CONFIRM] = "CONFIRM",
-        [DHCP6_RENEW] = "RENEW",
-        [DHCP6_REBIND] = "REBIND",
-        [DHCP6_REPLY] = "REPLY",
-        [DHCP6_RELEASE] = "RELEASE",
-        [DHCP6_DECLINE] = "DECLINE",
-        [DHCP6_RECONFIGURE] = "RECONFIGURE",
-        [DHCP6_INFORMATION_REQUEST] = "INFORMATION-REQUEST",
-        [DHCP6_RELAY_FORW] = "RELAY-FORW",
-        [DHCP6_RELAY_REPL] = "RELAY-REPL",
+const char * dhcp6_message_type_table[_DHCP6_MESSAGE_TYPE_MAX] = {
+        [DHCP6_MESSAGE_SOLICIT]             = "Solicit",
+        [DHCP6_MESSAGE_ADVERTISE]           = "Advertise",
+        [DHCP6_MESSAGE_REQUEST]             = "Request",
+        [DHCP6_MESSAGE_CONFIRM]             = "Confirm",
+        [DHCP6_MESSAGE_RENEW]               = "Renew",
+        [DHCP6_MESSAGE_REBIND]              = "Rebind",
+        [DHCP6_MESSAGE_REPLY]               = "Reply",
+        [DHCP6_MESSAGE_RELEASE]             = "Release",
+        [DHCP6_MESSAGE_DECLINE]             = "Decline",
+        [DHCP6_MESSAGE_RECONFIGURE]         = "Reconfigure",
+        [DHCP6_MESSAGE_INFORMATION_REQUEST] = "Information Request",
+        [DHCP6_MESSAGE_RELAY_FORWARD]       = "Relay Forward",
+        [DHCP6_MESSAGE_RELAY_REPLY]         = "Relay Reply",
+        [DHCP6_MESSAGE_LEASE_QUERY]         = "Lease Query",
+        [DHCP6_MESSAGE_LEASE_QUERY_REPLY]   = "Lease Query Reply",
+        [DHCP6_MESSAGE_LEASE_QUERY_DONE]    = "Lease Query Done",
+        [DHCP6_MESSAGE_LEASE_QUERY_DATA]    = "Lease Query Data",
+        [DHCP6_MESSAGE_RECONFIGURE_REQUEST] = "Reconfigure Request",
+        [DHCP6_MESSAGE_RECONFIGURE_REPLY]   = "Reconfigure Reply",
+        [DHCP6_MESSAGE_DHCPV4_QUERY]        = "DHCPv4 Query",
+        [DHCP6_MESSAGE_DHCPV4_RESPONSE]     = "DHCPv4 Response",
+        [DHCP6_MESSAGE_ACTIVE_LEASE_QUERY]  = "Active Lease Query",
+        [DHCP6_MESSAGE_START_TLS]           = "Start TLS",
+        [DHCP6_MESSAGE_BINDING_UPDATE]      = "Binding Update",
+        [DHCP6_MESSAGE_BINDING_REPLY]       = "Binding Reply",
+        [DHCP6_MESSAGE_POOL_REQUEST]        = "Pool Request",
+        [DHCP6_MESSAGE_POOL_RESPONSE]       = "Pool Response",
+        [DHCP6_MESSAGE_UPDATE_REQUEST]      = "Update Request",
+        [DHCP6_MESSAGE_UPDATE_REQUEST_ALL]  = "Update Request All",
+        [DHCP6_MESSAGE_UPDATE_DONE]         = "Update Done",
+        [DHCP6_MESSAGE_CONNECT]             = "Connect",
+        [DHCP6_MESSAGE_CONNECT_REPLY]       = "Connect Reply",
+        [DHCP6_MESSAGE_DISCONNECT]          = "Disconnect",
+        [DHCP6_MESSAGE_STATE]               = "State",
+        [DHCP6_MESSAGE_CONTACT]             = "Contact",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
@@ -151,7 +174,7 @@ DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
 #define DHCP6_CLIENT_DONT_DESTROY(client) \
         _cleanup_(sd_dhcp6_client_unrefp) _unused_ sd_dhcp6_client *_dont_destroy_##client = sd_dhcp6_client_ref(client)
 
-static int client_start(sd_dhcp6_client *client, enum DHCP6State state);
+static int client_start(sd_dhcp6_client *client, DHCP6State state);
 
 int sd_dhcp6_client_set_callback(
                 sd_dhcp6_client *client,
@@ -234,10 +257,6 @@ int sd_dhcp6_client_set_mac(
                 return 0;
         }
 
-        if (client->mac_addr_len == addr_len &&
-            memcmp(&client->mac_addr, addr, addr_len) == 0)
-                return 0;
-
         memcpy(&client->mac_addr, addr, addr_len);
         client->mac_addr_len = addr_len;
         client->arp_type = arp_type;
@@ -475,7 +494,7 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
         assert_return(client, -EINVAL);
         assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
 
-        if (option <= 0 || option >= UINT8_MAX)
+        if (!dhcp6_option_can_request(option))
                 return -EINVAL;
 
         for (t = 0; t < client->req_opts_len; t++)
@@ -548,7 +567,7 @@ int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegati
         assert_return(client, -EINVAL);
         assert_return(delegation, -EINVAL);
 
-        *delegation = FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD);
+        *delegation = FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD);
 
         return 0;
 }
@@ -556,7 +575,7 @@ int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegati
 int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, int delegation) {
         assert_return(client, -EINVAL);
 
-        SET_FLAG(client->request, DHCP6_REQUEST_IA_PD, delegation);
+        SET_FLAG(client->request_ia, DHCP6_REQUEST_IA_PD, delegation);
 
         return 0;
 }
@@ -565,7 +584,7 @@ int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request) {
         assert_return(client, -EINVAL);
         assert_return(request, -EINVAL);
 
-        *request = FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA);
+        *request = FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA);
 
         return 0;
 }
@@ -573,7 +592,7 @@ int sd_dhcp6_client_get_address_request(sd_dhcp6_client *client, int *request) {
 int sd_dhcp6_client_set_address_request(sd_dhcp6_client *client, int request) {
         assert_return(client, -EINVAL);
 
-        SET_FLAG(client->request, DHCP6_REQUEST_IA_NA, request);
+        SET_FLAG(client->request_ia, DHCP6_REQUEST_IA_NA, request);
 
         return 0;
 }
@@ -678,11 +697,11 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
 
         switch(client->state) {
         case DHCP6_STATE_INFORMATION_REQUEST:
-                message->type = DHCP6_INFORMATION_REQUEST;
+                message->type = DHCP6_MESSAGE_INFORMATION_REQUEST;
 
                 if (client->mudurl) {
                         r = dhcp6_option_append(&opt, &optlen,
-                                                SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
+                                                SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl),
                                                 client->mudurl);
                         if (r < 0)
                                 return r;
@@ -691,14 +710,14 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                 break;
 
         case DHCP6_STATE_SOLICITATION:
-                message->type = DHCP6_SOLICIT;
+                message->type = DHCP6_MESSAGE_SOLICIT;
 
                 r = dhcp6_option_append(&opt, &optlen,
                                         SD_DHCP6_OPTION_RAPID_COMMIT, 0, NULL);
                 if (r < 0)
                         return r;
 
-                if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
+                if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA)) {
                         r = dhcp6_option_append_ia(&opt, &optlen,
                                                    &client->ia_na);
                         if (r < 0)
@@ -713,7 +732,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
 
                 if (client->mudurl) {
                         r = dhcp6_option_append(&opt, &optlen,
-                                                SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
+                                                SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl),
                                                 client->mudurl);
                         if (r < 0)
                                 return r;
@@ -738,7 +757,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                                 return r;
                 }
 
-                if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
+                if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD)) {
                         r = dhcp6_option_append_pd(&opt, &optlen, &client->ia_pd, &client->hint_pd_prefix);
                         if (r < 0)
                                 return r;
@@ -750,9 +769,9 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
         case DHCP6_STATE_RENEW:
 
                 if (client->state == DHCP6_STATE_REQUEST)
-                        message->type = DHCP6_REQUEST;
+                        message->type = DHCP6_MESSAGE_REQUEST;
                 else
-                        message->type = DHCP6_RENEW;
+                        message->type = DHCP6_MESSAGE_RENEW;
 
                 r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_SERVERID,
                                         client->lease->serverid_len,
@@ -760,7 +779,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                 if (r < 0)
                         return r;
 
-                if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
+                if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
                         r = dhcp6_option_append_ia(&opt, &optlen,
                                                    &client->lease->ia);
                         if (r < 0)
@@ -775,7 +794,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
 
                 if (client->mudurl) {
                         r = dhcp6_option_append(&opt, &optlen,
-                                                SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
+                                                SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl),
                                                 client->mudurl);
                         if (r < 0)
                                 return r;
@@ -799,7 +818,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                                 return r;
                 }
 
-                if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
+                if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
                         r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL);
                         if (r < 0)
                                 return r;
@@ -808,9 +827,9 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                 break;
 
         case DHCP6_STATE_REBIND:
-                message->type = DHCP6_REBIND;
+                message->type = DHCP6_MESSAGE_REBIND;
 
-                if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA)) {
+                if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA)) {
                         r = dhcp6_option_append_ia(&opt, &optlen, &client->lease->ia);
                         if (r < 0)
                                 return r;
@@ -824,7 +843,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
 
                 if (client->mudurl) {
                         r = dhcp6_option_append(&opt, &optlen,
-                                                SD_DHCP6_OPTION_MUD_URL, strlen(client->mudurl),
+                                                SD_DHCP6_OPTION_MUD_URL_V6, strlen(client->mudurl),
                                                 client->mudurl);
                         if (r < 0)
                                 return r;
@@ -848,7 +867,7 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                                 return r;
                 }
 
-                if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
+                if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
                         r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL);
                         if (r < 0)
                                 return r;
@@ -859,6 +878,8 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
         case DHCP6_STATE_STOPPED:
         case DHCP6_STATE_BOUND:
                 return -EINVAL;
+        default:
+                assert_not_reached();
         }
 
         r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ORO,
@@ -873,14 +894,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
         if (r < 0)
                 return r;
 
-        elapsed_usec = time_now - client->transaction_start;
-        if (elapsed_usec < 0xffff * USEC_PER_MSEC * 10)
-                elapsed_time = htobe16(elapsed_usec / USEC_PER_MSEC / 10);
-        else
-                elapsed_time = 0xffff;
-
-        r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME,
-                                sizeof(elapsed_time), &elapsed_time);
+        /* RFC 8415 Section 21.9.
+         * A client MUST include an Elapsed Time option in messages to indicate how long the client has
+         * been trying to complete a DHCP message exchange. */
+        elapsed_usec = MIN(usec_sub_unsigned(time_now, client->transaction_start) / USEC_PER_MSEC / 10, (usec_t) UINT16_MAX);
+        elapsed_time = htobe16(elapsed_usec);
+        r = dhcp6_option_append(&opt, &optlen, SD_DHCP6_OPTION_ELAPSED_TIME, sizeof(elapsed_time), &elapsed_time);
         if (r < 0)
                 return r;
 
@@ -936,7 +955,7 @@ static int client_timeout_t1(sd_event_source *s, uint64_t usec, void *userdata)
 static int client_timeout_resend_expire(sd_event_source *s, uint64_t usec, void *userdata) {
         sd_dhcp6_client *client = userdata;
         DHCP6_CLIENT_DONT_DESTROY(client);
-        enum DHCP6State state;
+        DHCP6State state;
 
         assert(s);
         assert(client);
@@ -980,7 +999,7 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
 
         case DHCP6_STATE_SOLICITATION:
 
-                if (client->retransmit_count && client->lease) {
+                if (client->retransmit_count > 0 && client->lease) {
                         client_start(client, DHCP6_STATE_REQUEST);
                         return 0;
                 }
@@ -1027,6 +1046,8 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
         case DHCP6_STATE_STOPPED:
         case DHCP6_STATE_BOUND:
                 return 0;
+        default:
+                assert_not_reached();
         }
 
         if (max_retransmit_count > 0 &&
@@ -1051,8 +1072,8 @@ static int client_timeout_resend(sd_event_source *s, uint64_t usec, void *userda
                         client->retransmit_time += init_retransmit_time / 10;
 
         } else {
-                if (max_retransmit_time > 0 &&
-                    client->retransmit_time > max_retransmit_time / 2)
+                assert(max_retransmit_time > 0);
+                if (client->retransmit_time > max_retransmit_time / 2)
                         client->retransmit_time = client_timeout_compute_random(max_retransmit_time);
                 else
                         client->retransmit_time += client_timeout_compute_random(client->retransmit_time);
@@ -1113,7 +1134,7 @@ static int client_ensure_iaid(sd_dhcp6_client *client) {
         return 0;
 }
 
-static int client_parse_message(
+int client_parse_message(
                 sd_dhcp6_client *client,
                 DHCP6Message *message,
                 size_t len,
@@ -1121,7 +1142,6 @@ static int client_parse_message(
 
         uint32_t lt_t1 = UINT32_MAX, lt_t2 = UINT32_MAX;
         usec_t irt = IRT_DEFAULT;
-        bool clientid = false;
         int r;
 
         assert(client);
@@ -1141,23 +1161,19 @@ static int client_parse_message(
 
                 switch (optcode) {
                 case SD_DHCP6_OPTION_CLIENTID:
-                        if (clientid)
-                                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple clientids",
+                        if (dhcp6_lease_get_clientid(lease, NULL, NULL) >= 0)
+                                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple client IDs",
                                                               dhcp6_message_type_to_string(message->type));
 
-                        if (optlen != client->duid_len ||
-                            memcmp(&client->duid, optval, optlen) != 0)
-                                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s DUID does not match",
-                                                              dhcp6_message_type_to_string(message->type));
-
-                        clientid = true;
+                        r = dhcp6_lease_set_clientid(lease, optval, optlen);
+                        if (r < 0)
+                                return r;
 
                         break;
 
                 case SD_DHCP6_OPTION_SERVERID:
-                        r = dhcp6_lease_get_serverid(lease, NULL, NULL);
-                        if (r >= 0)
-                                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple serverids",
+                        if (dhcp6_lease_get_serverid(lease, NULL, NULL) >= 0)
+                                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s contains multiple server IDs",
                                                               dhcp6_message_type_to_string(message->type));
 
                         r = dhcp6_lease_set_serverid(lease, optval, optlen);
@@ -1255,35 +1271,35 @@ static int client_parse_message(
                 case SD_DHCP6_OPTION_DNS_SERVERS:
                         r = dhcp6_lease_add_dns(lease, optval, optlen);
                         if (r < 0)
-                                return r;
+                                log_dhcp6_client_errno(client, r, "Failed to parse DNS server option, ignoring: %m");
 
                         break;
 
                 case SD_DHCP6_OPTION_DOMAIN_LIST:
                         r = dhcp6_lease_add_domains(lease, optval, optlen);
                         if (r < 0)
-                                return r;
+                                log_dhcp6_client_errno(client, r, "Failed to parse domain list option, ignoring: %m");
 
                         break;
 
                 case SD_DHCP6_OPTION_NTP_SERVER:
                         r = dhcp6_lease_add_ntp(lease, optval, optlen);
                         if (r < 0)
-                                return r;
+                                log_dhcp6_client_errno(client, r, "Failed to parse NTP server option, ignoring: %m");
 
                         break;
 
                 case SD_DHCP6_OPTION_SNTP_SERVERS:
                         r = dhcp6_lease_add_sntp(lease, optval, optlen);
                         if (r < 0)
-                                return r;
+                                log_dhcp6_client_errno(client, r, "Failed to parse SNTP server option, ignoring: %m");
 
                         break;
 
-                case SD_DHCP6_OPTION_FQDN:
+                case SD_DHCP6_OPTION_CLIENT_FQDN:
                         r = dhcp6_lease_set_fqdn(lease, optval, optlen);
                         if (r < 0)
-                                return r;
+                                log_dhcp6_client_errno(client, r, "Failed to parse FQDN option, ignoring: %m");
 
                         break;
 
@@ -1296,8 +1312,15 @@ static int client_parse_message(
                 }
         }
 
-        if (!clientid)
-                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s has incomplete options",
+        uint8_t *clientid;
+        size_t clientid_len;
+        if (dhcp6_lease_get_clientid(lease, &clientid, &clientid_len) < 0)
+                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s message does not contain client ID. Ignoring.",
+                                              dhcp6_message_type_to_string(message->type));
+
+        if (clientid_len != client->duid_len ||
+            memcmp(clientid, &client->duid, clientid_len) != 0)
+                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "The client ID in %s message does not match. Ignoring.",
                                               dhcp6_message_type_to_string(message->type));
 
         if (client->state != DHCP6_STATE_INFORMATION_REQUEST) {
@@ -1325,21 +1348,32 @@ static int client_parse_message(
         return 0;
 }
 
-static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, size_t len) {
+static int client_receive_reply(
+                sd_dhcp6_client *client,
+                DHCP6Message *reply,
+                size_t len,
+                const triple_timestamp *t,
+                const struct in6_addr *server_address) {
+
         _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
         bool rapid_commit;
         int r;
 
         assert(client);
         assert(reply);
+        assert(t);
 
-        if (reply->type != DHCP6_REPLY)
+        if (reply->type != DHCP6_MESSAGE_REPLY)
                 return 0;
 
         r = dhcp6_lease_new(&lease);
         if (r < 0)
                 return -ENOMEM;
 
+        lease->timestamp = *t;
+        if (server_address)
+                lease->server_address = *server_address;
+
         r = client_parse_message(client, reply, len, lease);
         if (r < 0)
                 return r;
@@ -1359,18 +1393,32 @@ static int client_receive_reply(sd_dhcp6_client *client, DHCP6Message *reply, si
         return DHCP6_STATE_BOUND;
 }
 
-static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *advertise, size_t len) {
+static int client_receive_advertise(
+                sd_dhcp6_client *client,
+                DHCP6Message *advertise,
+                size_t len,
+                const triple_timestamp *t,
+                const struct in6_addr *server_address) {
+
         _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
         uint8_t pref_advertise = 0, pref_lease = 0;
         int r;
 
-        if (advertise->type != DHCP6_ADVERTISE)
+        assert(client);
+        assert(advertise);
+        assert(t);
+
+        if (advertise->type != DHCP6_MESSAGE_ADVERTISE)
                 return 0;
 
         r = dhcp6_lease_new(&lease);
         if (r < 0)
                 return r;
 
+        lease->timestamp = *t;
+        if (server_address)
+                lease->server_address = *server_address;
+
         r = client_parse_message(client, advertise, len, lease);
         if (r < 0)
                 return r;
@@ -1401,7 +1449,22 @@ static int client_receive_message(
 
         sd_dhcp6_client *client = userdata;
         DHCP6_CLIENT_DONT_DESTROY(client);
+        /* This needs to be initialized with zero. See #20741. */
+        CMSG_BUFFER_TYPE(CMSG_SPACE_TIMEVAL) control = {};
+        struct iovec iov;
+        union sockaddr_union sa = {};
+        struct msghdr msg = {
+                .msg_name = &sa.sa,
+                .msg_namelen = sizeof(sa),
+                .msg_iov = &iov,
+                .msg_iovlen = 1,
+                .msg_control = &control,
+                .msg_controllen = sizeof(control),
+        };
+        struct cmsghdr *cmsg;
+        triple_timestamp t = {};
         _cleanup_free_ DHCP6Message *message = NULL;
+        struct in6_addr *server_address = NULL;
         ssize_t buflen, len;
         int r = 0;
 
@@ -1410,52 +1473,59 @@ static int client_receive_message(
         assert(client->event);
 
         buflen = next_datagram_size_fd(fd);
-        if (buflen == -ENETDOWN)
-                /* the link is down. Don't return an error or the I/O event
-                   source will be disconnected and we won't be able to receive
-                   packets again when the link comes back. */
+        if (buflen < 0) {
+                if (ERRNO_IS_TRANSIENT(buflen) || ERRNO_IS_DISCONNECT(buflen))
+                        return 0;
+
+                log_dhcp6_client_errno(client, buflen, "Failed to determine datagram size to read, ignoring: %m");
                 return 0;
-        if (buflen < 0)
-                return buflen;
+        }
 
         message = malloc(buflen);
         if (!message)
                 return -ENOMEM;
 
-        len = recv(fd, message, buflen, 0);
+        iov = IOVEC_MAKE(message, buflen);
+
+        len = recvmsg_safe(fd, &msg, MSG_DONTWAIT);
         if (len < 0) {
-                /* see comment above for why we shouldn't error out on ENETDOWN. */
-                if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN))
+                if (ERRNO_IS_TRANSIENT(len) || ERRNO_IS_DISCONNECT(len))
                         return 0;
 
-                return log_dhcp6_client_errno(client, errno, "Could not receive message from UDP socket: %m");
-
+                log_dhcp6_client_errno(client, len, "Could not receive message from UDP socket, ignoring: %m");
+                return 0;
         }
         if ((size_t) len < sizeof(DHCP6Message)) {
                 log_dhcp6_client(client, "Too small to be DHCP6 message: ignoring");
                 return 0;
         }
 
-        switch(message->type) {
-        case DHCP6_SOLICIT:
-        case DHCP6_REQUEST:
-        case DHCP6_CONFIRM:
-        case DHCP6_RENEW:
-        case DHCP6_REBIND:
-        case DHCP6_RELEASE:
-        case DHCP6_DECLINE:
-        case DHCP6_INFORMATION_REQUEST:
-        case DHCP6_RELAY_FORW:
-        case DHCP6_RELAY_REPL:
-                return 0;
+        /* msg_namelen == 0 happens when running the test-suite over a socketpair */
+        if (msg.msg_namelen > 0) {
+                if (msg.msg_namelen != sizeof(struct sockaddr_in6) || sa.in6.sin6_family != AF_INET6) {
+                        log_dhcp6_client(client, "Received message from invalid source, ignoring.");
+                        return 0;
+                }
 
-        case DHCP6_ADVERTISE:
-        case DHCP6_REPLY:
-        case DHCP6_RECONFIGURE:
-                break;
+                server_address = &sa.in6.sin6_addr;
+        }
 
-        default:
-                log_dhcp6_client(client, "Unknown message type %d", message->type);
+        CMSG_FOREACH(cmsg, &msg) {
+                if (cmsg->cmsg_level == SOL_SOCKET &&
+                    cmsg->cmsg_type == SO_TIMESTAMP &&
+                    cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval)))
+                        triple_timestamp_from_realtime(&t, timeval_load((struct timeval*) CMSG_DATA(cmsg)));
+        }
+
+        if (!triple_timestamp_is_set(&t))
+                triple_timestamp_get(&t);
+
+        if (!IN_SET(message->type, DHCP6_MESSAGE_ADVERTISE, DHCP6_MESSAGE_REPLY, DHCP6_MESSAGE_RECONFIGURE)) {
+                const char *type_str = dhcp6_message_type_to_string(message->type);
+                if (type_str)
+                        log_dhcp6_client(client, "Received unexpected %s message, ignoring.", type_str);
+                else
+                        log_dhcp6_client(client, "Received unsupported message type %u, ignoring.", message->type);
                 return 0;
         }
 
@@ -1464,7 +1534,7 @@ static int client_receive_message(
 
         switch (client->state) {
         case DHCP6_STATE_INFORMATION_REQUEST:
-                r = client_receive_reply(client, message, len);
+                r = client_receive_reply(client, message, len, &t, server_address);
                 if (r < 0) {
                         log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
                         return 0;
@@ -1477,7 +1547,7 @@ static int client_receive_message(
                 break;
 
         case DHCP6_STATE_SOLICITATION:
-                r = client_receive_advertise(client, message, len);
+                r = client_receive_advertise(client, message, len, &t, server_address);
                 if (r < 0) {
                         log_dhcp6_client_errno(client, r, "Failed to process received advertise message, ignoring: %m");
                         return 0;
@@ -1493,7 +1563,7 @@ static int client_receive_message(
         case DHCP6_STATE_RENEW:
         case DHCP6_STATE_REBIND:
 
-                r = client_receive_reply(client, message, len);
+                r = client_receive_reply(client, message, len, &t, server_address);
                 if (r < 0) {
                         log_dhcp6_client_errno(client, r, "Failed to process received reply message, ignoring: %m");
                         return 0;
@@ -1517,6 +1587,8 @@ static int client_receive_message(
 
         case DHCP6_STATE_STOPPED:
                 return 0;
+        default:
+                assert_not_reached();
         }
 
         log_dhcp6_client(client, "Recv %s",
@@ -1530,14 +1602,14 @@ static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1,
         assert_return(client, -EINVAL);
         assert_return(client->lease, -EINVAL);
 
-        if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
+        if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_NA) && client->lease->ia.addresses) {
                 *lifetime_t1 = be32toh(client->lease->ia.ia_na.lifetime_t1);
                 *lifetime_t2 = be32toh(client->lease->ia.ia_na.lifetime_t2);
 
                 return 0;
         }
 
-        if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
+        if (FLAGS_SET(client->request_ia, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
                 *lifetime_t1 = be32toh(client->lease->pd.ia_pd.lifetime_t1);
                 *lifetime_t2 = be32toh(client->lease->pd.ia_pd.lifetime_t2);
 
@@ -1547,7 +1619,7 @@ static int client_get_lifetime(sd_dhcp6_client *client, uint32_t *lifetime_t1,
         return -ENOMSG;
 }
 
-static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
+static int client_start(sd_dhcp6_client *client, DHCP6State state) {
         int r;
         usec_t timeout, time_now;
         uint32_t lifetime_t1, lifetime_t2;
@@ -1647,6 +1719,8 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) {
                 client->state = state;
 
                 return 0;
+        default:
+                assert_not_reached();
         }
 
         client->transaction_id = random_u32() & htobe32(0x00ffffff);
@@ -1685,7 +1759,7 @@ int sd_dhcp6_client_is_running(sd_dhcp6_client *client) {
 }
 
 int sd_dhcp6_client_start(sd_dhcp6_client *client) {
-        enum DHCP6State state = DHCP6_STATE_SOLICITATION;
+        DHCP6State state = DHCP6_STATE_SOLICITATION;
         int r;
 
         assert_return(client, -EINVAL);
@@ -1696,7 +1770,7 @@ int sd_dhcp6_client_start(sd_dhcp6_client *client) {
         if (client->state != DHCP6_STATE_STOPPED)
                 return -EBUSY;
 
-        if (!client->information_request && !client->request)
+        if (!client->information_request && client->request_ia == 0)
                 return -EINVAL;
 
         r = client_reset(client);
@@ -1824,7 +1898,7 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) {
                 .ia_na.type = SD_DHCP6_OPTION_IA_NA,
                 .ia_pd.type = SD_DHCP6_OPTION_IA_PD,
                 .ifindex = -1,
-                .request = DHCP6_REQUEST_IA_NA,
+                .request_ia = DHCP6_REQUEST_IA_NA | DHCP6_REQUEST_IA_PD,
                 .fd = -1,
                 .req_opts_len = ELEMENTSOF(default_req_opts),
                 .hint_pd_prefix.iapdprefix.lifetime_preferred = (be32_t) -1,
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 85649d92..b46a2e33 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
@@ -13,6 +13,27 @@
 #include "strv.h"
 #include "util.h"
 
+int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret) {
+        assert_return(lease, -EINVAL);
+        assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
+        assert_return(clock_supported(clock), -EOPNOTSUPP);
+        assert_return(ret, -EINVAL);
+
+        if (!triple_timestamp_is_set(&lease->timestamp))
+                return -ENODATA;
+
+        *ret = triple_timestamp_by_clock(&lease->timestamp, clock);
+        return 0;
+}
+
+int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret) {
+        assert_return(lease, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        *ret = lease->server_address;
+        return 0;
+}
+
 int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) {
         DHCP6Address *addr;
         uint32_t valid = 0, t;
@@ -52,12 +73,43 @@ DHCP6IA *dhcp6_lease_free_ia(DHCP6IA *ia) {
         return NULL;
 }
 
-int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id,
-                             size_t len) {
+int dhcp6_lease_set_clientid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) {
+        uint8_t *clientid;
+
+        assert_return(lease, -EINVAL);
+        assert_return(id, -EINVAL);
+        assert_return(len > 0, -EINVAL);
+
+        clientid = memdup(id, len);
+        if (!clientid)
+                return -ENOMEM;
+
+        free_and_replace(lease->clientid, clientid);
+        lease->clientid_len = len;
+
+        return 0;
+}
+
+int dhcp6_lease_get_clientid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) {
+        assert_return(lease, -EINVAL);
+
+        if (!lease->clientid)
+                return -ENODATA;
+
+        if (ret_id)
+                *ret_id = lease->clientid;
+        if (ret_len)
+                *ret_len = lease->clientid_len;
+
+        return 0;
+}
+
+int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id, size_t len) {
         uint8_t *serverid;
 
         assert_return(lease, -EINVAL);
         assert_return(id, -EINVAL);
+        assert_return(len > 0, -EINVAL);
 
         serverid = memdup(id, len);
         if (!serverid)
@@ -69,16 +121,16 @@ int dhcp6_lease_set_serverid(sd_dhcp6_lease *lease, const uint8_t *id,
         return 0;
 }
 
-int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **id, size_t *len) {
+int dhcp6_lease_get_serverid(sd_dhcp6_lease *lease, uint8_t **ret_id, size_t *ret_len) {
         assert_return(lease, -EINVAL);
 
         if (!lease->serverid)
-                return -ENOMSG;
+                return -ENODATA;
 
-        if (id)
-                *id = lease->serverid;
-        if (len)
-                *len = lease->serverid_len;
+        if (ret_id)
+                *ret_id = lease->serverid;
+        if (ret_len)
+                *ret_len = lease->serverid_len;
 
         return 0;
 }
@@ -119,24 +171,6 @@ int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit) {
         return 0;
 }
 
-int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid) {
-        assert_return(lease, -EINVAL);
-        assert_return(iaid, -EINVAL);
-
-        *iaid = lease->ia.ia_na.id;
-
-        return 0;
-}
-
-int dhcp6_lease_get_pd_iaid(sd_dhcp6_lease *lease, be32_t *iaid) {
-        assert_return(lease, -EINVAL);
-        assert_return(iaid, -EINVAL);
-
-        *iaid = lease->pd.ia_pd.id;
-
-        return 0;
-}
-
 int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease, struct in6_addr *addr,
                                uint32_t *lifetime_preferred,
                                uint32_t *lifetime_valid) {
@@ -207,12 +241,13 @@ int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t opt
 
 int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
         assert_return(lease, -EINVAL);
-        assert_return(ret, -EINVAL);
 
         if (!lease->dns)
                 return -ENOENT;
 
-        *ret = lease->dns;
+        if (ret)
+                *ret = lease->dns;
+
         return lease->dns_count;
 }
 
@@ -305,16 +340,17 @@ int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t op
 
 int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret) {
         assert_return(lease, -EINVAL);
-        assert_return(ret, -EINVAL);
 
         if (lease->ntp) {
-                *ret = lease->ntp;
+                if (ret)
+                        *ret = lease->ntp;
                 return lease->ntp_count;
         }
 
         if (lease->sntp && !lease->ntp_fqdn) {
                 /* Fallback to the deprecated SNTP option. */
-                *ret = lease->sntp;
+                if (ret)
+                        *ret = lease->sntp;
                 return lease->sntp_count;
         }
 
@@ -323,12 +359,12 @@ int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **
 
 int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) {
         assert_return(lease, -EINVAL);
-        assert_return(ret, -EINVAL);
 
         if (!lease->ntp_fqdn)
                 return -ENOENT;
 
-        *ret = lease->ntp_fqdn;
+        if (ret)
+                *ret = lease->ntp_fqdn;
         return strv_length(lease->ntp_fqdn);
 }
 
@@ -366,6 +402,7 @@ static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
         if (!lease)
                 return NULL;
 
+        free(lease->clientid);
         free(lease->serverid);
         dhcp6_lease_free_ia(&lease->ia);
         dhcp6_lease_free_ia(&lease->pd);
diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4acd.c b/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4acd.c
deleted file mode 100644
index f0612ed5..00000000
--- a/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4acd.c
+++ /dev/null
@@ -1,616 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/***
-  Copyright © 2014 Axis Communications AB. All rights reserved.
-***/
-
-#include "nm-sd-adapt-core.h"
-
-#include <arpa/inet.h>
-#include <errno.h>
-#include <netinet/if_ether.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "sd-ipv4acd.h"
-
-#include "alloc-util.h"
-#include "arp-util.h"
-#include "ether-addr-util.h"
-#include "event-util.h"
-#include "fd-util.h"
-#include "in-addr-util.h"
-#include "memory-util.h"
-#include "network-common.h"
-#include "random-util.h"
-#include "siphash24.h"
-#include "string-table.h"
-#include "string-util.h"
-#include "time-util.h"
-
-/* Constants from the RFC */
-#define PROBE_WAIT_USEC (1U * USEC_PER_SEC)
-#define PROBE_NUM 3U
-#define PROBE_MIN_USEC (1U * USEC_PER_SEC)
-#define PROBE_MAX_USEC (2U * USEC_PER_SEC)
-#define ANNOUNCE_WAIT_USEC (2U * USEC_PER_SEC)
-#define ANNOUNCE_NUM 2U
-#define ANNOUNCE_INTERVAL_USEC (2U * USEC_PER_SEC)
-#define MAX_CONFLICTS 10U
-#define RATE_LIMIT_INTERVAL_USEC (60U * USEC_PER_SEC)
-#define DEFEND_INTERVAL_USEC (10U * USEC_PER_SEC)
-
-typedef enum IPv4ACDState {
-        IPV4ACD_STATE_INIT,
-        IPV4ACD_STATE_STARTED,
-        IPV4ACD_STATE_WAITING_PROBE,
-        IPV4ACD_STATE_PROBING,
-        IPV4ACD_STATE_WAITING_ANNOUNCE,
-        IPV4ACD_STATE_ANNOUNCING,
-        IPV4ACD_STATE_RUNNING,
-        _IPV4ACD_STATE_MAX,
-        _IPV4ACD_STATE_INVALID = -EINVAL,
-} IPv4ACDState;
-
-struct sd_ipv4acd {
-        unsigned n_ref;
-
-        IPv4ACDState state;
-        int ifindex;
-        int fd;
-
-        char *ifname;
-        unsigned n_iteration;
-        unsigned n_conflict;
-
-        sd_event_source *receive_message_event_source;
-        sd_event_source *timer_event_source;
-
-        usec_t defend_window;
-        struct in_addr address;
-
-        /* External */
-        struct ether_addr mac_addr;
-
-        sd_event *event;
-        int event_priority;
-        sd_ipv4acd_callback_t callback;
-        void *userdata;
-        sd_ipv4acd_check_mac_callback_t check_mac_callback;
-        void *check_mac_userdata;
-};
-
-#define log_ipv4acd_errno(acd, error, fmt, ...)         \
-        log_interface_prefix_full_errno(                \
-                "IPv4ACD: ",                            \
-                sd_ipv4acd, acd,                        \
-                error, fmt, ##__VA_ARGS__)
-#define log_ipv4acd(acd, fmt, ...)                      \
-        log_interface_prefix_full_errno_zerook(         \
-                "IPv4ACD: ",                            \
-                sd_ipv4acd, acd,                        \
-                0, fmt, ##__VA_ARGS__)
-
-static const char * const ipv4acd_state_table[_IPV4ACD_STATE_MAX] = {
-        [IPV4ACD_STATE_INIT]             = "init",
-        [IPV4ACD_STATE_STARTED]          = "started",
-        [IPV4ACD_STATE_WAITING_PROBE]    = "waiting-probe",
-        [IPV4ACD_STATE_PROBING]          = "probing",
-        [IPV4ACD_STATE_WAITING_ANNOUNCE] = "waiting-announce",
-        [IPV4ACD_STATE_ANNOUNCING]       = "announcing",
-        [IPV4ACD_STATE_RUNNING]          = "running",
-};
-
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(ipv4acd_state, IPv4ACDState);
-
-static void ipv4acd_set_state(sd_ipv4acd *acd, IPv4ACDState st, bool reset_counter) {
-        assert(acd);
-        assert(st < _IPV4ACD_STATE_MAX);
-
-        if (st != acd->state)
-                log_ipv4acd(acd, "%s -> %s", ipv4acd_state_to_string(acd->state), ipv4acd_state_to_string(st));
-
-        if (st == acd->state && !reset_counter)
-                acd->n_iteration++;
-        else {
-                acd->state = st;
-                acd->n_iteration = 0;
-        }
-}
-
-static void ipv4acd_reset(sd_ipv4acd *acd) {
-        assert(acd);
-
-        (void) event_source_disable(acd->timer_event_source);
-        acd->receive_message_event_source = sd_event_source_disable_unref(acd->receive_message_event_source);
-
-        acd->fd = safe_close(acd->fd);
-
-        ipv4acd_set_state(acd, IPV4ACD_STATE_INIT, true);
-}
-
-static sd_ipv4acd *ipv4acd_free(sd_ipv4acd *acd) {
-        assert(acd);
-
-        ipv4acd_reset(acd);
-        sd_event_source_unref(acd->timer_event_source);
-        sd_ipv4acd_detach_event(acd);
-        free(acd->ifname);
-        return mfree(acd);
-}
-
-DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_ipv4acd, sd_ipv4acd, ipv4acd_free);
-
-int sd_ipv4acd_new(sd_ipv4acd **ret) {
-        _cleanup_(sd_ipv4acd_unrefp) sd_ipv4acd *acd = NULL;
-
-        assert_return(ret, -EINVAL);
-
-        acd = new(sd_ipv4acd, 1);
-        if (!acd)
-                return -ENOMEM;
-
-        *acd = (sd_ipv4acd) {
-                .n_ref = 1,
-                .state = IPV4ACD_STATE_INIT,
-                .ifindex = -1,
-                .fd = -1,
-        };
-
-        *ret = TAKE_PTR(acd);
-
-        return 0;
-}
-
-static void ipv4acd_client_notify(sd_ipv4acd *acd, int event) {
-        assert(acd);
-
-        if (!acd->callback)
-                return;
-
-        acd->callback(acd, event, acd->userdata);
-}
-
-int sd_ipv4acd_stop(sd_ipv4acd *acd) {
-        IPv4ACDState old_state;
-
-        if (!acd)
-                return 0;
-
-        old_state = acd->state;
-
-        ipv4acd_reset(acd);
-
-        if (old_state == IPV4ACD_STATE_INIT)
-                return 0;
-
-        log_ipv4acd(acd, "STOPPED");
-
-        ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_STOP);
-
-        return 0;
-}
-
-static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata);
-
-static int ipv4acd_set_next_wakeup(sd_ipv4acd *acd, usec_t usec, usec_t random_usec) {
-        usec_t next_timeout, time_now;
-
-        assert(acd);
-
-        next_timeout = usec;
-
-        if (random_usec > 0)
-                next_timeout += (usec_t) random_u64() % random_usec;
-
-        assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &time_now) >= 0);
-
-        return event_reset_time(acd->event, &acd->timer_event_source,
-                                clock_boottime_or_monotonic(),
-                                time_now + next_timeout, 0,
-                                ipv4acd_on_timeout, acd,
-                                acd->event_priority, "ipv4acd-timer", true);
-}
-
-static int ipv4acd_on_timeout(sd_event_source *s, uint64_t usec, void *userdata) {
-        sd_ipv4acd *acd = userdata;
-        int r = 0;
-
-        assert(acd);
-
-        switch (acd->state) {
-
-        case IPV4ACD_STATE_STARTED:
-                acd->defend_window = 0;
-
-                ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_PROBE, true);
-
-                if (acd->n_conflict >= MAX_CONFLICTS) {
-                        log_ipv4acd(acd, "Max conflicts reached, delaying by %s",
-                                    FORMAT_TIMESPAN(RATE_LIMIT_INTERVAL_USEC, 0));
-                        r = ipv4acd_set_next_wakeup(acd, RATE_LIMIT_INTERVAL_USEC, PROBE_WAIT_USEC);
-                } else
-                        r = ipv4acd_set_next_wakeup(acd, 0, PROBE_WAIT_USEC);
-                if (r < 0)
-                        goto fail;
-
-                break;
-
-        case IPV4ACD_STATE_WAITING_PROBE:
-        case IPV4ACD_STATE_PROBING:
-                /* Send a probe */
-                r = arp_send_probe(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr);
-                if (r < 0) {
-                        log_ipv4acd_errno(acd, r, "Failed to send ARP probe: %m");
-                        goto fail;
-                }
-
-                log_ipv4acd(acd, "Probing "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address));
-
-                if (acd->n_iteration < PROBE_NUM - 2) {
-                        ipv4acd_set_state(acd, IPV4ACD_STATE_PROBING, false);
-
-                        r = ipv4acd_set_next_wakeup(acd, PROBE_MIN_USEC, (PROBE_MAX_USEC-PROBE_MIN_USEC));
-                        if (r < 0)
-                                goto fail;
-                } else {
-                        ipv4acd_set_state(acd, IPV4ACD_STATE_WAITING_ANNOUNCE, true);
-
-                        r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_WAIT_USEC, 0);
-                        if (r < 0)
-                                goto fail;
-                }
-
-                break;
-
-        case IPV4ACD_STATE_ANNOUNCING:
-                if (acd->n_iteration >= ANNOUNCE_NUM - 1) {
-                        ipv4acd_set_state(acd, IPV4ACD_STATE_RUNNING, false);
-                        break;
-                }
-
-                _fallthrough_;
-        case IPV4ACD_STATE_WAITING_ANNOUNCE:
-                /* Send announcement packet */
-                r = arp_send_announcement(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr);
-                if (r < 0) {
-                        log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
-                        goto fail;
-                }
-
-                log_ipv4acd(acd, "Announcing "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address));
-
-                ipv4acd_set_state(acd, IPV4ACD_STATE_ANNOUNCING, false);
-
-                r = ipv4acd_set_next_wakeup(acd, ANNOUNCE_INTERVAL_USEC, 0);
-                if (r < 0)
-                        goto fail;
-
-                if (acd->n_iteration == 0) {
-                        acd->n_conflict = 0;
-                        ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_BIND);
-                }
-
-                break;
-
-        default:
-                assert_not_reached();
-        }
-
-        return 0;
-
-fail:
-        sd_ipv4acd_stop(acd);
-        return 0;
-}
-
-static bool ipv4acd_arp_conflict(sd_ipv4acd *acd, const struct ether_arp *arp, bool announced) {
-        assert(acd);
-        assert(arp);
-
-        /* RFC 5227 section 2.1.1.
-         * "the host receives any ARP packet (Request *or* Reply) on the interface where the probe is
-         * being performed, where the packet's 'sender IP address' is the address being probed for,
-         * then the host MUST treat this address as being in use by some other host" */
-        if (memcmp(arp->arp_spa, &acd->address, sizeof(struct in_addr)) == 0)
-                return true;
-
-        if (announced)
-                /* the TPA matched instead of SPA, this is not a conflict */
-                return false;
-
-        /* "any ARP Probe where the packet's 'target IP address' is the address being probed for, and
-         * the packet's 'sender hardware address' is not the hardware address of any of the host's
-         * interfaces, then the host SHOULD similarly treat this as an address conflict" */
-        if (arp->ea_hdr.ar_op != htobe16(ARPOP_REQUEST))
-                return false; /* not ARP Request, ignoring. */
-        if (memeqzero(arp->arp_spa, sizeof(struct in_addr)) == 0)
-                return false; /* not ARP Probe, ignoring. */
-        if (memcmp(arp->arp_tpa, &acd->address, sizeof(struct in_addr)) != 0)
-                return false; /* target IP address does not match, BPF code is broken? */
-
-        if (acd->check_mac_callback &&
-            acd->check_mac_callback(acd, (const struct ether_addr*) arp->arp_sha, acd->check_mac_userdata) > 0)
-                /* sender hardware is one of the host's interfaces, ignoring. */
-                return true;
-
-        return true; /* conflict! */
-}
-
-static void ipv4acd_on_conflict(sd_ipv4acd *acd) {
-        assert(acd);
-
-        acd->n_conflict++;
-
-        log_ipv4acd(acd, "Conflict on "IPV4_ADDRESS_FMT_STR" (%u)", IPV4_ADDRESS_FMT_VAL(acd->address), acd->n_conflict);
-
-        ipv4acd_reset(acd);
-        ipv4acd_client_notify(acd, SD_IPV4ACD_EVENT_CONFLICT);
-}
-
-static int ipv4acd_on_packet(
-                sd_event_source *s,
-                int fd,
-                uint32_t revents,
-                void *userdata) {
-
-        sd_ipv4acd *acd = userdata;
-        struct ether_arp packet;
-        ssize_t n;
-        int r;
-
-        assert(s);
-        assert(acd);
-        assert(fd >= 0);
-
-        n = recv(fd, &packet, sizeof(struct ether_arp), 0);
-        if (n < 0) {
-                if (IN_SET(errno, EAGAIN, EINTR))
-                        return 0;
-
-                log_ipv4acd_errno(acd, errno, "Failed to read ARP packet: %m");
-                goto fail;
-        }
-        if ((size_t) n != sizeof(struct ether_arp)) {
-                log_ipv4acd(acd, "Ignoring too short ARP packet.");
-                return 0;
-        }
-
-        switch (acd->state) {
-
-        case IPV4ACD_STATE_ANNOUNCING:
-        case IPV4ACD_STATE_RUNNING:
-
-                if (ipv4acd_arp_conflict(acd, &packet, true)) {
-                        usec_t ts;
-
-                        assert_se(sd_event_now(acd->event, clock_boottime_or_monotonic(), &ts) >= 0);
-
-                        /* Defend address */
-                        if (ts > acd->defend_window) {
-                                acd->defend_window = ts + DEFEND_INTERVAL_USEC;
-                                r = arp_send_announcement(acd->fd, acd->ifindex, &acd->address, &acd->mac_addr);
-                                if (r < 0) {
-                                        log_ipv4acd_errno(acd, r, "Failed to send ARP announcement: %m");
-                                        goto fail;
-                                }
-
-                                log_ipv4acd(acd, "Defending "IPV4_ADDRESS_FMT_STR, IPV4_ADDRESS_FMT_VAL(acd->address));
-
-                        } else
-                                ipv4acd_on_conflict(acd);
-                }
-                break;
-
-        case IPV4ACD_STATE_WAITING_PROBE:
-        case IPV4ACD_STATE_PROBING:
-        case IPV4ACD_STATE_WAITING_ANNOUNCE:
-                if (ipv4acd_arp_conflict(acd, &packet, false))
-                        ipv4acd_on_conflict(acd);
-                break;
-
-        default:
-                assert_not_reached();
-        }
-
-        return 0;
-
-fail:
-        sd_ipv4acd_stop(acd);
-        return 0;
-}
-
-int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int ifindex) {
-        assert_return(acd, -EINVAL);
-        assert_return(ifindex > 0, -EINVAL);
-        assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
-
-        acd->ifindex = ifindex;
-
-        return 0;
-}
-
-int sd_ipv4acd_get_ifindex(sd_ipv4acd *acd) {
-        if (!acd)
-                return -EINVAL;
-
-        return acd->ifindex;
-}
-
-int sd_ipv4acd_set_ifname(sd_ipv4acd *acd, const char *ifname) {
-        assert_return(acd, -EINVAL);
-        assert_return(ifname, -EINVAL);
-
-        if (!ifname_valid_full(ifname, IFNAME_VALID_ALTERNATIVE))
-                return -EINVAL;
-
-        return free_and_strdup(&acd->ifname, ifname);
-}
-
-int sd_ipv4acd_get_ifname(sd_ipv4acd *acd, const char **ret) {
-        int r;
-
-        assert_return(acd, -EINVAL);
-
-        r = get_ifname(acd->ifindex, &acd->ifname);
-        if (r < 0)
-                return r;
-
-        if (ret)
-                *ret = acd->ifname;
-
-        return 0;
-}
-
-int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr) {
-        int r;
-
-        assert_return(acd, -EINVAL);
-        assert_return(addr, -EINVAL);
-        assert_return(!ether_addr_is_null(addr), -EINVAL);
-
-        acd->mac_addr = *addr;
-
-        if (!sd_ipv4acd_is_running(acd))
-                return 0;
-
-        assert(acd->fd >= 0);
-        r = arp_update_filter(acd->fd, &acd->address, &acd->mac_addr);
-        if (r < 0) {
-                ipv4acd_reset(acd);
-                return r;
-        }
-
-        return 0;
-}
-
-int sd_ipv4acd_detach_event(sd_ipv4acd *acd) {
-        assert_return(acd, -EINVAL);
-
-        acd->event = sd_event_unref(acd->event);
-
-        return 0;
-}
-
-int sd_ipv4acd_attach_event(sd_ipv4acd *acd, sd_event *event, int64_t priority) {
-        int r;
-
-        assert_return(acd, -EINVAL);
-        assert_return(!acd->event, -EBUSY);
-
-        if (event)
-                acd->event = sd_event_ref(event);
-        else {
-                r = sd_event_default(&acd->event);
-                if (r < 0)
-                        return r;
-        }
-
-        acd->event_priority = priority;
-
-        return 0;
-}
-
-int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *userdata) {
-        assert_return(acd, -EINVAL);
-
-        acd->callback = cb;
-        acd->userdata = userdata;
-
-        return 0;
-}
-
-int sd_ipv4acd_set_check_mac_callback(sd_ipv4acd *acd, sd_ipv4acd_check_mac_callback_t cb, void *userdata) {
-        assert_return(acd, -EINVAL);
-
-        acd->check_mac_callback = cb;
-        acd->check_mac_userdata = userdata;
-        return 0;
-}
-
-int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address) {
-        int r;
-
-        assert_return(acd, -EINVAL);
-        assert_return(address, -EINVAL);
-        assert_return(in4_addr_is_set(address), -EINVAL);
-
-        if (in4_addr_equal(&acd->address, address))
-                return 0;
-
-        acd->address = *address;
-
-        if (!sd_ipv4acd_is_running(acd))
-                return 0;
-
-        assert(acd->fd >= 0);
-        r = arp_update_filter(acd->fd, &acd->address, &acd->mac_addr);
-        if (r < 0)
-                goto fail;
-
-        r = ipv4acd_set_next_wakeup(acd, 0, 0);
-        if (r < 0)
-                goto fail;
-
-        ipv4acd_set_state(acd, IPV4ACD_STATE_STARTED, true);
-        return 0;
-
-fail:
-        ipv4acd_reset(acd);
-        return r;
-}
-
-int sd_ipv4acd_get_address(sd_ipv4acd *acd, struct in_addr *address) {
-        assert_return(acd, -EINVAL);
-        assert_return(address, -EINVAL);
-
-        *address = acd->address;
-
-        return 0;
-}
-
-int sd_ipv4acd_is_running(sd_ipv4acd *acd) {
-        assert_return(acd, false);
-
-        return acd->state != IPV4ACD_STATE_INIT;
-}
-
-int sd_ipv4acd_start(sd_ipv4acd *acd, bool reset_conflicts) {
-        int r;
-
-        assert_return(acd, -EINVAL);
-        assert_return(acd->event, -EINVAL);
-        assert_return(acd->ifindex > 0, -EINVAL);
-        assert_return(in4_addr_is_set(&acd->address), -EINVAL);
-        assert_return(!ether_addr_is_null(&acd->mac_addr), -EINVAL);
-        assert_return(acd->state == IPV4ACD_STATE_INIT, -EBUSY);
-
-        r = arp_network_bind_raw_socket(acd->ifindex, &acd->address, &acd->mac_addr);
-        if (r < 0)
-                return r;
-
-        CLOSE_AND_REPLACE(acd->fd, r);
-
-        if (reset_conflicts)
-                acd->n_conflict = 0;
-
-        r = sd_event_add_io(acd->event, &acd->receive_message_event_source, acd->fd, EPOLLIN, ipv4acd_on_packet, acd);
-        if (r < 0)
-                goto fail;
-
-        r = sd_event_source_set_priority(acd->receive_message_event_source, acd->event_priority);
-        if (r < 0)
-                goto fail;
-
-        (void) sd_event_source_set_description(acd->receive_message_event_source, "ipv4acd-receive-message");
-
-        r = ipv4acd_set_next_wakeup(acd, 0, 0);
-        if (r < 0)
-                goto fail;
-
-        ipv4acd_set_state(acd, IPV4ACD_STATE_STARTED, true);
-        return 0;
-
-fail:
-        ipv4acd_reset(acd);
-        return r;
-}
diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4ll.c b/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4ll.c
deleted file mode 100644
index 4a500dfc..00000000
--- a/src/libnm-systemd-core/src/libsystemd-network/sd-ipv4ll.c
+++ /dev/null
@@ -1,382 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-/***
-  Copyright © 2014 Axis Communications AB. All rights reserved.
-***/
-
-#include "nm-sd-adapt-core.h"
-
-#include <arpa/inet.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "sd-id128.h"
-#include "sd-ipv4acd.h"
-#include "sd-ipv4ll.h"
-
-#include "alloc-util.h"
-#include "ether-addr-util.h"
-#include "in-addr-util.h"
-#include "network-common.h"
-#include "random-util.h"
-#include "siphash24.h"
-#include "sparse-endian.h"
-#include "string-util.h"
-#include "util.h"
-
-#define IPV4LL_NETWORK UINT32_C(0xA9FE0000)
-#define IPV4LL_NETMASK UINT32_C(0xFFFF0000)
-
-#define IPV4LL_DONT_DESTROY(ll) \
-        _cleanup_(sd_ipv4ll_unrefp) _unused_ sd_ipv4ll *_dont_destroy_##ll = sd_ipv4ll_ref(ll)
-
-struct sd_ipv4ll {
-        unsigned n_ref;
-
-        sd_ipv4acd *acd;
-
-        be32_t address; /* the address pushed to ACD */
-        struct ether_addr mac;
-
-        struct {
-                le64_t value;
-                le64_t generation;
-        } seed;
-        bool seed_set;
-
-        /* External */
-        be32_t claimed_address;
-
-        sd_ipv4ll_callback_t callback;
-        void *userdata;
-
-        sd_ipv4ll_check_mac_callback_t check_mac_callback;
-        void *check_mac_userdata;
-};
-
-#define log_ipv4ll_errno(ll, error, fmt, ...)           \
-        log_interface_prefix_full_errno(                \
-                "IPv4LL: ",                             \
-                sd_ipv4ll, ll,                          \
-                error, fmt, ##__VA_ARGS__)
-#define log_ipv4ll(ll, fmt, ...)                        \
-        log_interface_prefix_full_errno_zerook(         \
-                "IPv4LL: ",                             \
-                sd_ipv4ll, ll,                          \
-                0, fmt, ##__VA_ARGS__)
-
-static void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata);
-static int ipv4ll_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata);
-
-static sd_ipv4ll *ipv4ll_free(sd_ipv4ll *ll) {
-        assert(ll);
-
-        sd_ipv4acd_unref(ll->acd);
-        return mfree(ll);
-}
-
-DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_ipv4ll, sd_ipv4ll, ipv4ll_free);
-
-int sd_ipv4ll_new(sd_ipv4ll **ret) {
-        _cleanup_(sd_ipv4ll_unrefp) sd_ipv4ll *ll = NULL;
-        int r;
-
-        assert_return(ret, -EINVAL);
-
-        ll = new0(sd_ipv4ll, 1);
-        if (!ll)
-                return -ENOMEM;
-
-        ll->n_ref = 1;
-
-        r = sd_ipv4acd_new(&ll->acd);
-        if (r < 0)
-                return r;
-
-        r = sd_ipv4acd_set_callback(ll->acd, ipv4ll_on_acd, ll);
-        if (r < 0)
-                return r;
-
-        r = sd_ipv4acd_set_check_mac_callback(ll->acd, ipv4ll_check_mac, ll);
-        if (r < 0)
-                return r;
-
-        *ret = TAKE_PTR(ll);
-
-        return 0;
-}
-
-int sd_ipv4ll_stop(sd_ipv4ll *ll) {
-        if (!ll)
-                return 0;
-
-        return sd_ipv4acd_stop(ll->acd);
-}
-
-int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int ifindex) {
-        assert_return(ll, -EINVAL);
-        assert_return(ifindex > 0, -EINVAL);
-        assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
-
-        return sd_ipv4acd_set_ifindex(ll->acd, ifindex);
-}
-
-int sd_ipv4ll_get_ifindex(sd_ipv4ll *ll) {
-        if (!ll)
-                return -EINVAL;
-
-        return sd_ipv4acd_get_ifindex(ll->acd);
-}
-
-int sd_ipv4ll_set_ifname(sd_ipv4ll *ll, const char *ifname) {
-        assert_return(ll, -EINVAL);
-        assert_return(ifname, -EINVAL);
-
-        return sd_ipv4acd_set_ifname(ll->acd, ifname);
-}
-
-int sd_ipv4ll_get_ifname(sd_ipv4ll *ll, const char **ret) {
-        assert_return(ll, -EINVAL);
-
-        return sd_ipv4acd_get_ifname(ll->acd, ret);
-}
-
-int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr) {
-        int r;
-
-        assert_return(ll, -EINVAL);
-        assert_return(addr, -EINVAL);
-        assert_return(!ether_addr_is_null(addr), -EINVAL);
-
-        r = sd_ipv4acd_set_mac(ll->acd, addr);
-        if (r < 0)
-                return r;
-
-        ll->mac = *addr;
-        return 0;
-}
-
-int sd_ipv4ll_detach_event(sd_ipv4ll *ll) {
-        assert_return(ll, -EINVAL);
-
-        return sd_ipv4acd_detach_event(ll->acd);
-}
-
-int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority) {
-        assert_return(ll, -EINVAL);
-
-        return sd_ipv4acd_attach_event(ll->acd, event, priority);
-}
-
-int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata) {
-        assert_return(ll, -EINVAL);
-
-        ll->callback = cb;
-        ll->userdata = userdata;
-
-        return 0;
-}
-
-int sd_ipv4ll_set_check_mac_callback(sd_ipv4ll *ll, sd_ipv4ll_check_mac_callback_t cb, void *userdata) {
-        assert_return(ll, -EINVAL);
-
-        ll->check_mac_callback = cb;
-        ll->check_mac_userdata = userdata;
-
-        return 0;
-}
-
-int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address) {
-        assert_return(ll, -EINVAL);
-        assert_return(address, -EINVAL);
-
-        if (ll->claimed_address == 0)
-                return -ENOENT;
-
-        address->s_addr = ll->claimed_address;
-
-        return 0;
-}
-
-int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint64_t seed) {
-        assert_return(ll, -EINVAL);
-        assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
-
-        ll->seed.value = htole64(seed);
-        ll->seed_set = true;
-
-        return 0;
-}
-
-int sd_ipv4ll_is_running(sd_ipv4ll *ll) {
-        assert_return(ll, false);
-
-        return sd_ipv4acd_is_running(ll->acd);
-}
-
-static bool ipv4ll_address_is_valid(const struct in_addr *address) {
-        assert(address);
-
-        if (!in4_addr_is_link_local(address))
-                return false;
-
-        return !IN_SET(be32toh(address->s_addr) & 0x0000FF00U, 0x0000U, 0xFF00U);
-}
-
-int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address) {
-        int r;
-
-        assert_return(ll, -EINVAL);
-        assert_return(address, -EINVAL);
-        assert_return(ipv4ll_address_is_valid(address), -EINVAL);
-
-        r = sd_ipv4acd_set_address(ll->acd, address);
-        if (r < 0)
-                return r;
-
-        ll->address = address->s_addr;
-
-        return 0;
-}
-
-#define PICK_HASH_KEY SD_ID128_MAKE(15,ac,82,a6,d6,3f,49,78,98,77,5d,0c,69,02,94,0b)
-
-static int ipv4ll_pick_address(sd_ipv4ll *ll) {
-        _cleanup_free_ char *address = NULL;
-        be32_t addr;
-
-        assert(ll);
-
-        do {
-                uint64_t h;
-
-                h = siphash24(&ll->seed, sizeof(ll->seed), PICK_HASH_KEY.bytes);
-
-                /* Increase the generation counter by one */
-                ll->seed.generation = htole64(le64toh(ll->seed.generation) + 1);
-
-                addr = htobe32((h & UINT32_C(0x0000FFFF)) | IPV4LL_NETWORK);
-        } while (addr == ll->address ||
-                 IN_SET(be32toh(addr) & 0x0000FF00U, 0x0000U, 0xFF00U));
-
-        (void) in_addr_to_string(AF_INET, &(union in_addr_union) { .in.s_addr = addr }, &address);
-        log_ipv4ll(ll, "Picked new IP address %s.", strna(address));
-
-        return sd_ipv4ll_set_address(ll, &(struct in_addr) { addr });
-}
-
-#define MAC_HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2)
-
-static int ipv4ll_start_internal(sd_ipv4ll *ll, bool reset_generation) {
-        int r;
-        bool picked_address = false;
-
-        assert_return(ll, -EINVAL);
-        assert_return(!ether_addr_is_null(&ll->mac), -EINVAL);
-
-        /* If no random seed is set, generate some from the MAC address */
-        if (!ll->seed_set)
-                ll->seed.value = htole64(siphash24(ll->mac.ether_addr_octet, ETH_ALEN, MAC_HASH_KEY.bytes));
-
-        if (reset_generation)
-                ll->seed.generation = 0;
-
-        if (ll->address == 0) {
-                r = ipv4ll_pick_address(ll);
-                if (r < 0)
-                        return r;
-
-                picked_address = true;
-        }
-
-        r = sd_ipv4acd_start(ll->acd, reset_generation);
-        if (r < 0) {
-
-                /* We couldn't start? If so, let's forget the picked address again, the user might make a change and
-                 * retry, and we want the new data to take effect when picking an address. */
-                if (picked_address)
-                        ll->address = 0;
-
-                return r;
-        }
-
-        return 1;
-}
-
-int sd_ipv4ll_start(sd_ipv4ll *ll) {
-        assert_return(ll, -EINVAL);
-
-        if (sd_ipv4ll_is_running(ll))
-                return 0;
-
-        return ipv4ll_start_internal(ll, true);
-}
-
-int sd_ipv4ll_restart(sd_ipv4ll *ll) {
-        ll->address = 0;
-
-        return ipv4ll_start_internal(ll, false);
-}
-
-static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) {
-        assert(ll);
-
-        if (ll->callback)
-                ll->callback(ll, event, ll->userdata);
-}
-
-void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) {
-        sd_ipv4ll *ll = userdata;
-        IPV4LL_DONT_DESTROY(ll);
-        int r;
-
-        assert(acd);
-        assert(ll);
-
-        switch (event) {
-
-        case SD_IPV4ACD_EVENT_STOP:
-                ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
-                ll->claimed_address = 0;
-                break;
-
-        case SD_IPV4ACD_EVENT_BIND:
-                ll->claimed_address = ll->address;
-                ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_BIND);
-                break;
-
-        case SD_IPV4ACD_EVENT_CONFLICT:
-                /* if an address was already bound we must call up to the
-                   user to handle this, otherwise we just try again */
-                if (ll->claimed_address != 0) {
-                        ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_CONFLICT);
-
-                        ll->claimed_address = 0;
-                } else {
-                        r = sd_ipv4ll_restart(ll);
-                        if (r < 0)
-                                goto error;
-                }
-
-                break;
-
-        default:
-                assert_not_reached();
-        }
-
-        return;
-
-error:
-        ipv4ll_client_notify(ll, SD_IPV4LL_EVENT_STOP);
-}
-
-static int ipv4ll_check_mac(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata) {
-        sd_ipv4ll *ll = userdata;
-
-        assert(ll);
-
-        if (ll->check_mac_callback)
-                return ll->check_mac_callback(ll, mac, ll->check_mac_userdata);
-
-        return 0;
-}
diff --git a/src/libnm-systemd-core/src/libsystemd-network/sd-lldp-rx.c b/src/libnm-systemd-core/src/libsystemd-network/sd-lldp-rx.c
index 9da2b149..f50e9c8d 100644
--- a/src/libnm-systemd-core/src/libsystemd-network/sd-lldp-rx.c
+++ b/src/libnm-systemd-core/src/libsystemd-network/sd-lldp-rx.c
@@ -202,6 +202,9 @@ static int lldp_rx_receive_datagram(sd_event_source *s, int fd, uint32_t revents
 
         space = next_datagram_size_fd(fd);
         if (space < 0) {
+                if (ERRNO_IS_TRANSIENT(space) || ERRNO_IS_DISCONNECT(space))
+                        return 0;
+
                 log_lldp_rx_errno(lldp_rx, space, "Failed to determine datagram size to read, ignoring: %m");
                 return 0;
         }
@@ -214,7 +217,7 @@ static int lldp_rx_receive_datagram(sd_event_source *s, int fd, uint32_t revents
 
         length = recv(fd, LLDP_NEIGHBOR_RAW(n), n->raw_size, MSG_DONTWAIT);
         if (length < 0) {
-                if (IN_SET(errno, EAGAIN, EINTR))
+                if (ERRNO_IS_TRANSIENT(errno) || ERRNO_IS_DISCONNECT(errno))
                         return 0;
 
                 log_lldp_rx_errno(lldp_rx, errno, "Failed to read LLDP datagram, ignoring: %m");
@@ -251,7 +254,7 @@ int sd_lldp_rx_is_running(sd_lldp_rx *lldp_rx) {
         return lldp_rx->fd >= 0;
 }
 
-_public_ int sd_lldp_rx_start(sd_lldp_rx *lldp_rx) {
+int sd_lldp_rx_start(sd_lldp_rx *lldp_rx) {
         int r;
 
         assert_return(lldp_rx, -EINVAL);
@@ -285,7 +288,7 @@ fail:
         return r;
 }
 
-_public_ int sd_lldp_rx_stop(sd_lldp_rx *lldp_rx) {
+int sd_lldp_rx_stop(sd_lldp_rx *lldp_rx) {
         if (!sd_lldp_rx_is_running(lldp_rx))
                 return 0;
 
@@ -297,7 +300,7 @@ _public_ int sd_lldp_rx_stop(sd_lldp_rx *lldp_rx) {
         return 1;
 }
 
-_public_ int sd_lldp_rx_attach_event(sd_lldp_rx *lldp_rx, sd_event *event, int64_t priority) {
+int sd_lldp_rx_attach_event(sd_lldp_rx *lldp_rx, sd_event *event, int64_t priority) {
         int r;
 
         assert_return(lldp_rx, -EINVAL);
@@ -317,7 +320,7 @@ _public_ int sd_lldp_rx_attach_event(sd_lldp_rx *lldp_rx, sd_event *event, int64
         return 0;
 }
 
-_public_ int sd_lldp_rx_detach_event(sd_lldp_rx *lldp_rx) {
+int sd_lldp_rx_detach_event(sd_lldp_rx *lldp_rx) {
         assert_return(lldp_rx, -EINVAL);
         assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY);
 
@@ -327,13 +330,13 @@ _public_ int sd_lldp_rx_detach_event(sd_lldp_rx *lldp_rx) {
         return 0;
 }
 
-_public_ sd_event* sd_lldp_rx_get_event(sd_lldp_rx *lldp_rx) {
+sd_event* sd_lldp_rx_get_event(sd_lldp_rx *lldp_rx) {
         assert_return(lldp_rx, NULL);
 
         return lldp_rx->event;
 }
 
-_public_ int sd_lldp_rx_set_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_callback_t cb, void *userdata) {
+int sd_lldp_rx_set_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_callback_t cb, void *userdata) {
         assert_return(lldp_rx, -EINVAL);
 
         lldp_rx->callback = cb;
@@ -342,7 +345,7 @@ _public_ int sd_lldp_rx_set_callback(sd_lldp_rx *lldp_rx, sd_lldp_rx_callback_t
         return 0;
 }
 
-_public_ int sd_lldp_rx_set_ifindex(sd_lldp_rx *lldp_rx, int ifindex) {
+int sd_lldp_rx_set_ifindex(sd_lldp_rx *lldp_rx, int ifindex) {
         assert_return(lldp_rx, -EINVAL);
         assert_return(ifindex > 0, -EINVAL);
         assert_return(!sd_lldp_rx_is_running(lldp_rx), -EBUSY);
@@ -394,7 +397,7 @@ static sd_lldp_rx *lldp_rx_free(sd_lldp_rx *lldp_rx) {
 
 DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_lldp_rx, sd_lldp_rx, lldp_rx_free);
 
-_public_ int sd_lldp_rx_new(sd_lldp_rx **ret) {
+int sd_lldp_rx_new(sd_lldp_rx **ret) {
         _cleanup_(sd_lldp_rx_unrefp) sd_lldp_rx *lldp_rx = NULL;
 
         assert_return(ret, -EINVAL);
@@ -462,7 +465,7 @@ static inline int neighbor_compare_func(sd_lldp_neighbor * const *a, sd_lldp_nei
         return lldp_neighbor_id_compare_func(&(*a)->id, &(*b)->id);
 }
 
-_public_ int sd_lldp_rx_get_neighbors(sd_lldp_rx *lldp_rx, sd_lldp_neighbor ***ret) {
+int sd_lldp_rx_get_neighbors(sd_lldp_rx *lldp_rx, sd_lldp_neighbor ***ret) {
         _cleanup_free_ sd_lldp_neighbor **l = NULL;
         sd_lldp_neighbor *n;
         int k = 0;
@@ -491,7 +494,7 @@ _public_ int sd_lldp_rx_get_neighbors(sd_lldp_rx *lldp_rx, sd_lldp_neighbor ***r
         return k;
 }
 
-_public_ int sd_lldp_rx_set_neighbors_max(sd_lldp_rx *lldp_rx, uint64_t m) {
+int sd_lldp_rx_set_neighbors_max(sd_lldp_rx *lldp_rx, uint64_t m) {
         assert_return(lldp_rx, -EINVAL);
         assert_return(m > 0, -EINVAL);
 
@@ -501,7 +504,7 @@ _public_ int sd_lldp_rx_set_neighbors_max(sd_lldp_rx *lldp_rx, uint64_t m) {
         return 0;
 }
 
-_public_ int sd_lldp_rx_match_capabilities(sd_lldp_rx *lldp_rx, uint16_t mask) {
+int sd_lldp_rx_match_capabilities(sd_lldp_rx *lldp_rx, uint16_t mask) {
         assert_return(lldp_rx, -EINVAL);
         assert_return(mask != 0, -EINVAL);
 
@@ -510,7 +513,7 @@ _public_ int sd_lldp_rx_match_capabilities(sd_lldp_rx *lldp_rx, uint16_t mask) {
         return 0;
 }
 
-_public_ int sd_lldp_rx_set_filter_address(sd_lldp_rx *lldp_rx, const struct ether_addr *addr) {
+int sd_lldp_rx_set_filter_address(sd_lldp_rx *lldp_rx, const struct ether_addr *addr) {
         assert_return(lldp_rx, -EINVAL);
 
         /* In order to deal nicely with bridges that send back our own packets, allow one address to be filtered, so
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 08b409fe..74cbc269 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
@@ -71,6 +71,7 @@ struct sd_event_source {
         uint64_t prepare_iteration;
 
         sd_event_destroy_t destroy_callback;
+        sd_event_handler_t ratelimit_expire_callback;
 
         LIST_FIELDS(sd_event_source, sources);
 
@@ -214,6 +215,11 @@ struct inotify_data {
          * the events locally if they can't be coalesced). */
         unsigned n_pending;
 
+        /* If this counter is non-zero, don't GC the inotify data object even if not used to watch any inode
+         * anymore. This is useful to pin the object for a bit longer, after the last event source needing it
+         * is gone. */
+        unsigned n_busy;
+
         /* A linked list of all inotify objects with data already read, that still need processing. We keep this list
          * to make it efficient to figure out what inotify objects to process data on next. */
         LIST_FIELDS(struct inotify_data, buffered);
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 7cc55be1..13e16f9b 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
@@ -86,6 +86,30 @@ int event_reset_time(
         return created;
 }
 
+int event_reset_time_relative(
+                sd_event *e,
+                sd_event_source **s,
+                clockid_t clock,
+                uint64_t usec,
+                uint64_t accuracy,
+                sd_event_time_handler_t callback,
+                void *userdata,
+                int64_t priority,
+                const char *description,
+                bool force_reset) {
+
+        usec_t usec_now;
+        int r;
+
+        assert(e);
+
+        r = sd_event_now(e, clock, &usec_now);
+        if (r < 0)
+                return log_debug_errno(r, "sd-event: Failed to get the current time: %m");
+
+        return event_reset_time(e, s, clock, usec_add(usec_now, usec), accuracy, callback, userdata, priority, description, force_reset);
+}
+
 int event_source_disable(sd_event_source *s) {
         if (!s)
                 return 0;
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 c8f97bc8..64a41992 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
@@ -5,9 +5,27 @@
 
 #include "sd-event.h"
 
-int event_reset_time(sd_event *e, sd_event_source **s,
-                     clockid_t clock, uint64_t usec, uint64_t accuracy,
-                     sd_event_time_handler_t callback, void *userdata,
-                     int64_t priority, const char *description, bool force_reset);
+int event_reset_time(
+                sd_event *e,
+                sd_event_source **s,
+                clockid_t clock,
+                uint64_t usec,
+                uint64_t accuracy,
+                sd_event_time_handler_t callback,
+                void *userdata,
+                int64_t priority,
+                const char *description,
+                bool force_reset);
+int event_reset_time_relative(
+                sd_event *e,
+                sd_event_source **s,
+                clockid_t clock,
+                uint64_t usec,
+                uint64_t accuracy,
+                sd_event_time_handler_t callback,
+                void *userdata,
+                int64_t priority,
+                const char *description,
+                bool force_reset);
 int event_source_disable(sd_event_source *s);
 int event_source_is_enabled(sd_event_source *s);
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 0acbdd54..e87d900c 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
@@ -1824,6 +1824,29 @@ static void event_free_inode_data(
         free(d);
 }
 
+static void event_gc_inotify_data(
+                sd_event *e,
+                struct inotify_data *d) {
+
+        assert(e);
+
+        /* GCs the inotify data object if we don't need it anymore. That's the case if we don't want to watch
+         * any inode with it anymore, which in turn happens if no event source of this priority is interested
+         * in any inode any longer. That said, we maintain an extra busy counter: if non-zero we'll delay GC
+         * (under the expectation that the GC is called again once the counter is decremented). */
+
+        if (!d)
+                return;
+
+        if (!hashmap_isempty(d->inodes))
+                return;
+
+        if (d->n_busy > 0)
+                return;
+
+        event_free_inotify_data(e, d);
+}
+
 static void event_gc_inode_data(
                 sd_event *e,
                 struct inode_data *d) {
@@ -1841,8 +1864,7 @@ static void event_gc_inode_data(
         inotify_data = d->inotify_data;
         event_free_inode_data(e, d);
 
-        if (inotify_data && hashmap_isempty(inotify_data->inodes))
-                event_free_inotify_data(e, inotify_data);
+        event_gc_inotify_data(e, inotify_data);
 }
 
 static int event_make_inode_data(
@@ -1971,24 +1993,25 @@ static int inotify_exit_callback(sd_event_source *s, const struct inotify_event
         return sd_event_exit(sd_event_source_get_event(s), PTR_TO_INT(userdata));
 }
 
-_public_ int sd_event_add_inotify(
+static int event_add_inotify_fd_internal(
                 sd_event *e,
                 sd_event_source **ret,
-                const char *path,
+                int fd,
+                bool donate,
                 uint32_t mask,
                 sd_event_inotify_handler_t callback,
                 void *userdata) {
 
+        _cleanup_close_ int donated_fd = donate ? fd : -1;
+        _cleanup_(source_freep) sd_event_source *s = NULL;
         struct inotify_data *inotify_data = NULL;
         struct inode_data *inode_data = NULL;
-        _cleanup_close_ int fd = -1;
-        _cleanup_(source_freep) sd_event_source *s = NULL;
         struct stat st;
         int r;
 
         assert_return(e, -EINVAL);
         assert_return(e = event_resolve(e), -ENOPKG);
-        assert_return(path, -EINVAL);
+        assert_return(fd >= 0, -EBADF);
         assert_return(e->state != SD_EVENT_FINISHED, -ESTALE);
         assert_return(!event_pid_changed(e), -ECHILD);
 
@@ -2001,12 +2024,6 @@ _public_ int sd_event_add_inotify(
         if (mask & IN_MASK_ADD)
                 return -EINVAL;
 
-        fd = open(path, O_PATH|O_CLOEXEC|
-                  (mask & IN_ONLYDIR ? O_DIRECTORY : 0)|
-                  (mask & IN_DONT_FOLLOW ? O_NOFOLLOW : 0));
-        if (fd < 0)
-                return -errno;
-
         if (fstat(fd, &st) < 0)
                 return -errno;
 
@@ -2026,14 +2043,24 @@ _public_ int sd_event_add_inotify(
 
         r = event_make_inode_data(e, inotify_data, st.st_dev, st.st_ino, &inode_data);
         if (r < 0) {
-                event_free_inotify_data(e, inotify_data);
+                event_gc_inotify_data(e, inotify_data);
                 return r;
         }
 
         /* Keep the O_PATH fd around until the first iteration of the loop, so that we can still change the priority of
          * the event source, until then, for which we need the original inode. */
         if (inode_data->fd < 0) {
-                inode_data->fd = TAKE_FD(fd);
+                if (donated_fd >= 0)
+                        inode_data->fd = TAKE_FD(donated_fd);
+                else {
+                        inode_data->fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
+                        if (inode_data->fd < 0) {
+                                r = -errno;
+                                event_gc_inode_data(e, inode_data);
+                                return r;
+                        }
+                }
+
                 LIST_PREPEND(to_close, e->inode_data_to_close, inode_data);
         }
 
@@ -2046,8 +2073,6 @@ _public_ int sd_event_add_inotify(
         if (r < 0)
                 return r;
 
-        (void) sd_event_source_set_description(s, path);
-
         if (ret)
                 *ret = s;
         TAKE_PTR(s);
@@ -2055,6 +2080,48 @@ _public_ int sd_event_add_inotify(
         return 0;
 }
 
+_public_ int sd_event_add_inotify_fd(
+                sd_event *e,
+                sd_event_source **ret,
+                int fd,
+                uint32_t mask,
+                sd_event_inotify_handler_t callback,
+                void *userdata) {
+
+        return event_add_inotify_fd_internal(e, ret, fd, /* donate= */ false, mask, callback, userdata);
+}
+
+_public_ int sd_event_add_inotify(
+                sd_event *e,
+                sd_event_source **ret,
+                const char *path,
+                uint32_t mask,
+                sd_event_inotify_handler_t callback,
+                void *userdata) {
+
+        sd_event_source *s = NULL; /* avoid false maybe-uninitialized warning */
+        int fd, r;
+
+        assert_return(path, -EINVAL);
+
+        fd = open(path, O_PATH|O_CLOEXEC|
+                  (mask & IN_ONLYDIR ? O_DIRECTORY : 0)|
+                  (mask & IN_DONT_FOLLOW ? O_NOFOLLOW : 0));
+        if (fd < 0)
+                return -errno;
+
+        r = event_add_inotify_fd_internal(e, &s, fd, /* donate= */ true, mask, callback, userdata);
+        if (r < 0)
+                return r;
+
+        (void) sd_event_source_set_description(s, path);
+
+        if (ret)
+                *ret = s;
+
+        return r;
+}
+
 static sd_event_source* event_source_free(sd_event_source *s) {
         if (!s)
                 return NULL;
@@ -2845,7 +2912,7 @@ fail:
         return r;
 }
 
-static int event_source_leave_ratelimit(sd_event_source *s) {
+static int event_source_leave_ratelimit(sd_event_source *s, bool run_callback) {
         int r;
 
         assert(s);
@@ -2877,6 +2944,30 @@ static int event_source_leave_ratelimit(sd_event_source *s) {
         ratelimit_reset(&s->rate_limit);
 
         log_debug("Event source %p (%s) left rate limit state.", s, strna(s->description));
+
+        if (run_callback && s->ratelimit_expire_callback) {
+                s->dispatching = true;
+                r = s->ratelimit_expire_callback(s, s->userdata);
+                s->dispatching = false;
+
+                if (r < 0) {
+                        log_debug_errno(r, "Ratelimit expiry callback of event source %s (type %s) returned error, %s: %m",
+                                        strna(s->description),
+                                        event_source_type_to_string(s->type),
+                                        s->exit_on_failure ? "exiting" : "disabling");
+
+                        if (s->exit_on_failure)
+                                (void) sd_event_exit(s->event, r);
+                }
+
+                if (s->n_ref == 0)
+                        source_free(s);
+                else if (r < 0)
+                        assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
+
+                return 1;
+        }
+
         return 0;
 
 fail:
@@ -3055,7 +3146,7 @@ static int flush_timer(sd_event *e, int fd, uint32_t events, usec_t *next) {
 
         ss = read(fd, &x, sizeof(x));
         if (ss < 0) {
-                if (IN_SET(errno, EAGAIN, EINTR))
+                if (ERRNO_IS_TRANSIENT(errno))
                         return 0;
 
                 return -errno;
@@ -3076,6 +3167,7 @@ static int process_timer(
                 struct clock_data *d) {
 
         sd_event_source *s;
+        bool callback_invoked = false;
         int r;
 
         assert(e);
@@ -3093,9 +3185,11 @@ static int process_timer(
                          * again. */
                         assert(s->ratelimited);
 
-                        r = event_source_leave_ratelimit(s);
+                        r = event_source_leave_ratelimit(s, /* run_callback */ true);
                         if (r < 0)
                                 return r;
+                        else if (r == 1)
+                                callback_invoked = true;
 
                         continue;
                 }
@@ -3110,7 +3204,7 @@ static int process_timer(
                 event_source_time_prioq_reshuffle(s);
         }
 
-        return 0;
+        return callback_invoked;
 }
 
 static int process_child(sd_event *e, int64_t threshold, int64_t *ret_min_priority) {
@@ -3258,7 +3352,7 @@ static int process_signal(sd_event *e, struct signal_data *d, uint32_t events, i
 
                 n = read(d->fd, &si, sizeof(si));
                 if (n < 0) {
-                        if (IN_SET(errno, EAGAIN, EINTR))
+                        if (ERRNO_IS_TRANSIENT(errno))
                                 return 0;
 
                         return -errno;
@@ -3312,7 +3406,7 @@ static int event_inotify_data_read(sd_event *e, struct inotify_data *d, uint32_t
 
         n = read(d->fd, &d->buffer, sizeof(d->buffer));
         if (n < 0) {
-                if (IN_SET(errno, EAGAIN, EINTR))
+                if (ERRNO_IS_TRANSIENT(errno))
                         return 0;
 
                 return -errno;
@@ -3560,13 +3654,23 @@ static int source_dispatch(sd_event_source *s) {
                 sz = offsetof(struct inotify_event, name) + d->buffer.ev.len;
                 assert(d->buffer_filled >= sz);
 
+                /* If the inotify callback destroys the event source then this likely means we don't need to
+                 * watch the inode anymore, and thus also won't need the inotify object anymore. But if we'd
+                 * free it immediately, then we couldn't drop the event from the inotify event queue without
+                 * memory corruption anymore, as below. Hence, let's not free it immediately, but mark it
+                 * "busy" with a counter (which will ensure it's not GC'ed away prematurely). Let's then
+                 * explicitly GC it after we are done dropping the inotify event from the buffer. */
+                d->n_busy++;
                 r = s->inotify.callback(s, &d->buffer.ev, s->userdata);
+                d->n_busy--;
 
-                /* When no event is pending anymore on this inotify object, then let's drop the event from the
-                 * buffer. */
+                /* When no event is pending anymore on this inotify object, then let's drop the event from
+                 * the inotify event queue buffer. */
                 if (d->n_pending == 0)
                         event_inotify_data_drop(e, d, sz);
 
+                /* Now we don't want to access 'd' anymore, it's OK to GC now. */
+                event_gc_inotify_data(e, d);
                 break;
         }
 
@@ -3591,7 +3695,7 @@ static int source_dispatch(sd_event_source *s) {
         if (s->n_ref == 0)
                 source_free(s);
         else if (r < 0)
-                sd_event_source_set_enabled(s, SD_EVENT_OFF);
+                assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
 
         return 1;
 }
@@ -3632,7 +3736,7 @@ static int event_prepare(sd_event *e) {
                 if (s->n_ref == 0)
                         source_free(s);
                 else if (r < 0)
-                        sd_event_source_set_enabled(s, SD_EVENT_OFF);
+                        assert_se(sd_event_source_set_enabled(s, SD_EVENT_OFF) >= 0);
         }
 
         return 0;
@@ -3693,10 +3797,7 @@ static int arm_watchdog(sd_event *e) {
         if (its.it_value.tv_sec == 0 && its.it_value.tv_nsec == 0)
                 its.it_value.tv_nsec = 1;
 
-        if (timerfd_settime(e->watchdog_fd, TFD_TIMER_ABSTIME, &its, NULL) < 0)
-                return -errno;
-
-        return 0;
+        return RET_NERRNO(timerfd_settime(e->watchdog_fd, TFD_TIMER_ABSTIME, &its, NULL));
 }
 
 static int process_watchdog(sd_event *e) {
@@ -3806,7 +3907,7 @@ static int epoll_wait_usec(
                 int maxevents,
                 usec_t timeout) {
 
-        int r, msec;
+        int msec;
 #if 0
         static bool epoll_pwait2_absent = false;
 
@@ -3846,14 +3947,7 @@ static int epoll_wait_usec(
                         msec = (int) k;
         }
 
-        r = epoll_wait(fd,
-                       events,
-                       maxevents,
-                       msec);
-        if (r < 0)
-                return -errno;
-
-        return r;
+        return RET_NERRNO(epoll_wait(fd, events, maxevents, msec));
 }
 
 static int process_epoll(sd_event *e, usec_t timeout, int64_t threshold, int64_t *ret_min_priority) {
@@ -4024,15 +4118,15 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
         if (r < 0)
                 goto finish;
 
-        r = process_timer(e, e->timestamp.realtime, &e->realtime);
+        r = process_inotify(e);
         if (r < 0)
                 goto finish;
 
-        r = process_timer(e, e->timestamp.boottime, &e->boottime);
+        r = process_timer(e, e->timestamp.realtime, &e->realtime);
         if (r < 0)
                 goto finish;
 
-        r = process_timer(e, e->timestamp.monotonic, &e->monotonic);
+        r = process_timer(e, e->timestamp.boottime, &e->boottime);
         if (r < 0)
                 goto finish;
 
@@ -4044,9 +4138,20 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) {
         if (r < 0)
                 goto finish;
 
-        r = process_inotify(e);
+        r = process_timer(e, e->timestamp.monotonic, &e->monotonic);
         if (r < 0)
                 goto finish;
+        else if (r == 1) {
+                /* Ratelimit expiry callback was called. Let's postpone processing pending sources and
+                 * put loop in the initial state in order to evaluate (in the next iteration) also sources
+                 * there were potentially re-enabled by the callback.
+                 *
+                 * Wondering why we treat only this invocation of process_timer() differently? Once event
+                 * source is ratelimited we essentially transform it into CLOCK_MONOTONIC timer hence
+                 * ratelimit expiry callback is never called for any other timer type. */
+                r = 0;
+                goto finish;
+        }
 
         if (event_next_pending(e)) {
                 e->state = SD_EVENT_PENDING;
@@ -4117,7 +4222,7 @@ _public_ int sd_event_run(sd_event *e, uint64_t timeout) {
 
                 this_run = now(CLOCK_MONOTONIC);
 
-                l = u64log2(this_run - e->last_run_usec);
+                l = log2u64(this_run - e->last_run_usec);
                 assert(l < ELEMENTSOF(e->delays));
                 e->delays[l]++;
 
@@ -4416,7 +4521,7 @@ _public_ int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval
 
         /* When ratelimiting is configured we'll always reset the rate limit state first and start fresh,
          * non-ratelimited. */
-        r = event_source_leave_ratelimit(s);
+        r = event_source_leave_ratelimit(s, /* run_callback */ false);
         if (r < 0)
                 return r;
 
@@ -4424,6 +4529,13 @@ _public_ int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval
         return 0;
 }
 
+_public_ int sd_event_source_set_ratelimit_expire_callback(sd_event_source *s, sd_event_handler_t callback) {
+        assert_return(s, -EINVAL);
+
+        s->ratelimit_expire_callback = callback;
+        return 0;
+}
+
 _public_ int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval, unsigned *ret_burst) {
         assert_return(s, -EINVAL);
 
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 d80145a8..9ec2abc8 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
@@ -224,7 +224,7 @@ int id128_get_product(sd_id128_t *ret) {
 
         r = id128_read("/sys/class/dmi/id/product_uuid", ID128_UUID, &uuid);
         if (r == -ENOENT)
-                r = id128_read("/sys/firmware/devicetree/base/vm,uuid", ID128_UUID, &uuid);
+                r = id128_read("/proc/device-tree/vm,uuid", ID128_UUID, &uuid);
         if (r < 0)
                 return 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 0860f493..c3ed3bbb 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
@@ -6,21 +6,14 @@
 #include <fcntl.h>
 #include <unistd.h>
 
-#if HAVE_OPENSSL
-#include <openssl/hmac.h>
-#include <openssl/sha.h>
-#endif
-
 #include "sd-id128.h"
 
 #include "alloc-util.h"
 #include "fd-util.h"
 #include "hexdecoct.h"
+#include "hmac.h"
 #include "id128-util.h"
 #include "io-util.h"
-#if !HAVE_OPENSSL
-#include "khash.h"
-#endif
 #include "macro.h"
 #include "missing_syscall.h"
 #include "random-util.h"
@@ -283,43 +276,15 @@ _public_ int sd_id128_randomize(sd_id128_t *ret) {
 }
 
 static int get_app_specific(sd_id128_t base, sd_id128_t app_id, sd_id128_t *ret) {
+        uint8_t hmac[SHA256_DIGEST_SIZE];
         sd_id128_t result;
 
         assert(ret);
 
-#if HAVE_OPENSSL
-        /* We prefer doing this in-process, since we this means we are not dependent on kernel configuration,
-         * and this also works in locked down container environments. But some distros don't like OpenSSL's
-         * license and its (in-) compatibility with GPL2, hence also support khash */
-        uint8_t md[256/8];
-        if (!HMAC(EVP_sha256(),
-                  &base, sizeof(base),
-                  (const unsigned char*) &app_id, sizeof(app_id),
-                  md, NULL))
-                return -ENOTRECOVERABLE;
+        hmac_sha256(&base, sizeof(base), &app_id, sizeof(app_id), hmac);
 
         /* Take only the first half. */
-        memcpy(&result, md, MIN(sizeof(md), sizeof(result)));
-#else
-        _cleanup_(khash_unrefp) khash *h = NULL;
-        const void *p;
-        int r;
-
-        r = khash_new_with_key(&h, "hmac(sha256)", &base, sizeof(base));
-        if (r < 0)
-                return r;
-
-        r = khash_put(h, &app_id, sizeof(app_id));
-        if (r < 0)
-                return r;
-
-        r = khash_digest_data(h, &p);
-        if (r < 0)
-                return r;
-
-        /* We chop off the trailing 16 bytes */
-        memcpy(&result, p, MIN(khash_get_size(h), sizeof(result)));
-#endif
+        memcpy(&result, hmac, MIN(sizeof(hmac), sizeof(result)));
 
         *ret = id128_make_v4_uuid(result);
         return 0;
diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp-client.h b/src/libnm-systemd-core/src/systemd/sd-dhcp-client.h
index 23979b12..4af4b45f 100644
--- a/src/libnm-systemd-core/src/systemd/sd-dhcp-client.h
+++ b/src/libnm-systemd-core/src/systemd/sd-dhcp-client.h
@@ -45,65 +45,66 @@ enum {
 };
 
 enum {
-        SD_DHCP_OPTION_PAD                         = 0,
-        SD_DHCP_OPTION_SUBNET_MASK                 = 1,
-        SD_DHCP_OPTION_TIME_OFFSET                 = 2,
-        SD_DHCP_OPTION_ROUTER                      = 3,
-        SD_DHCP_OPTION_DOMAIN_NAME_SERVER          = 6,
-        SD_DHCP_OPTION_LPR_SERVER                  = 9,
-        SD_DHCP_OPTION_HOST_NAME                   = 12,
-        SD_DHCP_OPTION_BOOT_FILE_SIZE              = 13,
-        SD_DHCP_OPTION_DOMAIN_NAME                 = 15,
-        SD_DHCP_OPTION_ROOT_PATH                   = 17,
-        SD_DHCP_OPTION_ENABLE_IP_FORWARDING        = 19,
-        SD_DHCP_OPTION_ENABLE_IP_FORWARDING_NL     = 20,
-        SD_DHCP_OPTION_POLICY_FILTER               = 21,
-        SD_DHCP_OPTION_INTERFACE_MDR               = 22,
-        SD_DHCP_OPTION_INTERFACE_TTL               = 23,
-        SD_DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT = 24,
-        SD_DHCP_OPTION_INTERFACE_MTU               = 26,
-        SD_DHCP_OPTION_BROADCAST                   = 28,
-       /* Windows 10 option to send when Anonymize=true */
-        SD_DHCP_OPTION_ROUTER_DISCOVER             = 31,
-        SD_DHCP_OPTION_STATIC_ROUTE                = 33,
-        SD_DHCP_OPTION_NTP_SERVER                  = 42,
-        SD_DHCP_OPTION_VENDOR_SPECIFIC             = 43,
-       /* Windows 10 option to send when Anonymize=true */
-        SD_DHCP_OPTION_NETBIOS_NAMESERVER          = 44,
-       /* Windows 10 option to send when Anonymize=true */
-        SD_DHCP_OPTION_NETBIOS_NODETYPE            = 46,
-       /* Windows 10 option to send when Anonymize=true */
-        SD_DHCP_OPTION_NETBIOS_SCOPE               = 47,
-        SD_DHCP_OPTION_REQUESTED_IP_ADDRESS        = 50,
-        SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME       = 51,
-        SD_DHCP_OPTION_OVERLOAD                    = 52,
-        SD_DHCP_OPTION_MESSAGE_TYPE                = 53,
-        SD_DHCP_OPTION_SERVER_IDENTIFIER           = 54,
-        SD_DHCP_OPTION_PARAMETER_REQUEST_LIST      = 55,
-        SD_DHCP_OPTION_ERROR_MESSAGE               = 56,
-        SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE        = 57,
-        SD_DHCP_OPTION_RENEWAL_T1_TIME             = 58,
-        SD_DHCP_OPTION_REBINDING_T2_TIME           = 59,
-        SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER     = 60,
-        SD_DHCP_OPTION_CLIENT_IDENTIFIER           = 61,
-        SD_DHCP_OPTION_SMTP_SERVER                 = 69,
-        SD_DHCP_OPTION_POP3_SERVER                 = 70,
-        SD_DHCP_OPTION_USER_CLASS                  = 77,
-        SD_DHCP_OPTION_FQDN                        = 81,
-        SD_DHCP_OPTION_RELAY_AGENT_INFORMATION     = 82,
-        SD_DHCP_OPTION_NEW_POSIX_TIMEZONE          = 100,
-        SD_DHCP_OPTION_NEW_TZDB_TIMEZONE           = 101,
-        SD_DHCP_OPTION_DOMAIN_SEARCH_LIST          = 119,
-        SD_DHCP_OPTION_SIP_SERVER                  = 120,
-        SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE      = 121,
-        SD_DHCP_OPTION_MUD_URL                     = 161,
-        SD_DHCP_OPTION_PRIVATE_BASE                = 224,
-       /* Windows 10 option to send when Anonymize=true */
+        SD_DHCP_OPTION_PAD                            = 0,
+        SD_DHCP_OPTION_SUBNET_MASK                    = 1,
+        SD_DHCP_OPTION_TIME_OFFSET                    = 2,
+        SD_DHCP_OPTION_ROUTER                         = 3,
+        SD_DHCP_OPTION_DOMAIN_NAME_SERVER             = 6,
+        SD_DHCP_OPTION_LPR_SERVER                     = 9,
+        SD_DHCP_OPTION_HOST_NAME                      = 12,
+        SD_DHCP_OPTION_BOOT_FILE_SIZE                 = 13,
+        SD_DHCP_OPTION_DOMAIN_NAME                    = 15,
+        SD_DHCP_OPTION_ROOT_PATH                      = 17,
+        SD_DHCP_OPTION_ENABLE_IP_FORWARDING           = 19,
+        SD_DHCP_OPTION_ENABLE_IP_FORWARDING_NL        = 20,
+        SD_DHCP_OPTION_POLICY_FILTER                  = 21,
+        SD_DHCP_OPTION_INTERFACE_MDR                  = 22,
+        SD_DHCP_OPTION_INTERFACE_TTL                  = 23,
+        SD_DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT    = 24,
+        SD_DHCP_OPTION_INTERFACE_MTU                  = 26,
+        SD_DHCP_OPTION_BROADCAST                      = 28,
+        /* Windows 10 option to send when Anonymize=true */
+        SD_DHCP_OPTION_ROUTER_DISCOVER                = 31,
+        SD_DHCP_OPTION_STATIC_ROUTE                   = 33,
+        SD_DHCP_OPTION_NTP_SERVER                     = 42,
+        SD_DHCP_OPTION_VENDOR_SPECIFIC                = 43,
+        /* Windows 10 option to send when Anonymize=true */
+        SD_DHCP_OPTION_NETBIOS_NAMESERVER             = 44,
+        /* Windows 10 option to send when Anonymize=true */
+        SD_DHCP_OPTION_NETBIOS_NODETYPE               = 46,
+        /* Windows 10 option to send when Anonymize=true */
+        SD_DHCP_OPTION_NETBIOS_SCOPE                  = 47,
+        SD_DHCP_OPTION_REQUESTED_IP_ADDRESS           = 50,
+        SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME          = 51,
+        SD_DHCP_OPTION_OVERLOAD                       = 52,
+        SD_DHCP_OPTION_MESSAGE_TYPE                   = 53,
+        SD_DHCP_OPTION_SERVER_IDENTIFIER              = 54,
+        SD_DHCP_OPTION_PARAMETER_REQUEST_LIST         = 55,
+        SD_DHCP_OPTION_ERROR_MESSAGE                  = 56,
+        SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE           = 57,
+        SD_DHCP_OPTION_RENEWAL_T1_TIME                = 58,
+        SD_DHCP_OPTION_REBINDING_T2_TIME              = 59,
+        SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER        = 60,
+        SD_DHCP_OPTION_CLIENT_IDENTIFIER              = 61,
+        SD_DHCP_OPTION_SMTP_SERVER                    = 69,
+        SD_DHCP_OPTION_POP3_SERVER                    = 70,
+        SD_DHCP_OPTION_USER_CLASS                     = 77,
+        SD_DHCP_OPTION_FQDN                           = 81,
+        SD_DHCP_OPTION_RELAY_AGENT_INFORMATION        = 82,
+        SD_DHCP_OPTION_NEW_POSIX_TIMEZONE             = 100,
+        SD_DHCP_OPTION_NEW_TZDB_TIMEZONE              = 101,
+        SD_DHCP_OPTION_DOMAIN_SEARCH_LIST             = 119,
+        SD_DHCP_OPTION_SIP_SERVER                     = 120,
+        SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE         = 121,
+        SD_DHCP_OPTION_MUD_URL                        = 161,
+        SD_DHCP_OPTION_6RD                            = 212,
+        SD_DHCP_OPTION_PRIVATE_BASE                   = 224,
+        /* Windows 10 option to send when Anonymize=true */
         SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE = 249,
-       /* Windows 10 option to send when Anonymize=true */
-        SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY = 252,
-        SD_DHCP_OPTION_PRIVATE_LAST                = 254,
-        SD_DHCP_OPTION_END                         = 255,
+        /* Windows 10 option to send when Anonymize=true */
+        SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY    = 252,
+        SD_DHCP_OPTION_PRIVATE_LAST                   = 254,
+        SD_DHCP_OPTION_END                            = 255,
 };
 
 /* Suboptions for SD_DHCP_OPTION_RELAY_AGENT_INFORMATION option */
diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp-lease.h b/src/libnm-systemd-core/src/systemd/sd-dhcp-lease.h
index 5abf9a40..478bbfd7 100644
--- a/src/libnm-systemd-core/src/systemd/sd-dhcp-lease.h
+++ b/src/libnm-systemd-core/src/systemd/sd-dhcp-lease.h
@@ -71,6 +71,13 @@ int sd_dhcp_lease_get_routes(sd_dhcp_lease *lease, sd_dhcp_route ***routes);
 int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const void **data, size_t *data_len);
 int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const void **client_id, size_t *client_id_len);
 int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone);
+int sd_dhcp_lease_get_6rd(
+                sd_dhcp_lease *lease,
+                uint8_t *ret_ipv4masklen,
+                uint8_t *ret_prefixlen,
+                struct in6_addr *ret_prefix,
+                const struct in_addr **ret_br_addresses,
+                size_t *ret_n_br_addresses);
 
 int sd_dhcp_route_get_destination(sd_dhcp_route *route, struct in_addr *destination);
 int sd_dhcp_route_get_destination_prefix_length(sd_dhcp_route *route, uint8_t *length);
diff --git a/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h b/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h
index f3889782..0e23c84e 100644
--- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h
+++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-client.h
@@ -39,44 +39,151 @@ enum {
         SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST       = 13,
 };
 
+/* https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-2 */
 enum {
-        SD_DHCP6_OPTION_CLIENTID                   = 1,
-        SD_DHCP6_OPTION_SERVERID                   = 2,
-        SD_DHCP6_OPTION_IA_NA                      = 3,
-        SD_DHCP6_OPTION_IA_TA                      = 4,
-        SD_DHCP6_OPTION_IAADDR                     = 5,
-        SD_DHCP6_OPTION_ORO                        = 6,
-        SD_DHCP6_OPTION_PREFERENCE                 = 7,
-        SD_DHCP6_OPTION_ELAPSED_TIME               = 8,
-        SD_DHCP6_OPTION_RELAY_MSG                  = 9,
+        SD_DHCP6_OPTION_CLIENTID                   = 1,  /* RFC 8415 */
+        SD_DHCP6_OPTION_SERVERID                   = 2,  /* RFC 8415 */
+        SD_DHCP6_OPTION_IA_NA                      = 3,  /* RFC 8415 */
+        SD_DHCP6_OPTION_IA_TA                      = 4,  /* RFC 8415 */
+        SD_DHCP6_OPTION_IAADDR                     = 5,  /* RFC 8415 */
+        SD_DHCP6_OPTION_ORO                        = 6,  /* RFC 8415 */
+        SD_DHCP6_OPTION_PREFERENCE                 = 7,  /* RFC 8415 */
+        SD_DHCP6_OPTION_ELAPSED_TIME               = 8,  /* RFC 8415 */
+        SD_DHCP6_OPTION_RELAY_MSG                  = 9,  /* RFC 8415 */
         /* option code 10 is unassigned */
-        SD_DHCP6_OPTION_AUTH                       = 11,
-        SD_DHCP6_OPTION_UNICAST                    = 12,
-        SD_DHCP6_OPTION_STATUS_CODE                = 13,
-        SD_DHCP6_OPTION_RAPID_COMMIT               = 14,
-        SD_DHCP6_OPTION_USER_CLASS                 = 15,
-        SD_DHCP6_OPTION_VENDOR_CLASS               = 16,
-        SD_DHCP6_OPTION_VENDOR_OPTS                = 17,
-        SD_DHCP6_OPTION_INTERFACE_ID               = 18,
-        SD_DHCP6_OPTION_RECONF_MSG                 = 19,
-        SD_DHCP6_OPTION_RECONF_ACCEPT              = 20,
-
+        SD_DHCP6_OPTION_AUTH                       = 11,  /* RFC 8415 */
+        SD_DHCP6_OPTION_UNICAST                    = 12,  /* RFC 8415 */
+        SD_DHCP6_OPTION_STATUS_CODE                = 13,  /* RFC 8415 */
+        SD_DHCP6_OPTION_RAPID_COMMIT               = 14,  /* RFC 8415 */
+        SD_DHCP6_OPTION_USER_CLASS                 = 15,  /* RFC 8415 */
+        SD_DHCP6_OPTION_VENDOR_CLASS               = 16,  /* RFC 8415 */
+        SD_DHCP6_OPTION_VENDOR_OPTS                = 17,  /* RFC 8415 */
+        SD_DHCP6_OPTION_INTERFACE_ID               = 18,  /* RFC 8415 */
+        SD_DHCP6_OPTION_RECONF_MSG                 = 19,  /* RFC 8415 */
+        SD_DHCP6_OPTION_RECONF_ACCEPT              = 20,  /* RFC 8415 */
+        SD_DHCP6_OPTION_SIP_SERVER_DOMAIN_NAME     = 21,  /* RFC 3319 */
+        SD_DHCP6_OPTION_SIP_SERVER_ADDRESS         = 22,  /* RFC 3319 */
         SD_DHCP6_OPTION_DNS_SERVERS                = 23,  /* RFC 3646 */
         SD_DHCP6_OPTION_DOMAIN_LIST                = 24,  /* RFC 3646 */
-        SD_DHCP6_OPTION_IA_PD                      = 25,  /* RFC 3633, prefix delegation */
-        SD_DHCP6_OPTION_IA_PD_PREFIX               = 26,  /* RFC 3633, prefix delegation */
-
+        SD_DHCP6_OPTION_IA_PD                      = 25,  /* RFC 3633, RFC 8415 */
+        SD_DHCP6_OPTION_IA_PD_PREFIX               = 26,  /* RFC 3633, RFC 8415 */
+        SD_DHCP6_OPTION_NIS_SERVERS                = 27,  /* RFC 3898 */
+        SD_DHCP6_OPTION_NISP_SERVERS               = 28,  /* RFC 3898 */
+        SD_DHCP6_OPTION_NIS_DOMAIN_NAME            = 29,  /* RFC 3898 */
+        SD_DHCP6_OPTION_NISP_DOMAIN_NAME           = 30,  /* RFC 3898 */
         SD_DHCP6_OPTION_SNTP_SERVERS               = 31,  /* RFC 4075, deprecated */
-        SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME   = 32,  /* RFC 8415, sec. 21.23 */
-
+        SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME   = 32,  /* RFC 4242, 8415, sec. 21.23 */
+        SD_DHCP6_OPTION_BCMCS_SERVER_D             = 33,  /* RFC 4280 */
+        SD_DHCP6_OPTION_BCMCS_SERVER_A             = 34,  /* RFC 4280 */
         /* option code 35 is unassigned */
-
-        SD_DHCP6_OPTION_FQDN                       = 39,  /* RFC 4704 */
-
+        SD_DHCP6_OPTION_GEOCONF_CIVIC              = 36,  /* RFC 4776 */
+        SD_DHCP6_OPTION_REMOTE_ID                  = 37,  /* RFC 4649 */
+        SD_DHCP6_OPTION_SUBSCRIBER_ID              = 38,  /* RFC 4580 */
+        SD_DHCP6_OPTION_CLIENT_FQDN                = 39,  /* RFC 4704 */
+        SD_DHCP6_OPTION_PANA_AGENT                 = 40,  /* RFC 5192 */
+        SD_DHCP6_OPTION_NEW_POSIX_TIMEZONE         = 41,  /* RFC 4833 */
+        SD_DHCP6_OPTION_NEW_TZDB_TIMEZONE          = 42,  /* RFC 4833 */
+        SD_DHCP6_OPTION_ERO                        = 43,  /* RFC 4994 */
+        SD_DHCP6_OPTION_LQ_QUERY                   = 44,  /* RFC 5007 */
+        SD_DHCP6_OPTION_CLIENT_DATA                = 45,  /* RFC 5007 */
+        SD_DHCP6_OPTION_CLT_TIME                   = 46,  /* RFC 5007 */
+        SD_DHCP6_OPTION_LQ_RELAY_DATA              = 47,  /* RFC 5007 */
+        SD_DHCP6_OPTION_LQ_CLIENT_LINK             = 48,  /* RFC 5007 */
+        SD_DHCP6_OPTION_MIP6_HNIDF                 = 49,  /* RFC 6610 */
+        SD_DHCP6_OPTION_MIP6_VDINF                 = 50,  /* RFC 6610 */
+        SD_DHCP6_OPTION_V6_LOST                    = 51,  /* RFC 5223 */
+        SD_DHCP6_OPTION_CAPWAP_AC_V6               = 52,  /* RFC 5417 */
+        SD_DHCP6_OPTION_RELAY_ID                   = 53,  /* RFC 5460 */
+        SD_DHCP6_OPTION_IPV6_ADDRESS_MOS           = 54,  /* RFC 5678 */
+        SD_DHCP6_OPTION_IPV6_FQDN_MOS              = 55,  /* RFC 5678 */
         SD_DHCP6_OPTION_NTP_SERVER                 = 56,  /* RFC 5908 */
-        SD_DHCP6_OPTION_MUD_URL                    = 112, /* RFC 8250 */
-
-        /* option codes 89-142 are unassigned */
+        SD_DHCP6_OPTION_V6_ACCESS_DOMAIN           = 57,  /* RFC 5986 */
+        SD_DHCP6_OPTION_SIP_UA_CS_LIST             = 58,  /* RFC 6011 */
+        SD_DHCP6_OPTION_BOOTFILE_URL               = 59,  /* RFC 5970 */
+        SD_DHCP6_OPTION_BOOTFILE_PARAM             = 60,  /* RFC 5970 */
+        SD_DHCP6_OPTION_CLIENT_ARCH_TYPE           = 61,  /* RFC 5970 */
+        SD_DHCP6_OPTION_NII                        = 62,  /* RFC 5970 */
+        SD_DHCP6_OPTION_GEOLOCATION                = 63,  /* RFC 6225 */
+        SD_DHCP6_OPTION_AFTR_NAME                  = 64,  /* RFC 6334 */
+        SD_DHCP6_OPTION_ERP_LOCAL_DOMAIN_NAME      = 65,  /* RFC 6440 */
+        SD_DHCP6_OPTION_RSOO                       = 66,  /* RFC 6422 */
+        SD_DHCP6_OPTION_PD_EXCLUDE                 = 67,  /* RFC 6603 */
+        SD_DHCP6_OPTION_VSS                        = 68,  /* RFC 6607 */
+        SD_DHCP6_OPTION_MIP6_IDINF                 = 69,  /* RFC 6610 */
+        SD_DHCP6_OPTION_MIP6_UDINF                 = 70,  /* RFC 6610 */
+        SD_DHCP6_OPTION_MIP6_HNP                   = 71,  /* RFC 6610 */
+        SD_DHCP6_OPTION_MIP6_HAA                   = 72,  /* RFC 6610 */
+        SD_DHCP6_OPTION_MIP6_HAF                   = 73,  /* RFC 6610 */
+        SD_DHCP6_OPTION_RDNSS_SELECTION            = 74,  /* RFC 6731 */
+        SD_DHCP6_OPTION_KRB_PRINCIPAL_NAME         = 75,  /* RFC 6784 */
+        SD_DHCP6_OPTION_KRB_REALM_NAME             = 76,  /* RFC 6784 */
+        SD_DHCP6_OPTION_KRB_DEFAULT_REALM_NAME     = 77,  /* RFC 6784 */
+        SD_DHCP6_OPTION_KRB_KDC                    = 78,  /* RFC 6784 */
+        SD_DHCP6_OPTION_CLIENT_LINKLAYER_ADDR      = 79,  /* RFC 6939 */
+        SD_DHCP6_OPTION_LINK_ADDRESS               = 80,  /* RFC 6977 */
+        SD_DHCP6_OPTION_RADIUS                     = 81,  /* RFC 7037 */
+        SD_DHCP6_OPTION_SOL_MAX_RT                 = 82,  /* RFC 7083, RFC 8415 */
+        SD_DHCP6_OPTION_INF_MAX_RT                 = 83,  /* RFC 7083, RFC 8415 */
+        SD_DHCP6_OPTION_ADDRSEL                    = 84,  /* RFC 7078 */
+        SD_DHCP6_OPTION_ADDRSEL_TABLE              = 85,  /* RFC 7078 */
+        SD_DHCP6_OPTION_V6_PCP_SERVER              = 86,  /* RFC 7291 */
+        SD_DHCP6_OPTION_DHCPV4_MSG                 = 87,  /* RFC 7341 */
+        SD_DHCP6_OPTION_DHCP4_O_DHCP6_SERVER       = 88,  /* RFC 7341 */
+        SD_DHCP6_OPTION_S46_RULE                   = 89,  /* RFC 7598 */
+        SD_DHCP6_OPTION_S46_BR                     = 90,  /* RFC 7598, RFC 8539 */
+        SD_DHCP6_OPTION_S46_DMR                    = 91,  /* RFC 7598 */
+        SD_DHCP6_OPTION_S46_V4V6BIND               = 92,  /* RFC 7598 */
+        SD_DHCP6_OPTION_S46_PORTPARAMS             = 93,  /* RFC 7598 */
+        SD_DHCP6_OPTION_S46_CONT_MAPE              = 94,  /* RFC 7598 */
+        SD_DHCP6_OPTION_S46_CONT_MAPT              = 95,  /* RFC 7598 */
+        SD_DHCP6_OPTION_S46_CONT_LW                = 96,  /* RFC 7598 */
+        SD_DHCP6_OPTION_4RD                        = 97,  /* RFC 7600 */
+        SD_DHCP6_OPTION_4RD_MAP_RULE               = 98,  /* RFC 7600 */
+        SD_DHCP6_OPTION_4RD_NON_MAP_RULE           = 99,  /* RFC 7600 */
+        SD_DHCP6_OPTION_LQ_BASE_TIME               = 100, /* RFC 7653 */
+        SD_DHCP6_OPTION_LQ_START_TIME              = 101, /* RFC 7653 */
+        SD_DHCP6_OPTION_LQ_END_TIME                = 102, /* RFC 7653 */
+        SD_DHCP6_OPTION_CAPTIVE_PORTAL             = 103, /* RFC 8910 */
+        SD_DHCP6_OPTION_MPL_PARAMETERS             = 104, /* RFC 7774 */
+        SD_DHCP6_OPTION_ANI_ATT                    = 105, /* RFC 7839 */
+        SD_DHCP6_OPTION_ANI_NETWORK_NAME           = 106, /* RFC 7839 */
+        SD_DHCP6_OPTION_ANI_AP_NAME                = 107, /* RFC 7839 */
+        SD_DHCP6_OPTION_ANI_AP_BSSID               = 108, /* RFC 7839 */
+        SD_DHCP6_OPTION_ANI_OPERATOR_ID            = 109, /* RFC 7839 */
+        SD_DHCP6_OPTION_ANI_OPERATOR_REALM         = 110, /* RFC 7839 */
+        SD_DHCP6_OPTION_S46_PRIORITY               = 111, /* RFC 8026 */
+        SD_DHCP6_OPTION_MUD_URL_V6                 = 112, /* RFC 8520 */
+        SD_DHCP6_OPTION_V6_PREFIX64                = 113, /* RFC 8115 */
+        SD_DHCP6_OPTION_F_BINDING_STATUS           = 114, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_CONNECT_FLAGS            = 115, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_DNS_REMOVAL_INFO         = 116, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_DNS_HOST_NAME            = 117, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_DNS_ZONE_NAME            = 118, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_DNS_FLAGS                = 119, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_EXPIRATION_TIME          = 120, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_MAX_UNACKED_BNDUPD       = 121, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_MCLT                     = 122, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_PARTNER_LIFETIME         = 123, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_PARTNER_LIFETIME_SENT    = 124, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_PARTNER_DOWN_TIME        = 125, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_PARTNER_RAW_CLT_TIME     = 126, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_PROTOCOL_VERSION         = 127, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_KEEPALIVE_TIME           = 128, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_RECONFIGURE_DATA         = 129, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_RELATIONSHIP_NAME        = 130, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_SERVER_FLAGS             = 131, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_SERVER_STATE             = 132, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_START_TIME_OF_STATE      = 133, /* RFC 8156 */
+        SD_DHCP6_OPTION_F_STATE_EXPIRATION_TIME    = 134, /* RFC 8156 */
+        SD_DHCP6_OPTION_RELAY_PORT                 = 135, /* RFC 8357 */
+        SD_DHCP6_OPTION_V6_SZTP_REDIRECT           = 136, /* RFC 8572 */
+        SD_DHCP6_OPTION_S46_BIND_IPV6_PREFIX       = 137, /* RFC 8539 */
+        SD_DHCP6_OPTION_IA_LL                      = 138, /* RFC 8947 */
+        SD_DHCP6_OPTION_LLADDR                     = 139, /* RFC 8947 */
+        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 */
 };
 
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 a10901e5..472276de 100644
--- a/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h
+++ b/src/libnm-systemd-core/src/systemd/sd-dhcp6-lease.h
@@ -21,6 +21,7 @@
 
 #include <inttypes.h>
 #include <netinet/in.h>
+#include <sys/types.h>
 
 #include "_sd-common.h"
 
@@ -28,6 +29,9 @@ _SD_BEGIN_DECLARATIONS;
 
 typedef struct sd_dhcp6_lease sd_dhcp6_lease;
 
+int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_t *ret);
+int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret);
+
 void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease);
 int sd_dhcp6_lease_get_address(sd_dhcp6_lease *lease,
                                struct in6_addr *addr,
diff --git a/src/libnm-systemd-core/src/systemd/sd-event.h b/src/libnm-systemd-core/src/systemd/sd-event.h
index 2ae2a0da..63984eef 100644
--- a/src/libnm-systemd-core/src/systemd/sd-event.h
+++ b/src/libnm-systemd-core/src/systemd/sd-event.h
@@ -93,6 +93,7 @@ int sd_event_add_signal(sd_event *e, sd_event_source **s, int sig, sd_event_sign
 int sd_event_add_child(sd_event *e, sd_event_source **s, pid_t pid, int options, sd_event_child_handler_t callback, void *userdata);
 int sd_event_add_child_pidfd(sd_event *e, sd_event_source **s, int pidfd, int options, sd_event_child_handler_t callback, void *userdata);
 int sd_event_add_inotify(sd_event *e, sd_event_source **s, const char *path, uint32_t mask, sd_event_inotify_handler_t callback, void *userdata);
+int sd_event_add_inotify_fd(sd_event *e, sd_event_source **s, int fd, uint32_t mask, sd_event_inotify_handler_t callback, void *userdata);
 int sd_event_add_defer(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
 int sd_event_add_post(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
 int sd_event_add_exit(sd_event *e, sd_event_source **s, sd_event_handler_t callback, void *userdata);
@@ -165,6 +166,7 @@ int sd_event_source_set_exit_on_failure(sd_event_source *s, int b);
 int sd_event_source_set_ratelimit(sd_event_source *s, uint64_t interval_usec, unsigned burst);
 int sd_event_source_get_ratelimit(sd_event_source *s, uint64_t *ret_interval_usec, unsigned *ret_burst);
 int sd_event_source_is_ratelimited(sd_event_source *s);
+int sd_event_source_set_ratelimit_expire_callback(sd_event_source *s, sd_event_handler_t callback);
 
 /* Define helpers so that __attribute__((cleanup(sd_event_unrefp))) and similar may be used. */
 _SD_DEFINE_POINTER_CLEANUP_FUNC(sd_event, sd_event_unref);
diff --git a/src/libnm-systemd-core/src/systemd/sd-ipv4acd.h b/src/libnm-systemd-core/src/systemd/sd-ipv4acd.h
deleted file mode 100644
index 90d3f0a0..00000000
--- a/src/libnm-systemd-core/src/systemd/sd-ipv4acd.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#ifndef foosdipv4acdfoo
-#define foosdipv4acdfoo
-
-/***
-  Copyright © 2014 Axis Communications AB. All rights reserved.
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <net/ethernet.h>
-#include <netinet/in.h>
-#include <stdbool.h>
-
-#include "sd-event.h"
-
-#include "_sd-common.h"
-
-_SD_BEGIN_DECLARATIONS;
-
-enum {
-        SD_IPV4ACD_EVENT_STOP           = 0,
-        SD_IPV4ACD_EVENT_BIND           = 1,
-        SD_IPV4ACD_EVENT_CONFLICT       = 2,
-};
-
-typedef struct sd_ipv4acd sd_ipv4acd;
-typedef void (*sd_ipv4acd_callback_t)(sd_ipv4acd *acd, int event, void *userdata);
-typedef int (*sd_ipv4acd_check_mac_callback_t)(sd_ipv4acd *acd, const struct ether_addr *mac, void *userdata);
-
-int sd_ipv4acd_detach_event(sd_ipv4acd *acd);
-int sd_ipv4acd_attach_event(sd_ipv4acd *acd, sd_event *event, int64_t priority);
-int sd_ipv4acd_get_address(sd_ipv4acd *acd, struct in_addr *address);
-int sd_ipv4acd_set_callback(sd_ipv4acd *acd, sd_ipv4acd_callback_t cb, void *userdata);
-int sd_ipv4acd_set_check_mac_callback(sd_ipv4acd *acd, sd_ipv4acd_check_mac_callback_t cb, void *userdata);
-int sd_ipv4acd_set_mac(sd_ipv4acd *acd, const struct ether_addr *addr);
-int sd_ipv4acd_set_ifindex(sd_ipv4acd *acd, int interface_index);
-int sd_ipv4acd_get_ifindex(sd_ipv4acd *acd);
-int sd_ipv4acd_set_ifname(sd_ipv4acd *acd, const char *interface_name);
-int sd_ipv4acd_get_ifname(sd_ipv4acd *acd, const char **ret);
-int sd_ipv4acd_set_address(sd_ipv4acd *acd, const struct in_addr *address);
-int sd_ipv4acd_is_running(sd_ipv4acd *acd);
-int sd_ipv4acd_start(sd_ipv4acd *acd, bool reset_conflicts);
-int sd_ipv4acd_stop(sd_ipv4acd *acd);
-sd_ipv4acd *sd_ipv4acd_ref(sd_ipv4acd *acd);
-sd_ipv4acd *sd_ipv4acd_unref(sd_ipv4acd *acd);
-int sd_ipv4acd_new(sd_ipv4acd **ret);
-
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ipv4acd, sd_ipv4acd_unref);
-
-_SD_END_DECLARATIONS;
-
-#endif
diff --git a/src/libnm-systemd-core/src/systemd/sd-ipv4ll.h b/src/libnm-systemd-core/src/systemd/sd-ipv4ll.h
deleted file mode 100644
index ed014b53..00000000
--- a/src/libnm-systemd-core/src/systemd/sd-ipv4ll.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/* SPDX-License-Identifier: LGPL-2.1-or-later */
-#ifndef foosdipv4llfoo
-#define foosdipv4llfoo
-
-/***
-  Copyright © 2014 Axis Communications AB. All rights reserved.
-
-  systemd is free software; you can redistribute it and/or modify it
-  under the terms of the GNU Lesser General Public License as published by
-  the Free Software Foundation; either version 2.1 of the License, or
-  (at your option) any later version.
-
-  systemd is distributed in the hope that it will be useful, but
-  WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-  Lesser General Public License for more details.
-
-  You should have received a copy of the GNU Lesser General Public License
-  along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include <net/ethernet.h>
-#include <netinet/in.h>
-
-#include "sd-event.h"
-
-#include "_sd-common.h"
-
-_SD_BEGIN_DECLARATIONS;
-
-enum {
-        SD_IPV4LL_EVENT_STOP            = 0,
-        SD_IPV4LL_EVENT_BIND            = 1,
-        SD_IPV4LL_EVENT_CONFLICT        = 2,
-};
-
-typedef struct sd_ipv4ll sd_ipv4ll;
-typedef void (*sd_ipv4ll_callback_t)(sd_ipv4ll *ll, int event, void *userdata);
-typedef int (*sd_ipv4ll_check_mac_callback_t)(sd_ipv4ll *ll, const struct ether_addr *mac, void *userdata);
-
-int sd_ipv4ll_detach_event(sd_ipv4ll *ll);
-int sd_ipv4ll_attach_event(sd_ipv4ll *ll, sd_event *event, int64_t priority);
-int sd_ipv4ll_get_address(sd_ipv4ll *ll, struct in_addr *address);
-int sd_ipv4ll_set_callback(sd_ipv4ll *ll, sd_ipv4ll_callback_t cb, void *userdata);
-int sd_ipv4ll_set_check_mac_callback(sd_ipv4ll *ll, sd_ipv4ll_check_mac_callback_t cb, void *userdata);
-int sd_ipv4ll_set_mac(sd_ipv4ll *ll, const struct ether_addr *addr);
-int sd_ipv4ll_set_ifindex(sd_ipv4ll *ll, int interface_index);
-int sd_ipv4ll_get_ifindex(sd_ipv4ll *ll);
-int sd_ipv4ll_set_ifname(sd_ipv4ll *ll, const char *interface_name);
-int sd_ipv4ll_get_ifname(sd_ipv4ll *ll, const char **ret);
-int sd_ipv4ll_set_address(sd_ipv4ll *ll, const struct in_addr *address);
-int sd_ipv4ll_set_address_seed(sd_ipv4ll *ll, uint64_t seed);
-int sd_ipv4ll_is_running(sd_ipv4ll *ll);
-int sd_ipv4ll_restart(sd_ipv4ll *ll);
-int sd_ipv4ll_start(sd_ipv4ll *ll);
-int sd_ipv4ll_stop(sd_ipv4ll *ll);
-sd_ipv4ll *sd_ipv4ll_ref(sd_ipv4ll *ll);
-sd_ipv4ll *sd_ipv4ll_unref(sd_ipv4ll *ll);
-int sd_ipv4ll_new(sd_ipv4ll **ret);
-
-_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_ipv4ll, sd_ipv4ll_unref);
-
-_SD_END_DECLARATIONS;
-
-#endif
diff --git a/src/libnm-systemd-core/src/systemd/sd-ndisc.h b/src/libnm-systemd-core/src/systemd/sd-ndisc.h
index f45b2ad6..ab9ff55d 100644
--- a/src/libnm-systemd-core/src/systemd/sd-ndisc.h
+++ b/src/libnm-systemd-core/src/systemd/sd-ndisc.h
@@ -82,9 +82,6 @@ int sd_ndisc_set_ifname(sd_ndisc *nd, const char *interface_name);
 int sd_ndisc_get_ifname(sd_ndisc *nd, const char **ret);
 int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr);
 
-int sd_ndisc_get_mtu(sd_ndisc *nd, uint32_t *ret);
-int sd_ndisc_get_hop_limit(sd_ndisc *nd, uint8_t *ret);
-
 int sd_ndisc_router_from_raw(sd_ndisc_router **ret, const void *raw, size_t raw_size);
 sd_ndisc_router *sd_ndisc_router_ref(sd_ndisc_router *rt);
 sd_ndisc_router *sd_ndisc_router_unref(sd_ndisc_router *rt);