diff options
author | Lennart Poettering <lennart@poettering.net> | 2018-01-15 12:02:37 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-01-15 12:02:37 +0100 |
commit | 38edb7674bc22551f7c7ac010b296406ce25e7f1 (patch) | |
tree | f3ac7a58d28595754c348d97314e24a8a493b002 /src/libsystemd-network | |
parent | man: --this-boot is deprecated (#7880) (diff) | |
parent | man: Update man page regarding DHCPv6 Prefix Delegation (diff) | |
download | systemd-38edb7674bc22551f7c7ac010b296406ce25e7f1.tar.xz systemd-38edb7674bc22551f7c7ac010b296406ce25e7f1.zip |
Merge pull request #7582 from pfl/dhcp6_prefix_delegation
DHCPv6 prefix delegation
Diffstat (limited to 'src/libsystemd-network')
-rw-r--r-- | src/libsystemd-network/dhcp6-internal.h | 65 | ||||
-rw-r--r-- | src/libsystemd-network/dhcp6-lease-internal.h | 2 | ||||
-rw-r--r-- | src/libsystemd-network/dhcp6-option.c | 315 | ||||
-rw-r--r-- | src/libsystemd-network/dhcp6-protocol.h | 1 | ||||
-rw-r--r-- | src/libsystemd-network/radv-internal.h | 3 | ||||
-rw-r--r-- | src/libsystemd-network/sd-dhcp6-client.c | 151 | ||||
-rw-r--r-- | src/libsystemd-network/sd-dhcp6-lease.c | 36 | ||||
-rw-r--r-- | src/libsystemd-network/sd-radv.c | 97 | ||||
-rw-r--r-- | src/libsystemd-network/test-dhcp6-client.c | 231 | ||||
-rw-r--r-- | src/libsystemd-network/test-ndisc-ra.c | 4 |
10 files changed, 743 insertions, 162 deletions
diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index cb5b359cbe..13844a86c6 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -29,25 +29,65 @@ #include "macro.h" #include "sparse-endian.h" +/* Common option header */ +typedef struct DHCP6Option { + be16_t code; + be16_t len; + uint8_t data[]; +} _packed_ DHCP6Option; + +/* Address option */ +struct iaaddr { + struct in6_addr address; + be32_t lifetime_preferred; + be32_t lifetime_valid; +} _packed_; + +/* Prefix Delegation Prefix option */ +struct iapdprefix { + be32_t lifetime_preferred; + be32_t lifetime_valid; + uint8_t prefixlen; + struct in6_addr address; +} _packed_; + typedef struct DHCP6Address DHCP6Address; struct DHCP6Address { LIST_FIELDS(DHCP6Address, addresses); - struct { - struct in6_addr address; - be32_t lifetime_preferred; - be32_t lifetime_valid; - } iaaddr _packed_; + union { + struct iaaddr iaaddr; + struct iapdprefix iapdprefix; + }; }; +/* Non-temporary Address option */ +struct ia_na { + be32_t id; + be32_t lifetime_t1; + be32_t lifetime_t2; +} _packed_; + +/* Prefix Delegation option */ +struct ia_pd { + be32_t id; + be32_t lifetime_t1; + be32_t lifetime_t2; +} _packed_; + +/* Temporary Address option */ +struct ia_ta { + be32_t id; +} _packed_; + struct DHCP6IA { uint16_t type; - struct { - be32_t id; - be32_t lifetime_t1; - be32_t lifetime_t2; - } _packed_; + union { + struct ia_na ia_na; + struct ia_pd ia_pd; + struct ia_ta ia_ta; + }; sd_event_source *timeout_t1; sd_event_source *timeout_t2; @@ -62,11 +102,12 @@ 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, DHCP6IA *ia); +int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd); int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn); int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen, uint8_t **optvalue); -int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, - DHCP6IA *ia); +int dhcp6_option_parse_status(DHCP6Option *option); +int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia); int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen, struct in6_addr **addrs, size_t count, size_t *allocated); diff --git a/src/libsystemd-network/dhcp6-lease-internal.h b/src/libsystemd-network/dhcp6-lease-internal.h index a3f00d4a5d..45e0e82427 100644 --- a/src/libsystemd-network/dhcp6-lease-internal.h +++ b/src/libsystemd-network/dhcp6-lease-internal.h @@ -36,8 +36,10 @@ struct sd_dhcp6_lease { bool rapid_commit; DHCP6IA ia; + DHCP6IA pd; DHCP6Address *addr_iter; + DHCP6Address *prefix_iter; struct in6_addr *dns; size_t dns_count; diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index f346bda5fc..784a11dc9d 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -26,6 +26,7 @@ #include "alloc-util.h" #include "dhcp6-internal.h" +#include "dhcp6-lease-internal.h" #include "dhcp6-protocol.h" #include "dns-domain.h" #include "sparse-endian.h" @@ -33,14 +34,27 @@ #include "unaligned.h" #include "util.h" -#define DHCP6_OPTION_IA_NA_LEN 12 -#define DHCP6_OPTION_IA_TA_LEN 4 +typedef struct DHCP6StatusOption { + struct DHCP6Option option; + be16_t status; + char msg[]; +} _packed_ DHCP6StatusOption; -typedef struct DHCP6Option { - be16_t code; - be16_t len; - uint8_t data[]; -} _packed_ DHCP6Option; +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)) static int option_append_hdr(uint8_t **buf, size_t *buflen, uint16_t optcode, size_t optlen) { @@ -82,6 +96,7 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { uint16_t len; + be32_t *iaid; uint8_t *ia_hdr; size_t ia_buflen, ia_addrlen = 0; DHCP6Address *addr; @@ -92,10 +107,12 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { switch (ia->type) { case SD_DHCP6_OPTION_IA_NA: len = DHCP6_OPTION_IA_NA_LEN; + iaid = &ia->ia_na.id; break; case SD_DHCP6_OPTION_IA_TA: len = DHCP6_OPTION_IA_TA_LEN; + iaid = &ia->ia_ta.id; break; default: @@ -111,7 +128,7 @@ int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia) { *buf += sizeof(DHCP6Option); *buflen -= sizeof(DHCP6Option); - memcpy(*buf, &ia->id, len); + memcpy(*buf, iaid, len); *buf += len; *buflen -= len; @@ -164,6 +181,42 @@ int dhcp6_option_append_fqdn(uint8_t **buf, size_t *buflen, const char *fqdn) { return r; } +int dhcp6_option_append_pd(uint8_t *buf, size_t len, DHCP6IA *pd) { + DHCP6Option *option = (DHCP6Option *)buf; + size_t i = sizeof(*option) + sizeof(pd->ia_pd); + 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) { + DHCP6PDPrefixOption *prefix_opt; + + 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); + } + + 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; @@ -210,35 +263,147 @@ int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, return 0; } -int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, - DHCP6IA *ia) { +int dhcp6_option_parse_status(DHCP6Option *option) { + DHCP6StatusOption *statusopt = (DHCP6StatusOption *)option; + + if (be16toh(option->len) + sizeof(DHCP6Option) < sizeof(*statusopt)) + return -ENOBUFS; + + return be16toh(statusopt->status); +} + +static int dhcp6_option_parse_address(DHCP6Option *option, DHCP6IA *ia, + uint32_t *lifetime_valid) { + DHCP6AddressOption *addr_option = (DHCP6AddressOption *)option; + DHCP6Address *addr; + uint32_t lt_valid, lt_pref; int r; - uint16_t opt, status; - size_t optlen; + + if (be16toh(option->len) + sizeof(DHCP6Option) < 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) { + log_dhcp6_client(client, "Valid lifetime of an IA address is zero or preferred lifetime %d > valid lifetime %d", + lt_pref, lt_valid); + + return 0; + } + + if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*addr_option)) { + r = dhcp6_option_parse_status((DHCP6Option *)addr_option->options); + if (r != 0) + return r < 0 ? r: 0; + } + + addr = new0(DHCP6Address, 1); + if (!addr) + return -ENOMEM; + + LIST_INIT(addresses, addr); + memcpy(&addr->iaaddr, option->data, sizeof(addr->iaaddr)); + + LIST_PREPEND(addresses, ia->addresses, addr); + + *lifetime_valid = be32toh(addr->iaaddr.lifetime_valid); + + return 0; +} + +static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia, + uint32_t *lifetime_valid) { + DHCP6PDPrefixOption *pdprefix_option = (DHCP6PDPrefixOption *)option; + DHCP6Address *prefix; + uint32_t lt_valid, lt_pref; + int r; + + if (be16toh(option->len) + sizeof(DHCP6Option) < 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) { + log_dhcp6_client(client, "Valid lifetieme of a PD prefix is zero or preferred lifetime %d > valid lifetime %d", + lt_pref, lt_valid); + + return 0; + } + + if (be16toh(option->len) + sizeof(DHCP6Option) > sizeof(*pdprefix_option)) { + r = dhcp6_option_parse_status((DHCP6Option *)pdprefix_option->options); + if (r != 0) + return r < 0 ? r: 0; + } + + 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); + + *lifetime_valid = be32toh(prefix->iapdprefix.lifetime_valid); + + return 0; +} + +int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia) { + uint16_t iatype, optlen; + size_t i, len; + int r = 0, status; + uint16_t opt; size_t iaaddr_offset; - DHCP6Address *addr; - uint32_t lt_t1, lt_t2, lt_valid, lt_pref, lt_min = ~0; + uint32_t lt_t1, lt_t2, lt_valid, lt_min = ~0; assert_return(ia, -EINVAL); assert_return(!ia->addresses, -EINVAL); + iatype = be16toh(iaoption->code); + len = be16toh(iaoption->len); + switch (iatype) { case SD_DHCP6_OPTION_IA_NA: - if (*buflen < DHCP6_OPTION_IA_NA_LEN + sizeof(DHCP6Option) + - sizeof(addr->iaaddr)) { + if (len < DHCP6_OPTION_IA_NA_LEN) { r = -ENOBUFS; goto error; } iaaddr_offset = DHCP6_OPTION_IA_NA_LEN; - memcpy(&ia->id, *buf, iaaddr_offset); + memcpy(&ia->ia_na, iaoption->data, sizeof(ia->ia_na)); - lt_t1 = be32toh(ia->lifetime_t1); - lt_t2 = be32toh(ia->lifetime_t2); + lt_t1 = be32toh(ia->ia_na.lifetime_t1); + lt_t2 = be32toh(ia->ia_na.lifetime_t2); if (lt_t1 && lt_t2 && lt_t1 > lt_t2) { - log_dhcp6_client(client, "IA T1 %ds > T2 %ds", + log_dhcp6_client(client, "IA NA T1 %ds > T2 %ds", + lt_t1, lt_t2); + r = -EINVAL; + goto error; + } + + break; + + case SD_DHCP6_OPTION_IA_PD: + + if (len < sizeof(ia->ia_pd)) { + r = -ENOBUFS; + goto error; + } + + 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 && lt_t1 > lt_t2) { + log_dhcp6_client(client, "IA PD T1 %ds > T2 %ds", lt_t1, lt_t2); r = -EINVAL; goto error; @@ -247,17 +412,13 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, break; case SD_DHCP6_OPTION_IA_TA: - if (*buflen < DHCP6_OPTION_IA_TA_LEN + sizeof(DHCP6Option) + - sizeof(addr->iaaddr)) { + if (len < DHCP6_OPTION_IA_TA_LEN) { r = -ENOBUFS; goto error; } iaaddr_offset = DHCP6_OPTION_IA_TA_LEN; - memcpy(&ia->id, *buf, iaaddr_offset); - - ia->lifetime_t1 = 0; - ia->lifetime_t2 = 0; + memcpy(&ia->ia_ta.id, iaoption->data, sizeof(ia->ia_ta)); break; @@ -267,48 +428,63 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, } ia->type = iatype; + i = iaaddr_offset; - *buflen -= iaaddr_offset; - *buf += iaaddr_offset; + while (i < len) { + DHCP6Option *option = (DHCP6Option *)&iaoption->data[i]; - while ((r = option_parse_hdr(buf, buflen, &opt, &optlen)) >= 0) { + if (len < i + sizeof(*option) || len < i + sizeof(*option) + be16toh(option->len)) { + r = -ENOBUFS; + goto error; + } + + opt = be16toh(option->code); + optlen = be16toh(option->len); switch (opt) { case SD_DHCP6_OPTION_IAADDR: - addr = new0(DHCP6Address, 1); - if (!addr) { - r = -ENOMEM; + if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_NA, SD_DHCP6_OPTION_IA_TA)) { + log_dhcp6_client(client, "IA Address option not in IA NA or TA option"); + r = -EINVAL; goto error; } - LIST_INIT(addresses, addr); + r = dhcp6_option_parse_address(option, ia, <_valid); + if (r < 0) + goto error; + + if (lt_valid < lt_min) + lt_min = lt_valid; - memcpy(&addr->iaaddr, *buf, sizeof(addr->iaaddr)); + break; - lt_valid = be32toh(addr->iaaddr.lifetime_valid); - lt_pref = be32toh(addr->iaaddr.lifetime_valid); + case SD_DHCP6_OPTION_IA_PD_PREFIX: - if (!lt_valid || lt_pref > lt_valid) { - log_dhcp6_client(client, "IA preferred %ds > valid %ds", - lt_pref, lt_valid); - free(addr); - } else { - LIST_PREPEND(addresses, ia->addresses, addr); - if (lt_valid < lt_min) - lt_min = lt_valid; + if (!IN_SET(ia->type, SD_DHCP6_OPTION_IA_PD)) { + log_dhcp6_client(client, "IA PD Prefix option not in IA PD option"); + r = -EINVAL; + goto error; } + r = dhcp6_option_parse_pdprefix(option, ia, <_valid); + if (r < 0) + goto error; + + if (lt_valid < lt_min) + lt_min = lt_valid; + break; case SD_DHCP6_OPTION_STATUS_CODE: - if (optlen < sizeof(status)) - break; - status = (*buf)[0] << 8 | (*buf)[1]; + status = dhcp6_option_parse_status(option); if (status) { log_dhcp6_client(client, "IA status %d", status); + + dhcp6_lease_free_ia(ia); + r = -EINVAL; goto error; } @@ -320,30 +496,41 @@ int dhcp6_option_parse_ia(uint8_t **buf, size_t *buflen, uint16_t iatype, break; } - *buflen -= optlen; - *buf += optlen; + i += sizeof(*option) + optlen; } - if (r == -ENOMSG) - r = 0; + switch(iatype) { + case SD_DHCP6_OPTION_IA_NA: + if (!ia->ia_na.lifetime_t1 && !ia->ia_na.lifetime_t2) { + 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); - if (!ia->lifetime_t1 && !ia->lifetime_t2) { - lt_t1 = lt_min / 2; - lt_t2 = lt_min / 10 * 8; - ia->lifetime_t1 = htobe32(lt_t1); - ia->lifetime_t2 = htobe32(lt_t2); + log_dhcp6_client(client, "Computed IA NA T1 %ds and T2 %ds as both were zero", + lt_t1, lt_t2); + } - log_dhcp6_client(client, "Computed IA T1 %ds and T2 %ds as both were zero", - lt_t1, lt_t2); - } + break; - if (*buflen) - r = -ENOMSG; + case SD_DHCP6_OPTION_IA_PD: + if (!ia->ia_pd.lifetime_t1 && !ia->ia_pd.lifetime_t2) { + 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); -error: - *buf += *buflen; - *buflen = 0; + log_dhcp6_client(client, "Computed IA PD T1 %ds and T2 %ds as both were zero", + lt_t1, lt_t2); + } + break; + + default: + break; + } + +error: return r; } diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h index 7bbf183996..5f7e809ba1 100644 --- a/src/libsystemd-network/dhcp6-protocol.h +++ b/src/libsystemd-network/dhcp6-protocol.h @@ -34,6 +34,7 @@ struct DHCP6Message { } _packed_; be32_t transaction_id; }; + uint8_t options[]; } _packed_; typedef struct DHCP6Message DHCP6Message; diff --git a/src/libsystemd-network/radv-internal.h b/src/libsystemd-network/radv-internal.h index 441939b717..837e7f2603 100644 --- a/src/libsystemd-network/radv-internal.h +++ b/src/libsystemd-network/radv-internal.h @@ -93,6 +93,9 @@ struct sd_radv_prefix { } _packed_ opt; LIST_FIELDS(struct sd_radv_prefix, prefix); + + usec_t valid_until; + usec_t preferred_until; }; #define log_radv_full(level, error, fmt, ...) log_internal(level, error, __FILE__, __LINE__, __func__, "RADV: " fmt, ##__VA_ARGS__) diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 074a409cbf..0584059fec 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -54,6 +54,8 @@ struct sd_dhcp6_client { size_t mac_addr_len; uint16_t arp_type; DHCP6IA ia_na; + DHCP6IA ia_pd; + bool prefix_delegation; be32_t transaction_id; usec_t transaction_start; struct sd_dhcp6_lease *lease; @@ -230,7 +232,8 @@ int sd_dhcp6_client_set_iaid(sd_dhcp6_client *client, uint32_t iaid) { assert_return(client, -EINVAL); assert_return(IN_SET(client->state, DHCP6_STATE_STOPPED), -EBUSY); - client->ia_na.id = htobe32(iaid); + client->ia_na.ia_na.id = htobe32(iaid); + client->ia_pd.ia_pd.id = htobe32(iaid); return 0; } @@ -298,6 +301,14 @@ int sd_dhcp6_client_set_request_option(sd_dhcp6_client *client, uint16_t option) return 0; } +int sd_dhcp6_client_set_prefix_delegation(sd_dhcp6_client *client, bool delegation) { + assert_return(client, -EINVAL); + + client->prefix_delegation = delegation; + + return 0; +} + int sd_dhcp6_client_get_lease(sd_dhcp6_client *client, sd_dhcp6_lease **ret) { assert_return(client, -EINVAL); @@ -411,6 +422,15 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return r; } + if (client->prefix_delegation) { + r = dhcp6_option_append_pd(opt, optlen, &client->ia_pd); + if (r < 0) + return r; + + opt += r; + optlen -= r; + } + break; case DHCP6_STATE_REQUEST: @@ -437,6 +457,15 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return r; } + if (client->prefix_delegation) { + r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd); + if (r < 0) + return r; + + opt += r; + optlen -= r; + } + break; case DHCP6_STATE_REBIND: @@ -452,6 +481,15 @@ static int client_send_message(sd_dhcp6_client *client, usec_t time_now) { return r; } + if (client->prefix_delegation) { + r = dhcp6_option_append_pd(opt, optlen, &client->lease->pd); + if (r < 0) + return r; + + opt += r; + optlen -= r; + } + break; case DHCP6_STATE_STOPPED: @@ -707,16 +745,20 @@ error: static int client_ensure_iaid(sd_dhcp6_client *client) { int r; + be32_t iaid; assert(client); - if (client->ia_na.id) + if (client->ia_na.ia_na.id) return 0; - r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &client->ia_na.id); + r = dhcp_identifier_set_iaid(client->ifindex, client->mac_addr, client->mac_addr_len, &iaid); if (r < 0) return r; + client->ia_na.ia_na.id = iaid; + client->ia_pd.ia_pd.id = iaid; + return 0; } @@ -725,23 +767,34 @@ static int client_parse_message( DHCP6Message *message, size_t len, sd_dhcp6_lease *lease) { + size_t pos = 0; int r; - uint8_t *optval, *option, *id = NULL; - uint16_t optcode, status; - size_t optlen, id_len; bool clientid = false; - be32_t iaid_lease; + uint8_t *id = NULL; + size_t id_len; + uint32_t lt_t1 = ~0, lt_t2 = ~0; assert(client); assert(message); assert(len >= sizeof(DHCP6Message)); assert(lease); - option = (uint8_t *)message + sizeof(DHCP6Message); len -= sizeof(DHCP6Message); - while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen, - &optval)) >= 0) { + while (pos < len) { + DHCP6Option *option = (DHCP6Option *)&message->options[pos]; + uint16_t optcode, optlen; + int status; + uint8_t *optval; + be32_t iaid_lease; + + if (len < sizeof(DHCP6Option) || len < sizeof(DHCP6Option) + be16toh(option->len)) + return -ENOBUFS; + + optcode = be16toh(option->code); + optlen = be16toh(option->len); + optval = option->data; + switch (optcode) { case SD_DHCP6_OPTION_CLIENTID: if (clientid) { @@ -779,21 +832,21 @@ static int client_parse_message( if (optlen != 1) return -EINVAL; - r = dhcp6_lease_set_preference(lease, *optval); + r = dhcp6_lease_set_preference(lease, optval[0]); if (r < 0) return r; break; case SD_DHCP6_OPTION_STATUS_CODE: - if (optlen < 2) - return -EINVAL; - - status = optval[0] << 8 | optval[1]; + status = dhcp6_option_parse_status(option); if (status) { log_dhcp6_client(client, "%s Status %s", dhcp6_message_type_to_string(message->type), dhcp6_message_status_to_string(status)); + dhcp6_lease_free_ia(&lease->ia); + dhcp6_lease_free_ia(&lease->pd); + return -EINVAL; } @@ -806,8 +859,7 @@ static int client_parse_message( break; } - r = dhcp6_option_parse_ia(&optval, &optlen, optcode, - &lease->ia); + r = dhcp6_option_parse_ia(option, &lease->ia); if (r < 0 && r != -ENOMSG) return r; @@ -815,12 +867,45 @@ static int client_parse_message( if (r < 0) return r; - if (client->ia_na.id != iaid_lease) { - log_dhcp6_client(client, "%s has wrong IAID", + if (client->ia_na.ia_na.id != iaid_lease) { + log_dhcp6_client(client, "%s has wrong IAID for IA NA", dhcp6_message_type_to_string(message->type)); return -EINVAL; } + 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_t1)); + } + + break; + + case SD_DHCP6_OPTION_IA_PD: + if (client->state == DHCP6_STATE_INFORMATION_REQUEST) { + log_dhcp6_client(client, "Information request ignoring IA PD option"); + + break; + } + + r = dhcp6_option_parse_ia(option, &lease->pd); + if (r < 0 && r != -ENOMSG) + return r; + + r = dhcp6_lease_get_iaid(lease, &iaid_lease); + if (r < 0) + return r; + + if (client->ia_pd.ia_pd.id != iaid_lease) { + log_dhcp6_client(client, "%s has wrong IAID for IA PD", + dhcp6_message_type_to_string(message->type)); + return -EINVAL; + } + + 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)); + } + break; case SD_DHCP6_OPTION_RAPID_COMMIT: @@ -859,11 +944,9 @@ static int client_parse_message( break; } + pos += sizeof(*option) + optlen; } - if (r == -ENOMSG) - r = 0; - if (r < 0 || !clientid) { log_dhcp6_client(client, "%s has incomplete options", dhcp6_message_type_to_string(message->type)); @@ -875,6 +958,17 @@ static int client_parse_message( if (r < 0) log_dhcp6_client(client, "%s has no server id", dhcp6_message_type_to_string(message->type)); + return r; + } + + 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); } return r; @@ -1133,17 +1227,17 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { case DHCP6_STATE_BOUND: - if (client->lease->ia.lifetime_t1 == 0xffffffff || - client->lease->ia.lifetime_t2 == 0xffffffff) { + if (client->lease->ia.ia_na.lifetime_t1 == 0xffffffff || + client->lease->ia.ia_na.lifetime_t2 == 0xffffffff) { log_dhcp6_client(client, "Infinite T1 0x%08x or T2 0x%08x", - be32toh(client->lease->ia.lifetime_t1), - be32toh(client->lease->ia.lifetime_t2)); + be32toh(client->lease->ia.ia_na.lifetime_t1), + be32toh(client->lease->ia.ia_na.lifetime_t2)); return 0; } - timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t1) * USEC_PER_SEC); + timeout = client_timeout_compute_random(be32toh(client->lease->ia.ia_na.lifetime_t1) * USEC_PER_SEC); log_dhcp6_client(client, "T1 expires in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC)); @@ -1165,7 +1259,7 @@ static int client_start(sd_dhcp6_client *client, enum DHCP6State state) { if (r < 0) goto error; - timeout = client_timeout_compute_random(be32toh(client->lease->ia.lifetime_t2) * USEC_PER_SEC); + timeout = client_timeout_compute_random(be32toh(client->lease->ia.ia_na.lifetime_t2) * USEC_PER_SEC); log_dhcp6_client(client, "T2 expires in %s", format_timespan(time_string, FORMAT_TIMESPAN_MAX, timeout, USEC_PER_SEC)); @@ -1354,6 +1448,7 @@ int sd_dhcp6_client_new(sd_dhcp6_client **ret) { client->n_ref = 1; client->ia_na.type = SD_DHCP6_OPTION_IA_NA; + client->ia_pd.type = SD_DHCP6_OPTION_IA_PD; client->ifindex = -1; client->fd = -1; diff --git a/src/libsystemd-network/sd-dhcp6-lease.c b/src/libsystemd-network/sd-dhcp6-lease.c index 6f604e072f..1f3a782f8c 100644 --- a/src/libsystemd-network/sd-dhcp6-lease.c +++ b/src/libsystemd-network/sd-dhcp6-lease.c @@ -49,7 +49,7 @@ int dhcp6_lease_ia_rebind_expire(const DHCP6IA *ia, uint32_t *expire) { valid = t; } - t = be32toh(ia->lifetime_t2); + t = be32toh(ia->ia_na.lifetime_t2); if (t > valid) return -EINVAL; @@ -144,7 +144,7 @@ int dhcp6_lease_get_iaid(sd_dhcp6_lease *lease, be32_t *iaid) { assert_return(lease, -EINVAL); assert_return(iaid, -EINVAL); - *iaid = lease->ia.id; + *iaid = lease->ia.ia_na.id; return 0; } @@ -176,6 +176,37 @@ void sd_dhcp6_lease_reset_address_iter(sd_dhcp6_lease *lease) { lease->addr_iter = lease->ia.addresses; } +int sd_dhcp6_lease_get_pd(sd_dhcp6_lease *lease, struct in6_addr *prefix, + uint8_t *prefix_len, + uint32_t *lifetime_preferred, + uint32_t *lifetime_valid) { + assert_return(lease, -EINVAL); + assert_return(prefix, -EINVAL); + assert_return(prefix_len, -EINVAL); + assert_return(lifetime_preferred, -EINVAL); + assert_return(lifetime_valid, -EINVAL); + + if (!lease->prefix_iter) + return -ENOMSG; + + memcpy(prefix, &lease->prefix_iter->iapdprefix.address, + sizeof(struct in6_addr)); + *prefix_len = lease->prefix_iter->iapdprefix.prefixlen; + *lifetime_preferred = + be32toh(lease->prefix_iter->iapdprefix.lifetime_preferred); + *lifetime_valid = + be32toh(lease->prefix_iter->iapdprefix.lifetime_valid); + + lease->prefix_iter = lease->prefix_iter->addresses_next; + + return 0; +} + +void sd_dhcp6_lease_reset_pd_prefix_iter(sd_dhcp6_lease *lease) { + if (lease) + lease->prefix_iter = lease->pd.addresses; +} + int dhcp6_lease_set_dns(sd_dhcp6_lease *lease, uint8_t *optval, size_t optlen) { int r; @@ -382,6 +413,7 @@ sd_dhcp6_lease *sd_dhcp6_lease_unref(sd_dhcp6_lease *lease) { free(lease->serverid); dhcp6_lease_free_ia(&lease->ia); + dhcp6_lease_free_ia(&lease->pd); free(lease->dns); diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c index f6c984f011..f30d6164ea 100644 --- a/src/libsystemd-network/sd-radv.c +++ b/src/libsystemd-network/sd-radv.c @@ -168,6 +168,12 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, .msg_namelen = sizeof(dst_addr), .msg_iov = iov, }; + usec_t time_now; + int r; + + r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now); + if (r < 0) + return r; if (dst && !in_addr_is_null(AF_INET6, (union in_addr_union*) dst)) dst_addr.sin6_addr = *dst; @@ -197,6 +203,18 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, } LIST_FOREACH(prefix, p, ra->prefixes) { + if (p->valid_until) { + + if (time_now > p->valid_until) + p->opt.valid_lifetime = 0; + else + p->opt.valid_lifetime = htobe32((p->valid_until - time_now) / USEC_PER_SEC); + + if (time_now > p->preferred_until) + p->opt.preferred_lifetime = 0; + else + p->opt.preferred_lifetime = htobe32((p->preferred_until - time_now) / USEC_PER_SEC); + } iov[msg.msg_iovlen].iov_base = &p->opt; iov[msg.msg_iovlen].iov_len = sizeof(p->opt); msg.msg_iovlen++; @@ -445,9 +463,6 @@ _public_ int sd_radv_set_mtu(sd_radv *ra, uint32_t mtu) { assert_return(ra, -EINVAL); assert_return(mtu >= 1280, -EINVAL); - if (ra->state != SD_RADV_STATE_IDLE) - return -EBUSY; - ra->mtu = mtu; return 0; @@ -517,9 +532,13 @@ _public_ int sd_radv_set_preference(sd_radv *ra, unsigned preference) { return r; } -_public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) { +_public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p, bool dynamic) { sd_radv_prefix *cur; + int r; _cleanup_free_ char *addr_p = NULL; + char time_string_preferred[FORMAT_TIMESPAN_MAX]; + char time_string_valid[FORMAT_TIMESPAN_MAX]; + usec_t time_now, valid, preferred, valid_until, preferred_until; assert_return(ra, -EINVAL); @@ -527,7 +546,6 @@ _public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) { return -EINVAL; LIST_FOREACH(prefix, cur, ra->prefixes) { - int r; r = in_addr_prefix_intersect(AF_INET6, (union in_addr_union*) &cur->opt.in6_addr, @@ -538,12 +556,15 @@ _public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) { _cleanup_free_ char *addr_cur = NULL; (void) in_addr_to_string(AF_INET6, - (union in_addr_union*) &cur->opt.in6_addr, - &addr_cur); - (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &p->opt.in6_addr, &addr_p); + if (dynamic && cur->opt.prefixlen == p->opt.prefixlen) + goto update; + + (void) in_addr_to_string(AF_INET6, + (union in_addr_union*) &cur->opt.in6_addr, + &addr_cur); log_radv("IPv6 prefix %s/%u already configured, ignoring %s/%u", addr_cur, cur->opt.prefixlen, addr_p, p->opt.prefixlen); @@ -559,11 +580,69 @@ _public_ int sd_radv_add_prefix(sd_radv *ra, sd_radv_prefix *p) { ra->n_prefixes++; (void) in_addr_to_string(AF_INET6, (union in_addr_union*) &p->opt.in6_addr, &addr_p); - log_radv("Added prefix %s/%d", addr_p, p->opt.prefixlen); + + if (!dynamic) { + log_radv("Added prefix %s/%d", addr_p, p->opt.prefixlen); + return 0; + } + + cur = p; + + update: + r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now); + if (r < 0) + return r; + + valid = be32toh(p->opt.valid_lifetime) * USEC_PER_SEC; + valid_until = usec_add(valid, time_now); + if (valid_until == USEC_INFINITY) + return -EOVERFLOW; + + preferred = be32toh(p->opt.preferred_lifetime) * USEC_PER_SEC; + preferred_until = usec_add(preferred, time_now); + if (preferred_until == USEC_INFINITY) + return -EOVERFLOW; + + cur->valid_until = valid_until; + cur->preferred_until = preferred_until; + + log_radv("%s prefix %s/%u preferred %s valid %s", + cur? "Updated": "Added", + addr_p, p->opt.prefixlen, + format_timespan(time_string_preferred, FORMAT_TIMESPAN_MAX, + preferred, USEC_PER_SEC), + format_timespan(time_string_valid, FORMAT_TIMESPAN_MAX, + valid, USEC_PER_SEC)); return 0; } +_public_ sd_radv_prefix *sd_radv_remove_prefix(sd_radv *ra, + struct in6_addr *prefix, + uint8_t prefixlen) { + sd_radv_prefix *cur, *next; + + assert_return(ra, NULL); + assert_return(prefix, NULL); + + LIST_FOREACH_SAFE(prefix, cur, next, ra->prefixes) { + if (prefixlen != cur->opt.prefixlen) + continue; + + if (!in_addr_equal(AF_INET6, + (union in_addr_union *)prefix, + (union in_addr_union *)&cur->opt.in6_addr)) + continue; + + LIST_REMOVE(prefix, ra->prefixes, cur); + ra->n_prefixes--; + + break; + } + + return cur; +} + _public_ int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime, const struct in6_addr *dns, size_t n_dns) { _cleanup_free_ struct sd_radv_opt_dns *opt_rdnss = NULL; diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index a0418ecdb9..3eaf9111dc 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -156,6 +156,135 @@ static int test_option(sd_event *e) { return 0; } +static int test_option_status(sd_event *e) { + uint8_t option1[] = { + /* IA NA */ + 0x00, 0x03, 0x00, 0x12, 0x1a, 0x1d, 0x1a, 0x1d, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, + /* status option */ + 0x00, 0x0d, 0x00, 0x02, 0x00, 0x01, + }; + static const uint8_t option2[] = { + /* IA NA */ + 0x00, 0x03, 0x00, 0x2e, 0x1a, 0x1d, 0x1a, 0x1d, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, + /* IA Addr */ + 0x00, 0x05, 0x00, 0x1e, + 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 */ + 0x00, 0x0d, 0x00, 0x02, 0x00, 0x01, + }; + static const uint8_t option3[] = { + /* IA NA */ + 0x00, 0x03, 0x00, 0x34, 0x1a, 0x1d, 0x1a, 0x1d, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, + /* IA Addr */ + 0x00, 0x05, 0x00, 0x24, + 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 */ + 0x00, 0x0d, 0x00, 0x08, 0x00, 0x00, 'f', 'o', + 'o', 'b', 'a', 'r', + }; + static const uint8_t option4[] = { + /* IA PD */ + 0x00, 0x19, 0x00, 0x2f, 0x1a, 0x1d, 0x1a, 0x1d, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, + /* IA PD Prefix */ + 0x00, 0x1a, 0x00, 0x1f, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x80, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, + 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + /* status option */ + 0x00, 0x0d, 0x00, 0x02, 0x00, 0x00, + }; + static const uint8_t option5[] = { + /* IA PD */ + 0x00, 0x19, 0x00, 0x52, 0x1a, 0x1d, 0x1a, 0x1d, + 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, + /* IA PD Prefix #1 */ + 0x00, 0x1a, 0x00, 0x1f, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x80, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, + 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + /* status option */ + 0x00, 0x0d, 0x00, 0x02, 0x00, 0x00, + /* IA PD Prefix #2 */ + 0x00, 0x1a, 0x00, 0x1f, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x80, 0x20, 0x01, 0x0d, 0xb8, 0xc0, 0x0l, 0xd0, + 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + 0x00, 0x0d, 0x00, 0x02, 0x00, 0x00, + }; + DHCP6Option *option; + DHCP6IA ia, pd; + int r = 0; + + if (verbose) + printf("* %s\n", __FUNCTION__); + + zero(ia); + option = (DHCP6Option *)option1; + assert_se(sizeof(option1) == sizeof(DHCP6Option) + be16toh(option->len)); + + r = dhcp6_option_parse_ia(option, &ia); + assert_se(r == -EINVAL); + assert_se(ia.addresses == NULL); + + option->len = htobe16(17); + r = dhcp6_option_parse_ia(option, &ia); + assert_se(r == -ENOBUFS); + assert_se(ia.addresses == NULL); + + option->len = htobe16(sizeof(DHCP6Option)); + r = dhcp6_option_parse_ia(option, &ia); + assert_se(r == -ENOBUFS); + assert_se(ia.addresses == NULL); + + zero(ia); + option = (DHCP6Option *)option2; + assert_se(sizeof(option2) == sizeof(DHCP6Option) + be16toh(option->len)); + + r = dhcp6_option_parse_ia(option, &ia); + assert_se(r >= 0); + assert_se(ia.addresses == NULL); + + zero(ia); + option = (DHCP6Option *)option3; + assert_se(sizeof(option3) == sizeof(DHCP6Option) + be16toh(option->len)); + + r = dhcp6_option_parse_ia(option, &ia); + assert_se(r >= 0); + assert_se(ia.addresses != NULL); + + zero(pd); + option = (DHCP6Option *)option4; + assert_se(sizeof(option4) == sizeof(DHCP6Option) + be16toh(option->len)); + + r = dhcp6_option_parse_ia(option, &pd); + assert_se(r == 0); + assert_se(pd.addresses != NULL); + 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); + + zero(pd); + option = (DHCP6Option *)option5; + assert_se(sizeof(option5) == sizeof(DHCP6Option) + be16toh(option->len)); + + r = dhcp6_option_parse_ia(option, &pd); + assert_se(r == 0); + assert_se(pd.addresses != NULL); + + return 0; +} + static uint8_t msg_advertise[198] = { 0x02, 0x0f, 0xb4, 0xe5, 0x00, 0x01, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b, 0xf3, 0x30, @@ -217,14 +346,13 @@ static uint8_t fqdn_wire[16] = { static int test_advertise_option(sd_event *e) { _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; DHCP6Message *advertise = (DHCP6Message *)msg_advertise; - uint8_t *optval, *opt = msg_advertise + sizeof(DHCP6Message); - uint16_t optcode; - size_t optlen, len = sizeof(msg_advertise) - sizeof(DHCP6Message); + size_t len = sizeof(msg_advertise) - sizeof(DHCP6Message), pos = 0; be32_t val; uint8_t preference = 255; struct in6_addr addr; uint32_t lt_pref, lt_valid; int r; + uint8_t *opt; bool opt_clientid = false; struct in6_addr *addrs; char **domains; @@ -232,14 +360,19 @@ static int test_advertise_option(sd_event *e) { if (verbose) printf("* %s\n", __FUNCTION__); + assert_se(len >= sizeof(DHCP6Message)); + assert_se(dhcp6_lease_new(&lease) >= 0); assert_se(advertise->type == DHCP6_ADVERTISE); assert_se((be32toh(advertise->transaction_id) & 0x00ffffff) == 0x0fb4e5); - while ((r = dhcp6_option_parse(&opt, &len, &optcode, &optlen, - &optval)) >= 0) { + while (pos < len) { + DHCP6Option *option = (DHCP6Option *)&advertise->options[pos]; + const uint16_t optcode = be16toh(option->code); + const uint16_t optlen = be16toh(option->len); + uint8_t *optval = option->data; switch(optcode) { case SD_DHCP6_OPTION_CLIENTID: @@ -261,9 +394,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(&optval, &optlen, - optcode, - &lease->ia) >= 0); + assert_se(dhcp6_option_parse_ia(option, &lease->ia) >= 0); break; @@ -309,11 +440,11 @@ static int test_advertise_option(sd_event *e) { default: break; } - } - - assert_se(r == -ENOMSG); + pos += sizeof(*option) + optlen; + } + assert_se(pos == len); assert_se(opt_clientid); sd_dhcp6_lease_reset_address_iter(lease); @@ -415,15 +546,11 @@ static int test_client_send_reply(DHCP6Message *request) { return 0; } -static int test_client_verify_request(DHCP6Message *request, uint8_t *option, - size_t len) { +static int test_client_verify_request(DHCP6Message *request, size_t len) { _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - uint8_t *optval; - uint16_t optcode; - size_t optlen; + size_t pos = 0; bool found_clientid = false, found_iana = false, found_serverid = false, found_elapsed_time = false, found_fqdn = false; - int r; struct in6_addr addr; be32_t val; uint32_t lt_pref, lt_valid; @@ -432,8 +559,14 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option, assert_se(dhcp6_lease_new(&lease) >= 0); - while ((r = dhcp6_option_parse(&option, &len, - &optcode, &optlen, &optval)) >= 0) { + len -= sizeof(DHCP6Message); + + while (pos < len) { + DHCP6Option *option = (DHCP6Option *)&request->options[pos]; + uint16_t optcode = be16toh(option->code); + uint16_t optlen = be16toh(option->len); + uint8_t *optval = option->data; + switch(optcode) { case SD_DHCP6_OPTION_CLIENTID: assert_se(!found_clientid); @@ -458,8 +591,7 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option, val = htobe32(120); assert_se(!memcmp(optval + 8, &val, sizeof(val))); - assert_se(!dhcp6_option_parse_ia(&optval, &optlen, - optcode, &lease->ia)); + assert_se(!dhcp6_option_parse_ia(option, &lease->ia)); break; @@ -489,9 +621,10 @@ static int test_client_verify_request(DHCP6Message *request, uint8_t *option, assert_se(!memcmp(optval + 1, fqdn_wire, sizeof(fqdn_wire))); break; } + + pos += sizeof(*option) + optlen; } - assert_se(r == -ENOMSG); assert_se(found_clientid && found_iana && found_serverid && found_elapsed_time); @@ -526,19 +659,21 @@ static int test_client_send_advertise(DHCP6Message *solicit) { return 0; } -static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option, - size_t len) { - uint8_t *optval; - uint16_t optcode; - size_t optlen; +static int test_client_verify_solicit(DHCP6Message *solicit, size_t len) { bool found_clientid = false, found_iana = false, found_elapsed_time = false, found_fqdn = false; - int r; + size_t pos = 0; assert_se(solicit->type == DHCP6_SOLICIT); - while ((r = dhcp6_option_parse(&option, &len, - &optcode, &optlen, &optval)) >= 0) { + len -= sizeof(DHCP6Message); + + while (pos < len) { + DHCP6Option *option = (DHCP6Option *)&solicit->options[pos]; + uint16_t optcode = be16toh(option->code); + uint16_t optlen = be16toh(option->len); + uint8_t *optval = option->data; + switch(optcode) { case SD_DHCP6_OPTION_CLIENTID: assert_se(!found_clientid); @@ -578,9 +713,11 @@ static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option, break; } + + pos += sizeof(*option) + optlen; } - assert_se(r == -ENOMSG); + assert_se(pos == len); assert_se(found_clientid && found_iana && found_elapsed_time); return 0; @@ -623,17 +760,15 @@ static void test_client_information_cb(sd_dhcp6_client *client, int event, assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0); assert_se(sd_dhcp6_client_start(client) >= 0); + } static int test_client_verify_information_request(DHCP6Message *information_request, - uint8_t *option, size_t len) { + size_t len) { _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL; - uint8_t *optval; - uint16_t optcode; - size_t optlen; + size_t pos = 0; bool found_clientid = false, found_elapsed_time = false; - int r; struct in6_addr addr; uint32_t lt_pref, lt_valid; @@ -641,8 +776,14 @@ static int test_client_verify_information_request(DHCP6Message *information_requ assert_se(dhcp6_lease_new(&lease) >= 0); - while ((r = dhcp6_option_parse(&option, &len, - &optcode, &optlen, &optval)) >= 0) { + len -= sizeof(DHCP6Message); + + while (pos < len) { + DHCP6Option *option = (DHCP6Option *)&information_request->options[pos]; + uint16_t optcode = be16toh(option->code); + uint16_t optlen = be16toh(option->len); + uint8_t *optval = option->data; + switch(optcode) { case SD_DHCP6_OPTION_CLIENTID: assert_se(!found_clientid); @@ -671,9 +812,11 @@ static int test_client_verify_information_request(DHCP6Message *information_requ break; } + + pos += sizeof(*option) + optlen; } - assert_se(r == -ENOMSG); + assert_se(pos == len); assert_se(found_clientid && found_elapsed_time); sd_dhcp6_lease_reset_address_iter(lease); @@ -689,7 +832,6 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, struct in6_addr mcast = IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT; DHCP6Message *message; - uint8_t *option; assert_se(s == test_dhcp_fd[0]); assert_se(server_address); @@ -699,21 +841,19 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, assert_se(IN6_ARE_ADDR_EQUAL(server_address, &mcast)); message = (DHCP6Message *)packet; - option = (uint8_t *)(message + 1); - len -= sizeof(DHCP6Message); assert_se(message->transaction_id & 0x00ffffff); if (test_client_message_num == 0) { - test_client_verify_information_request(message, option, len); + test_client_verify_information_request(message, len); test_client_send_reply(message); test_client_message_num++; } else if (test_client_message_num == 1) { - test_client_verify_solicit(message, option, len); + test_client_verify_solicit(message, len); test_client_send_advertise(message); test_client_message_num++; } else if (test_client_message_num == 2) { - test_client_verify_request(message, option, len); + test_client_verify_request(message, len); test_client_send_reply(message); test_client_message_num++; } @@ -789,6 +929,7 @@ int main(int argc, char *argv[]) { test_client_basic(e); test_option(e); + test_option_status(e); test_advertise_option(e); test_client_solicit(e); diff --git a/src/libsystemd-network/test-ndisc-ra.c b/src/libsystemd-network/test-ndisc-ra.c index c1a8d5a00d..1fc8ca9eba 100644 --- a/src/libsystemd-network/test-ndisc-ra.c +++ b/src/libsystemd-network/test-ndisc-ra.c @@ -342,8 +342,8 @@ static void test_ra(void) { if (prefix[i].preferred) assert_se(sd_radv_prefix_set_preferred_lifetime(p, prefix[i].preferred) >= 0); - assert_se((sd_radv_add_prefix(ra, p) >= 0) == prefix[i].succesful); - assert_se(sd_radv_add_prefix(ra, p) < 0); + assert_se((sd_radv_add_prefix(ra, p, false) >= 0) == prefix[i].succesful); + assert_se(sd_radv_add_prefix(ra, p, false) < 0); p = sd_radv_prefix_unref(p); assert_se(!p); |