]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-dhcp6-client: introduce dhcp6_lease_new_from_message()
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 6 Feb 2022 10:36:30 +0000 (19:36 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 14 Feb 2022 05:43:45 +0000 (14:43 +0900)
src/libsystemd-network/dhcp6-internal.h
src/libsystemd-network/dhcp6-lease-internal.h
src/libsystemd-network/sd-dhcp6-client.c
src/libsystemd-network/sd-dhcp6-lease.c
src/libsystemd-network/test-dhcp6-client.c

index 23738d3197266772a809bba9ca34f6a12efc8d0c..7f03ce1e07e475d7dc15bf172480a2eae2296af2 100644 (file)
@@ -11,7 +11,9 @@
 #include "sd-event.h"
 #include "sd-dhcp6-client.h"
 
+#include "dhcp-identifier.h"
 #include "dhcp6-protocol.h"
+#include "ether-addr-util.h"
 #include "hashmap.h"
 #include "list.h"
 #include "macro.h"
@@ -75,7 +77,59 @@ typedef struct DHCP6IA {
         LIST_HEAD(DHCP6Address, addresses);
 } DHCP6IA;
 
-typedef struct sd_dhcp6_client sd_dhcp6_client;
+/* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
+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;
+
+typedef struct sd_dhcp6_client {
+        unsigned n_ref;
+
+        DHCP6State state;
+        sd_event *event;
+        int event_priority;
+        int ifindex;
+        char *ifname;
+        struct in6_addr local_address;
+        uint8_t mac_addr[HW_ADDR_MAX_SIZE];
+        size_t mac_addr_len;
+        uint16_t arp_type;
+        DHCP6IA ia_na;
+        DHCP6IA ia_pd;
+        sd_event_source *timeout_t1;
+        sd_event_source *timeout_t2;
+        DHCP6RequestIA request_ia;
+        be32_t transaction_id;
+        usec_t transaction_start;
+        struct sd_dhcp6_lease *lease;
+        int fd;
+        bool information_request;
+        bool iaid_set;
+        be16_t *req_opts;
+        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;
+        sd_event_source *timeout_resend;
+        sd_event_source *timeout_resend_expire;
+        sd_dhcp6_client_callback_t callback;
+        void *userdata;
+        struct duid duid;
+        size_t duid_len;
+        usec_t information_request_time_usec;
+        usec_t information_refresh_time_usec;
+        OrderedHashmap *extra_options;
+        OrderedHashmap *vendor_options;
+
+        /* Ignore ifindex when generating iaid. See dhcp_identifier_set_iaid(). */
+        bool test_mode;
+} sd_dhcp6_client;
 
 bool dhcp6_option_can_request(uint16_t option);
 int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
@@ -113,12 +167,6 @@ 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_;
index 7a5d14cb4f8c3e429d1f319345c1c3d2fd409394..589274c23bfab6ec40b649a4669576966cf8201a 100644 (file)
@@ -49,7 +49,6 @@ void dhcp6_ia_clear_addresses(DHCP6IA *ia);
 DHCP6IA *dhcp6_ia_free(DHCP6IA *ia);
 DEFINE_TRIVIAL_CLEANUP_FUNC(DHCP6IA*, dhcp6_ia_free);
 
-void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease);
 int dhcp6_lease_get_lifetime(sd_dhcp6_lease *lease, usec_t *ret_t1, usec_t *ret_t2);
 int dhcp6_lease_get_max_retransmit_duration(sd_dhcp6_lease *lease, usec_t *ret);
 
@@ -69,3 +68,10 @@ int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t op
 int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
 
 int dhcp6_lease_new(sd_dhcp6_lease **ret);
+int dhcp6_lease_new_from_message(
+                sd_dhcp6_client *client,
+                const DHCP6Message *message,
+                size_t len,
+                const triple_timestamp *timestamp,
+                const struct in6_addr *server_address,
+                sd_dhcp6_lease **ret);
index f19c6977135f088d149ef636288002eb66481faf..9bd386531446261ce52d0a8125c18879d2fe1277 100644 (file)
 #include "util.h"
 #include "web-util.h"
 
-#define MAX_MAC_ADDR_LEN INFINIBAND_ALEN
-
-#define IRT_DEFAULT (1 * USEC_PER_DAY)
-#define IRT_MINIMUM (600 * USEC_PER_SEC)
-
-/* what to request from the server, addresses (IA_NA) and/or prefixes (IA_PD) */
-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;
-
-        DHCP6State state;
-        sd_event *event;
-        int event_priority;
-        int ifindex;
-        char *ifname;
-        struct in6_addr local_address;
-        uint8_t mac_addr[MAX_MAC_ADDR_LEN];
-        size_t mac_addr_len;
-        uint16_t arp_type;
-        DHCP6IA ia_na;
-        DHCP6IA ia_pd;
-        sd_event_source *timeout_t1;
-        sd_event_source *timeout_t2;
-        DHCP6RequestIA request_ia;
-        be32_t transaction_id;
-        usec_t transaction_start;
-        struct sd_dhcp6_lease *lease;
-        int fd;
-        bool information_request;
-        bool iaid_set;
-        be16_t *req_opts;
-        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;
-        sd_event_source *timeout_resend;
-        sd_event_source *timeout_resend_expire;
-        sd_dhcp6_client_callback_t callback;
-        void *userdata;
-        struct duid duid;
-        size_t duid_len;
-        usec_t information_request_time_usec;
-        usec_t information_refresh_time_usec;
-        OrderedHashmap *extra_options;
-        OrderedHashmap *vendor_options;
-
-        /* Ignore ifindex when generating iaid. See dhcp_identifier_set_iaid(). */
-        bool test_mode;
-};
-
 static const uint16_t default_req_opts[] = {
         SD_DHCP6_OPTION_DNS_SERVERS,
         SD_DHCP6_OPTION_DOMAIN_LIST,
@@ -237,7 +178,7 @@ int sd_dhcp6_client_set_mac(
 
         assert_return(client, -EINVAL);
         assert_return(addr, -EINVAL);
-        assert_return(addr_len <= MAX_MAC_ADDR_LEN, -EINVAL);
+        assert_return(addr_len <= HW_ADDR_MAX_SIZE, -EINVAL);
         assert_return(client->state == DHCP6_STATE_STOPPED, -EBUSY);
 
         if (arp_type == ARPHRD_ETHER)
@@ -1081,208 +1022,11 @@ static int client_ensure_iaid(sd_dhcp6_client *client) {
         return 0;
 }
 
-int client_parse_message(
-                sd_dhcp6_client *client,
-                DHCP6Message *message,
-                size_t len,
-                sd_dhcp6_lease *lease) {
-
-        usec_t irt = IRT_DEFAULT;
-        int r;
-
-        assert(client);
-        assert(message);
-        assert(len >= sizeof(DHCP6Message));
-        assert(lease);
-
-        len -= sizeof(DHCP6Message);
-        for (size_t offset = 0; offset < len;) {
-                uint16_t optcode;
-                size_t optlen;
-                const uint8_t *optval;
-
-                r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval);
-                if (r < 0)
-                        return r;
-
-                switch (optcode) {
-                case SD_DHCP6_OPTION_CLIENTID:
-                        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));
-
-                        r = dhcp6_lease_set_clientid(lease, optval, optlen);
-                        if (r < 0)
-                                return r;
-
-                        break;
-
-                case SD_DHCP6_OPTION_SERVERID:
-                        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);
-                        if (r < 0)
-                                return r;
-
-                        break;
-
-                case SD_DHCP6_OPTION_PREFERENCE:
-                        if (optlen != 1)
-                                return -EINVAL;
-
-                        r = dhcp6_lease_set_preference(lease, optval[0]);
-                        if (r < 0)
-                                return r;
-
-                        break;
-
-                case SD_DHCP6_OPTION_STATUS_CODE: {
-                        _cleanup_free_ char *msg = NULL;
-
-                        r = dhcp6_option_parse_status(optval, optlen, &msg);
-                        if (r < 0)
-                                return r;
-
-                        if (r > 0)
-                                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
-                                                              "Received %s message with non-zero status: %s%s%s",
-                                                              dhcp6_message_type_to_string(message->type),
-                                                              strempty(msg), isempty(msg) ? "" : ": ",
-                                                              dhcp6_message_status_to_string(r));
-                        break;
-                }
-                case SD_DHCP6_OPTION_IA_NA: {
-                        _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
-
-                        if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
-                                log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode.");
-                                break;
-                        }
-
-                        r = dhcp6_option_parse_ia(client, client->ia_na.header.id, optcode, optlen, optval, &ia);
-                        if (r == -ENOMEM)
-                                return r;
-                        if (r < 0)
-                                continue;
-
-                        if (lease->ia_na) {
-                                log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring.");
-                                continue;
-                        }
-
-                        dhcp6_ia_free(lease->ia_na);
-                        lease->ia_na = TAKE_PTR(ia);
-                        break;
-                }
-                case SD_DHCP6_OPTION_IA_PD: {
-                        _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
-
-                        if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
-                                log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode.");
-                                break;
-                        }
-
-                        r = dhcp6_option_parse_ia(client, client->ia_pd.header.id, optcode, optlen, optval, &ia);
-                        if (r == -ENOMEM)
-                                return r;
-                        if (r < 0)
-                                continue;
-
-                        if (lease->ia_pd) {
-                                log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring.");
-                                continue;
-                        }
-
-                        dhcp6_ia_free(lease->ia_pd);
-                        lease->ia_pd = TAKE_PTR(ia);
-                        break;
-                }
-                case SD_DHCP6_OPTION_RAPID_COMMIT:
-                        r = dhcp6_lease_set_rapid_commit(lease);
-                        if (r < 0)
-                                return r;
-
-                        break;
-
-                case SD_DHCP6_OPTION_DNS_SERVERS:
-                        r = dhcp6_lease_add_dns(lease, optval, optlen);
-                        if (r < 0)
-                                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)
-                                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)
-                                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)
-                                log_dhcp6_client_errno(client, r, "Failed to parse SNTP server option, ignoring: %m");
-
-                        break;
-
-                case SD_DHCP6_OPTION_CLIENT_FQDN:
-                        r = dhcp6_lease_set_fqdn(lease, optval, optlen);
-                        if (r < 0)
-                                log_dhcp6_client_errno(client, r, "Failed to parse FQDN option, ignoring: %m");
-
-                        break;
-
-                case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
-                        if (optlen != 4)
-                                return -EINVAL;
-
-                        irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC;
-                        break;
-                }
-        }
-
-        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) {
-                r = dhcp6_lease_get_serverid(lease, NULL, NULL);
-                if (r < 0)
-                        return log_dhcp6_client_errno(client, r, "%s has no server id",
-                                                      dhcp6_message_type_to_string(message->type));
-
-                if (!lease->ia_na && !lease->ia_pd)
-                        return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "No IA_PD prefix or IA_NA address received. Ignoring.");
-
-                dhcp6_lease_set_lifetime(lease);
-        }
-
-        client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);
-
-        return 0;
-}
-
 static int client_receive_reply(
                 sd_dhcp6_client *client,
-                DHCP6Message *reply,
+                DHCP6Message *message,
                 size_t len,
-                const triple_timestamp *t,
+                const triple_timestamp *timestamp,
                 const struct in6_addr *server_address) {
 
         _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
@@ -1290,21 +1034,12 @@ static int client_receive_reply(
         int r;
 
         assert(client);
-        assert(reply);
-        assert(t);
+        assert(message);
 
-        if (reply->type != DHCP6_MESSAGE_REPLY)
+        if (message->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);
+        r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease);
         if (r < 0)
                 return r;
 
@@ -1325,9 +1060,9 @@ static int client_receive_reply(
 
 static int client_receive_advertise(
                 sd_dhcp6_client *client,
-                DHCP6Message *advertise,
+                DHCP6Message *message,
                 size_t len,
-                const triple_timestamp *t,
+                const triple_timestamp *timestamp,
                 const struct in6_addr *server_address) {
 
         _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
@@ -1335,21 +1070,12 @@ static int client_receive_advertise(
         int r;
 
         assert(client);
-        assert(advertise);
-        assert(t);
+        assert(message);
 
-        if (advertise->type != DHCP6_MESSAGE_ADVERTISE)
+        if (message->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);
+        r = dhcp6_lease_new_from_message(client, message, len, timestamp, server_address, &lease);
         if (r < 0)
                 return r;
 
@@ -1451,9 +1177,6 @@ static int client_receive_message(
                         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)
index e323fd3923cc0d924834b909f94a1f8a117b4ad7..9cc7e2b642831aca71f4b902a9f93076523f8558 100644 (file)
 #include "strv.h"
 #include "util.h"
 
+#define IRT_DEFAULT (1 * USEC_PER_DAY)
+#define IRT_MINIMUM (600 * USEC_PER_SEC)
+
+static void dhcp6_lease_set_timestamp(sd_dhcp6_lease *lease, const triple_timestamp *timestamp) {
+        assert(lease);
+
+        if (timestamp && triple_timestamp_is_set(timestamp))
+                lease->timestamp = *timestamp;
+        else
+                triple_timestamp_get(&lease->timestamp);
+}
+
 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);
@@ -24,7 +36,7 @@ int sd_dhcp6_lease_get_timestamp(sd_dhcp6_lease *lease, clockid_t clock, uint64_
         return 0;
 }
 
-void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) {
+static void dhcp6_lease_set_lifetime(sd_dhcp6_lease *lease) {
         uint32_t t1 = UINT32_MAX, t2 = UINT32_MAX, min_valid_lt = UINT32_MAX;
         DHCP6Address *a;
 
@@ -85,6 +97,15 @@ int dhcp6_lease_get_max_retransmit_duration(sd_dhcp6_lease *lease, usec_t *ret)
         return 0;
 }
 
+static void dhcp6_lease_set_server_address(sd_dhcp6_lease *lease, const struct in6_addr *server_address) {
+        assert(lease);
+
+        if (server_address)
+                lease->server_address = *server_address;
+        else
+                lease->server_address = (struct in6_addr) {};
+}
+
 int sd_dhcp6_lease_get_server_address(sd_dhcp6_lease *lease, struct in6_addr *ret) {
         assert_return(lease, -EINVAL);
         assert_return(ret, -EINVAL);
@@ -435,6 +456,203 @@ int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) {
         return 0;
 }
 
+static int dhcp6_lease_parse_message(
+                sd_dhcp6_client *client,
+                sd_dhcp6_lease *lease,
+                const DHCP6Message *message,
+                size_t len) {
+
+        usec_t irt = IRT_DEFAULT;
+        int r;
+
+        assert(client);
+        assert(lease);
+        assert(message);
+        assert(len >= sizeof(DHCP6Message));
+
+        len -= sizeof(DHCP6Message);
+        for (size_t offset = 0; offset < len;) {
+                uint16_t optcode;
+                size_t optlen;
+                const uint8_t *optval;
+
+                r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval);
+                if (r < 0)
+                        return r;
+
+                switch (optcode) {
+                case SD_DHCP6_OPTION_CLIENTID:
+                        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));
+
+                        r = dhcp6_lease_set_clientid(lease, optval, optlen);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case SD_DHCP6_OPTION_SERVERID:
+                        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);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case SD_DHCP6_OPTION_PREFERENCE:
+                        if (optlen != 1)
+                                return -EINVAL;
+
+                        r = dhcp6_lease_set_preference(lease, optval[0]);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case SD_DHCP6_OPTION_STATUS_CODE: {
+                        _cleanup_free_ char *msg = NULL;
+
+                        r = dhcp6_option_parse_status(optval, optlen, &msg);
+                        if (r < 0)
+                                return r;
+
+                        if (r > 0)
+                                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
+                                                              "Received %s message with non-zero status: %s%s%s",
+                                                              dhcp6_message_type_to_string(message->type),
+                                                              strempty(msg), isempty(msg) ? "" : ": ",
+                                                              dhcp6_message_status_to_string(r));
+                        break;
+                }
+                case SD_DHCP6_OPTION_IA_NA: {
+                        _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
+
+                        if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
+                                log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode.");
+                                break;
+                        }
+
+                        r = dhcp6_option_parse_ia(client, client->ia_na.header.id, optcode, optlen, optval, &ia);
+                        if (r == -ENOMEM)
+                                return r;
+                        if (r < 0)
+                                continue;
+
+                        if (lease->ia_na) {
+                                log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring.");
+                                continue;
+                        }
+
+                        dhcp6_ia_free(lease->ia_na);
+                        lease->ia_na = TAKE_PTR(ia);
+                        break;
+                }
+                case SD_DHCP6_OPTION_IA_PD: {
+                        _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
+
+                        if (client->state == DHCP6_STATE_INFORMATION_REQUEST) {
+                                log_dhcp6_client(client, "Ignoring IA PD option in information requesting mode.");
+                                break;
+                        }
+
+                        r = dhcp6_option_parse_ia(client, client->ia_pd.header.id, optcode, optlen, optval, &ia);
+                        if (r == -ENOMEM)
+                                return r;
+                        if (r < 0)
+                                continue;
+
+                        if (lease->ia_pd) {
+                                log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring.");
+                                continue;
+                        }
+
+                        dhcp6_ia_free(lease->ia_pd);
+                        lease->ia_pd = TAKE_PTR(ia);
+                        break;
+                }
+                case SD_DHCP6_OPTION_RAPID_COMMIT:
+                        r = dhcp6_lease_set_rapid_commit(lease);
+                        if (r < 0)
+                                return r;
+
+                        break;
+
+                case SD_DHCP6_OPTION_DNS_SERVERS:
+                        r = dhcp6_lease_add_dns(lease, optval, optlen);
+                        if (r < 0)
+                                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)
+                                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)
+                                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)
+                                log_dhcp6_client_errno(client, r, "Failed to parse SNTP server option, ignoring: %m");
+
+                        break;
+
+                case SD_DHCP6_OPTION_CLIENT_FQDN:
+                        r = dhcp6_lease_set_fqdn(lease, optval, optlen);
+                        if (r < 0)
+                                log_dhcp6_client_errno(client, r, "Failed to parse FQDN option, ignoring: %m");
+
+                        break;
+
+                case SD_DHCP6_OPTION_INFORMATION_REFRESH_TIME:
+                        if (optlen != 4)
+                                return -EINVAL;
+
+                        irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC;
+                        break;
+                }
+        }
+
+        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) {
+                r = dhcp6_lease_get_serverid(lease, NULL, NULL);
+                if (r < 0)
+                        return log_dhcp6_client_errno(client, r, "%s has no server id",
+                                                      dhcp6_message_type_to_string(message->type));
+
+                if (!lease->ia_na && !lease->ia_pd)
+                        return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "No IA_PD prefix or IA_NA address received. Ignoring.");
+
+                dhcp6_lease_set_lifetime(lease);
+        }
+
+        client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM);
+
+        return 0;
+}
+
 static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) {
         if (!lease)
                 return NULL;
@@ -471,3 +689,34 @@ int dhcp6_lease_new(sd_dhcp6_lease **ret) {
         *ret = lease;
         return 0;
 }
+
+int dhcp6_lease_new_from_message(
+                sd_dhcp6_client *client,
+                const DHCP6Message *message,
+                size_t len,
+                const triple_timestamp *timestamp,
+                const struct in6_addr *server_address,
+                sd_dhcp6_lease **ret) {
+
+        _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
+        int r;
+
+        assert(client);
+        assert(message);
+        assert(len >= sizeof(DHCP6Message));
+        assert(ret);
+
+        r = dhcp6_lease_new(&lease);
+        if (r < 0)
+                return r;
+
+        dhcp6_lease_set_timestamp(lease, timestamp);
+        dhcp6_lease_set_server_address(lease, server_address);
+
+        r = dhcp6_lease_parse_message(client, lease, message, len);
+        if (r < 0)
+                return r;
+
+        *ret = TAKE_PTR(lease);
+        return 0;
+}
index 6152a30863e65d64d758d35ba709cbadfed68c00..03862bd5ac9e6447d6b67c941d87176703ebea93 100644 (file)
@@ -401,9 +401,7 @@ static void test_client_parse_message_issue_22099(void) {
         assert_se(sd_dhcp6_client_set_iaid(client, 0xcc59117b) >= 0);
         assert_se(sd_dhcp6_client_set_duid(client, 2, duid, sizeof(duid)) >= 0);
 
-        assert_se(dhcp6_lease_new(&lease) >= 0);
-
-        assert_se(client_parse_message(client, (DHCP6Message*) msg, sizeof(msg), lease) >= 0);
+        assert_se(dhcp6_lease_new_from_message(client, (const DHCP6Message*) msg, sizeof(msg), NULL, NULL, &lease) >= 0);
 }
 
 static uint8_t msg_advertise[198] = {