about summary refs log tree commit diff
path: root/src/systemd
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2020-06-28 18:58:14 +0200
committerMichael Biebl <biebl@debian.org>2020-06-28 18:58:14 +0200
commita54ac63bbf9b2c71026ac9028a8ffaf186cf3c82 (patch)
tree6a32883bd916c4096357b35298beff6db8bcd0b5 /src/systemd
parent45e8e1149027529194982212c804c0468aa01d98 (diff)
New upstream version 1.25.90 upstream/1.25.90
Diffstat (limited to 'src/systemd')
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-identifier.c1
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-identifier.h2
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-internal.h6
-rw-r--r--src/systemd/src/libsystemd-network/dhcp-lease-internal.h19
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-internal.h17
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-option.c167
-rw-r--r--src/systemd/src/libsystemd-network/dhcp6-protocol.h35
-rw-r--r--src/systemd/src/libsystemd-network/lldp-neighbor.c25
-rw-r--r--src/systemd/src/libsystemd-network/lldp-neighbor.h1
-rw-r--r--src/systemd/src/libsystemd-network/network-internal.c72
-rw-r--r--src/systemd/src/libsystemd-network/network-internal.h22
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp-client.c264
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp-lease.c169
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp6-client.c279
-rw-r--r--src/systemd/src/libsystemd-network/sd-dhcp6-lease.c4
-rw-r--r--src/systemd/src/libsystemd-network/sd-ipv4ll.c6
-rw-r--r--src/systemd/src/libsystemd-network/sd-lldp.c2
-rw-r--r--src/systemd/src/systemd/sd-dhcp-client.h6
-rw-r--r--src/systemd/src/systemd/sd-dhcp-lease.h17
-rw-r--r--src/systemd/src/systemd/sd-dhcp6-client.h22
-rw-r--r--src/systemd/src/systemd/sd-dhcp6-lease.h5
-rw-r--r--src/systemd/src/systemd/sd-dhcp6-option.h37
-rw-r--r--src/systemd/src/systemd/sd-lldp.h4
-rw-r--r--src/systemd/src/systemd/sd-ndisc.h2
24 files changed, 870 insertions, 314 deletions
diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.c b/src/systemd/src/libsystemd-network/dhcp-identifier.c
index b28e0ba0..026015a7 100644
--- a/src/systemd/src/libsystemd-network/dhcp-identifier.c
+++ b/src/systemd/src/libsystemd-network/dhcp-identifier.c
@@ -17,7 +17,6 @@
 #include "udev-util.h"
 #include "virt.h"
 
-#define SYSTEMD_PEN    43793
 #define HASH_KEY       SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
 #define APPLICATION_ID SD_ID128_MAKE(a5,0a,d1,12,bf,60,45,77,a2,fb,74,1a,b1,95,5b,03)
 #define USEC_2000       ((usec_t) 946684800000000) /* 2000-01-01 00:00:00 UTC */
diff --git a/src/systemd/src/libsystemd-network/dhcp-identifier.h b/src/systemd/src/libsystemd-network/dhcp-identifier.h
index b3115125..76abd658 100644
--- a/src/systemd/src/libsystemd-network/dhcp-identifier.h
+++ b/src/systemd/src/libsystemd-network/dhcp-identifier.h
@@ -8,6 +8,8 @@
 #include "time-util.h"
 #include "unaligned.h"
 
+#define SYSTEMD_PEN    43793
+
 typedef enum DUIDType {
         DUID_TYPE_LLT       = 1,
         DUID_TYPE_EN        = 2,
diff --git a/src/systemd/src/libsystemd-network/dhcp-internal.h b/src/systemd/src/libsystemd-network/dhcp-internal.h
index 6a803d7b..7e814948 100644
--- a/src/systemd/src/libsystemd-network/dhcp-internal.h
+++ b/src/systemd/src/libsystemd-network/dhcp-internal.h
@@ -7,7 +7,6 @@
 
 #include <linux/if_packet.h>
 #include <net/ethernet.h>
-#include <net/if_arp.h>
 #include <stdint.h>
 
 #include "sd-dhcp-client.h"
@@ -23,6 +22,11 @@ typedef struct sd_dhcp_option {
         size_t length;
 } sd_dhcp_option;
 
+typedef struct DHCPServerData {
+        struct in_addr *addr;
+        size_t size;
+} DHCPServerData;
+
 extern const struct hash_ops dhcp_option_hash_ops;
 
 int dhcp_network_bind_raw_socket(int ifindex, union sockaddr_union *link,
diff --git a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h
index 5cbebb4a..aed30d61 100644
--- a/src/systemd/src/libsystemd-network/dhcp-lease-internal.h
+++ b/src/systemd/src/libsystemd-network/dhcp-lease-internal.h
@@ -5,11 +5,9 @@
   Copyright © 2013 Intel Corporation. All rights reserved.
 ***/
 
-#include <stdint.h>
-#include <linux/if_packet.h>
-
 #include "sd-dhcp-client.h"
 
+#include "dhcp-internal.h"
 #include "dhcp-protocol.h"
 #include "list.h"
 #include "util.h"
@@ -52,20 +50,7 @@ struct sd_dhcp_lease {
         struct in_addr *router;
         size_t router_size;
 
-        struct in_addr *dns;
-        size_t dns_size;
-
-        struct in_addr *ntp;
-        size_t ntp_size;
-
-        struct in_addr *sip;
-        size_t sip_size;
-
-        struct in_addr *pop3_server;
-        size_t pop3_server_size;
-
-        struct in_addr *smtp_server;
-        size_t smtp_server_size;
+        DHCPServerData servers[_SD_DHCP_LEASE_SERVER_TYPE_MAX];
 
         struct sd_dhcp_route *static_route;
         size_t static_route_size, static_route_allocated;
diff --git a/src/systemd/src/libsystemd-network/dhcp6-internal.h b/src/systemd/src/libsystemd-network/dhcp6-internal.h
index 517e357d..b0d1216e 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-internal.h
+++ b/src/systemd/src/libsystemd-network/dhcp6-internal.h
@@ -11,9 +11,21 @@
 #include "sd-event.h"
 
 #include "list.h"
+#include "hashmap.h"
 #include "macro.h"
 #include "sparse-endian.h"
 
+typedef struct sd_dhcp6_option {
+        unsigned n_ref;
+
+        uint32_t enterprise_identifier;
+        uint16_t option;
+        void *data;
+        size_t length;
+} sd_dhcp6_option;
+
+extern const struct hash_ops dhcp6_option_hash_ops;
+
 /* Common option header */
 typedef struct DHCP6Option {
         be16_t code;
@@ -87,10 +99,13 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
 int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia);
 int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix);
 int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
+int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char **user_class);
+int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **user_class);
+int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options);
 int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
                        size_t *optlen, uint8_t **optvalue);
 int dhcp6_option_parse_status(DHCP6Option *option, size_t len);
-int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia);
+int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code);
 int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
                                 struct in6_addr **addrs, size_t count,
                                 size_t *allocated);
diff --git a/src/systemd/src/libsystemd-network/dhcp6-option.c b/src/systemd/src/libsystemd-network/dhcp6-option.c
index bb4c4d91..d596752b 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-option.c
+++ b/src/systemd/src/libsystemd-network/dhcp6-option.c
@@ -11,6 +11,7 @@
 #include "sd-dhcp6-client.h"
 
 #include "alloc-util.h"
+#include "dhcp-identifier.h"
 #include "dhcp6-internal.h"
 #include "dhcp6-lease-internal.h"
 #include "dhcp6-protocol.h"
@@ -80,6 +81,39 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
         return 0;
 }
 
+int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHashmap *vendor_options) {
+        sd_dhcp6_option *options;
+        Iterator i;
+        int r;
+
+        assert(buf);
+        assert(*buf);
+        assert(buflen);
+        assert(vendor_options);
+
+        ORDERED_HASHMAP_FOREACH(options, vendor_options, i) {
+                _cleanup_free_ uint8_t *p = NULL;
+                size_t total;
+
+                total = 4 + 2 + 2 + options->length;
+
+                p = malloc(total);
+                if (!p)
+                        return -ENOMEM;
+
+                unaligned_write_be32(p, options->enterprise_identifier);
+                unaligned_write_be16(p + 4, options->option);
+                unaligned_write_be16(p + 6, options->length);
+                memcpy(p + 8, options->data, options->length);
+
+                r = dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_OPTS, total, p);
+                if (r < 0)
+                        return r;
+        }
+
+        return 0;
+}
+
 int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
         uint16_t len;
         uint8_t *ia_hdr;
@@ -169,6 +203,75 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
         return r;
 }
 
+int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char **user_class) {
+        _cleanup_free_ uint8_t *p = NULL;
+        size_t total = 0, offset = 0;
+        char **s;
+
+        assert_return(buf && *buf && buflen && user_class, -EINVAL);
+
+        STRV_FOREACH(s, user_class) {
+                size_t len = strlen(*s);
+                uint8_t *q;
+
+                if (len > 0xffff)
+                        return -ENAMETOOLONG;
+                q = realloc(p, total + len + 2);
+                if (!q)
+                        return -ENOMEM;
+
+                p = q;
+
+                unaligned_write_be16(&p[offset], len);
+                memcpy(&p[offset + 2], *s, len);
+
+                offset += 2 + len;
+                total += 2 + len;
+        }
+
+        return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_USER_CLASS, total, p);
+}
+
+int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char **vendor_class) {
+        _cleanup_free_ uint8_t *p = NULL;
+        uint32_t enterprise_identifier;
+        size_t total, offset;
+        char **s;
+
+        assert(buf);
+        assert(*buf);
+        assert(buflen);
+        assert(vendor_class);
+
+        enterprise_identifier = htobe32(SYSTEMD_PEN);
+
+        p = memdup(&enterprise_identifier, sizeof(enterprise_identifier));
+        if (!p)
+                return -ENOMEM;
+
+        total = sizeof(enterprise_identifier);
+        offset = total;
+
+        STRV_FOREACH(s, vendor_class) {
+                size_t len = strlen(*s);
+                uint8_t *q;
+
+                q = realloc(p, total + len + 2);
+                if (!q)
+                        return -ENOMEM;
+
+                p = q;
+
+                unaligned_write_be16(&p[offset], len);
+                memcpy(&p[offset + 2], *s, len);
+
+                offset += 2 + len;
+                total += 2 + len;
+        }
+
+        return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_CLASS, total, p);
+}
+
 int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix) {
         DHCP6Option *option = (DHCP6Option *)buf;
         size_t i = sizeof(*option) + sizeof(pd->ia_pd);
@@ -349,13 +452,13 @@ static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia,
         return 0;
 }
 
-int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
+int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code) {
+        uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX;
         uint16_t iatype, optlen;
-        size_t i, len;
+        size_t iaaddr_offset;
         int r = 0, status;
+        size_t i, len;
         uint16_t opt;
-        size_t iaaddr_offset;
-        uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX;
 
         assert_return(ia, -EINVAL);
         assert_return(!ia->addresses, -EINVAL);
@@ -465,11 +568,15 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
                         status = dhcp6_option_parse_status(option, optlen + offsetof(DHCP6Option, data));
                         if (status < 0)
                                 return status;
+
                         if (status > 0) {
-                                log_dhcp6_client(client, "IA status %d",
-                                                 status);
+                                if (ret_status_code)
+                                        *ret_status_code = status;
 
-                                return -EINVAL;
+                                log_dhcp6_client(client, "IA status %s",
+                                                 dhcp6_message_status_to_string(status));
+
+                                return 0;
                         }
 
                         break;
@@ -513,7 +620,10 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) {
                 break;
         }
 
-        return 0;
+        if (ret_status_code)
+                *ret_status_code = 0;
+
+        return 1;
 }
 
 int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
@@ -599,3 +709,44 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char *
 
         return idx;
 }
+
+static sd_dhcp6_option* dhcp6_option_free(sd_dhcp6_option *i) {
+        if (!i)
+                return NULL;
+
+        free(i->data);
+        return mfree(i);
+}
+
+int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, uint32_t enterprise_identifier, sd_dhcp6_option **ret) {
+        assert_return(ret, -EINVAL);
+        assert_return(length == 0 || data, -EINVAL);
+
+        _cleanup_free_ void *q = memdup(data, length);
+        if (!q)
+                return -ENOMEM;
+
+        sd_dhcp6_option *p = new(sd_dhcp6_option, 1);
+        if (!p)
+                return -ENOMEM;
+
+        *p = (sd_dhcp6_option) {
+                .n_ref = 1,
+                .option = option,
+                .enterprise_identifier = enterprise_identifier,
+                .length = length,
+                .data = TAKE_PTR(q),
+        };
+
+        *ret = p;
+        return 0;
+}
+
+DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_dhcp6_option, sd_dhcp6_option, dhcp6_option_free);
+DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
+                dhcp6_option_hash_ops,
+                void,
+                trivial_hash_func,
+                trivial_compare_func,
+                sd_dhcp6_option,
+                sd_dhcp6_option_unref);
diff --git a/src/systemd/src/libsystemd-network/dhcp6-protocol.h b/src/systemd/src/libsystemd-network/dhcp6-protocol.h
index ffae4453..f7a27028 100644
--- a/src/systemd/src/libsystemd-network/dhcp6-protocol.h
+++ b/src/systemd/src/libsystemd-network/dhcp6-protocol.h
@@ -82,14 +82,35 @@ enum {
         DHCP6_NTP_SUBOPTION_SRV_FQDN            = 3,
 };
 
+/*
+ * RFC 8415, RFC 5007 and RFC 7653 status codes:
+ * https://www.iana.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xhtml#dhcpv6-parameters-5
+ */
 enum {
-        DHCP6_STATUS_SUCCESS                    = 0,
-        DHCP6_STATUS_UNSPEC_FAIL                = 1,
-        DHCP6_STATUS_NO_ADDRS_AVAIL             = 2,
-        DHCP6_STATUS_NO_BINDING                 = 3,
-        DHCP6_STATUS_NOT_ON_LINK                = 4,
-        DHCP6_STATUS_USE_MULTICAST              = 5,
-        _DHCP6_STATUS_MAX                       = 6,
+        DHCP6_STATUS_SUCCESS                      = 0,
+        DHCP6_STATUS_UNSPEC_FAIL                  = 1,
+        DHCP6_STATUS_NO_ADDRS_AVAIL               = 2,
+        DHCP6_STATUS_NO_BINDING                   = 3,
+        DHCP6_STATUS_NOT_ON_LINK                  = 4,
+        DHCP6_STATUS_USE_MULTICAST                = 5,
+        DHCP6_STATUS_NO_PREFIX_AVAIL              = 6,
+        DHCP6_STATUS_UNKNOWN_QUERY_TYPE           = 7,
+        DHCP6_STATUS_MALFORMED_QUERY              = 8,
+        DHCP6_STATUS_NOT_CONFIGURED               = 9,
+        DHCP6_STATUS_NOT_ALLOWED                  = 10,
+        DHCP6_STATUS_QUERY_TERMINATED             = 11,
+        DHCP6_STATUS_DATA_MISSING                 = 12,
+        DHCP6_STATUS_CATCHUP_COMPLETE             = 13,
+        DHCP6_STATUS_NOT_SUPPORTED                = 14,
+        DHCP6_STATUS_TLS_CONNECTION_REFUSED       = 15,
+        DHCP6_STATUS_ADDRESS_IN_USE               = 16,
+        DHCP6_STATUS_CONFIGURATION_CONFLICT       = 17,
+        DHCP6_STATUS_MISSING_BINDING_INFORMATION  = 18,
+        DHCP6_STATUS_OUTDATED_BINDING_INFORMATION = 19,
+        DHCP6_STATUS_SERVER_SHUTTING_DOWN         = 20,
+        DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED     = 21,
+        DHCP6_STATUS_EXCESSIVE_TIME_SKEW          = 22,
+        _DHCP6_STATUS_MAX                         = 23,
 };
 
 enum {
diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.c b/src/systemd/src/libsystemd-network/lldp-neighbor.c
index 238d718b..9baa8e8c 100644
--- a/src/systemd/src/libsystemd-network/lldp-neighbor.c
+++ b/src/systemd/src/libsystemd-network/lldp-neighbor.c
@@ -52,6 +52,7 @@ static void lldp_neighbor_free(sd_lldp_neighbor *n) {
         free(n->port_description);
         free(n->system_name);
         free(n->system_description);
+        free(n->mud_url);
         free(n->chassis_id_as_string);
         free(n->port_id_as_string);
         free(n);
@@ -294,9 +295,20 @@ int lldp_neighbor_parse(sd_lldp_neighbor *n) {
 
                         break;
 
-                case SD_LLDP_TYPE_PRIVATE:
+                case SD_LLDP_TYPE_PRIVATE: {
                         if (length < 4)
                                 log_lldp("Found private TLV that is too short, ignoring.");
+                        else {
+                                /* RFC 8520: MUD URL */
+                                if (memcmp(p, SD_LLDP_OUI_MUD, sizeof(SD_LLDP_OUI_MUD)) == 0 &&
+                                    p[sizeof(SD_LLDP_OUI_MUD)] == SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION) {
+                                        r = parse_string(&n->mud_url, p + sizeof(SD_LLDP_OUI_MUD) + 1,
+                                                         length - 1 - sizeof(SD_LLDP_OUI_MUD));
+                                        if (r < 0)
+                                                return r;
+                                }
+                        }
+                }
 
                         break;
                 }
@@ -595,6 +607,17 @@ _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) {
+        assert_return(n, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        if (!n->mud_url)
+                return -ENODATA;
+
+        *ret = n->mud_url;
+        return 0;
+}
+
 _public_ int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret) {
         assert_return(n, -EINVAL);
         assert_return(ret, -EINVAL);
diff --git a/src/systemd/src/libsystemd-network/lldp-neighbor.h b/src/systemd/src/libsystemd-network/lldp-neighbor.h
index 62dbff42..74175edf 100644
--- a/src/systemd/src/libsystemd-network/lldp-neighbor.h
+++ b/src/systemd/src/libsystemd-network/lldp-neighbor.h
@@ -54,6 +54,7 @@ struct sd_lldp_neighbor {
         char *port_description;
         char *system_name;
         char *system_description;
+        char *mud_url;
 
         uint16_t port_vlan_id;
 
diff --git a/src/systemd/src/libsystemd-network/network-internal.c b/src/systemd/src/libsystemd-network/network-internal.c
index 94a6236a..6a7382ac 100644
--- a/src/systemd/src/libsystemd-network/network-internal.c
+++ b/src/systemd/src/libsystemd-network/network-internal.c
@@ -195,30 +195,34 @@ bool net_match_config(Set *match_mac,
                       Set *match_permanent_mac,
                       char * const *match_paths,
                       char * const *match_drivers,
-                      char * const *match_types,
+                      char * const *match_iftypes,
                       char * const *match_names,
                       char * const *match_property,
                       char * const *match_wifi_iftype,
                       char * const *match_ssid,
                       Set *match_bssid,
-                      unsigned short iftype,
                       sd_device *device,
                       const struct ether_addr *dev_mac,
                       const struct ether_addr *dev_permanent_mac,
+                      const char *dev_driver,
+                      unsigned short dev_iftype,
                       const char *dev_name,
                       char * const *alternative_names,
-                      enum nl80211_iftype wifi_iftype,
-                      const char *ssid,
-                      const struct ether_addr *bssid) {
+                      enum nl80211_iftype dev_wifi_iftype,
+                      const char *dev_ssid,
+                      const struct ether_addr *dev_bssid) {
 
-        const char *dev_path = NULL, *dev_driver = NULL, *mac_str;
-        _cleanup_free_ char *dev_type;
+        _cleanup_free_ char *dev_iftype_str;
+        const char *dev_path = NULL;
 
-        dev_type = link_get_type_string(iftype, device);
+        dev_iftype_str = link_get_type_string(dev_iftype, device);
 
         if (device) {
+                const char *mac_str;
+
                 (void) sd_device_get_property_value(device, "ID_PATH", &dev_path);
-                (void) sd_device_get_property_value(device, "ID_NET_DRIVER", &dev_driver);
+                if (!dev_driver)
+                        (void) sd_device_get_property_value(device, "ID_NET_DRIVER", &dev_driver);
                 if (!dev_name)
                         (void) sd_device_get_sysname(device, &dev_name);
                 if (!dev_mac &&
@@ -241,7 +245,7 @@ bool net_match_config(Set *match_mac,
         if (!net_condition_test_strv(match_drivers, dev_driver))
                 return false;
 
-        if (!net_condition_test_strv(match_types, dev_type))
+        if (!net_condition_test_strv(match_iftypes, dev_iftype_str))
                 return false;
 
         if (!net_condition_test_ifname(match_names, dev_name, alternative_names))
@@ -250,13 +254,13 @@ bool net_match_config(Set *match_mac,
         if (!net_condition_test_property(match_property, device))
                 return false;
 
-        if (!net_condition_test_strv(match_wifi_iftype, wifi_iftype_to_string(wifi_iftype)))
+        if (!net_condition_test_strv(match_wifi_iftype, wifi_iftype_to_string(dev_wifi_iftype)))
                 return false;
 
-        if (!net_condition_test_strv(match_ssid, ssid))
+        if (!net_condition_test_strv(match_ssid, dev_ssid))
                 return false;
 
-        if (match_bssid && (!bssid || !set_contains(match_bssid, bssid)))
+        if (match_bssid && (!dev_bssid || !set_contains(match_bssid, dev_bssid)))
                 return false;
 
         return true;
@@ -660,27 +664,27 @@ int config_parse_bridge_port_priority(
 size_t serialize_in_addrs(FILE *f,
                           const struct in_addr *addresses,
                           size_t size,
-                          bool with_leading_space,
+                          bool *with_leading_space,
                           bool (*predicate)(const struct in_addr *addr)) {
-        size_t count;
-        size_t i;
-
         assert(f);
         assert(addresses);
 
-        count = 0;
+        size_t count = 0;
+        bool _space = false;
+        if (!with_leading_space)
+                with_leading_space = &_space;
 
-        for (i = 0; i < size; i++) {
+        for (size_t i = 0; i < size; i++) {
                 char sbuf[INET_ADDRSTRLEN];
 
                 if (predicate && !predicate(&addresses[i]))
                         continue;
-                if (with_leading_space)
+
+                if (*with_leading_space)
                         fputc(' ', f);
-                else
-                        with_leading_space = true;
                 fputs(inet_ntop(AF_INET, &addresses[i], sbuf, sizeof(sbuf)), f);
                 count++;
+                *with_leading_space = true;
         }
 
         return count;
@@ -722,20 +726,22 @@ int deserialize_in_addrs(struct in_addr **ret, const char *string) {
         return size;
 }
 
-void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, size_t size) {
-        unsigned i;
-
+void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, size_t size, bool *with_leading_space) {
         assert(f);
         assert(addresses);
         assert(size);
 
-        for (i = 0; i < size; i++) {
-                char buffer[INET6_ADDRSTRLEN];
+        bool _space = false;
+        if (!with_leading_space)
+                with_leading_space = &_space;
 
-                fputs(inet_ntop(AF_INET6, addresses+i, buffer, sizeof(buffer)), f);
+        for (size_t i = 0; i < size; i++) {
+                char buffer[INET6_ADDRSTRLEN];
 
-                if (i < size - 1)
+                if (*with_leading_space)
                         fputc(' ', f);
+                fputs(inet_ntop(AF_INET6, addresses+i, buffer, sizeof(buffer)), f);
+                *with_leading_space = true;
         }
 }
 
@@ -776,8 +782,6 @@ int deserialize_in6_addrs(struct in6_addr **ret, const char *string) {
 }
 
 void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, size_t size) {
-        unsigned i;
-
         assert(f);
         assert(key);
         assert(routes);
@@ -785,7 +789,7 @@ void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, siz
 
         fprintf(f, "%s=", key);
 
-        for (i = 0; i < size; i++) {
+        for (size_t i = 0; i < size; i++) {
                 char sbuf[INET_ADDRSTRLEN];
                 struct in_addr dest, gw;
                 uint8_t length;
@@ -794,8 +798,8 @@ void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, siz
                 assert_se(sd_dhcp_route_get_gateway(routes[i], &gw) >= 0);
                 assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &length) >= 0);
 
-                fprintf(f, "%s/%" PRIu8, inet_ntop(AF_INET, &dest, sbuf, sizeof(sbuf)), length);
-                fprintf(f, ",%s%s", inet_ntop(AF_INET, &gw, sbuf, sizeof(sbuf)), (i < (size - 1)) ? " ": "");
+                fprintf(f, "%s/%" PRIu8, inet_ntop(AF_INET, &dest, sbuf, sizeof sbuf), length);
+                fprintf(f, ",%s%s", inet_ntop(AF_INET, &gw, sbuf, sizeof sbuf), i < size - 1 ? " ": "");
         }
 
         fputs("\n", f);
diff --git a/src/systemd/src/libsystemd-network/network-internal.h b/src/systemd/src/libsystemd-network/network-internal.h
index 6724cbbf..8e6377e0 100644
--- a/src/systemd/src/libsystemd-network/network-internal.h
+++ b/src/systemd/src/libsystemd-network/network-internal.h
@@ -17,23 +17,24 @@
 #if 0 /* NM_IGNORED */
 bool net_match_config(Set *match_mac,
                       Set *match_permanent_mac,
-                      char * const *match_path,
-                      char * const *match_driver,
-                      char * const *match_type,
-                      char * const *match_name,
+                      char * const *match_paths,
+                      char * const *match_drivers,
+                      char * const *match_iftypes,
+                      char * const *match_names,
                       char * const *match_property,
                       char * const *match_wifi_iftype,
                       char * const *match_ssid,
                       Set *match_bssid,
-                      unsigned short iftype,
                       sd_device *device,
                       const struct ether_addr *dev_mac,
                       const struct ether_addr *dev_permanent_mac,
+                      const char *dev_driver,
+                      unsigned short dev_iftype,
                       const char *dev_name,
                       char * const *alternative_names,
-                      enum nl80211_iftype wifi_iftype,
-                      const char *ssid,
-                      const struct ether_addr *bssid);
+                      enum nl80211_iftype dev_wifi_iftype,
+                      const char *dev_ssid,
+                      const struct ether_addr *dev_bssid);
 
 CONFIG_PARSER_PROTOTYPE(config_parse_net_condition);
 CONFIG_PARSER_PROTOTYPE(config_parse_hwaddr);
@@ -51,11 +52,12 @@ const char *net_get_name_persistent(sd_device *device);
 size_t serialize_in_addrs(FILE *f,
                           const struct in_addr *addresses,
                           size_t size,
-                          bool with_leading_space,
+                          bool *with_leading_space,
                           bool (*predicate)(const struct in_addr *addr));
 int deserialize_in_addrs(struct in_addr **addresses, const char *string);
 void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses,
-                         size_t size);
+                         size_t size,
+                         bool *with_leading_space);
 int deserialize_in6_addrs(struct in6_addr **addresses, const char *string);
 
 /* don't include "dhcp-lease-internal.h" as it causes conflicts between netinet/ip.h and linux/ip.h */
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-client.c b/src/systemd/src/libsystemd-network/sd-dhcp-client.c
index 63321cca..70821ac2 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp-client.c
@@ -29,6 +29,7 @@
 #include "random-util.h"
 #include "string-util.h"
 #include "strv.h"
+#include "utf8.h"
 #include "web-util.h"
 
 #define MAX_CLIENT_ID_LEN (sizeof(uint32_t) + MAX_DUID_LEN)  /* Arbitrary limit */
@@ -37,6 +38,32 @@
 #define RESTART_AFTER_NAK_MIN_USEC (1 * USEC_PER_SEC)
 #define RESTART_AFTER_NAK_MAX_USEC (30 * USEC_PER_MINUTE)
 
+typedef struct sd_dhcp_client_id {
+        uint8_t type;
+        union {
+                struct {
+                        /* 0: Generic (non-LL) (RFC 2132) */
+                        uint8_t data[MAX_CLIENT_ID_LEN];
+                } _packed_ gen;
+                struct {
+                        /* 1: Ethernet Link-Layer (RFC 2132) */
+                        uint8_t haddr[ETH_ALEN];
+                } _packed_ eth;
+                struct {
+                        /* 2 - 254: ARP/Link-Layer (RFC 2132) */
+                        uint8_t haddr[0];
+                } _packed_ ll;
+                struct {
+                        /* 255: Node-specific (RFC 4361) */
+                        be32_t iaid;
+                        struct duid duid;
+                } _packed_ ns;
+                struct {
+                        uint8_t data[MAX_CLIENT_ID_LEN];
+                } _packed_ raw;
+        };
+} _packed_ sd_dhcp_client_id;
+
 struct sd_dhcp_client {
         unsigned n_ref;
 
@@ -58,37 +85,14 @@ struct sd_dhcp_client {
         uint8_t mac_addr[MAX_MAC_ADDR_LEN];
         size_t mac_addr_len;
         uint16_t arp_type;
-        struct {
-                uint8_t type;
-                union {
-                        struct {
-                                /* 0: Generic (non-LL) (RFC 2132) */
-                                uint8_t data[MAX_CLIENT_ID_LEN];
-                        } _packed_ gen;
-                        struct {
-                                /* 1: Ethernet Link-Layer (RFC 2132) */
-                                uint8_t haddr[ETH_ALEN];
-                        } _packed_ eth;
-                        struct {
-                                /* 2 - 254: ARP/Link-Layer (RFC 2132) */
-                                uint8_t haddr[0];
-                        } _packed_ ll;
-                        struct {
-                                /* 255: Node-specific (RFC 4361) */
-                                be32_t iaid;
-                                struct duid duid;
-                        } _packed_ ns;
-                        struct {
-                                uint8_t data[MAX_CLIENT_ID_LEN];
-                        } _packed_ raw;
-                };
-        } _packed_ client_id;
+        sd_dhcp_client_id client_id;
         size_t client_id_len;
         char *hostname;
         char *vendor_class_identifier;
         char *mudurl;
         char **user_class;
         uint32_t mtu;
+        uint32_t fallback_lease_lifetime;
         uint32_t xid;
         usec_t start_time;
         uint64_t attempt;
@@ -152,6 +156,60 @@ static int client_receive_message_udp(
                 void *userdata);
 static void client_stop(sd_dhcp_client *client, int error);
 
+int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret) {
+        const sd_dhcp_client_id *client_id = data;
+        _cleanup_free_ char *t = NULL;
+        int r = 0;
+
+        assert_return(data, -EINVAL);
+        assert_return(len >= 1, -EINVAL);
+        assert_return(ret, -EINVAL);
+
+        len -= 1;
+        if (len > MAX_CLIENT_ID_LEN)
+                return -EINVAL;
+
+        switch (client_id->type) {
+        case 0:
+                if (utf8_is_printable((char *) client_id->gen.data, len))
+                        r = asprintf(&t, "%.*s", (int) len, client_id->gen.data);
+                else
+                        r = asprintf(&t, "DATA");
+                break;
+        case 1:
+                if (len != sizeof_field(sd_dhcp_client_id, eth))
+                        return -EINVAL;
+
+                r = asprintf(&t, "%x:%x:%x:%x:%x:%x",
+                             client_id->eth.haddr[0],
+                             client_id->eth.haddr[1],
+                             client_id->eth.haddr[2],
+                             client_id->eth.haddr[3],
+                             client_id->eth.haddr[4],
+                             client_id->eth.haddr[5]);
+                break;
+        case 2 ... 254:
+                r = asprintf(&t, "ARP/LL");
+                break;
+        case 255:
+                if (len < 6)
+                        return -EINVAL;
+
+                uint32_t iaid = be32toh(client_id->ns.iaid);
+                uint16_t duid_type = be16toh(client_id->ns.duid.type);
+                if (dhcp_validate_duid_len(duid_type, len - 6, true) < 0)
+                        return -EINVAL;
+
+                r = asprintf(&t, "IAID:0x%x/DUID", iaid);
+                break;
+        }
+
+        if (r < 0)
+                return -ENOMEM;
+        *ret = TAKE_PTR(t);
+        return 0;
+}
+
 int sd_dhcp_client_set_callback(
                 sd_dhcp_client *client,
                 sd_dhcp_client_callback_t cb,
@@ -617,6 +675,15 @@ int sd_dhcp_client_set_service_type(sd_dhcp_client *client, int type) {
         return 0;
 }
 
+int sd_dhcp_client_set_fallback_lease_lifetime(sd_dhcp_client *client, uint32_t fallback_lease_lifetime) {
+        assert_return(client, -EINVAL);
+        assert_return(fallback_lease_lifetime > 0, -EINVAL);
+
+        client->fallback_lease_lifetime = fallback_lease_lifetime;
+
+        return 0;
+}
+
 static int client_notify(sd_dhcp_client *client, int event) {
         assert(client);
 
@@ -855,36 +922,12 @@ static int dhcp_client_send_raw(
                                             packet, len);
 }
 
-static int client_send_discover(sd_dhcp_client *client) {
-        _cleanup_free_ DHCPPacket *discover = NULL;
-        size_t optoffset, optlen;
+static int client_append_common_discover_request_options(sd_dhcp_client *client, DHCPPacket *packet, size_t *optoffset, size_t optlen) {
         sd_dhcp_option *j;
         Iterator i;
         int r;
 
         assert(client);
-        assert(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_SELECTING));
-
-        r = client_message_init(client, &discover, DHCP_DISCOVER,
-                                &optlen, &optoffset);
-        if (r < 0)
-                return r;
-
-        /* the client may suggest values for the network address
-           and lease time in the DHCPDISCOVER message. The client may include
-           the ’requested IP address’ option to suggest that a particular IP
-           address be assigned, and may include the ’IP address lease time’
-           option to suggest the lease time it would like.
-         */
-        /* RFC7844 section 3:
-           SHOULD NOT contain any other option. */
-        if (!client->anonymize && client->last_addr != INADDR_ANY) {
-                r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
-                                       SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
-                                       4, &client->last_addr);
-                if (r < 0)
-                        return r;
-        }
 
         if (client->hostname) {
                 /* According to RFC 4702 "clients that send the Client FQDN option in
@@ -895,18 +938,18 @@ static int client_send_discover(sd_dhcp_client *client) {
                         /* it is unclear from RFC 2131 if client should send hostname in
                            DHCPDISCOVER but dhclient does and so we do as well
                         */
-                        r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+                        r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
                                                SD_DHCP_OPTION_HOST_NAME,
                                                strlen(client->hostname), client->hostname);
                 } else
-                        r = client_append_fqdn_option(&discover->dhcp, optlen, &optoffset,
+                        r = client_append_fqdn_option(&packet->dhcp, optlen, optoffset,
                                                       client->hostname);
                 if (r < 0)
                         return r;
         }
 
         if (client->vendor_class_identifier) {
-                r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+                r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
                                        SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
                                        strlen(client->vendor_class_identifier),
                                        client->vendor_class_identifier);
@@ -915,7 +958,7 @@ static int client_send_discover(sd_dhcp_client *client) {
         }
 
         if (client->mudurl) {
-                r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+                r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
                                        SD_DHCP_OPTION_MUD_URL,
                                        strlen(client->mudurl),
                                        client->mudurl);
@@ -924,7 +967,7 @@ static int client_send_discover(sd_dhcp_client *client) {
         }
 
         if (client->user_class) {
-                r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+                r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
                                        SD_DHCP_OPTION_USER_CLASS,
                                        strv_length(client->user_class),
                                        client->user_class);
@@ -933,7 +976,7 @@ static int client_send_discover(sd_dhcp_client *client) {
         }
 
         ORDERED_HASHMAP_FOREACH(j, client->extra_options, i) {
-                r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+                r = dhcp_option_append(&packet->dhcp, optlen, optoffset, 0,
                                        j->option, j->length, j->data);
                 if (r < 0)
                         return r;
@@ -941,13 +984,50 @@ static int client_send_discover(sd_dhcp_client *client) {
 
         if (!ordered_hashmap_isempty(client->vendor_options)) {
                 r = dhcp_option_append(
-                                &discover->dhcp, optlen, &optoffset, 0,
+                                &packet->dhcp, optlen, optoffset, 0,
                                 SD_DHCP_OPTION_VENDOR_SPECIFIC,
                                 ordered_hashmap_size(client->vendor_options), client->vendor_options);
                 if (r < 0)
                         return r;
         }
 
+
+        return 0;
+}
+
+static int client_send_discover(sd_dhcp_client *client) {
+        _cleanup_free_ DHCPPacket *discover = NULL;
+        size_t optoffset, optlen;
+        int r;
+
+        assert(client);
+        assert(IN_SET(client->state, DHCP_STATE_INIT, DHCP_STATE_SELECTING));
+
+        r = client_message_init(client, &discover, DHCP_DISCOVER,
+                                &optlen, &optoffset);
+        if (r < 0)
+                return r;
+
+        /* the client may suggest values for the network address
+           and lease time in the DHCPDISCOVER message. The client may include
+           the ’requested IP address’ option to suggest that a particular IP
+           address be assigned, and may include the ’IP address lease time’
+           option to suggest the lease time it would like.
+         */
+        /* RFC7844 section 3:
+           SHOULD NOT contain any other option. */
+        if (!client->anonymize && client->last_addr != INADDR_ANY) {
+                r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+                                       SD_DHCP_OPTION_REQUESTED_IP_ADDRESS,
+                                       4, &client->last_addr);
+                if (r < 0)
+                        return r;
+        }
+
+        r = client_append_common_discover_request_options(client, discover, &optoffset, optlen);
+        if (r < 0)
+                return r;
+
         r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
                                SD_DHCP_OPTION_END, 0, NULL);
         if (r < 0)
@@ -1039,36 +1119,9 @@ static int client_send_request(sd_dhcp_client *client) {
                 return -EINVAL;
         }
 
-        if (client->hostname) {
-                if (dns_name_is_single_label(client->hostname))
-                        r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
-                                               SD_DHCP_OPTION_HOST_NAME,
-                                               strlen(client->hostname), client->hostname);
-                else
-                        r = client_append_fqdn_option(&request->dhcp, optlen, &optoffset,
-                                                      client->hostname);
-                if (r < 0)
-                        return r;
-        }
-
-        if (client->vendor_class_identifier) {
-                r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
-                                       SD_DHCP_OPTION_VENDOR_CLASS_IDENTIFIER,
-                                       strlen(client->vendor_class_identifier),
-                                       client->vendor_class_identifier);
-                if (r < 0)
-                        return r;
-        }
-
-        if (client->mudurl) {
-                r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
-                                       SD_DHCP_OPTION_MUD_URL,
-                                       strlen(client->mudurl),
-                                       client->mudurl);
-                if (r < 0)
-                        return r;
-        }
-
+        r = client_append_common_discover_request_options(client, request, &optoffset, optlen);
+        if (r < 0)
+                return r;
 
         r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
                                SD_DHCP_OPTION_END, 0, NULL);
@@ -1424,6 +1477,9 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, size_
         lease->next_server = offer->siaddr;
         lease->address = offer->yiaddr;
 
+        if (lease->lifetime == 0 && client->fallback_lease_lifetime > 0)
+                lease->lifetime = client->fallback_lease_lifetime;
+
         if (lease->address == 0 ||
             lease->server_address == 0 ||
             lease->lifetime == 0) {
@@ -1904,13 +1960,13 @@ static int client_receive_message_raw(
 
         sd_dhcp_client *client = userdata;
         _cleanup_free_ DHCPPacket *packet = NULL;
-        uint8_t cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))];
+        CMSG_BUFFER_TYPE(CMSG_SPACE(sizeof(struct tpacket_auxdata))) control;
         struct iovec iov = {};
         struct msghdr msg = {
                 .msg_iov = &iov,
                 .msg_iovlen = 1,
-                .msg_control = cmsgbuf,
-                .msg_controllen = sizeof(cmsgbuf),
+                .msg_control = &control,
+                .msg_controllen = sizeof(control),
         };
         struct cmsghdr *cmsg;
         bool checksum = true;
@@ -1932,25 +1988,21 @@ static int client_receive_message_raw(
 
         iov = IOVEC_MAKE(packet, buflen);
 
-        len = recvmsg(fd, &msg, 0);
-        if (len < 0) {
-                if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN))
-                        return 0;
-
-                return log_dhcp_client_errno(client, errno,
-                                             "Could not receive message from raw socket: %m");
-        } else if ((size_t)len < sizeof(DHCPPacket))
+        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");
 
-        CMSG_FOREACH(cmsg, &msg)
-                if (cmsg->cmsg_level == SOL_PACKET &&
-                    cmsg->cmsg_type == PACKET_AUXDATA &&
-                    cmsg->cmsg_len == CMSG_LEN(sizeof(struct tpacket_auxdata))) {
-                        struct tpacket_auxdata *aux = (struct tpacket_auxdata*)CMSG_DATA(cmsg);
+        if ((size_t) len < sizeof(DHCPPacket))
+                return 0;
 
-                        checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
-                        break;
-                }
+        cmsg = cmsg_find(&msg, SOL_PACKET, PACKET_AUXDATA, CMSG_LEN(sizeof(struct tpacket_auxdata)));
+        if (cmsg) {
+                struct tpacket_auxdata *aux = (struct tpacket_auxdata*) CMSG_DATA(cmsg);
+                checksum = !(aux->tp_status & TP_STATUS_CSUMNOTREADY);
+        }
 
         r = dhcp_packet_verify_headers(packet, len, checksum, client->port);
         if (r < 0)
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c
index 86f21290..ae581f91 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp-lease.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp-lease.c
@@ -98,59 +98,40 @@ int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu) {
         return 0;
 }
 
-int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) {
+int sd_dhcp_lease_get_servers(
+                sd_dhcp_lease *lease,
+                sd_dhcp_lease_server_type what,
+                const struct in_addr **addr) {
+
         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->dns_size <= 0)
+        if (lease->servers[what].size <= 0)
                 return -ENODATA;
 
-        *addr = lease->dns;
-        return (int) lease->dns_size;
+        *addr = lease->servers[what].addr;
+        return (int) lease->servers[what].size;
 }
 
+int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr) {
+        return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_DNS, addr);
+}
 int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr) {
-        assert_return(lease, -EINVAL);
-        assert_return(addr, -EINVAL);
-
-        if (lease->ntp_size <= 0)
-                return -ENODATA;
-
-        *addr = lease->ntp;
-        return (int) lease->ntp_size;
+        return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_NTP, addr);
 }
-
 int sd_dhcp_lease_get_sip(sd_dhcp_lease *lease, const struct in_addr **addr) {
-        assert_return(lease, -EINVAL);
-        assert_return(addr, -EINVAL);
-
-        if (lease->sip_size <= 0)
-                return -ENODATA;
-
-        *addr = lease->sip;
-        return (int) lease->sip_size;
+        return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_SIP, addr);
 }
-
-int sd_dhcp_lease_get_pop3_server(sd_dhcp_lease *lease, const struct in_addr **addr) {
-        assert_return(lease, -EINVAL);
-        assert_return(addr, -EINVAL);
-
-        if (lease->pop3_server_size <= 0)
-                return -ENODATA;
-
-        *addr = lease->pop3_server;
-        return (int) lease->pop3_server_size;
+int sd_dhcp_lease_get_pop3(sd_dhcp_lease *lease, const struct in_addr **addr) {
+        return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_POP3, addr);
 }
-
-int sd_dhcp_lease_get_smtp_server(sd_dhcp_lease *lease, const struct in_addr **addr) {
-        assert_return(lease, -EINVAL);
-        assert_return(addr, -EINVAL);
-
-        if (lease->smtp_server_size <= 0)
-                return -ENODATA;
-
-        *addr = lease->smtp_server;
-        return (int) lease->smtp_server_size;
+int sd_dhcp_lease_get_smtp(sd_dhcp_lease *lease, const struct in_addr **addr) {
+        return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_SMTP, addr);
+}
+int sd_dhcp_lease_get_lpr(sd_dhcp_lease *lease, const struct in_addr **addr) {
+        return sd_dhcp_lease_get_servers(lease, SD_DHCP_LEASE_LPR, addr);
 }
 
 int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname) {
@@ -300,11 +281,10 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) {
         free(lease->timezone);
         free(lease->hostname);
         free(lease->domainname);
-        free(lease->dns);
-        free(lease->ntp);
-        free(lease->sip);
-        free(lease->pop3_server);
-        free(lease->smtp_server);
+
+        for (sd_dhcp_lease_server_type i = 0; i < _SD_DHCP_LEASE_SERVER_TYPE_MAX; i++)
+                free(lease->servers[i].addr);
+
         free(lease->static_route);
         free(lease->client_id);
         free(lease->vendor_specific);
@@ -410,7 +390,7 @@ static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) {
 }
 
 static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) {
-        assert(option);
+        assert(option || len == 0);
         assert(ret);
         assert(n_ret);
 
@@ -439,33 +419,24 @@ static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_add
 }
 
 static int lease_parse_sip_server(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) {
-        assert(option);
+        assert(option || len == 0);
         assert(ret);
         assert(n_ret);
 
-        if (len <= 0) {
-                *ret = mfree(*ret);
-                *n_ret = 0;
-        } else {
-                size_t n_addresses;
-                struct in_addr *addresses;
-                int l = len - 1;
-
-                if (l % 4 != 0)
-                        return -EINVAL;
-
-                n_addresses = l / 4;
+        if (len <= 0)
+                return -EINVAL;
 
-                addresses = newdup(struct in_addr, option + 1, n_addresses);
-                if (!addresses)
-                        return -ENOMEM;
+        /* The SIP record is like the other, regular server records, but prefixed with a single "encoding"
+         * byte that is either 0 or 1. We only support it to be 1 for now. Let's drop it and parse it like
+         * the other fields */
 
-                free(*ret);
-                *ret = addresses;
-                *n_ret = n_addresses;
+        if (option[0] != 1) { /* We only support IP address encoding for now */
+                *ret = mfree(*ret);
+                *n_ret = 0;
+                return 0;
         }
 
-        return 0;
+        return lease_parse_in_addrs(option + 1, len - 1, ret, n_ret);
 }
 
 static int lease_parse_routes(
@@ -610,35 +581,41 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
                 break;
 
         case SD_DHCP_OPTION_DOMAIN_NAME_SERVER:
-                r = lease_parse_in_addrs(option, len, &lease->dns, &lease->dns_size);
+                r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_DNS].addr, &lease->servers[SD_DHCP_LEASE_DNS].size);
                 if (r < 0)
                         log_debug_errno(r, "Failed to parse DNS server, ignoring: %m");
                 break;
 
         case SD_DHCP_OPTION_NTP_SERVER:
-                r = lease_parse_in_addrs(option, len, &lease->ntp, &lease->ntp_size);
+                r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_NTP].addr, &lease->servers[SD_DHCP_LEASE_NTP].size);
                 if (r < 0)
                         log_debug_errno(r, "Failed to parse NTP server, ignoring: %m");
                 break;
 
         case SD_DHCP_OPTION_SIP_SERVER:
-                r = lease_parse_sip_server(option, len, &lease->sip, &lease->sip_size);
+                r = lease_parse_sip_server(option, len, &lease->servers[SD_DHCP_LEASE_SIP].addr, &lease->servers[SD_DHCP_LEASE_SIP].size);
                 if (r < 0)
                         log_debug_errno(r, "Failed to parse SIP server, ignoring: %m");
                 break;
 
         case SD_DHCP_OPTION_POP3_SERVER:
-                r = lease_parse_in_addrs(option, len, &lease->pop3_server, &lease->pop3_server_size);
+                r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_POP3].addr, &lease->servers[SD_DHCP_LEASE_POP3].size);
                 if (r < 0)
                         log_debug_errno(r, "Failed to parse POP3 server, ignoring: %m");
                 break;
 
         case SD_DHCP_OPTION_SMTP_SERVER:
-                r = lease_parse_in_addrs(option, len, &lease->smtp_server, &lease->smtp_server_size);
+                r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_SMTP].addr, &lease->servers[SD_DHCP_LEASE_SMTP].size);
                 if (r < 0)
                         log_debug_errno(r, "Failed to parse SMTP server, ignoring: %m");
                 break;
 
+        case SD_DHCP_OPTION_LPR_SERVER:
+                r = lease_parse_in_addrs(option, len, &lease->servers[SD_DHCP_LEASE_LPR].addr, &lease->servers[SD_DHCP_LEASE_LPR].size);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to parse LPR server, ignoring: %m");
+                break;
+
         case SD_DHCP_OPTION_STATIC_ROUTE:
                 r = lease_parse_routes(option, len, &lease->static_route, &lease->static_route_size, &lease->static_route_allocated);
                 if (r < 0)
@@ -674,7 +651,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void
         case SD_DHCP_OPTION_HOST_NAME:
                 r = lease_parse_domain(option, len, &lease->hostname);
                 if (r < 0) {
-                        log_debug_errno(r, "Failed to parse host name, ignoring: %m");
+                        log_debug_errno(r, "Failed to parse hostname, ignoring: %m");
                         return 0;
                 }
 
@@ -1075,8 +1052,9 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
                 *dns = NULL,
                 *ntp = NULL,
                 *sip = NULL,
-                *pop3_server = NULL,
-                *smtp_server = NULL,
+                *pop3 = NULL,
+                *smtp = NULL,
+                *lpr = NULL,
                 *mtu = NULL,
                 *routes = NULL,
                 *domains = NULL,
@@ -1106,8 +1084,9 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
                            "DNS", &dns,
                            "NTP", &ntp,
                            "SIP", &sip,
-                           "POP3_SERVERS", &pop3_server,
-                           "SMTP_SERVERS", &smtp_server,
+                           "POP3", &pop3,
+                           "SMTP", &smtp,
+                           "LPR", &lpr,
                            "MTU", &mtu,
                            "DOMAINNAME", &lease->domainname,
                            "HOSTNAME", &lease->hostname,
@@ -1197,43 +1176,51 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) {
         }
 
         if (dns) {
-                r = deserialize_in_addrs(&lease->dns, dns);
+                r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_DNS].addr, dns);
                 if (r < 0)
                         log_debug_errno(r, "Failed to deserialize DNS servers %s, ignoring: %m", dns);
                 else
-                        lease->dns_size = r;
+                        lease->servers[SD_DHCP_LEASE_DNS].size = r;
         }
 
         if (ntp) {
-                r = deserialize_in_addrs(&lease->ntp, ntp);
+                r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_NTP].addr, ntp);
                 if (r < 0)
                         log_debug_errno(r, "Failed to deserialize NTP servers %s, ignoring: %m", ntp);
                 else
-                        lease->ntp_size = r;
+                        lease->servers[SD_DHCP_LEASE_NTP].size = r;
         }
 
         if (sip) {
-                r = deserialize_in_addrs(&lease->sip, sip);
+                r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_SIP].addr, sip);
                 if (r < 0)
                         log_debug_errno(r, "Failed to deserialize SIP servers %s, ignoring: %m", sip);
                 else
-                        lease->sip_size = r;
+                        lease->servers[SD_DHCP_LEASE_SIP].size = r;
+        }
+
+        if (pop3) {
+                r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_POP3].addr, pop3);
+                if (r < 0)
+                        log_debug_errno(r, "Failed to deserialize POP3 server %s, ignoring: %m", pop3);
+                else
+                        lease->servers[SD_DHCP_LEASE_POP3].size = r;
         }
 
-        if (pop3_server) {
-                r = deserialize_in_addrs(&lease->pop3_server, pop3_server);
+        if (smtp) {
+                r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_SMTP].addr, smtp);
                 if (r < 0)
-                        log_debug_errno(r, "Failed to deserialize POP3 server %s, ignoring: %m", pop3_server);
+                        log_debug_errno(r, "Failed to deserialize SMTP server %s, ignoring: %m", smtp);
                 else
-                        lease->pop3_server_size = r;
+                        lease->servers[SD_DHCP_LEASE_SMTP].size = r;
         }
 
-        if (smtp_server) {
-                r = deserialize_in_addrs(&lease->smtp_server, smtp_server);
+        if (lpr) {
+                r = deserialize_in_addrs(&lease->servers[SD_DHCP_LEASE_LPR].addr, lpr);
                 if (r < 0)
-                        log_debug_errno(r, "Failed to deserialize SMTP server %s, ignoring: %m", smtp_server);
+                        log_debug_errno(r, "Failed to deserialize LPR server %s, ignoring: %m", lpr);
                 else
-                        lease->smtp_server_size = r;
+                        lease->servers[SD_DHCP_LEASE_LPR].size = r;
         }
 
         if (mtu) {
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
index 826ad56a..d653b257 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp6-client.c
@@ -20,6 +20,7 @@
 #include "dns-domain.h"
 #include "event-util.h"
 #include "fd-util.h"
+#include "hexdecoct.h"
 #include "hostname-util.h"
 #include "in-addr-util.h"
 #include "network-internal.h"
@@ -69,6 +70,8 @@ struct sd_dhcp6_client {
         size_t req_opts_len;
         char *fqdn;
         char *mudurl;
+        char **user_class;
+        char **vendor_class;
         sd_event_source *receive_message;
         usec_t retransmit_time;
         uint8_t retransmit_count;
@@ -80,6 +83,8 @@ struct sd_dhcp6_client {
         size_t duid_len;
         usec_t information_request_time_usec;
         usec_t information_refresh_time_usec;
+        OrderedHashmap *extra_options;
+        OrderedHashmap *vendor_options;
 };
 
 static const uint16_t default_req_opts[] = {
@@ -108,12 +113,29 @@ const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
 
 const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
-        [DHCP6_STATUS_SUCCESS] = "Success",
-        [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
-        [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
-        [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
-        [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
-        [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
+        [DHCP6_STATUS_SUCCESS]                      = "Success",
+        [DHCP6_STATUS_UNSPEC_FAIL]                  = "Unspecified failure",
+        [DHCP6_STATUS_NO_ADDRS_AVAIL]               = "No addresses available",
+        [DHCP6_STATUS_NO_BINDING]                   = "Binding unavailable",
+        [DHCP6_STATUS_NOT_ON_LINK]                  = "Not on link",
+        [DHCP6_STATUS_USE_MULTICAST]                = "Use multicast",
+        [DHCP6_STATUS_NO_PREFIX_AVAIL]              = "No prefix available",
+        [DHCP6_STATUS_UNKNOWN_QUERY_TYPE]           = "Unknown query type",
+        [DHCP6_STATUS_MALFORMED_QUERY]              = "Malformed query",
+        [DHCP6_STATUS_NOT_CONFIGURED]               = "Not configured",
+        [DHCP6_STATUS_NOT_ALLOWED]                  = "Not allowed",
+        [DHCP6_STATUS_QUERY_TERMINATED]             = "Query terminated",
+        [DHCP6_STATUS_DATA_MISSING]                 = "Data missing",
+        [DHCP6_STATUS_CATCHUP_COMPLETE]             = "Catch up complete",
+        [DHCP6_STATUS_NOT_SUPPORTED]                = "Not supported",
+        [DHCP6_STATUS_TLS_CONNECTION_REFUSED]       = "TLS connection refused",
+        [DHCP6_STATUS_ADDRESS_IN_USE]               = "Address in use",
+        [DHCP6_STATUS_CONFIGURATION_CONFLICT]       = "Configuration conflict",
+        [DHCP6_STATUS_MISSING_BINDING_INFORMATION]  = "Missing binding information",
+        [DHCP6_STATUS_OUTDATED_BINDING_INFORMATION] = "Outdated binding information",
+        [DHCP6_STATUS_SERVER_SHUTTING_DOWN]         = "Server shutting down",
+        [DHCP6_STATUS_DNS_UPDATE_NOT_SUPPORTED]     = "DNS update not supported",
+        [DHCP6_STATUS_EXCESSIVE_TIME_SKEW]          = "Excessive time skew",
 };
 
 DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
@@ -207,6 +229,25 @@ int sd_dhcp6_client_set_prefix_delegation_hint(
         return 0;
 }
 
+int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client, sd_dhcp6_option *v) {
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(v, -EINVAL);
+
+        r = ordered_hashmap_ensure_allocated(&client->vendor_options, &dhcp6_option_hash_ops);
+        if (r < 0)
+                return r;
+
+        r = ordered_hashmap_put(client->vendor_options, v, v);
+        if (r < 0)
+                return r;
+
+        sd_dhcp6_option_ref(v);
+
+        return 1;
+}
+
 static int client_ensure_duid(sd_dhcp6_client *client) {
         if (client->duid_len != 0)
                 return 0;
@@ -237,7 +278,8 @@ static int dhcp6_client_set_duid_internal(
                         r = dhcp_validate_duid_len(duid_type, duid_len, false);
                         if (r < 0)
                                 return log_dhcp6_client_errno(client, r, "Failed to validate length of DUID: %m");
-                        log_dhcp6_client(client, "Setting DUID of type %u with unexpected content", duid_type);
+
+                        log_dhcp6_client(client, "Using DUID of type %u of incorrect length, proceeding.", duid_type);
                 }
 
                 client->duid.type = htobe16(duid_type);
@@ -296,6 +338,48 @@ int sd_dhcp6_client_set_duid_llt(
         return dhcp6_client_set_duid_internal(client, DUID_TYPE_LLT, NULL, 0, llt_time);
 }
 
+static const char* const dhcp6_duid_type_table[_DUID_TYPE_MAX] = {
+        [DUID_TYPE_LLT]  = "DUID-LLT",
+        [DUID_TYPE_EN]   = "DUID-EN/Vendor",
+        [DUID_TYPE_LL]   = "DUID-LL",
+        [DUID_TYPE_UUID] = "UUID",
+};
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(dhcp6_duid_type, DUIDType);
+
+int sd_dhcp6_client_duid_as_string(
+                sd_dhcp6_client *client,
+                char **duid) {
+        _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
+        const char *v;
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(client->duid_len > 0, -ENODATA);
+
+        v = dhcp6_duid_type_to_string(be16toh(client->duid.type));
+        if (v) {
+                s = strdup(v);
+                if (!s)
+                        return -ENOMEM;
+        } else {
+                r = asprintf(&s, "%0x", client->duid.type);
+                if (r < 0)
+                        return -ENOMEM;
+        }
+
+        t = hexmem(&client->duid.raw.data, client->duid_len);
+        if (!t)
+                return -ENOMEM;
+
+        p = strjoin(s, ":", t);
+        if (!p)
+                return -ENOMEM;
+
+        *duid = TAKE_PTR(p);
+
+        return 0;
+}
+
 int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
         assert_return(client, -EINVAL);
         assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY);
@@ -307,6 +391,18 @@ int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) {
         return 0;
 }
 
+int sd_dhcp6_client_get_iaid(sd_dhcp6_client *client, uint32_t *iaid) {
+        assert_return(client, -EINVAL);
+        assert_return(iaid, -EINVAL);
+
+        if (!client->iaid_set)
+                return -ENODATA;
+
+        *iaid = be32toh(client->ia_na.ia_na.id);
+
+        return 0;
+}
+
 int sd_dhcp6_client_set_fqdn(
                 sd_dhcp6_client *client,
                 const char *fqdn) {
@@ -345,18 +441,8 @@ 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);
 
-        switch(option) {
-
-        case SD_DHCP6_OPTION_DNS_SERVERS:
-        case SD_DHCP6_OPTION_DOMAIN_LIST:
-        case SD_DHCP6_OPTION_SNTP_SERVERS:
-        case SD_DHCP6_OPTION_NTP_SERVER:
-        case SD_DHCP6_OPTION_RAPID_COMMIT:
-                break;
-
-        default:
+        if (option <= 0 || option >= UINT8_MAX)
                 return -EINVAL;
-        }
 
         for (t = 0; t < client->req_opts_len; t++)
                 if (client->req_opts[t] == htobe16(option))
@@ -371,17 +457,60 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option)
         return 0;
 }
 
-int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, char *mudurl) {
+int sd_dhcp6_client_set_request_mud_url(sd_dhcp6_client *client, const char *mudurl) {
 
         assert_return(client, -EINVAL);
         assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
         assert_return(mudurl, -EINVAL);
-        assert_return(strlen(mudurl) <= 255, -EINVAL);
+        assert_return(strlen(mudurl) <= UINT8_MAX, -EINVAL);
         assert_return(http_url_is_valid(mudurl), -EINVAL);
 
         return free_and_strdup(&client->mudurl, mudurl);
 }
 
+int sd_dhcp6_client_set_request_user_class(sd_dhcp6_client *client, char **user_class) {
+        _cleanup_strv_free_ char **s = NULL;
+        char **p;
+
+        assert_return(client, -EINVAL);
+        assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
+
+        assert_return(user_class, -EINVAL);
+
+        STRV_FOREACH(p, user_class)
+                if (strlen(*p) > UINT16_MAX)
+                        return -ENAMETOOLONG;
+
+        s = strv_copy(user_class);
+        if (!s)
+                return -ENOMEM;
+
+        client->user_class = TAKE_PTR(s);
+
+        return 0;
+}
+
+int sd_dhcp6_client_set_request_vendor_class(sd_dhcp6_client *client, char **vendor_class) {
+        _cleanup_strv_free_ char **s = NULL;
+        char **p;
+
+        assert_return(client, -EINVAL);
+        assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
+        assert_return(vendor_class, -EINVAL);
+
+        STRV_FOREACH(p, vendor_class)
+                if (strlen(*p) > UINT8_MAX)
+                        return -ENAMETOOLONG;
+
+        s = strv_copy(vendor_class);
+        if (!s)
+                return -ENOMEM;
+
+        client->vendor_class = TAKE_PTR(s);
+
+        return 0;
+}
+
 int sd_dhcp6_client_get_prefix_delegation(sd_dhcp6_client *client, int *delegation) {
         assert_return(client, -EINVAL);
         assert_return(delegation, -EINVAL);
@@ -436,6 +565,24 @@ int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) {
         return 0;
 }
 
+int sd_dhcp6_client_add_option(sd_dhcp6_client *client, sd_dhcp6_option *v) {
+        int r;
+
+        assert_return(client, -EINVAL);
+        assert_return(v, -EINVAL);
+
+        r = ordered_hashmap_ensure_allocated(&client->extra_options, &dhcp6_option_hash_ops);
+        if (r < 0)
+                return r;
+
+        r = ordered_hashmap_put(client->extra_options, UINT_TO_PTR(v->option), v);
+        if (r < 0)
+                return r;
+
+        sd_dhcp6_option_ref(v);
+        return 0;
+}
+
 static void client_notify(sd_dhcp6_client *client, int event) {
         assert(client);
 
@@ -481,7 +628,9 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
         _cleanup_free_ DHCP6Message *message = NULL;
         struct in6_addr all_servers =
                 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
+        struct sd_dhcp6_option *j;
         size_t len, optlen = 512;
+        Iterator i;
         uint8_t *opt;
         int r;
         usec_t elapsed_usec;
@@ -542,6 +691,25 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                                 return r;
                 }
 
+                if (client->user_class) {
+                        r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (client->vendor_class) {
+                        r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (!ordered_hashmap_isempty(client->vendor_options)) {
+                        r = dhcp6_option_append_vendor_option(&opt, &optlen,
+                                                       client->vendor_options);
+                        if (r < 0)
+                                return r;
+                }
+
                 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
                         r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd, &client->hint_pd_prefix);
                         if (r < 0)
@@ -588,6 +756,24 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                                 return r;
                 }
 
+                if (client->user_class) {
+                        r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (client->vendor_class) {
+                        r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (!ordered_hashmap_isempty(client->vendor_options)) {
+                        r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options);
+                        if (r < 0)
+                                return r;
+                }
+
                 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
                         r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
                         if (r < 0)
@@ -622,6 +808,24 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
                                 return r;
                 }
 
+                if (client->user_class) {
+                        r = dhcp6_option_append_user_class(&opt, &optlen, client->user_class);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (client->vendor_class) {
+                        r = dhcp6_option_append_vendor_class(&opt, &optlen, client->vendor_class);
+                        if (r < 0)
+                                return r;
+                }
+
+                if (!ordered_hashmap_isempty(client->vendor_options)) {
+                        r = dhcp6_option_append_vendor_option(&opt, &optlen, client->vendor_options);
+                        if (r < 0)
+                                return r;
+                }
+
                 if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
                         r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
                         if (r < 0)
@@ -661,6 +865,12 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
         if (r < 0)
                 return r;
 
+        ORDERED_HASHMAP_FOREACH(j, client->extra_options, i) {
+                r = dhcp6_option_append(&opt, &optlen, j->option, j->length, j->data);
+                if (r < 0)
+                        return r;
+        }
+
         r = dhcp6_network_send_udp_socket(client->fd, &all_servers, message,
                                           len - optlen);
         if (r < 0)
@@ -888,10 +1098,11 @@ static int client_parse_message(
                 size_t len,
                 sd_dhcp6_lease *lease) {
 
+        uint16_t ia_na_status = 0, ia_pd_status = 0;
         uint32_t lt_t1 = ~0, lt_t2 = ~0;
+        usec_t irt = IRT_DEFAULT;
         bool clientid = false;
         size_t pos = 0;
-        usec_t irt = IRT_DEFAULT;
         int r;
 
         assert(client);
@@ -905,8 +1116,8 @@ static int client_parse_message(
                 DHCP6Option *option = (DHCP6Option *) &message->options[pos];
                 uint16_t optcode, optlen;
                 be32_t iaid_lease;
+                int  status;
                 uint8_t *optval;
-                int status;
 
                 if (len < pos + offsetof(DHCP6Option, data))
                         return -ENOBUFS;
@@ -983,10 +1194,15 @@ static int client_parse_message(
                                 break;
                         }
 
-                        r = dhcp6_option_parse_ia(option, &lease->ia);
+                        r = dhcp6_option_parse_ia(option, &lease->ia, &ia_na_status);
                         if (r < 0 && r != -ENOMSG)
                                 return r;
 
+                        if (ia_na_status == DHCP6_STATUS_NO_ADDRS_AVAIL) {
+                                pos += offsetof(DHCP6Option, data) + optlen;
+                                continue;
+                        }
+
                         r = dhcp6_lease_get_iaid(lease, &iaid_lease);
                         if (r < 0)
                                 return r;
@@ -1011,10 +1227,15 @@ static int client_parse_message(
                                 break;
                         }
 
-                        r = dhcp6_option_parse_ia(option, &lease->pd);
+                        r = dhcp6_option_parse_ia(option, &lease->pd, &ia_pd_status);
                         if (r < 0 && r != -ENOMSG)
                                 return r;
 
+                        if (ia_pd_status == DHCP6_STATUS_NO_PREFIX_AVAIL) {
+                                pos += offsetof(DHCP6Option, data) + optlen;
+                                continue;
+                        }
+
                         r = dhcp6_lease_get_pd_iaid(lease, &iaid_lease);
                         if (r < 0)
                                 return r;
@@ -1078,6 +1299,11 @@ static int client_parse_message(
                 pos += offsetof(DHCP6Option, data) + optlen;
         }
 
+        if (ia_na_status > 0 && ia_pd_status > 0) {
+                log_dhcp6_client(client, "No IA_PD prefix or IA_NA address received. Ignoring.");
+                return -EINVAL;
+        }
+
         if (!clientid) {
                 log_dhcp6_client(client, "%s has incomplete options",
                                  dhcp6_message_type_to_string(message->type));
@@ -1573,6 +1799,11 @@ static sd_dhcp6_client *dhcp6_client_free(sd_dhcp6_client *client) {
         free(client->req_opts);
         free(client->fqdn);
         free(client->mudurl);
+
+        ordered_hashmap_free(client->extra_options);
+        strv_free(client->user_class);
+        strv_free(client->vendor_class);
+
         return mfree(client);
 }
 
diff --git a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c
index 35caec7a..b6dc0279 100644
--- a/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c
+++ b/src/systemd/src/libsystemd-network/sd-dhcp6-lease.c
@@ -215,7 +215,7 @@ int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
         return 0;
 }
 
-int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs) {
+int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs) {
         assert_return(lease, -EINVAL);
         assert_return(addrs, -EINVAL);
 
@@ -343,7 +343,7 @@ int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen)
 }
 
 int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease,
-                                 struct in6_addr **addrs) {
+                                 const struct in6_addr **addrs) {
         assert_return(lease, -EINVAL);
         assert_return(addrs, -EINVAL);
 
diff --git a/src/systemd/src/libsystemd-network/sd-ipv4ll.c b/src/systemd/src/libsystemd-network/sd-ipv4ll.c
index a610546c..0bfaf265 100644
--- a/src/systemd/src/libsystemd-network/sd-ipv4ll.c
+++ b/src/systemd/src/libsystemd-network/sd-ipv4ll.c
@@ -254,12 +254,14 @@ static int ipv4ll_start_internal(sd_ipv4ll *ll, bool reset_generation) {
                 return r;
         }
 
-        return 0;
+        return 1;
 }
 
 int sd_ipv4ll_start(sd_ipv4ll *ll) {
         assert_return(ll, -EINVAL);
-        assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY);
+
+        if (sd_ipv4ll_is_running(ll))
+                return 0;
 
         return ipv4ll_start_internal(ll, true);
 }
diff --git a/src/systemd/src/libsystemd-network/sd-lldp.c b/src/systemd/src/libsystemd-network/sd-lldp.c
index bdf58dac..eb083f5e 100644
--- a/src/systemd/src/libsystemd-network/sd-lldp.c
+++ b/src/systemd/src/libsystemd-network/sd-lldp.c
@@ -468,7 +468,7 @@ _public_ int sd_lldp_get_neighbors(sd_lldp *lldp, sd_lldp_neighbor ***ret) {
 
 _public_ int sd_lldp_set_neighbors_max(sd_lldp *lldp, uint64_t m) {
         assert_return(lldp, -EINVAL);
-        assert_return(m <= 0, -EINVAL);
+        assert_return(m > 0, -EINVAL);
 
         lldp->neighbors_max = m;
         lldp_make_space(lldp, 0);
diff --git a/src/systemd/src/systemd/sd-dhcp-client.h b/src/systemd/src/systemd/sd-dhcp-client.h
index da2aa6c7..ac3b5b36 100644
--- a/src/systemd/src/systemd/sd-dhcp-client.h
+++ b/src/systemd/src/systemd/sd-dhcp-client.h
@@ -48,6 +48,7 @@ enum {
         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,
@@ -184,6 +185,9 @@ int sd_dhcp_client_get_lease(
 int sd_dhcp_client_set_service_type(
                 sd_dhcp_client *client,
                 int type);
+int sd_dhcp_client_set_fallback_lease_lifetime(
+                sd_dhcp_client *client,
+                uint32_t fallback_lease_lifetime);
 
 int sd_dhcp_client_add_option(sd_dhcp_client *client, sd_dhcp_option *v);
 int sd_dhcp_client_add_vendor_option(sd_dhcp_client *client, sd_dhcp_option *v);
@@ -201,6 +205,8 @@ sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client);
  * options when using RFC7844 Anonymity Profiles */
 int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize);
 
+int sd_dhcp_client_id_to_string(const void *data, size_t len, char **ret);
+
 int sd_dhcp_client_attach_event(
                 sd_dhcp_client *client,
                 sd_event *event,
diff --git a/src/systemd/src/systemd/sd-dhcp-lease.h b/src/systemd/src/systemd/sd-dhcp-lease.h
index 1ed5bf27..17bd4918 100644
--- a/src/systemd/src/systemd/sd-dhcp-lease.h
+++ b/src/systemd/src/systemd/sd-dhcp-lease.h
@@ -33,6 +33,17 @@ typedef struct sd_dhcp_route sd_dhcp_route;
 sd_dhcp_lease *sd_dhcp_lease_ref(sd_dhcp_lease *lease);
 sd_dhcp_lease *sd_dhcp_lease_unref(sd_dhcp_lease *lease);
 
+typedef enum sd_dhcp_lease_server_type {
+        SD_DHCP_LEASE_DNS,
+        SD_DHCP_LEASE_NTP,
+        SD_DHCP_LEASE_SIP,
+        SD_DHCP_LEASE_POP3,
+        SD_DHCP_LEASE_SMTP,
+        SD_DHCP_LEASE_LPR,
+        _SD_DHCP_LEASE_SERVER_TYPE_MAX,
+        _SD_DHCP_LEASE_SERVER_TYPE_INVALID = -1,
+} sd_dhcp_lease_server_type;
+
 int sd_dhcp_lease_get_address(sd_dhcp_lease *lease, struct in_addr *addr);
 int sd_dhcp_lease_get_lifetime(sd_dhcp_lease *lease, uint32_t *lifetime);
 int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1);
@@ -42,11 +53,13 @@ int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr);
 int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, const struct in_addr **addr);
 int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr);
 int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr);
+int sd_dhcp_lease_get_servers(sd_dhcp_lease *lease, sd_dhcp_lease_server_type what, const struct in_addr **addr);
 int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr);
 int sd_dhcp_lease_get_ntp(sd_dhcp_lease *lease, const struct in_addr **addr);
 int sd_dhcp_lease_get_sip(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_pop3_server(sd_dhcp_lease *lease, const struct in_addr **addr);
-int sd_dhcp_lease_get_smtp_server(sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_pop3(sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_smtp(sd_dhcp_lease *lease, const struct in_addr **addr);
+int sd_dhcp_lease_get_lpr(sd_dhcp_lease *lease, const struct in_addr **addr);
 int sd_dhcp_lease_get_mtu(sd_dhcp_lease *lease, uint16_t *mtu);
 int sd_dhcp_lease_get_domainname(sd_dhcp_lease *lease, const char **domainname);
 int sd_dhcp_lease_get_search_domains(sd_dhcp_lease *lease, char ***domains);
diff --git a/src/systemd/src/systemd/sd-dhcp6-client.h b/src/systemd/src/systemd/sd-dhcp6-client.h
index 42d4ec75..2b0d63a5 100644
--- a/src/systemd/src/systemd/sd-dhcp6-client.h
+++ b/src/systemd/src/systemd/sd-dhcp6-client.h
@@ -24,6 +24,7 @@
 #include <sys/types.h>
 
 #include "sd-dhcp6-lease.h"
+#include "sd-dhcp6-option.h"
 #include "sd-event.h"
 
 #include "_sd-common.h"
@@ -109,6 +110,12 @@ int sd_dhcp6_client_set_duid_llt(
 int sd_dhcp6_client_set_iaid(
                 sd_dhcp6_client *client,
                 uint32_t iaid);
+int sd_dhcp6_client_get_iaid(
+                sd_dhcp6_client *client,
+                uint32_t *iaid);
+int sd_dhcp6_client_duid_as_string(
+                sd_dhcp6_client *client,
+                char **duid);
 int sd_dhcp6_client_set_fqdn(
                 sd_dhcp6_client *client,
                 const char *fqdn);
@@ -123,7 +130,13 @@ int sd_dhcp6_client_set_request_option(
                 uint16_t option);
 int sd_dhcp6_client_set_request_mud_url(
                 sd_dhcp6_client *client,
-                char *mudurl);
+                const char *mudurl);
+int sd_dhcp6_client_set_request_user_class(
+                sd_dhcp6_client *client,
+                char** user_class);
+int sd_dhcp6_client_set_request_vendor_class(
+                sd_dhcp6_client *client,
+                char** vendor_class);
 int sd_dhcp6_client_set_prefix_delegation_hint(
                 sd_dhcp6_client *client,
                 uint8_t prefixlen,
@@ -136,12 +149,17 @@ 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);
-int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client, uint32_t transaction_id);
+int sd_dhcp6_client_set_transaction_id(sd_dhcp6_client *client,
+                                       uint32_t transaction_id);
+int sd_dhcp6_client_add_vendor_option(sd_dhcp6_client *client,
+                                      sd_dhcp6_option *v);
 
 int sd_dhcp6_client_get_lease(
                 sd_dhcp6_client *client,
                 sd_dhcp6_lease **ret);
 
+int sd_dhcp6_client_add_option(sd_dhcp6_client *client, sd_dhcp6_option *v);
+
 int sd_dhcp6_client_stop(sd_dhcp6_client *client);
 int sd_dhcp6_client_start(sd_dhcp6_client *client);
 int sd_dhcp6_client_is_running(sd_dhcp6_client *client);
diff --git a/src/systemd/src/systemd/sd-dhcp6-lease.h b/src/systemd/src/systemd/sd-dhcp6-lease.h
index 33a32a6d..4301c6db 100644
--- a/src/systemd/src/systemd/sd-dhcp6-lease.h
+++ b/src/systemd/src/systemd/sd-dhcp6-lease.h
@@ -39,10 +39,9 @@ int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix,
                           uint32_t *lifetime_preferred,
                           uint32_t *lifetime_valid);
 
-int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, struct in6_addr **addrs);
+int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs);
 int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains);
-int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease,
-                                 struct in6_addr **addrs);
+int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **addrs);
 int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn);
 
 sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease);
diff --git a/src/systemd/src/systemd/sd-dhcp6-option.h b/src/systemd/src/systemd/sd-dhcp6-option.h
new file mode 100644
index 00000000..88a49863
--- /dev/null
+++ b/src/systemd/src/systemd/sd-dhcp6-option.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+#ifndef foosddhcp6optionhfoo
+#define foosddhcp6optionhfoo
+
+/***
+  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 <inttypes.h>
+#include <sys/types.h>
+
+#include "_sd-common.h"
+
+_SD_BEGIN_DECLARATIONS;
+
+typedef struct sd_dhcp6_option sd_dhcp6_option;
+
+int sd_dhcp6_option_new(uint16_t option, const void *data, size_t length, uint32_t enterprise_identifier, sd_dhcp6_option **ret);
+sd_dhcp6_option *sd_dhcp6_option_ref(sd_dhcp6_option *ra);
+sd_dhcp6_option *sd_dhcp6_option_unref(sd_dhcp6_option *ra);
+
+_SD_DEFINE_POINTER_CLEANUP_FUNC(sd_dhcp6_option, sd_dhcp6_option_unref);
+
+_SD_END_DECLARATIONS;
+
+#endif
diff --git a/src/systemd/src/systemd/sd-lldp.h b/src/systemd/src/systemd/sd-lldp.h
index bf3afadc..2dc9f632 100644
--- a/src/systemd/src/systemd/sd-lldp.h
+++ b/src/systemd/src/systemd/sd-lldp.h
@@ -96,6 +96,9 @@ enum {
 #define SD_LLDP_OUI_802_1 (uint8_t[]) { 0x00, 0x80, 0xc2 }
 #define SD_LLDP_OUI_802_3 (uint8_t[]) { 0x00, 0x12, 0x0f }
 
+#define SD_LLDP_OUI_MUD   (uint8_t[]) { 0x00, 0x00, 0x5E }
+#define SD_LLDP_OUI_SUBTYPE_MUD_USAGE_DESCRIPTION  0x01
+
 /* IEEE 802.1AB-2009 Annex E */
 enum {
         SD_LLDP_OUI_802_1_SUBTYPE_PORT_VLAN_ID          = 1,
@@ -169,6 +172,7 @@ int sd_lldp_neighbor_get_ttl(sd_lldp_neighbor *n, uint16_t *ret_sec);
 int sd_lldp_neighbor_get_system_name(sd_lldp_neighbor *n, const char **ret);
 int sd_lldp_neighbor_get_system_description(sd_lldp_neighbor *n, const char **ret);
 int sd_lldp_neighbor_get_port_description(sd_lldp_neighbor *n, const char **ret);
+int sd_lldp_neighbor_get_mud_url(sd_lldp_neighbor *n, const char **ret);
 int sd_lldp_neighbor_get_system_capabilities(sd_lldp_neighbor *n, uint16_t *ret);
 int sd_lldp_neighbor_get_enabled_capabilities(sd_lldp_neighbor *n, uint16_t *ret);
 
diff --git a/src/systemd/src/systemd/sd-ndisc.h b/src/systemd/src/systemd/sd-ndisc.h
index d1bee343..3ddfc8cb 100644
--- a/src/systemd/src/systemd/sd-ndisc.h
+++ b/src/systemd/src/systemd/sd-ndisc.h
@@ -30,7 +30,7 @@
 
 _SD_BEGIN_DECLARATIONS;
 
-/* Neightbor Discovery Options, RFC 4861, Section 4.6 and
+/* Neighbor Discovery Options, RFC 4861, Section 4.6 and
  * https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-5 */
 enum {
         SD_NDISC_OPTION_SOURCE_LL_ADDRESS  = 1,