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 = true;
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 static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
340 sd_event *e = userdata;
343 assert_se(event == DHCP6_EVENT_IP_ACQUIRE);
345 assert_se(sd_dhcp6_client_set_request_option(client, DHCP6_OPTION_DNS_SERVERS) == -EBUSY);
348 printf(" got DHCPv6 event %d\n", event);
353 static int test_client_send_reply(DHCP6Message *request) {
356 reply.transaction_id = request->transaction_id;
357 reply.type = DHCP6_REPLY;
359 memcpy(msg_reply, &reply.transaction_id, 4);
361 memcpy(&msg_reply[26], test_duid, sizeof(test_duid));
363 memcpy(&msg_reply[44], &test_iaid, sizeof(test_iaid));
365 assert_se(write(test_dhcp_fd[1], msg_reply, sizeof(msg_reply))
366 == sizeof(msg_reply));
371 static int test_client_verify_request(DHCP6Message *request, uint8_t *option,
373 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
377 bool found_clientid = false, found_iana = false, found_serverid = false,
378 found_elapsed_time = false;
380 struct in6_addr addr;
382 uint32_t lt_pref, lt_valid;
384 assert_se(request->type == DHCP6_REQUEST);
386 assert_se(dhcp6_lease_new(&lease) >= 0);
388 while ((r = dhcp6_option_parse(&option, &len,
389 &optcode, &optlen, &optval)) >= 0) {
391 case DHCP6_OPTION_CLIENTID:
392 assert_se(!found_clientid);
393 found_clientid = true;
395 assert_se(!memcmp(optval, &test_duid,
400 case DHCP6_OPTION_IA_NA:
401 assert_se(!found_iana);
405 assert_se(optlen == 40);
406 assert_se(!memcmp(optval, &test_iaid, sizeof(test_iaid)));
409 assert_se(!memcmp(optval + 4, &val, sizeof(val)));
412 assert_se(!memcmp(optval + 8, &val, sizeof(val)));
414 assert_se(!dhcp6_option_parse_ia(&optval, &optlen,
415 optcode, &lease->ia));
419 case DHCP6_OPTION_SERVERID:
420 assert_se(!found_serverid);
421 found_serverid = true;
423 assert_se(optlen == 14);
424 assert_se(!memcmp(&msg_advertise[179], optval, optlen));
428 case DHCP6_OPTION_ELAPSED_TIME:
429 assert_se(!found_elapsed_time);
430 found_elapsed_time = true;
432 assert_se(optlen == 2);
438 assert_se(r == -ENOMSG);
439 assert_se(found_clientid && found_iana && found_serverid &&
442 assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, <_pref,
444 assert_se(!memcmp(&addr, &msg_advertise[42], sizeof(addr)));
445 assert_se(lt_pref == 150);
446 assert_se(lt_valid == 180);
448 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, <_pref,
449 <_valid) == -ENOMSG);
454 static int test_client_send_advertise(DHCP6Message *solicit)
456 DHCP6Message advertise;
458 advertise.transaction_id = solicit->transaction_id;
459 advertise.type = DHCP6_ADVERTISE;
461 memcpy(msg_advertise, &advertise.transaction_id, 4);
463 memcpy(&msg_advertise[8], test_duid, sizeof(test_duid));
465 memcpy(&msg_advertise[26], &test_iaid, sizeof(test_iaid));
467 assert_se(write(test_dhcp_fd[1], msg_advertise, sizeof(msg_advertise))
468 == sizeof(msg_advertise));
473 static int test_client_verify_solicit(DHCP6Message *solicit, uint8_t *option,
478 bool found_clientid = false, found_iana = false,
479 found_elapsed_time = false;
482 assert_se(solicit->type == DHCP6_SOLICIT);
484 while ((r = dhcp6_option_parse(&option, &len,
485 &optcode, &optlen, &optval)) >= 0) {
487 case DHCP6_OPTION_CLIENTID:
488 assert_se(!found_clientid);
489 found_clientid = true;
491 assert_se(optlen == sizeof(test_duid));
492 memcpy(&test_duid, optval, sizeof(test_duid));
496 case DHCP6_OPTION_IA_NA:
497 assert_se(!found_iana);
500 assert_se(optlen == 12);
502 memcpy(&test_iaid, optval, sizeof(test_iaid));
506 case DHCP6_OPTION_ELAPSED_TIME:
507 assert_se(!found_elapsed_time);
508 found_elapsed_time = true;
510 assert_se(optlen == 2);
516 assert_se(r == -ENOMSG);
517 assert_se(found_clientid && found_iana && found_elapsed_time);
522 static void test_client_information_cb(sd_dhcp6_client *client, int event,
524 sd_event *e = userdata;
527 assert_se(event == DHCP6_EVENT_INFORMATION_REQUEST);
530 printf(" got DHCPv6 event %d\n", event);
532 assert_se(sd_dhcp6_client_set_information_request(client, false) >= 0);
533 assert_se(sd_dhcp6_client_set_callback(client,
534 test_client_solicit_cb, e) >= 0);
536 assert_se(sd_dhcp6_client_start(client) >= 0);
539 static int test_client_verify_information_request(DHCP6Message *information_request,
540 uint8_t *option, size_t len) {
542 _cleanup_dhcp6_lease_free_ sd_dhcp6_lease *lease = NULL;
546 bool found_clientid = false, found_elapsed_time = false;
548 struct in6_addr addr;
549 uint32_t lt_pref, lt_valid;
551 assert_se(information_request->type == DHCP6_INFORMATION_REQUEST);
553 assert_se(dhcp6_lease_new(&lease) >= 0);
555 while ((r = dhcp6_option_parse(&option, &len,
556 &optcode, &optlen, &optval)) >= 0) {
558 case DHCP6_OPTION_CLIENTID:
559 assert_se(!found_clientid);
560 found_clientid = true;
562 assert_se(optlen == sizeof(test_duid));
563 memcpy(&test_duid, optval, sizeof(test_duid));
567 case DHCP6_OPTION_IA_NA:
568 assert_not_reached("IA TA option must not be present");
572 case DHCP6_OPTION_SERVERID:
573 assert_not_reached("Server ID option must not be present");
577 case DHCP6_OPTION_ELAPSED_TIME:
578 assert_se(!found_elapsed_time);
579 found_elapsed_time = true;
581 assert_se(optlen == 2);
587 assert_se(r == -ENOMSG);
588 assert_se(found_clientid && found_elapsed_time);
590 assert_se(sd_dhcp6_lease_get_first_address(lease, &addr, <_pref,
591 <_valid) == -ENOMSG);
593 assert_se(sd_dhcp6_lease_get_next_address(lease, &addr, <_pref,
594 <_valid) == -ENOMSG);
599 int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
600 const void *packet, size_t len) {
601 struct in6_addr mcast =
602 IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
603 DHCP6Message *message;
606 assert_se(s == test_dhcp_fd[0]);
607 assert_se(server_address);
609 assert_se(len > sizeof(DHCP6Message) + 4);
611 assert_se(IN6_ARE_ADDR_EQUAL(server_address, &mcast));
613 message = (DHCP6Message *)packet;
614 option = (uint8_t *)(message + 1);
615 len -= sizeof(DHCP6Message);
617 assert_se(message->transaction_id & 0x00ffffff);
619 if (test_client_message_num == 0) {
620 test_client_verify_information_request(message, option, len);
621 test_client_send_reply(message);
622 test_client_message_num++;
623 } else if (test_client_message_num == 1) {
624 test_client_verify_solicit(message, option, len);
625 test_client_send_advertise(message);
626 test_client_message_num++;
627 } else if (test_client_message_num == 2) {
628 test_client_verify_request(message, option, len);
629 test_client_send_reply(message);
630 test_client_message_num++;
636 int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) {
637 assert_se(index == test_index);
639 if (socketpair(AF_UNIX, SOCK_STREAM, 0, test_dhcp_fd) < 0)
642 return test_dhcp_fd[0];
645 static int test_client_solicit(sd_event *e) {
646 sd_dhcp6_client *client;
647 usec_t time_now = now(clock_boottime_or_monotonic());
651 printf("* %s\n", __FUNCTION__);
653 assert_se(sd_dhcp6_client_new(&client) >= 0);
656 assert_se(sd_dhcp6_client_attach_event(client, e, 0) >= 0);
658 assert_se(sd_dhcp6_client_set_index(client, test_index) == 0);
659 assert_se(sd_dhcp6_client_set_mac(client, (const uint8_t *) &mac_addr,
663 assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0);
664 assert_se(val == false);
665 assert_se(sd_dhcp6_client_set_information_request(client, true) >= 0);
666 assert_se(sd_dhcp6_client_get_information_request(client, &val) >= 0);
667 assert_se(val == true);
669 assert_se(sd_dhcp6_client_set_callback(client,
670 test_client_information_cb, e) >= 0);
672 assert_se(sd_event_add_time(e, &hangcheck, clock_boottime_or_monotonic(),
673 time_now + 2 * USEC_PER_SEC, 0,
674 test_hangcheck, NULL) >= 0);
676 assert_se(sd_dhcp6_client_start(client) >= 0);
680 hangcheck = sd_event_source_unref(hangcheck);
682 assert_se(!sd_dhcp6_client_unref(client));
684 test_dhcp_fd[1] = safe_close(test_dhcp_fd[1]);
689 int main(int argc, char *argv[]) {
690 _cleanup_event_unref_ sd_event *e;
692 assert_se(sd_event_new(&e) >= 0);
694 log_set_max_level(LOG_DEBUG);
695 log_parse_environment();
698 test_client_basic(e);
700 test_advertise_option(e);
701 test_client_solicit(e);
703 assert_se(!sd_event_unref(e));