From: Yu Watanabe Date: Sun, 6 Feb 2022 10:36:30 +0000 (+0900) Subject: sd-dhcp6-client: introduce dhcp6_lease_new_from_message() X-Git-Tag: v251-rc1~291^2~51 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=65ece4c85b149f4b7ae1c97793e00147da547a0b;p=thirdparty%2Fsystemd.git sd-dhcp6-client: introduce dhcp6_lease_new_from_message() --- diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 23738d31972..7f03ce1e07e 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -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_; diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index 7a5d14cb4f8..589274c23bf 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -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); diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index f19c6977135..9bd38653144 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -30,65 +30,6 @@ #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) diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index e323fd3923c..9cc7e2b6428 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -11,6 +11,18 @@ #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; +} diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 6152a30863e..03862bd5ac9 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -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] = {