1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2025-03-12 08:58:20 +03:00

Merge pull request #18730 from yuwata/dhcp6-client-do-not-set-t1-t2-18090

dhcp6: do not set T1 and T2 in IA_PD option or friends
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2021-02-24 09:53:37 +01:00 committed by GitHub
commit d9a65c5976
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 156 additions and 116 deletions

View File

@ -97,7 +97,7 @@ typedef struct DHCP6IA DHCP6IA;
int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code,
size_t optlen, const void *optval);
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia);
int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix);
int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd, const DHCP6Address *hint_pd_prefix);
int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn);
int dhcp6_option_append_user_class(uint8_t **buf, size_t *buflen, char * const *user_class);
int dhcp6_option_append_vendor_class(uint8_t **buf, size_t *buflen, char * const *user_class);

View File

@ -41,14 +41,15 @@ typedef struct DHCP6PDPrefixOption {
#define DHCP6_OPTION_IA_PD_LEN (sizeof(struct ia_pd))
#define DHCP6_OPTION_IA_TA_LEN (sizeof(struct ia_ta))
static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode,
size_t optlen) {
DHCP6Option *option = (DHCP6Option*) *buf;
static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, size_t optlen) {
DHCP6Option *option;
assert_return(buf, -EINVAL);
assert_return(*buf, -EINVAL);
assert_return(buflen, -EINVAL);
option = (DHCP6Option*) *buf;
if (optlen > 0xffff || *buflen < optlen + offsetof(DHCP6Option, data))
return -ENOBUFS;
@ -112,10 +113,13 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHash
}
int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
uint16_t len;
uint8_t *ia_hdr;
size_t iaid_offset, ia_buflen, ia_addrlen = 0;
size_t ia_buflen, ia_addrlen = 0;
struct ia_na ia_na;
struct ia_ta ia_ta;
DHCP6Address *addr;
uint8_t *ia_hdr;
uint16_t len;
void *p;
int r;
assert_return(buf, -EINVAL);
@ -123,15 +127,23 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
assert_return(buflen, -EINVAL);
assert_return(ia, -EINVAL);
/* client should not send set T1 and T2. See, RFC 8415, and issue #18090. */
switch (ia->type) {
case SD_DHCP6_OPTION_IA_NA:
len = DHCP6_OPTION_IA_NA_LEN;
iaid_offset = offsetof(DHCP6IA, ia_na);
ia_na = (struct ia_na) {
.id = ia->ia_na.id,
};
p = &ia_na;
break;
case SD_DHCP6_OPTION_IA_TA:
len = DHCP6_OPTION_IA_TA_LEN;
iaid_offset = offsetof(DHCP6IA, ia_ta);
ia_ta = (struct ia_ta) {
.id = ia->ia_ta.id,
};
p = &ia_ta;
break;
default:
@ -147,30 +159,113 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, const DHCP6IA *ia) {
*buf += offsetof(DHCP6Option, data);
*buflen -= offsetof(DHCP6Option, data);
memcpy(*buf, (char*) ia + iaid_offset, len);
memcpy(*buf, p, len);
*buf += len;
*buflen -= len;
LIST_FOREACH(addresses, addr, ia->addresses) {
r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR,
sizeof(addr->iaaddr));
struct iaaddr a = {
.address = addr->iaaddr.address,
};
r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IAADDR, sizeof(struct iaaddr));
if (r < 0)
return r;
memcpy(*buf, &addr->iaaddr, sizeof(addr->iaaddr));
memcpy(*buf, &a, sizeof(struct iaaddr));
*buf += sizeof(addr->iaaddr);
*buflen -= sizeof(addr->iaaddr);
*buf += sizeof(struct iaaddr);
*buflen -= sizeof(struct iaaddr);
ia_addrlen += offsetof(DHCP6Option, data) + sizeof(addr->iaaddr);
ia_addrlen += offsetof(DHCP6Option, data) + sizeof(struct iaaddr);
}
r = option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen);
return option_append_hdr(&ia_hdr, &ia_buflen, ia->type, len + ia_addrlen);
}
static int option_append_pd_prefix(uint8_t **buf, size_t *buflen, const DHCP6Address *prefix) {
struct iapdprefix p;
int r;
assert(buf);
assert(*buf);
assert(buflen);
assert(prefix);
if (prefix->iapdprefix.prefixlen == 0)
return -EINVAL;
/* Do not append T1 and T2. */
p = (struct iapdprefix) {
.prefixlen = prefix->iapdprefix.prefixlen,
.address = prefix->iapdprefix.address,
};
r = option_append_hdr(buf, buflen, SD_DHCP6_OPTION_IA_PD_PREFIX, sizeof(struct iapdprefix));
if (r < 0)
return r;
return 0;
memcpy(*buf, &p, sizeof(struct iapdprefix));
*buf += sizeof(struct iapdprefix);
*buflen -= sizeof(struct iapdprefix);
return offsetof(DHCP6Option, data) + sizeof(struct iapdprefix);
}
int dhcp6_option_append_pd(uint8_t **buf, size_t *buflen, const DHCP6IA *pd, const DHCP6Address *hint_pd_prefix) {
struct ia_pd ia_pd;
size_t len, pd_buflen;
uint8_t *pd_hdr;
int r;
assert_return(buf, -EINVAL);
assert_return(*buf, -EINVAL);
assert_return(buflen, -EINVAL);
assert_return(pd, -EINVAL);
assert_return(pd->type == SD_DHCP6_OPTION_IA_PD, -EINVAL);
/* Do not set T1 and T2. */
ia_pd = (struct ia_pd) {
.id = pd->ia_pd.id,
};
len = sizeof(struct ia_pd);
if (*buflen < offsetof(DHCP6Option, data) + len)
return -ENOBUFS;
pd_hdr = *buf;
pd_buflen = *buflen;
/* The header will be written at the end of this function. */
*buf += offsetof(DHCP6Option, data);
*buflen -= offsetof(DHCP6Option, data);
memcpy(*buf, &ia_pd, len);
*buf += sizeof(struct ia_pd);
*buflen -= sizeof(struct ia_pd);
DHCP6Address *prefix;
LIST_FOREACH(addresses, prefix, pd->addresses) {
r = option_append_pd_prefix(buf, buflen, prefix);
if (r < 0)
return r;
len += r;
}
if (hint_pd_prefix) {
r = option_append_pd_prefix(buf, buflen, hint_pd_prefix);
if (r < 0 && r != -EINVAL)
return r;
len += r;
}
return option_append_hdr(&pd_hdr, &pd_buflen, pd->type, len);
}
int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) {
@ -275,51 +370,6 @@ 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);
}
int dhcp6_option_append_pd(uint8_t *buf, size_t len, const DHCP6IA *pd, DHCP6Address *hint_pd_prefix) {
DHCP6Option *option = (DHCP6Option *)buf;
size_t i = sizeof(*option) + sizeof(pd->ia_pd);
DHCP6PDPrefixOption *prefix_opt;
DHCP6Address *prefix;
assert_return(buf, -EINVAL);
assert_return(pd, -EINVAL);
assert_return(pd->type == SD_DHCP6_OPTION_IA_PD, -EINVAL);
if (len < i)
return -ENOBUFS;
option->code = htobe16(SD_DHCP6_OPTION_IA_PD);
memcpy(&option->data, &pd->ia_pd, sizeof(pd->ia_pd));
LIST_FOREACH(addresses, prefix, pd->addresses) {
if (len < i + sizeof(*prefix_opt))
return -ENOBUFS;
prefix_opt = (DHCP6PDPrefixOption *)&buf[i];
prefix_opt->option.code = htobe16(SD_DHCP6_OPTION_IA_PD_PREFIX);
prefix_opt->option.len = htobe16(sizeof(prefix_opt->iapdprefix));
memcpy(&prefix_opt->iapdprefix, &prefix->iapdprefix, sizeof(struct iapdprefix));
i += sizeof(*prefix_opt);
}
if (hint_pd_prefix && hint_pd_prefix->iapdprefix.prefixlen > 0) {
if (len < i + sizeof(*prefix_opt))
return -ENOBUFS;
prefix_opt = (DHCP6PDPrefixOption *)&buf[i];
prefix_opt->option.code = htobe16(SD_DHCP6_OPTION_IA_PD_PREFIX);
prefix_opt->option.len = htobe16(sizeof(prefix_opt->iapdprefix));
memcpy(&prefix_opt->iapdprefix, &hint_pd_prefix->iapdprefix, sizeof(struct iapdprefix));
i += sizeof(*prefix_opt);
}
option->len = htobe16(i - sizeof(*option));
return i;
}
static int option_parse_hdr(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen) {
DHCP6Option *option = (DHCP6Option*) *buf;
uint16_t len;
@ -375,8 +425,7 @@ int dhcp6_option_parse_status(DHCP6Option *option, size_t len) {
return be16toh(statusopt->status);
}
static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia,
uint32_t *lifetime_valid) {
static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia, uint32_t *ret_lifetime_valid) {
DHCP6AddressOption *addr_option = (DHCP6AddressOption *)option;
DHCP6Address *addr;
uint32_t lt_valid, lt_pref;
@ -389,16 +438,22 @@ static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia,
lt_pref = be32toh(addr_option->iaaddr.lifetime_preferred);
if (lt_valid == 0 || lt_pref > lt_valid) {
log_dhcp6_client(client, "Valid lifetime of an IA address is zero or preferred lifetime %d > valid lifetime %d",
log_dhcp6_client(client,
"Valid lifetime of an IA address is zero or "
"preferred lifetime %"PRIu32" > valid lifetime %"PRIu32,
lt_pref, lt_valid);
return 0;
return -EINVAL;
}
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 (r != 0)
return r < 0 ? r: 0;
if (r < 0)
return r;
if (r > 0) {
log_dhcp6_client(client, "Non-zero status code '%s' for address is received",
dhcp6_message_status_to_string(r));
return -EINVAL;
}
}
addr = new0(DHCP6Address, 1);
@ -410,13 +465,12 @@ static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia,
LIST_PREPEND(addresses, ia->addresses, addr);
*lifetime_valid = be32toh(addr->iaaddr.lifetime_valid);
*ret_lifetime_valid = be32toh(addr->iaaddr.lifetime_valid);
return 0;
}
static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia,
uint32_t *lifetime_valid) {
static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia, uint32_t *ret_lifetime_valid) {
DHCP6PDPrefixOption *pdprefix_option = (DHCP6PDPrefixOption *)option;
DHCP6Address *prefix;
uint32_t lt_valid, lt_pref;
@ -429,16 +483,22 @@ static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia,
lt_pref = be32toh(pdprefix_option->iapdprefix.lifetime_preferred);
if (lt_valid == 0 || lt_pref > lt_valid) {
log_dhcp6_client(client, "Valid lifetieme of a PD prefix is zero or preferred lifetime %d > valid lifetime %d",
log_dhcp6_client(client,
"Valid lifetieme of a PD prefix is zero or "
"preferred lifetime %"PRIu32" > valid lifetime %"PRIu32,
lt_pref, lt_valid);
return 0;
return -EINVAL;
}
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 (r != 0)
return r < 0 ? r: 0;
if (r < 0)
return r;
if (r > 0) {
log_dhcp6_client(client, "Non-zero status code '%s' for PD prefix is received",
dhcp6_message_status_to_string(r));
return -EINVAL;
}
}
prefix = new0(DHCP6Address, 1);
@ -450,7 +510,7 @@ static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia,
LIST_PREPEND(addresses, ia->addresses, prefix);
*lifetime_valid = be32toh(prefix->iapdprefix.lifetime_valid);
*ret_lifetime_valid = be32toh(prefix->iapdprefix.lifetime_valid);
return 0;
}
@ -482,8 +542,7 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_stat
lt_t2 = be32toh(ia->ia_na.lifetime_t2);
if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
log_dhcp6_client(client, "IA NA T1 %ds > T2 %ds",
lt_t1, lt_t2);
log_dhcp6_client(client, "IA NA T1 %"PRIu32"sec > T2 %"PRIu32"sec", lt_t1, lt_t2);
return -EINVAL;
}
@ -501,8 +560,7 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_stat
lt_t2 = be32toh(ia->ia_pd.lifetime_t2);
if (lt_t1 && lt_t2 && lt_t1 > lt_t2) {
log_dhcp6_client(client, "IA PD T1 %ds > T2 %ds",
lt_t1, lt_t2);
log_dhcp6_client(client, "IA PD T1 %"PRIu32"sec > T2 %"PRIu32"sec", lt_t1, lt_t2);
return -EINVAL;
}
@ -542,10 +600,9 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_stat
}
r = dhcp6_option_parse_address(option, ia, &lt_valid);
if (r < 0)
if (r < 0 && r != -EINVAL)
return r;
if (lt_valid < lt_min)
if (r >= 0 && lt_valid < lt_min)
lt_min = lt_valid;
break;
@ -558,10 +615,9 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_stat
}
r = dhcp6_option_parse_pdprefix(option, ia, &lt_valid);
if (r < 0)
if (r < 0 && r != -EINVAL)
return r;
if (lt_valid < lt_min)
if (r >= 0 && lt_valid < lt_min)
lt_min = lt_valid;
break;
@ -594,26 +650,26 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_stat
switch(iatype) {
case SD_DHCP6_OPTION_IA_NA:
if (!ia->ia_na.lifetime_t1 && !ia->ia_na.lifetime_t2) {
if (!ia->ia_na.lifetime_t1 && !ia->ia_na.lifetime_t2 && 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 %ds and T2 %ds as both were zero",
log_dhcp6_client(client, "Computed IA NA T1 %"PRIu32"sec and T2 %"PRIu32"sec as both were zero",
lt_t1, lt_t2);
}
break;
case SD_DHCP6_OPTION_IA_PD:
if (!ia->ia_pd.lifetime_t1 && !ia->ia_pd.lifetime_t2) {
if (!ia->ia_pd.lifetime_t1 && !ia->ia_pd.lifetime_t2 && 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 %ds and T2 %ds as both were zero",
log_dhcp6_client(client, "Computed IA PD T1 %"PRIu32"sec and T2 %"PRIu32"sec as both were zero",
lt_t1, lt_t2);
}

View File

@ -698,12 +698,9 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
}
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd, &client->hint_pd_prefix);
r = dhcp6_option_append_pd(&opt, &optlen, &client->ia_pd, &client->hint_pd_prefix);
if (r < 0)
return r;
opt += r;
optlen -= r;
}
break;
@ -762,12 +759,9 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
}
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD) && client->lease->pd.addresses) {
r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL);
if (r < 0)
return r;
opt += r;
optlen -= r;
}
break;
@ -814,12 +808,9 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) {
}
if (FLAGS_SET(client->request, DHCP6_REQUEST_IA_PD)) {
r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd, NULL);
r = dhcp6_option_append_pd(&opt, &optlen, &client->lease->pd, NULL);
if (r < 0)
return r;
opt += r;
optlen -= r;
}
break;

View File

@ -650,12 +650,12 @@ static int test_client_verify_request(DHCP6Message *request, size_t len) {
assert_se(optlen == 40);
assert_se(!memcmp(optval, &test_iaid, sizeof(test_iaid)));
val = htobe32(80);
/* T1 and T2 should not be set. */
val = 0;
assert_se(!memcmp(optval + 4, &val, sizeof(val)));
val = htobe32(120);
assert_se(!memcmp(optval + 8, &val, sizeof(val)));
/* Then, this should refuse all addresses. */
assert_se(dhcp6_option_parse_ia(option, &lease->ia, NULL) >= 0);
break;
@ -694,14 +694,7 @@ static int test_client_verify_request(DHCP6Message *request, size_t len) {
found_elapsed_time);
sd_dhcp6_lease_reset_address_iter(lease);
assert_se(sd_dhcp6_lease_get_address(lease, &addr, &lt_pref,
&lt_valid) >= 0);
assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
assert_se(lt_pref == 150);
assert_se(lt_valid == 180);
assert_se(sd_dhcp6_lease_get_address(lease, &addr, &lt_pref,
&lt_valid) == -ENOMSG);
assert_se(sd_dhcp6_lease_get_address(lease, &addr, &lt_pref, &lt_valid) == -ENOMSG);
return 0;
}