1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright (C) 2014 Intel Corporation. All rights reserved.
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
24 #include <sys/types.h>
25 #include <sys/socket.h>
27 #include <net/ethernet.h>
29 #include "socket-util.h"
32 #include "event-util.h"
35 #include "sd-dhcp6-client.h"
36 #include "dhcp6-protocol.h"
37 #include "dhcp6-internal.h"
38 #include "dhcp6-lease-internal.h"
40 static struct ether_addr mac_addr = {
41 .ether_addr_octet = {'A', 'B', 'C', '1', '2', '3'}
44 static bool verbose = false;
46 static sd_event_source *hangcheck;
47 static int test_dhcp_fd[2];
48 static int test_index = 42;
49 static int test_client_message_num;
50 static be32_t test_iaid = 0;
51 static uint8_t test_duid[14] = { };
53 static int test_client_basic(sd_event *e) {
54 sd_dhcp6_client *client;
57 printf("* %s\n", __FUNCTION__);
59 assert_se(sd_dhcp6_client_new(&client) >= 0);
62 assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
64 assert_se(sd_dhcp6_client_set_index(client, 15) == 0);
65 assert_se(sd_dhcp6_client_set_index(client, -42) == -EINVAL);
66 assert_se(sd_dhcp6_client_set_index(client, -1) == 0);
67 assert_se(sd_dhcp6_client_set_index(client, 42) >= 0);
69 assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr,
73 assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_CLIENTID) == -EINVAL);
74 assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EEXIST);
75 assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_NTP_SERVER) == -EEXIST);
76 assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_SNTP_SERVERS) == 0);
77 assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DOMAIN_LIST) == -EEXIST);
78 assert_se(sd_dhcp6_client_set_request_option(client, 10) == -EINVAL);
80 assert_se(sd_dhcp6_client_set_callback(client, NULL, NULL) >= 0);
82 assert_se(sd_dhcp6_client_detach_event(client) >= 0);
83 assert_se(!sd_dhcp6_client_unref(client));
88 static int test_option(sd_event *e) {
91 0x00, DHCP6_OPTION_ORO, 0x00, 0x07,
92 'A', 'B', 'C', 'D', 'E', 'F', 'G',
93 0x00, DHCP6_OPTION_VENDOR_CLASS, 0x00, 0x09,
94 '1', '2', '3', '4', '5', '6', '7', '8', '9',
99 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
100 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
101 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
106 uint8_t *optval, *buf, *out;
107 size_t zero = 0, pos = 3;
108 size_t buflen = sizeof(packet), outlen = sizeof(result);
111 printf("* %s\n", __FUNCTION__);
113 assert_se(buflen == outlen);
115 assert_se(dhcp6_option_parse(&buf, &zero, &optcode, &optlen,
116 &optval) == -ENOMSG);
123 assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen,
126 assert_se(buf == &packet[pos]);
127 assert_se(optcode == DHCP6_OPTION_ORO);
128 assert_se(optlen == 7);
129 assert_se(buflen + pos == sizeof(packet));
131 assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen,
133 assert_se(out == &result[pos]);
134 assert_se(*out == 0x00);
136 assert_se(dhcp6_option_parse(&buf, &buflen, &optcode, &optlen,
139 assert_se(buf == &packet[pos]);
140 assert_se(optcode == DHCP6_OPTION_VENDOR_CLASS);
141 assert_se(optlen == 9);
142 assert_se(buflen + pos == sizeof(packet));
144 assert_se(dhcp6_option_append(&out, &outlen, optcode, optlen,
146 assert_se(out == &result[pos]);
147 assert_se(*out == 'B');
149 assert_se(memcmp(packet, result, sizeof(packet)) == 0);
154 static uint8_t msg_advertise[198] = {
155 0x02, 0x0f, 0xb4, 0xe5, 0x00, 0x01, 0x00, 0x0e,
156 0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b, 0xf3, 0x30,
157 0x3c, 0x97, 0x0e, 0xcf, 0xa3, 0x7d, 0x00, 0x03,
158 0x00, 0x5e, 0x0e, 0xcf, 0xa3, 0x7d, 0x00, 0x00,
159 0x00, 0x50, 0x00, 0x00, 0x00, 0x78, 0x00, 0x05,
160 0x00, 0x18, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad,
161 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3, 0x09, 0x3c,
162 0x55, 0xad, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00,
163 0x00, 0xb4, 0x00, 0x0d, 0x00, 0x32, 0x00, 0x00,
164 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x28,
165 0x65, 0x73, 0x29, 0x20, 0x72, 0x65, 0x6e, 0x65,
166 0x77, 0x65, 0x64, 0x2e, 0x20, 0x47, 0x72, 0x65,
167 0x65, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x20, 0x66,
168 0x72, 0x6f, 0x6d, 0x20, 0x70, 0x6c, 0x61, 0x6e,
169 0x65, 0x74, 0x20, 0x45, 0x61, 0x72, 0x74, 0x68,
170 0x00, 0x17, 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8,
171 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00,
172 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x0b,
173 0x03, 0x6c, 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74,
174 0x72, 0x61, 0x00, 0x00, 0x1f, 0x00, 0x10, 0x20,
175 0x01, 0x0d, 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00,
176 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
177 0x02, 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x19,
178 0x40, 0x5c, 0x53, 0x78, 0x2b, 0xcb, 0xb3, 0x6d,
179 0x53, 0x00, 0x07, 0x00, 0x01, 0x00
182 static uint8_t msg_reply[173] = {
183 0x07, 0xf7, 0x4e, 0x57, 0x00, 0x02, 0x00, 0x0e,
184 0x00, 0x01, 0x00, 0x01, 0x19, 0x40, 0x5c, 0x53,
185 0x78, 0x2b, 0xcb, 0xb3, 0x6d, 0x53, 0x00, 0x01,
186 0x00, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x1a, 0x6b,
187 0xf3, 0x30, 0x3c, 0x97, 0x0e, 0xcf, 0xa3, 0x7d,
188 0x00, 0x03, 0x00, 0x4a, 0x0e, 0xcf, 0xa3, 0x7d,
189 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x78,
190 0x00, 0x05, 0x00, 0x18, 0x20, 0x01, 0x0d, 0xb8,
191 0xde, 0xad, 0xbe, 0xef, 0x78, 0xee, 0x1c, 0xf3,
192 0x09, 0x3c, 0x55, 0xad, 0x00, 0x00, 0x00, 0x96,
193 0x00, 0x00, 0x00, 0xb4, 0x00, 0x0d, 0x00, 0x1e,
194 0x00, 0x00, 0x41, 0x6c, 0x6c, 0x20, 0x61, 0x64,
195 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x20,
196 0x77, 0x65, 0x72, 0x65, 0x20, 0x61, 0x73, 0x73,
197 0x69, 0x67, 0x6e, 0x65, 0x64, 0x2e, 0x00, 0x17,
198 0x00, 0x10, 0x20, 0x01, 0x0d, 0xb8, 0xde, 0xad,
199 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
200 0x00, 0x01, 0x00, 0x18, 0x00, 0x0b, 0x03, 0x6c,
201 0x61, 0x62, 0x05, 0x69, 0x6e, 0x74, 0x72, 0x61,
202 0x00, 0x00, 0x1f, 0x00, 0x10, 0x20, 0x01, 0x0d,
203 0xb8, 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00,
204 0x00, 0x00, 0x00, 0x00, 0x01
207 static int test_advertise_option(sd_event *e) {
208 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
209 DHCP6Message *advertise = (DHCP6Message *)msg_advertise;
210 uint8_t *optval, *opt = msg_advertise + sizeof(DHCP6Message);
212 size_t optlen, len = sizeof(msg_advertise) - sizeof(DHCP6Message);
214 uint8_t preference = 255;
215 struct in6_addr addr;
216 uint32_t lt_pref, lt_valid;
218 bool opt_clientid = false;
221 printf("* %s\n", __FUNCTION__);
223 assert_se(dhcp6_lease_new(&lease) >= 0);
225 assert_se(advertise->type == DHCP6_ADVERTISE);
226 assert_se((be32toh(advertise->transaction_id) & 0x00ffffff) ==
229 while ((r = dhcp6_option_parse(&opt, &len, &optcode, &optlen,
233 case DHCP6_OPTION_CLIENTID:
234 assert_se(optlen == 14);
239 case DHCP6_OPTION_IA_NA:
240 assert_se(optlen == 94);
241 assert_se(!memcmp(optval, &msg_advertise[26], optlen));
243 val = htobe32(0x0ecfa37d);
244 assert_se(!memcmp(optval, &val, sizeof(val)));
247 assert_se(!memcmp(optval + 4, &val, sizeof(val)));
250 assert_se(!memcmp(optval + 8, &val, sizeof(val)));
252 assert_se(dhcp6_option_parse_ia(&optval, &optlen,
258 case DHCP6_OPTION_SERVERID:
259 assert_se(optlen == 14);
260 assert_se(!memcmp(optval, &msg_advertise[179], optlen));
262 assert_se(dhcp6_lease_set_serverid(lease, optval,
266 case DHCP6_OPTION_PREFERENCE:
267 assert_se(optlen == 1);
270 assert_se(dhcp6_lease_set_preference(lease,
274 case DHCP6_OPTION_ELAPSED_TIME:
275 assert_se(optlen == 2);
285 assert_se(r == -ENOMSG);
287 assert_se(opt_clientid);
289 assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, <_pref,
291 assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
292 assert_se(lt_pref == 150);
293 assert_se(lt_valid == 180);
294 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, <_pref,
295 <_valid) == -ENOMSG);
297 assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, <_pref,
299 assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
300 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, <_pref,
301 <_valid) == -ENOMSG);
302 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, <_pref,
303 <_valid) == -ENOMSG);
304 assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, <_pref,
306 assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
307 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, <_pref,
308 <_valid) == -ENOMSG);
310 assert_se(dhcp6_lease_get_serverid(lease, &opt, &len) >= 0);
311 assert_se(len == 14);
312 assert_se(!memcmp(opt, &msg_advertise[179], len));
314 assert_se(dhcp6_lease_get_preference(lease, &preference) >= 0);
315 assert_se(preference == 0);
320 static int test_hangcheck(sd_event_source *s, uint64_t usec, void *userdata) {
321 assert_not_reached("Test case should have completed in 2 seconds");
326 int detect_vm(const char **id) {
330 int detect_container(const char **id) {
334 int detect_virtualization(const char **id) {
338 int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
339 assert_se(index == test_index);
341 if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_dhcp_fd) < 0)
344 return test_dhcp_fd[0];
347 static int test_client_send_reply(DHCP6Message *request) {
350 reply.transaction_id = request->transaction_id;
351 reply.type = DHCP6_REPLY;
353 memcpy(msg_reply, &reply.transaction_id, 4);
355 memcpy(&msg_reply[26], test_duid, sizeof(test_duid));
357 memcpy(&msg_reply[44], &test_iaid, sizeof(test_iaid));
359 assert_se(write(test_dhcp_fd[1], msg_reply, sizeof(msg_reply))
360 == sizeof(msg_reply));
365 static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
367 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
371 bool found_clientid = false, found_iana = false, found_serverid = false,
372 found_elapsed_time = false;
374 struct in6_addr addr;
376 uint32_t lt_pref, lt_valid;
378 assert_se(request->type == DHCP6_REQUEST);
380 assert_se(dhcp6_lease_new(&lease) >= 0);
382 while ((r = dhcp6_option_parse(&option, &len,
383 &optcode, &optlen, &optval)) >= 0) {
385 case DHCP6_OPTION_CLIENTID:
386 assert_se(!found_clientid);
387 found_clientid = true;
389 assert_se(!memcmp(optval, &test_duid,
394 case DHCP6_OPTION_IA_NA:
395 assert_se(!found_iana);
399 assert_se(optlen == 40);
400 assert_se(!memcmp(optval, &test_iaid, sizeof(test_iaid)));
403 assert_se(!memcmp(optval + 4, &val, sizeof(val)));
406 assert_se(!memcmp(optval + 8, &val, sizeof(val)));
408 assert_se(!dhcp6_option_parse_ia(&optval, &optlen,
409 optcode, &lease->ia));
413 case DHCP6_OPTION_SERVERID:
414 assert_se(!found_serverid);
415 found_serverid = true;
417 assert_se(optlen == 14);
418 assert_se(!memcmp(&msg_advertise[179], optval, optlen));
422 case DHCP6_OPTION_ELAPSED_TIME:
423 assert_se(!found_elapsed_time);
424 found_elapsed_time = true;
426 assert_se(optlen == 2);
432 assert_se(r == -ENOMSG);
433 assert_se(found_clientid && found_iana && found_serverid &&
436 assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, <_pref,
438 assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
439 assert_se(lt_pref == 150);
440 assert_se(lt_valid == 180);
442 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, <_pref,
443 <_valid) == -ENOMSG);
448 static int test_client_send_advertise(DHCP6Message *solicit)
450 DHCP6Message advertise;
452 advertise.transaction_id = solicit->transaction_id;
453 advertise.type = DHCP6_ADVERTISE;
455 memcpy(msg_advertise, &advertise.transaction_id, 4);
457 memcpy(&msg_advertise[8], test_duid, sizeof(test_duid));
459 memcpy(&msg_advertise[26], &test_iaid, sizeof(test_iaid));
461 assert_se(write(test_dhcp_fd[1], msg_advertise, sizeof(msg_advertise))
462 == sizeof(msg_advertise));
467 static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
472 bool found_clientid = false, found_iana = false,
473 found_elapsed_time = false;
476 assert_se(solicit->type == DHCP6_SOLICIT);
478 while ((r = dhcp6_option_parse(&option, &len,
479 &optcode, &optlen, &optval)) >= 0) {
481 case DHCP6_OPTION_CLIENTID:
482 assert_se(!found_clientid);
483 found_clientid = true;
485 assert_se(optlen == sizeof(test_duid));
486 memcpy(&test_duid, optval, sizeof(test_duid));
490 case DHCP6_OPTION_IA_NA:
491 assert_se(!found_iana);
494 assert_se(optlen == 12);
496 memcpy(&test_iaid, optval, sizeof(test_iaid));
500 case DHCP6_OPTION_ELAPSED_TIME:
501 assert_se(!found_elapsed_time);
502 found_elapsed_time = true;
504 assert_se(optlen == 2);
510 assert_se(r == -ENOMSG);
511 assert_se(found_clientid && found_iana && found_elapsed_time);
516 int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
517 const void *packet, size_t len) {
518 struct in6_addr mcast =
519 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
520 DHCP6Message *message;
523 assert_se(s == test_dhcp_fd[0]);
524 assert_se(server_address);
526 assert_se(len > sizeof(DHCP6Message) + 4);
528 assert_se(IN6_ARE_ADDR_EQUAL(server_address, &mcast));
530 message = (DHCP6Message *)packet;
531 option = (uint8_t *)(message + 1);
532 len -= sizeof(DHCP6Message);
534 assert_se(message->transaction_id & 0x00ffffff);
536 if (test_client_message_num == 0) {
537 test_client_verify_solicit(message, option, len);
538 test_client_send_advertise(message);
539 test_client_message_num++;
540 } else if (test_client_message_num == 1) {
541 test_client_verify_request(message, option, len);
542 test_client_send_reply(message);
543 test_client_message_num++;
549 static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
551 sd_event *e = userdata;
554 assert_se(event == DHCP6_EVENT_IP_ACQUIRE);
556 assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EBUSY);
559 printf(" got DHCPv6 event %d\n", event);
564 static int test_client_solicit(sd_event *e) {
565 sd_dhcp6_client *client;
566 usec_t time_now = now(clock_boottime_or_monotonic());
569 printf("* %s\n", __FUNCTION__);
571 assert_se(sd_dhcp6_client_new(&client) >= 0);
574 assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
576 assert_se(sd_dhcp6_client_set_index(client, test_index) == 0);
577 assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr,
581 assert_se(sd_dhcp6_client_set_callback(client,
582 test_client_solicit_cb, e) >= 0);
584 assert_se(sd_event_add_time(e, &hangcheck, clock_boottime_or_monotonic(),
585 time_now + 2 * USEC_PER_SEC, 0,
586 test_hangcheck, NULL) >= 0);
588 assert_se(sd_dhcp6_client_start(client) >= 0);
592 hangcheck = sd_event_source_unref(hangcheck);
594 assert_se(!sd_dhcp6_client_unref(client));
596 test_dhcp_fd[1] = safe_close(test_dhcp_fd[1]);
601 int main(int argc, char *argv[]) {
602 _cleanup_event_unref_ sd_event *e;
604 assert_se(sd_event_new(&e) >= 0);
606 log_set_max_level(LOG_DEBUG);
607 log_parse_environment();
610 test_client_basic(e);
612 test_advertise_option(e);
613 test_client_solicit(e);
615 assert_se(!sd_event_unref(e));