mirror of
https://github.com/systemd/systemd.git
synced 2025-03-29 06:50:16 +03:00
Merge pull request #20824 from yuwata/sd-dhcp6-client-cleanups
sd-dhcp6-client: several cleanups for parsing options
This commit is contained in:
commit
209abeac6d
@ -101,15 +101,29 @@ 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_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,
|
||||
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(
|
||||
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(const uint8_t *data, size_t data_len, char **ret_status_message);
|
||||
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_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, 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,
|
||||
|
@ -28,11 +28,11 @@ 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;
|
||||
struct in6_addr *sntp;
|
||||
size_t sntp_count;
|
||||
char *fqdn;
|
||||
};
|
||||
|
||||
@ -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_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) ;
|
||||
int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen);
|
||||
|
||||
int dhcp6_lease_new(sd_dhcp6_lease **ret);
|
||||
|
@ -14,29 +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;
|
||||
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))
|
||||
@ -370,367 +353,410 @@ 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)
|
||||
if (len > buflen - offsetof(DHCP6Option, data) - *offset)
|
||||
return -EBADMSG;
|
||||
|
||||
*offset += offsetof(DHCP6Option, data) + len;
|
||||
*ret_option_code = be16toh(option->code);
|
||||
*ret_option_data_len = len;
|
||||
*ret_option_data = option->data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp6_option_parse_status(const uint8_t *data, size_t data_len, char **ret_status_message) {
|
||||
assert(data);
|
||||
|
||||
if (data_len < sizeof(uint16_t))
|
||||
return -EBADMSG;
|
||||
|
||||
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_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;
|
||||
|
||||
assert(data);
|
||||
assert(ret);
|
||||
|
||||
if (len < sizeof(struct iaaddr))
|
||||
return -EBADMSG;
|
||||
|
||||
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),
|
||||
"Received an IA address with preferred lifetime %"PRIu32
|
||||
" larger than valid lifetime %"PRIu32", ignoring.",
|
||||
lt_pref, lt_valid);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
a = new(DHCP6Address, 1);
|
||||
if (!a)
|
||||
return -ENOMEM;
|
||||
|
||||
LIST_INIT(addresses, a);
|
||||
memcpy(&a->iaaddr, data, sizeof(struct iaaddr));
|
||||
|
||||
*ret = a;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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 (len < sizeof(struct iapdprefix))
|
||||
return -ENOMSG;
|
||||
|
||||
*optcode = be16toh(option->code);
|
||||
*optlen = len;
|
||||
lt_valid = be32toh(((const struct iapdprefix*) data)->lifetime_valid);
|
||||
lt_pref = be32toh(((const struct iapdprefix*) data)->lifetime_preferred);
|
||||
|
||||
*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;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp6_option_parse_status(DHCP6Option *option, size_t len) {
|
||||
DHCP6StatusOption *statusopt = (DHCP6StatusOption *)option;
|
||||
|
||||
if (len < sizeof(DHCP6StatusOption) ||
|
||||
be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(DHCP6StatusOption))
|
||||
return -ENOBUFS;
|
||||
|
||||
return be16toh(statusopt->status);
|
||||
}
|
||||
|
||||
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;
|
||||
uint32_t lt_valid, lt_pref;
|
||||
int r;
|
||||
|
||||
if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*addr_option))
|
||||
return -ENOBUFS;
|
||||
|
||||
lt_valid = be32toh(addr_option->iaaddr.lifetime_valid);
|
||||
lt_pref = be32toh(addr_option->iaaddr.lifetime_preferred);
|
||||
|
||||
if (lt_valid == 0 || lt_pref > lt_valid)
|
||||
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 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) > sizeof(*addr_option)) {
|
||||
r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options, be16toh(option->len) + offsetof(DHCP6Option, data) - sizeof(*addr_option));
|
||||
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;
|
||||
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);
|
||||
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);
|
||||
|
||||
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;
|
||||
uint32_t lt_valid, lt_pref;
|
||||
int r;
|
||||
|
||||
if (be16toh(option->len) + offsetof(DHCP6Option, data) < sizeof(*pdprefix_option))
|
||||
return -ENOBUFS;
|
||||
|
||||
lt_valid = be32toh(pdprefix_option->iapdprefix.lifetime_valid);
|
||||
lt_pref = be32toh(pdprefix_option->iapdprefix.lifetime_preferred);
|
||||
|
||||
if (lt_valid == 0 || 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,
|
||||
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 (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);
|
||||
if (!prefix)
|
||||
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;
|
||||
int r = 0, status;
|
||||
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");
|
||||
|
||||
r = dhcp6_option_parse_address(client, option, ia, <_valid);
|
||||
if (r < 0 && r != -EINVAL)
|
||||
return r;
|
||||
if (r >= 0 && lt_valid < lt_min)
|
||||
lt_min = lt_valid;
|
||||
|
||||
break;
|
||||
|
||||
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");
|
||||
|
||||
r = dhcp6_option_parse_pdprefix(client, option, ia, <_valid);
|
||||
if (r < 0 && r != -EINVAL)
|
||||
return r;
|
||||
if (r >= 0 && lt_valid < lt_min)
|
||||
lt_min = lt_valid;
|
||||
|
||||
break;
|
||||
|
||||
case SD_DHCP6_OPTION_STATUS_CODE:
|
||||
|
||||
status = dhcp6_option_parse_status(option, optlen + offsetof(DHCP6Option, data));
|
||||
if (status < 0)
|
||||
return status;
|
||||
|
||||
if (status > 0) {
|
||||
if (ret_status_code)
|
||||
*ret_status_code = status;
|
||||
|
||||
log_dhcp6_client(client, "IA status %s",
|
||||
dhcp6_message_status_to_string(status));
|
||||
|
||||
return 0;
|
||||
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;
|
||||
}
|
||||
|
||||
break;
|
||||
r = dhcp6_option_parse_ia_address(client, subdata, subdata_len, &a);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
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;
|
||||
|
||||
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_ia_pdprefix(client, subdata, subdata_len, &a);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
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(subdata, subdata_len, &msg);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
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(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) {
|
||||
_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;
|
||||
|
||||
@ -754,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;
|
||||
@ -798,39 +826,38 @@ 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;
|
||||
|
||||
assert(optval);
|
||||
assert(ret);
|
||||
|
||||
if (optlen <= 1)
|
||||
return -ENODATA;
|
||||
if (optval[optlen - 1] != '\0')
|
||||
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++;
|
||||
}
|
||||
|
||||
*str_arr = TAKE_PTR(names);
|
||||
|
||||
return idx;
|
||||
*ret = TAKE_PTR(names);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static sd_dhcp6_option* dhcp6_option_free(sd_dhcp6_option *i) {
|
||||
|
@ -1109,11 +1109,9 @@ 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;
|
||||
int r;
|
||||
|
||||
assert(client);
|
||||
@ -1122,22 +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;
|
||||
int status;
|
||||
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;
|
||||
r = dhcp6_option_parse(message->options, len, &offset, &optcode, &optlen, &optval);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
switch (optcode) {
|
||||
case SD_DHCP6_OPTION_CLIENTID:
|
||||
@ -1176,62 +1166,75 @@ 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: {
|
||||
_cleanup_(dhcp6_lease_free_ia) DHCP6IA ia = {};
|
||||
|
||||
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.");
|
||||
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)
|
||||
@ -1240,28 +1243,28 @@ 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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
@ -1281,13 +1284,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));
|
||||
@ -1297,16 +1295,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);
|
||||
|
@ -193,86 +193,69 @@ 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 r;
|
||||
|
||||
int dhcp6_lease_add_dns(sd_dhcp6_lease *lease, const uint8_t *optval, size_t optlen) {
|
||||
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) {
|
||||
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, 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);
|
||||
|
||||
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;
|
||||
|
||||
return r;
|
||||
return strv_extend_strv(&lease->domains, domains, true);
|
||||
}
|
||||
|
||||
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, uint8_t *optval, size_t optlen) {
|
||||
int dhcp6_lease_add_ntp(sd_dhcp6_lease *lease, const 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);
|
||||
|
||||
lease->ntp = mfree(lease->ntp);
|
||||
lease->ntp_count = 0;
|
||||
for (size_t offset = 0; offset < optlen;) {
|
||||
const uint8_t *subval;
|
||||
size_t sublen;
|
||||
uint16_t subopt;
|
||||
|
||||
while ((r = dhcp6_option_parse(&optval, &optlen, &subopt, &sublen,
|
||||
&subval)) >= 0) {
|
||||
int s;
|
||||
char **servers;
|
||||
r = dhcp6_option_parse(optval, optlen, &offset, &subopt, &sublen, &subval);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
switch(subopt) {
|
||||
case DHCP6_NTP_SUBOPTION_SRV_ADDR:
|
||||
@ -280,86 +263,74 @@ 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;
|
||||
|
||||
lease->ntp_count = s;
|
||||
|
||||
break;
|
||||
|
||||
case DHCP6_NTP_SUBOPTION_SRV_FQDN:
|
||||
r = dhcp6_option_parse_domainname_list(subval, sublen,
|
||||
&servers);
|
||||
r = dhcp6_option_parse_addresses(subval, sublen, &lease->ntp, &lease->ntp_count);
|
||||
if (r < 0)
|
||||
return 0;
|
||||
|
||||
strv_free_and_replace(lease->ntp_fqdn, servers);
|
||||
lease->ntp_fqdn_count = r;
|
||||
return r;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (r != -ENOMSG)
|
||||
return r;
|
||||
case DHCP6_NTP_SUBOPTION_SRV_FQDN: {
|
||||
_cleanup_free_ char *server = NULL;
|
||||
|
||||
r = dhcp6_option_parse_domainname(subval, sublen, &server);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (strv_contains(lease->ntp_fqdn, server))
|
||||
continue;
|
||||
|
||||
r = strv_consume(&lease->ntp_fqdn, TAKE_PTR(server));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
}}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dhcp6_lease_set_sntp(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) {
|
||||
int r;
|
||||
|
||||
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)
|
||||
if (optlen == 0)
|
||||
return 0;
|
||||
|
||||
if (lease->ntp || lease->ntp_fqdn)
|
||||
return -EEXIST;
|
||||
|
||||
/* 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;
|
||||
/* 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 **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;
|
||||
if (lease->ntp) {
|
||||
*ret = lease->ntp;
|
||||
return lease->ntp_count;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ntp_fqdn) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(ntp_fqdn, -EINVAL);
|
||||
|
||||
if (lease->ntp_fqdn_count) {
|
||||
*ntp_fqdn = lease->ntp_fqdn;
|
||||
return lease->ntp_fqdn_count;
|
||||
if (lease->sntp && !lease->ntp_fqdn) {
|
||||
/* Fallback to the deprecated SNTP option. */
|
||||
*ret = lease->sntp;
|
||||
return lease->sntp_count;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval,
|
||||
size_t optlen) {
|
||||
int sd_dhcp6_lease_get_ntp_fqdn(sd_dhcp6_lease *lease, char ***ret) {
|
||||
assert_return(lease, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (!lease->ntp_fqdn)
|
||||
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) {
|
||||
int r;
|
||||
char *fqdn;
|
||||
|
||||
@ -378,33 +349,31 @@ int dhcp6_lease_set_fqdn(sd_dhcp6_lease *lease, const uint8_t *optval,
|
||||
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) {
|
||||
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);
|
||||
free(lease->sntp);
|
||||
|
||||
lease->ntp_fqdn = strv_free(lease->ntp_fqdn);
|
||||
return mfree(lease);
|
||||
}
|
||||
|
||||
|
@ -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"));
|
||||
@ -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,53 +164,66 @@ 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;
|
||||
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];
|
||||
outlen -= 3;
|
||||
out = &result[3];
|
||||
offset = 3;
|
||||
assert_se(dhcp6_option_parse(packet, 0, &offset, &optcode, &optlen, &optval) == -EBADMSG);
|
||||
|
||||
/* 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(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(optval == packet + 11);
|
||||
|
||||
assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen,
|
||||
optval) >= 0);
|
||||
pos = 7;
|
||||
outlen -= 7;
|
||||
out = &result[pos];
|
||||
|
||||
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 + 22);
|
||||
|
||||
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');
|
||||
|
||||
@ -236,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[] = {
|
||||
@ -248,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',
|
||||
};
|
||||
@ -262,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[] = {
|
||||
@ -275,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,
|
||||
@ -283,6 +296,7 @@ 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;
|
||||
@ -295,62 +309,62 @@ 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);
|
||||
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, NULL);
|
||||
assert_se(r == 0);
|
||||
assert_se(ia.addresses == NULL);
|
||||
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, NULL);
|
||||
assert_se(r == -ENOBUFS);
|
||||
assert_se(ia.addresses == NULL);
|
||||
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, NULL);
|
||||
assert_se(r == -ENOBUFS);
|
||||
assert_se(ia.addresses == NULL);
|
||||
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;
|
||||
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, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia);
|
||||
assert_se(r == -ENODATA);
|
||||
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);
|
||||
r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia);
|
||||
assert_se(r >= 0);
|
||||
assert_se(ia.addresses != NULL);
|
||||
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);
|
||||
r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &pd);
|
||||
assert_se(r >= 0);
|
||||
assert_se(pd.addresses != NULL);
|
||||
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);
|
||||
r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &pd);
|
||||
assert_se(r >= 0);
|
||||
assert_se(pd.addresses != NULL);
|
||||
assert_se(pd.addresses);
|
||||
dhcp6_lease_free_ia(&pd);
|
||||
|
||||
return 0;
|
||||
@ -468,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;
|
||||
}
|
||||
@ -496,20 +510,17 @@ 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:
|
||||
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:
|
||||
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:
|
||||
@ -665,7 +676,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;
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user