From 8a8955507af363c31297bbc5df79852db4ad39d6 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 24 Sep 2021 00:50:01 +0900 Subject: 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(-) (limited to 'src') 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), + "Received an IA address with zero valid lifetime, ignoring."); + if (lt_pref > lt_valid) 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 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), + "Received a PD prefix with zero valid lifetime, ignoring."); + if (lt_pref > lt_valid) 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 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) { - - 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; + uint16_t option_code, + size_t option_data_len, + const uint8_t *option_data, + DHCP6IA *ret) { + + _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; - - /* 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 (option_data_len < DHCP6_OPTION_IA_NA_LEN) + return -EBADMSG; - 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; - - /* 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 (option_data_len < DHCP6_OPTION_IA_PD_LEN) + return -EBADMSG; - 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 (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"); + 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; + } - 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); } + } + + 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; - i += sizeof(*option) + optlen; + 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(iatype) { + 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 (ia_na_status == DHCP6_STATUS_NO_ADDRS_AVAIL) { - pos += offsetof(DHCP6Option, data) + optlen; + if (r < 0) 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)); + log_dhcp6_client(client, "Received duplicate matching IA_NA option, ignoring."); + continue; } + 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 (ia_pd_status == DHCP6_STATUS_NO_PREFIX_AVAIL) { - pos += offsetof(DHCP6Option, data) + optlen; + if (r < 0) 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)); + log_dhcp6_client(client, "Received duplicate matching IA_PD option, ignoring."); + continue; } - break; + 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; -- cgit v1.2.1