From dc95e21d33708e807d3e5872af428383aac3f9b7 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 23 Sep 2021 14:57:29 +0900 Subject: [PATCH 01/15] sd-dhcp6-client: constify one argument --- src/libsystemd-network/dhcp6-internal.h | 2 +- src/libsystemd-network/dhcp6-option.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 91418ad351..29c5c22ca0 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -105,7 +105,7 @@ 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(sd_dhcp6_client *client, DHCP6Option *iaoption, be32_t iaid, DHCP6IA *ia, uint16_t *ret_status_code); -int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, +int dhcp6_option_parse_ip6addrs(const uint8_t *optval, uint16_t optlen, struct in6_addr **addrs, size_t count); int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen, char ***str_arr); diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 34d7e997dd..0709cfd4fd 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -707,7 +707,7 @@ int dhcp6_option_parse_ia( return 1; } -int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, +int dhcp6_option_parse_ip6addrs(const uint8_t *optval, uint16_t optlen, struct in6_addr **addrs, size_t count) { if (optlen == 0 || optlen % sizeof(struct in6_addr) != 0) From b89a3758e92894162e3c2dcb594a55acff3274d5 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 23 Sep 2021 14:58:28 +0900 Subject: [PATCH 02/15] sd-dhcp6-client: modernize dhcp6_option_parse() - merge dhcp6_option_parse() with option_parse_hdr(). - do not assign/update any values on error. - use assert() instead of assert_return(), as the assertions cannot be triggered by a library user. --- src/libsystemd-network/dhcp6-internal.h | 10 +++- src/libsystemd-network/dhcp6-option.c | 62 ++++++++++------------ src/libsystemd-network/sd-dhcp6-lease.c | 37 ++++++------- src/libsystemd-network/test-dhcp6-client.c | 50 ++++++++--------- 4 files changed, 77 insertions(+), 82 deletions(-) diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 29c5c22ca0..8bcc826f42 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -101,8 +101,14 @@ 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 * const *user_class); int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *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( + const uint8_t *buf, + size_t buflen, + size_t *offset, + uint16_t *ret_option_code, + size_t *ret_option_data_len, + const uint8_t **ret_option_data); int dhcp6_option_parse_status(DHCP6Option *option, size_t len); int dhcp6_option_parse_ia(sd_dhcp6_client *client, DHCP6Option *iaoption, be32_t iaid, DHCP6IA *ia, uint16_t *ret_status_code); int dhcp6_option_parse_ip6addrs(const uint8_t *optval, uint16_t optlen, diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 0709cfd4fd..781d391c0c 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -370,47 +370,39 @@ int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const return dhcp6_option_append(buf, buflen, SD_DHCP6_OPTION_VENDOR_CLASS, total, p); } -static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) { - DHCP6Option *option = (DHCP6Option*) *buf; - uint16_t len; +int dhcp6_option_parse( + const uint8_t *buf, + size_t buflen, + size_t *offset, + uint16_t *ret_option_code, + size_t *ret_option_data_len, + const uint8_t **ret_option_data) { - assert_return(buf, -EINVAL); - assert_return(optcode, -EINVAL); - assert_return(optlen, -EINVAL); + const DHCP6Option *option; + size_t len; - if (*buflen < offsetof(DHCP6Option, data)) - return -ENOMSG; + assert(buf); + assert(offset); + assert(ret_option_code); + assert(ret_option_data_len); + assert(ret_option_data); + if (buflen < offsetof(DHCP6Option, data)) + return -EBADMSG; + + if (*offset >= buflen - offsetof(DHCP6Option, data)) + return -EBADMSG; + + option = (const DHCP6Option*) (buf + *offset); len = be16toh(option->len); - if (len > *buflen) - return -ENOMSG; + if (len > buflen - offsetof(DHCP6Option, data) - *offset) + return -EBADMSG; - *optcode = be16toh(option->code); - *optlen = len; - - *buf += 4; - *buflen -= 4; - - return 0; -} - -int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, - size_t *optlen, uint8_t **optvalue) { - int r; - - assert_return(buf && buflen && optcode && optlen && optvalue, -EINVAL); - - r = option_parse_hdr(buf, buflen, optcode, optlen); - if (r < 0) - return r; - - if (*optlen > *buflen) - return -ENOBUFS; - - *optvalue = *buf; - *buflen -= *optlen; - *buf += *optlen; + *offset += offsetof(DHCP6Option, data) + len; + *ret_option_code = be16toh(option->code); + *ret_option_data_len = len; + *ret_option_data = option->data; return 0; } diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 9082185bca..6375a22537 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -259,9 +259,6 @@ int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains) { int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { int r; - uint16_t subopt; - size_t sublen; - uint8_t *subval; assert_return(lease, -EINVAL); assert_return(optval, -EINVAL); @@ -269,10 +266,14 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { lease->ntp = mfree(lease->ntp); lease->ntp_count = 0; - while ((r = dhcp6_option_parse(&optval, &optlen, &subopt, &sublen, - &subval)) >= 0) { - int s; - char **servers; + for (size_t offset = 0; offset < optlen;) { + const uint8_t *subval; + size_t sublen; + uint16_t subopt; + + r = dhcp6_option_parse(optval, optlen, &offset, &subopt, &sublen, &subval); + if (r < 0) + return r; switch(subopt) { case DHCP6_NTP_SUBOPTION_SRV_ADDR: @@ -280,19 +281,18 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { if (sublen != 16) return 0; - s = dhcp6_option_parse_ip6addrs(subval, sublen, - &lease->ntp, - lease->ntp_count); - if (s < 0) - return s; + r = dhcp6_option_parse_ip6addrs(subval, sublen, &lease->ntp, lease->ntp_count); + if (r < 0) + return r; - lease->ntp_count = s; + lease->ntp_count = r; break; - case DHCP6_NTP_SUBOPTION_SRV_FQDN: - r = dhcp6_option_parse_domainname_list(subval, sublen, - &servers); + case DHCP6_NTP_SUBOPTION_SRV_FQDN: { + char **servers; + + r = dhcp6_option_parse_domainname_list(subval, sublen, &servers); if (r < 0) return 0; @@ -300,12 +300,9 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { lease->ntp_fqdn_count = r; break; - } + }} } - if (r != -ENOMSG) - return r; - return 0; } diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 22ab25b2b0..f057276476 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -170,47 +170,47 @@ static int test_option(sd_event *e) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 'B', 'A', 'R', }; + size_t offset, pos, optlen, outlen = sizeof(result); + const uint8_t *optval; uint16_t optcode; - size_t optlen; - uint8_t *optval, *buf, *out; - size_t zero = 0, pos = 3; - size_t buflen = sizeof(packet), outlen = sizeof(result); + uint8_t *out; log_debug("/* %s */", __func__); - assert_se(buflen == outlen); + assert_se(sizeof(packet) == sizeof(result)); - assert_se(dhcp6_option_parse(&buf, &zero, &optcode, &optlen, - &optval) == -ENOMSG); + offset = 0; + assert_se(dhcp6_option_parse(packet, 0, &offset, &optcode, &optlen, &optval) == -EBADMSG); - buflen -= 3; - buf = &packet[3]; + offset = 3; + assert_se(dhcp6_option_parse(packet, 0, &offset, &optcode, &optlen, &optval) == -EBADMSG); + + offset = 3; + assert_se(dhcp6_option_parse(packet, sizeof(packet), &offset, &optcode, &optlen, &optval) >= 0); + + assert_se(optcode == SD_DHCP6_OPTION_ORO); + assert_se(optlen == 7); + assert_se(optval == packet + 7); + + pos = 3; outlen -= 3; out = &result[3]; - assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen, - &optval) >= 0); - pos += 4 + optlen; - assert_se(buf == &packet[pos]); - assert_se(optcode == SD_DHCP6_OPTION_ORO); - assert_se(optlen == 7); - assert_se(buflen + pos == sizeof(packet)); + assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen, optval) >= 0); - assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen, - optval) >= 0); + pos += 4 + optlen; assert_se(out == &result[pos]); assert_se(*out == 0x00); - assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen, - &optval) >= 0); - pos += 4 + optlen; - assert_se(buf == &packet[pos]); + assert_se(dhcp6_option_parse(packet, sizeof(packet), &offset, &optcode, &optlen, &optval) >= 0); + assert_se(optcode == SD_DHCP6_OPTION_VENDOR_CLASS); assert_se(optlen == 9); - assert_se(buflen + pos == sizeof(packet)); + assert_se(optval == packet + 18); - assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen, - optval) >= 0); + assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen, optval) >= 0); + + pos += 4 + optlen; assert_se(out == &result[pos]); assert_se(*out == 'B'); From e620104956dff64244c0e73e86c3138c0b13b875 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 28 Sep 2021 00:48:59 +0900 Subject: [PATCH 03/15] test: add tests for reading unaligned data --- src/libsystemd-network/test-dhcp6-client.c | 29 ++++++++++++++++------ 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index f057276476..35aaa2a997 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -156,7 +156,7 @@ static int test_parse_domain(sd_event *e) { static int test_option(sd_event *e) { uint8_t packet[] = { - 'F', 'O', 'O', + 'F', 'O', 'O', 'H', 'O', 'G', 'E', 0x00, SD_DHCP6_OPTION_ORO, 0x00, 0x07, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 0x00, SD_DHCP6_OPTION_VENDOR_CLASS, 0x00, 0x09, @@ -164,12 +164,13 @@ static int test_option(sd_event *e) { 'B', 'A', 'R', }; uint8_t result[] = { - 'F', 'O', 'O', + 'F', 'O', 'O', 'H', 'O', 'G', 'E', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 'B', 'A', 'R', }; + _cleanup_free_ uint8_t *buf = NULL; size_t offset, pos, optlen, outlen = sizeof(result); const uint8_t *optval; uint16_t optcode; @@ -185,16 +186,28 @@ static int test_option(sd_event *e) { offset = 3; assert_se(dhcp6_option_parse(packet, 0, &offset, &optcode, &optlen, &optval) == -EBADMSG); - offset = 3; + /* Tests for reading unaligned data. */ + assert_se(buf = new(uint8_t, sizeof(packet))); + for (size_t i = 0; i <= 7; i++) { + memcpy(buf, packet + i, sizeof(packet) - i); + offset = 7 - i; + assert_se(dhcp6_option_parse(buf, sizeof(packet), &offset, &optcode, &optlen, &optval) >= 0); + + assert_se(optcode == SD_DHCP6_OPTION_ORO); + assert_se(optlen == 7); + assert_se(optval == buf + 11 - i); + } + + offset = 7; assert_se(dhcp6_option_parse(packet, sizeof(packet), &offset, &optcode, &optlen, &optval) >= 0); assert_se(optcode == SD_DHCP6_OPTION_ORO); assert_se(optlen == 7); - assert_se(optval == packet + 7); + assert_se(optval == packet + 11); - pos = 3; - outlen -= 3; - out = &result[3]; + pos = 7; + outlen -= 7; + out = &result[pos]; assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen, optval) >= 0); @@ -206,7 +219,7 @@ static int test_option(sd_event *e) { assert_se(optcode == SD_DHCP6_OPTION_VENDOR_CLASS); assert_se(optlen == 9); - assert_se(optval == packet + 18); + assert_se(optval == packet + 22); assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen, optval) >= 0); From 4af39cb8ecdf55fc0238bf3149696946f6f28fca Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Thu, 23 Sep 2021 16:30:40 +0900 Subject: [PATCH 04/15] sd-dhcp6-client: make dhcp6_option_parse_status() also parse error message This also introduce dhcp6_option_parse_ia_options(). Currently, it is assumed that each IA address or PD prefix may contain a status sub-option. But it is not prohibited that other sub-options or multiple status options are contained. --- src/libsystemd-network/dhcp6-internal.h | 2 +- src/libsystemd-network/dhcp6-option.c | 111 ++++++++++++++------- src/libsystemd-network/sd-dhcp6-client.c | 22 ++-- src/libsystemd-network/test-dhcp6-client.c | 61 ++++++----- 4 files changed, 122 insertions(+), 74 deletions(-) diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 8bcc826f42..dfb0734520 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -109,7 +109,7 @@ int dhcp6_option_parse( uint16_t *ret_option_code, size_t *ret_option_data_len, const uint8_t **ret_option_data); -int dhcp6_option_parse_status(DHCP6Option *option, size_t len); +int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message); int dhcp6_option_parse_ia(sd_dhcp6_client *client, DHCP6Option *iaoption, be32_t iaid, DHCP6IA *ia, uint16_t *ret_status_code); int dhcp6_option_parse_ip6addrs(const uint8_t *optval, uint16_t optlen, struct in6_addr **addrs, size_t count); diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 781d391c0c..35950386b0 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -14,17 +14,12 @@ #include "dhcp6-lease-internal.h" #include "dhcp6-protocol.h" #include "dns-domain.h" +#include "escape.h" #include "memory-util.h" #include "sparse-endian.h" #include "strv.h" #include "unaligned.h" -typedef struct DHCP6StatusOption { - struct DHCP6Option option; - be16_t status; - char msg[]; -} _packed_ DHCP6StatusOption; - typedef struct DHCP6AddressOption { struct DHCP6Option option; struct iaaddr iaaddr; @@ -407,14 +402,65 @@ int dhcp6_option_parse( return 0; } -int dhcp6_option_parse_status(DHCP6Option *option, size_t len) { - DHCP6StatusOption *statusopt = (DHCP6StatusOption *)option; +int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message) { + assert(data); - if (len < sizeof(DHCP6StatusOption) || - be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(DHCP6StatusOption)) - return -ENOBUFS; + if (data_len < sizeof(uint16_t)) + return -EBADMSG; - return be16toh(statusopt->status); + if (ret_status_message) { + char *msg; + + /* The status message MUST NOT be null-terminated. See section 21.13 of RFC8415. + * Let's escape unsafe characters for safety. */ + msg = cescape_length((const char*) (data + sizeof(uint16_t)), data_len - sizeof(uint16_t)); + if (!msg) + return -ENOMEM; + + *ret_status_message = msg; + } + + return unaligned_read_be16(data); +} + +static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t *buf, size_t buflen) { + int r; + + assert(buf); + + for(size_t offset = 0; offset < buflen;) { + const uint8_t *data; + size_t data_len; + uint16_t code; + + r = dhcp6_option_parse(buf, buflen, &offset, &code, &data_len, &data); + if (r < 0) + return r; + + switch(code) { + case SD_DHCP6_OPTION_STATUS_CODE: { + _cleanup_free_ char *msg = NULL; + + r = dhcp6_option_parse_status(data, data_len, &msg); + if (r == -ENOMEM) + return r; + if (r < 0) + /* Let's log but ignore the invalid status option. */ + log_dhcp6_client_errno(client, r, + "Received an IA address or PD prefix option with an invalid status sub option, ignoring: %m"); + else if (r > 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received an IA address or PD prefix option with non-zero status: %s%s%s", + strempty(msg), isempty(msg) ? "" : ": ", + dhcp6_message_status_to_string(r)); + break; + } + default: + log_dhcp6_client(client, "Received an unknown sub option %u in IA address or PD prefix, ignoring.", code); + } + } + + return 0; } static int dhcp6_option_parse_address(sd_dhcp6_client *client, DHCP6Option *option, DHCP6IA *ia, uint32_t *ret_lifetime_valid) { @@ -435,14 +481,10 @@ static int dhcp6_option_parse_address(sd_dhcp6_client *client, DHCP6Option *opti "preferred lifetime %"PRIu32" > valid lifetime %"PRIu32, lt_pref, lt_valid); - if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*addr_option)) { - r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*addr_option)); + if (be16toh(option->len) + offsetof(DHCP6Option, data) > offsetof(DHCP6AddressOption, options)) { + r = dhcp6_option_parse_ia_options(client, option->data + sizeof(struct iaaddr), be16toh(option->len) - sizeof(struct iaaddr)); if (r < 0) return r; - if (r > 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "Non-zero status code '%s' for address is received", - dhcp6_message_status_to_string(r)); } addr = new0(DHCP6Address, 1); @@ -477,14 +519,10 @@ static int dhcp6_option_parse_pdprefix(sd_dhcp6_client *client, DHCP6Option *opt "preferred lifetime %"PRIu32" > valid lifetime %"PRIu32, lt_pref, lt_valid); - if (be16toh(option->len) + offsetof(DHCP6Option, data) > sizeof(*pdprefix_option)) { - r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*pdprefix_option)); + if (be16toh(option->len) + offsetof(DHCP6Option, data) > offsetof(DHCP6PDPrefixOption, options)) { + r = dhcp6_option_parse_ia_options(client, option->data + sizeof(struct iapdprefix), be16toh(option->len) - sizeof(struct iapdprefix)); if (r < 0) return r; - if (r > 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "Non-zero status code '%s' for PD prefix is received", - dhcp6_message_status_to_string(r)); } prefix = new0(DHCP6Address, 1); @@ -511,9 +549,9 @@ int dhcp6_option_parse_ia( uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX; uint16_t iatype, optlen; size_t iaaddr_offset; - int r = 0, status; size_t i, len; uint16_t opt; + int r; assert_return(ia, -EINVAL); assert_return(!ia->addresses, -EINVAL); @@ -636,24 +674,25 @@ int dhcp6_option_parse_ia( break; - case SD_DHCP6_OPTION_STATUS_CODE: + case SD_DHCP6_OPTION_STATUS_CODE: { + _cleanup_free_ char *msg = NULL; - status = dhcp6_option_parse_status(option, optlen + offsetof(DHCP6Option, data)); - if (status < 0) - return status; - - if (status > 0) { + r = dhcp6_option_parse_status(option->data, optlen, &msg); + if (r < 0) + return r; + if (r > 0) { if (ret_status_code) - *ret_status_code = status; + *ret_status_code = r; - log_dhcp6_client(client, "IA status %s", - dhcp6_message_status_to_string(status)); + log_dhcp6_client(client, + "Received an IA option with non-zero status: %s%s%s", + strempty(msg), isempty(msg) ? "" : ": ", + dhcp6_message_status_to_string(r)); return 0; } - break; - + } default: log_dhcp6_client(client, "Unknown IA option %d", opt); break; diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 298a89b086..8955aedc1f 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -1126,7 +1126,6 @@ static int client_parse_message( while (pos < len) { DHCP6Option *option = (DHCP6Option *) &message->options[pos]; uint16_t optcode, optlen; - int status; uint8_t *optval; if (len < pos + offsetof(DHCP6Option, data)) @@ -1176,18 +1175,21 @@ static int client_parse_message( break; - case SD_DHCP6_OPTION_STATUS_CODE: - status = dhcp6_option_parse_status(option, optlen + sizeof(DHCP6Option)); - if (status < 0) - return status; + case SD_DHCP6_OPTION_STATUS_CODE: { + _cleanup_free_ char *msg = NULL; - if (status > 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s Status %s", + 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), - dhcp6_message_status_to_string(status)); - + strempty(msg), isempty(msg) ? "" : ": ", + dhcp6_message_status_to_string(r)); break; - + } case SD_DHCP6_OPTION_IA_NA: if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { log_dhcp6_client(client, "Ignoring IA NA option in information requesting mode."); diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 35aaa2a997..fb948ea8a9 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -249,7 +249,7 @@ static int test_option_status(sd_event *e) { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x01, 0x02, 0x03, 0x04, 0x0a, 0x0b, 0x0c, 0x0d, - /* status option */ + /* IA address status option */ 0x00, 0x0d, 0x00, 0x02, 0x00, 0x01, }; static const uint8_t option3[] = { @@ -261,7 +261,7 @@ static int test_option_status(sd_event *e) { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x01, 0x02, 0x03, 0x04, 0x0a, 0x0b, 0x0c, 0x0d, - /* status option */ + /* IA address status option */ 0x00, 0x0d, 0x00, 0x08, 0x00, 0x00, 'f', 'o', 'o', 'b', 'a', 'r', }; @@ -275,7 +275,7 @@ static int test_option_status(sd_event *e) { 0x80, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* status option */ + /* PD prefix status option */ 0x00, 0x0d, 0x00, 0x02, 0x00, 0x00, }; static const uint8_t option5[] = { @@ -288,7 +288,7 @@ static int test_option_status(sd_event *e) { 0x80, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* status option */ + /* PD prefix status option */ 0x00, 0x0d, 0x00, 0x02, 0x00, 0x00, /* IA PD Prefix #2 */ 0x00, 0x1a, 0x00, 0x1f, @@ -296,11 +296,13 @@ static int test_option_status(sd_event *e) { 0x80, 0x20, 0x01, 0x0d, 0xb8, 0xc0, 0x0l, 0xd0, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* PD prefix status option */ 0x00, 0x0d, 0x00, 0x02, 0x00, 0x00, }; DHCP6Option *option; DHCP6IA ia, pd; be32_t iaid; + uint16_t status; int r = 0; log_debug("/* %s */", __func__); @@ -308,62 +310,67 @@ static int test_option_status(sd_event *e) { memcpy(&iaid, option1 + 4, sizeof(iaid)); zero(ia); - option = (DHCP6Option *)option1; + option = (DHCP6Option*) option1; assert_se(sizeof(option1) == sizeof(DHCP6Option) + be16toh(option->len)); r = dhcp6_option_parse_ia(NULL, option, 0, &ia, NULL); assert_se(r == -ENOANO); - r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, NULL); + r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, &status); assert_se(r == 0); - assert_se(ia.addresses == NULL); + assert_se(status == 1); + assert_se(!ia.addresses); option->len = htobe16(17); - r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, NULL); + r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, &status); assert_se(r == -ENOBUFS); - assert_se(ia.addresses == NULL); + assert_se(!ia.addresses); option->len = htobe16(sizeof(DHCP6Option)); - r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, NULL); + r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, &status); assert_se(r == -ENOBUFS); - assert_se(ia.addresses == NULL); + assert_se(!ia.addresses); zero(ia); - option = (DHCP6Option *)option2; + option = (DHCP6Option*) option2; assert_se(sizeof(option2) == sizeof(DHCP6Option) + be16toh(option->len)); - r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, NULL); - assert_se(r >= 0); - assert_se(ia.addresses == NULL); + r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, &status); + assert_se(r > 0); + assert_se(status == 0); + assert_se(!ia.addresses); zero(ia); - option = (DHCP6Option *)option3; + option = (DHCP6Option*) option3; assert_se(sizeof(option3) == sizeof(DHCP6Option) + be16toh(option->len)); - r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, NULL); - assert_se(r >= 0); - assert_se(ia.addresses != NULL); + r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, &status); + assert_se(r > 0); + assert_se(status == 0); + assert_se(ia.addresses); dhcp6_lease_free_ia(&ia); zero(pd); - option = (DHCP6Option *)option4; + option = (DHCP6Option*) option4; assert_se(sizeof(option4) == sizeof(DHCP6Option) + be16toh(option->len)); - r = dhcp6_option_parse_ia(NULL, option, iaid, &pd, NULL); - assert_se(r >= 0); - assert_se(pd.addresses != NULL); + r = dhcp6_option_parse_ia(NULL, option, iaid, &pd, &status); + assert_se(r > 0); + assert_se(status == 0); + assert_se(pd.addresses); assert_se(memcmp(&pd.ia_pd.id, &option4[4], 4) == 0); assert_se(memcmp(&pd.ia_pd.lifetime_t1, &option4[8], 4) == 0); assert_se(memcmp(&pd.ia_pd.lifetime_t2, &option4[12], 4) == 0); dhcp6_lease_free_ia(&pd); zero(pd); - option = (DHCP6Option *)option5; + option = (DHCP6Option*) option5; assert_se(sizeof(option5) == sizeof(DHCP6Option) + be16toh(option->len)); - r = dhcp6_option_parse_ia(NULL, option, iaid, &pd, NULL); - assert_se(r >= 0); - assert_se(pd.addresses != NULL); + r = dhcp6_option_parse_ia(NULL, option, iaid, &pd, &status); + assert_se(r > 0); + assert_se(status == 0); + assert_se(pd.addresses); dhcp6_lease_free_ia(&pd); return 0; From 8a8955507af363c31297bbc5df79852db4ad39d6 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 Sep 2021 00:50:01 +0900 Subject: [PATCH 05/15] sd-dhcp6-client: modernize dhcp6_option_parse_ia() This makes - the function not update the arguments for storing results on error, - use dhcp6_option_parse() to parse sub options, - ignore all errors, except for -ENOMEM, in parsing sub options, - update log messages. --- src/libsystemd-network/dhcp6-internal.h | 8 +- src/libsystemd-network/dhcp6-option.c | 363 ++++++++++----------- src/libsystemd-network/sd-dhcp6-client.c | 77 +++-- src/libsystemd-network/test-dhcp6-client.c | 40 +-- 4 files changed, 240 insertions(+), 248 deletions(-) diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index dfb0734520..5c24692233 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -110,7 +110,13 @@ int dhcp6_option_parse( size_t *ret_option_data_len, const uint8_t **ret_option_data); int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message); -int dhcp6_option_parse_ia(sd_dhcp6_client *client, DHCP6Option *iaoption, be32_t iaid, DHCP6IA *ia, uint16_t *ret_status_code); +int dhcp6_option_parse_ia( + sd_dhcp6_client *client, + be32_t iaid, + uint16_t option_code, + size_t option_data_len, + const uint8_t *option_data, + DHCP6IA *ret); int dhcp6_option_parse_ip6addrs(const uint8_t *optval, uint16_t optlen, struct in6_addr **addrs, size_t count); int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen, diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 35950386b0..3dd1cb4956 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -20,18 +20,6 @@ #include "strv.h" #include "unaligned.h" -typedef struct DHCP6AddressOption { - struct DHCP6Option option; - struct iaaddr iaaddr; - uint8_t options[]; -} _packed_ DHCP6AddressOption; - -typedef struct DHCP6PDPrefixOption { - struct DHCP6Option option; - struct iapdprefix iapdprefix; - uint8_t options[]; -} _packed_ DHCP6PDPrefixOption; - #define DHCP6_OPTION_IA_NA_LEN (sizeof(struct ia_na)) #define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd)) #define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta)) @@ -463,279 +451,274 @@ static int dhcp6_option_parse_ia_options(sd_dhcp6_client *client, const uint8_t return 0; } -static int dhcp6_option_parse_address(sd_dhcp6_client *client, DHCP6Option *option, DHCP6IA *ia, uint32_t *ret_lifetime_valid) { - DHCP6AddressOption *addr_option = (DHCP6AddressOption *)option; - DHCP6Address *addr; +static int dhcp6_option_parse_ia_address(sd_dhcp6_client *client, const uint8_t *data, size_t len, DHCP6Address **ret) { uint32_t lt_valid, lt_pref; + DHCP6Address *a; int r; - if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*addr_option)) - return -ENOBUFS; + assert(data); + assert(ret); - lt_valid = be32toh(addr_option->iaaddr.lifetime_valid); - lt_pref = be32toh(addr_option->iaaddr.lifetime_preferred); + if (len < sizeof(struct iaaddr)) + return -EBADMSG; - if (lt_valid == 0 || lt_pref > lt_valid) + lt_valid = be32toh(((const struct iaaddr*) data)->lifetime_valid); + lt_pref = be32toh(((const struct iaaddr*) data)->lifetime_preferred); + + if (lt_valid == 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "Valid lifetime of an IA address is zero or " - "preferred lifetime %"PRIu32" > valid lifetime %"PRIu32, + "Received an IA address with zero valid lifetime, ignoring."); + if (lt_pref > lt_valid) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received an IA address with preferred lifetime %"PRIu32 + " larger than valid lifetime %"PRIu32", ignoring.", lt_pref, lt_valid); - if (be16toh(option->len) + offsetof(DHCP6Option, data) > offsetof(DHCP6AddressOption, options)) { - r = dhcp6_option_parse_ia_options(client, option->data + sizeof(struct iaaddr), be16toh(option->len) - sizeof(struct iaaddr)); + if (len > sizeof(struct iaaddr)) { + r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iaaddr), len - sizeof(struct iaaddr)); if (r < 0) return r; } - addr = new0(DHCP6Address, 1); - if (!addr) + a = new(DHCP6Address, 1); + if (!a) return -ENOMEM; - LIST_INIT(addresses, addr); - memcpy(&addr->iaaddr, option->data, sizeof(addr->iaaddr)); - - LIST_PREPEND(addresses, ia->addresses, addr); - - *ret_lifetime_valid = be32toh(addr->iaaddr.lifetime_valid); + LIST_INIT(addresses, a); + memcpy(&a->iaaddr, data, sizeof(struct iaaddr)); + *ret = a; return 0; } -static int dhcp6_option_parse_pdprefix(sd_dhcp6_client *client, DHCP6Option *option, DHCP6IA *ia, uint32_t *ret_lifetime_valid) { - DHCP6PDPrefixOption *pdprefix_option = (DHCP6PDPrefixOption *)option; - DHCP6Address *prefix; +static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, const uint8_t *data, size_t len, DHCP6Address **ret) { uint32_t lt_valid, lt_pref; + DHCP6Address *a; int r; - if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*pdprefix_option)) - return -ENOBUFS; + if (len < sizeof(struct iapdprefix)) + return -ENOMSG; - lt_valid = be32toh(pdprefix_option->iapdprefix.lifetime_valid); - lt_pref = be32toh(pdprefix_option->iapdprefix.lifetime_preferred); + lt_valid = be32toh(((const struct iapdprefix*) data)->lifetime_valid); + lt_pref = be32toh(((const struct iapdprefix*) data)->lifetime_preferred); - if (lt_valid == 0 || lt_pref > lt_valid) + if (lt_valid == 0) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "Valid lifetieme of a PD prefix is zero or " - "preferred lifetime %"PRIu32" > valid lifetime %"PRIu32, + "Received a PD prefix with zero valid lifetime, ignoring."); + if (lt_pref > lt_valid) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received a PD prefix with preferred lifetime %"PRIu32 + " larger than valid lifetime %"PRIu32", ignoring.", lt_pref, lt_valid); - if (be16toh(option->len) + offsetof(DHCP6Option, data) > offsetof(DHCP6PDPrefixOption, options)) { - r = dhcp6_option_parse_ia_options(client, option->data + sizeof(struct iapdprefix), be16toh(option->len) - sizeof(struct iapdprefix)); + if (len > sizeof(struct iapdprefix)) { + r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iapdprefix), len - sizeof(struct iapdprefix)); if (r < 0) return r; } - prefix = new0(DHCP6Address, 1); - if (!prefix) + a = new(DHCP6Address, 1); + if (!a) return -ENOMEM; - LIST_INIT(addresses, prefix); - memcpy(&prefix->iapdprefix, option->data, sizeof(prefix->iapdprefix)); - - LIST_PREPEND(addresses, ia->addresses, prefix); - - *ret_lifetime_valid = be32toh(prefix->iapdprefix.lifetime_valid); + LIST_INIT(addresses, a); + memcpy(&a->iapdprefix, data, sizeof(struct iapdprefix)); + *ret = a; return 0; } int dhcp6_option_parse_ia( sd_dhcp6_client *client, - DHCP6Option *iaoption, be32_t iaid, - DHCP6IA *ia, - uint16_t *ret_status_code) { + uint16_t option_code, + size_t option_data_len, + const uint8_t *option_data, + DHCP6IA *ret) { - uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX; - uint16_t iatype, optlen; - size_t iaaddr_offset; - size_t i, len; - uint16_t opt; + _cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {}; + uint32_t lt_t1, lt_t2, lt_min = UINT32_MAX; + be32_t received_iaid; + size_t offset; int r; - assert_return(ia, -EINVAL); - assert_return(!ia->addresses, -EINVAL); + assert(IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA, SD_DHCP6_OPTION_IA_PD)); + assert(option_data); + assert(ret); - iatype = be16toh(iaoption->code); - len = be16toh(iaoption->len); + /* This will return the following: + * -ENOMEM: memory allocation error, + * -ENOANO: unmatching IAID, + * -EINVAL: non-zero status code, or invalid lifetime, + * -EBADMSG: invalid message format, + * -ENODATA: no valid address or PD prefix, + * 0: success. */ - switch (iatype) { + switch (option_code) { case SD_DHCP6_OPTION_IA_NA: - if (len < DHCP6_OPTION_IA_NA_LEN) - return -ENOBUFS; + if (option_data_len < DHCP6_OPTION_IA_NA_LEN) + return -EBADMSG; - /* According to RFC8415, IAs which do not match the client's IAID should be ignored, - * but not necessary to ignore or refuse the whole message. */ - if (((const struct ia_na*) iaoption->data)->id != iaid) - /* ENOANO indicates the option should be ignored. */ - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO), - "Received an IA_NA option with a different IAID " - "from the one chosen by the client, ignoring."); - - iaaddr_offset = DHCP6_OPTION_IA_NA_LEN; - memcpy(&ia->ia_na, iaoption->data, sizeof(ia->ia_na)); - - lt_t1 = be32toh(ia->ia_na.lifetime_t1); - lt_t2 = be32toh(ia->ia_na.lifetime_t2); - - if (lt_t1 > lt_t2) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "IA NA T1 %"PRIu32"sec > T2 %"PRIu32"sec", - lt_t1, lt_t2); + offset = DHCP6_OPTION_IA_NA_LEN; + received_iaid = ((const struct ia_na*) option_data)->id; + lt_t1 = be32toh(((const struct ia_na*) option_data)->lifetime_t1); + lt_t2 = be32toh(((const struct ia_na*) option_data)->lifetime_t2); break; case SD_DHCP6_OPTION_IA_PD: - if (len < sizeof(ia->ia_pd)) - return -ENOBUFS; + if (option_data_len < DHCP6_OPTION_IA_PD_LEN) + return -EBADMSG; - /* According to RFC8415, IAs which do not match the client's IAID should be ignored, - * but not necessary to ignore or refuse the whole message. */ - if (((const struct ia_pd*) iaoption->data)->id != iaid) - /* ENOANO indicates the option should be ignored. */ - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO), - "Received an IA_PD option with a different IAID " - "from the one chosen by the client, ignoring."); - - iaaddr_offset = sizeof(ia->ia_pd); - memcpy(&ia->ia_pd, iaoption->data, sizeof(ia->ia_pd)); - - lt_t1 = be32toh(ia->ia_pd.lifetime_t1); - lt_t2 = be32toh(ia->ia_pd.lifetime_t2); - - if (lt_t1 > lt_t2) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "IA PD T1 %"PRIu32"sec > T2 %"PRIu32"sec", - lt_t1, lt_t2); + offset = DHCP6_OPTION_IA_PD_LEN; + received_iaid = ((const struct ia_pd*) option_data)->id; + lt_t1 = be32toh(((const struct ia_pd*) option_data)->lifetime_t1); + lt_t2 = be32toh(((const struct ia_pd*) option_data)->lifetime_t2); break; case SD_DHCP6_OPTION_IA_TA: - if (len < DHCP6_OPTION_IA_TA_LEN) - return -ENOBUFS; + if (option_data_len < DHCP6_OPTION_IA_TA_LEN) + return -ENOMSG; - /* According to RFC8415, IAs which do not match the client's IAID should be ignored, - * but not necessary to ignore or refuse the whole message. */ - if (((const struct ia_ta*) iaoption->data)->id != iaid) - /* ENOANO indicates the option should be ignored. */ - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO), - "Received an IA_TA option with a different IAID " - "from the one chosen by the client, ignoring."); - - iaaddr_offset = DHCP6_OPTION_IA_TA_LEN; - memcpy(&ia->ia_ta, iaoption->data, sizeof(ia->ia_ta)); + offset = DHCP6_OPTION_IA_TA_LEN; + received_iaid = ((const struct ia_ta*) option_data)->id; + lt_t1 = lt_t2 = 0; /* No lifetime for IA_TA. */ break; default: - return -EINVAL; + assert_not_reached(); } - ia->type = iatype; - i = iaaddr_offset; + /* According to RFC8415, IAs which do not match the client's IAID should be ignored, + * but not necessary to ignore or refuse the whole message. */ + if (received_iaid != iaid) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO), + "Received an IA option with a different IAID " + "from the one chosen by the client, ignoring."); - while (i < len) { - DHCP6Option *option = (DHCP6Option *)&iaoption->data[i]; + if (lt_t1 > lt_t2) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received an IA option with T1 %"PRIu32"sec > T2 %"PRIu32"sec, ignoring.", + lt_t1, lt_t2); - if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len)) - return -ENOBUFS; + for (; offset < option_data_len;) { + const uint8_t *subdata; + size_t subdata_len; + uint16_t subopt; - opt = be16toh(option->code); - optlen = be16toh(option->len); + r = dhcp6_option_parse(option_data, option_data_len, &offset, &subopt, &subdata_len, &subdata); + if (r < 0) + return r; - switch (opt) { - case SD_DHCP6_OPTION_IAADDR: + switch (subopt) { + case SD_DHCP6_OPTION_IAADDR: { + DHCP6Address *a; - if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "IA Address option not in IA NA or TA option"); + if (!IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) { + log_dhcp6_client(client, "Received an IA_PD option with an IA address, ignoring."); + continue; + } - r = dhcp6_option_parse_address(client, option, ia, <_valid); - if (r < 0 && r != -EINVAL) + r = dhcp6_option_parse_ia_address(client, subdata, subdata_len, &a); + if (r == -ENOMEM) return r; - if (r >= 0 && lt_valid < lt_min) - lt_min = lt_valid; + if (r < 0) + /* Ignore the sub-option on non-critical errors. */ + continue; + lt_min = MIN(lt_min, a->iaaddr.lifetime_valid); + LIST_PREPEND(addresses, ia.addresses, a); break; + } + case SD_DHCP6_OPTION_IA_PD_PREFIX: { + DHCP6Address *a; - case SD_DHCP6_OPTION_IA_PD_PREFIX: + if (option_code != SD_DHCP6_OPTION_IA_PD) { + log_dhcp6_client(client, "Received an IA_NA or IA_TA option with an PD prefix, ignoring"); + continue; + } - if (ia->type != SD_DHCP6_OPTION_IA_PD) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), - "IA PD Prefix option not in IA PD option"); - - r = dhcp6_option_parse_pdprefix(client, option, ia, <_valid); - if (r < 0 && r != -EINVAL) + r = dhcp6_option_parse_ia_pdprefix(client, subdata, subdata_len, &a); + if (r == -ENOMEM) return r; - if (r >= 0 && lt_valid < lt_min) - lt_min = lt_valid; + if (r < 0) + /* Ignore the sub-option on non-critical errors. */ + continue; + lt_min = MIN(lt_min, a->iapdprefix.lifetime_valid); + LIST_PREPEND(addresses, ia.addresses, a); break; - + } case SD_DHCP6_OPTION_STATUS_CODE: { _cleanup_free_ char *msg = NULL; - r = dhcp6_option_parse_status(option->data, optlen, &msg); - if (r < 0) + r = dhcp6_option_parse_status(subdata, subdata_len, &msg); + if (r == -ENOMEM) return r; - if (r > 0) { - if (ret_status_code) - *ret_status_code = r; - - log_dhcp6_client(client, - "Received an IA option with non-zero status: %s%s%s", - strempty(msg), isempty(msg) ? "" : ": ", - dhcp6_message_status_to_string(r)); - - return 0; - } + if (r < 0) + log_dhcp6_client_errno(client, r, + "Received an IA option with an invalid status sub option, ignoring: %m"); + else if (r > 0) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), + "Received an IA option with non-zero status: %s%s%s", + strempty(msg), isempty(msg) ? "" : ": ", + dhcp6_message_status_to_string(r)); break; } default: - log_dhcp6_client(client, "Unknown IA option %d", opt); - break; + log_dhcp6_client(client, "Received an IA option with an unknown sub-option %u, ignoring", subopt); } - - i += sizeof(*option) + optlen; } - switch(iatype) { + if (!ia.addresses) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENODATA), + "Received an IA option without valid IA addresses or PD prefixes, ignoring."); + + if (IN_SET(option_code, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_PD) && + lt_t1 == 0 && lt_t2 == 0 && lt_min != UINT32_MAX) { + lt_t1 = lt_min / 2; + lt_t2 = lt_min / 10 * 8; + + log_dhcp6_client(client, "Received an IA option with both T1 and T2 equal to zero. " + "Adjusting them based on the minimum valid lifetime of IA addresses or PD prefixes: " + "T1=%"PRIu32"sec, T2=%"PRIu32"sec", lt_t1, lt_t2); + } + + switch(option_code) { case SD_DHCP6_OPTION_IA_NA: - if (ia->ia_na.lifetime_t1 == 0 && ia->ia_na.lifetime_t2 == 0 && lt_min != UINT32_MAX) { - lt_t1 = lt_min / 2; - lt_t2 = lt_min / 10 * 8; - ia->ia_na.lifetime_t1 = htobe32(lt_t1); - ia->ia_na.lifetime_t2 = htobe32(lt_t2); - - log_dhcp6_client(client, "Computed IA NA T1 %"PRIu32"sec and T2 %"PRIu32"sec as both were zero", - lt_t1, lt_t2); - } - + *ret = (DHCP6IA) { + .type = option_code, + .ia_na.id = iaid, + .ia_na.lifetime_t1 = htobe32(lt_t1), + .ia_na.lifetime_t2 = htobe32(lt_t2), + .addresses = TAKE_PTR(ia.addresses), + }; + break; + case SD_DHCP6_OPTION_IA_TA: + *ret = (DHCP6IA) { + .type = option_code, + .ia_ta.id = iaid, + .addresses = TAKE_PTR(ia.addresses), + }; break; - case SD_DHCP6_OPTION_IA_PD: - if (ia->ia_pd.lifetime_t1 == 0 && ia->ia_pd.lifetime_t2 == 0 && lt_min != UINT32_MAX) { - lt_t1 = lt_min / 2; - lt_t2 = lt_min / 10 * 8; - ia->ia_pd.lifetime_t1 = htobe32(lt_t1); - ia->ia_pd.lifetime_t2 = htobe32(lt_t2); - - log_dhcp6_client(client, "Computed IA PD T1 %"PRIu32"sec and T2 %"PRIu32"sec as both were zero", - lt_t1, lt_t2); - } - + *ret = (DHCP6IA) { + .type = option_code, + .ia_pd.id = iaid, + .ia_pd.lifetime_t1 = htobe32(lt_t1), + .ia_pd.lifetime_t2 = htobe32(lt_t2), + .addresses = TAKE_PTR(ia.addresses), + }; break; - default: - break; + assert_not_reached(); } - if (ret_status_code) - *ret_status_code = 0; - - return 1; + return 0; } int dhcp6_option_parse_ip6addrs(const uint8_t *optval, uint16_t optlen, diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 8955aedc1f..5063bfa492 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -1109,8 +1109,7 @@ 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; + uint32_t lt_t1 = UINT32_MAX, lt_t2 = UINT32_MAX; usec_t irt = IRT_DEFAULT; bool clientid = false; size_t pos = 0; @@ -1138,6 +1137,8 @@ static int client_parse_message( if (len < pos + offsetof(DHCP6Option, data) + optlen) return -ENOBUFS; + pos += offsetof(DHCP6Option, data) + optlen; + switch (optcode) { case SD_DHCP6_OPTION_CLIENTID: if (clientid) @@ -1190,50 +1191,60 @@ static int client_parse_message( dhcp6_message_status_to_string(r)); break; } - case SD_DHCP6_OPTION_IA_NA: + case SD_DHCP6_OPTION_IA_NA: { + _cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {}; + 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, option, client->ia_pd.ia_na.id, &lease->ia, &ia_na_status); - if (r < 0 && r != -ENOANO) + r = dhcp6_option_parse_ia(client, client->ia_pd.ia_na.id, optcode, optlen, optval, &ia); + if (r == -ENOMEM) return r; + if (r < 0) + continue; - if (ia_na_status == DHCP6_STATUS_NO_ADDRS_AVAIL) { - pos += offsetof(DHCP6Option, data) + optlen; + if (lease->ia.addresses) { + log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring."); continue; } - if (lease->ia.addresses) { - lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1)); - lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t2)); - } + lease->ia = ia; + ia = (DHCP6IA) {}; + + lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1)); + lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t2)); break; + } + case SD_DHCP6_OPTION_IA_PD: { + _cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {}; - case SD_DHCP6_OPTION_IA_PD: 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, option, client->ia_pd.ia_pd.id, &lease->pd, &ia_pd_status); - if (r < 0 && r != -ENOANO) + r = dhcp6_option_parse_ia(client, client->ia_pd.ia_pd.id, optcode, optlen, optval, &ia); + if (r == -ENOMEM) return r; + if (r < 0) + continue; - if (ia_pd_status == DHCP6_STATUS_NO_PREFIX_AVAIL) { - pos += offsetof(DHCP6Option, data) + optlen; + if (lease->pd.addresses) { + log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring."); continue; } - if (lease->pd.addresses) { - lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1)); - lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2)); - } + lease->pd = ia; + ia = (DHCP6IA) {}; + + lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1)); + lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2)); break; - + } case SD_DHCP6_OPTION_RAPID_COMMIT: r = dhcp6_lease_set_rapid_commit(lease); if (r < 0) @@ -1283,13 +1294,8 @@ static int client_parse_message( irt = unaligned_read_be32((be32_t *) optval) * USEC_PER_SEC; break; } - - pos += offsetof(DHCP6Option, data) + optlen; } - if (ia_na_status > 0 && ia_pd_status > 0) - return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "No IA_PD prefix or IA_NA address received. Ignoring."); - if (!clientid) return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "%s has incomplete options", dhcp6_message_type_to_string(message->type)); @@ -1299,16 +1305,19 @@ static int client_parse_message( 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.addresses) { - lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1); - lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2); - } + if (!lease->ia.addresses && !lease->pd.addresses) + return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL), "No IA_PD prefix or IA_NA address received. Ignoring."); - if (lease->pd.addresses) { - lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1); - lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2); + if (lease->ia.addresses) { + lease->ia.ia_na.lifetime_t1 = htobe32(lt_t1); + lease->ia.ia_na.lifetime_t2 = htobe32(lt_t2); + } + + if (lease->pd.addresses) { + lease->pd.ia_pd.lifetime_t1 = htobe32(lt_t1); + lease->pd.ia_pd.lifetime_t2 = htobe32(lt_t2); + } } client->information_refresh_time_usec = MAX(irt, IRT_MINIMUM); diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index fb948ea8a9..d77b39b0d7 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -302,7 +302,6 @@ static int test_option_status(sd_event *e) { DHCP6Option *option; DHCP6IA ia, pd; be32_t iaid; - uint16_t status; int r = 0; log_debug("/* %s */", __func__); @@ -313,40 +312,37 @@ static int test_option_status(sd_event *e) { option = (DHCP6Option*) option1; assert_se(sizeof(option1) == sizeof(DHCP6Option) + be16toh(option->len)); - r = dhcp6_option_parse_ia(NULL, option, 0, &ia, NULL); + r = dhcp6_option_parse_ia(NULL, 0, be16toh(option->code), be16toh(option->len), option->data, &ia); assert_se(r == -ENOANO); - r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, &status); - assert_se(r == 0); - assert_se(status == 1); + r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia); + assert_se(r == -EINVAL); assert_se(!ia.addresses); option->len = htobe16(17); - r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, &status); - assert_se(r == -ENOBUFS); + r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia); + assert_se(r == -EBADMSG); assert_se(!ia.addresses); option->len = htobe16(sizeof(DHCP6Option)); - r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, &status); - assert_se(r == -ENOBUFS); + r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia); + assert_se(r == -EBADMSG); assert_se(!ia.addresses); zero(ia); option = (DHCP6Option*) option2; assert_se(sizeof(option2) == sizeof(DHCP6Option) + be16toh(option->len)); - r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, &status); - assert_se(r > 0); - assert_se(status == 0); + r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia); + assert_se(r == -ENODATA); assert_se(!ia.addresses); zero(ia); option = (DHCP6Option*) option3; assert_se(sizeof(option3) == sizeof(DHCP6Option) + be16toh(option->len)); - r = dhcp6_option_parse_ia(NULL, option, iaid, &ia, &status); - assert_se(r > 0); - assert_se(status == 0); + r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia); + assert_se(r >= 0); assert_se(ia.addresses); dhcp6_lease_free_ia(&ia); @@ -354,9 +350,8 @@ static int test_option_status(sd_event *e) { option = (DHCP6Option*) option4; assert_se(sizeof(option4) == sizeof(DHCP6Option) + be16toh(option->len)); - r = dhcp6_option_parse_ia(NULL, option, iaid, &pd, &status); - assert_se(r > 0); - assert_se(status == 0); + r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &pd); + assert_se(r >= 0); assert_se(pd.addresses); assert_se(memcmp(&pd.ia_pd.id, &option4[4], 4) == 0); assert_se(memcmp(&pd.ia_pd.lifetime_t1, &option4[8], 4) == 0); @@ -367,9 +362,8 @@ static int test_option_status(sd_event *e) { option = (DHCP6Option*) option5; assert_se(sizeof(option5) == sizeof(DHCP6Option) + be16toh(option->len)); - r = dhcp6_option_parse_ia(NULL, option, iaid, &pd, &status); - assert_se(r > 0); - assert_se(status == 0); + r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &pd); + assert_se(r >= 0); assert_se(pd.addresses); dhcp6_lease_free_ia(&pd); @@ -488,7 +482,7 @@ static int test_advertise_option(sd_event *e) { val = htobe32(120); assert_se(!memcmp(optval + 8, &val, sizeof(val))); - assert_se(dhcp6_option_parse_ia(NULL, option, iaid, &lease->ia, NULL) >= 0); + assert_se(dhcp6_option_parse_ia(NULL, iaid, optcode, optlen, optval, &lease->ia) >= 0); break; } @@ -685,7 +679,7 @@ static int test_client_verify_request(DHCP6Message *request, size_t len) { assert_se(!memcmp(optval + 8, &val, sizeof(val))); /* Then, this should refuse all addresses. */ - assert_se(dhcp6_option_parse_ia(NULL, option, test_iaid, &lease->ia, NULL) >= 0); + assert_se(dhcp6_option_parse_ia(NULL, test_iaid, optcode, optlen, optval, &lease->ia) == -ENODATA); break; From ad3c84204c76e03a0b9b761563f6cd8907515014 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 Sep 2021 01:24:52 +0900 Subject: [PATCH 06/15] sd-dhcp6-client: fix buffer size calculation in dhcp6_option_parse_ip6addrs() GREEDY_REALLOC() takes number of elements, not buffer size. This also rename dhcp6_option_parse_ip6addrs() to dhcp6_option_parse_addresses(). --- src/libsystemd-network/dhcp6-internal.h | 7 ++++-- src/libsystemd-network/dhcp6-option.c | 22 +++++++++++------- src/libsystemd-network/sd-dhcp6-lease.c | 30 +++++-------------------- 3 files changed, 24 insertions(+), 35 deletions(-) diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 5c24692233..ececddf7be 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -117,8 +117,11 @@ int dhcp6_option_parse_ia( size_t option_data_len, const uint8_t *option_data, DHCP6IA *ret); -int dhcp6_option_parse_ip6addrs(const uint8_t *optval, uint16_t optlen, - struct in6_addr **addrs, size_t count); +int dhcp6_option_parse_addresses( + const uint8_t *optval, + size_t optlen, + struct in6_addr **addrs, + size_t *count); int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen, char ***str_arr); int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char **str); diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 3dd1cb4956..f7afc0df33 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -721,20 +721,26 @@ int dhcp6_option_parse_ia( return 0; } -int dhcp6_option_parse_ip6addrs(const uint8_t *optval, uint16_t optlen, - struct in6_addr **addrs, size_t count) { +int dhcp6_option_parse_addresses( + const uint8_t *optval, + size_t optlen, + struct in6_addr **addrs, + size_t *count) { + + assert(optval); + assert(addrs); + assert(count); if (optlen == 0 || optlen % sizeof(struct in6_addr) != 0) - return -EINVAL; + return -EBADMSG; - if (!GREEDY_REALLOC(*addrs, count * sizeof(struct in6_addr) + optlen)) + if (!GREEDY_REALLOC(*addrs, *count + optlen / sizeof(struct in6_addr))) return -ENOMEM; - memcpy(*addrs + count, optval, optlen); + memcpy(*addrs + *count, optval, optlen); + *count += optlen / sizeof(struct in6_addr); - count += optlen / sizeof(struct in6_addr); - - return count; + return 0; } static int parse_domain(const uint8_t **data, uint16_t *len, char **out_domain) { diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 6375a22537..9c77b146c7 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -194,22 +194,13 @@ void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) { } int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { - int r; - assert_return(lease, -EINVAL); assert_return(optval, -EINVAL); - if (!optlen) + if (optlen == 0) return 0; - r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->dns, - lease->dns_count); - if (r < 0) - return r; - - lease->dns_count = r; - - return 0; + return dhcp6_option_parse_addresses(optval, optlen, &lease->dns, &lease->dns_count); } int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs) { @@ -281,12 +272,10 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { if (sublen != 16) return 0; - r = dhcp6_option_parse_ip6addrs(subval, sublen, &lease->ntp, lease->ntp_count); + r = dhcp6_option_parse_addresses(subval, sublen, &lease->ntp, &lease->ntp_count); if (r < 0) return r; - lease->ntp_count = r; - break; case DHCP6_NTP_SUBOPTION_SRV_FQDN: { @@ -307,12 +296,10 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { } int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { - int r; - assert_return(lease, -EINVAL); assert_return(optval, -EINVAL); - if (!optlen) + if (optlen == 0) return 0; if (lease->ntp || lease->ntp_fqdn) @@ -320,14 +307,7 @@ int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) /* Using deprecated SNTP information */ - r = dhcp6_option_parse_ip6addrs(optval, optlen, &lease->ntp, - lease->ntp_count); - if (r < 0) - return r; - - lease->ntp_count = r; - - return 0; + return dhcp6_option_parse_addresses(optval, optlen, &lease->ntp, &lease->ntp_count); } int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, From b27dcf081382d06decc657ed8c985cf071ad5c75 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 Sep 2021 03:49:14 +0900 Subject: [PATCH 07/15] sd-dhcp6-client: slightly modernize dhcp6_option_parse_domainname()/domainname_list() --- src/libsystemd-network/dhcp6-internal.h | 5 +- src/libsystemd-network/dhcp6-option.c | 67 ++++++++++++++----------- src/libsystemd-network/sd-dhcp6-lease.c | 6 +-- 3 files changed, 43 insertions(+), 35 deletions(-) diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index ececddf7be..4eb56aa8c9 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -122,9 +122,8 @@ int dhcp6_option_parse_addresses( size_t optlen, struct in6_addr **addrs, size_t *count); -int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen, - char ***str_arr); -int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char **str); +int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, char ***ret); +int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret); int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *address); int dhcp6_network_send_udp_socket(int s, struct in6_addr *address, diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index f7afc0df33..a868d0cd79 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -743,14 +743,20 @@ int dhcp6_option_parse_addresses( return 0; } -static int parse_domain(const uint8_t **data, uint16_t *len, char **out_domain) { - _cleanup_free_ char *ret = NULL; - const uint8_t *optval = *data; - uint16_t optlen = *len; - bool first = true; - size_t n = 0; +static int parse_domain(const uint8_t **data, size_t *len, char **ret) { + _cleanup_free_ char *domain = NULL; + const uint8_t *optval; + size_t optlen, n = 0; int r; + assert(data); + assert(*data); + assert(len); + assert(ret); + + optval = *data; + optlen = *len; + if (optlen <= 1) return -ENODATA; @@ -774,42 +780,44 @@ static int parse_domain(const uint8_t **data, uint16_t *len, char **out_domain) return -EMSGSIZE; /* Literal label */ - label = (const char *)optval; + label = (const char*) optval; optval += c; optlen -= c; - if (!GREEDY_REALLOC(ret, n + !first + DNS_LABEL_ESCAPED_MAX)) + if (!GREEDY_REALLOC(domain, n + (n != 0) + DNS_LABEL_ESCAPED_MAX)) return -ENOMEM; - if (first) - first = false; - else - ret[n++] = '.'; + if (n != 0) + domain[n++] = '.'; - r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX); + r = dns_label_escape(label, c, domain + n, DNS_LABEL_ESCAPED_MAX); if (r < 0) return r; n += r; } - if (n) { - if (!GREEDY_REALLOC(ret, n + 1)) + if (n > 0) { + if (!GREEDY_REALLOC(domain, n + 1)) return -ENOMEM; - ret[n] = 0; + + domain[n] = '\0'; } - *out_domain = TAKE_PTR(ret); + *ret = TAKE_PTR(domain); *data = optval; *len = optlen; return n; } -int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char **str) { +int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **ret) { _cleanup_free_ char *domain = NULL; int r; + assert(optval); + assert(ret); + r = parse_domain(&optval, &optlen, &domain); if (r < 0) return r; @@ -818,14 +826,16 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char * if (optlen != 0) return -EINVAL; - *str = TAKE_PTR(domain); + *ret = TAKE_PTR(domain); return 0; } -int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen, char ***str_arr) { - size_t idx = 0; +int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, char ***ret) { _cleanup_strv_free_ char **names = NULL; - int r; + int r, count = 0; + + assert(optval); + assert(ret); if (optlen <= 1) return -ENODATA; @@ -833,24 +843,23 @@ int dhcp6_option_parse_domainname_list(const uint8_t *optval, uint16_t optlen, c return -EINVAL; while (optlen > 0) { - _cleanup_free_ char *ret = NULL; + _cleanup_free_ char *name = NULL; - r = parse_domain(&optval, &optlen, &ret); + r = parse_domain(&optval, &optlen, &name); if (r < 0) return r; if (r == 0) continue; - r = strv_extend(&names, ret); + r = strv_consume(&names, TAKE_PTR(name)); if (r < 0) return r; - idx++; + count++; } - *str_arr = TAKE_PTR(names); - - return idx; + *ret = TAKE_PTR(names); + return count; } static sd_dhcp6_option* dhcp6_option_free(sd_dhcp6_option *i) { diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 9c77b146c7..3f416d6c6d 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -223,12 +223,12 @@ int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval, assert_return(lease, -EINVAL); assert_return(optval, -EINVAL); - if (!optlen) + if (optlen == 0) return 0; r = dhcp6_option_parse_domainname_list(optval, optlen, &domains); if (r < 0) - return 0; + return r; strv_free_and_replace(lease->domains, domains); lease->domains_count = r; @@ -283,7 +283,7 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { r = dhcp6_option_parse_domainname_list(subval, sublen, &servers); if (r < 0) - return 0; + return r; strv_free_and_replace(lease->ntp_fqdn, servers); lease->ntp_fqdn_count = r; From 3f8227bf830cc2b87ea9bce5394a71c186d12956 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 Sep 2021 13:34:14 +0900 Subject: [PATCH 08/15] sd-dhcp6-client: constify several arguments --- src/libsystemd-network/dhcp6-lease-internal.h | 10 ++++------ src/libsystemd-network/sd-dhcp6-lease.c | 12 +++++------- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index 391b4f1fa9..41b43ba7a4 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -50,12 +50,10 @@ int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit); int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid); int dhcp6_lease_get_pd_iaid(sd_dhcp6_lease *lease, be32_t *iaid); -int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen); -int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval, - size_t optlen); -int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen); -int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, - size_t optlen) ; +int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); +int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); +int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); +int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) ; int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_new(sd_dhcp6_lease **ret); diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 3f416d6c6d..1188c62ca1 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -193,7 +193,7 @@ void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) { lease->prefix_iter = lease->pd.addresses; } -int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { +int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { assert_return(lease, -EINVAL); assert_return(optval, -EINVAL); @@ -215,8 +215,7 @@ int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs) return -ENOENT; } -int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, uint8_t *optval, - size_t optlen) { +int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { int r; char **domains; @@ -248,7 +247,7 @@ int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains) { return -ENOENT; } -int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { +int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { int r; assert_return(lease, -EINVAL); @@ -295,7 +294,7 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { return 0; } -int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { +int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { assert_return(lease, -EINVAL); assert_return(optval, -EINVAL); @@ -335,8 +334,7 @@ int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn) { return -ENOENT; } -int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, - size_t optlen) { +int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { int r; char *fqdn; From 548c33d7124d7357802122ef29a293a1ffb25a04 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 Sep 2021 13:35:36 +0900 Subject: [PATCH 09/15] sd-dhcp6-client: use dhcp6_option_parse() in client_parse_message() --- src/libsystemd-network/sd-dhcp6-client.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 5063bfa492..a5eae6a55c 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -1112,7 +1112,6 @@ static int client_parse_message( uint32_t lt_t1 = UINT32_MAX, lt_t2 = UINT32_MAX; usec_t irt = IRT_DEFAULT; bool clientid = false; - size_t pos = 0; int r; assert(client); @@ -1121,23 +1120,14 @@ static int client_parse_message( assert(lease); len -= sizeof(DHCP6Message); + for (size_t offset = 0; offset < len;) { + uint16_t optcode; + size_t optlen; + const uint8_t *optval; - while (pos < len) { - DHCP6Option *option = (DHCP6Option *) &message->options[pos]; - uint16_t optcode, optlen; - uint8_t *optval; - - if (len < pos + offsetof(DHCP6Option, data)) - return -ENOBUFS; - - optcode = be16toh(option->code); - optlen = be16toh(option->len); - optval = option->data; - - if (len < pos + offsetof(DHCP6Option, data) + optlen) - return -ENOBUFS; - - pos += offsetof(DHCP6Option, data) + optlen; + r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval); + if (r < 0) + return r; switch (optcode) { case SD_DHCP6_OPTION_CLIENTID: From edeee50b3dfccc716ffee23cfc2f2a221ed09339 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 Sep 2021 14:05:29 +0900 Subject: [PATCH 10/15] sd-dhcp6-client: drop domains_count and ntp_fqdn_count --- src/libsystemd-network/dhcp6-lease-internal.h | 2 - src/libsystemd-network/dhcp6-option.c | 6 +- src/libsystemd-network/sd-dhcp6-lease.c | 70 ++++++++----------- src/libsystemd-network/test-dhcp6-client.c | 2 +- src/systemd/sd-dhcp6-lease.h | 10 +-- 5 files changed, 39 insertions(+), 51 deletions(-) diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index 41b43ba7a4..0b83ddfd64 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -28,11 +28,9 @@ struct sd_dhcp6_lease { struct in6_addr *dns; size_t dns_count; char **domains; - size_t domains_count; struct in6_addr *ntp; size_t ntp_count; char **ntp_fqdn; - size_t ntp_fqdn_count; char *fqdn; }; diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index a868d0cd79..0276ce693c 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -832,7 +832,7 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, size_t optlen, char **r int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, char ***ret) { _cleanup_strv_free_ char **names = NULL; - int r, count = 0; + int r; assert(optval); assert(ret); @@ -854,12 +854,10 @@ int dhcp6_option_parse_domainname_list(const uint8_t *optval, size_t optlen, cha r = strv_consume(&names, TAKE_PTR(name)); if (r < 0) return r; - - count++; } *ret = TAKE_PTR(names); - return count; + return 0; } static sd_dhcp6_option* dhcp6_option_free(sd_dhcp6_option *i) { diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 1188c62ca1..b69f59c84d 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -203,16 +203,15 @@ int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t opt return dhcp6_option_parse_addresses(optval, optlen, &lease->dns, &lease->dns_count); } -int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **addrs) { +int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret) { assert_return(lease, -EINVAL); - assert_return(addrs, -EINVAL); + assert_return(ret, -EINVAL); - if (lease->dns_count) { - *addrs = lease->dns; - return lease->dns_count; - } + if (!lease->dns) + return -ENOENT; - return -ENOENT; + *ret = lease->dns; + return lease->dns_count; } int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { @@ -230,21 +229,19 @@ int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t return r; strv_free_and_replace(lease->domains, domains); - lease->domains_count = r; - return r; + return 0; } -int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***domains) { +int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) { assert_return(lease, -EINVAL); - assert_return(domains, -EINVAL); + assert_return(ret, -EINVAL); - if (lease->domains_count) { - *domains = lease->domains; - return lease->domains_count; - } + if (!lease->domains) + return -ENOENT; - return -ENOENT; + *ret = lease->domains; + return strv_length(lease->domains); } int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { @@ -285,7 +282,6 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t opt return r; strv_free_and_replace(lease->ntp_fqdn, servers); - lease->ntp_fqdn_count = r; break; }} @@ -309,29 +305,26 @@ int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t op return dhcp6_option_parse_addresses(optval, optlen, &lease->ntp, &lease->ntp_count); } -int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, - const struct in6_addr **addrs) { +int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret) { assert_return(lease, -EINVAL); - assert_return(addrs, -EINVAL); + assert_return(ret, -EINVAL); - if (lease->ntp_count) { - *addrs = lease->ntp; - return lease->ntp_count; - } + if (!lease->ntp) + return -ENOENT; - return -ENOENT; + *ret = lease->ntp; + return lease->ntp_count; } -int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn) { +int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) { assert_return(lease, -EINVAL); - assert_return(ntp_fqdn, -EINVAL); + assert_return(ret, -EINVAL); - if (lease->ntp_fqdn_count) { - *ntp_fqdn = lease->ntp_fqdn; - return lease->ntp_fqdn_count; - } + if (!lease->ntp_fqdn) + return -ENOENT; - return -ENOENT; + *ret = lease->ntp_fqdn; + return strv_length(lease->ntp_fqdn); } int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { @@ -353,16 +346,15 @@ int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t op return free_and_replace(lease->fqdn, fqdn); } -int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **fqdn) { +int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) { assert_return(lease, -EINVAL); - assert_return(fqdn, -EINVAL); + assert_return(ret, -EINVAL); - if (lease->fqdn) { - *fqdn = lease->fqdn; - return 0; - } + if (!lease->fqdn) + return -ENOENT; - return -ENOENT; + *ret = lease->fqdn; + return 0; } static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) { diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index d77b39b0d7..e711e1d9f5 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -137,7 +137,7 @@ static int test_parse_domain(sd_event *e) { data = (uint8_t []) { 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0, 6, 'f', 'o', 'o', 'b', 'a', 'r', 0 }; r = dhcp6_option_parse_domainname_list(data, 21, &list); - assert_se(r == 2); + assert_se(r == 0); assert_se(list); assert_se(streq(list[0], "example.com")); assert_se(streq(list[1], "foobar")); diff --git a/src/systemd/sd-dhcp6-lease.h b/src/systemd/sd-dhcp6-lease.h index f77b31acf9..a10901e5cb 100644 --- a/src/systemd/sd-dhcp6-lease.h +++ b/src/systemd/sd-dhcp6-lease.h @@ -39,11 +39,11 @@ 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, 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, const struct in6_addr **addrs); -int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn); -int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **fqdn); +int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret); +int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret); +int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret); +int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret); +int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret); sd_dhcp6_lease *sd_dhcp6_lease_ref(sd_dhcp6_lease *lease); sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease); From 5cf67bb4072f149d0404398bfc359b068312ba28 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 Sep 2021 23:45:13 +0900 Subject: [PATCH 11/15] sd-dhcp6-client: make dhcp6_lease_free() accepts NULL --- src/libsystemd-network/sd-dhcp6-lease.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index b69f59c84d..aaafed661b 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -358,20 +358,18 @@ int sd_dhcp6_lease_get_fqdn(sd_dhcp6_lease *lease, const char **ret) { } static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) { - assert(lease); + if (!lease) + return NULL; free(lease->serverid); dhcp6_lease_free_ia(&lease->ia); dhcp6_lease_free_ia(&lease->pd); - free(lease->dns); free(lease->fqdn); - - lease->domains = strv_free(lease->domains); - + strv_free(lease->domains); free(lease->ntp); + strv_free(lease->ntp_fqdn); - lease->ntp_fqdn = strv_free(lease->ntp_fqdn); return mfree(lease); } From 41b14f03297ccecdee9429c447cc85b471d3df21 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 Sep 2021 14:16:44 +0900 Subject: [PATCH 12/15] sd-dhcp6-client: support multiple domains --- src/libsystemd-network/dhcp6-lease-internal.h | 2 +- src/libsystemd-network/sd-dhcp6-client.c | 2 +- src/libsystemd-network/sd-dhcp6-lease.c | 8 +++----- src/libsystemd-network/test-dhcp6-client.c | 3 +-- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index 0b83ddfd64..d6311b7d38 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -49,7 +49,7 @@ int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid); int dhcp6_lease_get_pd_iaid(sd_dhcp6_lease *lease, be32_t *iaid); int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); -int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); +int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) ; int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index a5eae6a55c..604b9f5e4b 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -1250,7 +1250,7 @@ static int client_parse_message( break; case SD_DHCP6_OPTION_DOMAIN_LIST: - r = dhcp6_lease_set_domains(lease, optval, optlen); + r = dhcp6_lease_add_domains(lease, optval, optlen); if (r < 0) return r; diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index aaafed661b..0a292a3f51 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -214,9 +214,9 @@ int sd_dhcp6_lease_get_dns(sd_dhcp6_lease *lease, const struct in6_addr **ret) { return lease->dns_count; } -int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { +int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { + _cleanup_strv_free_ char **domains = NULL; int r; - char **domains; assert_return(lease, -EINVAL); assert_return(optval, -EINVAL); @@ -228,9 +228,7 @@ int dhcp6_lease_set_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t if (r < 0) return r; - strv_free_and_replace(lease->domains, domains); - - return 0; + return strv_extend_strv(&lease->domains, domains, true); } int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) { diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index e711e1d9f5..665000ef45 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -516,8 +516,7 @@ static int test_advertise_option(sd_event *e) { case SD_DHCP6_OPTION_DOMAIN_LIST: assert_se(optlen == 11); - assert_se(dhcp6_lease_set_domains(lease, optval, - optlen) >= 0); + assert_se(dhcp6_lease_add_domains(lease, optval, optlen) >= 0); break; case SD_DHCP6_OPTION_SNTP_SERVERS: From 9c3d46bf8d933d6ff4da6876ebbc0f13d65bee76 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 Sep 2021 14:09:14 +0900 Subject: [PATCH 13/15] sd-dhcp6-client: support multiple NTP server options Also, it is allowed that the suboption appears multiple times, and each suboption contains one NTP server. --- src/libsystemd-network/dhcp6-lease-internal.h | 2 +- src/libsystemd-network/sd-dhcp6-client.c | 2 +- src/libsystemd-network/sd-dhcp6-lease.c | 16 +++++++++------- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index d6311b7d38..8584336341 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -50,7 +50,7 @@ int dhcp6_lease_get_pd_iaid(sd_dhcp6_lease *lease, be32_t *iaid); int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); -int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); +int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) ; int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 604b9f5e4b..1564ebee73 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -1257,7 +1257,7 @@ static int client_parse_message( break; case SD_DHCP6_OPTION_NTP_SERVER: - r = dhcp6_lease_set_ntp(lease, optval, optlen); + r = dhcp6_lease_add_ntp(lease, optval, optlen); if (r < 0) return r; diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 0a292a3f51..7c9d421a94 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -242,15 +242,12 @@ int sd_dhcp6_lease_get_domains(sd_dhcp6_lease *lease, char ***ret) { return strv_length(lease->domains); } -int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { +int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { int r; assert_return(lease, -EINVAL); assert_return(optval, -EINVAL); - lease->ntp = mfree(lease->ntp); - lease->ntp_count = 0; - for (size_t offset = 0; offset < optlen;) { const uint8_t *subval; size_t sublen; @@ -273,13 +270,18 @@ int dhcp6_lease_set_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t opt break; case DHCP6_NTP_SUBOPTION_SRV_FQDN: { - char **servers; + _cleanup_free_ char *server = NULL; - r = dhcp6_option_parse_domainname_list(subval, sublen, &servers); + r = dhcp6_option_parse_domainname(subval, sublen, &server); if (r < 0) return r; - strv_free_and_replace(lease->ntp_fqdn, servers); + if (strv_contains(lease->ntp_fqdn, server)) + continue; + + r = strv_consume(&lease->ntp_fqdn, TAKE_PTR(server)); + if (r < 0) + return r; break; }} From e693e969614062fea1746399cf5cff4c09526c6a Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 Sep 2021 15:00:43 +0900 Subject: [PATCH 14/15] sd-dhcp6-client: do not merge NTP and SNTP options Previously, SNTP option is ignored when it appears after NTP option(s), but merged later NTP options when it appears first. This makes split the NTP and SNTP addresses, and use SNTP addresses only when no NTP option is provided. --- src/libsystemd-network/dhcp6-lease-internal.h | 4 ++- src/libsystemd-network/sd-dhcp6-client.c | 2 +- src/libsystemd-network/sd-dhcp6-lease.c | 26 +++++++++++-------- src/libsystemd-network/test-dhcp6-client.c | 3 +-- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index 8584336341..562fce3d3a 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -31,6 +31,8 @@ struct sd_dhcp6_lease { struct in6_addr *ntp; size_t ntp_count; char **ntp_fqdn; + struct in6_addr *sntp; + size_t sntp_count; char *fqdn; }; @@ -51,7 +53,7 @@ int dhcp6_lease_get_pd_iaid(sd_dhcp6_lease *lease, be32_t *iaid); int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); -int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) ; +int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) ; int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_new(sd_dhcp6_lease **ret); diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 1564ebee73..d7c29d3e58 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -1264,7 +1264,7 @@ static int client_parse_message( break; case SD_DHCP6_OPTION_SNTP_SERVERS: - r = dhcp6_lease_set_sntp(lease, optval, optlen); + r = dhcp6_lease_add_sntp(lease, optval, optlen); if (r < 0) return r; diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 7c9d421a94..7dc4286a45 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -290,30 +290,33 @@ int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t opt return 0; } -int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { +int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { assert_return(lease, -EINVAL); assert_return(optval, -EINVAL); if (optlen == 0) return 0; - if (lease->ntp || lease->ntp_fqdn) - return -EEXIST; - - /* Using deprecated SNTP information */ - - return dhcp6_option_parse_addresses(optval, optlen, &lease->ntp, &lease->ntp_count); + /* SNTP option is defined in RFC4075, and deprecated by RFC5908. */ + return dhcp6_option_parse_addresses(optval, optlen, &lease->sntp, &lease->sntp_count); } int sd_dhcp6_lease_get_ntp_addrs(sd_dhcp6_lease *lease, const struct in6_addr **ret) { assert_return(lease, -EINVAL); assert_return(ret, -EINVAL); - if (!lease->ntp) - return -ENOENT; + if (lease->ntp) { + *ret = lease->ntp; + return lease->ntp_count; + } - *ret = lease->ntp; - return lease->ntp_count; + if (lease->sntp && !lease->ntp_fqdn) { + /* Fallback to the deprecated SNTP option. */ + *ret = lease->sntp; + return lease->sntp_count; + } + + return -ENOENT; } int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) { @@ -369,6 +372,7 @@ static sd_dhcp6_lease *dhcp6_lease_free(sd_dhcp6_lease *lease) { strv_free(lease->domains); free(lease->ntp); strv_free(lease->ntp_fqdn); + free(lease->sntp); return mfree(lease); } diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 665000ef45..64b51f1232 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -521,8 +521,7 @@ static int test_advertise_option(sd_event *e) { case SD_DHCP6_OPTION_SNTP_SERVERS: assert_se(optlen == 16); - assert_se(dhcp6_lease_set_sntp(lease, optval, - optlen) >= 0); + assert_se(dhcp6_lease_add_sntp(lease, optval, optlen) >= 0); break; default: From e210f027e07c71404d98a5d5b14d39891b2f7352 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 Sep 2021 21:03:24 +0900 Subject: [PATCH 15/15] sd-dhcp6-client: rename dhcp6_lease_set_dns() -> dhcp6_lease_add_dns() As it is allowed that multiple DNS server options appear in the message. --- src/libsystemd-network/dhcp6-lease-internal.h | 2 +- src/libsystemd-network/sd-dhcp6-client.c | 2 +- src/libsystemd-network/sd-dhcp6-lease.c | 2 +- src/libsystemd-network/test-dhcp6-client.c | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index 562fce3d3a..8801497b72 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -50,7 +50,7 @@ int dhcp6_lease_get_rapid_commit(sd_dhcp6_lease *lease, bool *rapid_commit); int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid); int dhcp6_lease_get_pd_iaid(sd_dhcp6_lease *lease, be32_t *iaid); -int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); +int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_add_domains(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen); int dhcp6_lease_add_sntp(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) ; diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index d7c29d3e58..87dd8c84d1 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -1243,7 +1243,7 @@ static int client_parse_message( break; case SD_DHCP6_OPTION_DNS_SERVERS: - r = dhcp6_lease_set_dns(lease, optval, optlen); + r = dhcp6_lease_add_dns(lease, optval, optlen); if (r < 0) return r; diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 7dc4286a45..c55b06d2f7 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -193,7 +193,7 @@ void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) { lease->prefix_iter = lease->pd.addresses; } -int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { +int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) { assert_return(lease, -EINVAL); assert_return(optval, -EINVAL); diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index 64b51f1232..b725f5c305 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -510,8 +510,7 @@ static int test_advertise_option(sd_event *e) { case SD_DHCP6_OPTION_DNS_SERVERS: assert_se(optlen == 16); - assert_se(dhcp6_lease_set_dns(lease, optval, - optlen) >= 0); + assert_se(dhcp6_lease_add_dns(lease, optval, optlen) >= 0); break; case SD_DHCP6_OPTION_DOMAIN_LIST: