]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
test-dhcp6-client: cleanups
authorYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 10 Feb 2022 07:14:21 +0000 (16:14 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 14 Feb 2022 06:09:28 +0000 (15:09 +0900)
This does many cleanups. Most prominently,
- check the sents packet more strictly,
- make the packets used in the test more readable,
- unify callbacks.

src/libsystemd-network/test-dhcp6-client.c

index 60bb7f0ca8c612f6ec49b42012e9cc200c04434f..5aa7ebb977aa3105ff1544c2ae4e3a2ad44b91ed 100644 (file)
 #include "tests.h"
 #include "time-util.h"
 
-static struct ether_addr mac_addr = {
-        .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
+#define DHCP6_CLIENT_EVENT_TEST_ADVERTISED 77
+#define IA_ID_BYTES                                                     \
+        0x0e, 0xcf, 0xa3, 0x7d
+#define IA_NA_ADDRESS1_BYTES                                            \
+        0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3, 0x09, 0x3c, 0x55, 0xad
+#define IA_NA_ADDRESS2_BYTES                                            \
+        0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3, 0x09, 0x3c, 0x55, 0xae
+#define IA_PD_PREFIX1_BYTES                                             \
+        0x2a, 0x02, 0x81, 0x0d, 0x98, 0x80, 0x37, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+#define IA_PD_PREFIX2_BYTES                                             \
+        0x2a, 0x02, 0x81, 0x0d, 0x98, 0x80, 0x37, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+#define DNS1_BYTES                                                      \
+        0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
+#define DNS2_BYTES                                                      \
+        0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02
+#define SNTP1_BYTES                                                     \
+        0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03
+#define SNTP2_BYTES                                                     \
+        0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04
+#define NTP1_BYTES                                                      \
+        0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05
+#define NTP2_BYTES                                                      \
+        0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06
+#define CLIENT_ID_BYTES                                                 \
+        0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x61, 0x77, 0x40, 0xde, 0x13, 0x42, 0xc3, 0xa2
+#define SERVER_ID_BYTES                                                 \
+        0x00, 0x01, 0x00, 0x01, 0x19, 0x40, 0x5c, 0x53, 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53
+
+static const struct in6_addr local_address =
+        { { { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, } } };
+static const struct in6_addr mcast_address =
+        IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
+static const struct in6_addr ia_na_address1 = { { { IA_NA_ADDRESS1_BYTES } } };
+static const struct in6_addr ia_na_address2 = { { { IA_NA_ADDRESS2_BYTES } } };
+static const struct in6_addr ia_pd_prefix1 = { { { IA_PD_PREFIX1_BYTES } } };
+static const struct in6_addr ia_pd_prefix2 = { { { IA_PD_PREFIX2_BYTES } } };
+static const struct in6_addr dns1 = { { { DNS1_BYTES } } };
+static const struct in6_addr dns2 = { { { DNS2_BYTES } } };
+static const struct in6_addr sntp1 = { { { SNTP1_BYTES } } };
+static const struct in6_addr sntp2 = { { { SNTP2_BYTES } } };
+static const struct in6_addr ntp1 = { { { NTP1_BYTES } } };
+static const struct in6_addr ntp2 = { { { NTP2_BYTES } } };
+static const uint8_t client_id[] = { CLIENT_ID_BYTES };
+static const uint8_t server_id[] = { SERVER_ID_BYTES };
+static const struct ether_addr mac = {
+        .ether_addr_octet = { 'A', 'B', 'C', '1', '2', '3' },
 };
-
-static sd_event_source *hangcheck;
-static int test_dhcp_fd[2];
+static int test_fd[2] = { -1, -1, };
 static int test_ifindex = 42;
-static int test_client_message_num;
-static be32_t test_iaid = 0;
-static uint8_t test_duid[14] = { };
+static unsigned test_client_sent_message_count = 0;
+static sd_dhcp6_client *client_ref = NULL;
+
+STATIC_DESTRUCTOR_REGISTER(client_ref, sd_dhcp6_client_unrefp);
 
-static void test_client_basic(sd_event *e) {
-        sd_dhcp6_client *client;
+static void test_client_basic(void) {
+        _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
         int v;
 
         log_debug("/* %s */", __func__);
@@ -46,16 +89,10 @@ static void test_client_basic(sd_event *e) {
         assert_se(sd_dhcp6_client_new(&client) >= 0);
         assert_se(client);
 
-        assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
-
         assert_se(sd_dhcp6_client_set_ifindex(client, 15) == 0);
-        assert_se(sd_dhcp6_client_set_ifindex(client, -42) == -EINVAL);
-        assert_se(sd_dhcp6_client_set_ifindex(client, -1) == -EINVAL);
         assert_se(sd_dhcp6_client_set_ifindex(client, 42) >= 0);
 
-        assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr,
-                                          sizeof (mac_addr),
-                                          ARPHRD_ETHER) >= 0);
+        assert_se(sd_dhcp6_client_set_mac(client, mac.ether_addr_octet, sizeof(mac), ARPHRD_ETHER) >= 0);
 
         assert_se(sd_dhcp6_client_set_fqdn(client, "host") == 1);
         assert_se(sd_dhcp6_client_set_fqdn(client, "host.domain") == 1);
@@ -107,51 +144,44 @@ static void test_client_basic(sd_event *e) {
         assert_se(sd_dhcp6_client_set_callback(client, NULL, NULL) >= 0);
 
         assert_se(sd_dhcp6_client_detach_event(client) >= 0);
-        assert_se(!sd_dhcp6_client_unref(client));
 }
 
 static void test_parse_domain(void) {
+        _cleanup_free_ char *domain = NULL;
+        _cleanup_strv_free_ char **list = NULL;
         uint8_t *data;
-        char *domain;
-        char **list;
-        int r;
 
         log_debug("/* %s */", __func__);
 
         data = (uint8_t []) { 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', 3, 'c', 'o', 'm', 0 };
-        r = dhcp6_option_parse_domainname(data, 13, &domain);
-        assert_se(r == 0);
+        assert_se(dhcp6_option_parse_domainname(data, 13, &domain) >= 0);
         assert_se(domain);
         assert_se(streq(domain, "example.com"));
-        free(domain);
+        domain = mfree(domain);
 
         data = (uint8_t []) { 4, 't', 'e', 's', 't' };
-        r = dhcp6_option_parse_domainname(data, 5, &domain);
-        assert_se(r == 0);
+        assert_se(dhcp6_option_parse_domainname(data, 5, &domain) >= 0);
         assert_se(domain);
         assert_se(streq(domain, "test"));
-        free(domain);
+        domain = mfree(domain);
 
         data = (uint8_t []) { 0 };
-        r = dhcp6_option_parse_domainname(data, 1, &domain);
-        assert_se(r < 0);
+        assert_se(dhcp6_option_parse_domainname(data, 1, &domain) < 0);
 
         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 == 0);
+        assert_se(dhcp6_option_parse_domainname_list(data, 21, &list) >= 0);
         assert_se(list);
         assert_se(streq(list[0], "example.com"));
         assert_se(streq(list[1], "foobar"));
-        strv_free(list);
+        assert_se(!list[2]);
+        list = strv_free(list);
 
         data = (uint8_t []) { 1, 'a', 0, 20, 'b', 'c' };
-        r = dhcp6_option_parse_domainname_list(data, 6, &list);
-        assert_se(r < 0);
+        assert_se(dhcp6_option_parse_domainname_list(data, 6, &list) < 0);
 
         data = (uint8_t []) { 0 , 0 };
-        r = dhcp6_option_parse_domainname_list(data, 2, &list);
-        assert_se(r < 0);
+        assert_se(dhcp6_option_parse_domainname_list(data, 2, &list) < 0);
 }
 
 static void test_option(void) {
@@ -358,36 +388,57 @@ static void test_option_status(void) {
 
 static void test_client_parse_message_issue_22099(void) {
         static const uint8_t msg[] = {
-                /* xid */
-                0x07, 0x7c, 0x4c, 0x16,
-                /* status code (zero length) */
-                0x00, 0x0e, 0x00, 0x00,
-                /* NTP servers (broken sub option and sub option length) */
-                0x00, 0x38, 0x00, 0x14, 0x01, 0x00, 0x10, 0x00, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                0xde, 0x15, 0xc8, 0xff, 0xfe, 0xef, 0x1e, 0x4e,
-                /* client ID */
-                0x00, 0x01, 0x00, 0x0e, 0x00, 0x02, 0x00, 0x00, 0xab, 0x11, 0x5c, 0x6b, 0x90, 0xec, 0xda, 0x95,
-                0x15, 0x45,
-                /* server ID */
-                0x00, 0x02, 0x00, 0x0a, 0x00, 0x03, 0x00, 0x01, 0xdc, 0x15, 0xc8, 0xef, 0x1e, 0x4e,
+                /* Message type */
+                DHCP6_MESSAGE_REPLY,
+                /* Transaction ID */
+                0x7c, 0x4c, 0x16,
+                /* Rapid commit */
+                0x00, SD_DHCP6_OPTION_RAPID_COMMIT, 0x00, 0x00,
+                /* NTP servers */
+                0x00, SD_DHCP6_OPTION_NTP_SERVER, 0x00, 0x14,
+                /* NTP server (broken sub option and sub option length) */
+                0x01, 0x00, 0x10, 0x00,
+                0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x15, 0xc8, 0xff, 0xfe, 0xef, 0x1e, 0x4e,
+                /* Client ID */
+                0x00, SD_DHCP6_OPTION_CLIENTID, 0x00, 0x0e,
+                0x00, 0x02, /* DUID-EN */
+                0x00, 0x00, 0xab, 0x11, /* pen */
+                0x5c, 0x6b, 0x90, 0xec, 0xda, 0x95, 0x15, 0x45, /* id */
+                /* Server ID */
+                0x00, SD_DHCP6_OPTION_SERVERID, 0x00, 0x0a,
+                0x00, 0x03, /* DUID-LL */
+                0x00, 0x01, /* htype */
+                0xdc, 0x15, 0xc8, 0xef, 0x1e, 0x4e, /* haddr */
                 /* preference */
-                0x00, 0x07, 0x00, 0x01, 0x00,
+                0x00, SD_DHCP6_OPTION_PREFERENCE, 0x00, 0x01,
+                0x00,
                 /* DNS servers */
-                0x00, 0x17, 0x00, 0x10, 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x15, 0xc8, 0xff,
-                0xfe, 0xef, 0x1e, 0x4e,
+                0x00, SD_DHCP6_OPTION_DNS_SERVERS, 0x00, 0x10,
+                0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x15, 0xc8, 0xff, 0xfe, 0xef, 0x1e, 0x4e,
                 /* v6 pcp server */
-                0x00, 0x56, 0x00, 0x10, 0x2a, 0x02, 0x81, 0x0d, 0x98, 0x80, 0x37, 0x00, 0xde, 0x15, 0xc8, 0xff,
-                0xfe, 0xef, 0x1e, 0x4e,
+                0x00, SD_DHCP6_OPTION_V6_PCP_SERVER, 0x00, 0x10,
+                0x2a, 0x02, 0x81, 0x0d, 0x98, 0x80, 0x37, 0x00, 0xde, 0x15, 0xc8, 0xff, 0xfe, 0xef, 0x1e, 0x4e,
                 /* IA_NA */
-                0x00, 0x03, 0x00, 0x28, 0xcc, 0x59, 0x11, 0x7b, 0x00, 0x00, 0x07, 0x08, 0x00, 0x00, 0x0b, 0x40,
-                /* IA_NA (iaaddr) */
-                0x00, 0x05, 0x00, 0x18, 0x2a, 0x02, 0x81, 0x0d, 0x98, 0x80, 0x37, 0x00, 0x6a, 0x05, 0xca, 0xff,
-                0xfe, 0xf1, 0x51, 0x53, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x00, 0x1c, 0x20,
+                0x00, SD_DHCP6_OPTION_IA_NA, 0x00, 0x28,
+                0xcc, 0x59, 0x11, 0x7b, /* iaid */
+                0x00, 0x00, 0x07, 0x08, /* lifetime T1 */
+                0x00, 0x00, 0x0b, 0x40, /* lifetime T2 */
+                /* IA_NA (iaaddr suboption) */
+                0x00, SD_DHCP6_OPTION_IAADDR, 0x00, 0x18,
+                0x2a, 0x02, 0x81, 0x0d, 0x98, 0x80, 0x37, 0x00, 0x6a, 0x05, 0xca, 0xff, 0xfe, 0xf1, 0x51, 0x53, /* address */
+                0x00, 0x00, 0x0e, 0x10, /* preferred lifetime */
+                0x00, 0x00, 0x1c, 0x20, /* valid lifetime */
                 /* IA_PD */
-                0x00, 0x19, 0x00, 0x29, 0xcc, 0x59, 0x11, 0x7b, 0x00, 0x00, 0x07, 0x08, 0x00, 0x00, 0x0b, 0x40,
-                /* IA_PD (iaprefix) */
-                0x00, 0x1a, 0x00, 0x19, 0x00, 0x00, 0x0e, 0x10, 0x00, 0x00, 0x1c, 0x20, 0x3a, 0x2a, 0x02, 0x81,
-                0x0d, 0x98, 0x80, 0x37, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0x00, SD_DHCP6_OPTION_IA_PD, 0x00, 0x29,
+                0xcc, 0x59, 0x11, 0x7b, /* iaid */
+                0x00, 0x00, 0x07, 0x08, /* lifetime T1 */
+                0x00, 0x00, 0x0b, 0x40, /* lifetime T2 */
+                /* IA_PD (iaprefix suboption) */
+                0x00, SD_DHCP6_OPTION_IA_PD_PREFIX, 0x00, 0x19,
+                0x00, 0x00, 0x0e, 0x10, /* preferred lifetime */
+                0x00, 0x00, 0x1c, 0x20, /* valid lifetime */
+                0x3a, /* prefixlen */
+                0x2a, 0x02, 0x81, 0x0d, 0x98, 0x80, 0x37, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* prefix */
         };
         static const uint8_t duid[] = {
                 0x00, 0x00, 0xab, 0x11, 0x5c, 0x6b, 0x90, 0xec, 0xda, 0x95, 0x15, 0x45,
@@ -404,614 +455,548 @@ static void test_client_parse_message_issue_22099(void) {
         assert_se(dhcp6_lease_new_from_message(client, (const DHCP6Message*) msg, sizeof(msg), NULL, NULL, &lease) >= 0);
 }
 
-static uint8_t msg_advertise[198] = {
-        0x02, 0x0f, 0xb4, 0xe5, 0x00, 0x01, 0x00, 0x0e,
-        0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b, 0xf3, 0x30,
-        0x3c, 0x97, 0x0e, 0xcf, 0xa3, 0x7d, 0x00, 0x03,
-        0x00, 0x5e, 0x0e, 0xcf, 0xa3, 0x7d, 0x00, 0x00,
-        0x00, 0x50, 0x00, 0x00, 0x00, 0x78, 0x00, 0x05,
-        0x00, 0x18, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad,
-        0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3, 0x09, 0x3c,
-        0x55, 0xad, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00,
-        0x00, 0xb4, 0x00, 0x0d, 0x00, 0x32, 0x00, 0x00,
-        0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x28,
-        0x65, 0x73, 0x29, 0x20, 0x72, 0x65, 0x6e, 0x65,
-        0x77, 0x65, 0x64, 0x2e, 0x20, 0x47, 0x72, 0x65,
-        0x65, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x66,
-        0x72, 0x6f, 0x6d, 0x20, 0x70, 0x6c, 0x61, 0x6e,
-        0x65, 0x74, 0x20, 0x45, 0x61, 0x72, 0x74, 0x68,
-        0x00, 0x17, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8,
-        0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x0b,
-        0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74,
-        0x72, 0x61, 0x00, 0x00, 0x1f, 0x00, 0x10, 0x20,
-        0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
-        0x02, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x19,
-        0x40, 0x5c, 0x53, 0x78, 0x2b, 0xcb, 0xb3, 0x6d,
-        0x53, 0x00, 0x07, 0x00, 0x01, 0x00
+static const uint8_t msg_information_request[] = {
+        /* Message type */
+        DHCP6_MESSAGE_INFORMATION_REQUEST,
+        /* Transaction ID */
+        0x0f, 0xb4, 0xe5,
+        /* MUD URL */
+        /* ORO */
+        0x00, SD_DHCP6_OPTION_ORO, 0x00, 0x08,
+        0x00, SD_DHCP6_OPTION_DNS_SERVERS,
+        0x00, SD_DHCP6_OPTION_DOMAIN_LIST,
+        0x00, SD_DHCP6_OPTION_NTP_SERVER,
+        0x00, SD_DHCP6_OPTION_SNTP_SERVERS,
+        /* Client ID */
+        0x00, SD_DHCP6_OPTION_CLIENTID, 0x00, 0x0e,
+        CLIENT_ID_BYTES,
+        /* Extra options */
+        /* Elapsed time */
+        0x00, SD_DHCP6_OPTION_ELAPSED_TIME, 0x00, 0x02,
+        0x00, 0x00,
 };
 
-static uint8_t msg_reply[191] = {
-        0x07, 0xf7, 0x4e, 0x57, 0x00, 0x02, 0x00, 0x0e,
-        0x00, 0x01, 0x00, 0x01, 0x19, 0x40, 0x5c, 0x53,
-        0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53, 0x00, 0x01,
-        0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b,
-        0xf3, 0x30, 0x3c, 0x97, 0x0e, 0xcf, 0xa3, 0x7d,
-        0x00, 0x03, 0x00, 0x4a, 0x0e, 0xcf, 0xa3, 0x7d,
-        0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x78,
-        0x00, 0x05, 0x00, 0x18, 0x20, 0x01, 0x0d, 0xb8,
-        0xde, 0xad, 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3,
-        0x09, 0x3c, 0x55, 0xad, 0x00, 0x00, 0x00, 0x96,
-        0x00, 0x00, 0x00, 0xb4, 0x00, 0x0d, 0x00, 0x1e,
-        0x00, 0x00, 0x41, 0x6c, 0x6c, 0x20, 0x61, 0x64,
-        0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x20,
-        0x77, 0x65, 0x72, 0x65, 0x20, 0x61, 0x73, 0x73,
-        0x69, 0x67, 0x6e, 0x65, 0x64, 0x2e, 0x00, 0x17,
-        0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad,
-        0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-        0x00, 0x01, 0x00, 0x18, 0x00, 0x0b, 0x03, 0x6c,
-        0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61,
-        0x00, 0x00, 0x1f, 0x00, 0x10, 0x20, 0x01, 0x0d,
-        0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00,
-        0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x27, 0x00,
-        0x0e, 0x01, 0x06, 0x63, 0x6c, 0x69, 0x65, 0x6e,
-        0x74, 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61
+static const uint8_t msg_solicit[] = {
+        /* Message type */
+        DHCP6_MESSAGE_SOLICIT,
+        /* Transaction ID */
+        0x0f, 0xb4, 0xe5,
+        /* Rapid commit */
+        0x00, SD_DHCP6_OPTION_RAPID_COMMIT, 0x00, 0x00,
+        /* IA_NA */
+        0x00, SD_DHCP6_OPTION_IA_NA, 0x00, 0x0c,
+        IA_ID_BYTES,
+        0x00, 0x00, 0x00, 0x00, /* lifetime T1 */
+        0x00, 0x00, 0x00, 0x00, /* lifetime T2 */
+        /* IA_PD */
+        0x00, SD_DHCP6_OPTION_IA_PD, 0x00, 0x0c,
+        IA_ID_BYTES,
+        0x00, 0x00, 0x00, 0x00, /* lifetime T1 */
+        0x00, 0x00, 0x00, 0x00, /* lifetime T2 */
+        /* Client FQDN */
+        0x00, SD_DHCP6_OPTION_CLIENT_FQDN, 0x00, 0x11,
+        DHCP6_FQDN_FLAG_S,
+        0x04, 'h', 'o', 's', 't', 0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a', 0x00,
+        /* User Class */
+        /* Vendor Class */
+        /* Vendor Options */
+        /* MUD URL */
+        /* ORO */
+        0x00, SD_DHCP6_OPTION_ORO, 0x00, 0x08,
+        0x00, SD_DHCP6_OPTION_DNS_SERVERS,
+        0x00, SD_DHCP6_OPTION_DOMAIN_LIST,
+        0x00, SD_DHCP6_OPTION_NTP_SERVER,
+        0x00, SD_DHCP6_OPTION_SNTP_SERVERS,
+        /* Client ID */
+        0x00, SD_DHCP6_OPTION_CLIENTID, 0x00, 0x0e,
+        CLIENT_ID_BYTES,
+        /* Extra options */
+        /* Elapsed time */
+        0x00, SD_DHCP6_OPTION_ELAPSED_TIME, 0x00, 0x02,
+        0x00, 0x00,
 };
 
-static uint8_t fqdn_wire[16] = {
-        0x04, 'h', 'o', 's', 't', 0x03, 'l', 'a', 'b',
-        0x05, 'i', 'n', 't', 'r', 'a', 0x00
+static const uint8_t msg_request[] = {
+        /* Message type */
+        DHCP6_MESSAGE_REQUEST,
+        /* Transaction ID */
+        0x00, 0x00, 0x00,
+        /* Server ID */
+        0x00, SD_DHCP6_OPTION_SERVERID, 0x00, 0x0e,
+        SERVER_ID_BYTES,
+        /* IA_NA */
+        0x00, SD_DHCP6_OPTION_IA_NA, 0x00, 0x44,
+        IA_ID_BYTES,
+        0x00, 0x00, 0x00, 0x00, /* lifetime T1 */
+        0x00, 0x00, 0x00, 0x00, /* lifetime T2 */
+        /* IA_NA (IAADDR suboption) */
+        0x00, SD_DHCP6_OPTION_IAADDR, 0x00, 0x18,
+        IA_NA_ADDRESS1_BYTES,
+        0x00, 0x00, 0x00, 0x00, /* preferred lifetime */
+        0x00, 0x00, 0x00, 0x00, /* valid lifetime */
+        /* IA_NA (IAADDR suboption) */
+        0x00, SD_DHCP6_OPTION_IAADDR, 0x00, 0x18,
+        IA_NA_ADDRESS2_BYTES,
+        0x00, 0x00, 0x00, 0x00, /* preferred lifetime */
+        0x00, 0x00, 0x00, 0x00, /* valid lifetime */
+        /* IA_PD */
+        0x00, SD_DHCP6_OPTION_IA_PD, 0x00, 0x46,
+        IA_ID_BYTES,
+        0x00, 0x00, 0x00, 0x00, /* lifetime T1 */
+        0x00, 0x00, 0x00, 0x00, /* lifetime T2 */
+        /* IA_PD (IA_PD_PREFIX suboption) */
+        0x00, SD_DHCP6_OPTION_IA_PD_PREFIX, 0x00, 0x19,
+        0x00, 0x00, 0x00, 0x00, /* preferred lifetime */
+        0x00, 0x00, 0x00, 0x00, /* valid lifetime */
+        0x40, /* prefixlen */
+        IA_PD_PREFIX1_BYTES,
+        /* IA_PD (IA_PD_PREFIX suboption) */
+        0x00, SD_DHCP6_OPTION_IA_PD_PREFIX, 0x00, 0x19,
+        0x00, 0x00, 0x00, 0x00, /* preferred lifetime */
+        0x00, 0x00, 0x00, 0x00, /* valid lifetime */
+        0x40, /* prefixlen */
+        IA_PD_PREFIX2_BYTES,
+        /* Client FQDN */
+        0x00, SD_DHCP6_OPTION_CLIENT_FQDN, 0x00, 0x11,
+        DHCP6_FQDN_FLAG_S,
+        0x04, 'h', 'o', 's', 't', 0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a', 0x00,
+        /* User Class */
+        /* Vendor Class */
+        /* Vendor Options */
+        /* MUD URL */
+        /* ORO */
+        0x00, SD_DHCP6_OPTION_ORO, 0x00, 0x08,
+        0x00, SD_DHCP6_OPTION_DNS_SERVERS,
+        0x00, SD_DHCP6_OPTION_DOMAIN_LIST,
+        0x00, SD_DHCP6_OPTION_NTP_SERVER,
+        0x00, SD_DHCP6_OPTION_SNTP_SERVERS,
+        /* Client ID */
+        0x00, SD_DHCP6_OPTION_CLIENTID, 0x00, 0x0e,
+        CLIENT_ID_BYTES,
+        /* Extra options */
+        /* Elapsed time */
+        0x00, SD_DHCP6_OPTION_ELAPSED_TIME, 0x00, 0x02,
+        0x00, 0x00,
 };
 
-static void test_advertise_option(sd_event *e) {
-        _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
-        DHCP6Message *advertise = (DHCP6Message *)msg_advertise;
-        size_t len = sizeof(msg_advertise) - sizeof(DHCP6Message), pos = 0;
-        uint32_t lt_pref, lt_valid;
-        bool opt_clientid = false;
-        const struct in6_addr *addrs;
-        uint8_t preference = 255;
-        struct in6_addr addr;
-        char **domains;
-        uint8_t *opt;
-        int r;
-        be32_t val;
-
-        log_debug("/* %s */", __func__);
-
-        assert_se(len >= sizeof(DHCP6Message));
-
-        assert_se(dhcp6_lease_new(&lease) >= 0);
-
-        assert_se(advertise->type == DHCP6_MESSAGE_ADVERTISE);
-        assert_se((be32toh(advertise->transaction_id) & 0x00ffffff) == 0x0fb4e5);
-
-        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:
-                        assert_se(optlen == 14);
+static const uint8_t msg_reply[] = {
+        /* Message type */
+        DHCP6_MESSAGE_REPLY,
+        /* Transaction ID */
+        0x0f, 0xb4, 0xe5,
+        /* Client ID */
+        0x00, SD_DHCP6_OPTION_CLIENTID, 0x00, 0x0e,
+        CLIENT_ID_BYTES,
+        /* Server ID */
+        0x00, SD_DHCP6_OPTION_SERVERID, 0x00, 0x0e,
+        SERVER_ID_BYTES,
+        /* Rapid commit */
+        0x00, SD_DHCP6_OPTION_RAPID_COMMIT, 0x00, 0x01,
+        0x00,
+        /* IA_NA */
+        0x00, SD_DHCP6_OPTION_IA_NA, 0x00, 0x66,
+        IA_ID_BYTES,
+        0x00, 0x00, 0x00, 0x50, /* lifetime T1 */
+        0x00, 0x00, 0x00, 0x78, /* lifetime T2 */
+        /* IA_NA (IAADDR suboption) */
+        0x00, SD_DHCP6_OPTION_IAADDR, 0x00, 0x18,
+        IA_NA_ADDRESS2_BYTES,
+        0x00, 0x00, 0x00, 0x96, /* preferred lifetime */
+        0x00, 0x00, 0x00, 0xb4, /* valid lifetime */
+        /* IA_NA (IAADDR suboption) */
+        0x00, SD_DHCP6_OPTION_IAADDR, 0x00, 0x18,
+        IA_NA_ADDRESS1_BYTES,
+        0x00, 0x00, 0x00, 0x96, /* preferred lifetime */
+        0x00, 0x00, 0x00, 0xb4, /* valid lifetime */
+        /* IA_NA (status code suboption) */
+        0x00, SD_DHCP6_OPTION_STATUS_CODE, 0x00, 0x1e,
+        0x00, 0x00, /* status code */
+        0x41, 0x6c, 0x6c, 0x20, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x20, 0x77, 0x65,
+        0x72, 0x65, 0x20, 0x61, 0x73, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x2e, /* status message */
+        /* IA_PD */
+        0x00, SD_DHCP6_OPTION_IA_PD, 0x00, 0x46,
+        IA_ID_BYTES,
+        0x00, 0x00, 0x00, 0x50, /* lifetime T1 */
+        0x00, 0x00, 0x00, 0x78, /* lifetime T2 */
+        /* IA_PD (IA_PD_PREFIX suboption) */
+        0x00, SD_DHCP6_OPTION_IA_PD_PREFIX, 0x00, 0x19,
+        0x00, 0x00, 0x00, 0x96, /* preferred lifetime */
+        0x00, 0x00, 0x00, 0xb4, /* valid lifetime */
+        0x40, /* prefixlen */
+        IA_PD_PREFIX2_BYTES,
+        /* IA_PD (IA_PD_PREFIX suboption) */
+        0x00, SD_DHCP6_OPTION_IA_PD_PREFIX, 0x00, 0x19,
+        0x00, 0x00, 0x00, 0x96, /* preferred lifetime */
+        0x00, 0x00, 0x00, 0xb4, /* valid lifetime */
+        0x40, /* prefixlen */
+        IA_PD_PREFIX1_BYTES,
+        /* DNS servers */
+        0x00, SD_DHCP6_OPTION_DNS_SERVERS, 0x00, 0x20,
+        DNS1_BYTES,
+        DNS2_BYTES,
+        /* SNTP servers */
+        0x00, SD_DHCP6_OPTION_SNTP_SERVERS, 0x00, 0x20,
+        SNTP1_BYTES,
+        SNTP2_BYTES,
+        /* NTP servers */
+        0x00, SD_DHCP6_OPTION_NTP_SERVER, 0x00, 0x37,
+        /* NTP server (address suboption) */
+        0x00, DHCP6_NTP_SUBOPTION_SRV_ADDR, 0x00, 0x10,
+        NTP1_BYTES,
+        /* NTP server (address suboption) */
+        0x00, DHCP6_NTP_SUBOPTION_SRV_ADDR, 0x00, 0x10,
+        NTP2_BYTES,
+        /* NTP server (fqdn suboption) */
+        0x00, DHCP6_NTP_SUBOPTION_SRV_FQDN, 0x00, 0x0b,
+        0x03, 'n', 't', 'p', 0x05, 'i', 'n', 't', 'r', 'a', 0x00,
+        /* Domain list */
+        0x00, SD_DHCP6_OPTION_DOMAIN_LIST, 0x00, 0x0b,
+        0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a', 0x00,
+        /* Client FQDN */
+        0x00, SD_DHCP6_OPTION_CLIENT_FQDN, 0x00, 0x12,
+        0x01, 0x06, 'c', 'l', 'i', 'e', 'n', 't', 0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a',
+};
 
-                        opt_clientid = true;
-                        break;
+static const uint8_t msg_advertise[] = {
+        /* Message type */
+        DHCP6_MESSAGE_ADVERTISE,
+        /* Transaction ID */
+        0x0f, 0xb4, 0xe5,
+        /* Client ID */
+        0x00, SD_DHCP6_OPTION_CLIENTID, 0x00, 0x0e,
+        CLIENT_ID_BYTES,
+        /* Server ID */
+        0x00, SD_DHCP6_OPTION_SERVERID, 0x00, 0x0e,
+        SERVER_ID_BYTES,
+        /* Preference */
+        0x00, SD_DHCP6_OPTION_PREFERENCE, 0x00, 0x01,
+        0xff,
+        /* IA_NA */
+        0x00, SD_DHCP6_OPTION_IA_NA, 0x00, 0x7a,
+        IA_ID_BYTES,
+        0x00, 0x00, 0x00, 0x50, /* lifetime T1 */
+        0x00, 0x00, 0x00, 0x78, /* lifetime T2 */
+        /* IA_NA (IAADDR suboption) */
+        0x00, SD_DHCP6_OPTION_IAADDR, 0x00, 0x18,
+        IA_NA_ADDRESS2_BYTES, /* address */
+        0x00, 0x00, 0x00, 0x96, /* preferred lifetime */
+        0x00, 0x00, 0x00, 0xb4, /* valid lifetime */
+        /* IA_NA (IAADDR suboption) */
+        0x00, SD_DHCP6_OPTION_IAADDR, 0x00, 0x18,
+        IA_NA_ADDRESS1_BYTES, /* address */
+        0x00, 0x00, 0x00, 0x96, /* preferred lifetime */
+        0x00, 0x00, 0x00, 0xb4, /* valid lifetime */
+        /* IA_NA (status code suboption) */
+        0x00, SD_DHCP6_OPTION_STATUS_CODE, 0x00, 0x32,
+        0x00, 0x00, /* status code */
+        0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x28, 0x65, 0x73, 0x29, 0x20, 0x72, 0x65, 0x6e, 0x65,
+        0x77, 0x65, 0x64, 0x2e, 0x20, 0x47, 0x72, 0x65, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x66,
+        0x72, 0x6f, 0x6d, 0x20, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x74, 0x20, 0x45, 0x61, 0x72, 0x74, 0x68, /* status message */
+        /* IA_PD */
+        0x00, SD_DHCP6_OPTION_IA_PD, 0x00, 0x46,
+        IA_ID_BYTES,
+        0x00, 0x00, 0x00, 0x50, /* lifetime T1 */
+        0x00, 0x00, 0x00, 0x78, /* lifetime T2 */
+        /* IA_PD (IA_PD_PREFIX suboption) */
+        0x00, SD_DHCP6_OPTION_IA_PD_PREFIX, 0x00, 0x19,
+        0x00, 0x00, 0x00, 0x96, /* preferred lifetime */
+        0x00, 0x00, 0x00, 0xb4, /* valid lifetime */
+        0x40, /* prefixlen */
+        IA_PD_PREFIX2_BYTES,
+        /* IA_PD (IA_PD_PREFIX suboption) */
+        0x00, SD_DHCP6_OPTION_IA_PD_PREFIX, 0x00, 0x19,
+        0x00, 0x00, 0x00, 0x96, /* preferred lifetime */
+        0x00, 0x00, 0x00, 0xb4, /* valid lifetime */
+        0x40, /* prefixlen */
+        IA_PD_PREFIX1_BYTES,
+        /* DNS servers */
+        0x00, SD_DHCP6_OPTION_DNS_SERVERS, 0x00, 0x20,
+        DNS1_BYTES,
+        DNS2_BYTES,
+        /* SNTP servers */
+        0x00, SD_DHCP6_OPTION_SNTP_SERVERS, 0x00, 0x20,
+        SNTP1_BYTES,
+        SNTP2_BYTES,
+        /* NTP servers */
+        0x00, SD_DHCP6_OPTION_NTP_SERVER, 0x00, 0x37,
+        /* NTP server (address suboption) */
+        0x00, DHCP6_NTP_SUBOPTION_SRV_ADDR, 0x00, 0x10,
+        NTP1_BYTES,
+        /* NTP server (address suboption) */
+        0x00, DHCP6_NTP_SUBOPTION_SRV_ADDR, 0x00, 0x10,
+        NTP2_BYTES,
+        /* NTP server (fqdn suboption) */
+        0x00, DHCP6_NTP_SUBOPTION_SRV_FQDN, 0x00, 0x0b,
+        0x03, 'n', 't', 'p', 0x05, 'i', 'n', 't', 'r', 'a', 0x00,
+        /* Domain list */
+        0x00, SD_DHCP6_OPTION_DOMAIN_LIST, 0x00, 0x0b,
+        0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a', 0x00,
+        /* Client FQDN */
+        0x00, SD_DHCP6_OPTION_CLIENT_FQDN, 0x00, 0x12,
+        0x01, 0x06, 'c', 'l', 'i', 'e', 'n', 't', 0x03, 'l', 'a', 'b', 0x05, 'i', 'n', 't', 'r', 'a',
+};
 
-                case SD_DHCP6_OPTION_IA_NA: {
-                        be32_t iaid = htobe32(0x0ecfa37d);
+static void test_client_verify_information_request(const DHCP6Message *msg, size_t len) {
+        log_debug("/* %s */", __func__);
 
-                        assert_se(optlen == 94);
-                        assert_se(optval == &msg_advertise[26]);
-                        assert_se(!memcmp(optval, &msg_advertise[26], optlen));
+        assert_se(len == sizeof(msg_information_request));
+        /* The elapsed time value is not deterministic. Skip it. */
+        assert_se(memcmp(msg, msg_information_request, len - sizeof(be16_t)) == 0);
+}
 
-                        assert_se(!memcmp(optval, &iaid, sizeof(val)));
+static void test_client_verify_solicit(const DHCP6Message *msg, size_t len) {
+        log_debug("/* %s */", __func__);
 
-                        val = htobe32(80);
-                        assert_se(!memcmp(optval + 4, &val, sizeof(val)));
+        assert_se(len == sizeof(msg_solicit));
+        /* The elapsed time value is not deterministic. Skip it. */
+        assert_se(memcmp(msg, msg_solicit, len - sizeof(be16_t)) == 0);
+}
 
-                        val = htobe32(120);
-                        assert_se(!memcmp(optval + 8, &val, sizeof(val)));
+static void test_client_verify_request(const DHCP6Message *msg, size_t len) {
+        log_debug("/* %s */", __func__);
 
-                        assert_se(dhcp6_option_parse_ia(NULL, iaid, optcode, optlen, optval, &lease->ia_na) >= 0);
+        assert_se(len == sizeof(msg_request));
+        assert_se(msg->type == DHCP6_MESSAGE_REQUEST);
+        /* The transaction ID and elapsed time value are not deterministic. Skip them. */
+        assert_se(memcmp(msg->options, msg_request + offsetof(DHCP6Message, options), len - offsetof(DHCP6Message, options) - sizeof(be16_t)) == 0);
+}
 
-                        break;
-                }
-                case SD_DHCP6_OPTION_SERVERID:
-                        assert_se(optlen == 14);
-                        assert_se(optval == &msg_advertise[179]);
-                        assert_se(!memcmp(optval, &msg_advertise[179], optlen));
+static void test_lease_common(sd_dhcp6_client *client) {
+        sd_dhcp6_lease *lease;
+        const struct in6_addr *addrs;
+        const char *str;
+        char **strv;
+        uint8_t *id;
+        size_t len;
 
-                        assert_se(dhcp6_lease_set_serverid(lease, optval, optlen) >= 0);
-                        break;
+        assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0);
 
-                case SD_DHCP6_OPTION_PREFERENCE:
-                        assert_se(optlen == 1);
-                        assert_se(!*optval);
+        assert_se(dhcp6_lease_get_clientid(lease, &id, &len) >= 0);
+        assert_se(memcmp_nn(id, len, client_id, sizeof(client_id)) == 0);
 
-                        assert_se(dhcp6_lease_set_preference(lease, *optval) >= 0);
-                        break;
+        assert_se(sd_dhcp6_lease_get_domains(lease, &strv) == 1);
+        assert_se(streq(strv[0], "lab.intra"));
+        assert_se(!strv[1]);
 
-                case SD_DHCP6_OPTION_ELAPSED_TIME:
-                        assert_se(optlen == 2);
+        assert_se(sd_dhcp6_lease_get_fqdn(lease, &str) >= 0);
+        assert_se(streq(str, "client.lab.intra"));
 
-                        break;
+        assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 2);
+        assert_se(in6_addr_equal(&addrs[0], &dns1));
+        assert_se(in6_addr_equal(&addrs[1], &dns2));
 
-                case SD_DHCP6_OPTION_DNS_SERVERS:
-                        assert_se(optlen == 16);
-                        assert_se(dhcp6_lease_add_dns(lease, optval, optlen) >= 0);
-                        break;
+        assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 2);
+        assert_se(in6_addr_equal(&addrs[0], &ntp1));
+        assert_se(in6_addr_equal(&addrs[1], &ntp2));
 
-                case SD_DHCP6_OPTION_DOMAIN_LIST:
-                        assert_se(optlen == 11);
-                        assert_se(dhcp6_lease_add_domains(lease, optval, optlen) >= 0);
-                        break;
+        assert_se(sd_dhcp6_lease_get_ntp_fqdn(lease, &strv) == 1);
+        assert_se(streq(strv[0], "ntp.intra"));
+        assert_se(!strv[1]);
 
-                case SD_DHCP6_OPTION_SNTP_SERVERS:
-                        assert_se(optlen == 16);
-                        assert_se(dhcp6_lease_add_sntp(lease, optval, optlen) >= 0);
-                        break;
+        assert_se(lease->sntp_count == 2);
+        assert_se(in6_addr_equal(&lease->sntp[0], &sntp1));
+        assert_se(in6_addr_equal(&lease->sntp[1], &sntp2));
+}
 
-                default:
-                        break;
-                }
+static void test_lease_managed(sd_dhcp6_client *client) {
+        sd_dhcp6_lease *lease;
+        struct in6_addr addr;
+        uint32_t lt_pref, lt_valid;
+        uint8_t *id, prefixlen;
+        size_t len;
 
-                pos += sizeof(*option) + optlen;
-        }
+        assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0);
 
-        assert_se(pos == len);
-        assert_se(opt_clientid);
+        assert_se(dhcp6_lease_get_serverid(lease, &id, &len) >= 0);
+        assert_se(memcmp_nn(id, len, server_id, sizeof(server_id)) == 0);
 
         sd_dhcp6_lease_reset_address_iter(lease);
         assert_se(sd_dhcp6_lease_get_address(lease, &addr, &lt_pref, &lt_valid) >= 0);
-        assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
+        assert_se(in6_addr_equal(&addr, &ia_na_address1));
+        assert_se(lt_pref == 150);
+        assert_se(lt_valid == 180);
+        assert_se(sd_dhcp6_lease_get_address(lease, &addr, &lt_pref, &lt_valid) >= 0);
+        assert_se(in6_addr_equal(&addr, &ia_na_address2));
         assert_se(lt_pref == 150);
         assert_se(lt_valid == 180);
         assert_se(sd_dhcp6_lease_get_address(lease, &addr, &lt_pref, &lt_valid) == -ENODATA);
 
         sd_dhcp6_lease_reset_address_iter(lease);
         assert_se(sd_dhcp6_lease_get_address(lease, &addr, &lt_pref, &lt_valid) >= 0);
-        assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
-        assert_se(sd_dhcp6_lease_get_address(lease, &addr, &lt_pref, &lt_valid) == -ENODATA);
-        sd_dhcp6_lease_reset_address_iter(lease);
+        assert_se(in6_addr_equal(&addr, &ia_na_address1));
+        assert_se(lt_pref == 150);
+        assert_se(lt_valid == 180);
         assert_se(sd_dhcp6_lease_get_address(lease, &addr, &lt_pref, &lt_valid) >= 0);
-        assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
+        assert_se(in6_addr_equal(&addr, &ia_na_address2));
+        assert_se(lt_pref == 150);
+        assert_se(lt_valid == 180);
         assert_se(sd_dhcp6_lease_get_address(lease, &addr, &lt_pref, &lt_valid) == -ENODATA);
 
-        assert_se(dhcp6_lease_get_serverid(lease, &opt, &len) >= 0);
-        assert_se(len == 14);
-        assert_se(!memcmp(opt, &msg_advertise[179], len));
-
-        assert_se(dhcp6_lease_get_preference(lease, &preference) >= 0);
-        assert_se(preference == 0);
-
-        r = sd_dhcp6_lease_get_dns(lease, &addrs);
-        assert_se(r == 1);
-        assert_se(!memcmp(addrs, &msg_advertise[124], r * 16));
-
-        r = sd_dhcp6_lease_get_domains(lease, &domains);
-        assert_se(r == 1);
-        assert_se(!strcmp("lab.intra", domains[0]));
-        assert_se(domains[1] == NULL);
-
-        r = sd_dhcp6_lease_get_ntp_addrs(lease, &addrs);
-        assert_se(r == 1);
-        assert_se(!memcmp(addrs, &msg_advertise[159], r * 16));
-}
-
-static int test_check_completed_in_2_seconds(sd_event_source *s, uint64_t usec, void *userdata) {
-        assert_not_reached();
-}
-
-static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
-                                   void *userdata) {
-        sd_event *e = userdata;
-        sd_dhcp6_lease *lease;
-        const struct in6_addr *addrs;
-        char **domains;
-
-        log_debug("/* %s */", __func__);
-
-        assert_se(e);
-        assert_se(event == SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE);
-
-        assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0);
-
-        assert_se(sd_dhcp6_lease_get_domains(lease, &domains) == 1);
-        assert_se(!strcmp("lab.intra", domains[0]));
-        assert_se(domains[1] == NULL);
-
-        assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 1);
-        assert_se(!memcmp(addrs, &msg_advertise[124], 16));
-
-        assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 1);
-        assert_se(!memcmp(addrs, &msg_advertise[159], 16));
-
-        assert_se(sd_dhcp6_client_set_request_option(client, SD_DHCP6_OPTION_DNS_SERVERS) == -EBUSY);
-
-        sd_event_exit(e, 0);
-}
-
-static void test_client_send_reply(DHCP6Message *request) {
-        DHCP6Message reply;
-
-        log_debug("/* %s */", __func__);
-
-        reply.transaction_id = request->transaction_id;
-        reply.type = DHCP6_MESSAGE_REPLY;
-
-        memcpy(msg_reply, &reply.transaction_id, 4);
-
-        memcpy(&msg_reply[26], test_duid, sizeof(test_duid));
-
-        memcpy(&msg_reply[44], &test_iaid, sizeof(test_iaid));
-
-        assert_se(write(test_dhcp_fd[1], msg_reply, sizeof(msg_reply)) == sizeof(msg_reply));
-}
-
-static void test_client_verify_request(DHCP6Message *request, size_t len) {
-        _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
-        bool found_clientid = false, found_iana = false, found_serverid = false,
-                found_elapsed_time = false, found_fqdn = false;
-        uint32_t lt_pref, lt_valid;
-        struct in6_addr addr;
-        size_t pos = 0;
-        be32_t val;
-
-        log_debug("/* %s */", __func__);
-
-        assert_se(request->type == DHCP6_MESSAGE_REQUEST);
-        assert_se(dhcp6_lease_new(&lease) >= 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);
-                        found_clientid = true;
-
-                        assert_se(!memcmp(optval, &test_duid,
-                                          sizeof(test_duid)));
-
-                        break;
-
-                case SD_DHCP6_OPTION_IA_NA:
-                        assert_se(!found_iana);
-                        found_iana = true;
-
-                        assert_se(optlen == 40);
-                        assert_se(!memcmp(optval, &test_iaid, sizeof(test_iaid)));
-
-                        /* T1 and T2 should not be set. */
-                        val = 0;
-                        assert_se(!memcmp(optval + 4, &val, sizeof(val)));
-                        assert_se(!memcmp(optval + 8, &val, sizeof(val)));
-
-                        /* Then, this should refuse all addresses. */
-                        assert_se(dhcp6_option_parse_ia(NULL, test_iaid, optcode, optlen, optval, &lease->ia_na) == -ENODATA);
-
-                        break;
-
-                case SD_DHCP6_OPTION_SERVERID:
-                        assert_se(!found_serverid);
-                        found_serverid = true;
-
-                        assert_se(optlen == 14);
-                        assert_se(!memcmp(&msg_advertise[179], optval, optlen));
-
-                        break;
-
-                case SD_DHCP6_OPTION_ELAPSED_TIME:
-                        assert_se(!found_elapsed_time);
-                        found_elapsed_time = true;
-
-                        assert_se(optlen == 2);
-
-                        break;
-                case SD_DHCP6_OPTION_CLIENT_FQDN:
-                        assert_se(!found_fqdn);
-                        found_fqdn = true;
-
-                        assert_se(optlen == 17);
-
-                        assert_se(optval[0] == 0x01);
-                        assert_se(!memcmp(optval + 1, fqdn_wire, sizeof(fqdn_wire)));
-                        break;
-                }
-
-                pos += sizeof(*option) + optlen;
-        }
-
-        assert_se(found_clientid && found_iana && found_serverid && found_elapsed_time);
-
-        sd_dhcp6_lease_reset_address_iter(lease);
+        sd_dhcp6_lease_reset_pd_prefix_iter(lease);
+        assert_se(sd_dhcp6_lease_get_pd(lease, &addr, &prefixlen, &lt_pref, &lt_valid) >= 0);
+        assert_se(in6_addr_equal(&addr, &ia_pd_prefix1));
+        assert_se(prefixlen == 64);
+        assert_se(lt_pref == 150);
+        assert_se(lt_valid == 180);
+        assert_se(sd_dhcp6_lease_get_pd(lease, &addr, &prefixlen, &lt_pref, &lt_valid) >= 0);
+        assert_se(in6_addr_equal(&addr, &ia_pd_prefix2));
+        assert_se(prefixlen == 64);
+        assert_se(lt_pref == 150);
+        assert_se(lt_valid == 180);
         assert_se(sd_dhcp6_lease_get_address(lease, &addr, &lt_pref, &lt_valid) == -ENODATA);
-}
-
-static void test_client_send_advertise(DHCP6Message *solicit) {
-        DHCP6Message advertise;
-
-        log_debug("/* %s */", __func__);
-
-        advertise.transaction_id = solicit->transaction_id;
-        advertise.type = DHCP6_MESSAGE_ADVERTISE;
-
-        memcpy(msg_advertise, &advertise.transaction_id, 4);
 
-        memcpy(&msg_advertise[8], test_duid, sizeof(test_duid));
-
-        memcpy(&msg_advertise[26], &test_iaid, sizeof(test_iaid));
+        sd_dhcp6_lease_reset_pd_prefix_iter(lease);
+        assert_se(sd_dhcp6_lease_get_pd(lease, &addr, &prefixlen, &lt_pref, &lt_valid) >= 0);
+        assert_se(in6_addr_equal(&addr, &ia_pd_prefix1));
+        assert_se(prefixlen == 64);
+        assert_se(lt_pref == 150);
+        assert_se(lt_valid == 180);
+        assert_se(sd_dhcp6_lease_get_pd(lease, &addr, &prefixlen, &lt_pref, &lt_valid) >= 0);
+        assert_se(in6_addr_equal(&addr, &ia_pd_prefix2));
+        assert_se(prefixlen == 64);
+        assert_se(lt_pref == 150);
+        assert_se(lt_valid == 180);
+        assert_se(sd_dhcp6_lease_get_address(lease, &addr, &lt_pref, &lt_valid) == -ENODATA);
 
-        assert_se(write(test_dhcp_fd[1], msg_advertise, sizeof(msg_advertise)) == sizeof(msg_advertise));
+        test_lease_common(client);
 }
 
-static void test_client_verify_solicit(DHCP6Message *solicit, size_t len) {
-        bool found_clientid = false, found_iana = false,
-                found_elapsed_time = false, found_fqdn = false;
-        size_t pos = 0;
-
-        log_debug("/* %s */", __func__);
-
-        assert_se(solicit->type == DHCP6_MESSAGE_SOLICIT);
+static void test_client_callback(sd_dhcp6_client *client, int event, void *userdata) {
+        switch (event) {
+        case SD_DHCP6_CLIENT_EVENT_STOP:
+                log_debug("/* %s (event=stop) */", __func__);
+                return;
 
-        len -= sizeof(DHCP6Message);
+        case SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST:
+                log_debug("/* %s (event=information-request) */", __func__);
 
-        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;
+                assert_se(test_client_sent_message_count == 1);
 
-                switch(optcode) {
-                case SD_DHCP6_OPTION_CLIENTID:
-                        assert_se(!found_clientid);
-                        found_clientid = true;
+                test_lease_common(client);
 
-                        assert_se(optlen == sizeof(test_duid));
-                        memcpy(&test_duid, optval, sizeof(test_duid));
+                assert_se(sd_dhcp6_client_set_information_request(client, false) >= 0);
+                assert_se(sd_dhcp6_client_start(client) >= 0);
+                assert_se(dhcp6_client_set_transaction_id(client, ((const DHCP6Message*) msg_advertise)->transaction_id) >= 0);
+                break;
 
-                        break;
+        case SD_DHCP6_CLIENT_EVENT_IP_ACQUIRE:
+                log_debug("/* %s (event=ip-acquire) */", __func__);
 
-                case SD_DHCP6_OPTION_IA_NA:
-                        assert_se(!found_iana);
-                        found_iana = true;
+                assert_se(IN_SET(test_client_sent_message_count, 3, 4));
 
-                        assert_se(optlen == 12);
+                test_lease_managed(client);
 
-                        memcpy(&test_iaid, optval, sizeof(test_iaid));
+                assert_se(sd_event_exit(sd_dhcp6_client_get_event(client), 0) >= 0);
+                break;
 
-                        break;
+        case DHCP6_CLIENT_EVENT_TEST_ADVERTISED: {
+                sd_dhcp6_lease *lease;
+                uint8_t preference;
 
-                case SD_DHCP6_OPTION_ELAPSED_TIME:
-                        assert_se(!found_elapsed_time);
-                        found_elapsed_time = true;
+                log_debug("/* %s (event=test-advertised) */", __func__);
 
-                        assert_se(optlen == 2);
+                assert_se(test_client_sent_message_count == 2);
 
-                        break;
+                test_lease_managed(client);
 
-                case SD_DHCP6_OPTION_CLIENT_FQDN:
-                        assert_se(!found_fqdn);
-                        found_fqdn = true;
+                assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0);
+                assert_se(dhcp6_lease_get_preference(lease, &preference) >= 0);
+                assert_se(preference == 0xff);
 
-                        assert_se(optlen == 17);
-
-                        assert_se(optval[0] == 0x01);
-                        assert_se(!memcmp(optval + 1, fqdn_wire, sizeof(fqdn_wire)));
-
-                        break;
-                }
-
-                pos += sizeof(*option) + optlen;
+                assert_se(dhcp6_client_set_transaction_id(client, ((const DHCP6Message*) msg_reply)->transaction_id) >= 0);
+                break;
         }
-
-        assert_se(pos == len);
-        assert_se(found_clientid && found_iana && found_elapsed_time);
-}
-
-static void test_client_information_cb(sd_dhcp6_client *client, int event, void *userdata) {
-        sd_event *e = userdata;
-        sd_dhcp6_lease *lease;
-        const struct in6_addr *addrs;
-        struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } };
-        char **domains;
-        const char *fqdn;
-
-        log_debug("/* %s */", __func__);
-
-        assert_se(e);
-        assert_se(event == SD_DHCP6_CLIENT_EVENT_INFORMATION_REQUEST);
-
-        assert_se(sd_dhcp6_client_get_lease(client, &lease) >= 0);
-
-        assert_se(sd_dhcp6_lease_get_domains(lease, &domains) == 1);
-        assert_se(!strcmp("lab.intra", domains[0]));
-        assert_se(domains[1] == NULL);
-
-        assert_se(sd_dhcp6_lease_get_fqdn(lease, &fqdn) >= 0);
-        assert_se(streq(fqdn, "client.intra"));
-
-        assert_se(sd_dhcp6_lease_get_dns(lease, &addrs) == 1);
-        assert_se(!memcmp(addrs, &msg_advertise[124], 16));
-
-        assert_se(sd_dhcp6_lease_get_ntp_addrs(lease, &addrs) == 1);
-        assert_se(!memcmp(addrs, &msg_advertise[159], 16));
-
-        assert_se(sd_dhcp6_client_set_callback(client, NULL, e) >= 0);
-        assert_se(sd_dhcp6_client_stop(client) >= 0);
-        assert_se(sd_dhcp6_client_set_information_request(client, false) >= 0);
-
-        assert_se(sd_dhcp6_client_set_callback(client, test_client_solicit_cb, e) >= 0);
-
-        assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0);
-
-        assert_se(sd_dhcp6_client_start(client) >= 0);
-}
-
-static void test_client_verify_information_request(DHCP6Message *information_request, size_t len) {
-        _cleanup_(sd_dhcp6_lease_unrefp) sd_dhcp6_lease *lease = NULL;
-        size_t pos = 0;
-        bool found_clientid = false, found_elapsed_time = false;
-        struct in6_addr addr;
-        uint32_t lt_pref, lt_valid;
-
-        log_debug("/* %s */", __func__);
-
-        assert_se(information_request->type == DHCP6_MESSAGE_INFORMATION_REQUEST);
-        assert_se(dhcp6_lease_new(&lease) >= 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);
-                        found_clientid = true;
-
-                        assert_se(optlen == sizeof(test_duid));
-                        memcpy(&test_duid, optval, sizeof(test_duid));
-
-                        break;
-
-                case SD_DHCP6_OPTION_IA_NA:
-                case SD_DHCP6_OPTION_SERVERID:
-                        assert_not_reached();
-
-                case SD_DHCP6_OPTION_ELAPSED_TIME:
-                        assert_se(!found_elapsed_time);
-                        found_elapsed_time = true;
-
-                        assert_se(optlen == 2);
-
-                        break;
-                }
-
-                pos += sizeof(*option) + optlen;
+        default:
+                assert_not_reached();
         }
-
-        assert_se(pos == len);
-        assert_se(found_clientid && found_elapsed_time);
-
-        sd_dhcp6_lease_reset_address_iter(lease);
-
-        assert_se(sd_dhcp6_lease_get_address(lease, &addr, &lt_pref, &lt_valid) == -ENODATA);
 }
 
-int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
-                                  const void *packet, size_t len) {
-        struct in6_addr mcast = IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
-        DHCP6Message *message;
+int dhcp6_network_send_udp_socket(int s, struct in6_addr *a, const void *packet, size_t len) {
+        log_debug("/* %s(count=%u) */", __func__, test_client_sent_message_count);
 
-        log_debug("/* %s */", __func__);
-
-        assert_se(s == test_dhcp_fd[0]);
-        assert_se(server_address);
+        assert_se(a);
+        assert_se(in6_addr_equal(a, &mcast_address));
         assert_se(packet);
-        assert_se(len > sizeof(DHCP6Message) + 4);
-        assert_se(IN6_ARE_ADDR_EQUAL(server_address, &mcast));
-
-        message = (DHCP6Message *)packet;
-
-        assert_se(message->transaction_id & 0x00ffffff);
-
-        if (test_client_message_num == 0) {
-                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, len);
-                test_client_send_advertise(message);
-                test_client_message_num++;
-        } else if (test_client_message_num == 2) {
-                test_client_verify_request(message, len);
-                test_client_send_reply(message);
-                test_client_message_num++;
+        assert_se(len >= sizeof(DHCP6Message));
+
+        switch (test_client_sent_message_count) {
+        case 0:
+                test_client_verify_information_request(packet, len);
+                assert_se(write(test_fd[1], msg_reply, sizeof(msg_reply)) == sizeof(msg_reply));
+                break;
+
+        case 1:
+                test_client_verify_solicit(packet, len);
+                assert_se(write(test_fd[1], msg_advertise, sizeof(msg_advertise)) == sizeof(msg_advertise));
+                break;
+
+        case 2:
+                test_client_callback(client_ref, DHCP6_CLIENT_EVENT_TEST_ADVERTISED, NULL);
+                test_client_verify_request(packet, len);
+                assert_se(write(test_fd[1], msg_reply, sizeof(msg_reply)) == sizeof(msg_reply));
+                break;
+
+        default:
+                assert_not_reached();
         }
 
+        test_client_sent_message_count++;
         return len;
 }
 
-int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *local_address) {
+int dhcp6_network_bind_udp_socket(int ifindex, struct in6_addr *a) {
         assert_se(ifindex == test_ifindex);
+        assert_se(a);
+        assert_se(in6_addr_equal(a, &local_address));
 
-        if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_dhcp_fd) < 0)
-                return -errno;
-
-        return test_dhcp_fd[0];
+        assert_se(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, test_fd) >= 0);
+        return TAKE_FD(test_fd[0]);
 }
 
-static void test_client_solicit(sd_event *e) {
-        sd_dhcp6_client *client;
-        struct in6_addr address = { { { 0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x01 } } };
-        int val;
+static void test_dhcp6_client(void) {
+        _cleanup_(sd_dhcp6_client_unrefp) sd_dhcp6_client *client = NULL;
+        _cleanup_(sd_event_unrefp) sd_event *e = NULL;
 
         log_debug("/* %s */", __func__);
 
-        assert_se(sd_dhcp6_client_new(&client) >= 0);
-        assert_se(client);
+        assert_se(sd_event_new(&e) >= 0);
+        assert_se(sd_event_add_time_relative(e, NULL, clock_boottime_or_monotonic(),
+                                             2 * USEC_PER_SEC, 0,
+                                             NULL, INT_TO_PTR(ETIMEDOUT)) >= 0);
 
+        assert_se(sd_dhcp6_client_new(&client) >= 0);
         assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
-
         assert_se(sd_dhcp6_client_set_ifindex(client, test_ifindex) == 0);
-        assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr,
-                                          sizeof (mac_addr),
-                                          ARPHRD_ETHER) >= 0);
-        assert_se(sd_dhcp6_client_set_fqdn(client, "host.lab.intra") == 1);
+        assert_se(sd_dhcp6_client_set_local_address(client, &local_address) >= 0);
+        assert_se(sd_dhcp6_client_set_fqdn(client, "host.lab.intra") >= 0);
+        assert_se(sd_dhcp6_client_set_iaid(client, unaligned_read_be32((uint8_t[]) { IA_ID_BYTES })) >= 0);
         dhcp6_client_set_test_mode(client, true);
 
-        assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0);
-        assert_se(val == 0);
-        assert_se(sd_dhcp6_client_set_information_request(client, 42) >= 0);
-        assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0);
-        assert_se(val);
-
-        assert_se(sd_dhcp6_client_set_callback(client,
-                                               test_client_information_cb, e) >= 0);
-
-        assert_se(sd_event_add_time_relative(e, &hangcheck, clock_boottime_or_monotonic(),
-                                             2 * USEC_PER_SEC, 0,
-                                             test_check_completed_in_2_seconds, NULL) >= 0);
-
-        assert_se(sd_dhcp6_client_set_local_address(client, &address) >= 0);
+        assert_se(sd_dhcp6_client_set_information_request(client, true) >= 0);
+        assert_se(sd_dhcp6_client_set_callback(client, test_client_callback, NULL) >= 0);
 
         assert_se(sd_dhcp6_client_start(client) >= 0);
 
-        sd_event_loop(e);
+        assert_se(dhcp6_client_set_transaction_id(client, ((const DHCP6Message*) msg_reply)->transaction_id) >= 0);
+
+        assert_se(client_ref = sd_dhcp6_client_ref(client));
 
-        hangcheck = sd_event_source_unref(hangcheck);
+        assert_se(sd_event_loop(e) >= 0);
 
-        assert_se(!sd_dhcp6_client_unref(client));
+        assert_se(test_client_sent_message_count == 3);
 
-        test_dhcp_fd[1] = safe_close(test_dhcp_fd[1]);
+        test_fd[1] = safe_close(test_fd[1]);
 }
 
 int main(int argc, char *argv[]) {
-        _cleanup_(sd_event_unrefp) sd_event *e;
-
-        assert_se(sd_event_new(&e) >= 0);
-
         test_setup_logging(LOG_DEBUG);
 
-        test_client_basic(e);
+        test_client_basic();
         test_parse_domain();
         test_option();
         test_option_status();
         test_client_parse_message_issue_22099();
-        test_advertise_option(e);
-        test_client_solicit(e);
+        test_dhcp6_client();
 
         return 0;
 }