summaryrefslogtreecommitdiffstats
path: root/src/libsystemd-network
diff options
context:
space:
mode:
authorLennart Poettering <lennart@poettering.net>2018-01-15 12:02:37 +0100
committerGitHub <noreply@github.com>2018-01-15 12:02:37 +0100
commit38edb7674bc22551f7c7ac010b296406ce25e7f1 (patch)
treef3ac7a58d28595754c348d97314e24a8a493b002 /src/libsystemd-network
parentman: --this-boot is deprecated (#7880) (diff)
parentman: Update man page regarding DHCPv6 Prefix Delegation (diff)
downloadsystemd-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.h65
-rw-r--r--src/libsystemd-network/dhcp6-lease-internal.h2
-rw-r--r--src/libsystemd-network/dhcp6-option.c315
-rw-r--r--src/libsystemd-network/dhcp6-protocol.h1
-rw-r--r--src/libsystemd-network/radv-internal.h3
-rw-r--r--src/libsystemd-network/sd-dhcp6-client.c151
-rw-r--r--src/libsystemd-network/sd-dhcp6-lease.c36
-rw-r--r--src/libsystemd-network/sd-radv.c97
-rw-r--r--src/libsystemd-network/test-dhcp6-client.c231
-rw-r--r--src/libsystemd-network/test-ndisc-ra.c4
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, &lt_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, &lt_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);